BatchNode now uses absolute world transforsm to update the underlying batch mesh, instead of computing the offset from next frame.

The real geometry mesh data is now used as a bind pose.
This avoids errors accumulations over time in some case and is less expensive.
See this post for the original issue http://hub.jmonkeyengine.org/forum/topic/batchnode-bug/

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10647 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 12 years ago
parent 9b8060bac2
commit 4c4e235e61
  1. 48
      engine/src/core/com/jme3/scene/BatchNode.java
  2. 54
      engine/src/core/com/jme3/scene/Geometry.java
  3. 22
      engine/src/core/com/jme3/scene/SimpleBatchNode.java

@ -139,28 +139,37 @@ public class BatchNode extends Node implements Savable {
assert refreshFlags == 0; assert refreshFlags == 0;
} }
protected Transform getTransforms(Geometry geom) { protected Matrix4f getTransformMatrix(Geometry g){
return geom.getWorldTransform(); return g.cachedWorldMat;
} }
protected void updateSubBatch(Geometry bg) { protected void updateSubBatch(Geometry bg) {
Batch batch = batchesByGeom.get(bg); Batch batch = batchesByGeom.get(bg);
if (batch != null) { if (batch != null) {
Mesh mesh = batch.geometry.getMesh(); Mesh mesh = batch.geometry.getMesh();
Mesh origMesh = bg.getMesh();
VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position); VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position);
FloatBuffer posBuf = (FloatBuffer) pvb.getData(); FloatBuffer posBuf = (FloatBuffer) pvb.getData();
VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal); VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal);
FloatBuffer normBuf = (FloatBuffer) nvb.getData(); FloatBuffer normBuf = (FloatBuffer) nvb.getData();
VertexBuffer opvb = origMesh.getBuffer(VertexBuffer.Type.Position);
FloatBuffer oposBuf = (FloatBuffer) opvb.getData();
VertexBuffer onvb = origMesh.getBuffer(VertexBuffer.Type.Normal);
FloatBuffer onormBuf = (FloatBuffer) onvb.getData();
Matrix4f transformMat = getTransformMatrix(bg);
if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) { if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) {
VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent); VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent);
FloatBuffer tanBuf = (FloatBuffer) tvb.getData(); FloatBuffer tanBuf = (FloatBuffer) tvb.getData();
doTransformsTangents(posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), bg.cachedOffsetMat); VertexBuffer otvb = origMesh.getBuffer(VertexBuffer.Type.Tangent);
FloatBuffer otanBuf = (FloatBuffer) otvb.getData();
doTransformsTangents(oposBuf, onormBuf, otanBuf, posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), transformMat);
tvb.updateData(tanBuf); tvb.updateData(tanBuf);
} else { } else {
doTransforms(posBuf, normBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), bg.cachedOffsetMat); doTransforms(oposBuf, onormBuf, posBuf, normBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), transformMat);
} }
pvb.updateData(posBuf); pvb.updateData(posBuf);
nvb.updateData(normBuf); nvb.updateData(normBuf);
@ -585,7 +594,7 @@ public class BatchNode extends Node implements Savable {
} }
} }
private void doTransforms(FloatBuffer bufPos, FloatBuffer bufNorm, int start, int end, Matrix4f transform) { private void doTransforms(FloatBuffer bindBufPos, FloatBuffer bindBufNorm, FloatBuffer bufPos, FloatBuffer bufNorm, int start, int end, Matrix4f transform) {
TempVars vars = TempVars.get(); TempVars vars = TempVars.get();
Vector3f pos = vars.vect1; Vector3f pos = vars.vect1;
Vector3f norm = vars.vect2; Vector3f norm = vars.vect2;
@ -595,10 +604,12 @@ public class BatchNode extends Node implements Savable {
// offset is given in element units // offset is given in element units
// convert to be in component units // convert to be in component units
int offset = start * 3; int offset = start * 3;
bufPos.position(offset); bindBufPos.rewind();
bufNorm.position(offset); bindBufNorm.rewind();
bufPos.get(tmpFloat, 0, length); //bufPos.position(offset);
bufNorm.get(tmpFloatN, 0, length); //bufNorm.position(offset);
bindBufPos.get(tmpFloat, 0, length);
bindBufNorm.get(tmpFloatN, 0, length);
int index = 0; int index = 0;
while (index < length) { while (index < length) {
pos.x = tmpFloat[index]; pos.x = tmpFloat[index];
@ -629,7 +640,7 @@ public class BatchNode extends Node implements Savable {
bufNorm.put(tmpFloatN, 0, length); bufNorm.put(tmpFloatN, 0, length);
} }
private void doTransformsTangents(FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) { private void doTransformsTangents(FloatBuffer bindBufPos, FloatBuffer bindBufNorm, FloatBuffer bindBufTangents,FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) {
TempVars vars = TempVars.get(); TempVars vars = TempVars.get();
Vector3f pos = vars.vect1; Vector3f pos = vars.vect1;
Vector3f norm = vars.vect2; Vector3f norm = vars.vect2;
@ -643,12 +654,13 @@ public class BatchNode extends Node implements Savable {
int offset = start * 3; int offset = start * 3;
int tanOffset = start * 4; int tanOffset = start * 4;
bufPos.position(offset);
bufNorm.position(offset); bindBufPos.rewind();
bufTangents.position(tanOffset); bindBufNorm.rewind();
bufPos.get(tmpFloat, 0, length); bindBufTangents.rewind();
bufNorm.get(tmpFloatN, 0, length); bindBufPos.get(tmpFloat, 0, length);
bufTangents.get(tmpFloatT, 0, tanLength); bindBufNorm.get(tmpFloatN, 0, length);
bindBufTangents.get(tmpFloatT, 0, tanLength);
int index = 0; int index = 0;
int tanIndex = 0; int tanIndex = 0;

@ -78,16 +78,7 @@ public class Geometry extends Spatial {
/** /**
* the start index of this geom's mesh in the batchNode mesh * the start index of this geom's mesh in the batchNode mesh
*/ */
protected int startIndex; protected int startIndex;
/**
* the previous transforms of the geometry used to compute world transforms
*/
protected Transform prevBatchTransforms = null;
/**
* the cached offset matrix used when the geometry is batched
*/
protected Matrix4f cachedOffsetMat = null;
/** /**
* Serialization only. Do not use. * Serialization only. Do not use.
*/ */
@ -291,11 +282,8 @@ public class Geometry extends Spatial {
super.updateWorldTransforms(); super.updateWorldTransforms();
computeWorldMatrix(); computeWorldMatrix();
if (isBatched()) { if (isBatched()) {
computeOffsetTransform(); batchNode.updateSubBatch(this);
batchNode.updateSubBatch(this);
prevBatchTransforms.set(batchNode.getTransforms(this));
} }
// geometry requires lights to be sorted // geometry requires lights to be sorted
worldLights.sort(true); worldLights.sort(true);
@ -308,9 +296,7 @@ public class Geometry extends Spatial {
*/ */
protected void batch(BatchNode node, int startIndex) { protected void batch(BatchNode node, int startIndex) {
this.batchNode = node; this.batchNode = node;
this.startIndex = startIndex; this.startIndex = startIndex;
prevBatchTransforms = new Transform();
cachedOffsetMat = new Matrix4f();
setCullHint(CullHint.Always); setCullHint(CullHint.Always);
} }
@ -319,8 +305,6 @@ public class Geometry extends Spatial {
*/ */
protected void unBatch() { protected void unBatch() {
this.startIndex = 0; this.startIndex = 0;
prevBatchTransforms = null;
cachedOffsetMat = null;
//once the geometry is removed from the screnegraph the batchNode needs to be rebatched. //once the geometry is removed from the screnegraph the batchNode needs to be rebatched.
if (batchNode != null) { if (batchNode != null) {
this.batchNode.setNeedsFullRebatch(true); this.batchNode.setNeedsFullRebatch(true);
@ -343,36 +327,6 @@ public class Geometry extends Spatial {
} }
} }
/**
* Recomputes the cached offset matrix used when the geometry is batched *
*/
public void computeOffsetTransform() {
TempVars vars = TempVars.get();
Matrix4f tmpMat = vars.tempMat42;
// Compute the cached world matrix
cachedOffsetMat.loadIdentity();
cachedOffsetMat.setRotationQuaternion(prevBatchTransforms.getRotation());
cachedOffsetMat.setTranslation(prevBatchTransforms.getTranslation());
Matrix4f scaleMat = vars.tempMat4;
scaleMat.loadIdentity();
scaleMat.scale(prevBatchTransforms.getScale());
cachedOffsetMat.multLocal(scaleMat);
cachedOffsetMat.invertLocal();
tmpMat.loadIdentity();
tmpMat.setRotationQuaternion(batchNode.getTransforms(this).getRotation());
tmpMat.setTranslation(batchNode.getTransforms(this).getTranslation());
scaleMat.loadIdentity();
scaleMat.scale(batchNode.getTransforms(this).getScale());
tmpMat.multLocal(scaleMat);
tmpMat.mult(cachedOffsetMat, cachedOffsetMat);
vars.release();
}
/** /**
* Indicate that the transform of this spatial has changed and that * Indicate that the transform of this spatial has changed and that

@ -31,7 +31,9 @@
*/ */
package com.jme3.scene; package com.jme3.scene;
import com.jme3.math.Matrix4f;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.util.TempVars;
/** /**
* *
@ -70,10 +72,24 @@ public class SimpleBatchNode extends BatchNode {
batch.geometry.setTransformRefresh(); batch.geometry.setTransformRefresh();
} }
} }
private Matrix4f cachedLocalMat = new Matrix4f();
protected Transform getTransforms(Geometry geom){
return geom.getLocalTransform(); @Override
protected Matrix4f getTransformMatrix(Geometry g){
// Compute the Local matrix for the geometry
cachedLocalMat.loadIdentity();
cachedLocalMat.setRotationQuaternion(g.localTransform.getRotation());
cachedLocalMat.setTranslation(g.localTransform.getTranslation());
TempVars vars = TempVars.get();
Matrix4f scaleMat = vars.tempMat4;
scaleMat.loadIdentity();
scaleMat.scale(g.localTransform.getScale());
cachedLocalMat.multLocal(scaleMat);
vars.release();
return cachedLocalMat;
} }
@Override @Override
public void batch() { public void batch() {

Loading…
Cancel
Save