diff --git a/model/AnimModel.cpp b/model/AnimModel.cpp index d5d856e0..09f01c8c 100644 --- a/model/AnimModel.cpp +++ b/model/AnimModel.cpp @@ -403,8 +403,8 @@ namespace { // TSubModel tree, which would make batched rendering unsafe. Most animations // loaded from .t3d files are global functions of time (clocks, wind, sky) and // only transform the local modelview matrix — those are safe to share. Camera- -// relative billboards also operate purely on the local matrix using whatever -// modelview the caller pushed, which is exactly per-instance behaviour. +// relative billboards are not: they're computed on the cpu from the modelview of +// the drawn instance, which in a batch is only the submodel-local chain. // The runtime SetRotate/SetTranslate animations (at_Rotate / at_RotateXYZ / // at_Translate) are tied to per-instance iAnimOwner and are unsafe to share. // at_Undefined is the type assigned to .t3d submodels declared with `anim: true` @@ -425,17 +425,40 @@ bool anim_type_unsafe_for_instancing( TAnimType a ) { } } +// returns true if this animation type depends only on global state (simulation +// time, wind, sky) and so gives every instance of the model the same local +// transform. The batch evaluates it once and the per-instance placement comes +// from the instance matrix, the result is the same as with per-instance drawing. +bool anim_type_shared_by_instances( TAnimType a ) { + switch( a ) { + case TAnimType::at_SecondsJump: + case TAnimType::at_MinutesJump: + case TAnimType::at_HoursJump: + case TAnimType::at_Hours24Jump: + case TAnimType::at_Seconds: + case TAnimType::at_Minutes: + case TAnimType::at_Hours: + case TAnimType::at_Hours24: + case TAnimType::at_Wind: + case TAnimType::at_Sky: + return true; + default: + return false; + } +} + // recursively walks a submodel tree and returns true if any submodel declares // an animation type that's unsafe to batch, OR carries the runtime "needs -// animation matrix" flag (iFlags bit 0x4000), which is set whenever the -// submodel was tagged as animatable in the .t3d file or had WillBeAnimated() -// called on it during model load. Either signal means the submodel may receive +// animation matrix" flag (iFlags bit 0x4000) for anything else than an animation +// shared by all instances. The flag is set whenever the submodel was tagged as +// animatable in the .t3d file (any `anim:` other than `false`) or had +// WillBeAnimated() called on it during model load. Such a submodel may receive // per-instance event-driven animation commands at runtime, which the GPU- // instanced path (one shared submodel tree across all instances) cannot serve. bool submodel_tree_blocks_instancing( TSubModel const *Sub ) { if( Sub == nullptr ) { return false; } if( anim_type_unsafe_for_instancing( Sub->b_Anim ) ) { return true; } - if( ( Sub->iFlags & 0x4000 ) != 0 ) { return true; } + if( ( ( Sub->iFlags & 0x4000 ) != 0 ) && ( false == anim_type_shared_by_instances( Sub->b_Anim ) ) ) { return true; } if( submodel_tree_blocks_instancing( Sub->Child ) ) { return true; } if( submodel_tree_blocks_instancing( Sub->Next ) ) { return true; } return false;