Added option to depth-sort the triangles in transparent meshes. I tried

to optimize this, and seems reasonably fast, but not fast enough to 
enable it for all transparent meshes.  Hopefully the artists can improve 
performance by merging meshes together, and we can strike a happy 
balance between sorting by mesh and sorting by triangles.


git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@29 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Andrzej Kapolka
2006-08-23 23:14:38 +00:00
parent 4a4f9755c8
commit f8c230df6f
2 changed files with 188 additions and 23 deletions
@@ -36,8 +36,11 @@ import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Properties;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.bounding.BoundingVolume;
import com.jme.image.Texture;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
@@ -105,6 +108,8 @@ public class ModelMesh extends TriMesh
_emissiveMap = props.getProperty(texture + ".emissive");
_solid = solid;
_transparent = transparent;
_depthSorted = _transparent && Boolean.parseBoolean(
props.getProperty("depth_sort"));
}
/**
@@ -131,6 +136,9 @@ public class ModelMesh extends TriMesh
_textureByteBuffer = textures;
_indexByteBuffer = indices;
// store any buffers that will be manipulated on a per-instance basis
storeOriginalBuffers();
// initialize the model if we're displaying
if (DisplaySystem.getDisplaySystem() == null) {
return;
@@ -160,6 +168,7 @@ public class ModelMesh extends TriMesh
getBatch(0).getModelBound().getCenter().set(Vector3f.ZERO);
getBatch(0).translatePoints(offset);
}
storeOriginalBuffers();
}
/**
@@ -271,7 +280,7 @@ public class ModelMesh extends TriMesh
mbatch.setTextureBuffer(properties.isSet("texcoords") ?
texcoords : BufferUtils.clone(texcoords), ii);
}
mbatch.setIndexBuffer(properties.isSet("indices") ?
mbatch.setIndexBuffer((properties.isSet("indices") && !_depthSorted) ?
batch.getIndexBuffer() :
BufferUtils.clone(batch.getIndexBuffer()));
if (properties.isSet("vboinfo")) {
@@ -300,6 +309,11 @@ public class ModelMesh extends TriMesh
mstore._filterMode = _filterMode;
mstore._mipMapMode = _mipMapMode;
mstore._emissiveMap = _emissiveMap;
mstore._solid = _solid;
mstore._transparent = _transparent;
mstore._depthSorted = _depthSorted;
mstore._oibuf = _oibuf;
mstore._vbuf = _vbuf;
return mstore;
}
@@ -332,6 +346,7 @@ public class ModelMesh extends TriMesh
out.writeObject(_emissiveMap);
out.writeBoolean(_solid);
out.writeBoolean(_transparent);
out.writeBoolean(_depthSorted);
}
// documentation inherited from interface Externalizable
@@ -355,6 +370,7 @@ public class ModelMesh extends TriMesh
_emissiveMap = (String)in.readObject();
_solid = in.readBoolean();
_transparent = in.readBoolean();
_depthSorted = in.readBoolean();
}
// documentation inherited from interface ModelSpatial
@@ -375,10 +391,10 @@ public class ModelMesh extends TriMesh
{
if (useVBOs && renderer.supportsVBO()) {
VBOInfo vboinfo = new VBOInfo(true);
vboinfo.setVBOIndexEnabled(true);
vboinfo.setVBOIndexEnabled(!_depthSorted);
setVBOInfo(vboinfo);
} else if (useDisplayLists) {
} else if (useDisplayLists && !_depthSorted) {
lockMeshes(renderer);
}
}
@@ -554,7 +570,7 @@ public class ModelMesh extends TriMesh
protected void setupBatchList ()
{
batchList = new ArrayList<GeomBatch>(1);
TriangleBatch batch = new OverlayBatch();
TriangleBatch batch = new ModelBatch();
batch.setParentGeom(this);
batchList.add(batch);
}
@@ -568,6 +584,24 @@ public class ModelMesh extends TriMesh
return (_emissiveMap == null) ? 1 : 2;
}
/**
* For buffers that must be manipulated in some fashion, this method stores
* the originals.
*/
protected void storeOriginalBuffers ()
{
if (!_depthSorted) {
return;
}
IntBuffer ibuf = getIndexBuffer(0);
ibuf.rewind();
IntBuffer.wrap(_oibuf = new int[ibuf.capacity()]).put(ibuf);
FloatBuffer vbuf = getVertexBuffer(0);
vbuf.rewind();
FloatBuffer.wrap(_vbuf = new float[vbuf.capacity()]).put(vbuf);
}
/**
* Locks the transform and bounds of this mesh on the assumption that its
* position will not change.
@@ -632,12 +666,65 @@ public class ModelMesh extends TriMesh
_overlayZBuffer.setWritable(false);
}
/** Renders overlays as well as the base layer. */
protected class OverlayBatch extends TriangleBatch
/**
* Sorts the encoded triangle index/distance pairs in {@link #_tcodes}
* using a two-pass (16 bit) radix sort (as described by
* <a href="http://codercorner.com/RadixSortRevisited.htm">Pierre
* Terdiman</a>. {@link #_bcounts} is assumed to be initialized to the
* counts for the first radix.
*/
protected static void sortTriangleCodes (int tcount)
{
// initialize the offsets for the first radix (LSB) and clear
// the counts
initByteOffsets();
// sort by the first radix and get the counts for the second
// (swapping directions in the hope of using the cache more
// effectively)
if (_stcodes == null || _stcodes.length < tcount) {
_stcodes = new int[tcount];
}
int tcode;
for (int ii = tcount - 1; ii >= 0; ii--) {
tcode = _tcodes[ii];
_stcodes[_boffsets[tcode & 0xFF]++] = tcode;
_bcounts[(tcode >> 8) & 0xFF]++;
}
// initialize offsets for the second radix, clear counts, and
// sort by the second radix
initByteOffsets();
for (int ii = 0; ii < tcount; ii++) {
tcode = _stcodes[ii];
_tcodes[_boffsets[(tcode >> 8) & 0xFF]++] = tcode;
}
}
/**
* Sets the initial byte offsets used to place bytes within the sorted
* array using the byte counts, clearing the counts in the process.
*/
protected static void initByteOffsets ()
{
_boffsets[0] = 0;
for (int ii = 1; ii < 256; ii++) {
_boffsets[ii] = _boffsets[ii - 1] + _bcounts[ii - 1];
_bcounts[ii - 1] = 0;
}
_bcounts[255] = 0;
}
/** Sorts triangles for transparent meshes and renders overlays as well as
* the base layer. */
protected class ModelBatch extends TriangleBatch
{
@Override // documentation inherited
public void draw (Renderer r)
{
if (_depthSorted && isEnabled() && r.isProcessingQueue()) {
sortTriangles(r);
}
super.draw(r);
if (_overlays != null && isEnabled() && r.isProcessingQueue()) {
for (RenderState[] overlay : _overlays) {
@@ -655,6 +742,72 @@ public class ModelMesh extends TriMesh
}
}
/**
* Sorts the batch's triangles by their distance to the camera.
*/
protected void sortTriangles (Renderer r)
{
// using the camera's direction in model space and the position
// and size of the model bound, find a set of plane parameters
// that determine the distance to a camera-aligned plane
// that touches the near edge of the bounding volume, as well
// as a scaling factor that brings the distance into a 16-bit
// integer range
getParentGeom().getWorldRotation().inverse().mult(
r.getCamera().getDirection(), _cdir);
BoundingVolume mbound = getModelBound();
Vector3f mc = mbound.getCenter();
float radius;
if (mbound instanceof BoundingSphere) {
radius = ((BoundingSphere)mbound).getRadius();
} else { // mbound instanceof BoundingBox
BoundingBox bbox = (BoundingBox)mbound;
radius = FastMath.sqrt(3f) * Math.max(bbox.xExtent,
Math.max(bbox.yExtent, bbox.zExtent));
}
float a = _cdir.x, b = _cdir.y, c = _cdir.z,
d = radius - a*mc.x - b*mc.y - c*mc.z,
dscale = 65535f / (radius * 2);
// encode the model's triangles into integers such that the
// high 16 bits represent the original triangle index and the
// low 16 bits represent the distance to the plane. also
// increment the byte counts used for radix sorting
int tcount = getTriangleCount(), idx, idist;
if (_tcodes == null || _tcodes.length < tcount) {
_tcodes = new int[tcount];
}
FloatBuffer vbuf = getVertexBuffer();
for (int ii = 0; ii < tcount; ii++) {
idx = _oibuf[ii*3] * 3;
idist = (int)((a*_vbuf[idx++] + b*_vbuf[idx++] +
c*_vbuf[idx] + d) * dscale);
_tcodes[ii] = (ii << 16) | idist;
_bcounts[idist & 0xFF]++;
}
// sort the encoded triangles by increasing distance
sortTriangleCodes(tcount);
// reorder the triangles as dictated by the sorted codes, furthest
// triangles first
int icount = tcount * 3;
if (_sibuf == null || _sibuf.length < icount) {
_sibuf = new int[icount];
}
for (int ii = tcount - 1, sidx = 0; ii >= 0; ii--) {
idx = ((_tcodes[ii] >> 16) & 0xFFFF) * 3;
_sibuf[sidx++] = _oibuf[idx++];
_sibuf[sidx++] = _oibuf[idx++];
_sibuf[sidx++] = _oibuf[idx];
}
// copy the indices to the buffer
IntBuffer ibuf = getIndexBuffer();
ibuf.rewind();
ibuf.put(_sibuf, 0, icount);
}
/** Temporarily stores the original states. */
protected RenderState[] _ostates =
new RenderState[RenderState.RS_MAX_STATE];
@@ -692,6 +845,10 @@ public class ModelMesh extends TriMesh
/** Whether or not this mesh must be rendered as transparent. */
protected boolean _transparent;
/** Whether or not the triangles of this mesh should be depth-sorted before
* rendering. */
protected boolean _depthSorted;
/** If non-null, additional layers to render over the base layer. */
protected ArrayList<RenderState[]> _overlays;
@@ -703,6 +860,12 @@ public class ModelMesh extends TriMesh
* transformations to display lists. */
protected boolean _transformLocked;
/** For depth-sorted and skinned meshes, the array of vertices. */
protected float[] _vbuf;
/** For depth-sorted meshes, the original array of indices. */
protected int[] _oibuf;
/** The shared state for back face culling. */
protected static CullState _backCull;
@@ -712,5 +875,17 @@ public class ModelMesh extends TriMesh
/** The shared state for checking, but not writing to, the z buffer. */
protected static ZBufferState _overlayZBuffer;
/** Work vector to store the camera direction. */
protected static Vector3f _cdir = new Vector3f();
/** Work arrays used to sort triangles. */
protected static int[] _tcodes, _stcodes;
/** Holds counts of each byte and array offsets for radix sorting. */
protected static int[] _bcounts = new int[256], _boffsets = new int[256];
/** Work array used to hold indices of sorted triangles. */
protected static int[] _sibuf;
private static final long serialVersionUID = 1;
}
@@ -175,20 +175,10 @@ public class SkinMesh extends ModelMesh
{
super.reconstruct(vertices, normals, colors, textures, indices);
// store the current buffers as the originals
storeOriginalBuffers();
// initialize the quantized frame table
_frames = new HashIntMap<Object>();
}
@Override // documentation inherited
public void centerVertices ()
{
super.centerVertices();
storeOriginalBuffers();
}
@Override // documentation inherited
public Spatial putClone (Spatial store, Model.CloneCreator properties)
{
@@ -279,10 +269,10 @@ public class SkinMesh extends ModelMesh
VBOInfo vboinfo = new VBOInfo(false);
vboinfo.setVBOColorEnabled(true);
vboinfo.setVBOTextureEnabled(true);
vboinfo.setVBOIndexEnabled(true);
vboinfo.setVBOIndexEnabled(!_depthSorted);
setVBOInfo(vboinfo);
}
_useDisplayLists = useDisplayLists;
_useDisplayLists = useDisplayLists && !_depthSorted;
}
@Override // documentation inherited
@@ -410,7 +400,7 @@ public class SkinMesh extends ModelMesh
VBOInfo ovboinfo = batch.getVBOInfo();
if (ovboinfo != null) {
VBOInfo vboinfo = new VBOInfo(true);
vboinfo.setVBOIndexEnabled(true);
vboinfo.setVBOIndexEnabled(!_depthSorted);
vboinfo.setVBOColorID(ovboinfo.getVBOColorID());
for (int ii = 0; ii < nunits; ii++) {
vboinfo.setVBOTextureID(ii, ovboinfo.getVBOTextureID(ii));
@@ -425,11 +415,11 @@ public class SkinMesh extends ModelMesh
}
}
/**
* Stores the current vertex and normal buffers for later deformation.
*/
@Override // documentation inherited
protected void storeOriginalBuffers ()
{
super.storeOriginalBuffers();
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
vbuf.rewind();
nbuf.rewind();
@@ -483,7 +473,7 @@ public class SkinMesh extends ModelMesh
/** The original (undeformed) vertex and normal buffers and the deformed
* versions. */
protected float[] _ovbuf, _onbuf, _vbuf, _nbuf;
protected float[] _onbuf, _ovbuf, _nbuf;
/** The frame id to store on the next update. If 0, don't store any frame
* and skin the mesh as normal. If -1, a frame has been stored and thus