3232package com .jme3 .animation ;
3333
3434import com .jme3 .anim .SkinningControl ;
35- import com .jme3 .bounding .BoundingBox ;
36- import com .jme3 .bounding .BoundingVolume ;
3735import com .jme3 .export .*;
3836import com .jme3 .material .MatParamOverride ;
3937import com .jme3 .math .FastMath ;
@@ -108,13 +106,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
108106 */
109107 private transient Matrix4f [] offsetMatrices ;
110108
111- /**
112- * When true, the bounding volumes of animated geometries are updated each
113- * frame to match the current pose, ensuring correct frustum culling.
114- * Disabled by default because it adds CPU cost every frame.
115- */
116- private boolean updateBounds = false ;
117-
118109 private MatParamOverride numberOfBonesParam ;
119110 private MatParamOverride boneMatricesParam ;
120111
@@ -200,32 +191,6 @@ public boolean isHardwareSkinningUsed() {
200191 return hwSkinningEnabled ;
201192 }
202193
203- /**
204- * Enables or disables per-frame bounding-volume updates for animated
205- * geometries. When enabled, the bounding volume of each deformed geometry
206- * is recomputed every render frame to match the current animation pose,
207- * ensuring correct frustum culling at the cost of additional CPU work.
208- * Disabled by default.
209- *
210- * @param updateBounds true to update bounds each frame, false to keep
211- * static bind-pose bounds (default=false)
212- * @see #isUpdateBounds()
213- */
214- public void setUpdateBounds (boolean updateBounds ) {
215- this .updateBounds = updateBounds ;
216- }
217-
218- /**
219- * Returns whether per-frame bounding-volume updates are enabled for
220- * animated geometries.
221- *
222- * @return true if bounds are updated each frame, false otherwise
223- * @see #setUpdateBounds(boolean)
224- */
225- public boolean isUpdateBounds () {
226- return updateBounds ;
227- }
228-
229194 /**
230195 * Creates a skeleton control. The list of targets will be acquired
231196 * automatically when the control is attached to a node.
@@ -294,28 +259,12 @@ private void controlRenderSoftware() {
294259 // already ensured this mesh is animated.
295260 // Otherwise a crash will happen in skin update.
296261 softwareSkinUpdate (mesh , offsetMatrices );
297- if (updateBounds ) {
298- // Update the mesh bounding volume to reflect the animated vertex positions.
299- geometry .updateModelBound ();
300- }
301262 }
302263 }
303264
304265 private void controlRenderHardware () {
305266 offsetMatrices = skeleton .computeSkinningMatrices ();
306267 boneMatricesParam .setValue (offsetMatrices );
307-
308- if (updateBounds ) {
309- // Hardware skinning transforms vertices on the GPU, so the CPU-side vertex
310- // buffer is not updated. Compute the animated bounding volume from the bind
311- // pose positions and the current skinning matrices so culling is correct.
312- for (Geometry geometry : targets ) {
313- Mesh mesh = geometry .getMesh ();
314- if (mesh != null && mesh .isAnimated ()) {
315- updateSkinnedMeshBound (geometry , mesh , offsetMatrices );
316- }
317- }
318- }
319268 }
320269
321270 @ Override
@@ -754,108 +703,6 @@ private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexB
754703 tb .updateData (ftb );
755704 }
756705
757- /**
758- * Computes the bounding volume of an animated mesh from the bind pose
759- * positions and the current skinning matrices, then sets it on the geometry.
760- * This is used during hardware skinning to keep culling correct, since the
761- * GPU-transformed vertex positions are not reflected in the CPU-side vertex
762- * buffer.
763- *
764- * @param geometry the geometry whose bound needs to be updated
765- * @param mesh the animated mesh
766- * @param offsetMatrices the bone offset matrices for this frame
767- */
768- private static void updateSkinnedMeshBound (Geometry geometry , Mesh mesh ,
769- Matrix4f [] offsetMatrices ) {
770- VertexBuffer bindPosVB = mesh .getBuffer (Type .BindPosePosition );
771- if (bindPosVB == null ) {
772- return ;
773- }
774- VertexBuffer boneIndexVB = mesh .getBuffer (Type .BoneIndex );
775- VertexBuffer boneWeightVB = mesh .getBuffer (Type .BoneWeight );
776- if (boneIndexVB == null || boneWeightVB == null ) {
777- return ;
778- }
779- int maxWeightsPerVert = mesh .getMaxNumWeights ();
780- if (maxWeightsPerVert <= 0 ) {
781- return ;
782- }
783- int fourMinusMaxWeights = 4 - maxWeightsPerVert ;
784-
785- FloatBuffer bindPos = (FloatBuffer ) bindPosVB .getData ();
786- bindPos .rewind ();
787- IndexBuffer boneIndex = IndexBuffer .wrapIndexBuffer (boneIndexVB .getData ());
788- FloatBuffer boneWeightBuf = (FloatBuffer ) boneWeightVB .getData ();
789- boneWeightBuf .rewind ();
790- // Use array() when available (heap buffer), otherwise copy to a local array.
791- float [] weights ;
792- if (boneWeightBuf .hasArray ()) {
793- weights = boneWeightBuf .array ();
794- } else {
795- weights = new float [boneWeightBuf .limit ()];
796- boneWeightBuf .get (weights );
797- }
798- int idxWeights = 0 ;
799-
800- int numVerts = bindPos .limit () / 3 ;
801- float minX = Float .POSITIVE_INFINITY , minY = Float .POSITIVE_INFINITY ,
802- minZ = Float .POSITIVE_INFINITY ;
803- float maxX = Float .NEGATIVE_INFINITY , maxY = Float .NEGATIVE_INFINITY ,
804- maxZ = Float .NEGATIVE_INFINITY ;
805-
806- for (int v = 0 ; v < numVerts ; v ++) {
807- float vtx = bindPos .get ();
808- float vty = bindPos .get ();
809- float vtz = bindPos .get ();
810-
811- float rx , ry , rz ;
812- if (weights [idxWeights ] == 0 ) {
813- idxWeights += 4 ;
814- rx = vtx ;
815- ry = vty ;
816- rz = vtz ;
817- } else {
818- rx = 0 ;
819- ry = 0 ;
820- rz = 0 ;
821- for (int w = 0 ; w < maxWeightsPerVert ; w ++) {
822- float weight = weights [idxWeights ];
823- Matrix4f mat = offsetMatrices [boneIndex .get (idxWeights ++)];
824- rx += (mat .m00 * vtx + mat .m01 * vty + mat .m02 * vtz + mat .m03 ) * weight ;
825- ry += (mat .m10 * vtx + mat .m11 * vty + mat .m12 * vtz + mat .m13 ) * weight ;
826- rz += (mat .m20 * vtx + mat .m21 * vty + mat .m22 * vtz + mat .m23 ) * weight ;
827- }
828- idxWeights += fourMinusMaxWeights ;
829- }
830-
831- if (rx < minX ) minX = rx ;
832- if (rx > maxX ) maxX = rx ;
833- if (ry < minY ) minY = ry ;
834- if (ry > maxY ) maxY = ry ;
835- if (rz < minZ ) minZ = rz ;
836- if (rz > maxZ ) maxZ = rz ;
837- }
838-
839- // Reuse the existing BoundingBox if possible to avoid allocation.
840- BoundingVolume bv = mesh .getBound ();
841- BoundingBox bbox ;
842- if (bv instanceof BoundingBox ) {
843- bbox = (BoundingBox ) bv ;
844- } else {
845- bbox = new BoundingBox ();
846- }
847- TempVars vars = TempVars .get ();
848- try {
849- vars .vect1 .set (minX , minY , minZ );
850- vars .vect2 .set (maxX , maxY , maxZ );
851- bbox .setMinMax (vars .vect1 , vars .vect2 );
852- } finally {
853- vars .release ();
854- }
855- // setModelBound() updates the mesh bound and triggers a world-bound refresh.
856- geometry .setModelBound (bbox );
857- }
858-
859706 @ Override
860707 public void write (JmeExporter ex ) throws IOException {
861708 super .write (ex );
@@ -864,7 +711,6 @@ public void write(JmeExporter ex) throws IOException {
864711
865712 oc .write (numberOfBonesParam , "numberOfBonesParam" , null );
866713 oc .write (boneMatricesParam , "boneMatricesParam" , null );
867- oc .write (updateBounds , "updateBounds" , false );
868714 }
869715
870716 @ Override
@@ -875,7 +721,6 @@ public void read(JmeImporter im) throws IOException {
875721
876722 numberOfBonesParam = (MatParamOverride ) in .readSavable ("numberOfBonesParam" , null );
877723 boneMatricesParam = (MatParamOverride ) in .readSavable ("boneMatricesParam" , null );
878- updateBounds = in .readBoolean ("updateBounds" , false );
879724
880725 if (numberOfBonesParam == null ) {
881726 numberOfBonesParam = new MatParamOverride (VarType .Int , "NumberOfBones" , null );
0 commit comments