diff --git a/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java b/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java
index 7cc5f3c58b..9ceb4b3638 100644
--- a/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java
+++ b/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2023 jMonkeyEngine
+ * Copyright (c) 2009-2025 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,12 +31,21 @@
*/
package com.jme3.anim;
-import com.jme3.export.*;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
import com.jme3.material.MatParamOverride;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix4f;
-import com.jme3.renderer.*;
-import com.jme3.scene.*;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.mesh.IndexBuffer;
@@ -53,64 +62,77 @@
import java.util.logging.Logger;
/**
- * The Skinning control deforms a model according to an armature, It handles the
- * computation of the deformation matrices and performs the transformations on
- * the mesh
+ * The `SkinningControl` deforms a 3D model according to an {@link Armature}. It manages the
+ * computation of deformation matrices and applies these transformations to the mesh,
+ * supporting both software and hardware-accelerated skinning.
+ *
+ *
+ * **Software Skinning:** Performed on the CPU, offering broader compatibility but
+ * potentially lower performance for complex models.
*
- * It can perform software skinning or Hardware skinning
+ * **Hardware Skinning:** Utilizes the GPU for deformation, providing significantly
+ * better performance but requiring shader support and having a limit on the number
+ * of bones (typically 255 in common shaders).
*
- * @author Rémy Bouquet Based on SkeletonControl by Kirill Vainer
+ * @author Nehon
*/
-public class SkinningControl extends AbstractControl implements Cloneable, JmeCloneable {
+public class SkinningControl extends AbstractControl implements JmeCloneable {
private static final Logger logger = Logger.getLogger(SkinningControl.class.getName());
+ /**
+ * The maximum number of bones supported for hardware skinning in common shaders.
+ */
+ private static final int MAX_BONES_HW_SKINNING_SUPPORT = 255;
+
/**
* The armature of the model.
*/
private Armature armature;
/**
- * List of geometries affected by this control.
+ * A list of geometries that this control will deform.
*/
private SafeArrayList targets = new SafeArrayList<>(Geometry.class);
/**
- * Used to track when a mesh was updated. Meshes are only updated if they
+ * Used to track when a mesh needs to be updated. Meshes are only updated if they
* are visible in at least one camera.
*/
- private boolean wasMeshUpdated = false;
+ private boolean meshUpdateRequired = true;
/**
- * User wishes to use hardware skinning if available.
+ * Indicates whether hardware skinning is preferred. If `true` and the GPU
+ * supports it, hardware skinning will be enabled.
*/
- private transient boolean hwSkinningDesired = true;
+ private transient boolean hwSkinningPreferred = true;
/**
- * Hardware skinning is currently being used.
+ * Indicates if hardware skinning is currently active and being used.
*/
private transient boolean hwSkinningEnabled = false;
/**
- * Hardware skinning was tested on this GPU, results
- * are stored in {@link #hwSkinningSupported} variable.
+ * Flag indicating whether hardware skinning compatibility has been tested
+ * on the current GPU. Results are stored in {@link #hwSkinningSupported}.
*/
private transient boolean hwSkinningTested = false;
/**
- * If hardware skinning was {@link #hwSkinningTested tested}, then
- * this variable will be set to true if supported, and false if otherwise.
+ * Stores the result of the hardware skinning compatibility test. `true` if
+ * supported, `false` otherwise. This is only valid after
+ * {@link #hwSkinningTested} is `true`.
*/
private transient boolean hwSkinningSupported = false;
/**
- * Bone offset matrices, recreated each frame
+ * Bone offset matrices, computed each frame to deform the mesh based on
+ * the armature's current pose.
*/
- private transient Matrix4f[] offsetMatrices;
-
+ private transient Matrix4f[] boneOffsetMatrices;
- private MatParamOverride numberOfJointsParam;
- private MatParamOverride jointMatricesParam;
+ private MatParamOverride numberOfJointsParam = new MatParamOverride(VarType.Int, "NumberOfBones", null);
+ private MatParamOverride jointMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null);
/**
* Serialization only. Do not use.
@@ -119,26 +141,26 @@ protected SkinningControl() {
}
/**
- * Creates an armature control. The list of targets will be acquired
- * automatically when the control is attached to a node.
+ * Creates a new `SkinningControl` for the given armature.
*
- * @param armature the armature
+ * @param armature The armature that drives the deformation (not null).
*/
public SkinningControl(Armature armature) {
if (armature == null) {
- throw new IllegalArgumentException("armature cannot be null");
+ throw new IllegalArgumentException("armature cannot be null.");
}
this.armature = armature;
- this.numberOfJointsParam = new MatParamOverride(VarType.Int, "NumberOfBones", null);
- this.jointMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null);
}
-
- private void switchToHardware() {
+ /**
+ * Configures the material parameters and meshes for hardware skinning.
+ */
+ private void enableHardwareSkinning() {
numberOfJointsParam.setEnabled(true);
jointMatricesParam.setEnabled(true);
- // Next full 10 bones (e.g. 30 on 24 bones)
+ // Calculate the number of bones rounded up to the nearest multiple of 10.
+ // This is often required by shaders for array uniform declarations.
int numBones = ((armature.getJointCount() / 10) + 1) * 10;
numberOfJointsParam.setValue(numBones);
@@ -150,7 +172,10 @@ private void switchToHardware() {
}
}
- private void switchToSoftware() {
+ /**
+ * Configures the material parameters and meshes for software skinning.
+ */
+ private void enableSoftwareSkinning() {
numberOfJointsParam.setEnabled(false);
jointMatricesParam.setEnabled(false);
@@ -162,22 +187,34 @@ private void switchToSoftware() {
}
}
- private boolean testHardwareSupported(RenderManager rm) {
-
- //Only 255 bones max supported with hardware skinning
- if (armature.getJointCount() > 255) {
+ /**
+ * Tests if hardware skinning is supported by the GPU for the current spatial.
+ *
+ * @param renderManager the RenderManager instance
+ * @return true if hardware skinning is supported, false otherwise
+ */
+ private boolean testHardwareSupported(RenderManager renderManager) {
+ // Only 255 bones max supported with hardware skinning in common shaders.
+ if (armature.getJointCount() > MAX_BONES_HW_SKINNING_SUPPORT) {
+ logger.log(Level.INFO, "Hardware skinning not supported for {0}: Too many bones ({1} > 255).",
+ new Object[]{spatial, armature.getJointCount()});
return false;
}
- switchToHardware();
+ // Temporarily enable hardware skinning to test shader compilation.
+ enableHardwareSkinning();
+ boolean hwSkinningEngaged = false;
try {
- rm.preloadScene(spatial);
- return true;
- } catch (RendererException e) {
- logger.log(Level.WARNING, "Could not enable HW skinning due to shader compile error:", e);
- return false;
+ renderManager.preloadScene(spatial);
+ logger.log(Level.INFO, "Hardware skinning engaged for {0}", spatial);
+ hwSkinningEngaged = true;
+
+ } catch (RendererException ex) {
+ logger.log(Level.WARNING, "Could not enable HW skinning due to shader compile error: ", ex);
}
+
+ return hwSkinningEngaged;
}
/**
@@ -190,7 +227,7 @@ private boolean testHardwareSupported(RenderManager rm) {
* @see #isHardwareSkinningUsed()
*/
public void setHardwareSkinningPreferred(boolean preferred) {
- hwSkinningDesired = preferred;
+ hwSkinningPreferred = preferred;
}
/**
@@ -199,7 +236,7 @@ public void setHardwareSkinningPreferred(boolean preferred) {
* @see #setHardwareSkinningPreferred(boolean)
*/
public boolean isHardwareSkinningPreferred() {
- return hwSkinningDesired;
+ return hwSkinningPreferred;
}
/**
@@ -209,25 +246,21 @@ public boolean isHardwareSkinningUsed() {
return hwSkinningEnabled;
}
-
/**
- * If specified the geometry has an animated mesh, add its mesh and material
- * to the lists of animation targets.
+ * Recursively finds and adds animated geometries to the targets list.
+ *
+ * @param sp The spatial to search within.
*/
- private void findTargets(Geometry geometry) {
- Mesh mesh = geometry.getMesh();
- if (mesh != null && mesh.isAnimated()) {
- targets.add(geometry);
- }
-
- }
-
- private void findTargets(Node node) {
- for (Spatial child : node.getChildren()) {
- if (child instanceof Geometry) {
- findTargets((Geometry) child);
- } else if (child instanceof Node) {
- findTargets((Node) child);
+ private void collectAnimatedGeometries(Spatial sp) {
+ if (sp instanceof Geometry) {
+ Geometry geo = (Geometry) sp;
+ Mesh mesh = geo.getMesh();
+ if (mesh != null && mesh.isAnimated()) {
+ targets.add(geo);
+ }
+ } else if (sp instanceof Node) {
+ for (Spatial child : ((Node) sp).getChildren()) {
+ collectAnimatedGeometries(child);
}
}
}
@@ -236,65 +269,72 @@ private void findTargets(Node node) {
public void setSpatial(Spatial spatial) {
Spatial oldSpatial = this.spatial;
super.setSpatial(spatial);
- updateTargetsAndMaterials(spatial);
+ updateAnimationTargets(spatial);
if (oldSpatial != null) {
+ // Ensure parameters are removed from the old spatial to prevent memory leaks
oldSpatial.removeMatParamOverride(numberOfJointsParam);
oldSpatial.removeMatParamOverride(jointMatricesParam);
}
if (spatial != null) {
- spatial.removeMatParamOverride(numberOfJointsParam);
- spatial.removeMatParamOverride(jointMatricesParam);
+ // Add parameters to the new spatial. No need to remove first if they are not already present.
spatial.addMatParamOverride(numberOfJointsParam);
spatial.addMatParamOverride(jointMatricesParam);
}
}
+ /**
+ * Performs software skinning updates.
+ */
private void controlRenderSoftware() {
resetToBind(); // reset morph meshes to bind pose
- offsetMatrices = armature.computeSkinningMatrices();
+ boneOffsetMatrices = armature.computeSkinningMatrices();
for (Geometry geometry : targets) {
Mesh mesh = geometry.getMesh();
- // NOTE: This assumes code higher up has
- // already ensured this mesh is animated.
- // Otherwise a crash will happen in skin update.
- softwareSkinUpdate(mesh, offsetMatrices);
+ // NOTE: This assumes code higher up has already ensured this mesh is animated.
+ // Otherwise, a crash will happen in skin update.
+ applySoftwareSkinning(mesh, boneOffsetMatrices);
}
}
+ /**
+ * Prepares parameters for hardware skinning.
+ */
private void controlRenderHardware() {
- offsetMatrices = armature.computeSkinningMatrices();
- jointMatricesParam.setValue(offsetMatrices);
+ boneOffsetMatrices = armature.computeSkinningMatrices();
+ jointMatricesParam.setValue(boneOffsetMatrices);
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
- if (!wasMeshUpdated) {
- updateTargetsAndMaterials(spatial);
+ if (meshUpdateRequired) {
+ updateAnimationTargets(spatial);
// Prevent illegal cases. These should never happen.
- assert hwSkinningTested || (!hwSkinningTested && !hwSkinningSupported && !hwSkinningEnabled);
- assert !hwSkinningEnabled || (hwSkinningEnabled && hwSkinningTested && hwSkinningSupported);
+ assert hwSkinningTested || (!hwSkinningSupported && !hwSkinningEnabled);
+ assert !hwSkinningEnabled || (hwSkinningTested && hwSkinningSupported);
- if (hwSkinningDesired && !hwSkinningTested) {
+ if (hwSkinningPreferred && !hwSkinningTested) {
+ // If hardware skinning is preferred and hasn't been tested yet, test it.
hwSkinningTested = true;
hwSkinningSupported = testHardwareSupported(rm);
if (hwSkinningSupported) {
hwSkinningEnabled = true;
-
- Logger.getLogger(SkinningControl.class.getName()).log(Level.INFO, "Hardware skinning engaged for {0}", spatial);
} else {
- switchToSoftware();
+ enableSoftwareSkinning();
}
- } else if (hwSkinningDesired && hwSkinningSupported && !hwSkinningEnabled) {
- switchToHardware();
+ } else if (hwSkinningPreferred && hwSkinningSupported && !hwSkinningEnabled) {
+ // If hardware skinning is preferred, supported, but not yet enabled, enable it.
+ enableHardwareSkinning();
hwSkinningEnabled = true;
- } else if (!hwSkinningDesired && hwSkinningEnabled) {
- switchToSoftware();
+
+ } else if (!hwSkinningPreferred && hwSkinningEnabled) {
+ // If hardware skinning is no longer preferred but is enabled, switch to software.
+ enableSoftwareSkinning();
hwSkinningEnabled = false;
}
@@ -304,17 +344,22 @@ protected void controlRender(RenderManager rm, ViewPort vp) {
controlRenderSoftware();
}
- wasMeshUpdated = true;
+ meshUpdateRequired = false; // Reset flag after update
}
}
@Override
protected void controlUpdate(float tpf) {
- wasMeshUpdated = false;
+ meshUpdateRequired = true; // Mark for mesh update on next render pass
armature.update();
}
- //only do this for software updates
+ /**
+ * Resets the vertex, normal, and tangent buffers of animated meshes to their
+ * original bind pose. This is crucial for software skinning to ensure
+ * transformations are applied from a consistent base.
+ * This method is only applied when performing software updates.
+ */
void resetToBind() {
for (Geometry geometry : targets) {
Mesh mesh = geometry.getMesh();
@@ -378,51 +423,51 @@ public void cloneFields(Cloner cloner, Object original) {
}
/**
- * Access the attachments node of the named bone. If the bone doesn't
- * already have an attachments node, create one and attach it to the scene
- * graph. Models and effects attached to the attachments node will follow
- * the bone's motions.
+ * Provides access to the attachment node for a specific joint in the armature.
+ * If an attachment node does not already exist for the named joint, one will be
+ * created and attached to the scene graph. Models or effects attached to this
+ * node will follow the motion of the corresponding bone.
*
* @param jointName the name of the joint
* @return the attachments node of the joint
*/
public Node getAttachmentsNode(String jointName) {
- Joint b = armature.getJoint(jointName);
- if (b == null) {
- throw new IllegalArgumentException("Given bone name does not exist "
- + "in the armature.");
+ Joint joint = armature.getJoint(jointName);
+ if (joint == null) {
+ throw new IllegalArgumentException(
+ "Given joint name '" + jointName + "' does not exist in the armature.");
}
- updateTargetsAndMaterials(spatial);
- int boneIndex = armature.getJointIndex(b);
- Node n = b.getAttachmentsNode(boneIndex, targets);
- /*
- * Select a node to parent the attachments node.
- */
+ updateAnimationTargets(spatial);
+ int jointIndex = armature.getJointIndex(joint);
+ Node attachNode = joint.getAttachmentsNode(jointIndex, targets);
+
+ // Determine the appropriate parent for the attachment node.
Node parent;
if (spatial instanceof Node) {
parent = (Node) spatial; // the usual case
} else {
parent = spatial.getParent();
}
- parent.attachChild(n);
+ parent.attachChild(attachNode);
- return n;
+ return attachNode;
}
/**
- * returns the armature of this control
+ * Returns the armature associated with this skinning control.
*
- * @return the pre-existing instance
+ * @return The pre-existing `Armature` instance.
*/
public Armature getArmature() {
return armature;
}
/**
- * Enumerate the target meshes of this control.
+ * Returns an array containing all the target meshes that this control
+ * is currently affecting.
*
- * @return a new array
+ * @return A new array of `Mesh` objects.
*/
public Mesh[] getTargets() {
Mesh[] result = new Mesh[targets.size()];
@@ -437,30 +482,31 @@ public Mesh[] getTargets() {
}
/**
- * Update the mesh according to the given transformation matrices
+ * Applies software skinning transformations to the given mesh using the
+ * provided bone offset matrices.
*
- * @param mesh then mesh
- * @param offsetMatrices the transformation matrices to apply
+ * @param mesh The mesh to deform.
+ * @param offsetMatrices The array of transformation matrices for each bone.
*/
- private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
+ private void applySoftwareSkinning(Mesh mesh, Matrix4f[] offsetMatrices) {
VertexBuffer tb = mesh.getBuffer(Type.Tangent);
if (tb == null) {
- //if there are no tangents use the classic skinning
+ // if there are no tangents use the classic skinning
applySkinning(mesh, offsetMatrices);
} else {
- //if there are tangents use the skinning with tangents
+ // if there are tangents use the skinning with tangents
applySkinningTangents(mesh, offsetMatrices, tb);
}
-
-
}
/**
- * Method to apply skinning transforms to a mesh's buffers
+ * Applies skinning transformations to a mesh's position and normal buffers.
+ * This method iterates through each vertex, applies the weighted sum of
+ * bone transformations, and updates the vertex buffers.
*
- * @param mesh the mesh
- * @param offsetMatrices the offset matrices to apply
+ * @param mesh The mesh to apply skinning to.
+ * @param offsetMatrices The bone offset matrices to use for transformation.
*/
private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) {
int maxWeightsPerVert = mesh.getMaxNumWeights();
@@ -555,19 +601,16 @@ private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) {
vb.updateData(fvb);
nb.updateData(fnb);
-
}
/**
- * Specific method for skinning with tangents to avoid cluttering the
- * classic skinning calculation with null checks that would slow down the
- * process even if tangents don't have to be computed. Also the iteration
- * has additional indexes since tangent has 4 components instead of 3 for
- * pos and norm
+ * Applies skinning transformations to a mesh's position, normal, and tangent buffers.
+ * This method is specifically designed for meshes that include tangent data,
+ * ensuring proper deformation of tangents alongside positions and normals.
*
- * @param mesh the mesh
- * @param offsetMatrices the offsetMatrices to apply
- * @param tb the tangent vertexBuffer
+ * @param mesh The mesh to apply skinning to.
+ * @param offsetMatrices The bone offset matrices to use for transformation.
+ * @param tb The tangent `VertexBuffer`.
*/
private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) {
int maxWeightsPerVert = mesh.getMaxNumWeights();
@@ -594,7 +637,6 @@ private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexB
FloatBuffer ftb = (FloatBuffer) tb.getData();
ftb.rewind();
-
// get boneIndexes and weights for mesh
IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData());
FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
@@ -605,8 +647,6 @@ private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexB
int idxWeights = 0;
TempVars vars = TempVars.get();
-
-
float[] posBuf = vars.skinPositions;
float[] normBuf = vars.skinNormals;
float[] tanBuf = vars.skinTangents;
@@ -723,9 +763,6 @@ public void write(JmeExporter ex) throws IOException {
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
oc.write(armature, "armature", null);
-
- oc.write(numberOfJointsParam, "numberOfBonesParam", null);
- oc.write(jointMatricesParam, "boneMatricesParam", null);
}
/**
@@ -741,15 +778,13 @@ public void read(JmeImporter im) throws IOException {
InputCapsule in = im.getCapsule(this);
armature = (Armature) in.readSavable("armature", null);
- numberOfJointsParam = (MatParamOverride) in.readSavable("numberOfBonesParam", null);
- jointMatricesParam = (MatParamOverride) in.readSavable("boneMatricesParam", null);
-
- if (numberOfJointsParam == null) {
- numberOfJointsParam = new MatParamOverride(VarType.Int, "NumberOfBones", null);
- jointMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null);
- getSpatial().addMatParamOverride(numberOfJointsParam);
- getSpatial().addMatParamOverride(jointMatricesParam);
+ for (MatParamOverride mpo : spatial.getLocalMatParamOverrides().getArray()) {
+ if (mpo.getName().equals("NumberOfBones") || mpo.getName().equals("BoneMatrices")) {
+ spatial.removeMatParamOverride(mpo);
+ }
}
+ spatial.addMatParamOverride(numberOfJointsParam);
+ spatial.addMatParamOverride(jointMatricesParam);
}
/**
@@ -757,13 +792,9 @@ public void read(JmeImporter im) throws IOException {
*
* @param spatial the controlled spatial
*/
- private void updateTargetsAndMaterials(Spatial spatial) {
+ private void updateAnimationTargets(Spatial spatial) {
targets.clear();
-
- if (spatial instanceof Node) {
- findTargets((Node) spatial);
- } else if (spatial instanceof Geometry) {
- findTargets((Geometry) spatial);
- }
+ collectAnimatedGeometries(spatial);
}
+
}
diff --git a/jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java b/jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java
index 57dba2fb54..2c1449f5d5 100644
--- a/jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java
+++ b/jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2025 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,53 +29,109 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package jme3test.export;
import com.jme3.anim.AnimComposer;
+import com.jme3.anim.SkinningControl;
import com.jme3.app.SimpleApplication;
import com.jme3.export.binary.BinaryExporter;
-import com.jme3.export.binary.BinaryImporter;
+import com.jme3.font.BitmapText;
+import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
+import com.jme3.material.MatParamOverride;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
-import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
-import java.io.*;
-
+/**
+ * This class is a jMonkeyEngine 3 (jME3) test application designed to verify
+ * the import, export, and runtime behavior of 3D models, particularly those
+ * in or compatible with the Ogre3D format (.mesh.xml).
+ * It loads an Ogre model, saves and reloads it using jME3's binary exporter,
+ * plays an animation, and displays debugging information about its skinning
+ * and material parameters.
+ *
+ * @author capdevon
+ */
public class TestOgreConvert extends SimpleApplication {
- public static void main(String[] args){
+ public static void main(String[] args) {
TestOgreConvert app = new TestOgreConvert();
+ app.setPauseOnLostFocus(false);
app.start();
}
+ private final StringBuilder sb = new StringBuilder();
+ private int frameCount = 0;
+ private BitmapText bmp;
+ private Spatial spCopy;
+ private SkinningControl skinningControl;
+
@Override
public void simpleInitApp() {
- Spatial ogreModel = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ configureCamera();
+ setupLights();
+
+ bmp = createLabelText(10, 20, "");
+
+ // Load the Ogre model (Oto.mesh.xml) from the assets
+ Spatial model = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ // Save the loaded model to jME3's binary format and then reload it.
+ // This tests the binary serialization/deserialization process.
+ spCopy = BinaryExporter.saveAndLoad(assetManager, model);
+ spCopy.setName("Oto-Copy");
+ rootNode.attachChild(spCopy);
+
+ AnimComposer animComposer = spCopy.getControl(AnimComposer.class);
+ animComposer.setCurrentAction("Walk");
+
+ // Get the SkinningControl from the model to inspect skinning properties
+ skinningControl = spCopy.getControl(SkinningControl.class);
+ }
+
+ private void setupLights() {
+ AmbientLight al = new AmbientLight();
+ rootNode.addLight(al);
DirectionalLight dl = new DirectionalLight();
- dl.setColor(ColorRGBA.White);
- dl.setDirection(new Vector3f(0,-1,-1).normalizeLocal());
+ dl.setDirection(new Vector3f(0, -1, -1).normalizeLocal());
rootNode.addLight(dl);
+ }
- try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- BinaryExporter exp = new BinaryExporter();
- exp.save(ogreModel, baos);
+ private void configureCamera() {
+ flyCam.setDragToRotate(true);
+ flyCam.setMoveSpeed(15f);
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- BinaryImporter imp = new BinaryImporter();
- imp.setAssetManager(assetManager);
- Node ogreModelReloaded = (Node) imp.load(bais, null, null);
+ cam.setLocation(new Vector3f(0, 0, 20));
+ }
- AnimComposer composer = ogreModelReloaded.getControl(AnimComposer.class);
- composer.setCurrentAction("Walk");
+ @Override
+ public void simpleUpdate(float tpf) {
+ frameCount++;
+ if (frameCount == 10) {
+ frameCount = 0;
- rootNode.attachChild(ogreModelReloaded);
- } catch (IOException ex){
- ex.printStackTrace();
+ sb.append("HW Skinning Preferred: ").append(skinningControl.isHardwareSkinningPreferred()).append("\n");
+ sb.append("HW Skinning Enabled: ").append(skinningControl.isHardwareSkinningUsed()).append("\n");
+ sb.append("Mesh Targets: ").append(skinningControl.getTargets().length).append("\n");
+
+ for (MatParamOverride mpo : spCopy.getLocalMatParamOverrides()) {
+ sb.append(mpo.getVarType()).append(" ");
+ sb.append(mpo.getName()).append(": ");
+ sb.append(mpo.getValue()).append("\n");
+ }
+
+ bmp.setText(sb.toString());
+ sb.setLength(0);
}
}
+
+ private BitmapText createLabelText(int x, int y, String text) {
+ BitmapText bmp = new BitmapText(guiFont);
+ bmp.setText(text);
+ bmp.setLocalTranslation(x, settings.getHeight() - y, 0);
+ bmp.setColor(ColorRGBA.Red);
+ guiNode.attachChild(bmp);
+ return bmp;
+ }
}