Files
nenya/src/java/com/threerings/jme/model/ModelMesh.java
T
Michael Bayne 51c01493f2 Type safety, redundant case removal, other cleanup.
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@490 ed5b42cb-e716-0410-a449-f6a68f950b19
2008-05-09 11:39:45 +00:00

911 lines
32 KiB
Java

//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.jme.model;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Properties;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.bounding.BoundingVolume;
import com.jme.image.Image;
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;
import com.jme.renderer.Renderer;
import com.jme.scene.Controller;
import com.jme.scene.SharedMesh;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.VBOInfo;
import com.jme.scene.batch.GeomBatch;
import com.jme.scene.batch.TriangleBatch;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.FogState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.OutputCapsule;
import com.jme.util.geom.BufferUtils;
import com.samskivert.util.PropertiesUtil;
import com.samskivert.util.StringUtil;
import com.threerings.jme.Log;
import com.threerings.jme.util.ShaderCache;
/**
* A {@link TriMesh} with a serialization mechanism tailored to stored models.
*/
public class ModelMesh extends TriMesh
implements ModelSpatial
{
/**
* No-arg constructor for deserialization.
*/
public ModelMesh ()
{
super("mesh");
}
/**
* Creates a mesh with no vertex data.
*/
public ModelMesh (String name)
{
super(name);
}
@Override // documentation inherited
public int hashCode ()
{
// hash on the name rather than the identity for consistent ordering
return getName().hashCode();
}
/**
* Reconfigures this model with a new set of (sub-)properties. Textures
* must be (re-)resolved after calling this method.
*/
public void reconfigure (Properties props)
{
configure(_solid, _textureKey, _transparent, props);
setRenderStates();
}
/**
* Configures this mesh based on the given parameters and (sub-)properties.
*
* @param texture the texture specified in the model export, if any (can be
* overridden by textures specified in the properties)
* @param solid whether or not the mesh allows back face culling
* @param transparent whether or not the mesh is (partially) transparent
*/
public void configure (
boolean solid, String texture, boolean transparent, Properties props)
{
_textureKey = texture;
_textures = (texture == null) ? null : StringUtil.parseStringArray(
props.getProperty(texture, texture));
Properties tprops = PropertiesUtil.getFilteredProperties(
props, texture);
_sphereMapped = Boolean.parseBoolean(tprops.getProperty("sphere_map"));
_filterMode = "nearest".equals(tprops.getProperty("filter")) ?
Texture.FM_NEAREST : Texture.FM_LINEAR;
_mipMapMode = getMipMapMode(tprops.getProperty("mipmap"));
_compress = Boolean.parseBoolean(tprops.getProperty("compress", "true"));
String emissive = tprops.getProperty("emissive");
if (Boolean.parseBoolean(emissive)) {
_emissive = true;
} else {
_emissiveMap = emissive;
}
_additive = Boolean.parseBoolean(tprops.getProperty("additive"));
_solid = solid;
_transparent = transparent;
String threshold = tprops.getProperty("alpha_threshold");
_alphaThreshold = (threshold == null) ?
DEFAULT_ALPHA_THRESHOLD : Float.parseFloat(threshold);
_translucent = _transparent &&
Boolean.parseBoolean(tprops.getProperty("translucent"));
}
/**
* Adjusts the vertices and the transform of the mesh so that the mesh's
* position lies at the center of its bounding volume.
*/
public void centerVertices ()
{
Vector3f offset = getBatch(0).getModelBound().getCenter().negate();
if (!offset.equals(Vector3f.ZERO)) {
getLocalTranslation().subtractLocal(offset);
getBatch(0).getModelBound().getCenter().set(Vector3f.ZERO);
getBatch(0).translatePoints(offset);
}
storeOriginalBuffers();
}
/**
* Adds an overlay layer to this mesh. After the mesh is rendered with
* its configured states, these states will be applied and the mesh will
* be rendered again.
*/
public void addOverlay (RenderState[] overlay)
{
if (_overlays == null) {
_overlays = new ArrayList<RenderState[]>(1);
}
_overlays.add(overlay);
}
/**
* Removes a layer from this mesh.
*/
public void removeOverlay (RenderState[] overlay)
{
if (_overlays != null) {
_overlays.remove(overlay);
if (_overlays.isEmpty()) {
_overlays = null;
}
}
}
@Override // documentation inherited
public void reconstruct (
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
FloatBuffer textures, IntBuffer indices)
{
super.reconstruct(vertices, normals, colors, textures, indices);
for (int ii = 1, nn = getTextureCount(); ii < nn; ii++) {
setTextureBuffer(0, getTextureBuffer(0, 0), ii);
}
// store any buffers that will be manipulated on a per-instance basis
storeOriginalBuffers();
// initialize the model if we're displaying
setRenderStates();
}
// documentation inherited from interface ModelSpatial
public Spatial putClone (Spatial store, Model.CloneCreator properties)
{
ModelMesh mstore = (ModelMesh)properties.originalToCopy.get(this);
if (mstore != null) {
return mstore;
} else if (store == null) {
mstore = new ModelMesh(getName());
} else {
mstore = (ModelMesh)store;
}
properties.originalToCopy.put(this, mstore);
mstore.normalsMode = normalsMode;
mstore.cullMode = cullMode;
for (int ii = 0; ii < RenderState.RS_MAX_STATE; ii++) {
RenderState rstate = getRenderState(ii);
if (rstate != null) {
mstore.setRenderState(rstate);
}
}
mstore.renderQueueMode = renderQueueMode;
mstore.lockedMode = lockedMode;
mstore.lightCombineMode = lightCombineMode;
mstore.textureCombineMode = textureCombineMode;
mstore.name = name;
mstore.isCollidable = isCollidable;
mstore.localRotation.set(localRotation);
mstore.localTranslation.set(localTranslation);
mstore.localScale.set(localScale);
for (Object controller : getControllers()) {
if (controller instanceof ModelController) {
mstore.addController(
((ModelController)controller).putClone(null, properties));
}
}
TriangleBatch batch = getBatch(0), mbatch = mstore.getBatch(0);
mbatch.setVertexBuffer(properties.isSet("vertices") ?
batch.getVertexBuffer() :
BufferUtils.clone(batch.getVertexBuffer()));
mbatch.setColorBuffer(properties.isSet("colors") ?
batch.getColorBuffer() :
BufferUtils.clone(batch.getColorBuffer()));
mbatch.setNormalBuffer(properties.isSet("normals") ?
batch.getNormalBuffer() :
BufferUtils.clone(batch.getNormalBuffer()));
FloatBuffer texcoords;
for (int ii = 0; (texcoords = batch.getTextureBuffer(ii)) != null;
ii++) {
mbatch.setTextureBuffer(properties.isSet("texcoords") ?
texcoords : BufferUtils.clone(texcoords), ii);
}
mbatch.setIndexBuffer((properties.isSet("indices") && !_translucent) ?
batch.getIndexBuffer() :
BufferUtils.clone(batch.getIndexBuffer()));
if (properties.isSet("vboinfo")) {
mbatch.setVBOInfo(batch.getVBOInfo());
}
if (properties.isSet("obbtree")) {
mbatch.setCollisionTree(batch.getCollisionTree());
}
if (properties.isSet("displaylistid")) {
mbatch.setDisplayListID(batch.getDisplayListID());
}
if (batch.getModelBound() != null) {
mbatch.setModelBound(properties.isSet("bound") ?
batch.getModelBound() : batch.getModelBound().clone(null));
}
mstore._textureKey = _textureKey;
if (_textures != null && _textures.length > 1) {
int tidx = properties.random % _textures.length;
mstore._textures = new String[] { _textures[tidx] };
mstore._tstates = new TextureState[] { _tstates[tidx] };
mstore.setRenderState(_tstates[tidx]);
} else {
mstore._textures = _textures;
mstore._tstates = _tstates;
}
mstore._sphereMapped = _sphereMapped;
mstore._filterMode = _filterMode;
mstore._mipMapMode = _mipMapMode;
mstore._compress = _compress;
mstore._emissiveMap = _emissiveMap;
mstore._emissive = _emissive;
mstore._additive = _additive;
mstore._solid = _solid;
mstore._transparent = _transparent;
mstore._alphaThreshold = _alphaThreshold;
mstore._translucent = _translucent;
mstore._oibuf = _oibuf;
mstore._vbuf = _vbuf;
return mstore;
}
@Override // documentation inherited
public void updateWorldVectors ()
{
if (!_transformLocked) {
super.updateWorldVectors();
}
}
@Override // documentation inherited
public void read (JMEImporter im)
throws IOException
{
InputCapsule capsule = im.getCapsule(this);
setName(capsule.readString("name", null));
setLocalTranslation((Vector3f)capsule.readSavable(
"localTranslation", null));
setLocalRotation((Quaternion)capsule.readSavable(
"localRotation", null));
setLocalScale((Vector3f)capsule.readSavable(
"localScale", null));
TriangleBatch batch = getBatch(0);
batch.setModelBound((BoundingVolume)capsule.readSavable(
"modelBound", null));
_textureKey = capsule.readString("textureKey", null);
_textures = capsule.readStringArray("textures", null);
_sphereMapped = capsule.readBoolean("sphereMapped", false);
_filterMode = capsule.readInt("filterMode", Texture.FM_LINEAR);
_mipMapMode = capsule.readInt("mipMapMode", Texture.MM_LINEAR_LINEAR);
_compress = capsule.readBoolean("compress", true);
_emissiveMap = capsule.readString("emissiveMap", null);
_emissive = capsule.readBoolean("emissive", false);
_additive = capsule.readBoolean("additive", false);
_solid = capsule.readBoolean("solid", true);
_transparent = capsule.readBoolean("transparent", false);
_alphaThreshold = capsule.readFloat("alphaThreshold",
DEFAULT_ALPHA_THRESHOLD);
_translucent = capsule.readBoolean("translucent", false);
reconstruct(capsule.readFloatBuffer("vertexBuffer", null),
capsule.readFloatBuffer("normalBuffer", null), null,
capsule.readFloatBuffer("textureBuffer", null),
capsule.readIntBuffer("indexBuffer", null));
}
@Override // documentation inherited
public void write (JMEExporter ex)
throws IOException
{
OutputCapsule capsule = ex.getCapsule(this);
capsule.write(getName(), "name", null);
capsule.write(getLocalTranslation(), "localTranslation", null);
capsule.write(getLocalRotation(), "localRotation", null);
capsule.write(getLocalScale(), "localScale", null);
capsule.write(getBatch(0).getModelBound(), "modelBound", null);
capsule.write(getVertexBuffer(0), "vertexBuffer", null);
capsule.write(getNormalBuffer(0), "normalBuffer", null);
capsule.write(getTextureBuffer(0, 0), "textureBuffer", null);
capsule.write(getIndexBuffer(0), "indexBuffer", null);
capsule.write(_textureKey, "textureKey", null);
capsule.write(_textures, "textures", null);
capsule.write(_sphereMapped, "sphereMapped", false);
capsule.write(_filterMode, "filterMode", Texture.FM_LINEAR);
capsule.write(_mipMapMode, "mipMapMode", Texture.MM_LINEAR_LINEAR);
capsule.write(_compress, "compress", true);
capsule.write(_emissiveMap, "emissiveMap", null);
capsule.write(_emissive, "emissive", false);
capsule.write(_additive, "additive", false);
capsule.write(_solid, "solid", true);
capsule.write(_transparent, "transparent", false);
capsule.write(_alphaThreshold, "alphaThreshold",
DEFAULT_ALPHA_THRESHOLD);
capsule.write(_translucent, "translucent", false);
}
// documentation inherited from interface ModelSpatial
public void expandModelBounds ()
{
// no-op
}
// documentation inherited from interface ModelSpatial
public void setReferenceTransforms ()
{
// no-op
}
// documentation inherited from interface ModelSpatial
public void lockStaticMeshes (
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
{
if (useVBOs && renderer.supportsVBO()) {
VBOInfo vboinfo = new VBOInfo(true);
vboinfo.setVBOIndexEnabled(!_translucent);
setVBOInfo(vboinfo);
} else if (useDisplayLists && !_translucent) {
lockMeshes(renderer);
}
}
// documentation inherited from interface ModelSpatial
public void resolveTextures (TextureProvider tprov)
{
if (_textures == null) {
return;
}
Texture emissiveTex = null;
if (_emissiveMap != null && TextureState.getNumberOfFixedUnits() >= 2) {
TextureState tstate = tprov.getTexture(_emissiveMap);
emissiveTex = tstate.getTexture();
emissiveTex.setApply(Texture.AM_BLEND);
emissiveTex.getBlendColor().set(ColorRGBA.white);
}
_tstates = new TextureState[_textures.length];
for (int ii = 0; ii < _textures.length; ii++) {
_tstates[ii] = tprov.getTexture(_textures[ii]);
Texture tex = _tstates[ii].getTexture();
if (_sphereMapped) {
tex.setEnvironmentalMapMode(Texture.EM_SPHERE);
}
tex.setFilter(_filterMode);
tex.setMipmapState(_mipMapMode);
if (_compress && _tstates[ii].isS3TCAvailable()) {
Image image = tex.getImage();
int type = image.getType();
if (type == Image.RGB888) {
image.setType(Image.RGB888_DXT1);
} else if (type == Image.RGBA8888) {
image.setType(Image.RGBA8888_DXT5);
}
}
if (emissiveTex != null) {
_tstates[ii] = DisplaySystem.getDisplaySystem().
getRenderer().createTextureState();
_tstates[ii].setTexture(emissiveTex, 0);
_tstates[ii].setTexture(tex, 1);
}
}
if (_tstates[0] != null) {
setRenderState(_tstates[0]);
} else {
clearRenderState(RenderState.RS_TEXTURE);
}
}
// documentation inherited from interface ModelSpatial
public void configureShaders (ShaderCache scache)
{
// no-op
}
// documentation inherited from interface ModelSpatial
public void storeMeshFrame (int frameId, boolean blend)
{
// no-op
}
// documentation inherited from interface ModelSpatial
public void setMeshFrame (int frameId)
{
// no-op
}
// documentation inherited from interface ModelSpatial
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
{
// no-op
}
@Override // documentation inherited
protected void setupBatchList ()
{
batchList = new ArrayList<GeomBatch>(1);
TriangleBatch batch = createModelBatch();
batch.setParentGeom(this);
batchList.add(batch);
}
/**
* Creates a batch for this mesh.
*/
protected ModelBatch createModelBatch ()
{
return new ModelBatch();
}
/**
* Returns the number of textures this mesh uses (they must all share the
* same texture coordinates).
*/
protected int getTextureCount ()
{
return (_emissiveMap == null || TextureState.getNumberOfFixedUnits() < 2) ? 1 : 2;
}
/**
* For buffers that must be manipulated in some fashion, this method stores
* the originals.
*/
protected void storeOriginalBuffers ()
{
if (!_translucent) {
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);
}
/**
* Sets the model's render states (excluding the texture state, which is
* set by {@link #resolveTextures}) according to its configuration.
*/
protected void setRenderStates ()
{
if (DisplaySystem.getDisplaySystem() == null) {
return;
}
if (_backCull == null) {
initSharedStates();
}
if (_emissive) {
setRenderState(_emissiveLight);
}
if (_solid) {
setRenderState(_backCull);
}
if (_additive) {
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
setRenderState(_addAlpha);
setRenderState(_overlayZBuffer);
setRenderState(_noFog);
} else if (_transparent) {
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
if (_translucent) {
setRenderState(_blendAlpha);
setRenderState(_overlayZBuffer);
} else if (_alphaThreshold == DEFAULT_ALPHA_THRESHOLD) {
setRenderState(_defaultTestAlpha);
} else {
setRenderState(createTestAlpha(_alphaThreshold));
}
}
}
/**
* Locks the transform and bounds of this mesh on the assumption that its
* position will not change.
*/
protected void lockInstance ()
{
lockBounds();
_transformLocked = true;
}
/**
* Returns the mip-map mode corresponding to the given string
* (defaulting to {@link Texture#MM_LINEAR_LINEAR}).
*/
protected static int getMipMapMode (String mmode)
{
if ("none".equals(mmode)) {
return Texture.MM_NONE;
} else if ("nearest".equals(mmode)) {
return Texture.MM_NEAREST;
} else if ("linear".equals(mmode)) {
return Texture.MM_LINEAR;
} else if ("nearest_nearest".equals(mmode)) {
return Texture.MM_NEAREST_NEAREST;
} else if ("nearest_linear".equals(mmode)) {
return Texture.MM_NEAREST_LINEAR;
} else if ("linear_nearest".equals(mmode)) {
return Texture.MM_LINEAR_NEAREST;
} else {
return Texture.MM_LINEAR_LINEAR;
}
}
/**
* Initializes the states shared between all models. Requires an active
* display.
*/
protected static void initSharedStates ()
{
Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer();
_backCull = renderer.createCullState();
_backCull.setCullMode(CullState.CS_BACK);
_blendAlpha = renderer.createAlphaState();
_blendAlpha.setBlendEnabled(true);
_addAlpha = renderer.createAlphaState();
_addAlpha.setBlendEnabled(true);
_addAlpha.setDstFunction(AlphaState.DB_ONE);
_defaultTestAlpha = createTestAlpha(DEFAULT_ALPHA_THRESHOLD);
_overlayZBuffer = renderer.createZBufferState();
_overlayZBuffer.setFunction(ZBufferState.CF_LEQUAL);
_overlayZBuffer.setWritable(false);
_emissiveLight = renderer.createLightState();
_emissiveLight.setGlobalAmbient(ColorRGBA.white);
_noFog = renderer.createFogState();
_noFog.setEnabled(false);
}
/**
* Creates an alpha state what will throw away fragments with alpha
* values less than or equal to the given threshold.
*/
protected static AlphaState createTestAlpha (float threshold)
{
AlphaState astate = DisplaySystem.getDisplaySystem().
getRenderer().createAlphaState();
astate.setBlendEnabled(true);
astate.setTestEnabled(true);
astate.setTestFunction(AlphaState.TF_GREATER);
astate.setReference(threshold);
return astate;
}
/**
* 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)
{
boolean drawing = (isEnabled() && r.isProcessingQueue());
if (drawing) {
if (_translucent) {
sortTriangles(r);
}
preDraw();
}
super.draw(r);
if (_overlays != null && drawing) {
for (int ii = 0, nn = _overlays.size(); ii < nn; ii++) {
preDrawOverlay(ii);
r.draw(this);
postDrawOverlay(ii);
}
}
}
/**
* Gives derived classes a chance to update states immediately before drawing.
*/
protected void preDraw ()
{
}
/**
* Updates the batch's states with those of the identified overlay.
*/
protected void preDrawOverlay (int oidx)
{
RenderState[] overlay = _overlays.get(oidx);
for (RenderState rstate : overlay) {
int idx = rstate.getType();
_ostates[idx] = states[idx];
states[idx] = rstate;
}
}
/**
* Restores the batch's original states after drawing an overlay.
*/
protected void postDrawOverlay (int oidx)
{
RenderState[] overlay = _overlays.get(oidx);
for (RenderState rstate : overlay) {
int idx = rstate.getType();
states[idx] = _ostates[idx];
}
}
/**
* 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);
// premultiply the scale and averaging factor
a *= dscale / 3f;
b *= dscale / 3f;
c *= dscale / 3f;
d *= dscale;
// 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(), idist;
if (_tcodes == null || _tcodes.length < tcount) {
_tcodes = new int[tcount];
}
int i1, i2, i3;
for (int ii = 0, idx = 0; ii < tcount; ii++) {
i1 = _oibuf[idx++] * 3;
i2 = _oibuf[idx++] * 3;
i3 = _oibuf[idx++] * 3;
idist = (int)(
a * (_vbuf[i1++] + _vbuf[i2++] + _vbuf[i3++]) +
b * (_vbuf[i1++] + _vbuf[i2++] + _vbuf[i3++]) +
c * (_vbuf[i1++] + _vbuf[i2++] + _vbuf[i3++]) + d);
_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, idx;
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);
}
/**
* Gives derived classes a chance to update the states immediately before drawing.
*
* @param oidx the index of the overlay being rendered, or -1 for the base layer.
*/
protected void processStates (int oidx)
{
}
/** Temporarily stores the original states. */
protected RenderState[] _ostates =
new RenderState[RenderState.RS_MAX_STATE];
}
/** The name of the texture specified in the model file, which acts as a
* property key and a default value. */
protected String _textureKey;
/** The name of this model's textures, or <code>null</code> for none. */
protected String[] _textures;
/** Whether or not to use sphere mapping on this model's textures. */
protected boolean _sphereMapped;
/** The filter mode to use on magnification. */
protected int _filterMode;
/** The mipmap mode to use on minification. */
protected int _mipMapMode;
/** Whether or not to use compressed textures, if available. */
protected boolean _compress;
/** The emissive map, if specified. */
protected String _emissiveMap;
/** Whether or not this mesh is completely emissive. */
protected boolean _emissive;
/** Whether or not this mesh should be rendered with additive blending. */
protected boolean _additive;
/** Whether or not this mesh can enable back-face culling. */
protected boolean _solid;
/** Whether or not this mesh must be rendered as transparent. */
protected boolean _transparent;
/** The alpha threshold below which fragments are discarded. */
protected float _alphaThreshold;
/** Whether or not the triangles of this mesh should be depth-sorted before
* rendering. */
protected boolean _translucent;
/** If non-null, additional layers to render over the base layer. */
protected ArrayList<RenderState[]> _overlays;
/** For prototype meshes, the resolved texture states. */
protected TextureState[] _tstates;
/** Whether or not the transform has been locked. This operates in a
* slightly different way than JME's locking, in that it allows applying
* 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;
/** The shared state for alpha blending. */
protected static AlphaState _blendAlpha;
/** The shared state for additive blending. */
protected static AlphaState _addAlpha;
/** The shared state for alpha testing with the default threshold. */
protected static AlphaState _defaultTestAlpha;
/** The shared state for checking, but not writing to, the z buffer. */
protected static ZBufferState _overlayZBuffer;
/** A light state that simulates emissivity. */
protected static LightState _emissiveLight;
/** A fog state that disables fog. */
protected static FogState _noFog;
/** 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;
/** The default alpha threshold. */
protected static final float DEFAULT_ALPHA_THRESHOLD = 0.5f;
private static final long serialVersionUID = 1;
}