Hardware Morph animation implementation and glTF loading
This commit is contained in:
parent
d9a8666742
commit
42215f4890
@ -16,7 +16,7 @@ public class AnimClip implements JmeCloneable, Savable {
|
||||
private String name;
|
||||
private double length;
|
||||
|
||||
private TransformTrack[] tracks;
|
||||
private AnimTrack[] tracks;
|
||||
|
||||
public AnimClip() {
|
||||
}
|
||||
@ -25,9 +25,9 @@ public class AnimClip implements JmeCloneable, Savable {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setTracks(TransformTrack[] tracks) {
|
||||
public void setTracks(AnimTrack[] tracks) {
|
||||
this.tracks = tracks;
|
||||
for (TransformTrack track : tracks) {
|
||||
for (AnimTrack track : tracks) {
|
||||
if (track.getLength() > length) {
|
||||
length = track.getLength();
|
||||
}
|
||||
@ -44,7 +44,7 @@ public class AnimClip implements JmeCloneable, Savable {
|
||||
}
|
||||
|
||||
|
||||
public TransformTrack[] getTracks() {
|
||||
public AnimTrack[] getTracks() {
|
||||
return tracks;
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ public class AnimClip implements JmeCloneable, Savable {
|
||||
|
||||
@Override
|
||||
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++) {
|
||||
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
|
||||
*/
|
||||
public class TransformTrack implements JmeCloneable, Savable {
|
||||
public class TransformTrack implements AnimTrack<Transform> {
|
||||
|
||||
private double length;
|
||||
private HasLocalTransform target;
|
||||
@ -81,15 +81,6 @@ public class TransformTrack implements JmeCloneable, Savable {
|
||||
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
|
||||
*
|
||||
@ -223,7 +214,7 @@ public class TransformTrack implements JmeCloneable, Savable {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void getTransformAtTime(double t, Transform transform) {
|
||||
public void getDataAtTime(double t, Transform transform) {
|
||||
float time = (float) t;
|
||||
|
||||
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
|
||||
|
||||
public static final AnimInterpolator<Vector3f> LinearVec3f = new AnimInterpolator<Vector3f>() {
|
||||
private Vector3f next = new Vector3f();
|
||||
|
||||
@ -70,7 +69,6 @@ public class AnimInterpolators {
|
||||
return store;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* CatmullRom interpolation
|
||||
*/
|
||||
|
@ -20,6 +20,7 @@ public class FrameInterpolator {
|
||||
private TrackDataReader<Vector3f> scaleReader = new TrackDataReader<>();
|
||||
private TrackTimeReader timesReader = new TrackTimeReader();
|
||||
|
||||
|
||||
private Transform transforms = new Transform();
|
||||
|
||||
public Transform interpolate(float t, int currentIndex, CompactVector3Array translations, CompactQuaternionArray rotations, CompactVector3Array scales, float[] times){
|
||||
@ -42,6 +43,20 @@ public class FrameInterpolator {
|
||||
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) {
|
||||
this.timeInterpolator = timeInterpolator;
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package com.jme3.anim.tween.action;
|
||||
|
||||
import com.jme3.anim.AnimClip;
|
||||
import com.jme3.anim.TransformTrack;
|
||||
import com.jme3.anim.*;
|
||||
import com.jme3.anim.tween.AbstractTween;
|
||||
import com.jme3.anim.util.HasLocalTransform;
|
||||
import com.jme3.math.Transform;
|
||||
import com.jme3.scene.Geometry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@ -22,20 +22,41 @@ public class ClipAction extends BlendableAction {
|
||||
|
||||
@Override
|
||||
public void doInterpolate(double t) {
|
||||
TransformTrack[] tracks = clip.getTracks();
|
||||
for (TransformTrack track : tracks) {
|
||||
HasLocalTransform target = track.getTarget();
|
||||
transform.set(target.getLocalTransform());
|
||||
track.getTransformAtTime(t, transform);
|
||||
|
||||
if (collectTransformDelegate != null) {
|
||||
collectTransformDelegate.collectTransform(target, transform, getWeight(), this);
|
||||
} else {
|
||||
this.collectTransform(target, transform, getTransitionWeight(), this);
|
||||
AnimTrack[] tracks = clip.getTracks();
|
||||
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();
|
||||
transform.set(target.getLocalTransform());
|
||||
track.getDataAtTime(t, transform);
|
||||
|
||||
if (collectTransformDelegate != null) {
|
||||
collectTransformDelegate.collectTransform(target, transform, getWeight(), this);
|
||||
} else {
|
||||
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() {
|
||||
|
||||
}
|
||||
@ -43,8 +64,10 @@ public class ClipAction extends BlendableAction {
|
||||
@Override
|
||||
public Collection<HasLocalTransform> getTargets() {
|
||||
List<HasLocalTransform> targets = new ArrayList<>(clip.getTracks().length);
|
||||
for (TransformTrack track : clip.getTracks()) {
|
||||
targets.add(track.getTarget());
|
||||
for (AnimTrack track : clip.getTracks()) {
|
||||
if (track instanceof TransformTrack) {
|
||||
targets.add(((TransformTrack) track).getTarget());
|
||||
}
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ import java.util.Map;
|
||||
*/
|
||||
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 float[] array;
|
||||
private boolean invalid;
|
||||
@ -114,6 +114,10 @@ public abstract class CompactArray<T> implements JmeCloneable {
|
||||
indexPool.clear();
|
||||
}
|
||||
|
||||
protected void setInvalid(boolean invalid) {
|
||||
this.invalid = invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index
|
||||
* @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.nio.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <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 SafeArrayList<VertexBuffer> buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class);
|
||||
private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
|
||||
private SafeArrayList<VertexBuffer> buffersList = new SafeArrayList<>(VertexBuffer.class);
|
||||
private IntMap<VertexBuffer> buffers = new IntMap<>();
|
||||
private VertexBuffer[] lodLevels;
|
||||
private float pointSize = 1;
|
||||
private float lineWidth = 1;
|
||||
@ -183,6 +184,11 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
|
||||
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}.
|
||||
*/
|
||||
@ -203,7 +209,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
clone.meshBound = meshBound.clone();
|
||||
clone.collisionTree = collisionTree != null ? collisionTree : null;
|
||||
clone.buffers = buffers.clone();
|
||||
clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class,buffersList);
|
||||
clone.buffersList = new SafeArrayList<>(VertexBuffer.class, buffersList);
|
||||
clone.vertexArrayID = -1;
|
||||
if (elementLengths != null) {
|
||||
clone.elementLengths = elementLengths.clone();
|
||||
@ -233,8 +239,8 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
//clone.collisionTree = collisionTree != null ? collisionTree : null;
|
||||
clone.collisionTree = null; // it will get re-generated in any case
|
||||
|
||||
clone.buffers = new IntMap<VertexBuffer>();
|
||||
clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class);
|
||||
clone.buffers = new IntMap<>();
|
||||
clone.buffersList = new SafeArrayList<>(VertexBuffer.class);
|
||||
for (VertexBuffer vb : buffersList.getArray()){
|
||||
VertexBuffer bufClone = vb.clone();
|
||||
clone.buffers.put(vb.getBufferType().ordinal(), bufClone);
|
||||
@ -697,7 +703,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
*/
|
||||
@Deprecated
|
||||
public void setInterleaved(){
|
||||
ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>();
|
||||
ArrayList<VertexBuffer> vbs = new ArrayList<>();
|
||||
vbs.addAll(buffersList);
|
||||
|
||||
// ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values());
|
||||
@ -820,8 +826,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
* {@link #setInterleaved() interleaved} format.
|
||||
*/
|
||||
public void updateCounts(){
|
||||
if (getBuffer(Type.InterleavedData) != null)
|
||||
if (getBuffer(Type.InterleavedData) != null) {
|
||||
throw new IllegalStateException("Should update counts before interleave");
|
||||
}
|
||||
|
||||
VertexBuffer pb = getBuffer(Type.Position);
|
||||
VertexBuffer ib = getBuffer(Type.Index);
|
||||
@ -844,11 +851,13 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
*/
|
||||
public int getTriangleCount(int lod){
|
||||
if (lodLevels != null){
|
||||
if (lod < 0)
|
||||
if (lod < 0) {
|
||||
throw new IllegalArgumentException("LOD level cannot be < 0");
|
||||
}
|
||||
|
||||
if (lod >= lodLevels.length)
|
||||
throw new IllegalArgumentException("LOD level "+lod+" does not exist!");
|
||||
if (lod >= lodLevels.length) {
|
||||
throw new IllegalArgumentException("LOD level " + lod + " does not exist!");
|
||||
}
|
||||
|
||||
return computeNumElements(lodLevels[lod].getData().limit());
|
||||
}else if (lod == 0){
|
||||
@ -968,8 +977,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
* Sets the mesh's VAO ID. Internal use only.
|
||||
*/
|
||||
public void setId(int id){
|
||||
if (vertexArrayID != -1)
|
||||
if (vertexArrayID != -1) {
|
||||
throw new IllegalStateException("ID has already been set.");
|
||||
}
|
||||
|
||||
vertexArrayID = id;
|
||||
}
|
||||
@ -1037,8 +1047,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
* @throws IllegalArgumentException If the buffer type is already set
|
||||
*/
|
||||
public void setBuffer(VertexBuffer vb){
|
||||
if (buffers.containsKey(vb.getBufferType().ordinal()))
|
||||
throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
|
||||
if (buffers.containsKey(vb.getBufferType().ordinal())) {
|
||||
throw new IllegalArgumentException("Buffer type already set: " + vb.getBufferType());
|
||||
}
|
||||
|
||||
buffers.put(vb.getBufferType().ordinal(), vb);
|
||||
buffersList.add(vb);
|
||||
@ -1151,8 +1162,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
*/
|
||||
public FloatBuffer getFloatBuffer(Type type) {
|
||||
VertexBuffer vb = getBuffer(type);
|
||||
if (vb == null)
|
||||
if (vb == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (FloatBuffer) vb.getData();
|
||||
}
|
||||
@ -1166,8 +1178,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
*/
|
||||
public ShortBuffer getShortBuffer(Type type) {
|
||||
VertexBuffer vb = getBuffer(type);
|
||||
if (vb == null)
|
||||
if (vb == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
public IndexBuffer getIndicesAsList(){
|
||||
if (mode == Mode.Hybrid)
|
||||
if (mode == Mode.Hybrid) {
|
||||
throw new UnsupportedOperationException("Hybrid mode not supported");
|
||||
}
|
||||
|
||||
IndexBuffer ib = getIndexBuffer();
|
||||
if (ib != null){
|
||||
@ -1209,8 +1223,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
*/
|
||||
public IndexBuffer getIndexBuffer() {
|
||||
VertexBuffer vb = getBuffer(Type.Index);
|
||||
if (vb == null)
|
||||
if (vb == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return IndexBuffer.wrapIndexBuffer(vb.getData());
|
||||
}
|
||||
@ -1233,8 +1248,8 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
IndexBuffer indexBuf = getIndexBuffer();
|
||||
int numIndices = indexBuf.size();
|
||||
|
||||
IntMap<Integer> oldIndicesToNewIndices = new IntMap<Integer>(numIndices);
|
||||
ArrayList<Integer> newIndicesToOldIndices = new ArrayList<Integer>();
|
||||
IntMap<Integer> oldIndicesToNewIndices = new IntMap<>(numIndices);
|
||||
ArrayList<Integer> newIndicesToOldIndices = new ArrayList<>();
|
||||
int newIndex = 0;
|
||||
|
||||
for (int i = 0; i < numIndices; i++) {
|
||||
@ -1345,14 +1360,17 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
*/
|
||||
public void scaleTextureCoordinates(Vector2f scaleFactor){
|
||||
VertexBuffer tc = getBuffer(Type.TexCoord);
|
||||
if (tc == null)
|
||||
if (tc == null) {
|
||||
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");
|
||||
}
|
||||
|
||||
if (tc.getNumComponents() != 2)
|
||||
if (tc.getNumComponents() != 2) {
|
||||
throw new UnsupportedOperationException("Only 2D texture coords are supported");
|
||||
}
|
||||
|
||||
FloatBuffer fb = (FloatBuffer) tc.getData();
|
||||
fb.clear();
|
||||
@ -1504,6 +1522,70 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
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 {
|
||||
OutputCapsule out = ex.getCapsule(this);
|
||||
|
||||
@ -1550,6 +1632,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
out.write(lodLevels, "lodLevels", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
InputCapsule in = im.getCapsule(this);
|
||||
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
|
||||
* 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
|
||||
* used by the CPU.
|
||||
*/
|
||||
CpuOnly;
|
||||
CpuOnly
|
||||
}
|
||||
|
||||
/**
|
||||
@ -610,8 +644,9 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
||||
return 0;
|
||||
}
|
||||
int elements = data.limit() / components;
|
||||
if (format == Format.Half)
|
||||
if (format == Format.Half) {
|
||||
elements /= 2;
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
@ -642,14 +677,17 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
||||
* argument.
|
||||
*/
|
||||
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.");
|
||||
}
|
||||
|
||||
if (usage == null || format == null || data == null)
|
||||
if (usage == null || format == null || data == null) {
|
||||
throw new IllegalArgumentException("None of the arguments can be null");
|
||||
|
||||
if (data.isReadOnly())
|
||||
throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
|
||||
}
|
||||
|
||||
if (data.isReadOnly()) {
|
||||
throw new IllegalArgumentException("VertexBuffer data cannot be read-only.");
|
||||
}
|
||||
|
||||
if (bufType != Type.InstanceData) {
|
||||
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.
|
||||
*/
|
||||
public void convertToHalf(){
|
||||
if (id != -1)
|
||||
if (id != -1) {
|
||||
throw new UnsupportedOperationException("Data has already been sent.");
|
||||
}
|
||||
|
||||
if (format != Format.Float)
|
||||
if (format != Format.Float) {
|
||||
throw new IllegalStateException("Format must be float!");
|
||||
}
|
||||
|
||||
int numElements = data.limit() / components;
|
||||
format = Format.Half;
|
||||
@ -913,8 +953,9 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
||||
* match.
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
int inPos = inIndex * 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.
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
}
|
||||
}
|
@ -110,6 +110,11 @@ MaterialDef Phong Lighting {
|
||||
// For hardware skinning
|
||||
Int NumberOfBones
|
||||
Matrix4Array BoneMatrices
|
||||
|
||||
// For Morph animation
|
||||
FloatArray MorphWeights
|
||||
Int NumberOfMorphTargets
|
||||
Int NumberOfTargetsBuffers : 1
|
||||
|
||||
//For instancing
|
||||
Boolean UseInstancing
|
||||
@ -152,6 +157,8 @@ MaterialDef Phong Lighting {
|
||||
SPHERE_MAP : EnvMapAsSphereMap
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +198,8 @@ MaterialDef Phong Lighting {
|
||||
SPHERE_MAP : EnvMapAsSphereMap
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,6 +219,8 @@ MaterialDef Phong Lighting {
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
@ -247,6 +258,8 @@ MaterialDef Phong Lighting {
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
BACKFACE_SHADOWS: BackfaceShadows
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
@ -274,6 +287,8 @@ MaterialDef Phong Lighting {
|
||||
DIFFUSEMAP_ALPHA : DiffuseMap
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
}
|
||||
@ -296,6 +311,8 @@ MaterialDef Phong Lighting {
|
||||
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
#import "Common/ShaderLib/Instancing.glsllib"
|
||||
#import "Common/ShaderLib/Skinning.glsllib"
|
||||
#import "Common/ShaderLib/Lighting.glsllib"
|
||||
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||
|
||||
#ifdef VERTEX_LIGHTING
|
||||
#import "Common/ShaderLib/BlinnPhongLighting.glsllib"
|
||||
#endif
|
||||
@ -90,6 +92,14 @@ void main(){
|
||||
vec3 modelSpaceTan = inTangent.xyz;
|
||||
#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
|
||||
#ifndef VERTEX_LIGHTING
|
||||
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||
|
@ -110,6 +110,11 @@ MaterialDef PBR Lighting {
|
||||
// For hardware skinning
|
||||
Int NumberOfBones
|
||||
Matrix4Array BoneMatrices
|
||||
|
||||
// For Morph animation
|
||||
FloatArray MorphWeights
|
||||
Int NumberOfMorphTargets
|
||||
Int NumberOfTargetsBuffers : 1
|
||||
|
||||
//For instancing
|
||||
Boolean UseInstancing
|
||||
@ -158,6 +163,8 @@ MaterialDef PBR Lighting {
|
||||
NORMAL_TYPE: NormalType
|
||||
VERTEX_COLOR : UseVertexColor
|
||||
AO_MAP: LightMapAsAOMap
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,6 +185,8 @@ MaterialDef PBR Lighting {
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
@ -215,6 +224,8 @@ MaterialDef PBR Lighting {
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
BACKFACE_SHADOWS: BackfaceShadows
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
@ -247,6 +258,8 @@ MaterialDef PBR Lighting {
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
BACKFACE_SHADOWS: BackfaceShadows
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
@ -272,6 +285,8 @@ MaterialDef PBR Lighting {
|
||||
Defines {
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
}
|
||||
@ -291,6 +306,8 @@ MaterialDef PBR Lighting {
|
||||
NEED_TEXCOORD1
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||
#import "Common/ShaderLib/Instancing.glsllib"
|
||||
#import "Common/ShaderLib/Skinning.glsllib"
|
||||
|
||||
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||
|
||||
uniform vec4 m_BaseColor;
|
||||
|
||||
uniform vec4 g_AmbientLightColor;
|
||||
varying vec2 texCoord;
|
||||
|
||||
@ -38,11 +37,19 @@ void main(){
|
||||
vec3 modelSpaceTan = inTangent.xyz;
|
||||
#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
|
||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||
#else
|
||||
Skinning_Compute(modelSpacePos, modelSpaceNorm);
|
||||
Skinning_Compute(modelSpacePos, modelSpaceNorm);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -64,4 +71,4 @@ void main(){
|
||||
#ifdef VERTEX_COLOR
|
||||
Color *= inColor;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
#import "Common/ShaderLib/Instancing.glsllib"
|
||||
#import "Common/ShaderLib/Skinning.glsllib"
|
||||
#import "Common/ShaderLib/Lighting.glsllib"
|
||||
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||
|
||||
#ifdef VERTEX_LIGHTING
|
||||
#import "Common/ShaderLib/BlinnPhongLighting.glsllib"
|
||||
#endif
|
||||
@ -84,6 +86,14 @@ void main(){
|
||||
vec3 modelSpaceTan = inTangent.xyz;
|
||||
#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
|
||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||
|
@ -20,6 +20,11 @@ MaterialDef Unshaded {
|
||||
Int NumberOfBones
|
||||
Matrix4Array BoneMatrices
|
||||
|
||||
// For Morph animation
|
||||
FloatArray MorphWeights
|
||||
Int NumberOfMorphTargets
|
||||
Int NumberOfTargetsBuffers : 1
|
||||
|
||||
// Alpha threshold for fragment discarding
|
||||
Float AlphaDiscardThreshold (AlphaTestFallOff)
|
||||
|
||||
@ -76,26 +81,30 @@ MaterialDef Unshaded {
|
||||
HAS_COLOR : Color
|
||||
NUM_BONES : NumberOfBones
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
}
|
||||
|
||||
Technique PreNormalPass {
|
||||
|
||||
VertexShader GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.vert
|
||||
FragmentShader GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.frag
|
||||
VertexShader GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.vert
|
||||
FragmentShader GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
WorldViewMatrix
|
||||
NormalMatrix
|
||||
ViewProjectionMatrix
|
||||
ViewMatrix
|
||||
}
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
WorldViewMatrix
|
||||
NormalMatrix
|
||||
ViewProjectionMatrix
|
||||
ViewMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
}
|
||||
Defines {
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
}
|
||||
|
||||
Technique PreShadow {
|
||||
@ -115,6 +124,8 @@ MaterialDef Unshaded {
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
@ -150,8 +161,10 @@ MaterialDef Unshaded {
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
INSTANCING : UseInstancing
|
||||
BACKFACE_SHADOWS: BackfaceShadows
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
@ -177,8 +190,10 @@ MaterialDef Unshaded {
|
||||
HAS_GLOWMAP : GlowMap
|
||||
HAS_GLOWCOLOR : GlowColor
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
INSTANCING : UseInstancing
|
||||
HAS_POINTSIZE : PointSize
|
||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||
#import "Common/ShaderLib/Skinning.glsllib"
|
||||
#import "Common/ShaderLib/Instancing.glsllib"
|
||||
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||
|
||||
attribute vec3 inPosition;
|
||||
|
||||
@ -38,6 +39,11 @@ void main(){
|
||||
#endif
|
||||
|
||||
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
||||
|
||||
#ifdef NUM_MORPH_TARGETS
|
||||
Morph_Compute(modelSpacePos);
|
||||
#endif
|
||||
|
||||
#ifdef NUM_BONES
|
||||
Skinning_Compute(modelSpacePos);
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
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;
|
||||
float distance = dot(cameraDir, dir);
|
||||
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.5, 0.5, 0.5, 1.0);
|
||||
|
||||
|
||||
void main(){
|
||||
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
||||
|
||||
|
||||
#ifdef NUM_MORPH_TARGETS
|
||||
Morph_Compute(modelSpacePos);
|
||||
#endif
|
||||
|
||||
#ifdef NUM_BONES
|
||||
Skinning_Compute(modelSpacePos);
|
||||
#endif
|
||||
|
@ -1,6 +1,8 @@
|
||||
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||
#import "Common/ShaderLib/Instancing.glsllib"
|
||||
#import "Common/ShaderLib/Skinning.glsllib"
|
||||
#import "Common/ShaderLib/MorphAnim.glsllib"
|
||||
|
||||
attribute vec3 inPosition;
|
||||
attribute vec2 inTexCoord;
|
||||
|
||||
@ -8,7 +10,11 @@ varying vec2 texCoord;
|
||||
|
||||
void main(){
|
||||
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
||||
|
||||
|
||||
#ifdef NUM_MORPH_TARGETS
|
||||
Morph_Compute(modelSpacePos, modelSpaceNorm);
|
||||
#endif
|
||||
|
||||
#ifdef NUM_BONES
|
||||
Skinning_Compute(modelSpacePos);
|
||||
#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.math.*;
|
||||
import com.jme3.renderer.Limits;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.scene.*;
|
||||
import com.jme3.scene.control.Control;
|
||||
import com.jme3.scene.debug.custom.ArmatureDebugAppState;
|
||||
import com.jme3.scene.plugins.gltf.GltfModelKey;
|
||||
import com.jme3.shader.VarType;
|
||||
import jme3test.model.anim.EraseTimer;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -58,9 +59,13 @@ public class TestGltfLoading extends SimpleApplication {
|
||||
int assetIndex = 0;
|
||||
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";
|
||||
int duration = 2;
|
||||
int duration = 1;
|
||||
boolean playAnim = true;
|
||||
|
||||
Geometry g;
|
||||
int morphIndex = 0;
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestGltfLoading app = new TestGltfLoading();
|
||||
app.start();
|
||||
@ -73,10 +78,12 @@ public class TestGltfLoading extends SimpleApplication {
|
||||
https://sketchfab.com/features/gltf
|
||||
You have to copy them in Model/gltf folder in the test-data project.
|
||||
*/
|
||||
@Override
|
||||
public void simpleInitApp() {
|
||||
|
||||
ArmatureDebugAppState armatureDebugappState = new ArmatureDebugAppState();
|
||||
getStateManager().attach(armatureDebugappState);
|
||||
setTimer(new EraseTimer());
|
||||
|
||||
String folder = System.getProperty("user.home");
|
||||
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);
|
||||
// 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/izzy/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));
|
||||
|
||||
// setMorphTarget(morphIndex);
|
||||
|
||||
ChaseCameraAppState chaseCam = new ChaseCameraAppState();
|
||||
chaseCam.setTarget(probeNode);
|
||||
getStateManager().attach(chaseCam);
|
||||
@ -200,6 +216,15 @@ public class TestGltfLoading extends SimpleApplication {
|
||||
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) {
|
||||
T ctrl = s.getControl(controlClass);
|
||||
if (ctrl != null) {
|
||||
@ -291,18 +316,19 @@ public class TestGltfLoading extends SimpleApplication {
|
||||
|
||||
@Override
|
||||
public void simpleUpdate(float tpf) {
|
||||
|
||||
if (!useAutoRotate) {
|
||||
return;
|
||||
}
|
||||
time += tpf;
|
||||
autoRotate.rotate(0, tpf * 0.5f, 0);
|
||||
// autoRotate.rotate(0, tpf * 0.5f, 0);
|
||||
if (time > duration) {
|
||||
// morphIndex++;
|
||||
// setMorphTarget(morphIndex);
|
||||
assets.get(assetIndex).removeFromParent();
|
||||
assetIndex = (assetIndex + 1) % assets.size();
|
||||
if (assetIndex == 0) {
|
||||
duration = 10;
|
||||
}
|
||||
// if (assetIndex == 0) {
|
||||
// duration = 10;
|
||||
// }
|
||||
probeNode.attachChild(assets.get(assetIndex));
|
||||
time = 0;
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ public class TestHWSkinning extends SimpleApplication implements ActionListener{
|
||||
|
||||
// private AnimComposer composer;
|
||||
private String[] animNames = {"Dodge", "Walk", "pull", "push"};
|
||||
private final static int SIZE = 60;
|
||||
private final static int SIZE = 40;
|
||||
private boolean hwSkinningEnable = true;
|
||||
private List<SkinningControl> skControls = new ArrayList<SkinningControl>();
|
||||
private List<SkinningControl> skControls = new ArrayList<>();
|
||||
private BitmapText hwsText;
|
||||
|
||||
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.scene.*;
|
||||
import com.jme3.scene.control.CameraControl;
|
||||
import com.jme3.scene.mesh.MorphTarget;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.util.IntMap;
|
||||
@ -19,6 +20,8 @@ import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.io.*;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.rmi.ServerError;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@ -128,8 +131,6 @@ public class GltfLoader implements AssetLoader {
|
||||
|
||||
rootNode = customContentManager.readExtensionAndExtras("root", docRoot, rootNode);
|
||||
|
||||
setupControls();
|
||||
|
||||
//Loading animations
|
||||
if (animations != null) {
|
||||
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.
|
||||
if (rootNode.getChildren().size() == 1) {
|
||||
rootNode = (Node) rootNode.getChild(0);
|
||||
@ -389,7 +392,7 @@ public class GltfLoader implements AssetLoader {
|
||||
//the buffers will be setup if ever used.
|
||||
VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight);
|
||||
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);
|
||||
weightsHW.setUsage(VertexBuffer.Usage.CpuOnly);
|
||||
mesh.setBuffer(weightsHW);
|
||||
@ -397,6 +400,22 @@ public class GltfLoader implements AssetLoader {
|
||||
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);
|
||||
|
||||
Geometry geom = new Geometry(null, mesh);
|
||||
@ -425,7 +444,6 @@ public class GltfLoader implements AssetLoader {
|
||||
geomArray[index] = geom;
|
||||
index++;
|
||||
|
||||
//TODO targets(morph anim...)
|
||||
}
|
||||
|
||||
geomArray = customContentManager.readExtensionAndExtras("mesh", meshData, geomArray);
|
||||
@ -721,6 +739,7 @@ public class GltfLoader implements AssetLoader {
|
||||
|
||||
//temp data storage of track data
|
||||
TrackData[] tracks = new TrackData[nodes.size()];
|
||||
boolean hasMorphTrack = false;
|
||||
|
||||
for (JsonElement channel : channels) {
|
||||
|
||||
@ -733,12 +752,12 @@ public class GltfLoader implements AssetLoader {
|
||||
continue;
|
||||
}
|
||||
assertNotNull(targetPath, "No target path for channel");
|
||||
|
||||
if (targetPath.equals("weight")) {
|
||||
//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");
|
||||
continue;
|
||||
}
|
||||
//
|
||||
// if (targetPath.equals("weights")) {
|
||||
// //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 track");
|
||||
// continue;
|
||||
// }
|
||||
|
||||
TrackData trackData = tracks[targetNode];
|
||||
if (trackData == null) {
|
||||
@ -782,9 +801,15 @@ public class GltfLoader implements AssetLoader {
|
||||
Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator);
|
||||
trackData.rotations = rotations;
|
||||
} else {
|
||||
//TODO support weights
|
||||
logger.log(Level.WARNING, "Morph animation is not supported");
|
||||
continue;
|
||||
trackData.timeArrays.add(new TrackData.TimeData(times, TrackData.Type.Morph));
|
||||
float[] weights = readAccessorData(dataIndex, floatArrayPopulator);
|
||||
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);
|
||||
}
|
||||
@ -795,7 +820,7 @@ public class GltfLoader implements AssetLoader {
|
||||
|
||||
List<Spatial> spatials = new ArrayList<>();
|
||||
AnimClip anim = new AnimClip(name);
|
||||
List<TransformTrack> ttracks = new ArrayList<>();
|
||||
List<AnimTrack> aTracks = new ArrayList<>();
|
||||
int skinIndex = -1;
|
||||
|
||||
List<Joint> usedJoints = new ArrayList<>();
|
||||
@ -809,8 +834,22 @@ public class GltfLoader implements AssetLoader {
|
||||
if (node instanceof Spatial) {
|
||||
Spatial s = (Spatial) node;
|
||||
spatials.add(s);
|
||||
TransformTrack track = new TransformTrack(s, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
|
||||
ttracks.add(track);
|
||||
if (trackData.rotations != null || trackData.translations != null || trackData.scales != null) {
|
||||
TransformTrack track = new TransformTrack(s, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
|
||||
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) {
|
||||
JointWrapper jw = (JointWrapper) node;
|
||||
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);
|
||||
ttracks.add(track);
|
||||
|
||||
aTracks.add(track);
|
||||
}
|
||||
}
|
||||
|
||||
@ -845,12 +883,12 @@ public class GltfLoader implements AssetLoader {
|
||||
Quaternion[] rotations = new Quaternion[]{joint.getLocalRotation()};
|
||||
Vector3f[] scales = new Vector3f[]{joint.getLocalScale()};
|
||||
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);
|
||||
|
||||
@ -862,11 +900,14 @@ public class GltfLoader implements AssetLoader {
|
||||
|
||||
if (!spatials.isEmpty()) {
|
||||
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);
|
||||
List<Spatial> spat = skinnedSpatials.get(skin);
|
||||
spat.addAll(spatials);
|
||||
//the animControl will be added in the setupControls();
|
||||
if (hasMorphTrack && skin.morphControl == null) {
|
||||
skin.morphControl = new MorphControl();
|
||||
}
|
||||
} else {
|
||||
//Spatial animation
|
||||
Spatial spatial = null;
|
||||
@ -882,6 +923,9 @@ public class GltfLoader implements AssetLoader {
|
||||
spatial.addControl(composer);
|
||||
}
|
||||
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.skinningControl);
|
||||
if (skinData.morphControl != null) {
|
||||
spatial.addControl(skinData.morphControl);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
@ -1131,7 +1178,9 @@ public class GltfLoader implements AssetLoader {
|
||||
|
||||
private class SkinData {
|
||||
SkinningControl skinningControl;
|
||||
MorphControl morphControl;
|
||||
AnimComposer animComposer;
|
||||
Spatial spatial;
|
||||
Spatial parent;
|
||||
Transform rootBoneTransformOffset;
|
||||
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[]> {
|
||||
|
||||
|
@ -486,6 +486,8 @@ public class GltfUtils {
|
||||
mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, BufferUtils.createShortBuffer(jointsArray));
|
||||
}
|
||||
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 {
|
||||
|
@ -10,7 +10,8 @@ public class TrackData {
|
||||
public enum Type {
|
||||
Translation,
|
||||
Rotation,
|
||||
Scale
|
||||
Scale,
|
||||
Morph
|
||||
}
|
||||
|
||||
Float length;
|
||||
@ -21,7 +22,6 @@ public class TrackData {
|
||||
Vector3f[] translations;
|
||||
Quaternion[] rotations;
|
||||
Vector3f[] scales;
|
||||
//not used for now
|
||||
float[] weights;
|
||||
|
||||
public void update() {
|
||||
@ -125,6 +125,13 @@ public class TrackData {
|
||||
System.arraycopy(scales, 0, newScales, 1, scales.length);
|
||||
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();
|
||||
@ -336,4 +343,4 @@ public class TrackData {
|
||||
Vector3f scale;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user