Hardware Morph animation implementation and glTF loading
This commit is contained in:
parent
909716fa03
commit
346173e2be
@ -16,7 +16,7 @@ public class AnimClip implements JmeCloneable, Savable {
|
|||||||
private String name;
|
private String name;
|
||||||
private double length;
|
private double length;
|
||||||
|
|
||||||
private TransformTrack[] tracks;
|
private AnimTrack[] tracks;
|
||||||
|
|
||||||
public AnimClip() {
|
public AnimClip() {
|
||||||
}
|
}
|
||||||
@ -25,9 +25,9 @@ public class AnimClip implements JmeCloneable, Savable {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTracks(TransformTrack[] tracks) {
|
public void setTracks(AnimTrack[] tracks) {
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
for (TransformTrack track : tracks) {
|
for (AnimTrack track : tracks) {
|
||||||
if (track.getLength() > length) {
|
if (track.getLength() > length) {
|
||||||
length = track.getLength();
|
length = track.getLength();
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ public class AnimClip implements JmeCloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TransformTrack[] getTracks() {
|
public AnimTrack[] getTracks() {
|
||||||
return tracks;
|
return tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ public class AnimClip implements JmeCloneable, Savable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cloneFields(Cloner cloner, Object original) {
|
public void cloneFields(Cloner cloner, Object original) {
|
||||||
TransformTrack[] newTracks = new TransformTrack[tracks.length];
|
AnimTrack[] newTracks = new AnimTrack[tracks.length];
|
||||||
for (int i = 0; i < tracks.length; i++) {
|
for (int i = 0; i < tracks.length; i++) {
|
||||||
newTracks[i] = (cloner.clone(tracks[i]));
|
newTracks[i] = (cloner.clone(tracks[i]));
|
||||||
}
|
}
|
||||||
|
12
jme3-core/src/main/java/com/jme3/anim/AnimTrack.java
Normal file
12
jme3-core/src/main/java/com/jme3/anim/AnimTrack.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.jme3.anim;
|
||||||
|
|
||||||
|
import com.jme3.export.Savable;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
|
|
||||||
|
public interface AnimTrack<T> extends Savable, JmeCloneable {
|
||||||
|
|
||||||
|
public void getDataAtTime(double time, T store);
|
||||||
|
public double getLength();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
165
jme3-core/src/main/java/com/jme3/anim/MorphControl.java
Normal file
165
jme3-core/src/main/java/com/jme3/anim/MorphControl.java
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package com.jme3.anim;
|
||||||
|
|
||||||
|
import com.jme3.material.*;
|
||||||
|
import com.jme3.renderer.*;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.SceneGraphVisitorAdapter;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.scene.control.AbstractControl;
|
||||||
|
import com.jme3.scene.mesh.MorphTarget;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
|
import com.jme3.util.SafeArrayList;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A control that handle morph animation for Position, Normal and Tangent buffers.
|
||||||
|
* All stock shaders only support morphing these 3 buffers, but note that MorphTargets can have any type of buffers.
|
||||||
|
* If you want to use other types of buffers you will need a custom MorphControl and a custom shader.
|
||||||
|
* @author Rémy Bouquet
|
||||||
|
*/
|
||||||
|
public class MorphControl extends AbstractControl {
|
||||||
|
|
||||||
|
private static final int MAX_MORPH_BUFFERS = 14;
|
||||||
|
private final static float MIN_WEIGHT = 0.005f;
|
||||||
|
|
||||||
|
private SafeArrayList<Geometry> targets = new SafeArrayList<>(Geometry.class);
|
||||||
|
private TargetLocator targetLocator = new TargetLocator();
|
||||||
|
|
||||||
|
private boolean approximateTangents = true;
|
||||||
|
private MatParamOverride nullNumberOfBones = new MatParamOverride(VarType.Int, "NumberOfBones", null);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void controlUpdate(float tpf) {
|
||||||
|
// gathering geometries in the sub graph.
|
||||||
|
// This must be done in the update phase as the gathering might add a matparam override
|
||||||
|
targets.clear();
|
||||||
|
this.spatial.depthFirstTraversal(targetLocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
|
for (Geometry target : targets) {
|
||||||
|
Mesh mesh = target.getMesh();
|
||||||
|
if (!mesh.isDirtyMorph()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int nbMaxBuffers = getRemainingBuffers(mesh, rm.getRenderer());
|
||||||
|
Material m = target.getMaterial();
|
||||||
|
|
||||||
|
float weights[] = mesh.getMorphState();
|
||||||
|
MorphTarget morphTargets[] = mesh.getMorphTargets();
|
||||||
|
float matWeights[];
|
||||||
|
MatParam param = m.getParam("MorphWeights");
|
||||||
|
|
||||||
|
//Number of buffer to handle for each morph target
|
||||||
|
int targetNumBuffers = getTargetNumBuffers(morphTargets[0]);
|
||||||
|
// compute the max number of targets to send to the GPU
|
||||||
|
int maxGPUTargets = Math.min(nbMaxBuffers, MAX_MORPH_BUFFERS) / targetNumBuffers;
|
||||||
|
if (param == null) {
|
||||||
|
matWeights = new float[maxGPUTargets];
|
||||||
|
m.setParam("MorphWeights", VarType.FloatArray, matWeights);
|
||||||
|
} else {
|
||||||
|
matWeights = (float[]) param.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// setting the maximum number as the real number may change every frame and trigger a shader recompilation since it's bound to a define.
|
||||||
|
m.setInt("NumberOfMorphTargets", maxGPUTargets);
|
||||||
|
m.setInt("NumberOfTargetsBuffers", targetNumBuffers);
|
||||||
|
|
||||||
|
int nbGPUTargets = 0;
|
||||||
|
int nbCPUBuffers = 0;
|
||||||
|
int boundBufferIdx = 0;
|
||||||
|
for (int i = 0; i < morphTargets.length; i++) {
|
||||||
|
if (weights[i] < MIN_WEIGHT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nbGPUTargets >= maxGPUTargets) {
|
||||||
|
//TODO we should fallback to CPU there.
|
||||||
|
nbCPUBuffers++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int start = VertexBuffer.Type.MorphTarget0.ordinal();
|
||||||
|
MorphTarget t = morphTargets[i];
|
||||||
|
if (targetNumBuffers >= 1) {
|
||||||
|
activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(VertexBuffer.Type.Position));
|
||||||
|
boundBufferIdx++;
|
||||||
|
}
|
||||||
|
if (targetNumBuffers >= 2) {
|
||||||
|
activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(VertexBuffer.Type.Normal));
|
||||||
|
boundBufferIdx++;
|
||||||
|
}
|
||||||
|
if (!approximateTangents && targetNumBuffers == 3) {
|
||||||
|
activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(VertexBuffer.Type.Tangent));
|
||||||
|
boundBufferIdx++;
|
||||||
|
}
|
||||||
|
matWeights[nbGPUTargets] = weights[i];
|
||||||
|
nbGPUTargets++;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (nbGPUTargets < matWeights.length) {
|
||||||
|
for (int i = nbGPUTargets; i < matWeights.length; i++) {
|
||||||
|
matWeights[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activateBuffer(Mesh mesh, int idx, int start, FloatBuffer b) {
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.values()[start + idx], 3, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getTargetNumBuffers(MorphTarget morphTarget) {
|
||||||
|
int num = 0;
|
||||||
|
if (morphTarget.getBuffer(VertexBuffer.Type.Position) != null) num++;
|
||||||
|
if (morphTarget.getBuffer(VertexBuffer.Type.Normal) != null) num++;
|
||||||
|
|
||||||
|
// if tangents are not needed we don't count the tangent buffer
|
||||||
|
if (!approximateTangents && morphTarget.getBuffer(VertexBuffer.Type.Tangent) != null) {
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getRemainingBuffers(Mesh mesh, Renderer renderer) {
|
||||||
|
int nbUsedBuffers = 0;
|
||||||
|
for (VertexBuffer vb : mesh.getBufferList().getArray()) {
|
||||||
|
boolean isMorphBuffer = vb.getBufferType().ordinal() >= VertexBuffer.Type.MorphTarget0.ordinal() && vb.getBufferType().ordinal() <= VertexBuffer.Type.MorphTarget9.ordinal();
|
||||||
|
if (vb.getBufferType() == VertexBuffer.Type.Index || isMorphBuffer) continue;
|
||||||
|
if (vb.getUsage() != VertexBuffer.Usage.CpuOnly) {
|
||||||
|
nbUsedBuffers++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return renderer.getLimits().get(Limits.VertexAttributes) - nbUsedBuffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApproximateTangents(boolean approximateTangents) {
|
||||||
|
this.approximateTangents = approximateTangents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isApproximateTangents() {
|
||||||
|
return approximateTangents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TargetLocator extends SceneGraphVisitorAdapter {
|
||||||
|
@Override
|
||||||
|
public void visit(Geometry geom) {
|
||||||
|
MatParam p = geom.getMaterial().getMaterialDef().getMaterialParam("MorphWeights");
|
||||||
|
if (p == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Mesh mesh = geom.getMesh();
|
||||||
|
if (mesh != null && mesh.hasMorphTargets()) {
|
||||||
|
targets.add(geom);
|
||||||
|
// If the mesh is in a subgraph of a node with a SkinningControl it might have hardware skinning activated through mat param override even if it's not skinned.
|
||||||
|
// this code makes sure that if the mesh has no hardware skinning buffers hardware skinning won't be activated.
|
||||||
|
// this is important, because if HW skinning is activated the shader will declare 2 additional useless attributes,
|
||||||
|
// and we desperately need all the attributes we can find for Morph animation.
|
||||||
|
if (mesh.getBuffer(VertexBuffer.Type.HWBoneIndex) == null && !geom.getLocalMatParamOverrides().contains(nullNumberOfBones)) {
|
||||||
|
geom.addMatParamOverride(nullNumberOfBones);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
217
jme3-core/src/main/java/com/jme3/anim/MorphTrack.java
Normal file
217
jme3-core/src/main/java/com/jme3/anim/MorphTrack.java
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.anim;
|
||||||
|
|
||||||
|
import com.jme3.anim.interpolator.FrameInterpolator;
|
||||||
|
import com.jme3.animation.*;
|
||||||
|
import com.jme3.export.*;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of weights and times for each keyframe.
|
||||||
|
*
|
||||||
|
* @author Rémy Bouquet
|
||||||
|
*/
|
||||||
|
public class MorphTrack implements AnimTrack<float[]> {
|
||||||
|
|
||||||
|
private double length;
|
||||||
|
private Geometry target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weights and times for track.
|
||||||
|
*/
|
||||||
|
private float[] weights;
|
||||||
|
private FrameInterpolator interpolator = FrameInterpolator.DEFAULT;
|
||||||
|
private float[] times;
|
||||||
|
private int nbMorphTargets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialization-only. Do not use.
|
||||||
|
*/
|
||||||
|
public MorphTrack() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a morph track with the given Geometry as a target
|
||||||
|
*
|
||||||
|
* @param times a float array with the time of each frame
|
||||||
|
* @param weights the morphs for each frames
|
||||||
|
*/
|
||||||
|
public MorphTrack(Geometry target, float[] times, float[] weights, int nbMorphTargets) {
|
||||||
|
this.target = target;
|
||||||
|
this.nbMorphTargets = nbMorphTargets;
|
||||||
|
this.setKeyframes(times, weights);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the array of weights of this track
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public float[] getWeights() {
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the arrays of time for this track
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public float[] getTimes() {
|
||||||
|
return times;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the keyframes times for this Joint track
|
||||||
|
*
|
||||||
|
* @param times the keyframes times
|
||||||
|
*/
|
||||||
|
public void setTimes(float[] times) {
|
||||||
|
if (times.length == 0) {
|
||||||
|
throw new RuntimeException("TransformTrack with no keyframes!");
|
||||||
|
}
|
||||||
|
this.times = times;
|
||||||
|
length = times[times.length - 1] - times[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the weight for this morph track
|
||||||
|
*
|
||||||
|
* @param times a float array with the time of each frame
|
||||||
|
* @param weights the weights of the morphs for each frame
|
||||||
|
|
||||||
|
*/
|
||||||
|
public void setKeyframes(float[] times, float[] weights) {
|
||||||
|
setTimes(times);
|
||||||
|
if (weights != null) {
|
||||||
|
if (times == null) {
|
||||||
|
throw new RuntimeException("MorphTrack doesn't have any time for key frames, please call setTimes first");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.weights = weights;
|
||||||
|
|
||||||
|
assert times != null && times.length == weights.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getDataAtTime(double t, float[] store) {
|
||||||
|
float time = (float) t;
|
||||||
|
|
||||||
|
int lastFrame = times.length - 1;
|
||||||
|
if (time < 0 || lastFrame == 0) {
|
||||||
|
if (weights != null) {
|
||||||
|
System.arraycopy(weights,0,store,0, nbMorphTargets);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startFrame = 0;
|
||||||
|
int endFrame = 1;
|
||||||
|
float blend = 0;
|
||||||
|
if (time >= times[lastFrame]) {
|
||||||
|
startFrame = lastFrame;
|
||||||
|
|
||||||
|
time = time - times[startFrame] + times[startFrame - 1];
|
||||||
|
blend = (time - times[startFrame - 1])
|
||||||
|
/ (times[startFrame] - times[startFrame - 1]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// use lastFrame so we never overflow the array
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < lastFrame && times[i] < time; i++) {
|
||||||
|
startFrame = i;
|
||||||
|
endFrame = i + 1;
|
||||||
|
}
|
||||||
|
blend = (time - times[startFrame])
|
||||||
|
/ (times[endFrame] - times[startFrame]);
|
||||||
|
}
|
||||||
|
|
||||||
|
interpolator.interpolateWeights(blend, startFrame, weights, nbMorphTargets, store);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFrameInterpolator(FrameInterpolator interpolator) {
|
||||||
|
this.interpolator = interpolator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Geometry getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTarget(Geometry target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter ex) throws IOException {
|
||||||
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
|
oc.write(weights, "weights", null);
|
||||||
|
oc.write(times, "times", null);
|
||||||
|
oc.write(target, "target", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter im) throws IOException {
|
||||||
|
InputCapsule ic = im.getCapsule(this);
|
||||||
|
weights = ic.readFloatArray("weights", null);
|
||||||
|
times = ic.readFloatArray("times", null);
|
||||||
|
target = (Geometry) ic.readSavable("target", null);
|
||||||
|
setTimes(times);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
MorphTrack clone = (MorphTrack) super.clone();
|
||||||
|
return clone;
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cloneFields(Cloner cloner, Object original) {
|
||||||
|
this.target = cloner.clone(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -48,7 +48,7 @@ import java.io.IOException;
|
|||||||
*
|
*
|
||||||
* @author Rémy Bouquet
|
* @author Rémy Bouquet
|
||||||
*/
|
*/
|
||||||
public class TransformTrack implements JmeCloneable, Savable {
|
public class TransformTrack implements AnimTrack<Transform> {
|
||||||
|
|
||||||
private double length;
|
private double length;
|
||||||
private HasLocalTransform target;
|
private HasLocalTransform target;
|
||||||
@ -81,15 +81,6 @@ public class TransformTrack implements JmeCloneable, Savable {
|
|||||||
this.setKeyframes(times, translations, rotations, scales);
|
this.setKeyframes(times, translations, rotations, scales);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a bone track for the given bone index
|
|
||||||
*
|
|
||||||
* @param targetJointIndex the bone's index
|
|
||||||
*/
|
|
||||||
public TransformTrack(int targetJointIndex) {
|
|
||||||
this();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return the array of rotations of this track
|
* return the array of rotations of this track
|
||||||
*
|
*
|
||||||
@ -223,7 +214,7 @@ public class TransformTrack implements JmeCloneable, Savable {
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getTransformAtTime(double t, Transform transform) {
|
public void getDataAtTime(double t, Transform transform) {
|
||||||
float time = (float) t;
|
float time = (float) t;
|
||||||
|
|
||||||
int lastFrame = times.length - 1;
|
int lastFrame = times.length - 1;
|
||||||
|
95
jme3-core/src/main/java/com/jme3/anim/Weights.java
Normal file
95
jme3-core/src/main/java/com/jme3/anim/Weights.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package com.jme3.anim;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class Weights {//} extends Savable, JmeCloneable{
|
||||||
|
|
||||||
|
|
||||||
|
private final static float MIN_WEIGHT = 0.005f;
|
||||||
|
|
||||||
|
private int[] indices;
|
||||||
|
private float[] data;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
public Weights(float[] array, int start, int length) {
|
||||||
|
ArrayList<Float> list = new ArrayList<>();
|
||||||
|
ArrayList<Integer> idx = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = start; i < length; i++) {
|
||||||
|
float val = array[i];
|
||||||
|
if (val > MIN_WEIGHT) {
|
||||||
|
list.add(val);
|
||||||
|
idx.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size = list.size();
|
||||||
|
data = new float[size];
|
||||||
|
indices = new int[size];
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
data[i] = list.get(i);
|
||||||
|
indices[i] = idx.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public Weights(float[] array, int start, int length) {
|
||||||
|
// LinkedList<Float> list = new LinkedList<>();
|
||||||
|
// LinkedList<Integer> idx = new LinkedList<>();
|
||||||
|
// for (int i = start; i < length; i++) {
|
||||||
|
// float val = array[i];
|
||||||
|
// if (val > MIN_WEIGHT) {
|
||||||
|
// int index = insert(list, val);
|
||||||
|
// if (idx.size() < index) {
|
||||||
|
// idx.add(i);
|
||||||
|
// } else {
|
||||||
|
// idx.add(index, i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// data = new float[list.size()];
|
||||||
|
// for (int i = 0; i < data.length; i++) {
|
||||||
|
// data[i] = list.get(i);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// indices = new int[idx.size()];
|
||||||
|
// for (int i = 0; i < indices.length; i++) {
|
||||||
|
// indices[i] = idx.get(i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private int insert(LinkedList<Float> list, float value) {
|
||||||
|
// for (int i = 0; i < list.size(); i++) {
|
||||||
|
// float w = list.get(i);
|
||||||
|
// if (value > w) {
|
||||||
|
// list.add(i, value);
|
||||||
|
// return i;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// list.add(value);
|
||||||
|
// return list.size();
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
for (int i = 0; i < indices.length; i++) {
|
||||||
|
b.append(indices[i]).append(",");
|
||||||
|
}
|
||||||
|
b.append("\n");
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
b.append(data[i]).append(",");
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String... args) {
|
||||||
|
// 6 7 4 8
|
||||||
|
float values[] = {0, 0, 0, 0, 0.5f, 0.001f, 0.7f, 0.6f, 0.2f, 0, 0, 0};
|
||||||
|
Weights w = new Weights(values, 0, values.length);
|
||||||
|
System.err.println(w);
|
||||||
|
}
|
||||||
|
}
|
@ -58,7 +58,6 @@ public class AnimInterpolators {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Position / Scale interpolators
|
//Position / Scale interpolators
|
||||||
|
|
||||||
public static final AnimInterpolator<Vector3f> LinearVec3f = new AnimInterpolator<Vector3f>() {
|
public static final AnimInterpolator<Vector3f> LinearVec3f = new AnimInterpolator<Vector3f>() {
|
||||||
private Vector3f next = new Vector3f();
|
private Vector3f next = new Vector3f();
|
||||||
|
|
||||||
@ -70,7 +69,6 @@ public class AnimInterpolators {
|
|||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CatmullRom interpolation
|
* CatmullRom interpolation
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +20,7 @@ public class FrameInterpolator {
|
|||||||
private TrackDataReader<Vector3f> scaleReader = new TrackDataReader<>();
|
private TrackDataReader<Vector3f> scaleReader = new TrackDataReader<>();
|
||||||
private TrackTimeReader timesReader = new TrackTimeReader();
|
private TrackTimeReader timesReader = new TrackTimeReader();
|
||||||
|
|
||||||
|
|
||||||
private Transform transforms = new Transform();
|
private Transform transforms = new Transform();
|
||||||
|
|
||||||
public Transform interpolate(float t, int currentIndex, CompactVector3Array translations, CompactQuaternionArray rotations, CompactVector3Array scales, float[] times){
|
public Transform interpolate(float t, int currentIndex, CompactVector3Array translations, CompactQuaternionArray rotations, CompactVector3Array scales, float[] times){
|
||||||
@ -42,6 +43,20 @@ public class FrameInterpolator {
|
|||||||
return transforms;
|
return transforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void interpolateWeights(float t, int currentIndex, float[] weights, int nbMorphTargets, float[] store) {
|
||||||
|
int start = currentIndex * nbMorphTargets;
|
||||||
|
for (int i = 0; i < nbMorphTargets; i++) {
|
||||||
|
int current = start + i;
|
||||||
|
int next = current + nbMorphTargets;
|
||||||
|
if (next >= weights.length) {
|
||||||
|
next = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
float val = FastMath.interpolateLinear(t, weights[current], weights[next]);
|
||||||
|
store[i] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setTimeInterpolator(AnimInterpolator<Float> timeInterpolator) {
|
public void setTimeInterpolator(AnimInterpolator<Float> timeInterpolator) {
|
||||||
this.timeInterpolator = timeInterpolator;
|
this.timeInterpolator = timeInterpolator;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package com.jme3.anim.tween.action;
|
package com.jme3.anim.tween.action;
|
||||||
|
|
||||||
import com.jme3.anim.AnimClip;
|
import com.jme3.anim.*;
|
||||||
import com.jme3.anim.TransformTrack;
|
|
||||||
import com.jme3.anim.tween.AbstractTween;
|
import com.jme3.anim.tween.AbstractTween;
|
||||||
import com.jme3.anim.util.HasLocalTransform;
|
import com.jme3.anim.util.HasLocalTransform;
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -22,11 +22,20 @@ public class ClipAction extends BlendableAction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doInterpolate(double t) {
|
public void doInterpolate(double t) {
|
||||||
TransformTrack[] tracks = clip.getTracks();
|
AnimTrack[] tracks = clip.getTracks();
|
||||||
for (TransformTrack track : tracks) {
|
for (AnimTrack track : tracks) {
|
||||||
|
if (track instanceof TransformTrack) {
|
||||||
|
interpolateTransformTrack(t, (TransformTrack) track);
|
||||||
|
} else if (track instanceof MorphTrack) {
|
||||||
|
interpolateMorphTrack(t, (MorphTrack) track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void interpolateTransformTrack(double t, TransformTrack track) {
|
||||||
HasLocalTransform target = track.getTarget();
|
HasLocalTransform target = track.getTarget();
|
||||||
transform.set(target.getLocalTransform());
|
transform.set(target.getLocalTransform());
|
||||||
track.getTransformAtTime(t, transform);
|
track.getDataAtTime(t, transform);
|
||||||
|
|
||||||
if (collectTransformDelegate != null) {
|
if (collectTransformDelegate != null) {
|
||||||
collectTransformDelegate.collectTransform(target, transform, getWeight(), this);
|
collectTransformDelegate.collectTransform(target, transform, getWeight(), this);
|
||||||
@ -34,6 +43,18 @@ public class ClipAction extends BlendableAction {
|
|||||||
this.collectTransform(target, transform, getTransitionWeight(), this);
|
this.collectTransform(target, transform, getTransitionWeight(), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void interpolateMorphTrack(double t, MorphTrack track) {
|
||||||
|
Geometry target = track.getTarget();
|
||||||
|
float[] weights = new float[target.getMesh().getMorphTargets().length];
|
||||||
|
track.getDataAtTime(t, weights);
|
||||||
|
target.getMesh().setMorphState(weights);
|
||||||
|
|
||||||
|
|
||||||
|
// if (collectTransformDelegate != null) {
|
||||||
|
// collectTransformDelegate.collectTransform(target, transform, getWeight(), this);
|
||||||
|
// } else {
|
||||||
|
// this.collectTransform(target, transform, getTransitionWeight(), this);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
@ -43,8 +64,10 @@ public class ClipAction extends BlendableAction {
|
|||||||
@Override
|
@Override
|
||||||
public Collection<HasLocalTransform> getTargets() {
|
public Collection<HasLocalTransform> getTargets() {
|
||||||
List<HasLocalTransform> targets = new ArrayList<>(clip.getTracks().length);
|
List<HasLocalTransform> targets = new ArrayList<>(clip.getTracks().length);
|
||||||
for (TransformTrack track : clip.getTracks()) {
|
for (AnimTrack track : clip.getTracks()) {
|
||||||
targets.add(track.getTarget());
|
if (track instanceof TransformTrack) {
|
||||||
|
targets.add(((TransformTrack) track).getTarget());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public abstract class CompactArray<T> implements JmeCloneable {
|
public abstract class CompactArray<T> implements JmeCloneable {
|
||||||
|
|
||||||
private Map<T, Integer> indexPool = new HashMap<T, Integer>();
|
protected Map<T, Integer> indexPool = new HashMap<T, Integer>();
|
||||||
protected int[] index;
|
protected int[] index;
|
||||||
protected float[] array;
|
protected float[] array;
|
||||||
private boolean invalid;
|
private boolean invalid;
|
||||||
@ -114,6 +114,10 @@ public abstract class CompactArray<T> implements JmeCloneable {
|
|||||||
indexPool.clear();
|
indexPool.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setInvalid(boolean invalid) {
|
||||||
|
this.invalid = invalid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param index
|
* @param index
|
||||||
* @param value
|
* @param value
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.animation;
|
||||||
|
|
||||||
|
import com.jme3.export.*;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize and compress Float by indexing similar values
|
||||||
|
* @author Lim, YongHoon
|
||||||
|
*/
|
||||||
|
public class CompactFloatArray extends CompactArray<Float> implements Savable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a compact vector array
|
||||||
|
*/
|
||||||
|
public CompactFloatArray() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a compact vector array
|
||||||
|
* @param dataArray the data array
|
||||||
|
* @param index the indices
|
||||||
|
*/
|
||||||
|
public CompactFloatArray(float[] dataArray, int[] index) {
|
||||||
|
super(dataArray, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final int getTupleSize() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final Class<Float> getElementClass() {
|
||||||
|
return Float.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter ex) throws IOException {
|
||||||
|
serialize();
|
||||||
|
OutputCapsule out = ex.getCapsule(this);
|
||||||
|
out.write(array, "array", null);
|
||||||
|
out.write(index, "index", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter im) throws IOException {
|
||||||
|
InputCapsule in = im.getCapsule(this);
|
||||||
|
array = in.readFloatArray("array", null);
|
||||||
|
index = in.readIntArray("index", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fill(int startIndex, float[] store ){
|
||||||
|
for (int i = 0; i < store.length; i++) {
|
||||||
|
store[i] = get(startIndex + i, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void serialize(int i, Float data) {
|
||||||
|
array[i] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Float deserialize(int i, Float store) {
|
||||||
|
return array[i];
|
||||||
|
}
|
||||||
|
}
|
@ -50,6 +50,7 @@ import com.jme3.util.clone.JmeCloneable;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>Mesh</code> is used to store rendering data.
|
* <code>Mesh</code> is used to store rendering data.
|
||||||
@ -164,8 +165,8 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
|
|
||||||
private CollisionData collisionTree = null;
|
private CollisionData collisionTree = null;
|
||||||
|
|
||||||
private SafeArrayList<VertexBuffer> buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class);
|
private SafeArrayList<VertexBuffer> buffersList = new SafeArrayList<>(VertexBuffer.class);
|
||||||
private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
|
private IntMap<VertexBuffer> buffers = new IntMap<>();
|
||||||
private VertexBuffer[] lodLevels;
|
private VertexBuffer[] lodLevels;
|
||||||
private float pointSize = 1;
|
private float pointSize = 1;
|
||||||
private float lineWidth = 1;
|
private float lineWidth = 1;
|
||||||
@ -183,6 +184,11 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
|
|
||||||
private Mode mode = Mode.Triangles;
|
private Mode mode = Mode.Triangles;
|
||||||
|
|
||||||
|
private SafeArrayList<MorphTarget> morphTargets;
|
||||||
|
private int numMorphBuffers = 0;
|
||||||
|
private float[] morphState;
|
||||||
|
private boolean dirtyMorph = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new mesh with no {@link VertexBuffer vertex buffers}.
|
* Creates a new mesh with no {@link VertexBuffer vertex buffers}.
|
||||||
*/
|
*/
|
||||||
@ -203,7 +209,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
clone.meshBound = meshBound.clone();
|
clone.meshBound = meshBound.clone();
|
||||||
clone.collisionTree = collisionTree != null ? collisionTree : null;
|
clone.collisionTree = collisionTree != null ? collisionTree : null;
|
||||||
clone.buffers = buffers.clone();
|
clone.buffers = buffers.clone();
|
||||||
clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class,buffersList);
|
clone.buffersList = new SafeArrayList<>(VertexBuffer.class, buffersList);
|
||||||
clone.vertexArrayID = -1;
|
clone.vertexArrayID = -1;
|
||||||
if (elementLengths != null) {
|
if (elementLengths != null) {
|
||||||
clone.elementLengths = elementLengths.clone();
|
clone.elementLengths = elementLengths.clone();
|
||||||
@ -233,8 +239,8 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
//clone.collisionTree = collisionTree != null ? collisionTree : null;
|
//clone.collisionTree = collisionTree != null ? collisionTree : null;
|
||||||
clone.collisionTree = null; // it will get re-generated in any case
|
clone.collisionTree = null; // it will get re-generated in any case
|
||||||
|
|
||||||
clone.buffers = new IntMap<VertexBuffer>();
|
clone.buffers = new IntMap<>();
|
||||||
clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class);
|
clone.buffersList = new SafeArrayList<>(VertexBuffer.class);
|
||||||
for (VertexBuffer vb : buffersList.getArray()){
|
for (VertexBuffer vb : buffersList.getArray()){
|
||||||
VertexBuffer bufClone = vb.clone();
|
VertexBuffer bufClone = vb.clone();
|
||||||
clone.buffers.put(vb.getBufferType().ordinal(), bufClone);
|
clone.buffers.put(vb.getBufferType().ordinal(), bufClone);
|
||||||
@ -697,7 +703,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setInterleaved(){
|
public void setInterleaved(){
|
||||||
ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>();
|
ArrayList<VertexBuffer> vbs = new ArrayList<>();
|
||||||
vbs.addAll(buffersList);
|
vbs.addAll(buffersList);
|
||||||
|
|
||||||
// ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values());
|
// ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values());
|
||||||
@ -820,8 +826,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
* {@link #setInterleaved() interleaved} format.
|
* {@link #setInterleaved() interleaved} format.
|
||||||
*/
|
*/
|
||||||
public void updateCounts(){
|
public void updateCounts(){
|
||||||
if (getBuffer(Type.InterleavedData) != null)
|
if (getBuffer(Type.InterleavedData) != null) {
|
||||||
throw new IllegalStateException("Should update counts before interleave");
|
throw new IllegalStateException("Should update counts before interleave");
|
||||||
|
}
|
||||||
|
|
||||||
VertexBuffer pb = getBuffer(Type.Position);
|
VertexBuffer pb = getBuffer(Type.Position);
|
||||||
VertexBuffer ib = getBuffer(Type.Index);
|
VertexBuffer ib = getBuffer(Type.Index);
|
||||||
@ -844,11 +851,13 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
*/
|
*/
|
||||||
public int getTriangleCount(int lod){
|
public int getTriangleCount(int lod){
|
||||||
if (lodLevels != null){
|
if (lodLevels != null){
|
||||||
if (lod < 0)
|
if (lod < 0) {
|
||||||
throw new IllegalArgumentException("LOD level cannot be < 0");
|
throw new IllegalArgumentException("LOD level cannot be < 0");
|
||||||
|
}
|
||||||
|
|
||||||
if (lod >= lodLevels.length)
|
if (lod >= lodLevels.length) {
|
||||||
throw new IllegalArgumentException("LOD level "+lod+" does not exist!");
|
throw new IllegalArgumentException("LOD level " + lod + " does not exist!");
|
||||||
|
}
|
||||||
|
|
||||||
return computeNumElements(lodLevels[lod].getData().limit());
|
return computeNumElements(lodLevels[lod].getData().limit());
|
||||||
}else if (lod == 0){
|
}else if (lod == 0){
|
||||||
@ -968,8 +977,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
* Sets the mesh's VAO ID. Internal use only.
|
* Sets the mesh's VAO ID. Internal use only.
|
||||||
*/
|
*/
|
||||||
public void setId(int id){
|
public void setId(int id){
|
||||||
if (vertexArrayID != -1)
|
if (vertexArrayID != -1) {
|
||||||
throw new IllegalStateException("ID has already been set.");
|
throw new IllegalStateException("ID has already been set.");
|
||||||
|
}
|
||||||
|
|
||||||
vertexArrayID = id;
|
vertexArrayID = id;
|
||||||
}
|
}
|
||||||
@ -1037,8 +1047,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
* @throws IllegalArgumentException If the buffer type is already set
|
* @throws IllegalArgumentException If the buffer type is already set
|
||||||
*/
|
*/
|
||||||
public void setBuffer(VertexBuffer vb){
|
public void setBuffer(VertexBuffer vb){
|
||||||
if (buffers.containsKey(vb.getBufferType().ordinal()))
|
if (buffers.containsKey(vb.getBufferType().ordinal())) {
|
||||||
throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
|
throw new IllegalArgumentException("Buffer type already set: " + vb.getBufferType());
|
||||||
|
}
|
||||||
|
|
||||||
buffers.put(vb.getBufferType().ordinal(), vb);
|
buffers.put(vb.getBufferType().ordinal(), vb);
|
||||||
buffersList.add(vb);
|
buffersList.add(vb);
|
||||||
@ -1151,8 +1162,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
*/
|
*/
|
||||||
public FloatBuffer getFloatBuffer(Type type) {
|
public FloatBuffer getFloatBuffer(Type type) {
|
||||||
VertexBuffer vb = getBuffer(type);
|
VertexBuffer vb = getBuffer(type);
|
||||||
if (vb == null)
|
if (vb == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (FloatBuffer) vb.getData();
|
return (FloatBuffer) vb.getData();
|
||||||
}
|
}
|
||||||
@ -1166,8 +1178,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
*/
|
*/
|
||||||
public ShortBuffer getShortBuffer(Type type) {
|
public ShortBuffer getShortBuffer(Type type) {
|
||||||
VertexBuffer vb = getBuffer(type);
|
VertexBuffer vb = getBuffer(type);
|
||||||
if (vb == null)
|
if (vb == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (ShortBuffer) vb.getData();
|
return (ShortBuffer) vb.getData();
|
||||||
}
|
}
|
||||||
@ -1179,8 +1192,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
* @return A virtual or wrapped index buffer to read the data as a list
|
* @return A virtual or wrapped index buffer to read the data as a list
|
||||||
*/
|
*/
|
||||||
public IndexBuffer getIndicesAsList(){
|
public IndexBuffer getIndicesAsList(){
|
||||||
if (mode == Mode.Hybrid)
|
if (mode == Mode.Hybrid) {
|
||||||
throw new UnsupportedOperationException("Hybrid mode not supported");
|
throw new UnsupportedOperationException("Hybrid mode not supported");
|
||||||
|
}
|
||||||
|
|
||||||
IndexBuffer ib = getIndexBuffer();
|
IndexBuffer ib = getIndexBuffer();
|
||||||
if (ib != null){
|
if (ib != null){
|
||||||
@ -1209,8 +1223,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
*/
|
*/
|
||||||
public IndexBuffer getIndexBuffer() {
|
public IndexBuffer getIndexBuffer() {
|
||||||
VertexBuffer vb = getBuffer(Type.Index);
|
VertexBuffer vb = getBuffer(Type.Index);
|
||||||
if (vb == null)
|
if (vb == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return IndexBuffer.wrapIndexBuffer(vb.getData());
|
return IndexBuffer.wrapIndexBuffer(vb.getData());
|
||||||
}
|
}
|
||||||
@ -1233,8 +1248,8 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
IndexBuffer indexBuf = getIndexBuffer();
|
IndexBuffer indexBuf = getIndexBuffer();
|
||||||
int numIndices = indexBuf.size();
|
int numIndices = indexBuf.size();
|
||||||
|
|
||||||
IntMap<Integer> oldIndicesToNewIndices = new IntMap<Integer>(numIndices);
|
IntMap<Integer> oldIndicesToNewIndices = new IntMap<>(numIndices);
|
||||||
ArrayList<Integer> newIndicesToOldIndices = new ArrayList<Integer>();
|
ArrayList<Integer> newIndicesToOldIndices = new ArrayList<>();
|
||||||
int newIndex = 0;
|
int newIndex = 0;
|
||||||
|
|
||||||
for (int i = 0; i < numIndices; i++) {
|
for (int i = 0; i < numIndices; i++) {
|
||||||
@ -1345,14 +1360,17 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
*/
|
*/
|
||||||
public void scaleTextureCoordinates(Vector2f scaleFactor){
|
public void scaleTextureCoordinates(Vector2f scaleFactor){
|
||||||
VertexBuffer tc = getBuffer(Type.TexCoord);
|
VertexBuffer tc = getBuffer(Type.TexCoord);
|
||||||
if (tc == null)
|
if (tc == null) {
|
||||||
throw new IllegalStateException("The mesh has no texture coordinates");
|
throw new IllegalStateException("The mesh has no texture coordinates");
|
||||||
|
}
|
||||||
|
|
||||||
if (tc.getFormat() != VertexBuffer.Format.Float)
|
if (tc.getFormat() != VertexBuffer.Format.Float) {
|
||||||
throw new UnsupportedOperationException("Only float texture coord format is supported");
|
throw new UnsupportedOperationException("Only float texture coord format is supported");
|
||||||
|
}
|
||||||
|
|
||||||
if (tc.getNumComponents() != 2)
|
if (tc.getNumComponents() != 2) {
|
||||||
throw new UnsupportedOperationException("Only 2D texture coords are supported");
|
throw new UnsupportedOperationException("Only 2D texture coords are supported");
|
||||||
|
}
|
||||||
|
|
||||||
FloatBuffer fb = (FloatBuffer) tc.getData();
|
FloatBuffer fb = (FloatBuffer) tc.getData();
|
||||||
fb.clear();
|
fb.clear();
|
||||||
@ -1504,6 +1522,70 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
return patchVertexCount;
|
return patchVertexCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addMorphTarget(MorphTarget target) {
|
||||||
|
if (morphTargets == null) {
|
||||||
|
morphTargets = new SafeArrayList<>(MorphTarget.class);
|
||||||
|
}
|
||||||
|
// if (numMorphBuffers == 0) {
|
||||||
|
// numMorphBuffers = target.getNumBuffers();
|
||||||
|
// int start = Type.MorphTarget0.ordinal();
|
||||||
|
// int end = start + numMorphBuffers;
|
||||||
|
// for (int i = start; i < end; i++) {
|
||||||
|
// VertexBuffer vb = new VertexBuffer(Type.values()[i]);
|
||||||
|
// setBuffer(vb);
|
||||||
|
// }
|
||||||
|
// } else if (target.getNumBuffers() != numMorphBuffers) {
|
||||||
|
// throw new IllegalArgumentException("Morph target has different number of buffers");
|
||||||
|
// }
|
||||||
|
|
||||||
|
morphTargets.add(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMorphState(float[] state) {
|
||||||
|
if (morphTargets.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (morphState == null) {
|
||||||
|
morphState = new float[morphTargets.size()];
|
||||||
|
}
|
||||||
|
System.arraycopy(state, 0, morphState, 0, morphState.length);
|
||||||
|
this.dirtyMorph = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getMorphState() {
|
||||||
|
if (morphState == null) {
|
||||||
|
morphState = new float[morphTargets.size()];
|
||||||
|
}
|
||||||
|
return morphState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveMorphTargets(int... targetsIndex) {
|
||||||
|
int start = Type.MorphTarget0.ordinal();
|
||||||
|
for (int i = 0; i < targetsIndex.length; i++) {
|
||||||
|
MorphTarget t = morphTargets.get(targetsIndex[i]);
|
||||||
|
int idx = 0;
|
||||||
|
for (Type type : t.getBuffers().keySet()) {
|
||||||
|
FloatBuffer b = t.getBuffer(type);
|
||||||
|
setBuffer(Type.values()[start + i + idx], 3, b);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MorphTarget[] getMorphTargets() {
|
||||||
|
return morphTargets.getArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMorphTargets() {
|
||||||
|
return morphTargets != null && !morphTargets.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirtyMorph() {
|
||||||
|
return dirtyMorph;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
OutputCapsule out = ex.getCapsule(this);
|
OutputCapsule out = ex.getCapsule(this);
|
||||||
|
|
||||||
@ -1550,6 +1632,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
out.write(lodLevels, "lodLevels", null);
|
out.write(lodLevels, "lodLevels", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
InputCapsule in = im.getCapsule(this);
|
InputCapsule in = im.getCapsule(this);
|
||||||
meshBound = (BoundingVolume) in.readSavable("modelBound", null);
|
meshBound = (BoundingVolume) in.readSavable("modelBound", null);
|
||||||
|
@ -212,7 +212,41 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
* Format should be {@link Format#Float} and number of components
|
* Format should be {@link Format#Float} and number of components
|
||||||
* should be 16.
|
* should be 16.
|
||||||
*/
|
*/
|
||||||
InstanceData
|
InstanceData,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Morph animations targets.
|
||||||
|
* Supports up tp 8 morph target buffers at the same time
|
||||||
|
* Limited due to the limited number of attributes you can bind to a vertex shader usually 16
|
||||||
|
* <p>
|
||||||
|
* MorphTarget buffers are either POSITION, NORMAL or TANGENT buffers.
|
||||||
|
* So we can support up to
|
||||||
|
* 10 simultaneous POSITION targets
|
||||||
|
* 5 simultaneous POSITION and NORMAL targets
|
||||||
|
* 3 simultaneous POSTION, NORMAL and TANGENT targets.
|
||||||
|
* <p>
|
||||||
|
* Note that all buffers have 3 components (Vector3f) even the Tangent buffer that
|
||||||
|
* does not contain the w (handedness) component that will not be interpolated for morph animation.
|
||||||
|
* <p>
|
||||||
|
* Note that those buffers contain the difference between the base buffer (POSITION, NORMAL or TANGENT) and the target value
|
||||||
|
* So that you can interpolate with a MADD operation in the vertex shader
|
||||||
|
* position = weight * diffPosition + basePosition;
|
||||||
|
*/
|
||||||
|
MorphTarget0,
|
||||||
|
MorphTarget1,
|
||||||
|
MorphTarget2,
|
||||||
|
MorphTarget3,
|
||||||
|
MorphTarget4,
|
||||||
|
MorphTarget5,
|
||||||
|
MorphTarget6,
|
||||||
|
MorphTarget7,
|
||||||
|
MorphTarget8,
|
||||||
|
MorphTarget9,
|
||||||
|
MorphTarget10,
|
||||||
|
MorphTarget11,
|
||||||
|
MorphTarget12,
|
||||||
|
MorphTarget13,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,7 +275,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
* Mesh data is <em>not</em> sent to GPU at all. It is only
|
* Mesh data is <em>not</em> sent to GPU at all. It is only
|
||||||
* used by the CPU.
|
* used by the CPU.
|
||||||
*/
|
*/
|
||||||
CpuOnly;
|
CpuOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -610,8 +644,9 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int elements = data.limit() / components;
|
int elements = data.limit() / components;
|
||||||
if (format == Format.Half)
|
if (format == Format.Half) {
|
||||||
elements /= 2;
|
elements /= 2;
|
||||||
|
}
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,14 +677,17 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
* argument.
|
* argument.
|
||||||
*/
|
*/
|
||||||
public void setupData(Usage usage, int components, Format format, Buffer data){
|
public void setupData(Usage usage, int components, Format format, Buffer data){
|
||||||
if (id != -1)
|
if (id != -1) {
|
||||||
throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
|
throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
|
||||||
|
}
|
||||||
|
|
||||||
if (usage == null || format == null || data == null)
|
if (usage == null || format == null || data == null) {
|
||||||
throw new IllegalArgumentException("None of the arguments can be null");
|
throw new IllegalArgumentException("None of the arguments can be null");
|
||||||
|
}
|
||||||
|
|
||||||
if (data.isReadOnly())
|
if (data.isReadOnly()) {
|
||||||
throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
|
throw new IllegalArgumentException("VertexBuffer data cannot be read-only.");
|
||||||
|
}
|
||||||
|
|
||||||
if (bufType != Type.InstanceData) {
|
if (bufType != Type.InstanceData) {
|
||||||
if (components < 1 || components > 4) {
|
if (components < 1 || components > 4) {
|
||||||
@ -720,11 +758,13 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
* Converts single floating-point data to {@link Format#Half half} floating-point data.
|
* Converts single floating-point data to {@link Format#Half half} floating-point data.
|
||||||
*/
|
*/
|
||||||
public void convertToHalf(){
|
public void convertToHalf(){
|
||||||
if (id != -1)
|
if (id != -1) {
|
||||||
throw new UnsupportedOperationException("Data has already been sent.");
|
throw new UnsupportedOperationException("Data has already been sent.");
|
||||||
|
}
|
||||||
|
|
||||||
if (format != Format.Float)
|
if (format != Format.Float) {
|
||||||
throw new IllegalStateException("Format must be float!");
|
throw new IllegalStateException("Format must be float!");
|
||||||
|
}
|
||||||
|
|
||||||
int numElements = data.limit() / components;
|
int numElements = data.limit() / components;
|
||||||
format = Format.Half;
|
format = Format.Half;
|
||||||
@ -913,8 +953,9 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
* match.
|
* match.
|
||||||
*/
|
*/
|
||||||
public void copyElements(int inIndex, VertexBuffer outVb, int outIndex, int len){
|
public void copyElements(int inIndex, VertexBuffer outVb, int outIndex, int len){
|
||||||
if (outVb.format != format || outVb.components != components)
|
if (outVb.format != format || outVb.components != components) {
|
||||||
throw new IllegalArgumentException("Buffer format mismatch. Cannot copy");
|
throw new IllegalArgumentException("Buffer format mismatch. Cannot copy");
|
||||||
|
}
|
||||||
|
|
||||||
int inPos = inIndex * components;
|
int inPos = inIndex * components;
|
||||||
int outPos = outIndex * components;
|
int outPos = outIndex * components;
|
||||||
@ -981,8 +1022,9 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
* of elements with the given number of components in each element.
|
* of elements with the given number of components in each element.
|
||||||
*/
|
*/
|
||||||
public static Buffer createBuffer(Format format, int components, int numElements){
|
public static Buffer createBuffer(Format format, int components, int numElements){
|
||||||
if (components < 1 || components > 4)
|
if (components < 1 || components > 4) {
|
||||||
throw new IllegalArgumentException("Num components must be between 1 and 4");
|
throw new IllegalArgumentException("Num components must be between 1 and 4");
|
||||||
|
}
|
||||||
|
|
||||||
int total = numElements * components;
|
int total = numElements * components;
|
||||||
|
|
||||||
|
40
jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java
Normal file
40
jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package com.jme3.scene.mesh;
|
||||||
|
|
||||||
|
import com.jme3.export.JmeExporter;
|
||||||
|
import com.jme3.export.JmeImporter;
|
||||||
|
import com.jme3.export.Savable;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
|
||||||
|
public class MorphTarget implements Savable {
|
||||||
|
private EnumMap<VertexBuffer.Type, FloatBuffer> buffers = new EnumMap<>(VertexBuffer.Type.class);
|
||||||
|
|
||||||
|
public void setBuffer(VertexBuffer.Type type, FloatBuffer buffer) {
|
||||||
|
buffers.put(type, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FloatBuffer getBuffer(VertexBuffer.Type type) {
|
||||||
|
return buffers.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumMap<VertexBuffer.Type, FloatBuffer> getBuffers() {
|
||||||
|
return buffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumBuffers() {
|
||||||
|
return buffers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter ex) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter im) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -111,6 +111,11 @@ MaterialDef Phong Lighting {
|
|||||||
Int NumberOfBones
|
Int NumberOfBones
|
||||||
Matrix4Array BoneMatrices
|
Matrix4Array BoneMatrices
|
||||||
|
|
||||||
|
// For Morph animation
|
||||||
|
FloatArray MorphWeights
|
||||||
|
Int NumberOfMorphTargets
|
||||||
|
Int NumberOfTargetsBuffers : 1
|
||||||
|
|
||||||
//For instancing
|
//For instancing
|
||||||
Boolean UseInstancing
|
Boolean UseInstancing
|
||||||
|
|
||||||
@ -152,6 +157,8 @@ MaterialDef Phong Lighting {
|
|||||||
SPHERE_MAP : EnvMapAsSphereMap
|
SPHERE_MAP : EnvMapAsSphereMap
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,6 +198,8 @@ MaterialDef Phong Lighting {
|
|||||||
SPHERE_MAP : EnvMapAsSphereMap
|
SPHERE_MAP : EnvMapAsSphereMap
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,6 +219,8 @@ MaterialDef Phong Lighting {
|
|||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -247,6 +258,8 @@ MaterialDef Phong Lighting {
|
|||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
BACKFACE_SHADOWS: BackfaceShadows
|
BACKFACE_SHADOWS: BackfaceShadows
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -274,6 +287,8 @@ MaterialDef Phong Lighting {
|
|||||||
DIFFUSEMAP_ALPHA : DiffuseMap
|
DIFFUSEMAP_ALPHA : DiffuseMap
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -296,6 +311,8 @@ MaterialDef Phong Lighting {
|
|||||||
|
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#import "Common/ShaderLib/Instancing.glsllib"
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
#import "Common/ShaderLib/Lighting.glsllib"
|
#import "Common/ShaderLib/Lighting.glsllib"
|
||||||
|
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||||
|
|
||||||
#ifdef VERTEX_LIGHTING
|
#ifdef VERTEX_LIGHTING
|
||||||
#import "Common/ShaderLib/BlinnPhongLighting.glsllib"
|
#import "Common/ShaderLib/BlinnPhongLighting.glsllib"
|
||||||
#endif
|
#endif
|
||||||
@ -90,6 +92,14 @@ void main(){
|
|||||||
vec3 modelSpaceTan = inTangent.xyz;
|
vec3 modelSpaceTan = inTangent.xyz;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef NUM_MORPH_TARGETS
|
||||||
|
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||||
|
Morph_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||||
|
#else
|
||||||
|
Morph_Compute(modelSpacePos, modelSpaceNorm);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
#ifndef VERTEX_LIGHTING
|
#ifndef VERTEX_LIGHTING
|
||||||
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||||
|
@ -111,6 +111,11 @@ MaterialDef PBR Lighting {
|
|||||||
Int NumberOfBones
|
Int NumberOfBones
|
||||||
Matrix4Array BoneMatrices
|
Matrix4Array BoneMatrices
|
||||||
|
|
||||||
|
// For Morph animation
|
||||||
|
FloatArray MorphWeights
|
||||||
|
Int NumberOfMorphTargets
|
||||||
|
Int NumberOfTargetsBuffers : 1
|
||||||
|
|
||||||
//For instancing
|
//For instancing
|
||||||
Boolean UseInstancing
|
Boolean UseInstancing
|
||||||
|
|
||||||
@ -158,6 +163,8 @@ MaterialDef PBR Lighting {
|
|||||||
NORMAL_TYPE: NormalType
|
NORMAL_TYPE: NormalType
|
||||||
VERTEX_COLOR : UseVertexColor
|
VERTEX_COLOR : UseVertexColor
|
||||||
AO_MAP: LightMapAsAOMap
|
AO_MAP: LightMapAsAOMap
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +185,8 @@ MaterialDef PBR Lighting {
|
|||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -215,6 +224,8 @@ MaterialDef PBR Lighting {
|
|||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
BACKFACE_SHADOWS: BackfaceShadows
|
BACKFACE_SHADOWS: BackfaceShadows
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -247,6 +258,8 @@ MaterialDef PBR Lighting {
|
|||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
BACKFACE_SHADOWS: BackfaceShadows
|
BACKFACE_SHADOWS: BackfaceShadows
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -272,6 +285,8 @@ MaterialDef PBR Lighting {
|
|||||||
Defines {
|
Defines {
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -291,6 +306,8 @@ MaterialDef PBR Lighting {
|
|||||||
NEED_TEXCOORD1
|
NEED_TEXCOORD1
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||||
#import "Common/ShaderLib/Instancing.glsllib"
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
|
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||||
|
|
||||||
uniform vec4 m_BaseColor;
|
uniform vec4 m_BaseColor;
|
||||||
|
|
||||||
uniform vec4 g_AmbientLightColor;
|
uniform vec4 g_AmbientLightColor;
|
||||||
varying vec2 texCoord;
|
varying vec2 texCoord;
|
||||||
|
|
||||||
@ -38,6 +37,14 @@ void main(){
|
|||||||
vec3 modelSpaceTan = inTangent.xyz;
|
vec3 modelSpaceTan = inTangent.xyz;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef NUM_MORPH_TARGETS
|
||||||
|
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||||
|
Morph_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||||
|
#else
|
||||||
|
Morph_Compute(modelSpacePos, modelSpaceNorm);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||||
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#import "Common/ShaderLib/Instancing.glsllib"
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
#import "Common/ShaderLib/Lighting.glsllib"
|
#import "Common/ShaderLib/Lighting.glsllib"
|
||||||
|
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||||
|
|
||||||
#ifdef VERTEX_LIGHTING
|
#ifdef VERTEX_LIGHTING
|
||||||
#import "Common/ShaderLib/BlinnPhongLighting.glsllib"
|
#import "Common/ShaderLib/BlinnPhongLighting.glsllib"
|
||||||
#endif
|
#endif
|
||||||
@ -84,6 +86,14 @@ void main(){
|
|||||||
vec3 modelSpaceTan = inTangent.xyz;
|
vec3 modelSpaceTan = inTangent.xyz;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef NUM_MORPH_TARGETS
|
||||||
|
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||||
|
Morph_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||||
|
#else
|
||||||
|
Morph_Compute(modelSpacePos, modelSpaceNorm);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||||
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||||
|
@ -20,6 +20,11 @@ MaterialDef Unshaded {
|
|||||||
Int NumberOfBones
|
Int NumberOfBones
|
||||||
Matrix4Array BoneMatrices
|
Matrix4Array BoneMatrices
|
||||||
|
|
||||||
|
// For Morph animation
|
||||||
|
FloatArray MorphWeights
|
||||||
|
Int NumberOfMorphTargets
|
||||||
|
Int NumberOfTargetsBuffers : 1
|
||||||
|
|
||||||
// Alpha threshold for fragment discarding
|
// Alpha threshold for fragment discarding
|
||||||
Float AlphaDiscardThreshold (AlphaTestFallOff)
|
Float AlphaDiscardThreshold (AlphaTestFallOff)
|
||||||
|
|
||||||
@ -76,6 +81,8 @@ MaterialDef Unshaded {
|
|||||||
HAS_COLOR : Color
|
HAS_COLOR : Color
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,6 +102,8 @@ MaterialDef Unshaded {
|
|||||||
Defines {
|
Defines {
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +124,8 @@ MaterialDef Unshaded {
|
|||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -152,6 +163,8 @@ MaterialDef Unshaded {
|
|||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
BACKFACE_SHADOWS: BackfaceShadows
|
BACKFACE_SHADOWS: BackfaceShadows
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -179,6 +192,8 @@ MaterialDef Unshaded {
|
|||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
HAS_POINTSIZE : PointSize
|
HAS_POINTSIZE : PointSize
|
||||||
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
#import "Common/ShaderLib/Instancing.glsllib"
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
|
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||||
|
|
||||||
attribute vec3 inPosition;
|
attribute vec3 inPosition;
|
||||||
|
|
||||||
@ -38,6 +39,11 @@ void main(){
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
||||||
|
|
||||||
|
#ifdef NUM_MORPH_TARGETS
|
||||||
|
Morph_Compute(modelSpacePos);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
Skinning_Compute(modelSpacePos);
|
Skinning_Compute(modelSpacePos);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
void main(){
|
void main(){
|
||||||
vec4 worldPos = worldMatrix * vec4(modelPosition, 1.0);
|
vec4 worldPos = worldMatrix * vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
vec3 dir = worldPos.xyz - cameraPos;
|
vec3 dir = worldPos.xyz - cameraPos;
|
||||||
float distance = dot(cameraDir, dir);
|
float distance = dot(cameraDir, dir);
|
||||||
float m11 = projectionMatrix[1][1];
|
float m11 = projectionMatrix[1][1];
|
||||||
|
@ -49,10 +49,13 @@ const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
|
|||||||
0.0, 0.0, 0.5, 0.0,
|
0.0, 0.0, 0.5, 0.0,
|
||||||
0.5, 0.5, 0.5, 1.0);
|
0.5, 0.5, 0.5, 1.0);
|
||||||
|
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
||||||
|
|
||||||
|
#ifdef NUM_MORPH_TARGETS
|
||||||
|
Morph_Compute(modelSpacePos);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
Skinning_Compute(modelSpacePos);
|
Skinning_Compute(modelSpacePos);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||||
#import "Common/ShaderLib/Instancing.glsllib"
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
|
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||||
|
|
||||||
attribute vec3 inPosition;
|
attribute vec3 inPosition;
|
||||||
attribute vec2 inTexCoord;
|
attribute vec2 inTexCoord;
|
||||||
|
|
||||||
@ -9,6 +11,10 @@ varying vec2 texCoord;
|
|||||||
void main(){
|
void main(){
|
||||||
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
||||||
|
|
||||||
|
#ifdef NUM_MORPH_TARGETS
|
||||||
|
Morph_Compute(modelSpacePos, modelSpaceNorm);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
Skinning_Compute(modelSpacePos);
|
Skinning_Compute(modelSpacePos);
|
||||||
#endif
|
#endif
|
||||||
|
212
jme3-core/src/main/resources/Common/ShaderLib/MorphAnim.glsllib
Normal file
212
jme3-core/src/main/resources/Common/ShaderLib/MorphAnim.glsllib
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/**
|
||||||
|
A glsllib that perform morph animation.
|
||||||
|
Note that it only handles morphing position, normals and tangents.
|
||||||
|
*/
|
||||||
|
#ifdef NUM_MORPH_TARGETS
|
||||||
|
vec3 dummy_norm = vec3(0.0);
|
||||||
|
vec3 dummy_tan = vec3(0.0);
|
||||||
|
#define NUM_BUFFERS NUM_MORPH_TARGETS * NUM_TARGETS_BUFFERS
|
||||||
|
#if (NUM_BUFFERS > 0)
|
||||||
|
uniform float m_MorphWeights[NUM_MORPH_TARGETS];
|
||||||
|
attribute vec3 inMorphTarget0;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 1)
|
||||||
|
attribute vec3 inMorphTarget1;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 2)
|
||||||
|
attribute vec3 inMorphTarget2;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 3)
|
||||||
|
attribute vec3 inMorphTarget3;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 4)
|
||||||
|
attribute vec3 inMorphTarget4;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 5)
|
||||||
|
attribute vec3 inMorphTarget5;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 6)
|
||||||
|
attribute vec3 inMorphTarget6;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 7)
|
||||||
|
attribute vec3 inMorphTarget7;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 8)
|
||||||
|
attribute vec3 inMorphTarget8;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 9)
|
||||||
|
attribute vec3 inMorphTarget9;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 10)
|
||||||
|
attribute vec3 inMorphTarget10;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 11)
|
||||||
|
attribute vec3 inMorphTarget11;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 12)
|
||||||
|
attribute vec3 inMorphTarget12;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 13)
|
||||||
|
attribute vec3 inMorphTarget13;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Morph_Compute_Pos(inout vec4 pos){
|
||||||
|
#if (NUM_TARGETS_BUFFERS == 1)
|
||||||
|
#if (NUM_MORPH_TARGETS > 0)
|
||||||
|
pos.xyz += m_MorphWeights[0] * inMorphTarget0;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 1)
|
||||||
|
pos.xyz += m_MorphWeights[1] * inMorphTarget1;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 2)
|
||||||
|
pos.xyz += m_MorphWeights[2] * inMorphTarget2;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 3)
|
||||||
|
pos.xyz += m_MorphWeights[3] * inMorphTarget3;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 4)
|
||||||
|
pos.xyz += m_MorphWeights[4] * inMorphTarget4;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 5)
|
||||||
|
pos.xyz += m_MorphWeights[5] * inMorphTarget5;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 6)
|
||||||
|
pos.xyz += m_MorphWeights[6] * inMorphTarget6;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 7)
|
||||||
|
pos.xyz += m_MorphWeights[7] * inMorphTarget7;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 8)
|
||||||
|
pos.xyz += m_MorphWeights[8] * inMorphTarget8;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 9)
|
||||||
|
pos.xyz += m_MorphWeights[9] * inMorphTarget9;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 10)
|
||||||
|
pos.xyz += m_MorphWeights[10] * inMorphTarget10;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 11)
|
||||||
|
pos.xyz += m_MorphWeights[11] * inMorphTarget11;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 12)
|
||||||
|
pos.xyz += m_MorphWeights[12] * inMorphTarget12;
|
||||||
|
#endif
|
||||||
|
#if (NUM_MORPH_TARGETS > 13)
|
||||||
|
pos.xyz += m_MorphWeights[13] * inMorphTarget13;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
float Get_Inverse_Weights_Sum(){
|
||||||
|
float sum = 0;
|
||||||
|
for( int i = 0;i < NUM_MORPH_TARGETS; i++){
|
||||||
|
sum += m_MorphWeights[i];
|
||||||
|
}
|
||||||
|
return 1.0 / max(1.0, sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Morph_Compute_Pos_Norm(inout vec4 pos, inout vec3 norm){
|
||||||
|
#if (NUM_TARGETS_BUFFERS == 2)
|
||||||
|
// weight sum may be over 1.0. It's totallyvalid for position
|
||||||
|
// but for normals. the weights needs to be normalized.
|
||||||
|
float invWeightsSum = Get_Inverse_Weights_Sum();
|
||||||
|
#if (NUM_BUFFERS > 1)
|
||||||
|
pos.xyz += m_MorphWeights[0] * inMorphTarget0;
|
||||||
|
norm += m_MorphWeights[0] * invWeightsSum * inMorphTarget1;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 3)
|
||||||
|
pos.xyz += m_MorphWeights[1] * inMorphTarget2;
|
||||||
|
norm.xyz += m_MorphWeights[1] * invWeightsSum * inMorphTarget3;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 5)
|
||||||
|
pos.xyz += m_MorphWeights[2] * inMorphTarget4;
|
||||||
|
norm += m_MorphWeights[2] * invWeightsSum * inMorphTarget5;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 7)
|
||||||
|
pos.xyz += m_MorphWeights[3] * inMorphTarget6;
|
||||||
|
norm += m_MorphWeights[3] * invWeightsSum * inMorphTarget7;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 9)
|
||||||
|
pos.xyz += m_MorphWeights[4] * inMorphTarget8;
|
||||||
|
norm += m_MorphWeights[4] * invWeightsSum * inMorphTarget9;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 11)
|
||||||
|
pos.xyz += m_MorphWeights[5] * inMorphTarget10;
|
||||||
|
norm += m_MorphWeights[5] * invWeightsSum * inMorphTarget11;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 13)
|
||||||
|
pos.xyz += m_MorphWeights[6] * inMorphTarget12;
|
||||||
|
norm += m_MorphWeights[6] * invWeightsSum * inMorphTarget13;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Morph_Compute_Pos_Norm_Tan(inout vec4 pos, inout vec3 norm, inout vec3 tan){
|
||||||
|
#if (NUM_TARGETS_BUFFERS == 3)
|
||||||
|
// weight sum may be over 1.0. It's totallyvalid for position
|
||||||
|
// but for normals. the weights needs to be normalized.
|
||||||
|
float invWeightsSum = Get_Inverse_Weights_Sum();
|
||||||
|
#if (NUM_BUFFERS > 2)
|
||||||
|
float normWeight = m_MorphWeights[0] * invWeightsSum;
|
||||||
|
pos.xyz += m_MorphWeights[0] * inMorphTarget0;
|
||||||
|
norm += normWeight * inMorphTarget1;
|
||||||
|
tan += normWeight * inMorphTarget2;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 5)
|
||||||
|
float normWeight = m_MorphWeights[1] * invWeightsSum;
|
||||||
|
pos.xyz += m_MorphWeights[1] * inMorphTarget3;
|
||||||
|
norm += normWeight * inMorphTarget4;
|
||||||
|
tan += normWeight * inMorphTarget5;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 8)
|
||||||
|
float normWeight = m_MorphWeights[2] * invWeightsSum;
|
||||||
|
pos.xyz += m_MorphWeights[2] * inMorphTarget6;
|
||||||
|
norm += normWeight * inMorphTarget7;
|
||||||
|
tan += normWeight * inMorphTarget8;
|
||||||
|
#endif
|
||||||
|
#if (NUM_BUFFERS > 11)
|
||||||
|
float normWeight = m_MorphWeights[3] * invWeightsSum;
|
||||||
|
pos.xyz += m_MorphWeights[3] * inMorphTarget9;
|
||||||
|
norm += normWeight * inMorphTarget10;
|
||||||
|
tan += normWeight * inMorphTarget11;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Morph_Compute(inout vec4 pos){
|
||||||
|
#if (NUM_TARGETS_BUFFERS == 2)
|
||||||
|
Morph_Compute_Pos_Norm(pos,dummy_norm);
|
||||||
|
return;
|
||||||
|
#elif (NUM_TARGETS_BUFFERS == 3)
|
||||||
|
Morph_Compute_Pos_Norm_Tan(pos, dummy_norm, dummy_tan);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
Morph_Compute_Pos(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Morph_Compute(inout vec4 pos, inout vec3 norm){
|
||||||
|
#if (NUM_TARGETS_BUFFERS == 1)
|
||||||
|
Morph_Compute_Pos(pos);
|
||||||
|
return;
|
||||||
|
#elif (NUM_TARGETS_BUFFERS == 3)
|
||||||
|
Morph_Compute_Pos_Norm_Tan(pos, dummy_norm, dummy_tan);
|
||||||
|
return;
|
||||||
|
#elif (NUM_TARGETS_BUFFERS == 2)
|
||||||
|
Morph_Compute_Pos_Norm(pos, norm);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Morph_Compute(inout vec4 pos, inout vec3 norm, inout vec3 tan){
|
||||||
|
#if (NUM_TARGETS_BUFFERS == 1)
|
||||||
|
Morph_Compute_Pos(pos);
|
||||||
|
return;
|
||||||
|
#elif (NUM_TARGETS_BUFFERS == 2)
|
||||||
|
Morph_Compute_Pos_Norm(pos, norm);
|
||||||
|
tan = normalize(tan - dot(tan, norm) * norm);
|
||||||
|
return;
|
||||||
|
#elif (NUM_TARGETS_BUFFERS == 3)
|
||||||
|
Morph_Compute_Pos_Norm_Tan(pos, norm, tan);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -41,11 +41,12 @@ import com.jme3.input.controls.ActionListener;
|
|||||||
import com.jme3.input.controls.KeyTrigger;
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
import com.jme3.math.*;
|
import com.jme3.math.*;
|
||||||
import com.jme3.renderer.Limits;
|
import com.jme3.renderer.Limits;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.*;
|
||||||
import com.jme3.scene.Spatial;
|
|
||||||
import com.jme3.scene.control.Control;
|
import com.jme3.scene.control.Control;
|
||||||
import com.jme3.scene.debug.custom.ArmatureDebugAppState;
|
import com.jme3.scene.debug.custom.ArmatureDebugAppState;
|
||||||
import com.jme3.scene.plugins.gltf.GltfModelKey;
|
import com.jme3.scene.plugins.gltf.GltfModelKey;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
|
import jme3test.model.anim.EraseTimer;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -58,9 +59,13 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
int assetIndex = 0;
|
int assetIndex = 0;
|
||||||
boolean useAutoRotate = false;
|
boolean useAutoRotate = false;
|
||||||
private final static String indentString = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
|
private final static String indentString = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
|
||||||
int duration = 2;
|
int duration = 1;
|
||||||
boolean playAnim = true;
|
boolean playAnim = true;
|
||||||
|
|
||||||
|
Geometry g;
|
||||||
|
int morphIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
TestGltfLoading app = new TestGltfLoading();
|
TestGltfLoading app = new TestGltfLoading();
|
||||||
app.start();
|
app.start();
|
||||||
@ -73,10 +78,12 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
https://sketchfab.com/features/gltf
|
https://sketchfab.com/features/gltf
|
||||||
You have to copy them in Model/gltf folder in the test-data project.
|
You have to copy them in Model/gltf folder in the test-data project.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
|
|
||||||
ArmatureDebugAppState armatureDebugappState = new ArmatureDebugAppState();
|
ArmatureDebugAppState armatureDebugappState = new ArmatureDebugAppState();
|
||||||
getStateManager().attach(armatureDebugappState);
|
getStateManager().attach(armatureDebugappState);
|
||||||
|
setTimer(new EraseTimer());
|
||||||
|
|
||||||
String folder = System.getProperty("user.home");
|
String folder = System.getProperty("user.home");
|
||||||
assetManager.registerLocator(folder, FileLocator.class);
|
assetManager.registerLocator(folder, FileLocator.class);
|
||||||
@ -110,7 +117,14 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
// PointLight pl1 = new PointLight(new Vector3f(-5.0f, -5.0f, -5.0f), ColorRGBA.White.mult(0.5f), 50);
|
// PointLight pl1 = new PointLight(new Vector3f(-5.0f, -5.0f, -5.0f), ColorRGBA.White.mult(0.5f), 50);
|
||||||
// rootNode.addLight(pl1);
|
// rootNode.addLight(pl1);
|
||||||
|
|
||||||
loadModel("Models/gltf/polly/project_polly.gltf", new Vector3f(0, 0, 0), 0.5f);
|
//loadModel("Models/gltf/polly/project_polly.gltf", new Vector3f(0, 0, 0), 0.5f);
|
||||||
|
//loadModel("Models/gltf/zophrac/scene.gltf", new Vector3f(0, 0, 0), 0.1f);
|
||||||
|
loadModel("Models/gltf/scifigirl/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
|
||||||
|
//loadModel("Models/gltf/man/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
|
||||||
|
//loadModel("Models/gltf/torus/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
|
||||||
|
//loadModel("Models/gltf/morph/scene.gltf", new Vector3f(0, 0, 0), 0.2f);
|
||||||
|
//loadModel("Models/gltf/morphCube/AnimatedMorphCube.gltf", new Vector3f(0, 0, 0), 1f);
|
||||||
|
// loadModel("Models/gltf/morph/SimpleMorph.gltf", new Vector3f(0, 0, 0), 0.1f);
|
||||||
//loadModel("Models/gltf/nier/scene.gltf", new Vector3f(0, -1.5f, 0), 0.01f);
|
//loadModel("Models/gltf/nier/scene.gltf", new Vector3f(0, -1.5f, 0), 0.01f);
|
||||||
//loadModel("Models/gltf/izzy/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
|
//loadModel("Models/gltf/izzy/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
|
||||||
//loadModel("Models/gltf/darth/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
|
//loadModel("Models/gltf/darth/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
|
||||||
@ -149,6 +163,8 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
|
|
||||||
probeNode.attachChild(assets.get(0));
|
probeNode.attachChild(assets.get(0));
|
||||||
|
|
||||||
|
// setMorphTarget(morphIndex);
|
||||||
|
|
||||||
ChaseCameraAppState chaseCam = new ChaseCameraAppState();
|
ChaseCameraAppState chaseCam = new ChaseCameraAppState();
|
||||||
chaseCam.setTarget(probeNode);
|
chaseCam.setTarget(probeNode);
|
||||||
getStateManager().attach(chaseCam);
|
getStateManager().attach(chaseCam);
|
||||||
@ -200,6 +216,15 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
dumpScene(rootNode, 0);
|
dumpScene(rootNode, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMorphTarget(int index) {
|
||||||
|
g = (Geometry) probeNode.getChild("0");
|
||||||
|
g.getMesh().setActiveMorphTargets(index);
|
||||||
|
g.getMaterial().setInt("NumberOfMorphTargets", 1);
|
||||||
|
g.getMaterial().setInt("NumberOfTargetsBuffers", 3);
|
||||||
|
float[] weights = {1.0f};
|
||||||
|
g.getMaterial().setParam("MorphWeights", VarType.FloatArray, weights);
|
||||||
|
}
|
||||||
|
|
||||||
private <T extends Control> T findControl(Spatial s, Class<T> controlClass) {
|
private <T extends Control> T findControl(Spatial s, Class<T> controlClass) {
|
||||||
T ctrl = s.getControl(controlClass);
|
T ctrl = s.getControl(controlClass);
|
||||||
if (ctrl != null) {
|
if (ctrl != null) {
|
||||||
@ -291,18 +316,19 @@ public class TestGltfLoading extends SimpleApplication {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void simpleUpdate(float tpf) {
|
public void simpleUpdate(float tpf) {
|
||||||
|
|
||||||
if (!useAutoRotate) {
|
if (!useAutoRotate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
time += tpf;
|
time += tpf;
|
||||||
autoRotate.rotate(0, tpf * 0.5f, 0);
|
// autoRotate.rotate(0, tpf * 0.5f, 0);
|
||||||
if (time > duration) {
|
if (time > duration) {
|
||||||
|
// morphIndex++;
|
||||||
|
// setMorphTarget(morphIndex);
|
||||||
assets.get(assetIndex).removeFromParent();
|
assets.get(assetIndex).removeFromParent();
|
||||||
assetIndex = (assetIndex + 1) % assets.size();
|
assetIndex = (assetIndex + 1) % assets.size();
|
||||||
if (assetIndex == 0) {
|
// if (assetIndex == 0) {
|
||||||
duration = 10;
|
// duration = 10;
|
||||||
}
|
// }
|
||||||
probeNode.attachChild(assets.get(assetIndex));
|
probeNode.attachChild(assets.get(assetIndex));
|
||||||
time = 0;
|
time = 0;
|
||||||
}
|
}
|
||||||
|
@ -52,9 +52,9 @@ public class TestHWSkinning extends SimpleApplication implements ActionListener{
|
|||||||
|
|
||||||
// private AnimComposer composer;
|
// private AnimComposer composer;
|
||||||
private String[] animNames = {"Dodge", "Walk", "pull", "push"};
|
private String[] animNames = {"Dodge", "Walk", "pull", "push"};
|
||||||
private final static int SIZE = 60;
|
private final static int SIZE = 40;
|
||||||
private boolean hwSkinningEnable = true;
|
private boolean hwSkinningEnable = true;
|
||||||
private List<SkinningControl> skControls = new ArrayList<SkinningControl>();
|
private List<SkinningControl> skControls = new ArrayList<>();
|
||||||
private BitmapText hwsText;
|
private BitmapText hwsText;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
121
jme3-examples/src/main/java/jme3test/model/anim/TestMorph.java
Normal file
121
jme3-examples/src/main/java/jme3test/model/anim/TestMorph.java
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package jme3test.model.anim;
|
||||||
|
|
||||||
|
import com.jme3.app.ChaseCameraAppState;
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.input.KeyInput;
|
||||||
|
import com.jme3.input.controls.ActionListener;
|
||||||
|
import com.jme3.input.controls.AnalogListener;
|
||||||
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.scene.mesh.MorphTarget;
|
||||||
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
|
import com.jme3.util.BufferUtils;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
public class TestMorph extends SimpleApplication {
|
||||||
|
|
||||||
|
float[] weights = new float[2];
|
||||||
|
|
||||||
|
public static void main(String... args) {
|
||||||
|
TestMorph app = new TestMorph();
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
final Box box = new Box(1, 1, 1);
|
||||||
|
FloatBuffer buffer = BufferUtils.createVector3Buffer(box.getVertexCount());
|
||||||
|
|
||||||
|
float[] d = new float[box.getVertexCount() * 3];
|
||||||
|
for (int i = 0; i < d.length; i++) {
|
||||||
|
d[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
d[12] = 1f;
|
||||||
|
d[15] = 1f;
|
||||||
|
d[18] = 1f;
|
||||||
|
d[21] = 1f;
|
||||||
|
|
||||||
|
buffer.put(d);
|
||||||
|
buffer.rewind();
|
||||||
|
|
||||||
|
MorphTarget target = new MorphTarget();
|
||||||
|
target.setBuffer(VertexBuffer.Type.Position, buffer);
|
||||||
|
box.addMorphTarget(target);
|
||||||
|
|
||||||
|
|
||||||
|
buffer = BufferUtils.createVector3Buffer(box.getVertexCount());
|
||||||
|
|
||||||
|
for (int i = 0; i < d.length; i++) {
|
||||||
|
d[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
d[13] = 1f;
|
||||||
|
d[16] = 1f;
|
||||||
|
d[19] = 1f;
|
||||||
|
d[22] = 1f;
|
||||||
|
|
||||||
|
buffer.put(d);
|
||||||
|
buffer.rewind();
|
||||||
|
|
||||||
|
final MorphTarget target2 = new MorphTarget();
|
||||||
|
target2.setBuffer(VertexBuffer.Type.Position, buffer);
|
||||||
|
box.addMorphTarget(target2);
|
||||||
|
|
||||||
|
Geometry g = new Geometry("box", box);
|
||||||
|
final Material m = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
g.setMaterial(m);
|
||||||
|
m.setColor("Color", ColorRGBA.Red);
|
||||||
|
m.setInt("NumberOfMorphTargets", 2);
|
||||||
|
|
||||||
|
rootNode.attachChild(g);
|
||||||
|
|
||||||
|
box.setActiveMorphTargets(0,1);
|
||||||
|
m.setParam("MorphWeights", VarType.FloatArray, weights);
|
||||||
|
|
||||||
|
ChaseCameraAppState chase = new ChaseCameraAppState();
|
||||||
|
chase.setTarget(rootNode);
|
||||||
|
getStateManager().attach(chase);
|
||||||
|
flyCam.setEnabled(false);
|
||||||
|
|
||||||
|
inputManager.addMapping("morphright", new KeyTrigger(KeyInput.KEY_I));
|
||||||
|
inputManager.addMapping("morphleft", new KeyTrigger(KeyInput.KEY_Y));
|
||||||
|
inputManager.addMapping("morphup", new KeyTrigger(KeyInput.KEY_U));
|
||||||
|
inputManager.addMapping("morphdown", new KeyTrigger(KeyInput.KEY_J));
|
||||||
|
inputManager.addMapping("change", new KeyTrigger(KeyInput.KEY_SPACE));
|
||||||
|
inputManager.addListener(new AnalogListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnalog(String name, float value, float tpf) {
|
||||||
|
if (name.equals("morphleft")) {
|
||||||
|
weights[0] -= tpf;
|
||||||
|
}
|
||||||
|
if (name.equals("morphright")) {
|
||||||
|
weights[0] += tpf;
|
||||||
|
}
|
||||||
|
if (name.equals("morphup")) {
|
||||||
|
weights[1] += tpf;
|
||||||
|
}
|
||||||
|
if (name.equals("morphdown")) {
|
||||||
|
weights[1] -= tpf;
|
||||||
|
}
|
||||||
|
m.setParam("MorphWeights", VarType.FloatArray, weights);
|
||||||
|
|
||||||
|
}
|
||||||
|
}, "morphup", "morphdown", "morphleft", "morphright");
|
||||||
|
|
||||||
|
inputManager.addListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void onAction(String name, boolean isPressed, float tpf) {
|
||||||
|
if (name.equals("change") && isPressed) {
|
||||||
|
box.setBuffer(VertexBuffer.Type.MorphTarget0, 3, target2.getBuffer(VertexBuffer.Type.Position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "change");
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import com.jme3.renderer.Camera;
|
|||||||
import com.jme3.renderer.queue.RenderQueue;
|
import com.jme3.renderer.queue.RenderQueue;
|
||||||
import com.jme3.scene.*;
|
import com.jme3.scene.*;
|
||||||
import com.jme3.scene.control.CameraControl;
|
import com.jme3.scene.control.CameraControl;
|
||||||
|
import com.jme3.scene.mesh.MorphTarget;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.Texture2D;
|
import com.jme3.texture.Texture2D;
|
||||||
import com.jme3.util.IntMap;
|
import com.jme3.util.IntMap;
|
||||||
@ -19,6 +20,8 @@ import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
|
|||||||
import javax.xml.bind.DatatypeConverter;
|
import javax.xml.bind.DatatypeConverter;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.rmi.ServerError;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -128,8 +131,6 @@ public class GltfLoader implements AssetLoader {
|
|||||||
|
|
||||||
rootNode = customContentManager.readExtensionAndExtras("root", docRoot, rootNode);
|
rootNode = customContentManager.readExtensionAndExtras("root", docRoot, rootNode);
|
||||||
|
|
||||||
setupControls();
|
|
||||||
|
|
||||||
//Loading animations
|
//Loading animations
|
||||||
if (animations != null) {
|
if (animations != null) {
|
||||||
for (int i = 0; i < animations.size(); i++) {
|
for (int i = 0; i < animations.size(); i++) {
|
||||||
@ -137,6 +138,8 @@ public class GltfLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupControls();
|
||||||
|
|
||||||
//only one scene let's not return the root.
|
//only one scene let's not return the root.
|
||||||
if (rootNode.getChildren().size() == 1) {
|
if (rootNode.getChildren().size() == 1) {
|
||||||
rootNode = (Node) rootNode.getChild(0);
|
rootNode = (Node) rootNode.getChild(0);
|
||||||
@ -389,7 +392,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
//the buffers will be setup if ever used.
|
//the buffers will be setup if ever used.
|
||||||
VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight);
|
VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight);
|
||||||
VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex);
|
VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex);
|
||||||
//setting usage to cpuOnly so that the buffer is not send empty to the GPU
|
//setting usage to cpuOnly so that the buffer is not sent empty to the GPU
|
||||||
indicesHW.setUsage(VertexBuffer.Usage.CpuOnly);
|
indicesHW.setUsage(VertexBuffer.Usage.CpuOnly);
|
||||||
weightsHW.setUsage(VertexBuffer.Usage.CpuOnly);
|
weightsHW.setUsage(VertexBuffer.Usage.CpuOnly);
|
||||||
mesh.setBuffer(weightsHW);
|
mesh.setBuffer(weightsHW);
|
||||||
@ -397,6 +400,22 @@ public class GltfLoader implements AssetLoader {
|
|||||||
mesh.generateBindPose();
|
mesh.generateBindPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonArray targets = meshObject.getAsJsonArray("targets");
|
||||||
|
if(targets != null){
|
||||||
|
for (JsonElement target : targets) {
|
||||||
|
MorphTarget morphTarget = new MorphTarget();
|
||||||
|
for (Map.Entry<String, JsonElement> entry : target.getAsJsonObject().entrySet()) {
|
||||||
|
String bufferType = entry.getKey();
|
||||||
|
VertexBuffer.Type type = getVertexBufferType(bufferType);
|
||||||
|
VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(type));
|
||||||
|
if (vb != null) {
|
||||||
|
morphTarget.setBuffer(type, (FloatBuffer)vb.getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mesh.addMorphTarget(morphTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mesh = customContentManager.readExtensionAndExtras("primitive", meshObject, mesh);
|
mesh = customContentManager.readExtensionAndExtras("primitive", meshObject, mesh);
|
||||||
|
|
||||||
Geometry geom = new Geometry(null, mesh);
|
Geometry geom = new Geometry(null, mesh);
|
||||||
@ -425,7 +444,6 @@ public class GltfLoader implements AssetLoader {
|
|||||||
geomArray[index] = geom;
|
geomArray[index] = geom;
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
//TODO targets(morph anim...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
geomArray = customContentManager.readExtensionAndExtras("mesh", meshData, geomArray);
|
geomArray = customContentManager.readExtensionAndExtras("mesh", meshData, geomArray);
|
||||||
@ -721,6 +739,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
|
|
||||||
//temp data storage of track data
|
//temp data storage of track data
|
||||||
TrackData[] tracks = new TrackData[nodes.size()];
|
TrackData[] tracks = new TrackData[nodes.size()];
|
||||||
|
boolean hasMorphTrack = false;
|
||||||
|
|
||||||
for (JsonElement channel : channels) {
|
for (JsonElement channel : channels) {
|
||||||
|
|
||||||
@ -733,12 +752,12 @@ public class GltfLoader implements AssetLoader {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
assertNotNull(targetPath, "No target path for channel");
|
assertNotNull(targetPath, "No target path for channel");
|
||||||
|
//
|
||||||
if (targetPath.equals("weight")) {
|
// if (targetPath.equals("weights")) {
|
||||||
//Morph animation, not implemented in JME, let's warn the user and skip the channel
|
// //Morph animation, not implemented in JME, let's warn the user and skip the channel
|
||||||
logger.log(Level.WARNING, "Morph animation is not supported by JME yet, skipping animation");
|
// logger.log(Level.WARNING, "Morph animation is not supported by JME yet, skipping animation track");
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
|
||||||
TrackData trackData = tracks[targetNode];
|
TrackData trackData = tracks[targetNode];
|
||||||
if (trackData == null) {
|
if (trackData == null) {
|
||||||
@ -782,9 +801,15 @@ public class GltfLoader implements AssetLoader {
|
|||||||
Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator);
|
Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator);
|
||||||
trackData.rotations = rotations;
|
trackData.rotations = rotations;
|
||||||
} else {
|
} else {
|
||||||
//TODO support weights
|
trackData.timeArrays.add(new TrackData.TimeData(times, TrackData.Type.Morph));
|
||||||
logger.log(Level.WARNING, "Morph animation is not supported");
|
float[] weights = readAccessorData(dataIndex, floatArrayPopulator);
|
||||||
continue;
|
Geometry g = fetchFromCache("nodes", targetNode, Geometry.class);
|
||||||
|
int expectedSize = g.getMesh().getMorphTargets().length * times.length;
|
||||||
|
if( expectedSize != weights.length ){
|
||||||
|
throw new AssetLoadException("Morph animation should contain " + expectedSize + " entries, got" + weights.length);
|
||||||
|
}
|
||||||
|
trackData.weights = weights;
|
||||||
|
hasMorphTrack = true;
|
||||||
}
|
}
|
||||||
tracks[targetNode] = customContentManager.readExtensionAndExtras("channel", channel, trackData);
|
tracks[targetNode] = customContentManager.readExtensionAndExtras("channel", channel, trackData);
|
||||||
}
|
}
|
||||||
@ -795,7 +820,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
|
|
||||||
List<Spatial> spatials = new ArrayList<>();
|
List<Spatial> spatials = new ArrayList<>();
|
||||||
AnimClip anim = new AnimClip(name);
|
AnimClip anim = new AnimClip(name);
|
||||||
List<TransformTrack> ttracks = new ArrayList<>();
|
List<AnimTrack> aTracks = new ArrayList<>();
|
||||||
int skinIndex = -1;
|
int skinIndex = -1;
|
||||||
|
|
||||||
List<Joint> usedJoints = new ArrayList<>();
|
List<Joint> usedJoints = new ArrayList<>();
|
||||||
@ -809,8 +834,22 @@ public class GltfLoader implements AssetLoader {
|
|||||||
if (node instanceof Spatial) {
|
if (node instanceof Spatial) {
|
||||||
Spatial s = (Spatial) node;
|
Spatial s = (Spatial) node;
|
||||||
spatials.add(s);
|
spatials.add(s);
|
||||||
|
if (trackData.rotations != null || trackData.translations != null || trackData.scales != null) {
|
||||||
TransformTrack track = new TransformTrack(s, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
|
TransformTrack track = new TransformTrack(s, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
|
||||||
ttracks.add(track);
|
aTracks.add(track);
|
||||||
|
}
|
||||||
|
if( trackData.weights != null && s instanceof Geometry){
|
||||||
|
Geometry g = (Geometry)s;
|
||||||
|
int nbMorph = g.getMesh().getMorphTargets().length;
|
||||||
|
// for (int k = 0; k < trackData.weights.length; k++) {
|
||||||
|
// System.err.print(trackData.weights[k] + ",");
|
||||||
|
// if(k % nbMorph == 0 && k!=0){
|
||||||
|
// System.err.println(" ");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
MorphTrack track = new MorphTrack(g, trackData.times, trackData.weights, nbMorph);
|
||||||
|
aTracks.add(track);
|
||||||
|
}
|
||||||
} else if (node instanceof JointWrapper) {
|
} else if (node instanceof JointWrapper) {
|
||||||
JointWrapper jw = (JointWrapper) node;
|
JointWrapper jw = (JointWrapper) node;
|
||||||
usedJoints.add(jw.joint);
|
usedJoints.add(jw.joint);
|
||||||
@ -826,8 +865,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransformTrack track = new TransformTrack(jw.joint, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
|
TransformTrack track = new TransformTrack(jw.joint, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
|
||||||
ttracks.add(track);
|
aTracks.add(track);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -845,12 +883,12 @@ public class GltfLoader implements AssetLoader {
|
|||||||
Quaternion[] rotations = new Quaternion[]{joint.getLocalRotation()};
|
Quaternion[] rotations = new Quaternion[]{joint.getLocalRotation()};
|
||||||
Vector3f[] scales = new Vector3f[]{joint.getLocalScale()};
|
Vector3f[] scales = new Vector3f[]{joint.getLocalScale()};
|
||||||
TransformTrack track = new TransformTrack(joint, times, translations, rotations, scales);
|
TransformTrack track = new TransformTrack(joint, times, translations, rotations, scales);
|
||||||
ttracks.add(track);
|
aTracks.add(track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anim.setTracks(ttracks.toArray(new TransformTrack[ttracks.size()]));
|
anim.setTracks(aTracks.toArray(new AnimTrack[aTracks.size()]));
|
||||||
|
|
||||||
anim = customContentManager.readExtensionAndExtras("animations", animation, anim);
|
anim = customContentManager.readExtensionAndExtras("animations", animation, anim);
|
||||||
|
|
||||||
@ -862,11 +900,14 @@ public class GltfLoader implements AssetLoader {
|
|||||||
|
|
||||||
if (!spatials.isEmpty()) {
|
if (!spatials.isEmpty()) {
|
||||||
if (skinIndex != -1) {
|
if (skinIndex != -1) {
|
||||||
//there are some spatial tracks in this bone animation... or the other way around. Let's add the spatials in the skinnedSpatials.
|
//there are some spatial or moph tracks in this bone animation... or the other way around. Let's add the spatials in the skinnedSpatials.
|
||||||
SkinData skin = fetchFromCache("skins", skinIndex, SkinData.class);
|
SkinData skin = fetchFromCache("skins", skinIndex, SkinData.class);
|
||||||
List<Spatial> spat = skinnedSpatials.get(skin);
|
List<Spatial> spat = skinnedSpatials.get(skin);
|
||||||
spat.addAll(spatials);
|
spat.addAll(spatials);
|
||||||
//the animControl will be added in the setupControls();
|
//the animControl will be added in the setupControls();
|
||||||
|
if (hasMorphTrack && skin.morphControl == null) {
|
||||||
|
skin.morphControl = new MorphControl();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//Spatial animation
|
//Spatial animation
|
||||||
Spatial spatial = null;
|
Spatial spatial = null;
|
||||||
@ -882,6 +923,9 @@ public class GltfLoader implements AssetLoader {
|
|||||||
spatial.addControl(composer);
|
spatial.addControl(composer);
|
||||||
}
|
}
|
||||||
composer.addAnimClip(anim);
|
composer.addAnimClip(anim);
|
||||||
|
if (hasMorphTrack && spatial.getControl(MorphControl.class) == null) {
|
||||||
|
spatial.addControl(new MorphControl());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1049,6 +1093,9 @@ public class GltfLoader implements AssetLoader {
|
|||||||
spatial.addControl(skinData.animComposer);
|
spatial.addControl(skinData.animComposer);
|
||||||
}
|
}
|
||||||
spatial.addControl(skinData.skinningControl);
|
spatial.addControl(skinData.skinningControl);
|
||||||
|
if (skinData.morphControl != null) {
|
||||||
|
spatial.addControl(skinData.morphControl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
@ -1131,7 +1178,9 @@ public class GltfLoader implements AssetLoader {
|
|||||||
|
|
||||||
private class SkinData {
|
private class SkinData {
|
||||||
SkinningControl skinningControl;
|
SkinningControl skinningControl;
|
||||||
|
MorphControl morphControl;
|
||||||
AnimComposer animComposer;
|
AnimComposer animComposer;
|
||||||
|
Spatial spatial;
|
||||||
Spatial parent;
|
Spatial parent;
|
||||||
Transform rootBoneTransformOffset;
|
Transform rootBoneTransformOffset;
|
||||||
Joint[] joints;
|
Joint[] joints;
|
||||||
@ -1221,6 +1270,27 @@ public class GltfLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
// private class FloaGridPopulator implements Populator<float[]> {
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public float[][] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException {
|
||||||
|
//
|
||||||
|
// int numComponents = getNumberOfComponents(type);
|
||||||
|
// int dataSize = numComponents * count;
|
||||||
|
// float[] data = new float[dataSize];
|
||||||
|
//
|
||||||
|
// if (bufferViewIndex == null) {
|
||||||
|
// //no referenced buffer, specs says to pad the data with zeros.
|
||||||
|
// padBuffer(data, dataSize);
|
||||||
|
// } else {
|
||||||
|
// readBuffer(bufferViewIndex, byteOffset, count, data, numComponents, getVertexBufferFormat(componentType));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return data;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
private class Vector3fArrayPopulator implements Populator<Vector3f[]> {
|
private class Vector3fArrayPopulator implements Populator<Vector3f[]> {
|
||||||
|
|
||||||
|
@ -486,6 +486,8 @@ public class GltfUtils {
|
|||||||
mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, BufferUtils.createShortBuffer(jointsArray));
|
mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, BufferUtils.createShortBuffer(jointsArray));
|
||||||
}
|
}
|
||||||
mesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, BufferUtils.createFloatBuffer(weightsArray));
|
mesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, BufferUtils.createFloatBuffer(weightsArray));
|
||||||
|
mesh.getBuffer(VertexBuffer.Type.BoneIndex).setUsage(VertexBuffer.Usage.CpuOnly);
|
||||||
|
mesh.getBuffer(VertexBuffer.Type.BoneWeight).setUsage(VertexBuffer.Usage.CpuOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void populateFloatArray(float[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException {
|
private static void populateFloatArray(float[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException {
|
||||||
|
@ -10,7 +10,8 @@ public class TrackData {
|
|||||||
public enum Type {
|
public enum Type {
|
||||||
Translation,
|
Translation,
|
||||||
Rotation,
|
Rotation,
|
||||||
Scale
|
Scale,
|
||||||
|
Morph
|
||||||
}
|
}
|
||||||
|
|
||||||
Float length;
|
Float length;
|
||||||
@ -21,7 +22,6 @@ public class TrackData {
|
|||||||
Vector3f[] translations;
|
Vector3f[] translations;
|
||||||
Quaternion[] rotations;
|
Quaternion[] rotations;
|
||||||
Vector3f[] scales;
|
Vector3f[] scales;
|
||||||
//not used for now
|
|
||||||
float[] weights;
|
float[] weights;
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
@ -125,6 +125,13 @@ public class TrackData {
|
|||||||
System.arraycopy(scales, 0, newScales, 1, scales.length);
|
System.arraycopy(scales, 0, newScales, 1, scales.length);
|
||||||
scales = newScales;
|
scales = newScales;
|
||||||
}
|
}
|
||||||
|
if (weights != null) {
|
||||||
|
int nbMorph = weights.length / (times.length - 1);
|
||||||
|
float[] newWeights = new float[weights.length + nbMorph];
|
||||||
|
System.arraycopy(weights, 0, newWeights, 0, nbMorph);
|
||||||
|
System.arraycopy(weights, 0, newWeights, nbMorph, weights.length);
|
||||||
|
weights = newWeights;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTimesConsistantcy();
|
checkTimesConsistantcy();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user