Merge branch 'master' of https://github.com/jMonkeyEngine/jmonkeyengine
This commit is contained in:
commit
bd69385571
@ -6,7 +6,6 @@ import android.opengl.ETC1Util.ETC1Texture;
|
|||||||
import android.opengl.GLES20;
|
import android.opengl.GLES20;
|
||||||
import android.opengl.GLUtils;
|
import android.opengl.GLUtils;
|
||||||
import com.jme3.asset.AndroidImageInfo;
|
import com.jme3.asset.AndroidImageInfo;
|
||||||
import com.jme3.math.FastMath;
|
|
||||||
import com.jme3.renderer.RendererException;
|
import com.jme3.renderer.RendererException;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
import com.jme3.texture.Image.Format;
|
import com.jme3.texture.Image.Format;
|
||||||
|
@ -5,6 +5,7 @@ import com.jme3.asset.AndroidImageInfo;
|
|||||||
import com.jme3.asset.AssetInfo;
|
import com.jme3.asset.AssetInfo;
|
||||||
import com.jme3.asset.AssetLoader;
|
import com.jme3.asset.AssetLoader;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
|
import com.jme3.texture.image.ColorSpace;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class AndroidImageLoader implements AssetLoader {
|
public class AndroidImageLoader implements AssetLoader {
|
||||||
@ -13,7 +14,8 @@ public class AndroidImageLoader implements AssetLoader {
|
|||||||
AndroidImageInfo imageInfo = new AndroidImageInfo(info);
|
AndroidImageInfo imageInfo = new AndroidImageInfo(info);
|
||||||
Bitmap bitmap = imageInfo.getBitmap();
|
Bitmap bitmap = imageInfo.getBitmap();
|
||||||
|
|
||||||
Image image = new Image(imageInfo.getFormat(), bitmap.getWidth(), bitmap.getHeight(), null);
|
Image image = new Image(imageInfo.getFormat(), bitmap.getWidth(), bitmap.getHeight(), null, ColorSpace.sRGB);
|
||||||
|
|
||||||
image.setEfficentData(imageInfo);
|
image.setEfficentData(imageInfo);
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import com.jme3.asset.AssetLoadException;
|
|||||||
import com.jme3.asset.AssetLoader;
|
import com.jme3.asset.AssetLoader;
|
||||||
import com.jme3.asset.TextureKey;
|
import com.jme3.asset.TextureKey;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
|
import com.jme3.texture.image.ColorSpace;
|
||||||
import com.jme3.util.BufferUtils;
|
import com.jme3.util.BufferUtils;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -90,7 +91,7 @@ public class AndroidNativeImageLoader implements AssetLoader {
|
|||||||
BufferUtils.destroyDirectBuffer(origDataBuffer);
|
BufferUtils.destroyDirectBuffer(origDataBuffer);
|
||||||
BufferUtils.destroyDirectBuffer(headerDataBuffer);
|
BufferUtils.destroyDirectBuffer(headerDataBuffer);
|
||||||
|
|
||||||
Image img = new Image(getImageFormat(numComponents), width, height, imageDataBuffer);
|
Image img = new Image(getImageFormat(numComponents), width, height, imageDataBuffer, ColorSpace.sRGB);
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ import com.jme3.export.JmeExporter;
|
|||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
@ -113,11 +114,17 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
protected float eventDispatchImpulseThreshold = 10;
|
protected float eventDispatchImpulseThreshold = 10;
|
||||||
protected float rootMass = 15;
|
protected float rootMass = 15;
|
||||||
protected float totalMass = 0;
|
protected float totalMass = 0;
|
||||||
|
private Map<String, Vector3f> ikTargets = new HashMap<String, Vector3f>();
|
||||||
|
private Map<String, Integer> ikChainDepth = new HashMap<String, Integer>();
|
||||||
|
private float ikRotSpeed = 7f;
|
||||||
|
private float limbDampening = 0.6f;
|
||||||
|
|
||||||
|
private float IKThreshold = 0.1f;
|
||||||
public static enum Mode {
|
public static enum Mode {
|
||||||
|
|
||||||
Kinematic,
|
Kinematic,
|
||||||
Ragdoll
|
Ragdoll,
|
||||||
|
IK
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PhysicsBoneLink implements Savable {
|
public class PhysicsBoneLink implements Savable {
|
||||||
@ -189,9 +196,10 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(mode == Mode.IK){
|
||||||
|
ikUpdate(tpf);
|
||||||
|
} else if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
|
||||||
//if the ragdoll has the control of the skeleton, we update each bone with its position in physic world space.
|
//if the ragdoll has the control of the skeleton, we update each bone with its position in physic world space.
|
||||||
if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
|
|
||||||
ragDollUpdate(tpf);
|
ragDollUpdate(tpf);
|
||||||
} else {
|
} else {
|
||||||
kinematicUpdate(tpf);
|
kinematicUpdate(tpf);
|
||||||
@ -260,6 +268,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
Quaternion tmpRot2 = vars.quat2;
|
Quaternion tmpRot2 = vars.quat2;
|
||||||
Vector3f position = vars.vect1;
|
Vector3f position = vars.vect1;
|
||||||
for (PhysicsBoneLink link : boneLinks.values()) {
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
||||||
|
// if(link.usedbyIK){
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
//if blended control this means, keyframed animation is updating the skeleton,
|
//if blended control this means, keyframed animation is updating the skeleton,
|
||||||
//but to allow smooth transition, we blend this transformation with the saved position of the ragdoll
|
//but to allow smooth transition, we blend this transformation with the saved position of the ragdoll
|
||||||
if (blendedControl) {
|
if (blendedControl) {
|
||||||
@ -300,6 +311,94 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
}
|
}
|
||||||
vars.release();
|
vars.release();
|
||||||
}
|
}
|
||||||
|
private void ikUpdate(float tpf){
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
|
||||||
|
Quaternion tmpRot1 = vars.quat1;
|
||||||
|
Quaternion[] tmpRot2 = new Quaternion[]{vars.quat2, new Quaternion()};
|
||||||
|
|
||||||
|
Iterator<String> it = ikTargets.keySet().iterator();
|
||||||
|
float distance;
|
||||||
|
Bone bone;
|
||||||
|
String boneName;
|
||||||
|
while (it.hasNext()) {
|
||||||
|
|
||||||
|
boneName = it.next();
|
||||||
|
bone = (Bone) boneLinks.get(boneName).bone;
|
||||||
|
if (!bone.hasUserControl()) {
|
||||||
|
Logger.getLogger(KinematicRagdollControl.class.getSimpleName()).log(Level.FINE, "{0} doesn't have user control", boneName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
distance = bone.getModelSpacePosition().distance(ikTargets.get(boneName));
|
||||||
|
if (distance < IKThreshold) {
|
||||||
|
Logger.getLogger(KinematicRagdollControl.class.getSimpleName()).log(Level.FINE, "Distance is close enough");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int depth = 0;
|
||||||
|
int maxDepth = ikChainDepth.get(bone.getName());
|
||||||
|
updateBone(boneLinks.get(bone.getName()), tpf * (float) FastMath.sqrt(distance), vars, tmpRot1, tmpRot2, bone, ikTargets.get(boneName), depth, maxDepth);
|
||||||
|
|
||||||
|
Vector3f position = vars.vect1;
|
||||||
|
|
||||||
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
||||||
|
matchPhysicObjectToBone(link, position, tmpRot1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateBone(PhysicsBoneLink link, float tpf, TempVars vars, Quaternion tmpRot1, Quaternion[] tmpRot2, Bone tipBone, Vector3f target, int depth, int maxDepth) {
|
||||||
|
if (link == null || link.bone.getParent() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Quaternion preQuat = link.bone.getLocalRotation();
|
||||||
|
Vector3f vectorAxis;
|
||||||
|
|
||||||
|
float[] measureDist = new float[]{Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY};
|
||||||
|
for (int dirIndex = 0; dirIndex < 3; dirIndex++) {
|
||||||
|
if (dirIndex == 0) {
|
||||||
|
vectorAxis = Vector3f.UNIT_Z;
|
||||||
|
} else if (dirIndex == 1) {
|
||||||
|
vectorAxis = Vector3f.UNIT_X;
|
||||||
|
} else {
|
||||||
|
vectorAxis = Vector3f.UNIT_Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int posOrNeg = 0; posOrNeg < 2; posOrNeg++) {
|
||||||
|
float rot = ikRotSpeed * tpf / (link.rigidBody.getMass() * 2);
|
||||||
|
|
||||||
|
rot = FastMath.clamp(rot, link.joint.getRotationalLimitMotor(dirIndex).getLoLimit(), link.joint.getRotationalLimitMotor(dirIndex).getHiLimit());
|
||||||
|
tmpRot1.fromAngleAxis(rot, vectorAxis);
|
||||||
|
// tmpRot1.fromAngleAxis(rotSpeed * tpf / (link.rigidBody.getMass() * 2), vectorAxis);
|
||||||
|
|
||||||
|
|
||||||
|
tmpRot2[posOrNeg] = link.bone.getLocalRotation().mult(tmpRot1);
|
||||||
|
tmpRot2[posOrNeg].normalizeLocal();
|
||||||
|
|
||||||
|
ikRotSpeed = -ikRotSpeed;
|
||||||
|
|
||||||
|
link.bone.setLocalRotation(tmpRot2[posOrNeg]);
|
||||||
|
link.bone.update();
|
||||||
|
measureDist[posOrNeg] = tipBone.getModelSpacePosition().distance(target);
|
||||||
|
link.bone.setLocalRotation(preQuat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (measureDist[0] < measureDist[1]) {
|
||||||
|
link.bone.setLocalRotation(tmpRot2[0]);
|
||||||
|
} else if (measureDist[0] > measureDist[1]) {
|
||||||
|
link.bone.setLocalRotation(tmpRot2[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
link.bone.getLocalRotation().normalizeLocal();
|
||||||
|
|
||||||
|
link.bone.update();
|
||||||
|
// link.usedbyIK = true;
|
||||||
|
if (link.bone.getParent() != null && depth < maxDepth) {
|
||||||
|
|
||||||
|
updateBone(boneLinks.get(link.bone.getParent().getName()), tpf * limbDampening, vars, tmpRot1, tmpRot2, tipBone, target, depth + 1, maxDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the transforms of a rigidBody to match the transforms of a bone. this
|
* Set the transforms of a rigidBody to match the transforms of a bone. this
|
||||||
@ -618,6 +717,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
animControl.setEnabled(mode == Mode.Kinematic);
|
animControl.setEnabled(mode == Mode.Kinematic);
|
||||||
|
|
||||||
baseRigidBody.setKinematic(mode == Mode.Kinematic);
|
baseRigidBody.setKinematic(mode == Mode.Kinematic);
|
||||||
|
if (mode != Mode.IK) {
|
||||||
TempVars vars = TempVars.get();
|
TempVars vars = TempVars.get();
|
||||||
|
|
||||||
for (PhysicsBoneLink link : boneLinks.values()) {
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
||||||
@ -631,12 +731,16 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
|
|
||||||
}
|
}
|
||||||
vars.release();
|
vars.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode != Mode.IK){
|
||||||
for (Bone bone : skeleton.getRoots()) {
|
for (Bone bone : skeleton.getRoots()) {
|
||||||
RagdollUtils.setUserControl(bone, mode == Mode.Ragdoll);
|
RagdollUtils.setUserControl(bone, mode == Mode.Ragdoll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Smoothly blend from Ragdoll mode to Kinematic mode This is useful to
|
* Smoothly blend from Ragdoll mode to Kinematic mode This is useful to
|
||||||
* blend ragdoll actual position to a keyframe animation for example
|
* blend ragdoll actual position to a keyframe animation for example
|
||||||
@ -703,6 +807,16 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the control into Inverse Kinematics mode. The affected bones are affected by IK.
|
||||||
|
* physics.
|
||||||
|
*/
|
||||||
|
public void setIKMode() {
|
||||||
|
if (mode != Mode.IK) {
|
||||||
|
setMode(Mode.IK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retruns the mode of this control
|
* retruns the mode of this control
|
||||||
*
|
*
|
||||||
@ -805,6 +919,96 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
return control;
|
return control;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector3f setIKTarget(Bone bone, Vector3f worldPos, int chainLength) {
|
||||||
|
Vector3f target = worldPos.subtract(targetModel.getWorldTranslation());
|
||||||
|
ikTargets.put(bone.getName(), target);
|
||||||
|
ikChainDepth.put(bone.getName(), chainLength);
|
||||||
|
int i = 0;
|
||||||
|
while (i < chainLength+2 && bone.getParent() != null) {
|
||||||
|
if (!bone.hasUserControl()) {
|
||||||
|
bone.setUserControl(true);
|
||||||
|
}
|
||||||
|
bone = bone.getParent();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// setIKMode();
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeIKTarget(Bone bone) {
|
||||||
|
int depth = ikChainDepth.remove(bone.getName());
|
||||||
|
int i = 0;
|
||||||
|
while (i < depth+2 && bone.getParent() != null) {
|
||||||
|
if (bone.hasUserControl()) {
|
||||||
|
// matchPhysicObjectToBone(boneLinks.get(bone.getName()), position, tmpRot1);
|
||||||
|
bone.setUserControl(false);
|
||||||
|
}
|
||||||
|
bone = bone.getParent();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAllIKTargets(){
|
||||||
|
ikTargets.clear();
|
||||||
|
ikChainDepth.clear();
|
||||||
|
applyUserControl();
|
||||||
|
}
|
||||||
|
public void applyUserControl() {
|
||||||
|
for (Bone bone : skeleton.getRoots()) {
|
||||||
|
RagdollUtils.setUserControl(bone, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ikTargets.isEmpty()) {
|
||||||
|
setKinematicMode();
|
||||||
|
} else {
|
||||||
|
Iterator iterator = ikTargets.keySet().iterator();
|
||||||
|
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Bone bone = (Bone) iterator.next();
|
||||||
|
while (bone.getParent() != null) {
|
||||||
|
|
||||||
|
Quaternion tmpRot1 = vars.quat1;
|
||||||
|
Vector3f position = vars.vect1;
|
||||||
|
matchPhysicObjectToBone(boneLinks.get(bone.getName()), position, tmpRot1);
|
||||||
|
bone.setUserControl(true);
|
||||||
|
bone = bone.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public float getIkRotSpeed() {
|
||||||
|
return ikRotSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIkRotSpeed(float ikRotSpeed) {
|
||||||
|
this.ikRotSpeed = ikRotSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getIKThreshold() {
|
||||||
|
return IKThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIKThreshold(float IKThreshold) {
|
||||||
|
this.IKThreshold = IKThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public float getLimbDampening() {
|
||||||
|
return limbDampening;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLimbDampening(float limbDampening) {
|
||||||
|
this.limbDampening = limbDampening;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bone getBone(String name){
|
||||||
|
return skeleton.getBone(name);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* serialize this control
|
* serialize this control
|
||||||
*
|
*
|
||||||
@ -831,6 +1035,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
oc.write(eventDispatchImpulseThreshold, "eventDispatchImpulseThreshold", 10);
|
oc.write(eventDispatchImpulseThreshold, "eventDispatchImpulseThreshold", 10);
|
||||||
oc.write(rootMass, "rootMass", 15);
|
oc.write(rootMass, "rootMass", 15);
|
||||||
oc.write(totalMass, "totalMass", 0);
|
oc.write(totalMass, "totalMass", 0);
|
||||||
|
oc.write(ikRotSpeed, "rotSpeed", 7f);
|
||||||
|
oc.write(limbDampening, "limbDampening", 0.6f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -459,7 +459,7 @@ public final class Bone implements Savable {
|
|||||||
/**
|
/**
|
||||||
* Updates world transforms for this bone and it's children.
|
* Updates world transforms for this bone and it's children.
|
||||||
*/
|
*/
|
||||||
final void update() {
|
public final void update() {
|
||||||
this.updateModelTransforms();
|
this.updateModelTransforms();
|
||||||
|
|
||||||
for (int i = children.size() - 1; i >= 0; i--) {
|
for (int i = children.size() - 1; i >= 0; i--) {
|
||||||
@ -796,4 +796,15 @@ public final class Bone implements Savable {
|
|||||||
output.write(bindScale, "bindScale", new Vector3f(1.0f, 1.0f, 1.0f));
|
output.write(bindScale, "bindScale", new Vector3f(1.0f, 1.0f, 1.0f));
|
||||||
output.writeSavableArrayList(children, "children", null);
|
output.writeSavableArrayList(children, "children", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLocalRotation(Quaternion rot){
|
||||||
|
if (!userControl) {
|
||||||
|
throw new IllegalStateException("User control must be on bone to allow user transforms");
|
||||||
|
}
|
||||||
|
this.localRot = rot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUserControl(){
|
||||||
|
return userControl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,24 @@ public class AnimationEvent extends AbstractCinematicEvent {
|
|||||||
this.channelIndex = channelIndex;
|
this.channelIndex = channelIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates an animation event
|
||||||
|
*
|
||||||
|
* @param model the model on which the animation will be played
|
||||||
|
* @param animationName the name of the animation to play
|
||||||
|
* @param channelIndex the index of the channel default is 0. Events on the
|
||||||
|
* @param blendTime the time during the animation are gonna be blended
|
||||||
|
* same channelIndex will use the same channel.
|
||||||
|
*/
|
||||||
|
public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, int channelIndex, float blendTime) {
|
||||||
|
this.model = model;
|
||||||
|
this.animationName = animationName;
|
||||||
|
this.loopMode = loopMode;
|
||||||
|
initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
|
||||||
|
this.channelIndex = channelIndex;
|
||||||
|
this.blendTime = blendTime;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates an animation event
|
* creates an animation event
|
||||||
*
|
*
|
||||||
@ -264,6 +282,10 @@ public class AnimationEvent extends AbstractCinematicEvent {
|
|||||||
Object s = cinematic.getEventData(MODEL_CHANNELS, model);
|
Object s = cinematic.getEventData(MODEL_CHANNELS, model);
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
s = new HashMap<Integer, AnimChannel>();
|
s = new HashMap<Integer, AnimChannel>();
|
||||||
|
int numChannels = model.getControl(AnimControl.class).getNumChannels();
|
||||||
|
for(int i = 0; i < numChannels; i++){
|
||||||
|
((HashMap<Integer, AnimChannel>)s).put(i, model.getControl(AnimControl.class).getChannel(i));
|
||||||
|
}
|
||||||
cinematic.putEventData(MODEL_CHANNELS, model, s);
|
cinematic.putEventData(MODEL_CHANNELS, model, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +341,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
|
|||||||
channel.setTime(t);
|
channel.setTime(t);
|
||||||
channel.getControl().update(0);
|
channel.getControl().update(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -167,11 +167,12 @@ public class ParticleEmitter extends Geometry {
|
|||||||
clone.endColor = endColor.clone();
|
clone.endColor = endColor.clone();
|
||||||
clone.particleInfluencer = particleInfluencer.clone();
|
clone.particleInfluencer = particleInfluencer.clone();
|
||||||
|
|
||||||
// remove wrong control
|
// remove original control from the clone
|
||||||
clone.controls.remove(control);
|
clone.controls.remove(this.control);
|
||||||
|
|
||||||
// put correct control
|
// put clone's control in
|
||||||
clone.controls.add(new ParticleEmitterControl(clone));
|
clone.control = new ParticleEmitterControl(clone);
|
||||||
|
clone.controls.add(clone.control);
|
||||||
|
|
||||||
// Reinitialize particle mesh
|
// Reinitialize particle mesh
|
||||||
switch (meshType) {
|
switch (meshType) {
|
||||||
|
@ -702,7 +702,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
int lodLevel = geom.getLodLevel();
|
int lodLevel = geom.getLodLevel();
|
||||||
if (geom instanceof InstancedGeometry) {
|
if (geom instanceof InstancedGeometry) {
|
||||||
InstancedGeometry instGeom = (InstancedGeometry) geom;
|
InstancedGeometry instGeom = (InstancedGeometry) geom;
|
||||||
renderer.renderMesh(mesh, lodLevel, instGeom.getCurrentNumInstances(), instGeom.getAllInstanceData());
|
int numInstances = instGeom.getActualNumInstances();
|
||||||
|
if (numInstances == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renderer.renderMesh(mesh, lodLevel, numInstances, instGeom.getAllInstanceData());
|
||||||
} else {
|
} else {
|
||||||
renderer.renderMesh(mesh, lodLevel, 1, null);
|
renderer.renderMesh(mesh, lodLevel, 1, null);
|
||||||
}
|
}
|
||||||
|
@ -206,10 +206,10 @@ public final class ColorRGBA implements Savable, Cloneable, java.io.Serializable
|
|||||||
* Saturate that color ensuring all channels have a value between 0 and 1
|
* Saturate that color ensuring all channels have a value between 0 and 1
|
||||||
*/
|
*/
|
||||||
public void clamp() {
|
public void clamp() {
|
||||||
FastMath.clamp(r, 0f, 1f);
|
r = FastMath.clamp(r, 0f, 1f);
|
||||||
FastMath.clamp(g, 0f, 1f);
|
g = FastMath.clamp(g, 0f, 1f);
|
||||||
FastMath.clamp(b, 0f, 1f);
|
b = FastMath.clamp(b, 0f, 1f);
|
||||||
FastMath.clamp(a, 0f, 1f);
|
a = FastMath.clamp(a, 0f, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -575,7 +575,7 @@ public class RenderManager {
|
|||||||
Geometry gm = (Geometry) s;
|
Geometry gm = (Geometry) s;
|
||||||
|
|
||||||
RenderQueue.ShadowMode shadowMode = s.getShadowMode();
|
RenderQueue.ShadowMode shadowMode = s.getShadowMode();
|
||||||
if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
|
if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive && !gm.isGrouped()) {
|
||||||
//forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
|
//forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
|
||||||
rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
|
rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ public class Statistics {
|
|||||||
if( !enabled )
|
if( !enabled )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
numObjects += count;
|
numObjects += 1;
|
||||||
numTriangles += mesh.getTriangleCount(lod) * count;
|
numTriangles += mesh.getTriangleCount(lod) * count;
|
||||||
numVertices += mesh.getVertexCount() * count;
|
numVertices += mesh.getVertexCount() * count;
|
||||||
}
|
}
|
||||||
|
@ -773,10 +773,6 @@ public class BatchNode extends GeometryGroupNode implements Savable {
|
|||||||
this.needsFullRebatch = needsFullRebatch;
|
this.needsFullRebatch = needsFullRebatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOffsetIndex(Geometry batchedGeometry) {
|
|
||||||
return batchedGeometry.startIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node clone(boolean cloneMaterials) {
|
public Node clone(boolean cloneMaterials) {
|
||||||
BatchNode clone = (BatchNode)super.clone(cloneMaterials);
|
BatchNode clone = (BatchNode)super.clone(cloneMaterials);
|
||||||
@ -790,8 +786,8 @@ public class BatchNode extends GeometryGroupNode implements Savable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
clone.needsFullRebatch = true;
|
clone.needsFullRebatch = true;
|
||||||
clone.batches.clear();
|
clone.batches = new SafeArrayList<Batch>(Batch.class);
|
||||||
clone.batchesByGeom.clear();
|
clone.batchesByGeom = new HashMap<Geometry, Batch>();
|
||||||
clone.batch();
|
clone.batch();
|
||||||
}
|
}
|
||||||
return clone;
|
return clone;
|
||||||
|
@ -81,7 +81,7 @@ public class Geometry extends Spatial {
|
|||||||
* The start index of this <code>Geometry's</code> inside
|
* The start index of this <code>Geometry's</code> inside
|
||||||
* the {@link GeometryGroupNode}.
|
* the {@link GeometryGroupNode}.
|
||||||
*/
|
*/
|
||||||
protected int startIndex;
|
protected int startIndex = -1;
|
||||||
/**
|
/**
|
||||||
* Serialization only. Do not use.
|
* Serialization only. Do not use.
|
||||||
*/
|
*/
|
||||||
@ -316,7 +316,7 @@ public class Geometry extends Spatial {
|
|||||||
* @param node Which {@link GeometryGroupNode} to associate with.
|
* @param node Which {@link GeometryGroupNode} to associate with.
|
||||||
* @param startIndex The starting index of this geometry in the group.
|
* @param startIndex The starting index of this geometry in the group.
|
||||||
*/
|
*/
|
||||||
protected void associateWithGroupNode(GeometryGroupNode node, int startIndex) {
|
public void associateWithGroupNode(GeometryGroupNode node, int startIndex) {
|
||||||
if (isGrouped()) {
|
if (isGrouped()) {
|
||||||
unassociateFromGroupNode();
|
unassociateFromGroupNode();
|
||||||
}
|
}
|
||||||
@ -331,13 +331,15 @@ public class Geometry extends Spatial {
|
|||||||
*
|
*
|
||||||
* Should only be called by the parent {@link GeometryGroupNode}.
|
* Should only be called by the parent {@link GeometryGroupNode}.
|
||||||
*/
|
*/
|
||||||
protected void unassociateFromGroupNode() {
|
public void unassociateFromGroupNode() {
|
||||||
if (groupNode != null) {
|
if (groupNode != null) {
|
||||||
// Once the geometry is removed
|
// Once the geometry is removed
|
||||||
// from the parent, the group node needs to be updated.
|
// from the parent, the group node needs to be updated.
|
||||||
groupNode.onGeoemtryUnassociated(this);
|
groupNode.onGeoemtryUnassociated(this);
|
||||||
groupNode = null;
|
groupNode = null;
|
||||||
startIndex = 0;
|
|
||||||
|
// change the default to -1 to make error detection easier
|
||||||
|
startIndex = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,9 +486,9 @@ public class Geometry extends Spatial {
|
|||||||
|
|
||||||
// This geometry is managed,
|
// This geometry is managed,
|
||||||
// but the cloned one is not attached to anything, hence not managed.
|
// but the cloned one is not attached to anything, hence not managed.
|
||||||
if (isGrouped()) {
|
if (geomClone.isGrouped()) {
|
||||||
groupNode = null;
|
geomClone.groupNode = null;
|
||||||
startIndex = 0;
|
geomClone.startIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
geomClone.cachedWorldMat = cachedWorldMat.clone();
|
geomClone.cachedWorldMat = cachedWorldMat.clone();
|
||||||
|
@ -8,6 +8,20 @@ package com.jme3.scene;
|
|||||||
*/
|
*/
|
||||||
public abstract class GeometryGroupNode extends Node {
|
public abstract class GeometryGroupNode extends Node {
|
||||||
|
|
||||||
|
protected static int getGeometryStartIndex(Geometry geom) {
|
||||||
|
if (geom.startIndex == -1) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
return geom.startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void setGeometryStartIndex(Geometry geom, int startIndex) {
|
||||||
|
if (startIndex < -1) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
geom.startIndex = startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a <code>GeometryGroupNode</code>
|
* Construct a <code>GeometryGroupNode</code>
|
||||||
*/
|
*/
|
||||||
|
@ -174,6 +174,7 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
|
|
||||||
private int vertCount = -1;
|
private int vertCount = -1;
|
||||||
private int elementCount = -1;
|
private int elementCount = -1;
|
||||||
|
private int instanceCount = -1;
|
||||||
private int maxNumWeights = -1; // only if using skeletal animation
|
private int maxNumWeights = -1; // only if using skeletal animation
|
||||||
|
|
||||||
private int[] elementLengths;
|
private int[] elementLengths;
|
||||||
@ -242,6 +243,7 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
clone.vertexArrayID = -1;
|
clone.vertexArrayID = -1;
|
||||||
clone.vertCount = vertCount;
|
clone.vertCount = vertCount;
|
||||||
clone.elementCount = elementCount;
|
clone.elementCount = elementCount;
|
||||||
|
clone.instanceCount = instanceCount;
|
||||||
|
|
||||||
// although this could change
|
// although this could change
|
||||||
// if the bone weight/index buffers are modified
|
// if the bone weight/index buffers are modified
|
||||||
@ -718,6 +720,17 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int computeInstanceCount() {
|
||||||
|
// Whatever the max of the base instance counts
|
||||||
|
int max = 0;
|
||||||
|
for( VertexBuffer vb : buffersList ) {
|
||||||
|
if( vb.getBaseInstanceCount() > max ) {
|
||||||
|
max = vb.getBaseInstanceCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the {@link #getVertexCount() vertex} and
|
* Update the {@link #getVertexCount() vertex} and
|
||||||
* {@link #getTriangleCount() triangle} counts for this mesh
|
* {@link #getTriangleCount() triangle} counts for this mesh
|
||||||
@ -742,6 +755,7 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
}else{
|
}else{
|
||||||
elementCount = computeNumElements(vertCount);
|
elementCount = computeNumElements(vertCount);
|
||||||
}
|
}
|
||||||
|
instanceCount = computeInstanceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -790,6 +804,14 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
return vertCount;
|
return vertCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of instances this mesh contains. The instance
|
||||||
|
* count is based on any VertexBuffers with instancing set.
|
||||||
|
*/
|
||||||
|
public int getInstanceCount() {
|
||||||
|
return instanceCount;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the triangle vertex positions at the given triangle index
|
* Gets the triangle vertex positions at the given triangle index
|
||||||
* and stores them into the v1, v2, v3 arguments.
|
* and stores them into the v1, v2, v3 arguments.
|
||||||
@ -1333,6 +1355,7 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
out.write(meshBound, "modelBound", null);
|
out.write(meshBound, "modelBound", null);
|
||||||
out.write(vertCount, "vertCount", -1);
|
out.write(vertCount, "vertCount", -1);
|
||||||
out.write(elementCount, "elementCount", -1);
|
out.write(elementCount, "elementCount", -1);
|
||||||
|
out.write(instanceCount, "instanceCount", -1);
|
||||||
out.write(maxNumWeights, "max_num_weights", -1);
|
out.write(maxNumWeights, "max_num_weights", -1);
|
||||||
out.write(mode, "mode", Mode.Triangles);
|
out.write(mode, "mode", Mode.Triangles);
|
||||||
out.write(collisionTree, "collisionTree", null);
|
out.write(collisionTree, "collisionTree", null);
|
||||||
@ -1370,6 +1393,7 @@ public class Mesh implements Savable, Cloneable {
|
|||||||
meshBound = (BoundingVolume) in.readSavable("modelBound", null);
|
meshBound = (BoundingVolume) in.readSavable("modelBound", null);
|
||||||
vertCount = in.readInt("vertCount", -1);
|
vertCount = in.readInt("vertCount", -1);
|
||||||
elementCount = in.readInt("elementCount", -1);
|
elementCount = in.readInt("elementCount", -1);
|
||||||
|
instanceCount = in.readInt("instanceCount", -1);
|
||||||
maxNumWeights = in.readInt("max_num_weights", -1);
|
maxNumWeights = in.readInt("max_num_weights", -1);
|
||||||
mode = in.readEnum("mode", Mode.class, Mode.Triangles);
|
mode = in.readEnum("mode", Mode.class, Mode.Triangles);
|
||||||
elementLengths = in.readIntArray("elementLengths", null);
|
elementLengths = in.readIntArray("elementLengths", null);
|
||||||
|
@ -333,7 +333,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
protected Type bufType;
|
protected Type bufType;
|
||||||
protected Format format;
|
protected Format format;
|
||||||
protected boolean normalized = false;
|
protected boolean normalized = false;
|
||||||
protected transient boolean instanced = false;
|
protected int instanceSpan = 0;
|
||||||
protected transient boolean dataSizeChanged = false;
|
protected transient boolean dataSizeChanged = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -545,14 +545,39 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* Sets the instanceSpan to 1 or 0 depending on
|
||||||
|
* the value of instanced and the existing value of
|
||||||
|
* instanceSpan.
|
||||||
*/
|
*/
|
||||||
public void setInstanced(boolean instanced) {
|
public void setInstanced(boolean instanced) {
|
||||||
this.instanced = instanced;
|
if( instanced && instanceSpan == 0 ) {
|
||||||
|
instanceSpan = 1;
|
||||||
|
} else if( !instanced ) {
|
||||||
|
instanceSpan = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if instanceSpan is more than 0 indicating
|
||||||
|
* that this vertex buffer contains per-instance data.
|
||||||
|
*/
|
||||||
public boolean isInstanced() {
|
public boolean isInstanced() {
|
||||||
return instanced;
|
return instanceSpan > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets how this vertex buffer matches with rendered instances
|
||||||
|
* where 0 means no instancing at all, ie: all elements are
|
||||||
|
* per vertex. If set to 1 then each element goes with one
|
||||||
|
* instance. If set to 2 then each element goes with two
|
||||||
|
* instances and so on.
|
||||||
|
*/
|
||||||
|
public void setInstanceSpan(int i) {
|
||||||
|
this.instanceSpan = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInstanceSpan() {
|
||||||
|
return instanceSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -587,6 +612,20 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of 'instances' in this VertexBuffer. This
|
||||||
|
* is dependent on the current instanceSpan. When instanceSpan
|
||||||
|
* is 0 then 'instances' is 1. Otherwise, instances is elements *
|
||||||
|
* instanceSpan. It is possible to render a mesh with more instances
|
||||||
|
* but the instance data begins to repeat.
|
||||||
|
*/
|
||||||
|
public int getBaseInstanceCount() {
|
||||||
|
if( instanceSpan == 0 ) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return getNumElements() * instanceSpan;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to initialize the data in the <code>VertexBuffer</code>. Must only
|
* Called to initialize the data in the <code>VertexBuffer</code>. Must only
|
||||||
* be called once.
|
* be called once.
|
||||||
@ -1009,7 +1048,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
vb.handleRef = new Object();
|
vb.handleRef = new Object();
|
||||||
vb.id = -1;
|
vb.id = -1;
|
||||||
vb.normalized = normalized;
|
vb.normalized = normalized;
|
||||||
vb.instanced = instanced;
|
vb.instanceSpan = instanceSpan;
|
||||||
vb.offset = offset;
|
vb.offset = offset;
|
||||||
vb.stride = stride;
|
vb.stride = stride;
|
||||||
vb.updateNeeded = true;
|
vb.updateNeeded = true;
|
||||||
@ -1060,9 +1099,6 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
if (instanced) {
|
|
||||||
throw new IOException("Serialization of instanced data not allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.write(components, "components", 0);
|
oc.write(components, "components", 0);
|
||||||
@ -1072,6 +1108,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
oc.write(normalized, "normalized", false);
|
oc.write(normalized, "normalized", false);
|
||||||
oc.write(offset, "offset", 0);
|
oc.write(offset, "offset", 0);
|
||||||
oc.write(stride, "stride", 0);
|
oc.write(stride, "stride", 0);
|
||||||
|
oc.write(instanceSpan, "instanceSpan", 0);
|
||||||
|
|
||||||
String dataName = "data" + format.name();
|
String dataName = "data" + format.name();
|
||||||
Buffer roData = getDataReadOnly();
|
Buffer roData = getDataReadOnly();
|
||||||
@ -1107,6 +1144,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
normalized = ic.readBoolean("normalized", false);
|
normalized = ic.readBoolean("normalized", false);
|
||||||
offset = ic.readInt("offset", 0);
|
offset = ic.readInt("offset", 0);
|
||||||
stride = ic.readInt("stride", 0);
|
stride = ic.readInt("stride", 0);
|
||||||
|
instanceSpan = ic.readInt("instanceSpan", 0);
|
||||||
componentsLength = components * format.getComponentSize();
|
componentsLength = components * format.getComponentSize();
|
||||||
|
|
||||||
String dataName = "data" + format.name();
|
String dataName = "data" + format.name();
|
||||||
|
@ -39,136 +39,28 @@ import com.jme3.export.Savable;
|
|||||||
import com.jme3.math.Matrix3f;
|
import com.jme3.math.Matrix3f;
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Transform;
|
|
||||||
import com.jme3.renderer.Camera;
|
|
||||||
import com.jme3.renderer.RenderManager;
|
|
||||||
import com.jme3.renderer.ViewPort;
|
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.Mesh;
|
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.VertexBuffer;
|
import com.jme3.scene.VertexBuffer;
|
||||||
import com.jme3.scene.VertexBuffer.Format;
|
import com.jme3.scene.VertexBuffer.Format;
|
||||||
import com.jme3.scene.VertexBuffer.Type;
|
import com.jme3.scene.VertexBuffer.Type;
|
||||||
import com.jme3.scene.VertexBuffer.Usage;
|
import com.jme3.scene.VertexBuffer.Usage;
|
||||||
import com.jme3.scene.control.AbstractControl;
|
|
||||||
import com.jme3.util.BufferUtils;
|
import com.jme3.util.BufferUtils;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>InstancedGeometry</code> allows rendering many similar
|
|
||||||
* geometries efficiently through a feature called geometry
|
|
||||||
* instancing.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* All rendered geometries share material, mesh, and lod level
|
|
||||||
* but have different world transforms or possibly other parameters.
|
|
||||||
* The settings for all instances are inherited from this geometry's
|
|
||||||
* {@link #setMesh(com.jme3.scene.Mesh) mesh},
|
|
||||||
* {@link #setMaterial(com.jme3.material.Material) material} and
|
|
||||||
* {@link #setLodLevel(int) lod level} and cannot be changed per-instance.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* In order to receive any per-instance parameters, the material's shader
|
|
||||||
* must be changed to retrieve per-instance data via
|
|
||||||
* {@link VertexBuffer#setInstanced(boolean) instanced vertex attributes}
|
|
||||||
* or uniform arrays indexed with the GLSL built-in uniform
|
|
||||||
* <code>gl_InstanceID</code>. At the very least, they should use the
|
|
||||||
* functions specified in <code>Instancing.glsllib</code> shader library
|
|
||||||
* to transform vertex positions and normals instead of multiplying by the
|
|
||||||
* built-in matrix uniforms.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This class can operate in two modes, {@link InstancedGeometry.Mode#Auto}
|
|
||||||
* and {@link InstancedGeometry.Mode#Manual}. See the respective enums
|
|
||||||
* for more information</p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Prior to usage, the maximum number of instances must be set via
|
|
||||||
* {@link #setMaxNumInstances(int) } and the current number of instances set
|
|
||||||
* via {@link #setCurrentNumInstances(int) }. The user is then
|
|
||||||
* expected to provide transforms for all instances up to the number
|
|
||||||
* of current instances.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @author Kirill Vainer
|
|
||||||
*/
|
|
||||||
public class InstancedGeometry extends Geometry {
|
public class InstancedGeometry extends Geometry {
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates how the per-instance data is to be specified.
|
|
||||||
*/
|
|
||||||
public static enum Mode {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The user must specify all per-instance transforms and
|
|
||||||
* parameters manually via
|
|
||||||
* {@link InstancedGeometry#setGlobalUserInstanceData(com.jme3.scene.VertexBuffer[]) }
|
|
||||||
* or
|
|
||||||
* {@link InstancedGeometry#setCameraUserInstanceData(com.jme3.renderer.Camera, com.jme3.scene.VertexBuffer) }.
|
|
||||||
*/
|
|
||||||
Manual,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The user
|
|
||||||
* {@link InstancedGeometry#setInstanceTransform(int, com.jme3.math.Transform) provides world transforms}
|
|
||||||
* and then uses the <code>Instancing.glsllib</code> transform functions in the
|
|
||||||
* shader to transform vertex attributes to the respective spaces.
|
|
||||||
* Additional per-instance data can be specified via
|
|
||||||
* {@link InstancedGeometry#setManualGlobalInstanceData(com.jme3.scene.VertexBuffer[]) }.
|
|
||||||
* {@link #setManualCameraInstanceData(com.jme3.renderer.Camera, com.jme3.scene.VertexBuffer) }
|
|
||||||
* cannot be used at this mode since it is computed automatically.
|
|
||||||
*/
|
|
||||||
Auto
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class InstancedGeometryControl extends AbstractControl {
|
|
||||||
|
|
||||||
private InstancedGeometry geom;
|
|
||||||
|
|
||||||
public InstancedGeometryControl() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public InstancedGeometryControl(InstancedGeometry geom) {
|
|
||||||
this.geom = geom;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void controlUpdate(float tpf) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
||||||
geom.renderFromControl(vp.getCamera());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int INSTANCE_SIZE = 16;
|
private static final int INSTANCE_SIZE = 16;
|
||||||
|
|
||||||
private InstancedGeometry.Mode mode;
|
|
||||||
private InstancedGeometryControl control;
|
|
||||||
private int currentNumInstances = 1;
|
|
||||||
private Camera lastCamera = null;
|
|
||||||
private Matrix4f[] worldMatrices = new Matrix4f[1];
|
|
||||||
private VertexBuffer[] globalInstanceData;
|
private VertexBuffer[] globalInstanceData;
|
||||||
|
private VertexBuffer transformInstanceData;
|
||||||
|
private Geometry[] geometries = new Geometry[1];
|
||||||
|
|
||||||
private final HashMap<Camera, VertexBuffer> instanceDataPerCam
|
private int firstUnusedIndex = 0;
|
||||||
= new HashMap<Camera, VertexBuffer>();
|
|
||||||
|
|
||||||
// TODO: determine if perhaps its better to use TempVars here.
|
|
||||||
|
|
||||||
private final Matrix4f tempMat4 = new Matrix4f();
|
|
||||||
private final Matrix4f tempMat4_2 = new Matrix4f();
|
|
||||||
private final Matrix3f tempMat3 = new Matrix3f();
|
|
||||||
private final Quaternion tempQuat = new Quaternion();
|
|
||||||
private final float[] tempFloatArray = new float[16];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization only. Do not use.
|
* Serialization only. Do not use.
|
||||||
@ -176,35 +68,22 @@ public class InstancedGeometry extends Geometry {
|
|||||||
public InstancedGeometry() {
|
public InstancedGeometry() {
|
||||||
super();
|
super();
|
||||||
setIgnoreTransform(true);
|
setIgnoreTransform(true);
|
||||||
|
setBatchHint(BatchHint.Never);
|
||||||
|
setMaxNumInstances(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates instanced geometry with the specified mode and name.
|
* Creates instanced geometry with the specified mode and name.
|
||||||
*
|
*
|
||||||
* @param mode The {@link Mode} at which the instanced geometry operates at.
|
|
||||||
* @param name The name of the spatial.
|
* @param name The name of the spatial.
|
||||||
*
|
*
|
||||||
* @see Mode
|
|
||||||
* @see Spatial#Spatial(java.lang.String)
|
* @see Spatial#Spatial(java.lang.String)
|
||||||
*/
|
*/
|
||||||
public InstancedGeometry(InstancedGeometry.Mode mode, String name) {
|
public InstancedGeometry(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
this.mode = mode;
|
|
||||||
setIgnoreTransform(true);
|
setIgnoreTransform(true);
|
||||||
if (mode == InstancedGeometry.Mode.Auto) {
|
setBatchHint(BatchHint.Never);
|
||||||
control = new InstancedGeometryControl(this);
|
setMaxNumInstances(1);
|
||||||
addControl(control);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The mode with which this instanced geometry was initialized
|
|
||||||
* with. Cannot be changed after initialization.
|
|
||||||
*
|
|
||||||
* @return instanced geometry mode.
|
|
||||||
*/
|
|
||||||
public InstancedGeometry.Mode getMode() {
|
|
||||||
return mode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -238,85 +117,27 @@ public class InstancedGeometry extends Geometry {
|
|||||||
/**
|
/**
|
||||||
* Specify camera specific user per-instance data.
|
* Specify camera specific user per-instance data.
|
||||||
*
|
*
|
||||||
* Only applies when operating in {@link Mode#Manual}.
|
* @param transformInstanceData The transforms for each instance.
|
||||||
* When operating in {@link Mode#Auto}, this data is computed automatically,
|
|
||||||
* and using this method is not allowed.
|
|
||||||
*
|
|
||||||
* @param camera The camera for which per-instance data is to be set.
|
|
||||||
* @param cameraInstanceData The camera's per-instance data.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException If camera is null.
|
|
||||||
* @throws IllegalStateException If {@link #getMode() mode} is set to
|
|
||||||
* {@link Mode#Auto}.
|
|
||||||
*
|
|
||||||
* @see Mode
|
|
||||||
* @see #getCameraUserInstanceData(com.jme3.renderer.Camera)
|
|
||||||
*/
|
*/
|
||||||
public void setCameraUserInstanceData(Camera camera, VertexBuffer cameraInstanceData) {
|
public void setTransformUserInstanceData(VertexBuffer transformInstanceData) {
|
||||||
if (mode == Mode.Auto) {
|
this.transformInstanceData = transformInstanceData;
|
||||||
throw new IllegalStateException("Not allowed in auto mode");
|
|
||||||
}
|
|
||||||
if (camera == null) {
|
|
||||||
throw new IllegalArgumentException("camera cannot be null");
|
|
||||||
}
|
|
||||||
instanceDataPerCam.put(camera, cameraInstanceData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return camera specific user per-instance data.
|
* Return user per-instance transform data.
|
||||||
*
|
*
|
||||||
* Only applies when operating in {@link Mode#Manual}.
|
* @return The per-instance transform data.
|
||||||
* When operating in {@link Mode#Auto}, this data is computed automatically,
|
|
||||||
* and using this method is not allowed.
|
|
||||||
*
|
*
|
||||||
* @param camera The camera to look up the per-instance data for.
|
* @see #setTransformUserInstanceData(com.jme3.scene.VertexBuffer)
|
||||||
* @return The per-instance data, or <code>null</code> if none was specified
|
|
||||||
* for the given camera.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException If camera is null.
|
|
||||||
* @throws IllegalStateException If {@link #getMode() mode} is set to
|
|
||||||
* {@link Mode#Auto}.
|
|
||||||
*
|
|
||||||
* @see Mode
|
|
||||||
* @see #setCameraUserInstanceData(com.jme3.renderer.Camera, com.jme3.scene.VertexBuffer)
|
|
||||||
*/
|
*/
|
||||||
public VertexBuffer getCameraUserInstanceData(Camera camera) {
|
public VertexBuffer getTransformUserInstanceData() {
|
||||||
if (mode == Mode.Auto) {
|
return transformInstanceData;
|
||||||
throw new IllegalStateException("Not allowed in auto mode");
|
|
||||||
}
|
|
||||||
if (camera == null) {
|
|
||||||
throw new IllegalArgumentException("camera cannot be null");
|
|
||||||
}
|
|
||||||
return instanceDataPerCam.get(camera);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void updateInstance(Matrix4f worldMatrix, float[] store,
|
||||||
* Return a read only map with the mappings between cameras and camera
|
int offset, Matrix3f tempMat3,
|
||||||
* specific per-instance data.
|
Quaternion tempQuat) {
|
||||||
*
|
worldMatrix.toRotationMatrix(tempMat3);
|
||||||
* Only applies when operating in {@link Mode#Manual}.
|
|
||||||
* When operating in {@link Mode#Auto}, this data is computed automatically,
|
|
||||||
* and using this method is not allowed.
|
|
||||||
*
|
|
||||||
* @return read only map with the mappings between cameras and camera
|
|
||||||
* specific per-instance data.
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException If {@link #getMode() mode} is set to
|
|
||||||
* {@link Mode#Auto}.
|
|
||||||
*
|
|
||||||
* @see Mode
|
|
||||||
* @see #setCameraUserInstanceData(com.jme3.renderer.Camera, com.jme3.scene.VertexBuffer)
|
|
||||||
*/
|
|
||||||
public Map<Camera, VertexBuffer> getAllCameraUserInstanceData() {
|
|
||||||
if (mode == Mode.Auto) {
|
|
||||||
throw new IllegalStateException("Not allowed in auto mode");
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableMap(instanceDataPerCam);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateInstance(Matrix4f viewMatrix, Matrix4f worldMatrix, float[] store, int offset) {
|
|
||||||
viewMatrix.mult(worldMatrix, tempMat4);
|
|
||||||
tempMat4.toRotationMatrix(tempMat3);
|
|
||||||
tempMat3.invertLocal();
|
tempMat3.invertLocal();
|
||||||
|
|
||||||
// NOTE: No need to take the transpose in order to encode
|
// NOTE: No need to take the transpose in order to encode
|
||||||
@ -326,82 +147,24 @@ public class InstancedGeometry extends Geometry {
|
|||||||
|
|
||||||
// Column-major encoding. The "W" field in each of the encoded
|
// Column-major encoding. The "W" field in each of the encoded
|
||||||
// vectors represents the quaternion.
|
// vectors represents the quaternion.
|
||||||
store[offset + 0] = tempMat4.m00;
|
store[offset + 0] = worldMatrix.m00;
|
||||||
store[offset + 1] = tempMat4.m10;
|
store[offset + 1] = worldMatrix.m10;
|
||||||
store[offset + 2] = tempMat4.m20;
|
store[offset + 2] = worldMatrix.m20;
|
||||||
store[offset + 3] = tempQuat.getX();
|
store[offset + 3] = tempQuat.getX();
|
||||||
store[offset + 4] = tempMat4.m01;
|
store[offset + 4] = worldMatrix.m01;
|
||||||
store[offset + 5] = tempMat4.m11;
|
store[offset + 5] = worldMatrix.m11;
|
||||||
store[offset + 6] = tempMat4.m21;
|
store[offset + 6] = worldMatrix.m21;
|
||||||
store[offset + 7] = tempQuat.getY();
|
store[offset + 7] = tempQuat.getY();
|
||||||
store[offset + 8] = tempMat4.m02;
|
store[offset + 8] = worldMatrix.m02;
|
||||||
store[offset + 9] = tempMat4.m12;
|
store[offset + 9] = worldMatrix.m12;
|
||||||
store[offset + 10] = tempMat4.m22;
|
store[offset + 10] = worldMatrix.m22;
|
||||||
store[offset + 11] = tempQuat.getZ();
|
store[offset + 11] = tempQuat.getZ();
|
||||||
store[offset + 12] = tempMat4.m03;
|
store[offset + 12] = worldMatrix.m03;
|
||||||
store[offset + 13] = tempMat4.m13;
|
store[offset + 13] = worldMatrix.m13;
|
||||||
store[offset + 14] = tempMat4.m23;
|
store[offset + 14] = worldMatrix.m23;
|
||||||
store[offset + 15] = tempQuat.getW();
|
store[offset + 15] = tempQuat.getW();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderFromControl(Camera cam) {
|
|
||||||
if (mode != Mode.Auto) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the instance data VBO for this camera.
|
|
||||||
VertexBuffer instanceDataVB = instanceDataPerCam.get(cam);
|
|
||||||
FloatBuffer instanceData;
|
|
||||||
|
|
||||||
if (instanceDataVB == null) {
|
|
||||||
// This is a new camera, create instance data VBO for it.
|
|
||||||
instanceData = BufferUtils.createFloatBuffer(worldMatrices.length * INSTANCE_SIZE);
|
|
||||||
instanceDataVB = new VertexBuffer(Type.InstanceData);
|
|
||||||
instanceDataVB.setInstanced(true);
|
|
||||||
instanceDataVB.setupData(Usage.Stream, INSTANCE_SIZE, Format.Float, instanceData);
|
|
||||||
instanceDataPerCam.put(cam, instanceDataVB);
|
|
||||||
} else {
|
|
||||||
// Retrieve the current instance data buffer.
|
|
||||||
instanceData = (FloatBuffer) instanceDataVB.getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
Matrix4f viewMatrix = cam.getViewMatrix();
|
|
||||||
|
|
||||||
instanceData.limit(instanceData.capacity());
|
|
||||||
instanceData.position(0);
|
|
||||||
|
|
||||||
assert currentNumInstances <= worldMatrices.length;
|
|
||||||
|
|
||||||
for (int i = 0; i < currentNumInstances; i++) {
|
|
||||||
Matrix4f worldMatrix = worldMatrices[i];
|
|
||||||
if (worldMatrix == null) {
|
|
||||||
worldMatrix = Matrix4f.IDENTITY;
|
|
||||||
}
|
|
||||||
updateInstance(viewMatrix, worldMatrix, tempFloatArray, 0);
|
|
||||||
instanceData.put(tempFloatArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceData.flip();
|
|
||||||
|
|
||||||
this.lastCamera = cam;
|
|
||||||
instanceDataVB.updateData(instanceDataVB.getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current number of instances to be rendered.
|
|
||||||
*
|
|
||||||
* @param currentNumInstances the current number of instances to be rendered.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException If current number of instances is
|
|
||||||
* greater than the maximum number of instances.
|
|
||||||
*/
|
|
||||||
public void setCurrentNumInstances(int currentNumInstances) {
|
|
||||||
if (currentNumInstances > worldMatrices.length) {
|
|
||||||
throw new IllegalArgumentException("currentNumInstances cannot be larger than maxNumInstances");
|
|
||||||
}
|
|
||||||
this.currentNumInstances = currentNumInstances;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the maximum amount of instances that can be rendered by this
|
* Set the maximum amount of instances that can be rendered by this
|
||||||
* instanced geometry when mode is set to auto.
|
* instanced geometry when mode is set to auto.
|
||||||
@ -415,88 +178,168 @@ public class InstancedGeometry extends Geometry {
|
|||||||
* @throws IllegalStateException If mode is set to manual.
|
* @throws IllegalStateException If mode is set to manual.
|
||||||
* @throws IllegalArgumentException If maxNumInstances is zero or negative
|
* @throws IllegalArgumentException If maxNumInstances is zero or negative
|
||||||
*/
|
*/
|
||||||
public void setMaxNumInstances(int maxNumInstances) {
|
public final void setMaxNumInstances(int maxNumInstances) {
|
||||||
if (mode == Mode.Manual) {
|
|
||||||
throw new IllegalStateException("Not allowed in manual mode");
|
|
||||||
}
|
|
||||||
if (maxNumInstances < 1) {
|
if (maxNumInstances < 1) {
|
||||||
throw new IllegalArgumentException("maxNumInstances must be 1 or higher");
|
throw new IllegalArgumentException("maxNumInstances must be 1 or higher");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.worldMatrices = new Matrix4f[maxNumInstances];
|
Geometry[] originalGeometries = geometries;
|
||||||
|
this.geometries = new Geometry[maxNumInstances];
|
||||||
|
|
||||||
if (currentNumInstances > maxNumInstances) {
|
if (originalGeometries != null) {
|
||||||
currentNumInstances = maxNumInstances;
|
System.arraycopy(originalGeometries, 0, geometries, 0, originalGeometries.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize instance data for each of the cameras.
|
// Resize instance data.
|
||||||
for (VertexBuffer instanceDataVB : instanceDataPerCam.values()) {
|
if (transformInstanceData != null) {
|
||||||
FloatBuffer instanceData = (FloatBuffer) instanceDataVB.getData();
|
BufferUtils.destroyDirectBuffer(transformInstanceData.getData());
|
||||||
if (instanceData.capacity() / INSTANCE_SIZE != worldMatrices.length) {
|
transformInstanceData.updateData(BufferUtils.createFloatBuffer(geometries.length * INSTANCE_SIZE));
|
||||||
// Delete old data.
|
} else if (transformInstanceData == null) {
|
||||||
BufferUtils.destroyDirectBuffer(instanceData);
|
transformInstanceData = new VertexBuffer(Type.InstanceData);
|
||||||
|
transformInstanceData.setInstanced(true);
|
||||||
// Resize instance data for this camera.
|
transformInstanceData.setupData(Usage.Stream,
|
||||||
// Create new data with new length.
|
INSTANCE_SIZE,
|
||||||
instanceData = BufferUtils.createFloatBuffer(worldMatrices.length * INSTANCE_SIZE);
|
Format.Float,
|
||||||
instanceDataVB.updateData(instanceData);
|
BufferUtils.createFloatBuffer(geometries.length * INSTANCE_SIZE));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxNumInstances() {
|
public int getMaxNumInstances() {
|
||||||
return worldMatrices.length;
|
return geometries.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCurrentNumInstances() {
|
public int getActualNumInstances() {
|
||||||
return currentNumInstances;
|
return firstUnusedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInstanceTransform(int instanceIndex, Matrix4f worldTransform) {
|
private void swap(int idx1, int idx2) {
|
||||||
if (mode == Mode.Manual) {
|
Geometry g = geometries[idx1];
|
||||||
throw new IllegalStateException("Not allowed in manual mode");
|
geometries[idx1] = geometries[idx2];
|
||||||
|
geometries[idx2] = g;
|
||||||
|
|
||||||
|
if (geometries[idx1] != null) {
|
||||||
|
InstancedNode.setGeometryStartIndex2(geometries[idx1], idx1);
|
||||||
}
|
}
|
||||||
if (worldTransform == null) {
|
if (geometries[idx2] != null) {
|
||||||
throw new IllegalArgumentException("worldTransform cannot be null");
|
InstancedNode.setGeometryStartIndex2(geometries[idx2], idx2);
|
||||||
}
|
}
|
||||||
if (instanceIndex < 0) {
|
|
||||||
throw new IllegalArgumentException("instanceIndex cannot be smaller than zero");
|
|
||||||
}
|
|
||||||
if (instanceIndex >= currentNumInstances) {
|
|
||||||
throw new IllegalArgumentException("instanceIndex cannot be larger than currentNumInstances");
|
|
||||||
}
|
|
||||||
// TODO: Determine if need to make a copy of matrix or just doing this
|
|
||||||
// is fine.
|
|
||||||
worldMatrices[instanceIndex] = worldTransform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInstanceTransform(int instanceIndex, Transform worldTransform) {
|
private void sanitize(boolean insideEntriesNonNull) {
|
||||||
if (worldTransform == null) {
|
if (firstUnusedIndex >= geometries.length) {
|
||||||
throw new IllegalArgumentException("worldTransform cannot be null");
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < geometries.length; i++) {
|
||||||
|
if (i < firstUnusedIndex) {
|
||||||
|
if (geometries[i] == null) {
|
||||||
|
if (insideEntriesNonNull) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
} else if (InstancedNode.getGeometryStartIndex2(geometries[i]) != i) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (geometries[i] != null) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the world transform matrix.
|
public void updateInstances() {
|
||||||
tempMat4.loadIdentity();
|
FloatBuffer fb = (FloatBuffer) transformInstanceData.getData();
|
||||||
tempMat4.setRotationQuaternion(worldTransform.getRotation());
|
fb.limit(fb.capacity());
|
||||||
tempMat4.setTranslation(worldTransform.getTranslation());
|
fb.position(0);
|
||||||
tempMat4_2.loadIdentity();
|
|
||||||
tempMat4_2.scale(worldTransform.getScale());
|
|
||||||
tempMat4.multLocal(tempMat4_2);
|
|
||||||
|
|
||||||
setInstanceTransform(instanceIndex, tempMat4.clone());
|
TempVars vars = TempVars.get();
|
||||||
|
{
|
||||||
|
float[] temp = vars.matrixWrite;
|
||||||
|
|
||||||
|
for (int i = 0; i < firstUnusedIndex; i++) {
|
||||||
|
Geometry geom = geometries[i];
|
||||||
|
|
||||||
|
if (geom == null) {
|
||||||
|
geom = geometries[firstUnusedIndex - 1];
|
||||||
|
|
||||||
|
if (geom == null) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
swap(i, firstUnusedIndex - 1);
|
||||||
|
|
||||||
|
while (geometries[firstUnusedIndex -1] == null) {
|
||||||
|
firstUnusedIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4f worldMatrix = geom.getWorldMatrix();
|
||||||
|
updateInstance(worldMatrix, temp, 0, vars.tempMat3, vars.quat1);
|
||||||
|
fb.put(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
|
||||||
|
fb.flip();
|
||||||
|
|
||||||
|
if (fb.limit() / INSTANCE_SIZE != firstUnusedIndex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
transformInstanceData.updateData(fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteInstance(Geometry geom) {
|
||||||
|
int idx = InstancedNode.getGeometryStartIndex2(geom);
|
||||||
|
InstancedNode.setGeometryStartIndex2(geom, -1);
|
||||||
|
|
||||||
|
geometries[idx] = null;
|
||||||
|
|
||||||
|
if (idx == firstUnusedIndex - 1) {
|
||||||
|
// Deleting the last element.
|
||||||
|
// Move index back.
|
||||||
|
firstUnusedIndex--;
|
||||||
|
while (geometries[firstUnusedIndex] == null) {
|
||||||
|
firstUnusedIndex--;
|
||||||
|
if (firstUnusedIndex < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
firstUnusedIndex++;
|
||||||
|
} else {
|
||||||
|
// Deleting element in the middle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInstance(Geometry geometry) {
|
||||||
|
if (geometry == null) {
|
||||||
|
throw new IllegalArgumentException("geometry cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take an index from the end.
|
||||||
|
if (firstUnusedIndex + 1 >= geometries.length) {
|
||||||
|
// No more room.
|
||||||
|
setMaxNumInstances(getMaxNumInstances() * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int freeIndex = firstUnusedIndex;
|
||||||
|
firstUnusedIndex++;
|
||||||
|
|
||||||
|
geometries[freeIndex] = geometry;
|
||||||
|
InstancedNode.setGeometryStartIndex2(geometry, freeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Geometry[] getGeometries() {
|
||||||
|
return geometries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VertexBuffer[] getAllInstanceData() {
|
public VertexBuffer[] getAllInstanceData() {
|
||||||
VertexBuffer instanceDataForCam = instanceDataPerCam.get(lastCamera);
|
|
||||||
ArrayList<VertexBuffer> allData = new ArrayList();
|
ArrayList<VertexBuffer> allData = new ArrayList();
|
||||||
|
if (transformInstanceData != null) {
|
||||||
if (instanceDataForCam != null) {
|
allData.add(transformInstanceData);
|
||||||
allData.add(instanceDataForCam);
|
|
||||||
}
|
}
|
||||||
if (globalInstanceData != null) {
|
if (globalInstanceData != null) {
|
||||||
allData.addAll(Arrays.asList(globalInstanceData));
|
allData.addAll(Arrays.asList(globalInstanceData));
|
||||||
}
|
}
|
||||||
|
|
||||||
return allData.toArray(new VertexBuffer[allData.size()]);
|
return allData.toArray(new VertexBuffer[allData.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,30 +347,20 @@ public class InstancedGeometry extends Geometry {
|
|||||||
public void write(JmeExporter exporter) throws IOException {
|
public void write(JmeExporter exporter) throws IOException {
|
||||||
super.write(exporter);
|
super.write(exporter);
|
||||||
OutputCapsule capsule = exporter.getCapsule(this);
|
OutputCapsule capsule = exporter.getCapsule(this);
|
||||||
capsule.write(currentNumInstances, "cur_num_instances", 1);
|
//capsule.write(currentNumInstances, "cur_num_instances", 1);
|
||||||
capsule.write(mode, "instancing_mode", InstancedGeometry.Mode.Auto);
|
capsule.write(geometries, "geometries", null);
|
||||||
if (mode == Mode.Auto) {
|
|
||||||
capsule.write(worldMatrices, "world_matrices", null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(JmeImporter importer) throws IOException {
|
public void read(JmeImporter importer) throws IOException {
|
||||||
super.read(importer);
|
super.read(importer);
|
||||||
InputCapsule capsule = importer.getCapsule(this);
|
InputCapsule capsule = importer.getCapsule(this);
|
||||||
currentNumInstances = capsule.readInt("cur_num_instances", 1);
|
//currentNumInstances = capsule.readInt("cur_num_instances", 1);
|
||||||
mode = capsule.readEnum("instancing_mode", InstancedGeometry.Mode.class,
|
|
||||||
InstancedGeometry.Mode.Auto);
|
|
||||||
|
|
||||||
if (mode == Mode.Auto) {
|
Savable[] geometrySavables = capsule.readSavableArray("geometries", null);
|
||||||
Savable[] matrixSavables = capsule.readSavableArray("world_matrices", null);
|
geometries = new Geometry[geometrySavables.length];
|
||||||
worldMatrices = new Matrix4f[matrixSavables.length];
|
for (int i = 0; i < geometrySavables.length; i++) {
|
||||||
for (int i = 0; i < worldMatrices.length; i++) {
|
geometries[i] = (Geometry) geometrySavables[i];
|
||||||
worldMatrices[i] = (Matrix4f) matrixSavables[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
control = getControl(InstancedGeometryControl.class);
|
|
||||||
control.geom = this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,335 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 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.scene.instancing;
|
||||||
|
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.ViewPort;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.GeometryGroupNode;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.UserData;
|
||||||
|
import com.jme3.scene.control.Control;
|
||||||
|
import com.jme3.export.JmeExporter;
|
||||||
|
import com.jme3.export.JmeImporter;
|
||||||
|
import com.jme3.material.MatParam;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class InstancedNode extends GeometryGroupNode {
|
||||||
|
|
||||||
|
static int getGeometryStartIndex2(Geometry geom) {
|
||||||
|
return getGeometryStartIndex(geom);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setGeometryStartIndex2(Geometry geom, int startIndex) {
|
||||||
|
setGeometryStartIndex(geom, startIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InstanceTypeKey implements Cloneable {
|
||||||
|
|
||||||
|
Mesh mesh;
|
||||||
|
Material material;
|
||||||
|
int lodLevel;
|
||||||
|
|
||||||
|
public InstanceTypeKey(Mesh mesh, Material material, int lodLevel) {
|
||||||
|
this.mesh = mesh;
|
||||||
|
this.material = material;
|
||||||
|
this.lodLevel = lodLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstanceTypeKey(){
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 3;
|
||||||
|
hash = 41 * hash + this.mesh.hashCode();
|
||||||
|
hash = 41 * hash + this.material.hashCode();
|
||||||
|
hash = 41 * hash + this.lodLevel;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
final InstanceTypeKey other = (InstanceTypeKey) obj;
|
||||||
|
if (this.mesh != other.mesh) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.material != other.material) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.lodLevel != other.lodLevel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstanceTypeKey clone() {
|
||||||
|
try {
|
||||||
|
return (InstanceTypeKey) super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InstancedNodeControl implements Control {
|
||||||
|
|
||||||
|
private InstancedNode node;
|
||||||
|
|
||||||
|
public InstancedNodeControl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstancedNodeControl(InstancedNode node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Control cloneForSpatial(Spatial spatial) {
|
||||||
|
return this;
|
||||||
|
// WARNING: Sets wrong control on spatial. Will be
|
||||||
|
// fixed automatically by InstancedNode.clone() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpatial(Spatial spatial){
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(float tpf){
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(RenderManager rm, ViewPort vp) {
|
||||||
|
node.renderFromControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(JmeExporter ex) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void read(JmeImporter im) throws IOException {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InstancedNodeControl control;
|
||||||
|
|
||||||
|
protected HashMap<Geometry, InstancedGeometry> igByGeom
|
||||||
|
= new HashMap<Geometry, InstancedGeometry>();
|
||||||
|
|
||||||
|
private InstanceTypeKey lookUp = new InstanceTypeKey();
|
||||||
|
|
||||||
|
private HashMap<InstanceTypeKey, InstancedGeometry> instancesMap =
|
||||||
|
new HashMap<InstanceTypeKey, InstancedGeometry>();
|
||||||
|
|
||||||
|
public InstancedNode() {
|
||||||
|
super();
|
||||||
|
// NOTE: since we are deserializing,
|
||||||
|
// the control is going to be added automatically here.
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstancedNode(String name) {
|
||||||
|
super(name);
|
||||||
|
control = new InstancedNodeControl(this);
|
||||||
|
addControl(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderFromControl() {
|
||||||
|
for (InstancedGeometry ig : instancesMap.values()) {
|
||||||
|
ig.updateInstances();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InstancedGeometry lookUpByGeometry(Geometry geom) {
|
||||||
|
lookUp.mesh = geom.getMesh();
|
||||||
|
lookUp.material = geom.getMaterial();
|
||||||
|
lookUp.lodLevel = geom.getLodLevel();
|
||||||
|
|
||||||
|
InstancedGeometry ig = instancesMap.get(lookUp);
|
||||||
|
|
||||||
|
if (ig == null) {
|
||||||
|
ig = new InstancedGeometry(
|
||||||
|
"mesh-" + System.identityHashCode(lookUp.mesh) + "," +
|
||||||
|
"material-" + lookUp.material.getMaterialDef().getName() + ","
|
||||||
|
+ "lod-" + lookUp.lodLevel);
|
||||||
|
ig.setMaterial(lookUp.material);
|
||||||
|
ig.setMesh(lookUp.mesh);
|
||||||
|
ig.setUserData(UserData.JME_PHYSICSIGNORE, true);
|
||||||
|
ig.setCullHint(CullHint.Never);
|
||||||
|
instancesMap.put(lookUp.clone(), ig);
|
||||||
|
attachChild(ig);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToInstancedGeometry(Geometry geom) {
|
||||||
|
Material material = geom.getMaterial();
|
||||||
|
MatParam param = material.getParam("UseInstancing");
|
||||||
|
if (param == null || !((Boolean)param.getValue()).booleanValue()) {
|
||||||
|
throw new IllegalStateException("You must set the 'UseInstancing' "
|
||||||
|
+ "parameter to true on the material prior "
|
||||||
|
+ "to adding it to InstancedNode");
|
||||||
|
}
|
||||||
|
|
||||||
|
InstancedGeometry ig = lookUpByGeometry(geom);
|
||||||
|
igByGeom.put(geom, ig);
|
||||||
|
geom.associateWithGroupNode(this, 0);
|
||||||
|
ig.addInstance(geom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFromInstancedGeometry(Geometry geom) {
|
||||||
|
InstancedGeometry ig = igByGeom.remove(geom);
|
||||||
|
if (ig != null) {
|
||||||
|
ig.deleteInstance(geom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void relocateInInstancedGeometry(Geometry geom) {
|
||||||
|
InstancedGeometry oldIG = igByGeom.get(geom);
|
||||||
|
InstancedGeometry newIG = lookUpByGeometry(geom);
|
||||||
|
if (oldIG != newIG) {
|
||||||
|
if (oldIG == null) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
oldIG.deleteInstance(geom);
|
||||||
|
newIG.addInstance(geom);
|
||||||
|
igByGeom.put(geom, newIG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ungroupSceneGraph(Spatial s) {
|
||||||
|
if (s instanceof Node) {
|
||||||
|
for (Spatial sp : ((Node) s).getChildren()) {
|
||||||
|
ungroupSceneGraph(sp);
|
||||||
|
}
|
||||||
|
} else if (s instanceof Geometry) {
|
||||||
|
Geometry g = (Geometry) s;
|
||||||
|
if (g.isGrouped()) {
|
||||||
|
// Will invoke onGeometryUnassociated automatically.
|
||||||
|
g.unassociateFromGroupNode();
|
||||||
|
|
||||||
|
if (InstancedNode.getGeometryStartIndex(g) != -1) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spatial detachChildAt(int index) {
|
||||||
|
Spatial s = super.detachChildAt(index);
|
||||||
|
if (s instanceof Node) {
|
||||||
|
ungroupSceneGraph(s);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void instance(Spatial n) {
|
||||||
|
if (n instanceof Geometry) {
|
||||||
|
Geometry g = (Geometry) n;
|
||||||
|
if (!g.isGrouped() && g.getBatchHint() != BatchHint.Never) {
|
||||||
|
addToInstancedGeometry(g);
|
||||||
|
}
|
||||||
|
} else if (n instanceof Node) {
|
||||||
|
for (Spatial child : ((Node) n).getChildren()) {
|
||||||
|
if (child instanceof GeometryGroupNode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
instance(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void instance() {
|
||||||
|
instance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node clone() {
|
||||||
|
return clone(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node clone(boolean cloneMaterials) {
|
||||||
|
InstancedNode clone = (InstancedNode)super.clone(cloneMaterials);
|
||||||
|
|
||||||
|
if (instancesMap.size() > 0) {
|
||||||
|
// Remove all instanced geometries from the clone
|
||||||
|
for (int i = 0; i < clone.children.size(); i++) {
|
||||||
|
if (clone.children.get(i) instanceof InstancedGeometry) {
|
||||||
|
clone.children.remove(i);
|
||||||
|
} else if (clone.children.get(i) instanceof Geometry) {
|
||||||
|
Geometry geom = (Geometry) clone.children.get(i);
|
||||||
|
if (geom.isGrouped()) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove original control from the clone
|
||||||
|
clone.controls.remove(this.control);
|
||||||
|
|
||||||
|
// put clone's control in
|
||||||
|
clone.control = new InstancedNodeControl(clone);
|
||||||
|
clone.controls.add(clone.control);
|
||||||
|
|
||||||
|
clone.lookUp = new InstanceTypeKey();
|
||||||
|
clone.igByGeom = new HashMap<Geometry, InstancedGeometry>();
|
||||||
|
clone.instancesMap = new HashMap<InstanceTypeKey, InstancedGeometry>();
|
||||||
|
|
||||||
|
clone.instance();
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransformChange(Geometry geom) {
|
||||||
|
// Handled automatically
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMaterialChange(Geometry geom) {
|
||||||
|
relocateInInstancedGeometry(geom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMeshChange(Geometry geom) {
|
||||||
|
relocateInInstancedGeometry(geom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeoemtryUnassociated(Geometry geom) {
|
||||||
|
removeFromInstancedGeometry(geom);
|
||||||
|
}
|
||||||
|
}
|
@ -132,6 +132,8 @@ MaterialDef Phong Lighting {
|
|||||||
// For hardware skinning
|
// For hardware skinning
|
||||||
Int NumberOfBones
|
Int NumberOfBones
|
||||||
Matrix4Array BoneMatrices
|
Matrix4Array BoneMatrices
|
||||||
|
|
||||||
|
Boolean UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
Technique {
|
Technique {
|
||||||
@ -148,6 +150,7 @@ MaterialDef Phong Lighting {
|
|||||||
ViewMatrix
|
ViewMatrix
|
||||||
CameraPosition
|
CameraPosition
|
||||||
WorldMatrix
|
WorldMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
@ -177,6 +180,8 @@ MaterialDef Phong Lighting {
|
|||||||
SPHERE_MAP : SphereMap
|
SPHERE_MAP : SphereMap
|
||||||
|
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,12 +193,15 @@ MaterialDef Phong Lighting {
|
|||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldViewMatrix
|
WorldViewMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
COLOR_MAP : ColorMap
|
COLOR_MAP : ColorMap
|
||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -214,6 +222,8 @@ MaterialDef Phong Lighting {
|
|||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldMatrix
|
WorldMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
@ -227,6 +237,7 @@ MaterialDef Phong Lighting {
|
|||||||
PSSM : Splits
|
PSSM : Splits
|
||||||
POINTLIGHT : LightViewProjectionMatrix5
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -243,6 +254,8 @@ MaterialDef Phong Lighting {
|
|||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldMatrix
|
WorldMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
@ -256,6 +269,7 @@ MaterialDef Phong Lighting {
|
|||||||
PSSM : Splits
|
PSSM : Splits
|
||||||
POINTLIGHT : LightViewProjectionMatrix5
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -274,11 +288,14 @@ MaterialDef Phong Lighting {
|
|||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldViewMatrix
|
WorldViewMatrix
|
||||||
NormalMatrix
|
NormalMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
DIFFUSEMAP_ALPHA : DiffuseMap
|
DIFFUSEMAP_ALPHA : DiffuseMap
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -293,11 +310,14 @@ MaterialDef Phong Lighting {
|
|||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldViewMatrix
|
WorldViewMatrix
|
||||||
NormalMatrix
|
NormalMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
DIFFUSEMAP_ALPHA : DiffuseMap
|
DIFFUSEMAP_ALPHA : DiffuseMap
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -339,6 +359,8 @@ MaterialDef Phong Lighting {
|
|||||||
|
|
||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
@ -347,6 +369,7 @@ MaterialDef Phong Lighting {
|
|||||||
HAS_GLOWCOLOR : GlowColor
|
HAS_GLOWCOLOR : GlowColor
|
||||||
|
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#define ATTENUATION
|
#define ATTENUATION
|
||||||
//#define HQ_ATTENUATION
|
//#define HQ_ATTENUATION
|
||||||
|
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
|
|
||||||
uniform mat4 g_WorldViewProjectionMatrix;
|
/*
|
||||||
uniform mat4 g_WorldViewMatrix;
|
uniform mat4 g_WorldViewProjectionMatrix;
|
||||||
uniform mat3 g_NormalMatrix;
|
uniform mat4 g_WorldViewMatrix;
|
||||||
uniform mat4 g_ViewMatrix;
|
uniform mat3 g_NormalMatrix;
|
||||||
|
uniform mat4 g_ViewMatrix;
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
uniform vec4 m_Ambient;
|
uniform vec4 m_Ambient;
|
||||||
uniform vec4 m_Diffuse;
|
uniform vec4 m_Diffuse;
|
||||||
@ -148,14 +152,14 @@ void main(){
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gl_Position = g_WorldViewProjectionMatrix * modelSpacePos;
|
gl_Position = TransformWorldViewProjection(modelSpacePos);// g_WorldViewProjectionMatrix * modelSpacePos;
|
||||||
texCoord = inTexCoord;
|
texCoord = inTexCoord;
|
||||||
#ifdef SEPARATE_TEXCOORD
|
#ifdef SEPARATE_TEXCOORD
|
||||||
texCoord2 = inTexCoord2;
|
texCoord2 = inTexCoord2;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vec3 wvPosition = (g_WorldViewMatrix * modelSpacePos).xyz;
|
vec3 wvPosition = TransformWorldView(modelSpacePos).xyz;// (g_WorldViewMatrix * modelSpacePos).xyz;
|
||||||
vec3 wvNormal = normalize(g_NormalMatrix * modelSpaceNorm);
|
vec3 wvNormal = normalize(TransformNormal(modelSpaceNorm));//normalize(g_NormalMatrix * modelSpaceNorm);
|
||||||
vec3 viewDir = normalize(-wvPosition);
|
vec3 viewDir = normalize(-wvPosition);
|
||||||
|
|
||||||
//vec4 lightColor = g_LightColor[gl_InstanceID];
|
//vec4 lightColor = g_LightColor[gl_InstanceID];
|
||||||
@ -168,7 +172,7 @@ void main(){
|
|||||||
vec4 lightColor = g_LightColor;
|
vec4 lightColor = g_LightColor;
|
||||||
|
|
||||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||||
vec3 wvTangent = normalize(g_NormalMatrix * modelSpaceTan);
|
vec3 wvTangent = normalize(TransformNormal(modelSpaceTan));
|
||||||
vec3 wvBinormal = cross(wvNormal, wvTangent);
|
vec3 wvBinormal = cross(wvNormal, wvTangent);
|
||||||
|
|
||||||
mat3 tbnMat = mat3(wvTangent, wvBinormal * inTangent.w,wvNormal);
|
mat3 tbnMat = mat3(wvTangent, wvBinormal * inTangent.w,wvNormal);
|
||||||
@ -187,7 +191,7 @@ void main(){
|
|||||||
lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
|
lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
|
||||||
|
|
||||||
#ifdef V_TANGENT
|
#ifdef V_TANGENT
|
||||||
vNormal = normalize(g_NormalMatrix * inTangent.xyz);
|
vNormal = normalize(TransformNormal(inTangent.xyz));
|
||||||
vNormal = -cross(cross(vLightDir.xyz, vNormal), vNormal);
|
vNormal = -cross(cross(vLightDir.xyz, vNormal), vNormal);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,6 +10,8 @@ MaterialDef Debug Normals {
|
|||||||
|
|
||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
ProjectionMatrix
|
ProjectionMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,8 @@ MaterialDef Unshaded {
|
|||||||
|
|
||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
ProjectionMatrix
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
@ -86,10 +87,13 @@ MaterialDef Unshaded {
|
|||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldViewMatrix
|
WorldViewMatrix
|
||||||
NormalMatrix
|
NormalMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,12 +105,15 @@ MaterialDef Unshaded {
|
|||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldViewMatrix
|
WorldViewMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
COLOR_MAP : ColorMap
|
COLOR_MAP : ColorMap
|
||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -127,6 +134,8 @@ MaterialDef Unshaded {
|
|||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldMatrix
|
WorldMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
@ -140,6 +149,7 @@ MaterialDef Unshaded {
|
|||||||
PSSM : Splits
|
PSSM : Splits
|
||||||
POINTLIGHT : LightViewProjectionMatrix5
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -156,6 +166,8 @@ MaterialDef Unshaded {
|
|||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
WorldMatrix
|
WorldMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
@ -169,6 +181,7 @@ MaterialDef Unshaded {
|
|||||||
PSSM : Splits
|
PSSM : Splits
|
||||||
POINTLIGHT : LightViewProjectionMatrix5
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -185,6 +198,8 @@ MaterialDef Unshaded {
|
|||||||
|
|
||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
|
ViewProjectionMatrix
|
||||||
|
ViewMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
@ -192,6 +207,7 @@ MaterialDef Unshaded {
|
|||||||
HAS_GLOWMAP : GlowMap
|
HAS_GLOWMAP : GlowMap
|
||||||
HAS_GLOWCOLOR : GlowColor
|
HAS_GLOWCOLOR : GlowColor
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,6 +29,7 @@ MaterialDef Post Shadow {
|
|||||||
Float PCFEdge
|
Float PCFEdge
|
||||||
|
|
||||||
Float ShadowMapSize
|
Float ShadowMapSize
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Technique {
|
Technique {
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
uniform mat4 m_LightViewProjectionMatrix0;
|
uniform mat4 m_LightViewProjectionMatrix0;
|
||||||
uniform mat4 m_LightViewProjectionMatrix1;
|
uniform mat4 m_LightViewProjectionMatrix1;
|
||||||
uniform mat4 m_LightViewProjectionMatrix2;
|
uniform mat4 m_LightViewProjectionMatrix2;
|
||||||
uniform mat4 m_LightViewProjectionMatrix3;
|
uniform mat4 m_LightViewProjectionMatrix3;
|
||||||
|
|
||||||
uniform mat4 g_WorldViewProjectionMatrix;
|
|
||||||
uniform mat4 g_WorldMatrix;
|
|
||||||
uniform mat4 g_ViewMatrix;
|
|
||||||
uniform vec3 m_LightPos;
|
uniform vec3 m_LightPos;
|
||||||
|
|
||||||
varying vec4 projCoord0;
|
varying vec4 projCoord0;
|
||||||
@ -52,7 +50,7 @@ void main(){
|
|||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
Skinning_Compute(modelSpacePos);
|
Skinning_Compute(modelSpacePos);
|
||||||
#endif
|
#endif
|
||||||
gl_Position = g_WorldViewProjectionMatrix * modelSpacePos;
|
gl_Position = TransformWorldViewProjection(modelSpacePos);
|
||||||
|
|
||||||
#ifndef POINTLIGHT
|
#ifndef POINTLIGHT
|
||||||
#ifdef PSSM
|
#ifdef PSSM
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
uniform mat4 m_LightViewProjectionMatrix0;
|
uniform mat4 m_LightViewProjectionMatrix0;
|
||||||
uniform mat4 m_LightViewProjectionMatrix1;
|
uniform mat4 m_LightViewProjectionMatrix1;
|
||||||
uniform mat4 m_LightViewProjectionMatrix2;
|
uniform mat4 m_LightViewProjectionMatrix2;
|
||||||
uniform mat4 m_LightViewProjectionMatrix3;
|
uniform mat4 m_LightViewProjectionMatrix3;
|
||||||
|
|
||||||
uniform mat4 g_WorldViewProjectionMatrix;
|
|
||||||
uniform mat4 g_WorldMatrix;
|
|
||||||
|
|
||||||
out vec4 projCoord0;
|
out vec4 projCoord0;
|
||||||
out vec4 projCoord1;
|
out vec4 projCoord1;
|
||||||
@ -51,7 +50,7 @@ void main(){
|
|||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
Skinning_Compute(modelSpacePos);
|
Skinning_Compute(modelSpacePos);
|
||||||
#endif
|
#endif
|
||||||
gl_Position = g_WorldViewProjectionMatrix * modelSpacePos;
|
gl_Position = TransformWorldViewProjection(modelSpacePos);
|
||||||
|
|
||||||
#ifndef POINTLIGHT
|
#ifndef POINTLIGHT
|
||||||
#ifdef PSSM
|
#ifdef PSSM
|
||||||
@ -60,7 +59,7 @@ void main(){
|
|||||||
vec4 worldPos=vec4(0.0);
|
vec4 worldPos=vec4(0.0);
|
||||||
#endif
|
#endif
|
||||||
// get the vertex in world space
|
// get the vertex in world space
|
||||||
worldPos = g_WorldMatrix * modelSpacePos;
|
worldPos = TransformWorld(modelSpacePos);
|
||||||
|
|
||||||
#ifdef DISCARD_ALPHA
|
#ifdef DISCARD_ALPHA
|
||||||
texCoord = inTexCoord;
|
texCoord = inTexCoord;
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
attribute vec3 inPosition;
|
attribute vec3 inPosition;
|
||||||
attribute vec2 inTexCoord;
|
attribute vec2 inTexCoord;
|
||||||
|
|
||||||
uniform mat4 g_WorldViewProjectionMatrix;
|
|
||||||
uniform mat4 g_WorldViewMatrix;
|
|
||||||
|
|
||||||
varying vec2 texCoord;
|
varying vec2 texCoord;
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
@ -13,6 +11,6 @@ void main(){
|
|||||||
#ifdef NUM_BONES
|
#ifdef NUM_BONES
|
||||||
Skinning_Compute(modelSpacePos);
|
Skinning_Compute(modelSpacePos);
|
||||||
#endif
|
#endif
|
||||||
gl_Position = g_WorldViewProjectionMatrix * modelSpacePos;
|
gl_Position = TransformWorldViewProjection(modelSpacePos);
|
||||||
texCoord = inTexCoord;
|
texCoord = inTexCoord;
|
||||||
}
|
}
|
@ -23,6 +23,7 @@ uniform mat4 g_ViewMatrix;
|
|||||||
uniform mat4 g_ProjectionMatrix;
|
uniform mat4 g_ProjectionMatrix;
|
||||||
uniform mat4 g_WorldViewMatrix;
|
uniform mat4 g_WorldViewMatrix;
|
||||||
uniform mat4 g_WorldViewProjectionMatrix;
|
uniform mat4 g_WorldViewProjectionMatrix;
|
||||||
|
uniform mat4 g_ViewProjectionMatrix;
|
||||||
uniform mat3 g_NormalMatrix;
|
uniform mat3 g_NormalMatrix;
|
||||||
|
|
||||||
#if defined INSTANCING
|
#if defined INSTANCING
|
||||||
@ -37,29 +38,36 @@ uniform mat3 g_NormalMatrix;
|
|||||||
// 2 vertex attributes which now can be used for additional per-vertex data.
|
// 2 vertex attributes which now can be used for additional per-vertex data.
|
||||||
attribute mat4 inInstanceData;
|
attribute mat4 inInstanceData;
|
||||||
|
|
||||||
// Extract the world view matrix out of the instance data, leaving out the
|
// Extract the world matrix out of the instance data, leaving out the
|
||||||
// quaternion at the end.
|
// quaternion at the end.
|
||||||
mat4 worldViewMatrix = mat4(vec4(inInstanceData[0].xyz, 0.0),
|
mat4 worldMatrix = mat4(vec4(inInstanceData[0].xyz, 0.0),
|
||||||
vec4(inInstanceData[1].xyz, 0.0),
|
vec4(inInstanceData[1].xyz, 0.0),
|
||||||
vec4(inInstanceData[2].xyz, 0.0),
|
vec4(inInstanceData[2].xyz, 0.0),
|
||||||
vec4(inInstanceData[3].xyz, 1.0));
|
vec4(inInstanceData[3].xyz, 1.0));
|
||||||
|
|
||||||
|
vec4 TransformWorld(vec4 position)
|
||||||
|
{
|
||||||
|
return (worldMatrix * position);
|
||||||
|
}
|
||||||
|
|
||||||
vec4 TransformWorldView(vec4 position)
|
vec4 TransformWorldView(vec4 position)
|
||||||
{
|
{
|
||||||
return worldViewMatrix * position;
|
return g_ViewMatrix * TransformWorld(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 TransformWorldViewProjection(vec4 position)
|
vec4 TransformWorldViewProjection(vec4 position)
|
||||||
{
|
{
|
||||||
return g_ProjectionMatrix * TransformWorldView(position);
|
return g_ViewProjectionMatrix * TransformWorld(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 TransformNormal(vec3 vec)
|
vec3 TransformNormal(vec3 vec)
|
||||||
{
|
{
|
||||||
vec4 quat = vec4(inInstanceData[0].w, inInstanceData[1].w,
|
vec4 quat = vec4(inInstanceData[0].w, inInstanceData[1].w,
|
||||||
inInstanceData[2].w, inInstanceData[3].w);
|
inInstanceData[2].w, inInstanceData[3].w);
|
||||||
return vec + vec3(2.0) * cross(cross(vec, quat.xyz) + vec3(quat.w) * vec, quat.xyz);
|
|
||||||
|
vec3 worldNormal = vec + vec3(2.0) * cross(cross(vec, quat.xyz) + vec3(quat.w) * vec, quat.xyz);
|
||||||
|
|
||||||
|
return (g_ViewMatrix * vec4(worldNormal, 0.0)).xyz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent user from using g_** matrices which will have invalid data in this case.
|
// Prevent user from using g_** matrices which will have invalid data in this case.
|
||||||
@ -70,6 +78,11 @@ vec3 TransformNormal(vec3 vec)
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
vec4 TransformWorld(vec4 position)
|
||||||
|
{
|
||||||
|
return g_WorldMatrix * position;
|
||||||
|
}
|
||||||
|
|
||||||
vec4 TransformWorldView(vec4 position)
|
vec4 TransformWorldView(vec4 position)
|
||||||
{
|
{
|
||||||
return g_WorldViewMatrix * position;
|
return g_WorldViewMatrix * position;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
#import "Common/ShaderLib/Instancing.glsllib"
|
||||||
#import "Common/ShaderLib/Skinning.glsllib"
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
uniform mat4 g_WorldViewProjectionMatrix;
|
// These are included in the above now
|
||||||
uniform mat3 g_NormalMatrix;
|
//uniform mat4 g_WorldViewProjectionMatrix;
|
||||||
|
//uniform mat3 g_NormalMatrix;
|
||||||
|
|
||||||
attribute vec3 inPosition;
|
attribute vec3 inPosition;
|
||||||
attribute vec3 inNormal;
|
attribute vec3 inNormal;
|
||||||
|
93
jme3-examples/src/main/java/jme3test/bullet/TestIK.java
Normal file
93
jme3-examples/src/main/java/jme3test/bullet/TestIK.java
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2010 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 jme3test.bullet;
|
||||||
|
|
||||||
|
import com.jme3.animation.AnimEventListener;
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.bullet.collision.RagdollCollisionListener;
|
||||||
|
import com.jme3.bullet.control.KinematicRagdollControl;
|
||||||
|
import com.jme3.input.KeyInput;
|
||||||
|
import com.jme3.input.controls.ActionListener;
|
||||||
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author reden
|
||||||
|
*/
|
||||||
|
public class TestIK extends TestBoneRagdoll implements RagdollCollisionListener, AnimEventListener {
|
||||||
|
|
||||||
|
Node targetNode = new Node("");
|
||||||
|
Vector3f targetPoint;
|
||||||
|
Bone mouseBone;
|
||||||
|
Vector3f oldMousePos;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestIK app = new TestIK();
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
super.simpleInitApp();
|
||||||
|
final KinematicRagdollControl ikControl = model.getControl(KinematicRagdollControl.class);
|
||||||
|
inputManager.addListener(new ActionListener() {
|
||||||
|
|
||||||
|
public void onAction(String name, boolean isPressed, float tpf) {
|
||||||
|
|
||||||
|
if (name.equals("stop") && isPressed) {
|
||||||
|
ikControl.setEnabled(!ikControl.isEnabled());
|
||||||
|
ikControl.setIKMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.equals("one") && isPressed) {
|
||||||
|
//ragdoll.setKinematicMode();
|
||||||
|
targetPoint = model.getWorldTranslation().add(new Vector3f(0,2,4));
|
||||||
|
targetNode.setLocalTranslation(targetPoint);
|
||||||
|
ikControl.setIKTarget(ikControl.getBone("Hand.L"), targetPoint, 2);
|
||||||
|
ikControl.setIKMode();
|
||||||
|
}
|
||||||
|
if (name.equals("two") && isPressed) {
|
||||||
|
//ragdoll.setKinematicMode();
|
||||||
|
targetPoint = model.getWorldTranslation().add(new Vector3f(-3,3,0));
|
||||||
|
targetNode.setLocalTranslation(targetPoint);
|
||||||
|
ikControl.setIKTarget(ikControl.getBone("Hand.R"), targetPoint, 3);
|
||||||
|
ikControl.setIKMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "one", "two");
|
||||||
|
inputManager.addMapping("one", new KeyTrigger(KeyInput.KEY_1));
|
||||||
|
inputManager.addMapping("two", new KeyTrigger(KeyInput.KEY_2));
|
||||||
|
inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_H));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* 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 jme3test.scene.instancing;
|
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.instancing.InstancedGeometry;
|
||||||
|
import com.jme3.scene.instancing.InstancedNode;
|
||||||
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.scene.shape.Sphere;
|
||||||
|
import com.jme3.system.AppSettings;
|
||||||
|
|
||||||
|
public class TestInstanceNode extends SimpleApplication {
|
||||||
|
|
||||||
|
private Mesh mesh1;
|
||||||
|
private Mesh mesh2;
|
||||||
|
private final Material[] materials = new Material[6];
|
||||||
|
private Node instancedNode;
|
||||||
|
private float time = 0;
|
||||||
|
private boolean INSTANCING = false;
|
||||||
|
|
||||||
|
public static void main(String[] args){
|
||||||
|
TestInstanceNode app = new TestInstanceNode();
|
||||||
|
AppSettings settings = new AppSettings(true);
|
||||||
|
settings.setVSync(false);
|
||||||
|
app.setSettings(settings);
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Geometry createInstance(float x, float z) {
|
||||||
|
Mesh mesh;
|
||||||
|
if (FastMath.nextRandomInt(0, 1) == 1) mesh = mesh2;
|
||||||
|
else mesh = mesh1;
|
||||||
|
Geometry geometry = new Geometry("randomGeom", mesh);
|
||||||
|
geometry.setMaterial(materials[FastMath.nextRandomInt(0, materials.length - 1)]);
|
||||||
|
geometry.setLocalTranslation(x, 0, z);
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
mesh1 = new Sphere(13, 13, 0.4f, true, false);
|
||||||
|
mesh2 = new Box(0.4f, 0.4f, 0.4f);
|
||||||
|
|
||||||
|
materials[0] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
materials[0].setBoolean("UseInstancing", INSTANCING);
|
||||||
|
materials[0].setColor("Color", ColorRGBA.Red);
|
||||||
|
|
||||||
|
materials[1] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
materials[1].setBoolean("UseInstancing", INSTANCING);
|
||||||
|
materials[1].setColor("Color", ColorRGBA.Green);
|
||||||
|
|
||||||
|
materials[2] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
materials[2].setBoolean("UseInstancing", INSTANCING);
|
||||||
|
materials[2].setColor("Color", ColorRGBA.Blue);
|
||||||
|
|
||||||
|
materials[3] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
materials[3].setBoolean("UseInstancing", INSTANCING);
|
||||||
|
materials[3].setColor("Color", ColorRGBA.Cyan);
|
||||||
|
|
||||||
|
materials[4] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
materials[4].setBoolean("UseInstancing", INSTANCING);
|
||||||
|
materials[4].setColor("Color", ColorRGBA.Magenta);
|
||||||
|
|
||||||
|
materials[5] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
materials[5].setBoolean("UseInstancing", INSTANCING);
|
||||||
|
materials[5].setColor("Color", ColorRGBA.Yellow);
|
||||||
|
|
||||||
|
instancedNode = new InstancedNode("instanced_node");
|
||||||
|
|
||||||
|
rootNode.attachChild(instancedNode);
|
||||||
|
|
||||||
|
int extent = 30;
|
||||||
|
|
||||||
|
for (int y = -extent; y < extent; y++) {
|
||||||
|
for (int x = -extent; x < extent; x++) {
|
||||||
|
Geometry instance = createInstance(x, y);
|
||||||
|
|
||||||
|
float height = (smoothstep(0, 1, FastMath.nextRandomFloat()) * 2.5f) - 1.25f;
|
||||||
|
instance.setUserData("height", height);
|
||||||
|
instance.setUserData("dir", 1f);
|
||||||
|
|
||||||
|
instancedNode.attachChild(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (INSTANCING) {
|
||||||
|
((InstancedNode)instancedNode).instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
instancedNode = (InstancedNode) instancedNode.clone();
|
||||||
|
instancedNode.move(0, 5, 0);
|
||||||
|
rootNode.attachChild(instancedNode);
|
||||||
|
|
||||||
|
cam.setLocation(new Vector3f(38.373516f, 6.689055f, 38.482082f));
|
||||||
|
cam.setRotation(new Quaternion(-0.04004206f, 0.918326f, -0.096310444f, -0.38183528f));
|
||||||
|
flyCam.setMoveSpeed(15);
|
||||||
|
flyCam.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float smoothstep(float edge0, float edge1, float x) {
|
||||||
|
// Scale, bias and saturate x to 0..1 range
|
||||||
|
x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
|
||||||
|
// Evaluate polynomial
|
||||||
|
return x * x * (3 - 2 * x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleUpdate(float tpf) {
|
||||||
|
time += tpf;
|
||||||
|
|
||||||
|
if (time > 1f) {
|
||||||
|
time = 0f;
|
||||||
|
|
||||||
|
for (Spatial instance : instancedNode.getChildren()) {
|
||||||
|
if (!(instance instanceof InstancedGeometry)) {
|
||||||
|
Geometry geom = (Geometry) instance;
|
||||||
|
geom.setMaterial(materials[FastMath.nextRandomInt(0, materials.length - 1)]);
|
||||||
|
|
||||||
|
Mesh mesh;
|
||||||
|
if (FastMath.nextRandomInt(0, 1) == 1) mesh = mesh2;
|
||||||
|
else mesh = mesh1;
|
||||||
|
geom.setMesh(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Spatial child : instancedNode.getChildren()) {
|
||||||
|
if (!(child instanceof InstancedGeometry)) {
|
||||||
|
float val = child.getUserData("height");
|
||||||
|
float dir = child.getUserData("dir");
|
||||||
|
|
||||||
|
val += (dir + ((FastMath.nextRandomFloat() * 0.5f) - 0.25f)) * tpf;
|
||||||
|
|
||||||
|
if (val > 1f) {
|
||||||
|
val = 1f;
|
||||||
|
dir = -dir;
|
||||||
|
} else if (val < 0f) {
|
||||||
|
val = 0f;
|
||||||
|
dir = -dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f translation = child.getLocalTranslation();
|
||||||
|
translation.y = (smoothstep(0, 1, val) * 2.5f) - 1.25f;
|
||||||
|
|
||||||
|
child.setUserData("height", val);
|
||||||
|
child.setUserData("dir", dir);
|
||||||
|
|
||||||
|
child.setLocalTranslation(translation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,146 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 jme3test.scene.instancing;
|
|
||||||
|
|
||||||
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.Quaternion;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.Node;
|
|
||||||
import com.jme3.scene.Spatial;
|
|
||||||
import com.jme3.scene.Spatial.CullHint;
|
|
||||||
import com.jme3.scene.instancing.InstancedGeometry;
|
|
||||||
import com.jme3.scene.shape.Sphere;
|
|
||||||
|
|
||||||
public class TestInstancing extends SimpleApplication {
|
|
||||||
|
|
||||||
private InstancedGeometry instancedGeometry;
|
|
||||||
private Node instancedGeoms;
|
|
||||||
private Material material;
|
|
||||||
private boolean enabled = true;
|
|
||||||
|
|
||||||
public static void main(String[] args){
|
|
||||||
TestInstancing app = new TestInstancing();
|
|
||||||
//app.setShowSettings(false);
|
|
||||||
//app.setDisplayFps(false);
|
|
||||||
//app.setDisplayStatView(false);
|
|
||||||
app.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Geometry createInstance(float x, float z) {
|
|
||||||
// Note: it doesn't matter what mesh or material we set here.
|
|
||||||
Geometry geometry = new Geometry("randomGeom", instancedGeometry.getMesh());
|
|
||||||
geometry.setMaterial(instancedGeometry.getMaterial());
|
|
||||||
geometry.setLocalTranslation(x, 0, z);
|
|
||||||
return geometry;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simpleInitApp() {
|
|
||||||
initInputs();
|
|
||||||
|
|
||||||
Sphere sphere = new Sphere(10, 10, 0.5f, true, false);
|
|
||||||
material = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
|
|
||||||
material.setBoolean("UseInstancing", true);
|
|
||||||
|
|
||||||
instancedGeometry = new InstancedGeometry(InstancedGeometry.Mode.Auto, "instanced_geom");
|
|
||||||
instancedGeometry.setMaxNumInstances(60 * 60);
|
|
||||||
instancedGeometry.setCurrentNumInstances(60 * 60);
|
|
||||||
instancedGeometry.setCullHint(CullHint.Never);
|
|
||||||
instancedGeometry.setMesh(sphere);
|
|
||||||
instancedGeometry.setMaterial(material);
|
|
||||||
rootNode.attachChild(instancedGeometry);
|
|
||||||
|
|
||||||
instancedGeoms = new Node("instances_node");
|
|
||||||
|
|
||||||
// Important: Do not render these geometries, only
|
|
||||||
// use their world transforms to instance them via
|
|
||||||
// InstancedGeometry.
|
|
||||||
instancedGeoms.setCullHint(CullHint.Always);
|
|
||||||
|
|
||||||
for (int y = -30; y < 30; y++) {
|
|
||||||
for (int x = -30; x < 30; x++) {
|
|
||||||
Geometry instance = createInstance(x, y);
|
|
||||||
instancedGeoms.attachChild(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rootNode.attachChild(instancedGeoms);
|
|
||||||
rootNode.setCullHint(CullHint.Never);
|
|
||||||
|
|
||||||
int instanceIndex = 0;
|
|
||||||
for (Spatial child : instancedGeoms.getChildren()) {
|
|
||||||
if (instanceIndex < instancedGeometry.getMaxNumInstances()) {
|
|
||||||
instancedGeometry.setInstanceTransform(instanceIndex++, child.getWorldTransform());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
instancedGeometry.setCurrentNumInstances(instanceIndex);
|
|
||||||
|
|
||||||
cam.setLocation(new Vector3f(38.373516f, 6.689055f, 38.482082f));
|
|
||||||
cam.setRotation(new Quaternion(-0.04004206f, 0.918326f, -0.096310444f, -0.38183528f));
|
|
||||||
flyCam.setMoveSpeed(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initInputs() {
|
|
||||||
inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
|
|
||||||
|
|
||||||
ActionListener acl = new ActionListener() {
|
|
||||||
|
|
||||||
public void onAction(String name, boolean keyPressed, float tpf) {
|
|
||||||
if (name.equals("toggle") && keyPressed) {
|
|
||||||
if (enabled) {
|
|
||||||
enabled = false;
|
|
||||||
instancedGeoms.setCullHint(CullHint.Dynamic);
|
|
||||||
instancedGeometry.setCullHint(CullHint.Always);
|
|
||||||
material.setBoolean("UseInstancing", false);
|
|
||||||
System.out.println("Instancing OFF");
|
|
||||||
} else {
|
|
||||||
enabled = true;
|
|
||||||
instancedGeoms.setCullHint(CullHint.Always);
|
|
||||||
instancedGeometry.setCullHint(CullHint.Never);
|
|
||||||
material.setBoolean("UseInstancing", true);
|
|
||||||
System.out.println("Instancing ON");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inputManager.addListener(acl, "toggle");
|
|
||||||
}
|
|
||||||
}
|
|
@ -2226,7 +2226,6 @@ public class LwjglRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int slotsRequired = 1;
|
|
||||||
if (vb.isInstanced()) {
|
if (vb.isInstanced()) {
|
||||||
if (!ctxCaps.GL_ARB_instanced_arrays
|
if (!ctxCaps.GL_ARB_instanced_arrays
|
||||||
|| !ctxCaps.GL_ARB_draw_instanced) {
|
|| !ctxCaps.GL_ARB_draw_instanced) {
|
||||||
@ -2234,7 +2233,10 @@ public class LwjglRenderer implements Renderer {
|
|||||||
+ "but not supported by the "
|
+ "but not supported by the "
|
||||||
+ "graphics hardware");
|
+ "graphics hardware");
|
||||||
}
|
}
|
||||||
if (vb.getNumComponents() > 4 && vb.getNumComponents() % 4 != 0) {
|
}
|
||||||
|
int slotsRequired = 1;
|
||||||
|
if (vb.getNumComponents() > 4) {
|
||||||
|
if (vb.getNumComponents() % 4 != 0) {
|
||||||
throw new RendererException("Number of components in multi-slot "
|
throw new RendererException("Number of components in multi-slot "
|
||||||
+ "buffers must be divisible by 4");
|
+ "buffers must be divisible by 4");
|
||||||
}
|
}
|
||||||
@ -2294,7 +2296,7 @@ public class LwjglRenderer implements Renderer {
|
|||||||
int slot = loc + i;
|
int slot = loc + i;
|
||||||
if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) {
|
if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) {
|
||||||
// non-instanced -> instanced
|
// non-instanced -> instanced
|
||||||
glVertexAttribDivisorARB(slot, 1);
|
glVertexAttribDivisorARB(slot, vb.getInstanceSpan());
|
||||||
} else if (!vb.isInstanced() && attribs[slot] != null && attribs[slot].isInstanced()) {
|
} else if (!vb.isInstanced() && attribs[slot] != null && attribs[slot].isInstanced()) {
|
||||||
// instanced -> non-instanced
|
// instanced -> non-instanced
|
||||||
glVertexAttribDivisorARB(slot, 0);
|
glVertexAttribDivisorARB(slot, 0);
|
||||||
@ -2491,6 +2493,11 @@ public class LwjglRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
|
private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
|
||||||
|
|
||||||
|
// Here while count is still passed in. Can be removed when/if
|
||||||
|
// the method is collapsed again. -pspeed
|
||||||
|
count = Math.max(mesh.getInstanceCount(), count);
|
||||||
|
|
||||||
VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
|
VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
|
||||||
if (interleavedData != null && interleavedData.isUpdateNeeded()) {
|
if (interleavedData != null && interleavedData.isUpdateNeeded()) {
|
||||||
updateBufferData(interleavedData);
|
updateBufferData(interleavedData);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user