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); }
private static void convertTexCoords2D(FloatBuffer input, Buffer output) { if (output.capacity() < input.capacity()) throw new RuntimeException("Output must be at least as large as input!"); input.clear(); output.clear(); Vector2f temp = new Vector2f(); int vertexCount = input.capacity() / 2; ShortBuffer sb = null; IntBuffer ib = null; if (output instanceof ShortBuffer) sb = (ShortBuffer) output; else if (output instanceof IntBuffer) ib = (IntBuffer) output; else throw new UnsupportedOperationException(); for (int i = 0; i < vertexCount; i++) { BufferUtils.populateFromBuffer(temp, input, i); if (sb != null) { sb.put((short) (temp.getX() * Short.MAX_VALUE)); sb.put((short) (temp.getY() * Short.MAX_VALUE)); } else { int v1 = (int) (temp.getX() * ((float) (1 << 16))); int v2 = (int) (temp.getY() * ((float) (1 << 16))); ib.put(v1).put(v2); } } }
private static List<VertexData> processTriangleStrip( Mesh mesh, int[] index, Vector3f[] v, Vector2f[] t) { IndexBuffer indexBuffer = mesh.getIndexBuffer(); FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData(); List<VertexData> vertices = initVertexData(vertexBuffer.limit() / 3); index[0] = indexBuffer.get(0); index[1] = indexBuffer.get(1); populateFromBuffer(v[0], vertexBuffer, index[0]); populateFromBuffer(v[1], vertexBuffer, index[1]); populateFromBuffer(t[0], textureBuffer, index[0]); populateFromBuffer(t[1], textureBuffer, index[1]); for (int i = 2; i < indexBuffer.size(); i++) { index[2] = indexBuffer.get(i); BufferUtils.populateFromBuffer(v[2], vertexBuffer, index[2]); BufferUtils.populateFromBuffer(t[2], textureBuffer, index[2]); boolean isDegenerate = isDegenerateTriangle(v[0], v[1], v[2]); TriangleData triData = processTriangle(index, v, t); if (triData != null && !isDegenerate) { vertices.get(index[0]).triangles.add(triData); vertices.get(index[1]).triangles.add(triData); vertices.get(index[2]).triangles.add(triData); } Vector3f vTemp = v[0]; v[0] = v[1]; v[1] = v[2]; v[2] = vTemp; Vector2f tTemp = t[0]; t[0] = t[1]; t[1] = t[2]; t[2] = tTemp; index[0] = index[1]; index[1] = index[2]; } return vertices; }
@Override public void extractTemplateFromMesh(Mesh mesh) { template = mesh; templateVerts = MeshUtils.getPositionBuffer(mesh); templateCoords = MeshUtils.getTexCoordBuffer(mesh); templateIndexes = MeshUtils.getIndexBuffer(mesh); templateNormals = MeshUtils.getNormalsBuffer(mesh); templateColors = BufferUtils.createFloatBuffer(templateVerts.capacity() / 3 * 4); }
protected ShortBuffer readShortBuffer(byte[] content) throws IOException { int length = readInt(content); if (length == BinaryOutputCapsule.NULL_OBJECT) return null; if (BinaryImporter.canUseFastBuffers()) { ByteBuffer value = BufferUtils.createByteBuffer(length * 2); value.put(content, index, length * 2).rewind(); index += length * 2; return value.asShortBuffer(); } else { ShortBuffer value = BufferUtils.createShortBuffer(length); for (int x = 0; x < length; x++) { value.put(readShortForBuffer(content)); } value.rewind(); return value; } }
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 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; }
private static void writeColorBuffer(List<VertexData> vertices, ColorRGBA[] cols, Mesh mesh) { FloatBuffer colors = BufferUtils.createFloatBuffer(vertices.size() * 4); colors.rewind(); for (ColorRGBA color : cols) { colors.put(color.r); colors.put(color.g); colors.put(color.b); colors.put(color.a); } mesh.clearBuffer(Type.Color); mesh.setBuffer(Type.Color, 4, colors); }
public static Mesh genNormalLines(Mesh mesh, float scale) { FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData(); ColorRGBA originColor = ColorRGBA.White; ColorRGBA normalColor = ColorRGBA.Blue; Mesh lineMesh = new Mesh(); lineMesh.setMode(Mesh.Mode.Lines); Vector3f origin = new Vector3f(); Vector3f point = new Vector3f(); FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.limit() * 2); FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.limit() / 3 * 4 * 2); for (int i = 0; i < vertexBuffer.limit() / 3; i++) { populateFromBuffer(origin, vertexBuffer, i); populateFromBuffer(point, normalBuffer, i); int index = i * 2; setInBuffer(origin, lineVertex, index); setInBuffer(originColor, lineColor, index); point.multLocal(scale); point.addLocal(origin); setInBuffer(point, lineVertex, index + 1); setInBuffer(normalColor, lineColor, index + 1); } lineMesh.setBuffer(Type.Position, 3, lineVertex); lineMesh.setBuffer(Type.Color, 4, lineColor); lineMesh.setStatic(); // lineMesh.setInterleaved(); return lineMesh; }
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 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); } } }
/** * Processes the given convex shape to retrieve a correctly ordered FloatBuffer to construct the * shape from with a TriMesh. * * @param convexShape the shape to retreieve the vertices from. * @return the vertices as a FloatBuffer, ordered as Triangles. */ private static FloatBuffer getVertices(ConvexShape convexShape) { // Check there is a hull shape to render if (convexShape.getUserPointer() == null) { // create a hull approximation ShapeHull hull = new ShapeHull(convexShape); float margin = convexShape.getMargin(); hull.buildHull(margin); convexShape.setUserPointer(hull); } // Assert state - should have a pointer to a hull (shape) that'll be drawn assert convexShape.getUserPointer() != null : "Should have a shape for the userPointer, instead got null"; ShapeHull hull = (ShapeHull) convexShape.getUserPointer(); // Assert we actually have a shape to render assert hull.numTriangles() > 0 : "Expecting the Hull shape to have triangles"; int numberOfTriangles = hull.numTriangles(); // The number of bytes needed is: (floats in a vertex) * (vertices in a triangle) * (# of // triangles) * (size of float in bytes) final int numberOfFloats = 3 * 3 * numberOfTriangles; FloatBuffer vertices = BufferUtils.createFloatBuffer(numberOfFloats); // Force the limit, set the cap - most number of floats we will use the buffer for vertices.limit(numberOfFloats); // Loop variables final IntArrayList hullIndicies = hull.getIndexPointer(); final List<Vector3f> hullVertices = hull.getVertexPointer(); Vector3f vertexA, vertexB, vertexC; int index = 0; for (int i = 0; i < numberOfTriangles; i++) { // Grab the data for this triangle from the hull vertexA = hullVertices.get(hullIndicies.get(index++)); vertexB = hullVertices.get(hullIndicies.get(index++)); vertexC = hullVertices.get(hullIndicies.get(index++)); // Put the verticies into the vertex buffer vertices.put(vertexA.x).put(vertexA.y).put(vertexA.z); vertices.put(vertexB.x).put(vertexB.y).put(vertexB.z); vertices.put(vertexC.x).put(vertexC.y).put(vertexC.z); } vertices.clear(); return vertices; }
/** Retrieves the vertices from the Triangle buffer. */ public FloatBuffer getVertices() { // There are 3 floats needed for each vertex (x,y,z) final int numberOfFloats = vertices.size() * 3; FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(numberOfFloats); // Force the limit, set the cap - most number of floats we will use the buffer for verticesBuffer.limit(numberOfFloats); // Copy the values from the list to the direct float buffer for (Vector3f v : vertices) { verticesBuffer.put(v.x).put(v.y).put(v.z); } vertices.clear(); return verticesBuffer; }
private void initOpenCL1() { clContext = context.getOpenCLContext(); Device device = clContext.getDevices().get(0); clQueue = clContext.createQueue(device).register(); // create kernel Program program = null; File tmpFolder = JmeSystem.getStorageFolder(); File binaryFile = new File(tmpFolder, getClass().getSimpleName() + ".clc"); try { // attempt to load cached binary byte[] bytes = Files.readAllBytes(binaryFile.toPath()); ByteBuffer bb = BufferUtils.createByteBuffer(bytes); program = clContext.createProgramFromBinary(bb, device); program.build(); LOG.info("reuse program from cached binaries"); } catch (java.nio.file.NoSuchFileException ex) { // do nothing, cache was not created yet } catch (Exception ex) { LOG.log(Level.INFO, "Unable to use cached program binaries", ex); } if (program == null) { // build from sources String source = "" + "__kernel void ScaleKernel(__global float* vb, float scale)\n" + "{\n" + " int idx = get_global_id(0);\n" + " float3 pos = vload3(idx, vb);\n" + " pos *= scale;\n" + " vstore3(pos, idx, vb);\n" + "}\n"; program = clContext.createProgramFromSourceCode(source); program.build(); // Save binary try { ByteBuffer bb = program.getBinary(device); byte[] bytes = new byte[bb.remaining()]; bb.get(bytes); Files.write(binaryFile.toPath(), bytes); } catch (UnsupportedOperationException | OpenCLException | IOException ex) { LOG.log(Level.SEVERE, "Unable to save program binaries", ex); } LOG.info("create new program from sources"); } program.register(); kernel = program.createKernel("ScaleKernel").register(); }
private static void convertNormals(FloatBuffer input, ByteBuffer output) { if (output.capacity() < input.capacity()) throw new RuntimeException("Output must be at least as large as input!"); input.clear(); output.clear(); Vector3f temp = new Vector3f(); int vertexCount = input.capacity() / 3; for (int i = 0; i < vertexCount; i++) { BufferUtils.populateFromBuffer(temp, input, i); // offset and scale vector into -128 ... 127 temp.multLocal(127).addLocal(0.5f, 0.5f, 0.5f); // quantize byte v1 = (byte) temp.getX(); byte v2 = (byte) temp.getY(); byte v3 = (byte) temp.getZ(); // store output.put(v1).put(v2).put(v3); } }
/** * Rebuilds the cylinder based on a new set of parameters. * * @param axisSamples the number of samples along the axis. * @param radialSamples the number of samples around the radial. * @param radius the radius of the bottom of the cylinder. * @param radius2 the radius of the top of the cylinder. * @param height the cylinder's height. * @param closed should the cylinder have top and bottom surfaces. * @param inverted is the cylinder is meant to be viewed from the inside. */ public void updateGeometry( int axisSamples, int radialSamples, float radius, float radius2, float height, boolean closed, boolean inverted) { this.axisSamples = axisSamples + (closed ? 2 : 0); this.radialSamples = radialSamples; this.radius = radius; this.radius2 = radius2; this.height = height; this.closed = closed; this.inverted = inverted; // VertexBuffer pvb = getBuffer(Type.Position); // VertexBuffer nvb = getBuffer(Type.Normal); // VertexBuffer tvb = getBuffer(Type.TexCoord); // Vertices int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0); setBuffer(Type.Position, 3, createVector3Buffer(getFloatBuffer(Type.Position), vertCount)); // Normals setBuffer(Type.Normal, 3, createVector3Buffer(getFloatBuffer(Type.Normal), vertCount)); // Texture co-ordinates setBuffer(Type.TexCoord, 2, createVector2Buffer(vertCount)); int triCount = ((closed ? 2 : 0) + 2 * (axisSamples - 1)) * radialSamples; setBuffer(Type.Index, 3, createShortBuffer(getShortBuffer(Type.Index), 3 * triCount)); // generate geometry float inverseRadial = 1.0f / radialSamples; float inverseAxisLess = 1.0f / (closed ? axisSamples - 3 : axisSamples - 1); float inverseAxisLessTexture = 1.0f / (axisSamples - 1); float halfHeight = 0.5f * height; // Generate points on the unit circle to be used in computing the mesh // points on a cylinder slice. float[] sin = new float[radialSamples + 1]; float[] cos = new float[radialSamples + 1]; for (int radialCount = 0; radialCount < radialSamples; radialCount++) { float angle = FastMath.TWO_PI * inverseRadial * radialCount; cos[radialCount] = FastMath.cos(angle); sin[radialCount] = FastMath.sin(angle); } sin[radialSamples] = sin[0]; cos[radialSamples] = cos[0]; // calculate normals Vector3f[] vNormals = null; Vector3f vNormal = Vector3f.UNIT_Z; if ((height != 0.0f) && (radius != radius2)) { vNormals = new Vector3f[radialSamples]; Vector3f vHeight = Vector3f.UNIT_Z.mult(height); Vector3f vRadial = new Vector3f(); for (int radialCount = 0; radialCount < radialSamples; radialCount++) { vRadial.set(cos[radialCount], sin[radialCount], 0.0f); Vector3f vRadius = vRadial.mult(radius); Vector3f vRadius2 = vRadial.mult(radius2); Vector3f vMantle = vHeight.subtract(vRadius2.subtract(vRadius)); Vector3f vTangent = vRadial.cross(Vector3f.UNIT_Z); vNormals[radialCount] = vMantle.cross(vTangent).normalize(); } } FloatBuffer nb = getFloatBuffer(Type.Normal); FloatBuffer pb = getFloatBuffer(Type.Position); FloatBuffer tb = getFloatBuffer(Type.TexCoord); // generate the cylinder itself Vector3f tempNormal = new Vector3f(); for (int axisCount = 0, i = 0; axisCount < axisSamples; axisCount++, i++) { float axisFraction; float axisFractionTexture; int topBottom = 0; if (!closed) { axisFraction = axisCount * inverseAxisLess; // in [0,1] axisFractionTexture = axisFraction; } else { if (axisCount == 0) { topBottom = -1; // bottom axisFraction = 0; axisFractionTexture = inverseAxisLessTexture; } else if (axisCount == axisSamples - 1) { topBottom = 1; // top axisFraction = 1; axisFractionTexture = 1 - inverseAxisLessTexture; } else { axisFraction = (axisCount - 1) * inverseAxisLess; axisFractionTexture = axisCount * inverseAxisLessTexture; } } // compute center of slice float z = -halfHeight + height * axisFraction; Vector3f sliceCenter = new Vector3f(0, 0, z); // compute slice vertices with duplication at end point int save = i; for (int radialCount = 0; radialCount < radialSamples; radialCount++, i++) { float radialFraction = radialCount * inverseRadial; // in [0,1) tempNormal.set(cos[radialCount], sin[radialCount], 0.0f); if (vNormals != null) { vNormal = vNormals[radialCount]; } else if (radius == radius2) { vNormal = tempNormal; } if (topBottom == 0) { if (!inverted) nb.put(vNormal.x).put(vNormal.y).put(vNormal.z); else nb.put(-vNormal.x).put(-vNormal.y).put(-vNormal.z); } else { nb.put(0).put(0).put(topBottom * (inverted ? -1 : 1)); } tempNormal.multLocal((radius - radius2) * axisFraction + radius2).addLocal(sliceCenter); pb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z); tb.put((inverted ? 1 - radialFraction : radialFraction)).put(axisFractionTexture); } BufferUtils.copyInternalVector3(pb, save, i); BufferUtils.copyInternalVector3(nb, save, i); tb.put((inverted ? 0.0f : 1.0f)).put(axisFractionTexture); } if (closed) { pb.put(0).put(0).put(-halfHeight); // bottom center nb.put(0).put(0).put(-1 * (inverted ? -1 : 1)); tb.put(0.5f).put(0); pb.put(0).put(0).put(halfHeight); // top center nb.put(0).put(0).put(1 * (inverted ? -1 : 1)); tb.put(0.5f).put(1); } IndexBuffer ib = getIndexBuffer(); int index = 0; // Connectivity for (int axisCount = 0, axisStart = 0; axisCount < axisSamples - 1; axisCount++) { int i0 = axisStart; int i1 = i0 + 1; axisStart += radialSamples + 1; int i2 = axisStart; int i3 = i2 + 1; for (int i = 0; i < radialSamples; i++) { if (closed && axisCount == 0) { if (!inverted) { ib.put(index++, i0++); ib.put(index++, vertCount - 2); ib.put(index++, i1++); } else { ib.put(index++, i0++); ib.put(index++, i1++); ib.put(index++, vertCount - 2); } } else if (closed && axisCount == axisSamples - 2) { ib.put(index++, i2++); ib.put(index++, inverted ? vertCount - 1 : i3++); ib.put(index++, inverted ? i3++ : vertCount - 1); } else { ib.put(index++, i0++); ib.put(index++, inverted ? i2 : i1); ib.put(index++, inverted ? i1 : i2); ib.put(index++, i1++); ib.put(index++, inverted ? i2++ : i3++); ib.put(index++, inverted ? i3++ : i2++); } } } updateBound(); }
private static Transform convertPositions(FloatBuffer input, BoundingBox bbox, Buffer output) { if (output.capacity() < input.capacity()) throw new RuntimeException("Output must be at least as large as input!"); Vector3f offset = bbox.getCenter().negate(); Vector3f size = new Vector3f(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent()); size.multLocal(2); ShortBuffer sb = null; ByteBuffer bb = null; float dataTypeSize; float dataTypeOffset; if (output instanceof ShortBuffer) { sb = (ShortBuffer) output; dataTypeOffset = shortOff; dataTypeSize = shortSize; } else { bb = (ByteBuffer) output; dataTypeOffset = byteOff; dataTypeSize = byteSize; } Vector3f scale = new Vector3f(); scale.set(dataTypeSize, dataTypeSize, dataTypeSize).divideLocal(size); Vector3f invScale = new Vector3f(); invScale.set(size).divideLocal(dataTypeSize); offset.multLocal(scale); offset.addLocal(dataTypeOffset, dataTypeOffset, dataTypeOffset); // offset = (-modelOffset * shortSize)/modelSize + shortOff // scale = shortSize / modelSize input.clear(); output.clear(); Vector3f temp = new Vector3f(); int vertexCount = input.capacity() / 3; for (int i = 0; i < vertexCount; i++) { BufferUtils.populateFromBuffer(temp, input, i); // offset and scale vector into -32768 ... 32767 // or into -128 ... 127 if using bytes temp.multLocal(scale); temp.addLocal(offset); // quantize and store if (sb != null) { short v1 = (short) temp.getX(); short v2 = (short) temp.getY(); short v3 = (short) temp.getZ(); sb.put(v1).put(v2).put(v3); } else { byte v1 = (byte) temp.getX(); byte v2 = (byte) temp.getY(); byte v3 = (byte) temp.getZ(); bb.put(v1).put(v2).put(v3); } } Transform transform = new Transform(); transform.setTranslation(offset.negate().multLocal(invScale)); transform.setScale(invScale); return transform; }
private static void processTriangleData( Mesh mesh, List<VertexData> vertices, boolean approxTangent, boolean splitMirrored) { ArrayList<VertexInfo> vertexMap = linkVertices(mesh, splitMirrored); FloatBuffer tangents = BufferUtils.createFloatBuffer(vertices.size() * 4); ColorRGBA[] cols = null; if (debug) { cols = new ColorRGBA[vertices.size()]; } Vector3f tangent = new Vector3f(); Vector3f binormal = new Vector3f(); // Vector3f normal = new Vector3f(); Vector3f givenNormal = new Vector3f(); Vector3f tangentUnit = new Vector3f(); Vector3f binormalUnit = new Vector3f(); for (int k = 0; k < vertexMap.size(); k++) { float wCoord = -1; VertexInfo vertexInfo = vertexMap.get(k); givenNormal.set(vertexInfo.normal); givenNormal.normalizeLocal(); TriangleData firstTriangle = vertices.get(vertexInfo.indices.get(0)).triangles.get(0); // check tangent and binormal consistency tangent.set(firstTriangle.tangent); tangent.normalizeLocal(); binormal.set(firstTriangle.binormal); binormal.normalizeLocal(); for (int i : vertexInfo.indices) { ArrayList<TriangleData> triangles = vertices.get(i).triangles; for (int j = 0; j < triangles.size(); j++) { TriangleData triangleData = triangles.get(j); tangentUnit.set(triangleData.tangent); tangentUnit.normalizeLocal(); if (tangent.dot(tangentUnit) < toleranceDot) { // log.log(Level.WARNING, // "Angle between tangents exceeds tolerance " // + "for vertex {0}.", i); break; } if (!approxTangent) { binormalUnit.set(triangleData.binormal); binormalUnit.normalizeLocal(); if (binormal.dot(binormalUnit) < toleranceDot) { // log.log(Level.WARNING, // "Angle between binormals exceeds tolerance " // + "for vertex {0}.", i); break; } } } } // find average tangent tangent.set(0, 0, 0); binormal.set(0, 0, 0); int triangleCount = 0; for (int i : vertexInfo.indices) { ArrayList<TriangleData> triangles = vertices.get(i).triangles; triangleCount += triangles.size(); if (debug) { cols[i] = ColorRGBA.White; } for (int j = 0; j < triangles.size(); j++) { TriangleData triangleData = triangles.get(j); tangent.addLocal(triangleData.tangent); binormal.addLocal(triangleData.binormal); } } int blameVertex = vertexInfo.indices.get(0); if (tangent.length() < ZERO_TOLERANCE) { log.log(Level.WARNING, "Shared tangent is zero for vertex {0}.", blameVertex); // attempt to fix from binormal if (binormal.length() >= ZERO_TOLERANCE) { binormal.cross(givenNormal, tangent); tangent.normalizeLocal(); } // if all fails use the tangent from the first triangle else { tangent.set(firstTriangle.tangent); } } else { tangent.divideLocal(triangleCount); } tangentUnit.set(tangent); tangentUnit.normalizeLocal(); if (Math.abs(Math.abs(tangentUnit.dot(givenNormal)) - 1) < ZERO_TOLERANCE) { log.log(Level.WARNING, "Normal and tangent are parallel for vertex {0}.", blameVertex); } if (!approxTangent) { if (binormal.length() < ZERO_TOLERANCE) { log.log(Level.WARNING, "Shared binormal is zero for vertex {0}.", blameVertex); // attempt to fix from tangent if (tangent.length() >= ZERO_TOLERANCE) { givenNormal.cross(tangent, binormal); binormal.normalizeLocal(); } // if all fails use the binormal from the first triangle else { binormal.set(firstTriangle.binormal); } } else { binormal.divideLocal(triangleCount); } binormalUnit.set(binormal); binormalUnit.normalizeLocal(); if (Math.abs(Math.abs(binormalUnit.dot(givenNormal)) - 1) < ZERO_TOLERANCE) { log.log(Level.WARNING, "Normal and binormal are parallel for vertex {0}.", blameVertex); } if (Math.abs(Math.abs(binormalUnit.dot(tangentUnit)) - 1) < ZERO_TOLERANCE) { log.log(Level.WARNING, "Tangent and binormal are parallel for vertex {0}.", blameVertex); } } Vector3f finalTangent = new Vector3f(); Vector3f tmp = new Vector3f(); for (int i : vertexInfo.indices) { if (approxTangent) { // Gram-Schmidt orthogonalize finalTangent .set(tangent) .subtractLocal(tmp.set(givenNormal).multLocal(givenNormal.dot(tangent))); finalTangent.normalizeLocal(); wCoord = tmp.set(givenNormal).crossLocal(tangent).dot(binormal) < 0f ? -1f : 1f; tangents.put((i * 4), finalTangent.x); tangents.put((i * 4) + 1, finalTangent.y); tangents.put((i * 4) + 2, finalTangent.z); tangents.put((i * 4) + 3, wCoord); } else { tangents.put((i * 4), tangent.x); tangents.put((i * 4) + 1, tangent.y); tangents.put((i * 4) + 2, tangent.z); tangents.put((i * 4) + 3, wCoord); // setInBuffer(binormal, binormals, i); } } } tangents.limit(tangents.capacity()); // If the model already had a tangent buffer, replace it with the regenerated one mesh.clearBuffer(Type.Tangent); mesh.setBuffer(Type.Tangent, 4, tangents); if (mesh.isAnimated()) { mesh.clearBuffer(Type.BindPoseNormal); mesh.clearBuffer(Type.BindPosePosition); mesh.clearBuffer(Type.BindPoseTangent); mesh.generateBindPose(true); } if (debug) { writeColorBuffer(vertices, cols, mesh); } mesh.updateBound(); mesh.updateCounts(); }
private static Mesh genTangentLines(Mesh mesh, float scale) { FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData(); FloatBuffer tangentBuffer = (FloatBuffer) mesh.getBuffer(Type.Tangent).getData(); FloatBuffer binormalBuffer = null; if (mesh.getBuffer(Type.Binormal) != null) { binormalBuffer = (FloatBuffer) mesh.getBuffer(Type.Binormal).getData(); } ColorRGBA originColor = ColorRGBA.White; ColorRGBA tangentColor = ColorRGBA.Red; ColorRGBA binormalColor = ColorRGBA.Green; ColorRGBA normalColor = ColorRGBA.Blue; Mesh lineMesh = new Mesh(); lineMesh.setMode(Mesh.Mode.Lines); Vector3f origin = new Vector3f(); Vector3f point = new Vector3f(); Vector3f tangent = new Vector3f(); Vector3f normal = new Vector3f(); IntBuffer lineIndex = BufferUtils.createIntBuffer(vertexBuffer.limit() / 3 * 6); FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.limit() * 4); FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.limit() / 3 * 4 * 4); boolean hasParity = mesh.getBuffer(Type.Tangent).getNumComponents() == 4; float tangentW = 1; for (int i = 0; i < vertexBuffer.limit() / 3; i++) { populateFromBuffer(origin, vertexBuffer, i); populateFromBuffer(normal, normalBuffer, i); if (hasParity) { tangent.x = tangentBuffer.get(i * 4); tangent.y = tangentBuffer.get(i * 4 + 1); tangent.z = tangentBuffer.get(i * 4 + 2); tangentW = tangentBuffer.get(i * 4 + 3); } else { populateFromBuffer(tangent, tangentBuffer, i); } int index = i * 4; int id = i * 6; lineIndex.put(id, index); lineIndex.put(id + 1, index + 1); lineIndex.put(id + 2, index); lineIndex.put(id + 3, index + 2); lineIndex.put(id + 4, index); lineIndex.put(id + 5, index + 3); setInBuffer(origin, lineVertex, index); setInBuffer(originColor, lineColor, index); point.set(tangent); point.multLocal(scale); point.addLocal(origin); setInBuffer(point, lineVertex, index + 1); setInBuffer(tangentColor, lineColor, index + 1); // wvBinormal = cross(wvNormal, wvTangent) * -inTangent.w if (binormalBuffer == null) { normal.cross(tangent, point); point.multLocal(-tangentW); point.normalizeLocal(); } else { populateFromBuffer(point, binormalBuffer, i); } point.multLocal(scale); point.addLocal(origin); setInBuffer(point, lineVertex, index + 2); setInBuffer(binormalColor, lineColor, index + 2); point.set(normal); point.multLocal(scale); point.addLocal(origin); setInBuffer(point, lineVertex, index + 3); setInBuffer(normalColor, lineColor, index + 3); } lineMesh.setBuffer(Type.Index, 1, lineIndex); lineMesh.setBuffer(Type.Position, 3, lineVertex); lineMesh.setBuffer(Type.Color, 4, lineColor); lineMesh.setStatic(); // lineMesh.setInterleaved(); return lineMesh; }
/** * <code>loadImage</code> is a manual image loader which is entirely independent of AWT. OUT: * RGB888 or RGBA8888 Image object * * @param in InputStream of an uncompressed 24b RGB or 32b RGBA TGA * @param flip Flip the image vertically * @return <code>Image</code> object that contains the image, either as a RGB888 or RGBA8888 * @throws java.io.IOException */ public static Image load(InputStream in, boolean flip) throws IOException { boolean flipH = false; // open a stream to the file DataInputStream dis = new DataInputStream(new BufferedInputStream(in)); // ---------- Start Reading the TGA header ---------- // // length of the image id (1 byte) int idLength = dis.readUnsignedByte(); // Type of color map (if any) included with the image // 0 - no color map data is included // 1 - a color map is included int colorMapType = dis.readUnsignedByte(); // Type of image being read: int imageType = dis.readUnsignedByte(); // Read Color Map Specification (5 bytes) // Index of first color map entry (if we want to use it, uncomment and remove extra read.) // short cMapStart = flipEndian(dis.readShort()); dis.readShort(); // number of entries in the color map short cMapLength = flipEndian(dis.readShort()); // number of bits per color map entry int cMapDepth = dis.readUnsignedByte(); // Read Image Specification (10 bytes) // horizontal coordinate of lower left corner of image. (if we want to use it, uncomment and // remove extra read.) // int xOffset = flipEndian(dis.readShort()); dis.readShort(); // vertical coordinate of lower left corner of image. (if we want to use it, uncomment and // remove extra read.) // int yOffset = flipEndian(dis.readShort()); dis.readShort(); // width of image - in pixels int width = flipEndian(dis.readShort()); // height of image - in pixels int height = flipEndian(dis.readShort()); // bits per pixel in image. int pixelDepth = dis.readUnsignedByte(); int imageDescriptor = dis.readUnsignedByte(); if ((imageDescriptor & 32) != 0) // bit 5 : if 1, flip top/bottom ordering { flip = !flip; } if ((imageDescriptor & 16) != 0) // bit 4 : if 1, flip left/right ordering { flipH = !flipH; } // ---------- Done Reading the TGA header ---------- // // Skip image ID if (idLength > 0) { dis.skip(idLength); } ColorMapEntry[] cMapEntries = null; if (colorMapType != 0) { // read the color map. int bytesInColorMap = (cMapDepth * cMapLength) >> 3; int bitsPerColor = Math.min(cMapDepth / 3, 8); byte[] cMapData = new byte[bytesInColorMap]; dis.read(cMapData); // Only go to the trouble of constructing the color map // table if this is declared a color mapped image. if (imageType == TYPE_COLORMAPPED || imageType == TYPE_COLORMAPPED_RLE) { cMapEntries = new ColorMapEntry[cMapLength]; int alphaSize = cMapDepth - (3 * bitsPerColor); float scalar = 255f / (FastMath.pow(2, bitsPerColor) - 1); float alphaScalar = 255f / (FastMath.pow(2, alphaSize) - 1); for (int i = 0; i < cMapLength; i++) { ColorMapEntry entry = new ColorMapEntry(); int offset = cMapDepth * i; entry.red = (byte) (int) (getBitsAsByte(cMapData, offset, bitsPerColor) * scalar); entry.green = (byte) (int) (getBitsAsByte(cMapData, offset + bitsPerColor, bitsPerColor) * scalar); entry.blue = (byte) (int) (getBitsAsByte(cMapData, offset + (2 * bitsPerColor), bitsPerColor) * scalar); if (alphaSize <= 0) { entry.alpha = (byte) 255; } else { entry.alpha = (byte) (int) (getBitsAsByte(cMapData, offset + (3 * bitsPerColor), alphaSize) * alphaScalar); } cMapEntries[i] = entry; } } } // Allocate image data array Format format; byte[] rawData = null; int dl; if (pixelDepth == 32) { rawData = new byte[width * height * 4]; dl = 4; } else { rawData = new byte[width * height * 3]; dl = 3; } int rawDataIndex = 0; if (imageType == TYPE_TRUECOLOR) { byte red = 0; byte green = 0; byte blue = 0; byte alpha = 0; // Faster than doing a 16-or-24-or-32 check on each individual pixel, // just make a seperate loop for each. if (pixelDepth == 16) { byte[] data = new byte[2]; float scalar = 255f / 31f; for (int i = 0; i <= (height - 1); i++) { if (!flip) { rawDataIndex = (height - 1 - i) * width * dl; } for (int j = 0; j < width; j++) { data[1] = dis.readByte(); data[0] = dis.readByte(); rawData[rawDataIndex++] = (byte) (int) (getBitsAsByte(data, 1, 5) * scalar); rawData[rawDataIndex++] = (byte) (int) (getBitsAsByte(data, 6, 5) * scalar); rawData[rawDataIndex++] = (byte) (int) (getBitsAsByte(data, 11, 5) * scalar); if (dl == 4) { // create an alpha channel alpha = getBitsAsByte(data, 0, 1); if (alpha == 1) { alpha = (byte) 255; } rawData[rawDataIndex++] = alpha; } } } format = dl == 4 ? Format.RGBA8 : Format.RGB8; } else if (pixelDepth == 24) { for (int y = 0; y < height; y++) { if (!flip) { rawDataIndex = (height - 1 - y) * width * dl; } else { rawDataIndex = y * width * dl; } dis.readFully(rawData, rawDataIndex, width * dl); // for (int x = 0; x < width; x++) { // read scanline // blue = dis.readByte(); // green = dis.readByte(); // red = dis.readByte(); // rawData[rawDataIndex++] = red; // rawData[rawDataIndex++] = green; // rawData[rawDataIndex++] = blue; // } } format = Format.BGR8; } else if (pixelDepth == 32) { for (int i = 0; i <= (height - 1); i++) { if (!flip) { rawDataIndex = (height - 1 - i) * width * dl; } for (int j = 0; j < width; j++) { blue = dis.readByte(); green = dis.readByte(); red = dis.readByte(); alpha = dis.readByte(); rawData[rawDataIndex++] = red; rawData[rawDataIndex++] = green; rawData[rawDataIndex++] = blue; rawData[rawDataIndex++] = alpha; } } format = Format.RGBA8; } else { throw new IOException("Unsupported TGA true color depth: " + pixelDepth); } } else if (imageType == TYPE_TRUECOLOR_RLE) { byte red = 0; byte green = 0; byte blue = 0; byte alpha = 0; // Faster than doing a 16-or-24-or-32 check on each individual pixel, // just make a seperate loop for each. if (pixelDepth == 32) { for (int i = 0; i <= (height - 1); ++i) { if (!flip) { rawDataIndex = (height - 1 - i) * width * dl; } for (int j = 0; j < width; ++j) { // Get the number of pixels the next chunk covers (either packed or unpacked) int count = dis.readByte(); if ((count & 0x80) != 0) { // Its an RLE packed block - use the following 1 pixel for the next <count> pixels count &= 0x07f; j += count; blue = dis.readByte(); green = dis.readByte(); red = dis.readByte(); alpha = dis.readByte(); while (count-- >= 0) { rawData[rawDataIndex++] = red; rawData[rawDataIndex++] = green; rawData[rawDataIndex++] = blue; rawData[rawDataIndex++] = alpha; } } else { // Its not RLE packed, but the next <count> pixels are raw. j += count; while (count-- >= 0) { blue = dis.readByte(); green = dis.readByte(); red = dis.readByte(); alpha = dis.readByte(); rawData[rawDataIndex++] = red; rawData[rawDataIndex++] = green; rawData[rawDataIndex++] = blue; rawData[rawDataIndex++] = alpha; } } } } format = Format.RGBA8; } else if (pixelDepth == 24) { for (int i = 0; i <= (height - 1); i++) { if (!flip) { rawDataIndex = (height - 1 - i) * width * dl; } for (int j = 0; j < width; ++j) { // Get the number of pixels the next chunk covers (either packed or unpacked) int count = dis.readByte(); if ((count & 0x80) != 0) { // Its an RLE packed block - use the following 1 pixel for the next <count> pixels count &= 0x07f; j += count; blue = dis.readByte(); green = dis.readByte(); red = dis.readByte(); while (count-- >= 0) { rawData[rawDataIndex++] = red; rawData[rawDataIndex++] = green; rawData[rawDataIndex++] = blue; } } else { // Its not RLE packed, but the next <count> pixels are raw. j += count; while (count-- >= 0) { blue = dis.readByte(); green = dis.readByte(); red = dis.readByte(); rawData[rawDataIndex++] = red; rawData[rawDataIndex++] = green; rawData[rawDataIndex++] = blue; } } } } format = Format.RGB8; } else if (pixelDepth == 16) { byte[] data = new byte[2]; float scalar = 255f / 31f; for (int i = 0; i <= (height - 1); i++) { if (!flip) { rawDataIndex = (height - 1 - i) * width * dl; } for (int j = 0; j < width; j++) { // Get the number of pixels the next chunk covers (either packed or unpacked) int count = dis.readByte(); if ((count & 0x80) != 0) { // Its an RLE packed block - use the following 1 pixel for the next <count> pixels count &= 0x07f; j += count; data[1] = dis.readByte(); data[0] = dis.readByte(); blue = (byte) (int) (getBitsAsByte(data, 1, 5) * scalar); green = (byte) (int) (getBitsAsByte(data, 6, 5) * scalar); red = (byte) (int) (getBitsAsByte(data, 11, 5) * scalar); while (count-- >= 0) { rawData[rawDataIndex++] = red; rawData[rawDataIndex++] = green; rawData[rawDataIndex++] = blue; } } else { // Its not RLE packed, but the next <count> pixels are raw. j += count; while (count-- >= 0) { data[1] = dis.readByte(); data[0] = dis.readByte(); blue = (byte) (int) (getBitsAsByte(data, 1, 5) * scalar); green = (byte) (int) (getBitsAsByte(data, 6, 5) * scalar); red = (byte) (int) (getBitsAsByte(data, 11, 5) * scalar); rawData[rawDataIndex++] = red; rawData[rawDataIndex++] = green; rawData[rawDataIndex++] = blue; } } } } format = Format.RGB8; } else { throw new IOException("Unsupported TGA true color depth: " + pixelDepth); } } else if (imageType == TYPE_COLORMAPPED) { int bytesPerIndex = pixelDepth / 8; if (bytesPerIndex == 1) { for (int i = 0; i <= (height - 1); i++) { if (!flip) { rawDataIndex = (height - 1 - i) * width * dl; } for (int j = 0; j < width; j++) { int index = dis.readUnsignedByte(); if (index >= cMapEntries.length || index < 0) { throw new IOException("TGA: Invalid color map entry referenced: " + index); } ColorMapEntry entry = cMapEntries[index]; rawData[rawDataIndex++] = entry.blue; rawData[rawDataIndex++] = entry.green; rawData[rawDataIndex++] = entry.red; if (dl == 4) { rawData[rawDataIndex++] = entry.alpha; } } } } else if (bytesPerIndex == 2) { for (int i = 0; i <= (height - 1); i++) { if (!flip) { rawDataIndex = (height - 1 - i) * width * dl; } for (int j = 0; j < width; j++) { int index = flipEndian(dis.readShort()); if (index >= cMapEntries.length || index < 0) { throw new IOException("TGA: Invalid color map entry referenced: " + index); } ColorMapEntry entry = cMapEntries[index]; rawData[rawDataIndex++] = entry.blue; rawData[rawDataIndex++] = entry.green; rawData[rawDataIndex++] = entry.red; if (dl == 4) { rawData[rawDataIndex++] = entry.alpha; } } } } else { throw new IOException("TGA: unknown colormap indexing size used: " + bytesPerIndex); } format = dl == 4 ? Format.RGBA8 : Format.RGB8; } else { throw new IOException("Monochrome and RLE colormapped images are not supported"); } in.close(); // Get a pointer to the image memory ByteBuffer scratch = BufferUtils.createByteBuffer(rawData.length); scratch.clear(); scratch.put(rawData); scratch.rewind(); // Create the Image object Image textureImage = new Image(); textureImage.setFormat(format); textureImage.setWidth(width); textureImage.setHeight(height); textureImage.setData(scratch); return textureImage; }
@Override public void initParticleData(Emitter emitter, int numParticles) { setMode(Mode.Triangles); this.emitter = emitter; this.finVerts = BufferUtils.createFloatBuffer(templateVerts.capacity() * numParticles); try { this.finCoords = BufferUtils.createFloatBuffer(templateCoords.capacity() * numParticles); } catch (Exception e) { } this.finIndexes = BufferUtils.createShortBuffer(templateIndexes.size() * numParticles); this.finNormals = BufferUtils.createFloatBuffer(templateNormals.capacity() * numParticles); this.finColors = BufferUtils.createFloatBuffer(templateVerts.capacity() / 3 * 4 * numParticles); int index = 0, index2 = 0, index3 = 0, index4 = 0, index5 = 0; int indexOffset = 0; for (int i = 0; i < numParticles; i++) { templateVerts.rewind(); for (int v = 0; v < templateVerts.capacity(); v += 3) { tempV3.set(templateVerts.get(v), templateVerts.get(v + 1), templateVerts.get(v + 2)); finVerts.put(index, tempV3.getX()); index++; finVerts.put(index, tempV3.getY()); index++; finVerts.put(index, tempV3.getZ()); index++; } try { templateCoords.rewind(); for (int v = 0; v < templateCoords.capacity(); v++) { finCoords.put(index2, templateCoords.get(v)); index2++; } } catch (Exception e) { } for (int v = 0; v < templateIndexes.size(); v++) { finIndexes.put(index3, (short) (templateIndexes.get(v) + indexOffset)); index3++; } indexOffset += templateVerts.capacity() / 3; templateNormals.rewind(); for (int v = 0; v < templateNormals.capacity(); v++) { finNormals.put(index4, templateNormals.get(v)); index4++; } for (int v = 0; v < finColors.capacity(); v++) { finColors.put(v, 1.0f); } } // Help GC // tempV3 = null; // templateVerts = null; // templateCoords = null; // templateIndexes = null; // templateNormals = null; // Clear & ssign buffers this.clearBuffer(VertexBuffer.Type.Position); this.setBuffer(VertexBuffer.Type.Position, 3, finVerts); this.clearBuffer(VertexBuffer.Type.TexCoord); try { this.setBuffer(VertexBuffer.Type.TexCoord, 2, finCoords); } catch (Exception e) { } this.clearBuffer(VertexBuffer.Type.Index); this.setBuffer(VertexBuffer.Type.Index, 3, finIndexes); this.clearBuffer(VertexBuffer.Type.Normal); this.setBuffer(VertexBuffer.Type.Normal, 3, finNormals); this.clearBuffer(VertexBuffer.Type.Color); this.setBuffer(VertexBuffer.Type.Color, 4, finColors); this.updateBound(); }
/** * Updates the geometry with the given parameters. Bounds are expressed as if the arrow was * pointing to the right, i.e.: x = length of the arrow (distance to the pointy bit.) y = height * of the arrow z = extrusion length. */ public void updateGeometry(Orientation orientation, Vector3f bounds, boolean isExtruded) { // Create three points, according to the size and orientation. Vector3f p0 = new Vector3f(), p1, p2; switch (orientation) { case LEFT: p1 = new Vector3f(0, bounds.y, 0); p2 = new Vector3f(-bounds.x, bounds.y / 2, 0); break; case RIGHT: p1 = new Vector3f(bounds.x, bounds.y / 2, 0); p2 = new Vector3f(0, bounds.y, 0); break; case UP: p1 = new Vector3f(bounds.y, 0, 0); p2 = new Vector3f(bounds.y / 2, bounds.x, 0); break; case DOWN: p1 = new Vector3f(bounds.y / 2, -bounds.x, 0); p2 = new Vector3f(bounds.y, 0, 0); break; default: p2 = p1 = p0; } // Then, make a list of vertices and indices. Vector3f[] vertices, normals; int[] indices; if (!isExtruded) { // For a flat triangle, easy one, just take the initial point and link them. vertices = new Vector3f[3]; vertices[0] = p0; vertices[1] = p1; vertices[2] = p2; // The normals are all along Z. normals = new Vector3f[3]; normals[0] = Vector3f.UNIT_Z; normals[1] = Vector3f.UNIT_Z; normals[2] = Vector3f.UNIT_Z; indices = new int[] {0, 1, 2}; } else { // For an extruded triangle vertices = new Vector3f[18]; // First (front) triangle is regular. vertices[0] = p0; vertices[1] = p1; vertices[2] = p2; // Back triangle is easy too: retake first triangle and shift the z-coordinate. vertices[3] = new Vector3f(p0.x, p0.y, bounds.z); vertices[4] = new Vector3f(p1.x, p1.y, bounds.z); vertices[5] = new Vector3f(p2.x, p2.y, bounds.z); // Then duplicate side vertices to allow hard edges normals, resulting in 4 quads. vertices[6] = vertices[0]; vertices[7] = vertices[3]; vertices[8] = vertices[1]; vertices[9] = vertices[4]; vertices[10] = vertices[1]; vertices[11] = vertices[4]; vertices[12] = vertices[2]; vertices[13] = vertices[5]; vertices[14] = vertices[2]; vertices[15] = vertices[5]; vertices[16] = vertices[0]; vertices[17] = vertices[3]; // Normals: normals = new Vector3f[18]; // The front side normal is still the same as for a flat. normals[0] = Vector3f.UNIT_Z; normals[1] = Vector3f.UNIT_Z; normals[2] = Vector3f.UNIT_Z; // Backside is the same, but reversed (d'uh). normals[3] = Vector3f.UNIT_Z.negate(); normals[4] = normals[3]; normals[5] = normals[3]; // Finally, sides normals are computed from the triangle sides. Vector3f normal1 = new Vector3f(p1.y - p0.y, p0.x - p1.x, 0); Vector3f normal2 = new Vector3f(p2.y - p1.y, p1.x - p2.x, 0); Vector3f normal3 = new Vector3f(p0.y - p2.y, p2.x - p0.x, 0); normals[6] = normal1; normals[7] = normal1; normals[8] = normal1; normals[9] = normal1; normals[10] = normal2; normals[11] = normal2; normals[12] = normal2; normals[13] = normal2; normals[14] = normal3; normals[15] = normal3; normals[16] = normal3; normals[17] = normal3; // Finally, the indices. indices = new int[] { // Front 0, 1, 2, // Back 5, 4, 3, // Sides 6, 7, 8, 9, 8, 7, 10, 11, 12, 13, 12, 11, 14, 15, 16, 17, 16, 15 }; } // Set the mesh data setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices)); setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals)); setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indices)); updateBound(); // setStatic(); }
public Mesh compileArrays() { // Assuming the shape is a perfect cube Vector3f v0, v1, v2, v3, v4, v5, v6, v7; float s = o.getEdgeSize() / 2f; v0 = o.getOrigin().add(new Vector3f(s, s, s)); v1 = o.getOrigin().add(new Vector3f(s, s, -s)); v2 = o.getOrigin().add(new Vector3f(-s, s, -s)); v3 = o.getOrigin().add(new Vector3f(-s, s, s)); v4 = o.getOrigin().add(new Vector3f(s, -s, s)); v5 = o.getOrigin().add(new Vector3f(s, -s, -s)); v6 = o.getOrigin().add(new Vector3f(-s, -s, -s)); v7 = o.getOrigin().add(new Vector3f(-s, -s, s)); posArray = new float[12 * 6]; texCoordsArray = new float[8 * 6]; normArray = new float[12 * 6]; tanArray = new float[16 * 6]; indArray = new short[6 * 6]; for (int i = 0; i < 6; i++) { QuadV4 quad; switch (i) { case 0: quad = new QuadV4(v7, v4, v0, v3, o, i); break; case 1: quad = new QuadV4(v4, v5, v1, v0, o, i); break; case 2: quad = new QuadV4(v5, v6, v2, v1, o, i); break; case 3: quad = new QuadV4(v6, v7, v3, v2, o, i); break; case 4: quad = new QuadV4(v3, v0, v1, v2, o, i); break; case 5: quad = new QuadV4(v6, v5, v4, v7, o, i); break; default: throw new IllegalStateException("Wrong side number"); } System.arraycopy( quad.positionArray, 0, posArray, i * quad.positionArray.length, quad.positionArray.length); System.arraycopy( quad.texCoordsArray, 0, texCoordsArray, i * quad.texCoordsArray.length, quad.texCoordsArray.length); System.arraycopy( quad.normalArray, 0, normArray, i * quad.normalArray.length, quad.normalArray.length); // System.arraycopy(quad.tangentArray, 0, tanArray, i * quad.tangentArray.length, // quad.tangentArray.length); for (int j = 0; j < quad.indexArray.length; j++) { short val = quad.indexArray[j]; val += 4 * i; indArray[(quad.indexArray.length * i) + j] = val; } } Mesh mesh = new Mesh(); mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(posArray)); mesh.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoordsArray)); mesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normArray)); mesh.setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(indArray)); mesh.updateBound(); return mesh; }
public class LwjglGL1Renderer implements GL1Renderer { private static final Logger logger = Logger.getLogger(LwjglRenderer.class.getName()); private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250); private final StringBuilder stringBuf = new StringBuilder(250); private final IntBuffer ib1 = BufferUtils.createIntBuffer(1); private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16); private final FloatBuffer fb16 = BufferUtils.createFloatBuffer(16); private final FloatBuffer fb4Null = BufferUtils.createFloatBuffer(4); private final RenderContext context = new RenderContext(); private final NativeObjectManager objManager = new NativeObjectManager(); private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class); private int maxTexSize; private int maxCubeTexSize; private int maxVertCount; private int maxTriCount; private int maxLights; private boolean gl12 = false; private final Statistics statistics = new Statistics(); private int vpX, vpY, vpW, vpH; private int clipX, clipY, clipW, clipH; private Matrix4f worldMatrix = new Matrix4f(); private Matrix4f viewMatrix = new Matrix4f(); private ArrayList<Light> lightList = new ArrayList<Light>(8); private ColorRGBA materialAmbientColor = new ColorRGBA(); private Vector3f tempVec = new Vector3f(); protected void updateNameBuffer() { int len = stringBuf.length(); nameBuf.position(0); nameBuf.limit(len); for (int i = 0; i < len; i++) { nameBuf.put((byte) stringBuf.charAt(i)); } nameBuf.rewind(); } public Statistics getStatistics() { return statistics; } public EnumSet<Caps> getCaps() { return caps; } public void initialize() { if (GLContext.getCapabilities().OpenGL12) { gl12 = true; } // Default values for certain GL state. glShadeModel(GL_SMOOTH); glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Enable rescaling/normaling of normal vectors. // Fixes lighting issues with scaled models. if (gl12) { glEnable(GL12.GL_RESCALE_NORMAL); } else { glEnable(GL_NORMALIZE); } if (GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) { caps.add(Caps.NonPowerOfTwoTextures); } else { logger.log( Level.WARNING, "Your graphics card does not " + "support non-power-of-2 textures. " + "Some features might not work."); } maxLights = glGetInteger(GL_MAX_LIGHTS); maxTexSize = glGetInteger(GL_MAX_TEXTURE_SIZE); } public void invalidateState() { context.reset(); } public void resetGLObjects() { logger.log(Level.FINE, "Reseting objects and invalidating state"); objManager.resetObjects(); statistics.clearMemory(); invalidateState(); } public void cleanup() { logger.log(Level.FINE, "Deleting objects and invalidating state"); objManager.deleteAllObjects(this); statistics.clearMemory(); invalidateState(); } public void setDepthRange(float start, float end) { glDepthRange(start, end); } public void clearBuffers(boolean color, boolean depth, boolean stencil) { int bits = 0; if (color) { // See explanations of the depth below, we must enable color write to be able to clear the // color buffer if (context.colorWriteEnabled == false) { glColorMask(true, true, true, true); context.colorWriteEnabled = true; } bits = GL_COLOR_BUFFER_BIT; } if (depth) { // glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false // here s some link on openl board // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223 // if depth clear is requested, we enable the depthMask if (context.depthWriteEnabled == false) { glDepthMask(true); context.depthWriteEnabled = true; } bits |= GL_DEPTH_BUFFER_BIT; } if (stencil) { bits |= GL_STENCIL_BUFFER_BIT; } if (bits != 0) { glClear(bits); } } public void setBackgroundColor(ColorRGBA color) { glClearColor(color.r, color.g, color.b, color.a); } private void setMaterialColor(int type, ColorRGBA color, ColorRGBA defaultColor) { if (color != null) { fb16.put(color.r).put(color.g).put(color.b).put(color.a).flip(); } else { fb16.put(defaultColor.r).put(defaultColor.g).put(defaultColor.b).put(defaultColor.a).flip(); } glMaterial(GL_FRONT_AND_BACK, type, fb16); } /** Applies fixed function bindings from the context to OpenGL */ private void applyFixedFuncBindings(boolean forLighting) { if (forLighting) { glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, context.shininess); setMaterialColor(GL_AMBIENT, context.ambient, ColorRGBA.DarkGray); setMaterialColor(GL_DIFFUSE, context.diffuse, ColorRGBA.White); setMaterialColor(GL_SPECULAR, context.specular, ColorRGBA.Black); if (context.useVertexColor) { glEnable(GL_COLOR_MATERIAL); } else { glDisable(GL_COLOR_MATERIAL); } } else { // Ignore other values as they have no effect when // GL_LIGHTING is disabled. ColorRGBA color = context.color; if (color != null) { glColor4f(color.r, color.g, color.b, color.a); } else { glColor4f(1, 1, 1, 1); } } if (context.alphaTestFallOff > 0f) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, context.alphaTestFallOff); } else { glDisable(GL_ALPHA_TEST); } } /** Reset fixed function bindings to default values. */ private void resetFixedFuncBindings() { context.alphaTestFallOff = 0f; // zero means disable alpha test! context.color = null; context.ambient = null; context.diffuse = null; context.specular = null; context.shininess = 0; context.useVertexColor = false; } public void setFixedFuncBinding(FixedFuncBinding ffBinding, Object val) { switch (ffBinding) { case Color: context.color = (ColorRGBA) val; break; case MaterialAmbient: context.ambient = (ColorRGBA) val; break; case MaterialDiffuse: context.diffuse = (ColorRGBA) val; break; case MaterialSpecular: context.specular = (ColorRGBA) val; break; case MaterialShininess: context.shininess = (Float) val; break; case UseVertexColor: context.useVertexColor = (Boolean) val; break; case AlphaTestFallOff: context.alphaTestFallOff = (Float) val; break; } } public void applyRenderState(RenderState state) { if (state.isWireframe() && !context.wireframe) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); context.wireframe = true; } else if (!state.isWireframe() && context.wireframe) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); context.wireframe = false; } if (state.isDepthTest() && !context.depthTestEnabled) { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); context.depthTestEnabled = true; } else if (!state.isDepthTest() && context.depthTestEnabled) { glDisable(GL_DEPTH_TEST); context.depthTestEnabled = false; } if (state.isAlphaTest()) { setFixedFuncBinding(FixedFuncBinding.AlphaTestFallOff, state.getAlphaFallOff()); } else { setFixedFuncBinding(FixedFuncBinding.AlphaTestFallOff, 0f); // disable it } if (state.isDepthWrite() && !context.depthWriteEnabled) { glDepthMask(true); context.depthWriteEnabled = true; } else if (!state.isDepthWrite() && context.depthWriteEnabled) { glDepthMask(false); context.depthWriteEnabled = false; } if (state.isColorWrite() && !context.colorWriteEnabled) { glColorMask(true, true, true, true); context.colorWriteEnabled = true; } else if (!state.isColorWrite() && context.colorWriteEnabled) { glColorMask(false, false, false, false); context.colorWriteEnabled = false; } if (state.isPointSprite()) { logger.log(Level.WARNING, "Point Sprite unsupported!"); } if (state.isPolyOffset()) { if (!context.polyOffsetEnabled) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(state.getPolyOffsetFactor(), state.getPolyOffsetUnits()); context.polyOffsetEnabled = true; context.polyOffsetFactor = state.getPolyOffsetFactor(); context.polyOffsetUnits = state.getPolyOffsetUnits(); } else { if (state.getPolyOffsetFactor() != context.polyOffsetFactor || state.getPolyOffsetUnits() != context.polyOffsetUnits) { glPolygonOffset(state.getPolyOffsetFactor(), state.getPolyOffsetUnits()); context.polyOffsetFactor = state.getPolyOffsetFactor(); context.polyOffsetUnits = state.getPolyOffsetUnits(); } } } else { if (context.polyOffsetEnabled) { glDisable(GL_POLYGON_OFFSET_FILL); context.polyOffsetEnabled = false; context.polyOffsetFactor = 0; context.polyOffsetUnits = 0; } } if (state.getFaceCullMode() != context.cullMode) { if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) { glDisable(GL_CULL_FACE); } else { glEnable(GL_CULL_FACE); } switch (state.getFaceCullMode()) { case Off: break; case Back: glCullFace(GL_BACK); break; case Front: glCullFace(GL_FRONT); break; case FrontAndBack: glCullFace(GL_FRONT_AND_BACK); break; default: throw new UnsupportedOperationException( "Unrecognized face cull mode: " + state.getFaceCullMode()); } context.cullMode = state.getFaceCullMode(); } if (state.getBlendMode() != context.blendMode) { if (state.getBlendMode() == RenderState.BlendMode.Off) { glDisable(GL_BLEND); } else { glEnable(GL_BLEND); switch (state.getBlendMode()) { case Off: break; case Additive: glBlendFunc(GL_ONE, GL_ONE); break; case AlphaAdditive: glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; case Color: glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); break; case Alpha: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; case PremultAlpha: glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break; case Modulate: glBlendFunc(GL_DST_COLOR, GL_ZERO); break; case ModulateX2: glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); break; default: throw new UnsupportedOperationException( "Unrecognized blend mode: " + state.getBlendMode()); } } context.blendMode = state.getBlendMode(); } if (state.isStencilTest()) { throw new UnsupportedOperationException( "OpenGL 1.1 doesn't support two sided stencil operations."); } } public void setViewPort(int x, int y, int w, int h) { if (x != vpX || vpY != y || vpW != w || vpH != h) { glViewport(x, y, w, h); vpX = x; vpY = y; vpW = w; vpH = h; } } public void setClipRect(int x, int y, int width, int height) { if (!context.clipRectEnabled) { glEnable(GL_SCISSOR_TEST); context.clipRectEnabled = true; } if (clipX != x || clipY != y || clipW != width || clipH != height) { glScissor(x, y, width, height); clipX = x; clipY = y; clipW = width; clipH = height; } } public void clearClipRect() { if (context.clipRectEnabled) { glDisable(GL_SCISSOR_TEST); context.clipRectEnabled = false; clipX = 0; clipY = 0; clipW = 0; clipH = 0; } } public void onFrame() { objManager.deleteUnused(this); // statistics.clearFrame(); } private FloatBuffer storeMatrix(Matrix4f matrix, FloatBuffer store) { store.clear(); matrix.fillFloatBuffer(store, true); store.clear(); return store; } private void setModelView(Matrix4f modelMatrix, Matrix4f viewMatrix) { if (context.matrixMode != GL_MODELVIEW) { glMatrixMode(GL_MODELVIEW); context.matrixMode = GL_MODELVIEW; } glLoadMatrix(storeMatrix(viewMatrix, fb16)); glMultMatrix(storeMatrix(modelMatrix, fb16)); } private void setProjection(Matrix4f projMatrix) { if (context.matrixMode != GL_PROJECTION) { glMatrixMode(GL_PROJECTION); context.matrixMode = GL_PROJECTION; } glLoadMatrix(storeMatrix(projMatrix, fb16)); } public void setWorldMatrix(Matrix4f worldMatrix) { this.worldMatrix.set(worldMatrix); } public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) { this.viewMatrix.set(viewMatrix); setProjection(projMatrix); } public void setLighting(LightList list) { // XXX: This is abuse of setLighting() to // apply fixed function bindings // and do other book keeping. if (list == null || list.size() == 0) { glDisable(GL_LIGHTING); applyFixedFuncBindings(false); setModelView(worldMatrix, viewMatrix); return; } // Number of lights set previously int numLightsSetPrev = lightList.size(); // If more than maxLights are defined, they will be ignored. // The GL1 renderer is not permitted to crash due to a // GL1 limitation. It must render anything that the GL2 renderer // can render (even incorrectly). lightList.clear(); materialAmbientColor.set(0, 0, 0, 0); for (int i = 0; i < list.size(); i++) { Light l = list.get(i); if (l.getType() == Light.Type.Ambient) { // Gather materialAmbientColor.addLocal(l.getColor()); } else { // Add to list lightList.add(l); // Once maximum lights reached, exit loop. if (lightList.size() >= maxLights) { break; } } } applyFixedFuncBindings(true); glEnable(GL_LIGHTING); fb16.clear(); fb16.put(materialAmbientColor.r) .put(materialAmbientColor.g) .put(materialAmbientColor.b) .put(1) .flip(); glLightModel(GL_LIGHT_MODEL_AMBIENT, fb16); if (context.matrixMode != GL_MODELVIEW) { glMatrixMode(GL_MODELVIEW); context.matrixMode = GL_MODELVIEW; } // Lights are already in world space, so just convert // them to view space. glLoadMatrix(storeMatrix(viewMatrix, fb16)); for (int i = 0; i < lightList.size(); i++) { int glLightIndex = GL_LIGHT0 + i; Light light = lightList.get(i); Light.Type lightType = light.getType(); ColorRGBA col = light.getColor(); Vector3f pos; // Enable the light glEnable(glLightIndex); // OGL spec states default value for light ambient is black switch (lightType) { case Directional: DirectionalLight dLight = (DirectionalLight) light; fb16.clear(); fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip(); glLight(glLightIndex, GL_DIFFUSE, fb16); glLight(glLightIndex, GL_SPECULAR, fb16); pos = tempVec.set(dLight.getDirection()).negateLocal().normalizeLocal(); fb16.clear(); fb16.put(pos.x).put(pos.y).put(pos.z).put(0.0f).flip(); glLight(glLightIndex, GL_POSITION, fb16); glLightf(glLightIndex, GL_SPOT_CUTOFF, 180); break; case Point: PointLight pLight = (PointLight) light; fb16.clear(); fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip(); glLight(glLightIndex, GL_DIFFUSE, fb16); glLight(glLightIndex, GL_SPECULAR, fb16); pos = pLight.getPosition(); fb16.clear(); fb16.put(pos.x).put(pos.y).put(pos.z).put(1.0f).flip(); glLight(glLightIndex, GL_POSITION, fb16); glLightf(glLightIndex, GL_SPOT_CUTOFF, 180); if (pLight.getRadius() > 0) { // Note: this doesn't follow the same attenuation model // as the one used in the lighting shader. glLightf(glLightIndex, GL_CONSTANT_ATTENUATION, 1); glLightf(glLightIndex, GL_LINEAR_ATTENUATION, pLight.getInvRadius() * 2); glLightf( glLightIndex, GL_QUADRATIC_ATTENUATION, pLight.getInvRadius() * pLight.getInvRadius()); } else { glLightf(glLightIndex, GL_CONSTANT_ATTENUATION, 1); glLightf(glLightIndex, GL_LINEAR_ATTENUATION, 0); glLightf(glLightIndex, GL_QUADRATIC_ATTENUATION, 0); } break; case Spot: SpotLight sLight = (SpotLight) light; fb16.clear(); fb16.put(col.r).put(col.g).put(col.b).put(col.a).flip(); glLight(glLightIndex, GL_DIFFUSE, fb16); glLight(glLightIndex, GL_SPECULAR, fb16); pos = sLight.getPosition(); fb16.clear(); fb16.put(pos.x).put(pos.y).put(pos.z).put(1.0f).flip(); glLight(glLightIndex, GL_POSITION, fb16); Vector3f dir = sLight.getDirection(); fb16.clear(); fb16.put(dir.x).put(dir.y).put(dir.z).put(1.0f).flip(); glLight(glLightIndex, GL_SPOT_DIRECTION, fb16); float outerAngleRad = sLight.getSpotOuterAngle(); float innerAngleRad = sLight.getSpotInnerAngle(); float spotCut = outerAngleRad * FastMath.RAD_TO_DEG; float spotExpo = 0.0f; if (outerAngleRad > 0) { spotExpo = (1.0f - (innerAngleRad / outerAngleRad)) * 128.0f; } glLightf(glLightIndex, GL_SPOT_CUTOFF, spotCut); glLightf(glLightIndex, GL_SPOT_EXPONENT, spotExpo); if (sLight.getSpotRange() > 0) { glLightf(glLightIndex, GL_LINEAR_ATTENUATION, sLight.getInvSpotRange()); } else { glLightf(glLightIndex, GL_LINEAR_ATTENUATION, 0); } break; default: throw new UnsupportedOperationException("Unrecognized light type: " + lightType); } } // Disable lights after the index for (int i = lightList.size(); i < numLightsSetPrev; i++) { glDisable(GL_LIGHT0 + i); } // This will set view matrix as well. setModelView(worldMatrix, viewMatrix); } private int convertTextureType(Texture.Type type) { switch (type) { case TwoDimensional: return GL_TEXTURE_2D; // case ThreeDimensional: // return GL_TEXTURE_3D; // case CubeMap: // return GL_TEXTURE_CUBE_MAP; default: throw new UnsupportedOperationException("Unknown texture type: " + type); } } private int convertMagFilter(Texture.MagFilter filter) { switch (filter) { case Bilinear: return GL_LINEAR; case Nearest: return GL_NEAREST; default: throw new UnsupportedOperationException("Unknown mag filter: " + filter); } } private int convertMinFilter(Texture.MinFilter filter) { switch (filter) { case Trilinear: return GL_LINEAR_MIPMAP_LINEAR; case BilinearNearestMipMap: return GL_LINEAR_MIPMAP_NEAREST; case NearestLinearMipMap: return GL_NEAREST_MIPMAP_LINEAR; case NearestNearestMipMap: return GL_NEAREST_MIPMAP_NEAREST; case BilinearNoMipMaps: return GL_LINEAR; case NearestNoMipMaps: return GL_NEAREST; default: throw new UnsupportedOperationException("Unknown min filter: " + filter); } } private int convertWrapMode(Texture.WrapMode mode) { switch (mode) { case EdgeClamp: case Clamp: case BorderClamp: return GL_CLAMP; case Repeat: return GL_REPEAT; default: throw new UnsupportedOperationException("Unknown wrap mode: " + mode); } } private void setupTextureParams(Texture tex) { int target = convertTextureType(tex.getType()); // filter things int minFilter = convertMinFilter(tex.getMinFilter()); int magFilter = convertMagFilter(tex.getMagFilter()); glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter); // repeat modes switch (tex.getType()) { // case ThreeDimensional: // case CubeMap: // glTexParameteri(target, GL_TEXTURE_WRAP_R, // convertWrapMode(tex.getWrap(WrapAxis.R))); case TwoDimensional: glTexParameteri(target, GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); // fall down here is intentional.. // case OneDimensional: glTexParameteri(target, GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); break; default: throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); } } public void updateTexImageData(Image img, Texture.Type type, int unit) { int texId = img.getId(); if (texId == -1) { // create texture glGenTextures(ib1); texId = ib1.get(0); img.setId(texId); objManager.registerObject(img); statistics.onNewTexture(); } // bind texture int target = convertTextureType(type); // if (context.boundTextureUnit != unit) { // glActiveTexture(GL_TEXTURE0 + unit); // context.boundTextureUnit = unit; // } if (context.boundTextures[unit] != img) { glEnable(target); glBindTexture(target, texId); context.boundTextures[unit] = img; statistics.onTextureUse(img, true); } // Check sizes if graphics card doesn't support NPOT if (!GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) { if (img.getWidth() != 0 && img.getHeight() != 0) { if (!FastMath.isPowerOfTwo(img.getWidth()) || !FastMath.isPowerOfTwo(img.getHeight())) { // Resize texture to Power-of-2 size MipMapGenerator.resizeToPowerOf2(img); } } } if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { // No pregenerated mips available, // generate from base level if required // Check if hardware mips are supported if (GLContext.getCapabilities().OpenGL14) { glTexParameteri(target, GL14.GL_GENERATE_MIPMAP, GL_TRUE); } else { MipMapGenerator.generateMipMaps(img); } img.setMipmapsGenerated(true); } else { } if (img.getWidth() > maxTexSize || img.getHeight() > maxTexSize) { throw new RendererException( "Cannot upload texture " + img + ". The maximum supported texture resolution is " + maxTexSize); } /* if (target == GL_TEXTURE_CUBE_MAP) { List<ByteBuffer> data = img.getData(); if (data.size() != 6) { logger.log(Level.WARNING, "Invalid texture: {0}\n" + "Cubemap textures must contain 6 data units.", img); return; } for (int i = 0; i < 6; i++) { TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc); } } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) { List<ByteBuffer> data = img.getData(); // -1 index specifies prepare data for 2D Array TextureUtil.uploadTexture(img, target, -1, 0, tdc); for (int i = 0; i < data.size(); i++) { // upload each slice of 2D array in turn // this time with the appropriate index TextureUtil.uploadTexture(img, target, i, 0, tdc); } } else {*/ TextureUtil.uploadTexture(img, target, 0, 0); // } img.clearUpdateNeeded(); } public void setTexture(int unit, Texture tex) { if (unit != 0 || tex.getType() != Texture.Type.TwoDimensional) { // throw new UnsupportedOperationException(); return; } Image image = tex.getImage(); if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { updateTexImageData(image, tex.getType(), unit); } int texId = image.getId(); assert texId != -1; Image[] textures = context.boundTextures; int type = convertTextureType(tex.getType()); // if (!context.textureIndexList.moveToNew(unit)) { // if (context.boundTextureUnit != unit){ // glActiveTexture(GL_TEXTURE0 + unit); // context.boundTextureUnit = unit; // } // glEnable(type); // } // if (context.boundTextureUnit != unit) { // glActiveTexture(GL_TEXTURE0 + unit); // context.boundTextureUnit = unit; // } if (textures[unit] != image) { glEnable(type); glBindTexture(type, texId); textures[unit] = image; statistics.onTextureUse(image, true); } else { statistics.onTextureUse(image, false); } setupTextureParams(tex); } public void modifyTexture(Texture tex, Image pixels, int x, int y) { setTexture(0, tex); TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y); } private void clearTextureUnits() { Image[] textures = context.boundTextures; if (textures[0] != null) { glDisable(GL_TEXTURE_2D); textures[0] = null; } } public void deleteImage(Image image) { int texId = image.getId(); if (texId != -1) { ib1.put(0, texId); ib1.position(0).limit(1); glDeleteTextures(ib1); image.resetObject(); } } private int convertArrayType(VertexBuffer.Type type) { switch (type) { case Position: return GL_VERTEX_ARRAY; case Normal: return GL_NORMAL_ARRAY; case TexCoord: return GL_TEXTURE_COORD_ARRAY; case Color: return GL_COLOR_ARRAY; default: return -1; // unsupported } } private int convertVertexFormat(VertexBuffer.Format fmt) { switch (fmt) { case Byte: return GL_BYTE; case Float: return GL_FLOAT; case Int: return GL_INT; case Short: return GL_SHORT; case UnsignedByte: return GL_UNSIGNED_BYTE; case UnsignedInt: return GL_UNSIGNED_INT; case UnsignedShort: return GL_UNSIGNED_SHORT; default: throw new UnsupportedOperationException("Unrecognized vertex format: " + fmt); } } private int convertElementMode(Mesh.Mode mode) { switch (mode) { case Points: return GL_POINTS; case Lines: return GL_LINES; case LineLoop: return GL_LINE_LOOP; case LineStrip: return GL_LINE_STRIP; case Triangles: return GL_TRIANGLES; case TriangleFan: return GL_TRIANGLE_FAN; case TriangleStrip: return GL_TRIANGLE_STRIP; default: throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode); } } public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) { if (count > 1) { throw new UnsupportedOperationException(); } glDrawArrays(convertElementMode(mode), 0, vertCount); } 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; } } public void setVertexAttrib(VertexBuffer vb) { setVertexAttrib(vb, null); } private void drawElements(int mode, int format, Buffer data) { switch (format) { case GL_UNSIGNED_BYTE: glDrawElements(mode, (ByteBuffer) data); break; case GL_UNSIGNED_SHORT: glDrawElements(mode, (ShortBuffer) data); break; case GL_UNSIGNED_INT: glDrawElements(mode, (IntBuffer) data); break; default: throw new UnsupportedOperationException(); } } 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); } } 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; } } } private void renderMeshDefault(Mesh mesh, int lod, int count) { VertexBuffer indices = null; 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 { glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount()); } // TODO: Fix these to use IDList?? clearVertexAttribs(); clearTextureUnits(); resetFixedFuncBindings(); } 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 setAlphaToCoverage(boolean value) {} public void setShader(Shader shader) {} public void deleteShader(Shader shader) {} public void deleteShaderSource(ShaderSource source) {} public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {} public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {} public void setMainFrameBufferOverride(FrameBuffer fb) {} public void setFrameBuffer(FrameBuffer fb) {} public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {} public void deleteFrameBuffer(FrameBuffer fb) {} public void updateBufferData(VertexBuffer vb) {} public void deleteBuffer(VertexBuffer vb) {} }
/** * This test renders a scene to an offscreen framebuffer, then copies the contents to a Swing * JFrame. Note that some parts are done inefficently, this is done to make the code more readable. */ public class TestRenderToMemory extends SimpleApplication implements SceneProcessor { private Geometry offBox; private float angle = 0; private FrameBuffer offBuffer; private ViewPort offView; private Texture2D offTex; private Camera offCamera; private ImageDisplay display; private static final int width = 800, height = 600; private final ByteBuffer cpuBuf = BufferUtils.createByteBuffer(width * height * 4); private final byte[] cpuArray = new byte[width * height * 4]; private final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR); private class ImageDisplay extends JPanel { private long t; private long total; private int frames; private int fps; @Override public void paintComponent(Graphics gfx) { super.paintComponent(gfx); Graphics2D g2d = (Graphics2D) gfx; if (t == 0) t = timer.getTime(); // g2d.setBackground(Color.BLACK); // g2d.clearRect(0,0,width,height); synchronized (image) { g2d.drawImage(image, null, 0, 0); } long t2 = timer.getTime(); long dt = t2 - t; total += dt; frames++; t = t2; if (total > 1000) { fps = frames; total = 0; frames = 0; } g2d.setColor(Color.white); g2d.drawString("FPS: " + fps, 0, getHeight() - 100); } } public static void main(String[] args) { TestRenderToMemory app = new TestRenderToMemory(); app.setPauseOnLostFocus(false); AppSettings settings = new AppSettings(true); settings.setResolution(1, 1); app.setSettings(settings); app.start(Type.OffscreenSurface); } public void createDisplayFrame() { SwingUtilities.invokeLater( new Runnable() { public void run() { JFrame frame = new JFrame("Render Display"); display = new ImageDisplay(); display.setPreferredSize(new Dimension(width, height)); frame.getContentPane().add(display); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.addWindowListener( new WindowAdapter() { public void windowClosed(WindowEvent e) { stop(); } }); frame.pack(); frame.setLocationRelativeTo(null); frame.setResizable(false); frame.setVisible(true); } }); } public void updateImageContents() { cpuBuf.clear(); renderer.readFrameBuffer(offBuffer, cpuBuf); synchronized (image) { Screenshots.convertScreenShot(cpuBuf, image); } if (display != null) display.repaint(); } public void setupOffscreenView() { offCamera = new Camera(width, height); // create a pre-view. a view that is rendered before the main view offView = renderManager.createPreView("Offscreen View", offCamera); offView.setBackgroundColor(ColorRGBA.DarkGray); offView.setClearFlags(true, true, true); // this will let us know when the scene has been rendered to the // frame buffer offView.addProcessor(this); // create offscreen framebuffer offBuffer = new FrameBuffer(width, height, 1); // setup framebuffer's cam offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f); offCamera.setLocation(new Vector3f(0f, 0f, -5f)); offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y); // setup framebuffer's texture // offTex = new Texture2D(width, height, Format.RGBA8); // setup framebuffer to use renderbuffer // this is faster for gpu -> cpu copies offBuffer.setDepthBuffer(Format.Depth); offBuffer.setColorBuffer(Format.RGBA8); // offBuffer.setColorTexture(offTex); // set viewport to render to offscreen framebuffer offView.setOutputFrameBuffer(offBuffer); // setup framebuffer's scene Box boxMesh = new Box(Vector3f.ZERO, 1, 1, 1); Material material = assetManager.loadMaterial("Interface/Logo/Logo.j3m"); offBox = new Geometry("box", boxMesh); offBox.setMaterial(material); // attach the scene to the viewport to be rendered offView.attachScene(offBox); } @Override public void simpleInitApp() { setupOffscreenView(); createDisplayFrame(); } @Override public void simpleUpdate(float tpf) { Quaternion q = new Quaternion(); angle += tpf; angle %= FastMath.TWO_PI; q.fromAngles(angle, 0, angle); offBox.setLocalRotation(q); offBox.updateLogicalState(tpf); offBox.updateGeometricState(); } public void initialize(RenderManager rm, ViewPort vp) {} public void reshape(ViewPort vp, int w, int h) {} public boolean isInitialized() { return true; } public void preFrame(float tpf) {} public void postQueue(RenderQueue rq) {} /** Update the CPU image's contents after the scene has been rendered to the framebuffer. */ public void postFrame(FrameBuffer out) { updateImageContents(); } public void cleanup() {} }
/** * 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}; }
/** Computes which points are inside the hull */ private Mesh buildPreviewMesh() { long start = System.currentTimeMillis(); // First get the bounding box. updateWorldBound(); BoundingBox bound = (BoundingBox) getWorldBound(); Vector3f maxBound = bound.getMax(null); Vector3f originPoint = bound.getMin(null); originPoint.x = Math.min(originPoint.x, -maxBound.x); originPoint.y = Math.min(originPoint.y, -maxBound.y); originPoint.z = Math.min(originPoint.z, -maxBound.z); // Thread Pool ForkJoinPool pool = new ForkJoinPool(); // Create an octree from the data OctreeNode octree = new OctreeNode(originPoint, maxBound); OctreeConstructionTask dcOctreeTask = new OctreeConstructionTask(octree, primitives, 3, 6); pool.invoke(dcOctreeTask); // Contour the octree. AdaptiveDualContouringTask adaptiveTask = new AdaptiveDualContouringTask(octree, primitives); pool.invoke(adaptiveTask); // Retrieve computed data. ArrayList<Vector3f> verticesList = dcOctreeTask.getVertices(); ArrayList<Vector3i> triangles = adaptiveTask.getTriangles(); int numberOfVerticesBefore = verticesList.size(); int numberOfTrianglesBefore = triangles.size(); // Compute normals both from data and triangles. Vector3f normals[] = MeshUtils.facetedNormalsFromFaces( triangles, verticesList, primitives, (float) Math.toRadians(10)); // Drop the triangles to an array. int index = 0; int[] triangleList = new int[3 * triangles.size()]; for (Vector3i v : triangles) { triangleList[index++] = v.x; triangleList[index++] = v.y; triangleList[index++] = v.z; } // Finally, make the mesh itself: Mesh mesh = new Mesh(); mesh.setBuffer( Type.Position, 3, BufferUtils.createFloatBuffer(verticesList.toArray(new Vector3f[0]))); mesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(triangleList)); mesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals)); mesh.updateBound(); mesh.setStatic(); long timeTaken = System.currentTimeMillis() - start; System.out.println( String.format( "%d Vertices, %d Triangles in %d Milliseconds", verticesList.size(), triangles.size(), timeTaken)); return mesh; }