/* An instanced static mesh component with utility functions for adding, removing, and updating instances at run-time, with constant instance IDs. For all functions except ClearAllInstances(), you will have to call ForceUpdate(false) on the component yourself after running the function. This is to give you more control over how many times the component updates (for example, if you're AddInstance()'ing 500 instances, you probably dont want to update the component 500 times.) Deleted instances are placed at RecycledLocationX, Y, Z. This is 0,0,0 by default. Make sure that location isnt visible, or change the macros. Notes about Instanced Static Mesh Components you should keep in mind: - Materials used by this component should have "Used With Instanced Meshes" checked - Using a static mesh with more than one material will put all instances at vect(0, 0, 0), no matter what the transform input is. - Negative scales don't work like they do in the editor, the normals will be inverted and thus backface culling will happen on the wrong side. Use a two sided material or a second component with a flipped static mesh asset to combat this. Certain negative scales are also equivalent to a 180 degree rotation, so consider whether you're able to get to the final result with different transformations. - This component does not support LODs or Imposters. */ class LK_DynamicInstancedStaticMeshComponent extends InstancedStaticMeshComponent; /* Whether to use safety checks on every function call. This can add up quick considering we have to Find() an index in the recycle list to check whether an ID exists. Set this to FALSE if you want the increased speed at the cost of losing warnings. */ `define SAFE TRUE `define RecycledLocationX 0 `define RecycledLocationY 0 `define RecycledLocationZ 0 var private transient Array RecycleList; // Creates a new instance and returns its InstanceID. final public function int AddInstance() { local InstancedStaticMeshInstanceData Data; local int result; if(RecycleList.Length == 0) { PerInstanceSMData.AddItem(Data); result = PerInstanceSMData.Length-1; } else { result = RecycleList[RecycleList.Length - 1]; RecycleList.Length = RecycleList.Length - 1; } return result; } // Checks whether an instance with the specified ID exists. final public function bool InstanceExists(int Index) { return Index >= 0 && Index < PerInstanceSMData.Length && RecycleList.Find(Index) == -1; } // Removes an instance with the specified ID. final public function RemoveInstance(int Index) { `if(`SAFE) if(Index < 0 || Index >= PerInstanceSMData.Length || RecycleList.Find(Index) != -1) { `Broadcast("Attempted to remove instance from component "$String(self)$", but index was out of bounds or not in use! ("$String(Index)$"/"$String(PerInstanceSMData.Length-1)$")"); return; } `endif PerInstanceSMData[Index].Transform = MakeRotationTranslationMatrix(vect(`RecycledLocationX,`RecycledLocationY,`RecycledLocationZ), rot(0,0,0)); RecycleList.AddItem(Index); } // Removes all instances and updates the component (pass false to skip updating the component) final public function ClearAllInstances(optional bool bForceUpdate = true) { PerInstanceSMData.Length = 0; RecycleList.Length = 0; if(bForceUpdate) ForceUpdate(false); } // Updates an instance's location and rotation based on its ID. final public function UpdateInstanceTransform(int Index, Vector InTranslation, Rotator InRotation) { `if(`SAFE) if(Index < 0 || Index >= PerInstanceSMData.Length || RecycleList.Find(Index) != -1 ) { `Broadcast("Attempted to update instance in component "$String(self)$", but index was out of bounds or not in use! ("$String(Index)$"/"$String(PerInstanceSMData.Length-1)$")"); return; } `endif PerInstanceSMData[Index].Transform = MakeRotationTranslationMatrix(InTranslation, InRotation); } // Updates an instance's location, rotation, and scale based on its ID. (Slower than UpdateInstanceTransform) final public function UpdateInstanceTransformWithScale(int Index, Vector InTranslation, Rotator InRotation, Vector InScale) { `if(`SAFE) if(Index < 0 || Index >= PerInstanceSMData.Length || RecycleList.Find(Index) != -1) { `Broadcast("Attempted to update instance in component "$String(self)$", but index was out of bounds or not in use! ("$String(Index)$"/"$String(PerInstanceSMData.Length-1)$")"); return; } `endif PerInstanceSMData[Index].Transform = MakeScaleMatrix(InScale) * MakeRotationTranslationMatrix(InTranslation, InRotation); } final private static function Matrix MakeScaleMatrix(Vector InScale) { local Matrix Result; Result.XPlane.X = InScale.X; Result.YPlane.Y = InScale.Y; Result.ZPlane.Z = InScale.Z; Result.WPlane.W = 1.f; return Result; }