private static Mesh getMeshFromGrid(final ResultGrid resultGrid, float worldScale) { Vector3f translate = new Vector3f(resultGrid.minLocation.x, resultGrid.minLocation.y, resultGrid.minLocation.z); float scale = 1.0f / worldScale; Mesh mesh = MarchingCubesMesher.createMesh(resultGrid.dataGrid, scale, translate); // Setup bone weight buffer FloatBuffer weights = BufferUtils.createFloatBuffer(mesh.getVertexCount() * 4); VertexBuffer weightsBuf = new VertexBuffer(VertexBuffer.Type.HWBoneWeight); weightsBuf.setupData(VertexBuffer.Usage.Static, 4, VertexBuffer.Format.Float, weights); mesh.setBuffer(weightsBuf); // Setup bone index buffer ByteBuffer indices = BufferUtils.createByteBuffer(mesh.getVertexCount() * 4); VertexBuffer indicesBuf = new VertexBuffer(VertexBuffer.Type.HWBoneIndex); indicesBuf.setupData(VertexBuffer.Usage.Static, 4, VertexBuffer.Format.UnsignedByte, indices); mesh.setBuffer(indicesBuf); Vector3f v1 = new Vector3f(); Vector3f v2 = new Vector3f(); Vector3f v3 = new Vector3f(); for (int i = 0; i < mesh.getTriangleCount(); i++) { mesh.getTriangle(i, v1, v2, v3); putBoneData(weights, indices, v1, resultGrid, scale, translate); putBoneData(weights, indices, v2, resultGrid, scale, translate); putBoneData(weights, indices, v3, resultGrid, scale, translate); } return mesh; }
public RenderDeviceJme(NiftyJmeDisplay display) { this.display = display; quadColor = new VertexBuffer(Type.Color); quadColor.setNormalized(true); ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4); quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb); quad.setBuffer(quadColor); quadModTC.setUsage(Usage.Stream); // Load the 3 material types separately to avoid // reloading the shader when the defines change. // Material with a single color (no texture or vertex color) colorMaterial = new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); // Material with a texture and a color (no vertex color) textureColorMaterial = new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); // Material with vertex color, used for gradients (no texture) vertexColorMaterial = new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); vertexColorMaterial.setBoolean("VertexColor", true); // Shared render state for all materials renderState.setDepthTest(false); renderState.setDepthWrite(false); }
public void clearVertexAttribs() { for (int i = 0; i < 16; i++) { VertexBuffer vb = context.boundAttribs[i]; if (vb != null) { int arrayType = convertArrayType(vb.getBufferType()); glDisableClientState(arrayType); context.boundAttribs[vb.getBufferType().ordinal()] = null; } } }
public static void compressIndexBuffer(Mesh mesh) { int vertCount = mesh.getVertexCount(); VertexBuffer vb = mesh.getBuffer(Type.Index); Format targetFmt; if (vb.getFormat() == Format.UnsignedInt && vertCount <= 0xffff) { if (vertCount <= 256) targetFmt = Format.UnsignedByte; else targetFmt = Format.UnsignedShort; } else if (vb.getFormat() == Format.UnsignedShort && vertCount <= 0xff) { targetFmt = Format.UnsignedByte; } else { return; } IndexBuffer src = mesh.getIndexBuffer(); Buffer newBuf = VertexBuffer.createBuffer(targetFmt, vb.getNumComponents(), src.size()); VertexBuffer newVb = new VertexBuffer(Type.Index); newVb.setupData(vb.getUsage(), vb.getNumComponents(), targetFmt, newBuf); mesh.clearBuffer(Type.Index); mesh.setBuffer(newVb); IndexBuffer dst = mesh.getIndexBuffer(); for (int i = 0; i < src.size(); i++) { dst.put(i, src.get(i)); } }
private void applyFrame(Mesh target, int frameIndex, float weight) { PoseFrame frame = frames[frameIndex]; VertexBuffer pb = target.getBuffer(Type.Position); for (int i = 0; i < frame.poses.length; i++) { Pose pose = frame.poses[i]; float poseWeight = frame.weights[i] * weight; pose.apply(poseWeight, (FloatBuffer) pb.getData()); } // force to re-upload data to gpu pb.updateData(pb.getData()); }
public static void generate(Mesh mesh, boolean approxTangents, boolean splitMirrored) { int[] index = new int[3]; Vector3f[] v = new Vector3f[3]; Vector2f[] t = new Vector2f[3]; for (int i = 0; i < 3; i++) { v[i] = new Vector3f(); t[i] = new Vector2f(); } if (mesh.getBuffer(Type.Normal) == null) { throw new IllegalArgumentException("The given mesh has no normal data!"); } List<VertexData> vertices; switch (mesh.getMode()) { case Triangles: vertices = processTriangles(mesh, index, v, t, splitMirrored); if (splitMirrored) { splitVertices(mesh, vertices, splitMirrored); } break; case TriangleStrip: vertices = processTriangleStrip(mesh, index, v, t); break; case TriangleFan: vertices = processTriangleFan(mesh, index, v, t); break; default: throw new UnsupportedOperationException(mesh.getMode() + " is not supported."); } processTriangleData(mesh, vertices, approxTangents, splitMirrored); // if the mesh has a bind pose, we need to generate the bind pose for the tangent buffer if (mesh.getBuffer(Type.BindPosePosition) != null) { VertexBuffer tangents = mesh.getBuffer(Type.Tangent); if (tangents != null) { VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent); bindTangents.setupData( Usage.CpuOnly, 4, Format.Float, BufferUtils.clone(tangents.getData())); if (mesh.getBuffer(Type.BindPoseTangent) != null) { mesh.clearBuffer(Type.BindPoseTangent); } mesh.setBuffer(bindTangents); tangents.setUsage(Usage.Stream); } } }
public static void printMesh(Mesh mesh) { for (int bufType = 0; bufType < Type.values().length; bufType++) { VertexBuffer outBuf = mesh.getBuffer(Type.values()[bufType]); if (outBuf == null) { continue; } System.out.println(outBuf.getBufferType() + ": "); for (int vert = 0; vert < outBuf.getNumElements(); vert++) { String str = "["; for (int comp = 0; comp < outBuf.getNumComponents(); comp++) { Object val = outBuf.getElementComponent(vert, comp); outBuf.setElementComponent(vert, comp, val); val = outBuf.getElementComponent(vert, comp); str += val; if (comp != outBuf.getNumComponents() - 1) { str += ", "; } } str += "]"; System.out.println(str); } System.out.println("------"); } }
public static VertexBuffer convertToUByte(VertexBuffer vb) { FloatBuffer fb = (FloatBuffer) vb.getData(); ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity()); convertToUByte(fb, bb); VertexBuffer newVb = new VertexBuffer(vb.getBufferType()); newVb.setupData(vb.getUsage(), vb.getNumComponents(), Format.UnsignedByte, bb); newVb.setNormalized(true); return newVb; }
public void renderMesh(Mesh mesh, int lod, int count) { if (mesh.getVertexCount() == 0) { return; } if (context.pointSize != mesh.getPointSize()) { glPointSize(mesh.getPointSize()); context.pointSize = mesh.getPointSize(); } if (context.lineWidth != mesh.getLineWidth()) { glLineWidth(mesh.getLineWidth()); context.lineWidth = mesh.getLineWidth(); } boolean dynamic = false; if (mesh.getBuffer(Type.InterleavedData) != null) { throw new UnsupportedOperationException("Interleaved meshes are not supported"); } if (mesh.getNumLodLevels() == 0) { for (VertexBuffer vb : mesh.getBufferList().getArray()) { if (vb.getUsage() != VertexBuffer.Usage.Static) { dynamic = true; break; } } } else { dynamic = true; } statistics.onMeshDrawn(mesh, lod); // if (!dynamic) { // dealing with a static object, generate display list // renderMeshDisplayList(mesh); // } else { renderMeshDefault(mesh, lod, count); // } }
public void renderQuad( int x, int y, int width, int height, Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) { ByteBuffer buf = (ByteBuffer) quadColor.getData(); buf.rewind(); buf.putInt(convertColor(topRight)); buf.putInt(convertColor(topLeft)); buf.putInt(convertColor(bottomLeft)); buf.putInt(convertColor(bottomRight)); buf.flip(); quadColor.updateData(buf); tempMat.loadIdentity(); tempMat.setTranslation(x, getHeight() - y, 0); tempMat.setScale(width, height, 0); rm.setWorldMatrix(tempMat); rm.setForcedRenderState(renderState); vertexColorMaterial.render(quadGeom, rm); // System.out.format("renderQuad2(%d, %d, %d, %d, %s, %s, %s, %s)\n", x, y, width, height, // topLeft.toString(), // // topRight.toString(), // // bottomRight.toString(), // // bottomLeft.toString()); }
public static VertexBuffer convertToFloat(VertexBuffer vb) { if (vb.getFormat() == Format.Float) return vb; IntBuffer ib = (IntBuffer) vb.getData(); FloatBuffer fb = BufferUtils.createFloatBuffer(ib.capacity()); convertToFloat(ib, fb); VertexBuffer newVb = new VertexBuffer(vb.getBufferType()); newVb.setupData(vb.getUsage(), vb.getNumComponents(), Format.Float, fb); return newVb; }
public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { Mesh.Mode mode = mesh.getMode(); Buffer indexData = indexBuf.getData(); indexData.rewind(); if (mesh.getMode() == Mode.Hybrid) { throw new UnsupportedOperationException(); /* int[] modeStart = mesh.getModeStart(); int[] elementLengths = mesh.getElementLengths(); int elMode = convertElementMode(Mode.Triangles); int fmt = convertVertexFormat(indexBuf.getFormat()); // int elSize = indexBuf.getFormat().getComponentSize(); // int listStart = modeStart[0]; int stripStart = modeStart[1]; int fanStart = modeStart[2]; int curOffset = 0; for (int i = 0; i < elementLengths.length; i++) { if (i == stripStart) { elMode = convertElementMode(Mode.TriangleStrip); } else if (i == fanStart) { elMode = convertElementMode(Mode.TriangleStrip); } int elementLength = elementLengths[i]; indexData.position(curOffset); drawElements(elMode, fmt, indexData); curOffset += elementLength; }*/ } else { drawElements(convertElementMode(mode), convertVertexFormat(indexBuf.getFormat()), indexData); } }
private void renderMeshDefault(Mesh mesh, int lod, int count) { VertexBuffer indices; VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); if (interleavedData != null && interleavedData.isUpdateNeeded()) { updateBufferData(interleavedData); } if (mesh.getNumLodLevels() > 0) { indices = mesh.getLodLevel(lod); } else { indices = mesh.getBuffer(Type.Index); } for (VertexBuffer vb : mesh.getBufferList().getArray()) { if (vb.getBufferType() == Type.InterleavedData || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers || vb.getBufferType() == Type.Index) { continue; } if (vb.getStride() == 0) { // not interleaved setVertexAttrib(vb); } else { // interleaved setVertexAttrib(vb, interleavedData); } } if (indices != null) { drawTriangleList(indices, mesh, count); } else { GL gl = GLContext.getCurrentGL(); gl.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount()); } // TODO: Fix these to use IDList?? clearVertexAttribs(); clearTextureUnits(); resetFixedFuncBindings(); }
// Don't remove splitmirorred boolean,It's not used right now, but i intend to // make this method also split vertice with rotated tangent space and I'll // add another splitRotated boolean private static List<VertexData> splitVertices( Mesh mesh, List<VertexData> vertexData, boolean splitMirorred) { int nbVertices = mesh.getBuffer(Type.Position).getNumElements(); List<VertexData> newVertices = new ArrayList<VertexData>(); Map<Integer, Integer> indiceMap = new HashMap<Integer, Integer>(); FloatBuffer normalBuffer = mesh.getFloatBuffer(Type.Normal); for (int i = 0; i < vertexData.size(); i++) { ArrayList<TriangleData> triangles = vertexData.get(i).triangles; Vector3f givenNormal = new Vector3f(); populateFromBuffer(givenNormal, normalBuffer, i); ArrayList<TriangleData> trianglesUp = new ArrayList<TriangleData>(); ArrayList<TriangleData> trianglesDown = new ArrayList<TriangleData>(); for (int j = 0; j < triangles.size(); j++) { TriangleData triangleData = triangles.get(j); if (parity(givenNormal, triangleData.normal) > 0) { trianglesUp.add(triangleData); } else { trianglesDown.add(triangleData); } } // if the vertex has triangles with opposite parity it has to be split if (!trianglesUp.isEmpty() && !trianglesDown.isEmpty()) { log.log(Level.FINE, "Splitting vertex {0}", i); // assigning triangle with the same parity to the original vertex vertexData.get(i).triangles.clear(); vertexData.get(i).triangles.addAll(trianglesUp); // creating a new vertex VertexData newVert = new VertexData(); // assigning triangles with opposite parity to it newVert.triangles.addAll(trianglesDown); newVertices.add(newVert); // keep vertex index to fix the index buffers later indiceMap.put(nbVertices, i); for (TriangleData tri : newVert.triangles) { for (int j = 0; j < tri.index.length; j++) { if (tri.index[j] == i) { tri.index[j] = nbVertices; } } } nbVertices++; } } if (!newVertices.isEmpty()) { // we have new vertices, we need to update the mesh's buffers. for (Type type : VertexBuffer.Type.values()) { // skip tangent buffer as we're gonna overwrite it later if (type == Type.Tangent || type == Type.BindPoseTangent) { continue; } VertexBuffer vb = mesh.getBuffer(type); // Some buffer (hardware skinning ones) can be there but not // initialized, they must be skipped. // They'll be initialized when Hardware Skinning is engaged if (vb == null || vb.getNumComponents() == 0) { continue; } Buffer buffer = vb.getData(); // IndexBuffer has special treatement, only swapping the vertex indices is needed if (type == Type.Index) { boolean isShortBuffer = vb.getFormat() == VertexBuffer.Format.UnsignedShort; for (VertexData vertex : newVertices) { for (TriangleData tri : vertex.triangles) { for (int i = 0; i < tri.index.length; i++) { if (isShortBuffer) { ((ShortBuffer) buffer).put(tri.triangleOffset + i, (short) tri.index[i]); } else { ((IntBuffer) buffer).put(tri.triangleOffset + i, tri.index[i]); } } } } vb.setUpdateNeeded(); } else { // copy the buffer in a bigger one and append nex vertices to the end Buffer newVerts = VertexBuffer.createBuffer(vb.getFormat(), vb.getNumComponents(), nbVertices); if (buffer != null) { buffer.rewind(); bulkPut(vb.getFormat(), newVerts, buffer); int index = vertexData.size(); newVerts.position(vertexData.size() * vb.getNumComponents()); for (int j = 0; j < newVertices.size(); j++) { int oldInd = indiceMap.get(index); for (int i = 0; i < vb.getNumComponents(); i++) { putValue(vb.getFormat(), newVerts, buffer, oldInd * vb.getNumComponents() + i); } index++; } vb.updateData(newVerts); // destroy previous buffer as it's no longer needed destroyDirectBuffer(buffer); } } } vertexData.addAll(newVertices); mesh.updateCounts(); } return vertexData; }
public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { if (vb.getBufferType() == VertexBuffer.Type.Color && !context.useVertexColor) { // Ignore vertex color buffer if vertex color is disabled. return; } int arrayType = convertArrayType(vb.getBufferType()); if (arrayType == -1) { return; // unsupported } glEnableClientState(arrayType); context.boundAttribs[vb.getBufferType().ordinal()] = vb; if (vb.getBufferType() == Type.Normal) { // normalize if requested if (vb.isNormalized() && !context.normalizeEnabled) { glEnable(GL_NORMALIZE); context.normalizeEnabled = true; } else if (!vb.isNormalized() && context.normalizeEnabled) { glDisable(GL_NORMALIZE); context.normalizeEnabled = false; } } // NOTE: Use data from interleaved buffer if specified Buffer data = idb != null ? idb.getData() : vb.getData(); int comps = vb.getNumComponents(); int type = convertVertexFormat(vb.getFormat()); data.rewind(); switch (vb.getBufferType()) { case Position: if (!(data instanceof FloatBuffer)) { throw new UnsupportedOperationException(); } glVertexPointer(comps, vb.getStride(), (FloatBuffer) data); break; case Normal: if (!(data instanceof FloatBuffer)) { throw new UnsupportedOperationException(); } glNormalPointer(vb.getStride(), (FloatBuffer) data); break; case Color: if (data instanceof FloatBuffer) { glColorPointer(comps, vb.getStride(), (FloatBuffer) data); } else if (data instanceof ByteBuffer) { glColorPointer(comps, true, vb.getStride(), (ByteBuffer) data); } else { throw new UnsupportedOperationException(); } break; case TexCoord: if (!(data instanceof FloatBuffer)) { throw new UnsupportedOperationException(); } glTexCoordPointer(comps, vb.getStride(), (FloatBuffer) data); break; default: // Ignore, this is an unsupported attribute for OpenGL1. break; } }
void resetToBind() { for (Mesh mesh : targets) { if (isMeshAnimated(mesh)) { FloatBuffer bwBuff = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); ByteBuffer biBuff = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData(); if (!biBuff.hasArray() || !bwBuff.hasArray()) { mesh.prepareForAnim(true); // prepare for software animation } VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition); VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal); VertexBuffer pos = mesh.getBuffer(Type.Position); VertexBuffer norm = mesh.getBuffer(Type.Normal); FloatBuffer pb = (FloatBuffer) pos.getData(); FloatBuffer nb = (FloatBuffer) norm.getData(); FloatBuffer bpb = (FloatBuffer) bindPos.getData(); FloatBuffer bnb = (FloatBuffer) bindNorm.getData(); pb.clear(); nb.clear(); bpb.clear(); bnb.clear(); // reseting bind tangents if there is a bind tangent buffer VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent); if (bindTangents != null) { VertexBuffer tangents = mesh.getBuffer(Type.Tangent); FloatBuffer tb = (FloatBuffer) tangents.getData(); FloatBuffer btb = (FloatBuffer) bindTangents.getData(); tb.clear(); btb.clear(); tb.put(btb).clear(); } pb.put(bpb).clear(); nb.put(bnb).clear(); } } }
/** * Merges all geometries in the collection into the output mesh. Creates a new material using the * TextureAtlas. * * @param geometries * @param outMesh */ public static void mergeGeometries(Collection<Geometry> geometries, Mesh outMesh) { int[] compsForBuf = new int[VertexBuffer.Type.values().length]; Format[] formatForBuf = new Format[compsForBuf.length]; int totalVerts = 0; int totalTris = 0; int totalLodLevels = 0; int maxWeights = -1; Mode mode = null; for (Geometry geom : geometries) { totalVerts += geom.getVertexCount(); totalTris += geom.getTriangleCount(); totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels()); Mode listMode; int components; switch (geom.getMesh().getMode()) { case Points: listMode = Mode.Points; components = 1; break; case LineLoop: case LineStrip: case Lines: listMode = Mode.Lines; components = 2; break; case TriangleFan: case TriangleStrip: case Triangles: listMode = Mode.Triangles; components = 3; break; default: throw new UnsupportedOperationException(); } for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) { int currentCompsForBuf = compsForBuf[vb.getBufferType().ordinal()]; if (vb.getBufferType() != Type.Index && currentCompsForBuf != 0 && currentCompsForBuf != vb.getNumComponents()) { throw new UnsupportedOperationException( "The geometry " + geom + " buffer " + vb.getBufferType() + " has different number of components than the rest of the meshes " + "(this: " + vb.getNumComponents() + ", expected: " + currentCompsForBuf + ")"); } compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents(); formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat(); } maxWeights = Math.max(maxWeights, geom.getMesh().getMaxNumWeights()); if (mode != null && mode != listMode) { throw new UnsupportedOperationException( "Cannot combine different" + " primitive types: " + mode + " != " + listMode); } mode = listMode; compsForBuf[Type.Index.ordinal()] = components; } outMesh.setMaxNumWeights(maxWeights); outMesh.setMode(mode); if (totalVerts >= 65536) { // make sure we create an UnsignedInt buffer so // we can fit all of the meshes formatForBuf[Type.Index.ordinal()] = Format.UnsignedInt; } else { formatForBuf[Type.Index.ordinal()] = Format.UnsignedShort; } // generate output buffers based on retrieved info for (int i = 0; i < compsForBuf.length; i++) { if (compsForBuf[i] == 0) { continue; } Buffer data; if (i == Type.Index.ordinal()) { data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris); } else { data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts); } VertexBuffer vb = new VertexBuffer(Type.values()[i]); vb.setupData(Usage.Static, compsForBuf[i], formatForBuf[i], data); outMesh.setBuffer(vb); } int globalVertIndex = 0; int globalTriIndex = 0; for (Geometry geom : geometries) { Mesh inMesh = geom.getMesh(); geom.computeWorldMatrix(); Matrix4f worldMatrix = geom.getWorldMatrix(); int geomVertCount = inMesh.getVertexCount(); int geomTriCount = inMesh.getTriangleCount(); for (int bufType = 0; bufType < compsForBuf.length; bufType++) { VertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]); VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]); if (inBuf == null || outBuf == null) { continue; } if (Type.Index.ordinal() == bufType) { int components = compsForBuf[bufType]; IndexBuffer inIdx = inMesh.getIndicesAsList(); IndexBuffer outIdx = outMesh.getIndexBuffer(); for (int tri = 0; tri < geomTriCount; tri++) { for (int comp = 0; comp < components; comp++) { int idx = inIdx.get(tri * components + comp) + globalVertIndex; outIdx.put((globalTriIndex + tri) * components + comp, idx); } } } else if (Type.Position.ordinal() == bufType) { FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); doTransformVerts(inPos, globalVertIndex, outPos, worldMatrix); } else if (Type.Normal.ordinal() == bufType) { FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix); } else if (Type.Tangent.ordinal() == bufType) { FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); int components = inBuf.getNumComponents(); doTransformTangents(inPos, globalVertIndex, components, outPos, worldMatrix); } else { inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount); } } globalVertIndex += geomVertCount; globalTriIndex += geomTriCount; } }
/** * Specific method for skinning with tangents to avoid cluttering the classic skinning calculation * with null checks that would slow down the process even if tangents don't have to be computed. * Also the iteration has additional indexes since tangent has 4 components instead of 3 for pos * and norm * * @param maxWeightsPerVert maximum number of weights per vertex * @param mesh the mesh * @param offsetMatrices the offsetMaytrices to apply * @param tb the tangent vertexBuffer */ private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) { int maxWeightsPerVert = mesh.getMaxNumWeights(); if (maxWeightsPerVert <= 0) { throw new IllegalStateException("Max weights per vert is incorrectly set!"); } int fourMinusMaxWeights = 4 - maxWeightsPerVert; // NOTE: This code assumes the vertex buffer is in bind pose // resetToBind() has been called this frame VertexBuffer vb = mesh.getBuffer(Type.Position); FloatBuffer fvb = (FloatBuffer) vb.getData(); fvb.rewind(); VertexBuffer nb = mesh.getBuffer(Type.Normal); FloatBuffer fnb = (FloatBuffer) nb.getData(); fnb.rewind(); FloatBuffer ftb = (FloatBuffer) tb.getData(); ftb.rewind(); // get boneIndexes and weights for mesh ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData(); FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); ib.rewind(); wb.rewind(); float[] weights = wb.array(); byte[] indices = ib.array(); int idxWeights = 0; TempVars vars = TempVars.get(); float[] posBuf = vars.skinPositions; float[] normBuf = vars.skinNormals; float[] tanBuf = vars.skinTangents; int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length)); int bufLength = 0; int tanLength = 0; for (int i = iterations - 1; i >= 0; i--) { // read next set of positions and normals from native buffer bufLength = Math.min(posBuf.length, fvb.remaining()); tanLength = Math.min(tanBuf.length, ftb.remaining()); fvb.get(posBuf, 0, bufLength); fnb.get(normBuf, 0, bufLength); ftb.get(tanBuf, 0, tanLength); int verts = bufLength / 3; int idxPositions = 0; // tangents has their own index because of the 4 components int idxTangents = 0; // iterate vertices and apply skinning transform for each effecting bone for (int vert = verts - 1; vert >= 0; vert--) { float nmx = normBuf[idxPositions]; float vtx = posBuf[idxPositions++]; float nmy = normBuf[idxPositions]; float vty = posBuf[idxPositions++]; float nmz = normBuf[idxPositions]; float vtz = posBuf[idxPositions++]; float tnx = tanBuf[idxTangents++]; float tny = tanBuf[idxTangents++]; float tnz = tanBuf[idxTangents++]; // skipping the 4th component of the tangent since it doesn't have to be transformed idxTangents++; float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0; for (int w = maxWeightsPerVert - 1; w >= 0; w--) { float weight = weights[idxWeights]; Matrix4f mat = offsetMatrices[indices[idxWeights++]]; rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight; ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight; rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight; rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight; rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight; rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight; rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight; rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight; rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight; } idxWeights += fourMinusMaxWeights; idxPositions -= 3; normBuf[idxPositions] = rnx; posBuf[idxPositions++] = rx; normBuf[idxPositions] = rny; posBuf[idxPositions++] = ry; normBuf[idxPositions] = rnz; posBuf[idxPositions++] = rz; idxTangents -= 4; tanBuf[idxTangents++] = rtx; tanBuf[idxTangents++] = rty; tanBuf[idxTangents++] = rtz; // once again skipping the 4th component of the tangent idxTangents++; } fvb.position(fvb.position() - bufLength); fvb.put(posBuf, 0, bufLength); fnb.position(fnb.position() - bufLength); fnb.put(normBuf, 0, bufLength); ftb.position(ftb.position() - tanLength); ftb.put(tanBuf, 0, tanLength); } vars.release(); vb.updateData(fvb); nb.updateData(fnb); tb.updateData(ftb); }
public void renderImage( RenderImage image, int x, int y, int w, int h, int srcX, int srcY, int srcW, int srcH, Color color, float scale, int centerX, int centerY) { RenderImageJme jmeImage = (RenderImageJme) image; Texture2D texture = jmeImage.getTexture(); textureColorMaterial.setColor("Color", convertColor(color, tempColor)); textureColorMaterial.setTexture("ColorMap", texture); float imageWidth = jmeImage.getWidth(); float imageHeight = jmeImage.getHeight(); FloatBuffer texCoords = (FloatBuffer) quadModTC.getData(); float startX = srcX / imageWidth; float startY = srcY / imageHeight; float endX = startX + (srcW / imageWidth); float endY = startY + (srcH / imageHeight); startY = 1f - startY; endY = 1f - endY; texCoords.rewind(); texCoords.put(startX).put(startY); texCoords.put(endX).put(startY); texCoords.put(endX).put(endY); texCoords.put(startX).put(endY); texCoords.flip(); quadModTC.updateData(texCoords); quad.clearBuffer(Type.TexCoord); quad.setBuffer(quadModTC); float x0 = centerX + (x - centerX) * scale; float y0 = centerY + (y - centerY) * scale; tempMat.loadIdentity(); tempMat.setTranslation(x0, getHeight() - y0, 0); tempMat.setScale(w * scale, h * scale, 0); rm.setWorldMatrix(tempMat); rm.setForcedRenderState(renderState); textureColorMaterial.render(quadGeom, rm); // System.out.format("renderImage2(%s, %d, %d, %d, %d, %d, %d, %d, %d, %s, %f, %d, %d)\n", // texture.getKey().toString(), // x, y, // w, h, srcX, srcY, srcW, srcH, // // color.toString(), scale, centerX, centerY); }
/** * This method returns an array of size 2. The first element is a vertex buffer holding bone * weights for every vertex in the model. The second element is a vertex buffer holding bone * indices for vertices (the indices of bones the vertices are assigned to). * * @param meshStructure the mesh structure object * @param vertexListSize a number of vertices in the model * @param bonesGroups this is an output parameter, it should be a one-sized array; the maximum * amount of weights per vertex (up to MAXIMUM_WEIGHTS_PER_VERTEX) is stored there * @param vertexReferenceMap this reference map allows to map the original vertices read from * blender to vertices that are really in the model; one vertex may appear several times in * the result model * @param groupToBoneIndexMap this object maps the group index (to which a vertices in blender * belong) to bone index of the model * @param blenderContext the blender context * @return arrays of vertices weights and their bone indices and (as an output parameter) the * maximum amount of weights for a vertex * @throws BlenderFileException this exception is thrown when the blend file structure is somehow * invalid or corrupted */ private VertexBuffer[] getBoneWeightAndIndexBuffer( Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext) throws BlenderFileException { Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert"); // dvert = DeformVERTices FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); if (pDvert.isNotNull()) { // assigning weights and bone indices List<Structure> dverts = pDvert.fetchData( blenderContext.getInputStream()); // dverts.size() == verticesAmount (one dvert per // vertex in blender) int vertexIndex = 0; for (Structure dvert : dverts) { int totweight = ((Number) dvert.getFieldValue("totweight")) .intValue(); // total amount of weights assignet to the vertex // (max. 4 in JME) Pointer pDW = (Pointer) dvert.getFieldValue("dw"); List<Integer> vertexIndices = vertexReferenceMap.get( Integer.valueOf(vertexIndex)); // we fetch the referenced vertices here if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap != null) { // pDW should never be null here, but I check it just in case :) int weightIndex = 0; List<Structure> dw = pDW.fetchData(blenderContext.getInputStream()); for (Structure deformWeight : dw) { Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue()); // Remove this code if 4 weights limitation is removed if (weightIndex == 4) { LOGGER.log( Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] {meshStructure.getName(), boneIndex}); break; } if (boneIndex != null) { // null here means that we came accross group that has no bone attached // to float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); if (weight == 0.0f) { weight = 1; boneIndex = Integer.valueOf(0); } // we apply the weight to all referenced vertices for (Integer index : vertexIndices) { weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight); indicesData.put( index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue()); } } ++weightIndex; } } else { for (Integer index : vertexIndices) { weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); } } ++vertexIndex; } } else { // always bind all vertices to 0-indexed bone // this bone makes the model look normally if vertices have no bone assigned // and it is used in object animation, so if we come accross object animation // we can use the 0-indexed bone for this for (List<Integer> vertexIndexList : vertexReferenceMap.values()) { // we apply the weight to all referenced vertices for (Integer index : vertexIndexList) { weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); } } } bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData); VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight); verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData); VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex); verticesWeightsIndices.setupData( Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData); return new VertexBuffer[] {verticesWeights, verticesWeightsIndices}; }
public static void convertToFixed(Geometry geom, Format posFmt, Format nmFmt, Format tcFmt) { geom.updateModelBound(); BoundingBox bbox = (BoundingBox) geom.getModelBound(); Mesh mesh = geom.getMesh(); VertexBuffer positions = mesh.getBuffer(Type.Position); VertexBuffer normals = mesh.getBuffer(Type.Normal); VertexBuffer texcoords = mesh.getBuffer(Type.TexCoord); VertexBuffer indices = mesh.getBuffer(Type.Index); // positions FloatBuffer fb = (FloatBuffer) positions.getData(); if (posFmt != Format.Float) { Buffer newBuf = VertexBuffer.createBuffer(posFmt, positions.getNumComponents(), mesh.getVertexCount()); Transform t = convertPositions(fb, bbox, newBuf); t.combineWithParent(geom.getLocalTransform()); geom.setLocalTransform(t); VertexBuffer newPosVb = new VertexBuffer(Type.Position); newPosVb.setupData(positions.getUsage(), positions.getNumComponents(), posFmt, newBuf); mesh.clearBuffer(Type.Position); mesh.setBuffer(newPosVb); } // normals, automatically convert to signed byte fb = (FloatBuffer) normals.getData(); ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity()); convertNormals(fb, bb); normals = new VertexBuffer(Type.Normal); normals.setupData(Usage.Static, 3, Format.Byte, bb); normals.setNormalized(true); mesh.clearBuffer(Type.Normal); mesh.setBuffer(normals); // texcoords fb = (FloatBuffer) texcoords.getData(); if (tcFmt != Format.Float) { Buffer newBuf = VertexBuffer.createBuffer(tcFmt, texcoords.getNumComponents(), mesh.getVertexCount()); convertTexCoords2D(fb, newBuf); VertexBuffer newTcVb = new VertexBuffer(Type.TexCoord); newTcVb.setupData(texcoords.getUsage(), texcoords.getNumComponents(), tcFmt, newBuf); mesh.clearBuffer(Type.TexCoord); mesh.setBuffer(newTcVb); } }
public class RenderDeviceJme implements RenderDevice { private NiftyJmeDisplay display; private RenderManager rm; private Renderer r; private HashMap<CachedTextKey, BitmapText> textCacheLastFrame = new HashMap<CachedTextKey, BitmapText>(); private HashMap<CachedTextKey, BitmapText> textCacheCurrentFrame = new HashMap<CachedTextKey, BitmapText>(); private final Quad quad = new Quad(1, -1, true); private final Geometry quadGeom = new Geometry("nifty-quad", quad); private boolean clipWasSet = false; private VertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord); private VertexBuffer quadModTC = quadDefaultTC.clone(); private VertexBuffer quadColor; private Matrix4f tempMat = new Matrix4f(); private ColorRGBA tempColor = new ColorRGBA(); private RenderState renderState = new RenderState(); private Material colorMaterial; private Material textureColorMaterial; private Material vertexColorMaterial; private static class CachedTextKey { BitmapFont font; String text; // ColorRGBA color; public CachedTextKey(BitmapFont font, String text /*, ColorRGBA color*/) { this.font = font; this.text = text; // this.color = color; } @Override public boolean equals(Object other) { CachedTextKey otherKey = (CachedTextKey) other; return font.equals(otherKey.font) && text.equals(otherKey.text) /* && color.equals(otherKey.color)*/; } @Override public int hashCode() { int hash = 5; hash = 53 * hash + font.hashCode(); hash = 53 * hash + text.hashCode(); // hash = 53 * hash + color.hashCode(); return hash; } } public RenderDeviceJme(NiftyJmeDisplay display) { this.display = display; quadColor = new VertexBuffer(Type.Color); quadColor.setNormalized(true); ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4); quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb); quad.setBuffer(quadColor); quadModTC.setUsage(Usage.Stream); // Load the 3 material types separately to avoid // reloading the shader when the defines change. // Material with a single color (no texture or vertex color) colorMaterial = new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); // Material with a texture and a color (no vertex color) textureColorMaterial = new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); // Material with vertex color, used for gradients (no texture) vertexColorMaterial = new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); vertexColorMaterial.setBoolean("VertexColor", true); // Shared render state for all materials renderState.setDepthTest(false); renderState.setDepthWrite(false); } public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {} public void setRenderManager(RenderManager rm) { this.rm = rm; this.r = rm.getRenderer(); } // TODO: Cursor support public MouseCursor createMouseCursor(String str, int x, int y) { return new MouseCursor() { public void dispose() {} }; } public void enableMouseCursor(MouseCursor cursor) {} public void disableMouseCursor() {} public RenderImage createImage(String filename, boolean linear) { // System.out.println("createImage(" + filename + ", " + linear + ")"); return new RenderImageJme(filename, linear, display); } public RenderFont createFont(String filename) { return new RenderFontJme(filename, display); } public void beginFrame() {} public void endFrame() { HashMap<CachedTextKey, BitmapText> temp = textCacheLastFrame; textCacheLastFrame = textCacheCurrentFrame; textCacheCurrentFrame = temp; textCacheCurrentFrame.clear(); rm.setForcedRenderState(null); } public int getWidth() { return display.getWidth(); } public int getHeight() { return display.getHeight(); } public void clear() {} public void setBlendMode(BlendMode blendMode) { renderState.setBlendMode(convertBlend(blendMode)); } private RenderState.BlendMode convertBlend(BlendMode blendMode) { if (blendMode == null) { return RenderState.BlendMode.Off; } else if (blendMode == BlendMode.BLEND) { return RenderState.BlendMode.Alpha; } else if (blendMode == BlendMode.MULIPLY) { return RenderState.BlendMode.Modulate; } else { throw new UnsupportedOperationException(); } } private int convertColor(Color color) { int color2 = 0; color2 |= ((int) (255.0 * color.getAlpha())) << 24; color2 |= ((int) (255.0 * color.getBlue())) << 16; color2 |= ((int) (255.0 * color.getGreen())) << 8; color2 |= ((int) (255.0 * color.getRed())); return color2; } private ColorRGBA convertColor(Color inColor, ColorRGBA outColor) { return outColor.set( inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha()); } @Override public void renderFont( RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) { if (str.length() == 0 || font instanceof RenderFontNull) { return; } RenderFontJme jmeFont = (RenderFontJme) font; ColorRGBA colorRgba = convertColor(color, tempColor); CachedTextKey key = new CachedTextKey(jmeFont.getFont(), str /*, colorRgba*/); BitmapText text = textCacheLastFrame.get(key); if (text == null) { text = jmeFont.createText(); text.setText(str); text.updateLogicalState(0); } textCacheCurrentFrame.put(key, text); // float width = text.getLineWidth(); // float height = text.getLineHeight(); float x0 = x; // + 0.5f * width * (1f - sizeX); float y0 = y; // + 0.5f * height * (1f - sizeY); tempMat.loadIdentity(); tempMat.setTranslation(x0, getHeight() - y0, 0); tempMat.setScale(sizeX, sizeY, 0); rm.setWorldMatrix(tempMat); rm.setForcedRenderState(renderState); text.setColor(colorRgba); text.updateLogicalState(0); text.render(rm, colorRgba); // System.out.format("renderFont(%s, %s, %d, %d, %s, %f, %f)\n", jmeFont.getFont(), str, // x, y, color.toString(), sizeX, sizeY); } public void renderImage( RenderImage image, int x, int y, int w, int h, int srcX, int srcY, int srcW, int srcH, Color color, float scale, int centerX, int centerY) { RenderImageJme jmeImage = (RenderImageJme) image; Texture2D texture = jmeImage.getTexture(); textureColorMaterial.setColor("Color", convertColor(color, tempColor)); textureColorMaterial.setTexture("ColorMap", texture); float imageWidth = jmeImage.getWidth(); float imageHeight = jmeImage.getHeight(); FloatBuffer texCoords = (FloatBuffer) quadModTC.getData(); float startX = srcX / imageWidth; float startY = srcY / imageHeight; float endX = startX + (srcW / imageWidth); float endY = startY + (srcH / imageHeight); startY = 1f - startY; endY = 1f - endY; texCoords.rewind(); texCoords.put(startX).put(startY); texCoords.put(endX).put(startY); texCoords.put(endX).put(endY); texCoords.put(startX).put(endY); texCoords.flip(); quadModTC.updateData(texCoords); quad.clearBuffer(Type.TexCoord); quad.setBuffer(quadModTC); float x0 = centerX + (x - centerX) * scale; float y0 = centerY + (y - centerY) * scale; tempMat.loadIdentity(); tempMat.setTranslation(x0, getHeight() - y0, 0); tempMat.setScale(w * scale, h * scale, 0); rm.setWorldMatrix(tempMat); rm.setForcedRenderState(renderState); textureColorMaterial.render(quadGeom, rm); // System.out.format("renderImage2(%s, %d, %d, %d, %d, %d, %d, %d, %d, %s, %f, %d, %d)\n", // texture.getKey().toString(), // x, y, // w, h, srcX, srcY, srcW, srcH, // // color.toString(), scale, centerX, centerY); } public void renderImage( RenderImage image, int x, int y, int width, int height, Color color, float imageScale) { RenderImageJme jmeImage = (RenderImageJme) image; textureColorMaterial.setColor("Color", convertColor(color, tempColor)); textureColorMaterial.setTexture("ColorMap", jmeImage.getTexture()); quad.clearBuffer(Type.TexCoord); quad.setBuffer(quadDefaultTC); float x0 = x + 0.5f * width * (1f - imageScale); float y0 = y + 0.5f * height * (1f - imageScale); tempMat.loadIdentity(); tempMat.setTranslation(x0, getHeight() - y0, 0); tempMat.setScale(width * imageScale, height * imageScale, 0); rm.setWorldMatrix(tempMat); rm.setForcedRenderState(renderState); textureColorMaterial.render(quadGeom, rm); // System.out.format("renderImage1(%s, %d, %d, %d, %d, %s, %f)\n", // jmeImage.getTexture().getKey().toString(), x, y, width, height, color.toString(), // imageScale); } public void renderQuad(int x, int y, int width, int height, Color color) { colorMaterial.setColor("Color", convertColor(color, tempColor)); tempMat.loadIdentity(); tempMat.setTranslation(x, getHeight() - y, 0); tempMat.setScale(width, height, 0); rm.setWorldMatrix(tempMat); rm.setForcedRenderState(renderState); colorMaterial.render(quadGeom, rm); // System.out.format("renderQuad1(%d, %d, %d, %d, %s)\n", x, y, width, height, // color.toString()); } public void renderQuad( int x, int y, int width, int height, Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) { ByteBuffer buf = (ByteBuffer) quadColor.getData(); buf.rewind(); buf.putInt(convertColor(topRight)); buf.putInt(convertColor(topLeft)); buf.putInt(convertColor(bottomLeft)); buf.putInt(convertColor(bottomRight)); buf.flip(); quadColor.updateData(buf); tempMat.loadIdentity(); tempMat.setTranslation(x, getHeight() - y, 0); tempMat.setScale(width, height, 0); rm.setWorldMatrix(tempMat); rm.setForcedRenderState(renderState); vertexColorMaterial.render(quadGeom, rm); // System.out.format("renderQuad2(%d, %d, %d, %d, %s, %s, %s, %s)\n", x, y, width, height, // topLeft.toString(), // // topRight.toString(), // // bottomRight.toString(), // // bottomLeft.toString()); } public void enableClip(int x0, int y0, int x1, int y1) { clipWasSet = true; r.setClipRect(x0, getHeight() - y1, x1 - x0, y1 - y0); } public void disableClip() { if (clipWasSet) { r.clearClipRect(); clipWasSet = false; } } }