/** * Copyright 2012 Jason Sorensen (sorensenj@smert.net) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package net.smert.frameworkgl.opengl.mesh; import java.util.ArrayList; import java.util.List; import net.smert.frameworkgl.math.AABB; import net.smert.frameworkgl.math.Vector2f; import net.smert.frameworkgl.math.Vector3f; import net.smert.frameworkgl.math.Vector4f; import net.smert.frameworkgl.opengl.GL; import net.smert.frameworkgl.opengl.constants.Primitives; import net.smert.frameworkgl.opengl.renderable.RenderableConfiguration; import net.smert.frameworkgl.utils.Color; import net.smert.frameworkgl.utils.ListUtils; /** * * @author Jason Sorensen <sorensenj@smert.net> */ public class Tessellator { private final static int INITIAL_SEGMENTS = 16; private final static int INITIAL_ELEMENTS_PER_SEGMENT = INITIAL_SEGMENTS * 1024; private final static int INITIAL_COMPONENT_ELEMENTS_PER_SEGMENT = INITIAL_ELEMENTS_PER_SEGMENT * 4; private boolean convertToTriangles; // Doesn't get reset private boolean enableConversionForPrimitiveMode; private boolean isStarted; private int elementCount; private int maxIndex; private int minIndex; private int primitiveMode; private int vertexIndex; private final AABB aabb; private final Color color; private final ConversionState conversionState; private final List<Float> colors; private final List<Float> normals; private final List<Float> texCoords; private final List<Float> vertices; private final List<Integer> elementCounts; private final List<Integer> maxIndexes; private final List<Integer> minIndexes; private final List<Integer> primitiveModes; private final List<Integer> vertexIndexes; private final List<Segment> segments; private RenderableConfiguration config; private final Vector3f normal; private final Vector3f texCoord; private final Vector4f localPosition; private final Vector4f vertex; public Tessellator() { convertToTriangles = true; aabb = new AABB(); color = new Color(); conversionState = new ConversionState(); colors = new ArrayList<>(INITIAL_COMPONENT_ELEMENTS_PER_SEGMENT); // Up to four per vertex normals = new ArrayList<>(INITIAL_COMPONENT_ELEMENTS_PER_SEGMENT); // Three per vertex texCoords = new ArrayList<>(INITIAL_COMPONENT_ELEMENTS_PER_SEGMENT); // Up to three per vertex vertices = new ArrayList<>(INITIAL_COMPONENT_ELEMENTS_PER_SEGMENT); // Up to four per vertex elementCounts = new ArrayList<>(INITIAL_SEGMENTS); // One per segment maxIndexes = new ArrayList<>(INITIAL_SEGMENTS); // One per segment minIndexes = new ArrayList<>(INITIAL_SEGMENTS); // One per segment primitiveModes = new ArrayList<>(INITIAL_SEGMENTS); // One per segment vertexIndexes = new ArrayList<>(INITIAL_ELEMENTS_PER_SEGMENT); // One per vertex segments = new ArrayList<>(INITIAL_SEGMENTS); normal = new Vector3f(); texCoord = new Vector3f(); localPosition = new Vector4f(); vertex = new Vector4f(); reset(); } private void findAABBMaxMin(Vector4f vertex) { aabb.getMax().setMax(vertex.getX(), vertex.getY(), vertex.getZ()); aabb.getMin().setMin(vertex.getX(), vertex.getY(), vertex.getZ()); } private void internalAddColor(Color color) { if (enableConversionForPrimitiveMode) { conversionState.addColorConversion(color); return; } internalAddColorToList(color); } private void internalAddColorToList(Color color) { colors.add(color.getR()); colors.add(color.getG()); colors.add(color.getB()); if (config.getColorSize() == 4) { colors.add(color.getA()); } } private void internalAddNormal(Vector3f normal) { if (enableConversionForPrimitiveMode) { conversionState.addNormalConversion(normal); return; } internalAddNormalToList(normal); } private void internalAddNormalToList(Vector3f normal) { normals.add(normal.getX()); normals.add(normal.getY()); normals.add(normal.getZ()); } private void internalAddTexCoord(Vector3f texCoord) { if (enableConversionForPrimitiveMode) { conversionState.addTexCoordConversion(texCoord); return; } internalAddTexCoordToList(texCoord); } private void internalAddTexCoordToList(Vector3f texCoord) { texCoords.add(texCoord.getX()); texCoords.add(texCoord.getY()); if (config.getTexCoordSize() == 3) { texCoords.add(texCoord.getZ()); } } private void internalAddVertex(Vector4f vertex) { vertex.add(localPosition); findAABBMaxMin(vertex); if (enableConversionForPrimitiveMode) { conversionState.addVertexConversion(vertex); conversionState.convert(this); return; } internalAddVertexToList(vertex); } private void internalAddVertexToList(Vector4f vertex) { vertices.add(vertex.getX()); vertices.add(vertex.getY()); if (config.getVertexSize() >= 3) { vertices.add(vertex.getZ()); } if (config.getVertexSize() == 4) { vertices.add(vertex.getW()); } vertexIndexes.add(vertexIndex); elementCount++; maxIndex = vertexIndex++; } public void addColor(float r, float g, float b) { color.set(r, g, b, 1f); internalAddColor(color); } public void addColor(float r, float g, float b, float a) { color.set(r, g, b, a); internalAddColor(color); } public void addColor(Color color) { this.color.set(color); internalAddColor(this.color); } public void addNormal(float x, float y, float z) { normal.set(x, y, z); internalAddNormal(normal); } public void addNormal(Vector3f normal) { this.normal.set(normal); internalAddNormal(this.normal); } public void addNormal(Vector3f pos1, Vector3f pos2, Vector3f pos3) { conversionState.calculateNormal(pos1, pos2, pos3); normal.set(conversionState.getNormal()); internalAddNormal(normal); } public void addNormalAgain() { internalAddNormal(normal); } public void addTexCoord(float s, float t) { texCoord.set(s, t, 0f); internalAddTexCoord(texCoord); } public void addSegment(String name) { if (isStarted) { throw new IllegalStateException("You cannot add a segment util stop() has been called"); } Segment segment = createSegment(name); segments.add(segment); } public void addTexCoord(float s, float t, float r) { config.setTexCoordSize(3); texCoord.set(s, t, r); internalAddTexCoord(texCoord); } public void addVertex(float x, float y) { vertex.set(x, y, 1f, 0f); internalAddVertex(vertex); } public void addVertex(float x, float y, float z) { vertex.set(x, y, z, 1f); internalAddVertex(vertex); } public void addVertex(float x, float y, float z, float w) { vertex.set(x, y, z, w); internalAddVertex(vertex); } public void addVertex(Vector2f vertex) { this.vertex.set(vertex, 1f, 0f); internalAddVertex(this.vertex); } public void addVertex(Vector3f vertex) { this.vertex.set(vertex, 1f); internalAddVertex(this.vertex); } public void addVertex(Vector4f vertex) { this.vertex.set(vertex); internalAddVertex(this.vertex); } public void calculateNormals() { if (isStarted) { throw new IllegalStateException("You cannot calculate normals until stop() has been called"); } conversionState.setPrimitiveMode(primitiveMode); normals.clear(); for (int i = 0; i < vertices.size(); i += config.getVertexSize()) { vertex.zero(); float x = vertices.get(i + 0); vertex.setX(x); float y = vertices.get(i + 1); vertex.setY(y); if (config.getVertexSize() >= 3) { float z = vertices.get(i + 2); vertex.setZ(z); } if (config.getVertexSize() == 4) { float w = vertices.get(i + 3); vertex.setW(w); } conversionState.addVertexConversion(vertex); conversionState.calculateNormals(this); } conversionState.finishCalculateNormals(this); conversionState.reset(); } public Segment createSegment(String name) { if (isStarted) { throw new IllegalStateException("You cannot create a segment util stop() has been called"); } Segment segment = GL.meshFactory.createSegment(); segment.setElementCount(elementCount); segment.setMaxIndex(maxIndex); segment.setMinIndex(minIndex); segment.setName(name); segment.setPrimitiveMode(primitiveMode); if (colors.size() > 0) { segment.setData(SegmentDataType.COLOR, getColors()); colors.clear(); } if (normals.size() > 0) { segment.setData(SegmentDataType.NORMAL, getNormals()); normals.clear(); } if (texCoords.size() > 0) { segment.setData(SegmentDataType.TEX_COORD0, getTexCoords()); texCoords.clear(); } if (vertices.size() > 0) { segment.setData(SegmentDataType.VERTEX, getVertices()); vertices.clear(); } return segment; } public float[] getColors() { return ListUtils.ToPrimitiveFloatArray(colors); } public float[] getNormals() { return ListUtils.ToPrimitiveFloatArray(normals); } public float[] getTexCoords() { return ListUtils.ToPrimitiveFloatArray(texCoords); } public float[] getVertices() { return ListUtils.ToPrimitiveFloatArray(vertices); } public int getColorsCount() { return colors.size(); } public int getElementCount() { return elementCount; } public int getNormalsCount() { return normals.size(); } public int getPrimitiveMode() { return primitiveMode; } public int getTexCoordsCount() { return texCoords.size(); } public int getVerticesCount() { return vertices.size(); } public int getVertexIndexesCount() { return vertexIndexes.size(); } public int[] getElementCounts() { return ListUtils.ToPrimitiveIntArray(elementCounts); } public int[] getMaxIndexes() { return ListUtils.ToPrimitiveIntArray(maxIndexes); } public int[] getMinIndexes() { return ListUtils.ToPrimitiveIntArray(minIndexes); } public int[] getPrimitiveModes() { return ListUtils.ToPrimitiveIntArray(primitiveModes); } public int[] getVertexIndexes() { return ListUtils.ToPrimitiveIntArray(vertexIndexes); } public List<Segment> getSegments() { return segments; } public RenderableConfiguration getRenderableConfiguration() { return config; } public void getAABB(AABB aabb) { aabb.setMax(this.aabb.getMax()); aabb.setMin(this.aabb.getMin()); } public boolean isConvertToTriangles() { return convertToTriangles; } public void setConvertToTriangles(boolean convertToTriangles) { this.convertToTriangles = convertToTriangles; } public final void reset() { enableConversionForPrimitiveMode = false; isStarted = false; elementCount = 0; maxIndex = -1; minIndex = -1; primitiveMode = -1; vertexIndex = 0; aabb.setMax(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); aabb.setMin(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE); color.setWhite(); conversionState.reset(); colors.clear(); normals.clear(); texCoords.clear(); vertices.clear(); elementCounts.clear(); maxIndexes.clear(); minIndexes.clear(); primitiveModes.clear(); vertexIndexes.clear(); segments.clear(); config = GL.meshFactory.createRenderableConfiguration(); normal.zero(); texCoord.zero(); localPosition.zero(); vertex.zero(); } public void setConfig(RenderableConfiguration config) { this.config = config; } public void setLocalPosition(float x, float y, float z) { this.localPosition.set(x, y, z, 0f); } public void setLocalPosition(Vector3f localPosition) { this.localPosition.set(localPosition, 0f); } public void start(int primitiveMode) { start(primitiveMode, false); } public void start(int primitiveMode, boolean forceConversionForPrimitiveMode) { if (convertToTriangles) { enableConversionForPrimitiveMode = ((primitiveMode != Primitives.LINES) && (primitiveMode != Primitives.LINE_LOOP) && (primitiveMode != Primitives.LINE_STRIP) && (primitiveMode != Primitives.POINTS) && (primitiveMode != Primitives.POLYGON) && (primitiveMode != Primitives.TRIANGLES)); } if (forceConversionForPrimitiveMode) { enableConversionForPrimitiveMode = true; } isStarted = true; elementCount = 0; maxIndex = -1; minIndex = vertexIndex; // Save first index this.primitiveMode = primitiveMode; primitiveModes.add(this.primitiveMode); conversionState.setPrimitiveMode(primitiveMode); } public void stop() { if (enableConversionForPrimitiveMode) { conversionState.reset(); primitiveMode = Primitives.TRIANGLES; primitiveModes.set(primitiveModes.size() - 1, primitiveMode); enableConversionForPrimitiveMode = false; } isStarted = false; elementCounts.add(elementCount); maxIndexes.add(maxIndex); minIndexes.add(minIndex); } public static ConversionState CreateConversionState(int primitiveMode) { ConversionState conversionState = new ConversionState(); conversionState.setPrimitiveMode(primitiveMode); return conversionState; } public static class ConversionState { private boolean firstConvert; private boolean flipFlop; private boolean hasColorsToConvert; private boolean hasNormalsToConvert; private boolean hasTexCoordsToConvert; private int colorConversionCount; private int normalConversionCount; private int primitiveMode; // Doesn't get reset private int texCoordConversionCount; private int vertexConversionCount; private final Color color; private final Color colorConversion0; private final Color colorConversion1; private final Color colorConversion2; private final Color colorConversion3; private final Vector3f normal; private final Vector3f normalConversion0; private final Vector3f normalConversion1; private final Vector3f normalConversion2; private final Vector3f normalConversion3; private final Vector3f normalCreation1; private final Vector3f normalCreation2; private final Vector3f texCoord; private final Vector3f texCoordConversion0; private final Vector3f texCoordConversion1; private final Vector3f texCoordConversion2; private final Vector3f texCoordConversion3; private final Vector4f vertex; private final Vector4f vertexConversion0; private final Vector4f vertexConversion1; private final Vector4f vertexConversion2; private final Vector4f vertexConversion3; public ConversionState() { primitiveMode = -1; color = new Color(); colorConversion0 = new Color(); colorConversion1 = new Color(); colorConversion2 = new Color(); colorConversion3 = new Color(); normal = new Vector3f(); normalConversion0 = new Vector3f(); normalConversion1 = new Vector3f(); normalConversion2 = new Vector3f(); normalConversion3 = new Vector3f(); normalCreation1 = new Vector3f(); normalCreation2 = new Vector3f(); texCoord = new Vector3f(); texCoordConversion0 = new Vector3f(); texCoordConversion1 = new Vector3f(); texCoordConversion2 = new Vector3f(); texCoordConversion3 = new Vector3f(); vertex = new Vector4f(); vertexConversion0 = new Vector4f(); vertexConversion1 = new Vector4f(); vertexConversion2 = new Vector4f(); vertexConversion3 = new Vector4f(); reset(); } private void calculateNormal(Vector3f pos1, Vector3f pos2, Vector3f pos3) { // CCW order normalCreation1.set(pos3).subtract(pos2); // pos2 is base pointing to pos3, index finger normalCreation2.set(pos1).subtract(pos2);// pos2 is base pointing to pos1, middle finger normal.set(normalCreation1).cross(normalCreation2); // right hand rule, thumb normal.normalize(); } private void calculateNormal(Vector4f pos1, Vector4f pos2, Vector4f pos3) { // CCW order normalCreation1.set( pos3.getX() - pos2.getX(), pos3.getY() - pos2.getY(), pos3.getZ() - pos2.getZ()); // pos2 is base pointing to pos3, index finger normalCreation2.set( pos1.getX() - pos2.getX(), pos1.getY() - pos2.getY(), pos1.getZ() - pos2.getZ()); // pos2 is base pointing to pos1, middle finger normal.set(normalCreation1).cross(normalCreation2); // right hand rule, thumb normal.normalize(); } private void convertQuad(Tessellator tessellator) { if (hasColorsToConvert) { tessellator.internalAddColorToList(colorConversion0); // Top right tessellator.internalAddColorToList(colorConversion1); // Top left tessellator.internalAddColorToList(colorConversion2); // Bottom left tessellator.internalAddColorToList(colorConversion3); // Bottom right } if (hasNormalsToConvert) { tessellator.internalAddNormalToList(normalConversion0); tessellator.internalAddNormalToList(normalConversion1); tessellator.internalAddNormalToList(normalConversion2); tessellator.internalAddNormalToList(normalConversion3); } if (hasTexCoordsToConvert) { tessellator.internalAddTexCoordToList(texCoordConversion0); tessellator.internalAddTexCoordToList(texCoordConversion1); tessellator.internalAddTexCoordToList(texCoordConversion2); tessellator.internalAddTexCoordToList(texCoordConversion3); } tessellator.internalAddVertexToList(vertexConversion0); tessellator.internalAddVertexToList(vertexConversion1); tessellator.internalAddVertexToList(vertexConversion2); tessellator.internalAddVertexToList(vertexConversion3); } private void convertQuadStripToTriangles(Tessellator tessellator) { if (hasColorsToConvert) { tessellator.internalAddColorToList(colorConversion2); // Top right tessellator.internalAddColorToList(colorConversion0); // Top left tessellator.internalAddColorToList(colorConversion1); // Bottom left tessellator.internalAddColorToList(colorConversion1); // Bottom left tessellator.internalAddColorToList(colorConversion3); // Bottom right tessellator.internalAddColorToList(colorConversion2); // Top Right } if (hasNormalsToConvert) { tessellator.internalAddNormalToList(normalConversion2); tessellator.internalAddNormalToList(normalConversion0); tessellator.internalAddNormalToList(normalConversion1); tessellator.internalAddNormalToList(normalConversion1); tessellator.internalAddNormalToList(normalConversion3); tessellator.internalAddNormalToList(normalConversion2); } if (hasTexCoordsToConvert) { tessellator.internalAddTexCoordToList(texCoordConversion2); tessellator.internalAddTexCoordToList(texCoordConversion0); tessellator.internalAddTexCoordToList(texCoordConversion1); tessellator.internalAddTexCoordToList(texCoordConversion1); tessellator.internalAddTexCoordToList(texCoordConversion3); tessellator.internalAddTexCoordToList(texCoordConversion2); } tessellator.internalAddVertexToList(vertexConversion2); tessellator.internalAddVertexToList(vertexConversion0); tessellator.internalAddVertexToList(vertexConversion1); tessellator.internalAddVertexToList(vertexConversion1); tessellator.internalAddVertexToList(vertexConversion3); tessellator.internalAddVertexToList(vertexConversion2); } private void convertQuadToTriangles(Tessellator tessellator) { if (hasColorsToConvert) { tessellator.internalAddColorToList(colorConversion0); // Top right tessellator.internalAddColorToList(colorConversion1); // Top left tessellator.internalAddColorToList(colorConversion2); // Bottom left tessellator.internalAddColorToList(colorConversion2); // Bottom left tessellator.internalAddColorToList(colorConversion3); // Bottom right tessellator.internalAddColorToList(colorConversion0); // Top right } if (hasNormalsToConvert) { tessellator.internalAddNormalToList(normalConversion0); tessellator.internalAddNormalToList(normalConversion1); tessellator.internalAddNormalToList(normalConversion2); tessellator.internalAddNormalToList(normalConversion2); tessellator.internalAddNormalToList(normalConversion3); tessellator.internalAddNormalToList(normalConversion0); } if (hasTexCoordsToConvert) { tessellator.internalAddTexCoordToList(texCoordConversion0); tessellator.internalAddTexCoordToList(texCoordConversion1); tessellator.internalAddTexCoordToList(texCoordConversion2); tessellator.internalAddTexCoordToList(texCoordConversion2); tessellator.internalAddTexCoordToList(texCoordConversion3); tessellator.internalAddTexCoordToList(texCoordConversion0); } tessellator.internalAddVertexToList(vertexConversion0); tessellator.internalAddVertexToList(vertexConversion1); tessellator.internalAddVertexToList(vertexConversion2); tessellator.internalAddVertexToList(vertexConversion2); tessellator.internalAddVertexToList(vertexConversion3); tessellator.internalAddVertexToList(vertexConversion0); } private void convertTriangle(Tessellator tessellator) { if (hasColorsToConvert) { tessellator.internalAddColorToList(colorConversion0); // Center tessellator.internalAddColorToList(colorConversion1); // Bottom left tessellator.internalAddColorToList(colorConversion2); // Bottom right } if (hasNormalsToConvert) { tessellator.internalAddNormalToList(normalConversion0); tessellator.internalAddNormalToList(normalConversion1); tessellator.internalAddNormalToList(normalConversion2); } if (hasTexCoordsToConvert) { tessellator.internalAddTexCoordToList(texCoordConversion0); tessellator.internalAddTexCoordToList(texCoordConversion1); tessellator.internalAddTexCoordToList(texCoordConversion2); } tessellator.internalAddVertexToList(vertexConversion0); tessellator.internalAddVertexToList(vertexConversion1); tessellator.internalAddVertexToList(vertexConversion2); } private boolean hasIncompatiblePrimitiveMode(Tessellator tessellator) { return ((tessellator.primitiveMode == Primitives.LINES) || (tessellator.primitiveMode == Primitives.LINE_LOOP) || (tessellator.primitiveMode == Primitives.LINE_STRIP) || (tessellator.primitiveMode == Primitives.POINTS) || (tessellator.primitiveMode == Primitives.POLYGON)); } public void addColorConversion(Color color) { if (colorConversionCount == 0) { colorConversion0.set(color); } else if (colorConversionCount == 1) { colorConversion1.set(color); } else if (colorConversionCount == 2) { colorConversion2.set(color); } else if (colorConversionCount == 3) { colorConversion3.set(color); } hasColorsToConvert = true; colorConversionCount++; } public void addNormalConversion(Vector3f normal) { if (normalConversionCount == 0) { normalConversion0.set(normal); } else if (normalConversionCount == 1) { normalConversion1.set(normal); } else if (normalConversionCount == 2) { normalConversion2.set(normal); } else if (normalConversionCount == 3) { normalConversion3.set(normal); } hasNormalsToConvert = true; normalConversionCount++; } public void addTexCoordConversion(Vector3f texCoord) { if (texCoordConversionCount == 0) { texCoordConversion0.set(texCoord); } else if (texCoordConversionCount == 1) { texCoordConversion1.set(texCoord); } else if (texCoordConversionCount == 2) { texCoordConversion2.set(texCoord); } else if (texCoordConversionCount == 3) { texCoordConversion3.set(texCoord); } hasTexCoordsToConvert = true; texCoordConversionCount++; } public void addQuad(Tessellator tessellator) { if (hasIncompatiblePrimitiveMode(tessellator)) { throw new IllegalStateException("You cannot add a quad in the primitive mode: " + tessellator.primitiveMode); } if ((!tessellator.enableConversionForPrimitiveMode) && (tessellator.primitiveMode != Primitives.QUADS)) { throw new IllegalStateException("You cannot add a quad in the primitive mode: " + tessellator.primitiveMode); } if (vertexConversionCount != 4) { throw new IllegalStateException("You must have four vertices to add a quad"); } if (tessellator.enableConversionForPrimitiveMode) { convertQuadToTriangles(tessellator); } else { convertQuad(tessellator); } } public void addTriangle(Tessellator tessellator) { if (hasIncompatiblePrimitiveMode(tessellator)) { throw new IllegalStateException("You cannot add a triangle in the primitive mode: " + tessellator.primitiveMode); } if ((!tessellator.enableConversionForPrimitiveMode) && (tessellator.primitiveMode != Primitives.TRIANGLES)) { throw new IllegalStateException("You cannot add a triangle in the primitive mode: " + tessellator.primitiveMode); } if (vertexConversionCount != 3) { throw new IllegalStateException("You must have three vertices to add a quad"); } convertTriangle(tessellator); } public void addVertexConversion(Vector4f vertex) { if (vertexConversionCount == 0) { vertexConversion0.set(vertex); } else if (vertexConversionCount == 1) { vertexConversion1.set(vertex); } else if (vertexConversionCount == 2) { vertexConversion2.set(vertex); } else if (vertexConversionCount == 3) { vertexConversion3.set(vertex); } vertexConversionCount++; } public void calculateNormals(Tessellator tessellator) { if (hasIncompatiblePrimitiveMode(tessellator)) { throw new IllegalStateException("You cannot calculate normals in the primitive mode: " + tessellator.primitiveMode); } if (tessellator.primitiveMode != primitiveMode) { throw new IllegalStateException("The Tessellator and ConversionState's primitiveMode must match"); } if ((tessellator.config.getVertexSize() != 3) && (tessellator.config.getVertexSize()) != 4) { throw new IllegalStateException("Currently do not support calculating normals for 2D vectors"); } switch (primitiveMode) { case Primitives.QUAD_STRIP: if (vertexConversionCount == 4) { // Top right, top left, bottom left calculateNormal(vertexConversion2, vertexConversion0, vertexConversion1); tessellator.internalAddNormal(normal); tessellator.internalAddNormal(normal); // Top right to top left, bottom right to bottom left colorConversion0.set(colorConversion2); colorConversion1.set(colorConversion3); normalConversion0.set(normalConversion2); normalConversion1.set(normalConversion3); texCoordConversion0.set(texCoordConversion2); texCoordConversion1.set(texCoordConversion3); vertexConversion0.set(vertexConversion2); vertexConversion1.set(vertexConversion3); colorConversionCount = 2; normalConversionCount = 2; texCoordConversionCount = 2; vertexConversionCount = 2; } break; case Primitives.QUADS: if (vertexConversionCount == 4) { // Top right, top left, bottom left calculateNormal(vertexConversion0, vertexConversion1, vertexConversion2); tessellator.internalAddNormal(normal); tessellator.internalAddNormal(normal); tessellator.internalAddNormal(normal); tessellator.internalAddNormal(normal); colorConversionCount = 0; normalConversionCount = 0; texCoordConversionCount = 0; vertexConversionCount = 0; } break; case Primitives.TRIANGLE_FAN: if (vertexConversionCount == 3) { if (firstConvert) { normal.set(vertexConversion0.getX(), vertexConversion0.getY(), vertexConversion0.getZ()); normal.normalize(); tessellator.internalAddNormal(normal); firstConvert = false; } // Center, bottom left, bottom right calculateNormal(vertexConversion0, vertexConversion1, vertexConversion2); tessellator.internalAddNormal(normal); // Bottom right to bottom left colorConversion1.set(colorConversion2); normalConversion1.set(normalConversion2); texCoordConversion1.set(texCoordConversion2); vertexConversion1.set(vertexConversion2); colorConversionCount = 2; normalConversionCount = 2; texCoordConversionCount = 2; vertexConversionCount = 2; } break; case Primitives.TRIANGLE_STRIP: if (vertexConversionCount == 3) { // Top right, top left, bottom left calculateNormal(vertexConversion2, vertexConversion0, vertexConversion1); tessellator.internalAddNormal(normal); if (flipFlop) { flipFlop = !flipFlop; // Top right to top left colorConversion0.set(colorConversion2); normalConversion0.set(normalConversion2); texCoordConversion0.set(texCoordConversion2); vertexConversion0.set(vertexConversion2); } else { flipFlop = !flipFlop; // Bottom right to bottom left colorConversion1.set(colorConversion2); normalConversion1.set(normalConversion2); texCoordConversion1.set(texCoordConversion2); vertexConversion1.set(vertexConversion2); } colorConversionCount = 2; normalConversionCount = 2; texCoordConversionCount = 2; vertexConversionCount = 2; } break; case Primitives.TRIANGLES: if (vertexConversionCount == 3) { // Center, bottom left, bottom right calculateNormal(vertexConversion0, vertexConversion1, vertexConversion2); tessellator.internalAddNormal(normal); tessellator.internalAddNormal(normal); tessellator.internalAddNormal(normal); colorConversionCount = 0; normalConversionCount = 0; texCoordConversionCount = 0; vertexConversionCount = 0; } break; default: throw new IllegalStateException("Unknown primitive mode: " + primitiveMode + " for normal calculation"); } } public void convert(Tessellator tessellator) { if (hasIncompatiblePrimitiveMode(tessellator)) { throw new IllegalStateException("You cannot convert in the primitive mode: " + tessellator.primitiveMode); } if ((!tessellator.enableConversionForPrimitiveMode) && (tessellator.primitiveMode == Primitives.TRIANGLES)) { throw new IllegalStateException("You cannot convert if the Tessellator does not have it enabled"); } switch (primitiveMode) { case Primitives.QUAD_STRIP: if (vertexConversionCount == 4) { convertQuadStripToTriangles(tessellator); // Top right to top left, bottom right to bottom left colorConversion0.set(colorConversion2); colorConversion1.set(colorConversion3); normalConversion0.set(normalConversion2); normalConversion1.set(normalConversion3); texCoordConversion0.set(texCoordConversion2); texCoordConversion1.set(texCoordConversion3); vertexConversion0.set(vertexConversion2); vertexConversion1.set(vertexConversion3); colorConversionCount = 2; normalConversionCount = 2; texCoordConversionCount = 2; vertexConversionCount = 2; } break; case Primitives.QUADS: if (vertexConversionCount == 4) { convertQuadToTriangles(tessellator); colorConversionCount = 0; normalConversionCount = 0; texCoordConversionCount = 0; vertexConversionCount = 0; } break; case Primitives.TRIANGLE_FAN: if (vertexConversionCount == 3) { convertTriangle(tessellator); // Bottom right to bottom left colorConversion1.set(colorConversion2); normalConversion1.set(normalConversion2); texCoordConversion1.set(texCoordConversion2); vertexConversion1.set(vertexConversion2); colorConversionCount = 2; normalConversionCount = 2; texCoordConversionCount = 2; vertexConversionCount = 2; } break; case Primitives.TRIANGLE_STRIP: if (vertexConversionCount == 3) { convertTriangle(tessellator); if (flipFlop) { flipFlop = !flipFlop; // Top right to top left colorConversion0.set(colorConversion2); normalConversion0.set(normalConversion2); texCoordConversion0.set(texCoordConversion2); vertexConversion0.set(vertexConversion2); } else { flipFlop = !flipFlop; // Bottom right to bottom left colorConversion1.set(colorConversion2); normalConversion1.set(normalConversion2); texCoordConversion1.set(texCoordConversion2); vertexConversion1.set(vertexConversion2); } colorConversionCount = 2; normalConversionCount = 2; texCoordConversionCount = 2; vertexConversionCount = 2; } break; default: throw new IllegalStateException("Unknown primitive mode: " + primitiveMode + " for conversion"); } } public void finishCalculateNormals(Tessellator tessellator) { if (hasIncompatiblePrimitiveMode(tessellator)) { throw new IllegalStateException("You cannot finish calculate normals in the primitive mode: " + tessellator.primitiveMode); } if (tessellator.primitiveMode != primitiveMode) { throw new IllegalStateException("The Tessellator and ConversionState's primitiveMode must match"); } if ((tessellator.config.getVertexSize() != 3) && (tessellator.config.getVertexSize()) != 4) { throw new IllegalStateException("Currently do not support finish calculating normals for 2D vectors"); } switch (primitiveMode) { case Primitives.QUAD_STRIP: case Primitives.TRIANGLE_STRIP: calculateNormal(vertexConversion2, vertexConversion0, vertexConversion1); tessellator.internalAddNormal(normal); tessellator.internalAddNormal(normal); break; case Primitives.QUADS: case Primitives.TRIANGLES: // Do nothing break; case Primitives.TRIANGLE_FAN: calculateNormal(vertexConversion0, vertexConversion1, vertexConversion2); tessellator.internalAddNormal(normal); break; default: throw new IllegalStateException("Unknown primitive mode: " + primitiveMode + " for finish normal calculation"); } } public int getPrimitiveMode() { return primitiveMode; } public void setPrimitiveMode(int primitiveMode) { this.primitiveMode = primitiveMode; } public Color getColor() { return color; } public Vector3f getNormal() { return normal; } public Vector3f getTexCoord() { return texCoord; } public Vector4f getVertex() { return vertex; } public final void reset() { firstConvert = true; flipFlop = true; hasColorsToConvert = false; hasNormalsToConvert = false; hasTexCoordsToConvert = false; colorConversionCount = 0; normalConversionCount = 0; texCoordConversionCount = 0; vertexConversionCount = 0; color.setWhite(); colorConversion0.setWhite(); colorConversion1.setWhite(); colorConversion2.setWhite(); colorConversion3.setWhite(); normal.zero(); normalConversion0.zero(); normalConversion1.zero(); normalConversion2.zero(); normalConversion3.zero(); texCoord.zero(); texCoordConversion0.zero(); texCoordConversion1.zero(); texCoordConversion2.zero(); texCoordConversion3.zero(); vertex.zero(); vertexConversion0.zero(); vertexConversion1.zero(); vertexConversion2.zero(); vertexConversion3.zero(); } } }