Merge branch 'renderer-improvements' into experimental
This commit is contained in:
		
						commit
						ff6b1be725
					
				| @ -139,6 +139,11 @@ class BitmapTextPage extends Geometry { | |||||||
|         int vertCount = pageQuads.size() * 4; |         int vertCount = pageQuads.size() * 4; | ||||||
|         int triCount = pageQuads.size() * 2; |         int triCount = pageQuads.size() * 2; | ||||||
|          |          | ||||||
|  |         if (vertCount > m.getVertexCount() || | ||||||
|  |             triCount  > m.getTriangleCount()) { | ||||||
|  |             m.setUpdateNeeded(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         VertexBuffer pb = m.getBuffer(Type.Position); |         VertexBuffer pb = m.getBuffer(Type.Position); | ||||||
|         VertexBuffer tb = m.getBuffer(Type.TexCoord); |         VertexBuffer tb = m.getBuffer(Type.TexCoord); | ||||||
|         VertexBuffer ib = m.getBuffer(Type.Index); |         VertexBuffer ib = m.getBuffer(Type.Index); | ||||||
|  | |||||||
| @ -284,6 +284,12 @@ public interface Renderer { | |||||||
|      */ |      */ | ||||||
|     public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData); |     public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData); | ||||||
|      |      | ||||||
|  |     /** | ||||||
|  |      * Delete a Mesh (or Vertex Array Object in GL terms) from the GPU. | ||||||
|  |      * @param mesh The mesh to delete. | ||||||
|  |      */ | ||||||
|  |     public void deleteMesh(Mesh mesh); | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Resets all previously used {@link NativeObject Native Objects} on this Renderer. |      * Resets all previously used {@link NativeObject Native Objects} on this Renderer. | ||||||
|      * The state of the native objects is reset in such way, that using |      * The state of the native objects is reset in such way, that using | ||||||
|  | |||||||
| @ -525,9 +525,9 @@ public final class GLRenderer implements Renderer { | |||||||
| 
 | 
 | ||||||
|         if (caps.contains(Caps.CoreProfile)) { |         if (caps.contains(Caps.CoreProfile)) { | ||||||
|             // Core Profile requires VAO to be bound. |             // Core Profile requires VAO to be bound. | ||||||
|             gl3.glGenVertexArrays(intBuf16); | //            gl3.glGenVertexArrays(intBuf16); | ||||||
|             int vaoId = intBuf16.get(0); | //            int vaoId = intBuf16.get(0); | ||||||
|             gl3.glBindVertexArray(vaoId); | //            gl3.glBindVertexArray(vaoId); | ||||||
|         } |         } | ||||||
|         if (gl2 != null) { |         if (gl2 != null) { | ||||||
|             gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE); |             gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE); | ||||||
| @ -971,12 +971,12 @@ public final class GLRenderer implements Renderer { | |||||||
|                 gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE); |                 gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE); | ||||||
|                 break; |                 break; | ||||||
|             case Matrix3: |             case Matrix3: | ||||||
|                 fb = (FloatBuffer) uniform.getValue(); |                 fb = uniform.getMultiData(); | ||||||
|                 assert fb.remaining() == 9; |                 assert fb.remaining() == 9; | ||||||
|                 gl.glUniformMatrix3(loc, false, fb); |                 gl.glUniformMatrix3(loc, false, fb); | ||||||
|                 break; |                 break; | ||||||
|             case Matrix4: |             case Matrix4: | ||||||
|                 fb = (FloatBuffer) uniform.getValue(); |                 fb = uniform.getMultiData(); | ||||||
|                 assert fb.remaining() == 16; |                 assert fb.remaining() == 16; | ||||||
|                 gl.glUniformMatrix4(loc, false, fb); |                 gl.glUniformMatrix4(loc, false, fb); | ||||||
|                 break; |                 break; | ||||||
| @ -985,23 +985,23 @@ public final class GLRenderer implements Renderer { | |||||||
|                 gl.glUniform1(loc, ib); |                 gl.glUniform1(loc, ib); | ||||||
|                 break; |                 break; | ||||||
|             case FloatArray: |             case FloatArray: | ||||||
|                 fb = (FloatBuffer) uniform.getValue(); |                 fb = uniform.getMultiData(); | ||||||
|                 gl.glUniform1(loc, fb); |                 gl.glUniform1(loc, fb); | ||||||
|                 break; |                 break; | ||||||
|             case Vector2Array: |             case Vector2Array: | ||||||
|                 fb = (FloatBuffer) uniform.getValue(); |                 fb = uniform.getMultiData(); | ||||||
|                 gl.glUniform2(loc, fb); |                 gl.glUniform2(loc, fb); | ||||||
|                 break; |                 break; | ||||||
|             case Vector3Array: |             case Vector3Array: | ||||||
|                 fb = (FloatBuffer) uniform.getValue(); |                 fb = uniform.getMultiData(); | ||||||
|                 gl.glUniform3(loc, fb); |                 gl.glUniform3(loc, fb); | ||||||
|                 break; |                 break; | ||||||
|             case Vector4Array: |             case Vector4Array: | ||||||
|                 fb = (FloatBuffer) uniform.getValue(); |                 fb = uniform.getMultiData(); | ||||||
|                 gl.glUniform4(loc, fb); |                 gl.glUniform4(loc, fb); | ||||||
|                 break; |                 break; | ||||||
|             case Matrix4Array: |             case Matrix4Array: | ||||||
|                 fb = (FloatBuffer) uniform.getValue(); |                 fb = uniform.getMultiData(); | ||||||
|                 gl.glUniformMatrix4(loc, false, fb); |                 gl.glUniformMatrix4(loc, false, fb); | ||||||
|                 break; |                 break; | ||||||
|             case Int: |             case Int: | ||||||
| @ -2048,6 +2048,7 @@ public final class GLRenderer implements Renderer { | |||||||
| 
 | 
 | ||||||
|         // bind texture |         // bind texture | ||||||
|         int target = convertTextureType(type, img.getMultiSamples(), -1); |         int target = convertTextureType(type, img.getMultiSamples(), -1); | ||||||
|  | 
 | ||||||
|         bindTextureAndUnit(target, img, unit); |         bindTextureAndUnit(target, img, unit); | ||||||
| 
 | 
 | ||||||
|         if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { |         if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { | ||||||
| @ -2334,32 +2335,37 @@ public final class GLRenderer implements Renderer { | |||||||
|         context.attribIndexList.copyNewToOld(); |         context.attribIndexList.copyNewToOld(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { |     private int updateAttributeLocation(Shader shader, VertexBuffer.Type attribType) { | ||||||
|         if (vb.getBufferType() == VertexBuffer.Type.Index) { |         Attribute attrib = shader.getAttribute(attribType); | ||||||
|             throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (context.boundShaderProgram <= 0) { |  | ||||||
|             throw new IllegalStateException("Cannot render mesh without shader bound"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Attribute attrib = context.boundShader.getAttribute(vb.getBufferType()); |  | ||||||
|         int loc = attrib.getLocation(); |         int loc = attrib.getLocation(); | ||||||
|         if (loc == -1) { |         if (loc == -1) { | ||||||
|             return; // not defined |             return -1; // not defined | ||||||
|         } |         } | ||||||
|         if (loc == -2) { |         if (loc == -2) { | ||||||
|             loc = gl.glGetAttribLocation(context.boundShaderProgram, "in" + vb.getBufferType().name()); |             loc = gl.glGetAttribLocation(context.boundShaderProgram, "in" + attribType.name()); | ||||||
| 
 | 
 | ||||||
|             // not really the name of it in the shader (inPosition) but |             // not really the name of it in the shader (inPosition) but | ||||||
|             // the internal name of the enum (Position). |             // the internal name of the enum (Position). | ||||||
|             if (loc < 0) { |             if (loc < 0) { | ||||||
|                 attrib.setLocation(-1); |                 attrib.setLocation(-1); | ||||||
|                 return; // not available in shader. |                 return -1; // not available in shader. | ||||||
|             } else { |             } else { | ||||||
|                 attrib.setLocation(loc); |                 attrib.setLocation(loc); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         return loc; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { | ||||||
|  |         if (vb.getBufferType() == VertexBuffer.Type.Index) { | ||||||
|  |             throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Shader shader = context.boundShader; | ||||||
|  |         int location = updateAttributeLocation(shader, vb.getBufferType()); | ||||||
|  |         if (location == -1) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (vb.isInstanced()) { |         if (vb.isInstanced()) { | ||||||
|             if (!caps.contains(Caps.MeshInstancing)) { |             if (!caps.contains(Caps.MeshInstancing)) { | ||||||
| @ -2383,11 +2389,11 @@ public final class GLRenderer implements Renderer { | |||||||
| 
 | 
 | ||||||
|         VertexBuffer[] attribs = context.boundAttribs; |         VertexBuffer[] attribs = context.boundAttribs; | ||||||
|         for (int i = 0; i < slotsRequired; i++) { |         for (int i = 0; i < slotsRequired; i++) { | ||||||
|             if (!context.attribIndexList.moveToNew(loc + i)) { |             if (!context.attribIndexList.moveToNew(location + i)) { | ||||||
|                 gl.glEnableVertexAttribArray(loc + i); |                 gl.glEnableVertexAttribArray(location + i); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         if (attribs[loc] != vb) { |         if (attribs[location] != vb) { | ||||||
|             // NOTE: Use id from interleaved buffer if specified |             // NOTE: Use id from interleaved buffer if specified | ||||||
|             int bufId = idb != null ? idb.getId() : vb.getId(); |             int bufId = idb != null ? idb.getId() : vb.getId(); | ||||||
|             assert bufId != -1; |             assert bufId != -1; | ||||||
| @ -2400,7 +2406,7 @@ public final class GLRenderer implements Renderer { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (slotsRequired == 1) { |             if (slotsRequired == 1) { | ||||||
|                 gl.glVertexAttribPointer(loc, |                 gl.glVertexAttribPointer(location, | ||||||
|                                          vb.getNumComponents(), |                                          vb.getNumComponents(), | ||||||
|                                          convertFormat(vb.getFormat()), |                                          convertFormat(vb.getFormat()), | ||||||
|                                          vb.isNormalized(), |                                          vb.isNormalized(), | ||||||
| @ -2416,7 +2422,7 @@ public final class GLRenderer implements Renderer { | |||||||
|                     // P4: ____________XXXX____________XXXX |                     // P4: ____________XXXX____________XXXX | ||||||
|                     // stride = 4 bytes in float * 4 floats in slot * num slots |                     // stride = 4 bytes in float * 4 floats in slot * num slots | ||||||
|                     // offset = 4 bytes in float * 4 floats in slot * slot index |                     // offset = 4 bytes in float * 4 floats in slot * slot index | ||||||
|                     gl.glVertexAttribPointer(loc + i, |                     gl.glVertexAttribPointer(location + i, | ||||||
|                                              4, |                                              4, | ||||||
|                                              convertFormat(vb.getFormat()), |                                              convertFormat(vb.getFormat()), | ||||||
|                                              vb.isNormalized(), |                                              vb.isNormalized(), | ||||||
| @ -2426,7 +2432,7 @@ public final class GLRenderer implements Renderer { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             for (int i = 0; i < slotsRequired; i++) { |             for (int i = 0; i < slotsRequired; i++) { | ||||||
|                 int slot = loc + i; |                 int slot = location + i; | ||||||
|                 if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) { |                 if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) { | ||||||
|                     // non-instanced -> instanced |                     // non-instanced -> instanced | ||||||
|                     glext.glVertexAttribDivisorARB(slot, vb.getInstanceSpan()); |                     glext.glVertexAttribDivisorARB(slot, vb.getInstanceSpan()); | ||||||
| @ -2439,6 +2445,92 @@ public final class GLRenderer implements Renderer { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Set VBO on VAO. Assumes a brand new mesh or modified mesh with new buffer. | ||||||
|  |      * | ||||||
|  |      * @param vb | ||||||
|  |      * @param idb | ||||||
|  |      */ | ||||||
|  |     public void setVertexAttribVAO(VertexBuffer vb, VertexBuffer idb) { | ||||||
|  |         if (vb.getBufferType() == VertexBuffer.Type.Index) { | ||||||
|  |             throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Shader shader = context.boundShader; | ||||||
|  |         int location = updateAttributeLocation(shader, vb.getBufferType()); | ||||||
|  |         if (location == -1) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (vb.isInstanced()) { | ||||||
|  |             if (!caps.contains(Caps.MeshInstancing)) { | ||||||
|  |                 throw new RendererException("Instancing is required, " | ||||||
|  |                         + "but not supported by the " | ||||||
|  |                         + "graphics hardware"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         int slotsRequired = 1; | ||||||
|  |         if (vb.getNumComponents() > 4) { | ||||||
|  |             if (vb.getNumComponents() % 4 != 0) { | ||||||
|  |                 throw new RendererException("Number of components in multi-slot " | ||||||
|  |                         + "buffers must be divisible by 4"); | ||||||
|  |             } | ||||||
|  |             slotsRequired = vb.getNumComponents() / 4; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (vb.isUpdateNeeded() && idb == null) { | ||||||
|  |             updateBufferData(vb); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (int i = 0; i < slotsRequired; i++) { | ||||||
|  |             gl.glEnableVertexAttribArray(location + i); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // NOTE: Use id from interleaved buffer if specified | ||||||
|  |         int bufId = idb != null ? idb.getId() : vb.getId(); | ||||||
|  |         assert bufId != -1; | ||||||
|  |         if (context.boundArrayVBO != bufId) { | ||||||
|  |             gl.glBindBuffer(GL.GL_ARRAY_BUFFER, bufId); | ||||||
|  |             context.boundArrayVBO = bufId; | ||||||
|  |             //statistics.onVertexBufferUse(vb, true); | ||||||
|  |         } else { | ||||||
|  |             //statistics.onVertexBufferUse(vb, false); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (slotsRequired == 1) { | ||||||
|  |             gl.glVertexAttribPointer(location, | ||||||
|  |                                      vb.getNumComponents(), | ||||||
|  |                                      convertFormat(vb.getFormat()), | ||||||
|  |                                      vb.isNormalized(), | ||||||
|  |                                      vb.getStride(), | ||||||
|  |                                      vb.getOffset()); | ||||||
|  |         } else { | ||||||
|  |             for (int i = 0; i < slotsRequired; i++) { | ||||||
|  |                 // The pointer maps the next 4 floats in the slot. | ||||||
|  |                 // E.g. | ||||||
|  |                 // P1: XXXX____________XXXX____________ | ||||||
|  |                 // P2: ____XXXX____________XXXX________ | ||||||
|  |                 // P3: ________XXXX____________XXXX____ | ||||||
|  |                 // P4: ____________XXXX____________XXXX | ||||||
|  |                 // stride = 4 bytes in float * 4 floats in slot * num slots | ||||||
|  |                 // offset = 4 bytes in float * 4 floats in slot * slot index | ||||||
|  |                 gl.glVertexAttribPointer(location + i, | ||||||
|  |                                          4, | ||||||
|  |                                          convertFormat(vb.getFormat()), | ||||||
|  |                                          vb.isNormalized(), | ||||||
|  |                                          4 * 4 * slotsRequired, | ||||||
|  |                                          4 * 4 * i); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (int i = 0; i < slotsRequired; i++) { | ||||||
|  |             int slot = location + i; | ||||||
|  |             if (vb.isInstanced()) { | ||||||
|  |                 glext.glVertexAttribDivisorARB(slot, vb.getInstanceSpan()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public void setVertexAttrib(VertexBuffer vb) { |     public void setVertexAttrib(VertexBuffer vb) { | ||||||
|         setVertexAttrib(vb, null); |         setVertexAttrib(vb, null); | ||||||
|     } |     } | ||||||
| @ -2488,44 +2580,8 @@ public final class GLRenderer implements Renderer { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         int vertCount = mesh.getVertexCount(); |         int vertCount = mesh.getVertexCount(); | ||||||
|         if (mesh.getMode() == Mode.Hybrid) { |         boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); | ||||||
|             int[] modeStart = mesh.getModeStart(); |         if (useInstancing) { | ||||||
|             int[] elementLengths = mesh.getElementLengths(); |  | ||||||
| 
 |  | ||||||
|             int elMode = convertElementMode(Mode.Triangles); |  | ||||||
|             int fmt = convertFormat(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.TriangleFan); |  | ||||||
|                 } |  | ||||||
|                 int elementLength = elementLengths[i]; |  | ||||||
| 
 |  | ||||||
|                 if (count > 1) { |  | ||||||
|                     glext.glDrawElementsInstancedARB(elMode, |  | ||||||
|                             elementLength, |  | ||||||
|                             fmt, |  | ||||||
|                             curOffset, |  | ||||||
|                             count); |  | ||||||
|                 } else { |  | ||||||
|                     gl.glDrawRangeElements(elMode, |  | ||||||
|                             0, |  | ||||||
|                             vertCount, |  | ||||||
|                             elementLength, |  | ||||||
|                             fmt, |  | ||||||
|                             curOffset); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 curOffset += elementLength * elSize; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             if (count > 1) { |  | ||||||
|             glext.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()), |             glext.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()), | ||||||
|                     indexBuf.getData().limit(), |                     indexBuf.getData().limit(), | ||||||
|                     convertFormat(indexBuf.getFormat()), |                     convertFormat(indexBuf.getFormat()), | ||||||
| @ -2540,7 +2596,6 @@ public final class GLRenderer implements Renderer { | |||||||
|                     0); |                     0); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     /*********************************************************************\ |     /*********************************************************************\ | ||||||
|      |* Render Calls                                                      *| |      |* Render Calls                                                      *| | ||||||
| @ -2568,91 +2623,12 @@ public final class GLRenderer implements Renderer { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) { |     private void setupVertexBuffersLegacy(Mesh mesh, VertexBuffer[] instanceData) { | ||||||
|         int id = mesh.getId(); |  | ||||||
|         if (id == -1) { |  | ||||||
|             IntBuffer temp = intBuf1; |  | ||||||
|             gl3.glGenVertexArrays(temp); |  | ||||||
|             id = temp.get(0); |  | ||||||
|             mesh.setId(id); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (context.boundVertexArray != id) { |  | ||||||
|             gl3.glBindVertexArray(id); |  | ||||||
|             context.boundVertexArray = id; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); |         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); | ||||||
|         if (interleavedData != null && interleavedData.isUpdateNeeded()) { |         if (interleavedData != null && interleavedData.isUpdateNeeded()) { | ||||||
|             updateBufferData(interleavedData); |             updateBufferData(interleavedData); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (instanceData != null) { |  | ||||||
|             setVertexAttrib(instanceData, null); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         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); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void renderMeshVertexArray(Mesh mesh, int lod, int count, VertexBuffer instanceData) { |  | ||||||
|         if (mesh.getId() == -1) { |  | ||||||
|             updateVertexArray(mesh, instanceData); |  | ||||||
|         } else { |  | ||||||
|             // TODO: Check if it was updated |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (context.boundVertexArray != mesh.getId()) { |  | ||||||
|             gl3.glBindVertexArray(mesh.getId()); |  | ||||||
|             context.boundVertexArray = mesh.getId(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| //        IntMap<VertexBuffer> buffers = mesh.getBuffers(); |  | ||||||
|         VertexBuffer indices; |  | ||||||
|         if (mesh.getNumLodLevels() > 0) { |  | ||||||
|             indices = mesh.getLodLevel(lod); |  | ||||||
|         } else { |  | ||||||
|             indices = mesh.getBuffer(Type.Index); |  | ||||||
|         } |  | ||||||
|         if (indices != null) { |  | ||||||
|             drawTriangleList(indices, mesh, count); |  | ||||||
|         } else { |  | ||||||
|             drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); |  | ||||||
|         } |  | ||||||
|         clearVertexAttribs(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { |  | ||||||
| 
 |  | ||||||
|         // Here while count is still passed in.  Can be removed when/if |  | ||||||
|         // the method is collapsed again.  -pspeed         |  | ||||||
|         count = Math.max(mesh.getInstanceCount(), count); |  | ||||||
| 
 |  | ||||||
|         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); |  | ||||||
|         if (interleavedData != null && interleavedData.isUpdateNeeded()) { |  | ||||||
|             updateBufferData(interleavedData); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         VertexBuffer indices; |  | ||||||
|         if (mesh.getNumLodLevels() > 0) { |  | ||||||
|             indices = mesh.getLodLevel(lod); |  | ||||||
|         } else { |  | ||||||
|             indices = mesh.getBuffer(Type.Index); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (instanceData != null) { |         if (instanceData != null) { | ||||||
|             for (VertexBuffer vb : instanceData) { |             for (VertexBuffer vb : instanceData) { | ||||||
|                 setVertexAttrib(vb, null); |                 setVertexAttrib(vb, null); | ||||||
| @ -2674,6 +2650,125 @@ public final class GLRenderer implements Renderer { | |||||||
|                 setVertexAttrib(vb, interleavedData); |                 setVertexAttrib(vb, interleavedData); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void setupVertexBuffers(Mesh mesh, VertexBuffer[] instanceData) { | ||||||
|  |         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); | ||||||
|  |         if (instanceData != null) { | ||||||
|  |             for (VertexBuffer vb : instanceData) { | ||||||
|  |                 setVertexAttribVAO(vb, null); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         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 | ||||||
|  |                 setVertexAttribVAO(vb, null); | ||||||
|  |             } else { | ||||||
|  |                 // interleaved | ||||||
|  |                 setVertexAttribVAO(vb, interleavedData); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         mesh.clearUpdateNeeded(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void updateVertexBuffers(Mesh mesh, VertexBuffer[] instanceData) { | ||||||
|  |         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); | ||||||
|  |         if (interleavedData != null && interleavedData.isUpdateNeeded()) { | ||||||
|  |             updateBufferData(interleavedData); | ||||||
|  |         } | ||||||
|  |         if (instanceData != null) { | ||||||
|  |             for (VertexBuffer vb : instanceData) { | ||||||
|  |                 if (vb.isUpdateNeeded()) { | ||||||
|  |                     updateBufferData(vb); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         for (VertexBuffer vb : mesh.getBufferList().getArray()) { | ||||||
|  |             if (vb.getBufferType() == Type.InterleavedData | ||||||
|  |                     || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers | ||||||
|  |                     || vb.getBufferType() == Type.Index | ||||||
|  |                     || !vb.isUpdateNeeded() | ||||||
|  |                     || !context.boundShader.isAttributeDefined(vb.getBufferType())) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             updateBufferData(vb); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private VertexBuffer getIndexBuffer(Mesh mesh, int lod) { | ||||||
|  |         VertexBuffer indices; | ||||||
|  |         if (mesh.getNumLodLevels() > 0) { | ||||||
|  |             indices = mesh.getLodLevel(lod); | ||||||
|  |         } else { | ||||||
|  |             indices = mesh.getBuffer(Type.Index); | ||||||
|  |         } | ||||||
|  |         return indices; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void setVertexArrayObject(Mesh mesh) { | ||||||
|  |         int id = mesh.getId(); | ||||||
|  | 
 | ||||||
|  |         if (id == -1) { | ||||||
|  |             IntBuffer temp = intBuf1; | ||||||
|  |             gl3.glGenVertexArrays(temp); | ||||||
|  |             id = temp.get(0); | ||||||
|  |             mesh.setId(id); | ||||||
|  | 
 | ||||||
|  |             objManager.registerObject(mesh); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (context.boundVertexArray != id) { | ||||||
|  |             gl3.glBindVertexArray(id); | ||||||
|  |             context.boundVertexArray = id; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { | ||||||
|  |         setVertexArrayObject(mesh); | ||||||
|  | 
 | ||||||
|  |         // VAO clears current bound VBO automatically | ||||||
|  |         context.boundElementArrayVBO = 0; | ||||||
|  |         context.boundArrayVBO = 0; | ||||||
|  | 
 | ||||||
|  |         VertexBuffer indices = getIndexBuffer(mesh, lod); | ||||||
|  |         if (mesh.isUpdateNeeded()) { | ||||||
|  |             setupVertexBuffers(mesh, instanceData); | ||||||
|  |             if (indices != null) { | ||||||
|  |                 updateBufferData(indices); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             updateVertexBuffers(mesh, instanceData); | ||||||
|  |             if (indices != null) { | ||||||
|  |                 // NOTE: context.boundElementArrayVBO gets captured in the VAO. | ||||||
|  |                 // Make everyone think its already bound. | ||||||
|  |                 context.boundElementArrayVBO = indices.getId(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (indices != null) { | ||||||
|  |             if (indices.isUpdateNeeded()) { | ||||||
|  |                 updateBufferData(indices); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             drawTriangleList(indices, mesh, count); | ||||||
|  | 
 | ||||||
|  |             context.boundElementArrayVBO = 0; | ||||||
|  |         } else { | ||||||
|  |             drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void renderMeshLegacy(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { | ||||||
|  |         setupVertexBuffersLegacy(mesh, instanceData); | ||||||
|  |         VertexBuffer indices = getIndexBuffer(mesh, lod); | ||||||
| 
 | 
 | ||||||
|         clearVertexAttribs(); |         clearVertexAttribs(); | ||||||
| 
 | 
 | ||||||
| @ -2682,6 +2777,7 @@ public final class GLRenderer implements Renderer { | |||||||
|         } else { |         } else { | ||||||
|             drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); |             drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { |     public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { | ||||||
| @ -2701,12 +2797,30 @@ public final class GLRenderer implements Renderer { | |||||||
|         if (gl4 != null && mesh.getMode().equals(Mode.Patch)) { |         if (gl4 != null && mesh.getMode().equals(Mode.Patch)) { | ||||||
|             gl4.glPatchParameter(mesh.getPatchVertexCount()); |             gl4.glPatchParameter(mesh.getPatchVertexCount()); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         statistics.onMeshDrawn(mesh, lod, count); |         statistics.onMeshDrawn(mesh, lod, count); | ||||||
| //        if (ctxCaps.GL_ARB_vertex_array_object){ | 
 | ||||||
| //            renderMeshVertexArray(mesh, lod, count); |         // Here while count is still passed in.  Can be removed when/if | ||||||
| //        }else{ |         // the method is collapsed again.  -pspeed | ||||||
|  |         count = Math.max(mesh.getInstanceCount(), count); | ||||||
|  | 
 | ||||||
|  | //         if (caps.contains(Caps.VertexBufferArray)) { | ||||||
|              renderMeshDefault(mesh, lod, count, instanceData); |              renderMeshDefault(mesh, lod, count, instanceData); | ||||||
| //        } | //         } else { | ||||||
|  | //            renderMeshLegacy(mesh, lod, count, instanceData); | ||||||
|  | //        // } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void deleteMesh(Mesh mesh) { | ||||||
|  |         int bufId = mesh.getId(); | ||||||
|  |         if (bufId != -1) { | ||||||
|  |             // delete vertex array object | ||||||
|  |             intBuf1.put(0, bufId); | ||||||
|  |             intBuf1.position(0).limit(1); | ||||||
|  |             gl3.glDeleteVertexArrays(intBuf1); | ||||||
|  |             mesh.resetObject(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void setMainFrameBufferSrgb(boolean enableSrgb) { |     public void setMainFrameBufferSrgb(boolean enableSrgb) { | ||||||
|  | |||||||
| @ -42,6 +42,7 @@ import com.jme3.math.Matrix4f; | |||||||
| import com.jme3.math.Triangle; | import com.jme3.math.Triangle; | ||||||
| import com.jme3.math.Vector2f; | import com.jme3.math.Vector2f; | ||||||
| import com.jme3.math.Vector3f; | import com.jme3.math.Vector3f; | ||||||
|  | import com.jme3.renderer.Renderer; | ||||||
| import com.jme3.scene.VertexBuffer.Format; | import com.jme3.scene.VertexBuffer.Format; | ||||||
| import com.jme3.scene.VertexBuffer.Type; | import com.jme3.scene.VertexBuffer.Type; | ||||||
| import com.jme3.scene.VertexBuffer.Usage; | import com.jme3.scene.VertexBuffer.Usage; | ||||||
| @ -49,6 +50,7 @@ import com.jme3.scene.mesh.*; | |||||||
| import com.jme3.util.BufferUtils; | import com.jme3.util.BufferUtils; | ||||||
| import com.jme3.util.IntMap; | import com.jme3.util.IntMap; | ||||||
| import com.jme3.util.IntMap.Entry; | import com.jme3.util.IntMap.Entry; | ||||||
|  | import com.jme3.util.NativeObject; | ||||||
| import com.jme3.util.SafeArrayList; | import com.jme3.util.SafeArrayList; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.nio.*; | import java.nio.*; | ||||||
| @ -71,7 +73,7 @@ import java.util.ArrayList; | |||||||
|  *  |  *  | ||||||
|  * @author Kirill Vainer |  * @author Kirill Vainer | ||||||
|  */ |  */ | ||||||
| public class Mesh implements Savable, Cloneable { | public class Mesh extends NativeObject implements Savable { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * The mode of the Mesh specifies both the type of primitive represented |      * The mode of the Mesh specifies both the type of primitive represented | ||||||
| @ -127,19 +129,14 @@ public class Mesh implements Savable, Cloneable { | |||||||
|          */ |          */ | ||||||
|         TriangleFan(false), |         TriangleFan(false), | ||||||
|          |          | ||||||
|         /** |         Reserved(false), | ||||||
|          * A combination of various triangle modes. It is best to avoid |          | ||||||
|          * using this mode as it may not be supported by all renderers. |  | ||||||
|          * The {@link Mesh#setModeStart(int[]) mode start points} and |  | ||||||
|          * {@link Mesh#setElementLengths(int[]) element lengths} must  |  | ||||||
|          * be specified for this mode. |  | ||||||
|          */ |  | ||||||
|         Hybrid(false), |  | ||||||
|         /** |         /** | ||||||
|          * Used for Tesselation only. Requires to set the number of vertices |          * Used for Tesselation only. Requires to set the number of vertices | ||||||
|          * for each patch (default is 3 for triangle tesselation) |          * for each patch (default is 3 for triangle tesselation) | ||||||
|          */ |          */ | ||||||
|         Patch(true); |         Patch(true); | ||||||
|  |          | ||||||
|         private boolean listMode = false; |         private boolean listMode = false; | ||||||
|          |          | ||||||
|         private Mode(boolean listMode){ |         private Mode(boolean listMode){ | ||||||
| @ -182,9 +179,6 @@ public class Mesh implements Savable, Cloneable { | |||||||
|     private int patchVertexCount=3; //only used for tesselation |     private int patchVertexCount=3; //only used for tesselation | ||||||
|     private int maxNumWeights = -1; // only if using skeletal animation |     private int maxNumWeights = -1; // only if using skeletal animation | ||||||
| 
 | 
 | ||||||
|     private int[] elementLengths; |  | ||||||
|     private int[] modeStart; |  | ||||||
| 
 |  | ||||||
|     private Mode mode = Mode.Triangles; |     private Mode mode = Mode.Triangles; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -193,6 +187,10 @@ public class Mesh implements Savable, Cloneable { | |||||||
|     public Mesh(){ |     public Mesh(){ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     protected Mesh(int id) { | ||||||
|  |         super(id); | ||||||
|  |     } | ||||||
|  |      | ||||||
|     /** |     /** | ||||||
|      * Create a shallow clone of this Mesh. The {@link VertexBuffer vertex |      * Create a shallow clone of this Mesh. The {@link VertexBuffer vertex | ||||||
|      * buffers} are shared between this and the clone mesh, the rest |      * buffers} are shared between this and the clone mesh, the rest | ||||||
| @ -202,23 +200,12 @@ public class Mesh implements Savable, Cloneable { | |||||||
|      */ |      */ | ||||||
|     @Override |     @Override | ||||||
|     public Mesh clone() { |     public Mesh clone() { | ||||||
|         try { |  | ||||||
|         Mesh clone = (Mesh) super.clone(); |         Mesh clone = (Mesh) super.clone(); | ||||||
|         clone.meshBound = meshBound.clone(); |         clone.meshBound = meshBound.clone(); | ||||||
|         clone.collisionTree = collisionTree != null ? collisionTree : null; |         clone.collisionTree = collisionTree != null ? collisionTree : null; | ||||||
|         clone.buffers = buffers.clone(); |         clone.buffers = buffers.clone(); | ||||||
|         clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class,buffersList); |         clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class,buffersList); | ||||||
|             clone.vertexArrayID = -1; |  | ||||||
|             if (elementLengths != null) { |  | ||||||
|                 clone.elementLengths = elementLengths.clone(); |  | ||||||
|             } |  | ||||||
|             if (modeStart != null) { |  | ||||||
|                 clone.modeStart = modeStart.clone(); |  | ||||||
|             } |  | ||||||
|         return clone; |         return clone; | ||||||
|         } catch (CloneNotSupportedException ex) { |  | ||||||
|             throw new AssertionError(); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -229,7 +216,6 @@ public class Mesh implements Savable, Cloneable { | |||||||
|      * @return a deep clone of this mesh. |      * @return a deep clone of this mesh. | ||||||
|      */ |      */ | ||||||
|     public Mesh deepClone(){ |     public Mesh deepClone(){ | ||||||
|         try{ |  | ||||||
|         Mesh clone = (Mesh) super.clone(); |         Mesh clone = (Mesh) super.clone(); | ||||||
|         clone.meshBound = meshBound != null ? meshBound.clone() : null; |         clone.meshBound = meshBound != null ? meshBound.clone() : null; | ||||||
| 
 | 
 | ||||||
| @ -245,7 +231,6 @@ public class Mesh implements Savable, Cloneable { | |||||||
|             clone.buffersList.add(bufClone); |             clone.buffersList.add(bufClone); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|             clone.vertexArrayID = -1; |  | ||||||
|         clone.vertCount = vertCount; |         clone.vertCount = vertCount; | ||||||
|         clone.elementCount = elementCount; |         clone.elementCount = elementCount; | ||||||
|         clone.instanceCount = instanceCount; |         clone.instanceCount = instanceCount; | ||||||
| @ -254,12 +239,7 @@ public class Mesh implements Savable, Cloneable { | |||||||
|         // if the bone weight/index buffers are modified |         // if the bone weight/index buffers are modified | ||||||
|         clone.maxNumWeights = maxNumWeights;  |         clone.maxNumWeights = maxNumWeights;  | ||||||
|          |          | ||||||
|             clone.elementLengths = elementLengths != null ? elementLengths.clone() : null; |  | ||||||
|             clone.modeStart = modeStart != null ? modeStart.clone() : null; |  | ||||||
|         return clone; |         return clone; | ||||||
|         }catch (CloneNotSupportedException ex){ |  | ||||||
|             throw new AssertionError(); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -374,11 +354,16 @@ public class Mesh implements Savable, Cloneable { | |||||||
|             // convert weights on the heap		 |             // convert weights on the heap		 | ||||||
|             VertexBuffer weights = getBuffer(Type.BoneWeight);		 |             VertexBuffer weights = getBuffer(Type.BoneWeight);		 | ||||||
|             if (!weights.getData().hasArray()) { |             if (!weights.getData().hasArray()) { | ||||||
|  |                 if (weights.getFormat() == Format.Float) { | ||||||
|                     FloatBuffer originalWeight = (FloatBuffer) weights.getData();		 |                     FloatBuffer originalWeight = (FloatBuffer) weights.getData();		 | ||||||
|                     FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());		 |                     FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());		 | ||||||
|                     originalWeight.clear();		 |                     originalWeight.clear();		 | ||||||
|                     arrayWeight.put(originalWeight);		 |                     arrayWeight.put(originalWeight);		 | ||||||
|                     weights.updateData(arrayWeight);		 |                     weights.updateData(arrayWeight);		 | ||||||
|  |                 } else { | ||||||
|  |                     // UByte to Float conversion | ||||||
|  |                     throw new UnsupportedOperationException("Not yet supported"); | ||||||
|  |                 } | ||||||
|             }		 |             }		 | ||||||
|             weights.setUsage(Usage.CpuOnly); |             weights.setUsage(Usage.CpuOnly); | ||||||
|             // position, normal, and tanget buffers to be in "Stream" mode |             // position, normal, and tanget buffers to be in "Stream" mode | ||||||
| @ -475,40 +460,6 @@ public class Mesh implements Savable, Cloneable { | |||||||
|         return lodLevels[lod]; |         return lodLevels[lod]; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     /** |  | ||||||
|      * Get the element lengths for {@link Mode#Hybrid} mesh mode. |  | ||||||
|      *  |  | ||||||
|      * @return element lengths |  | ||||||
|      */ |  | ||||||
|     public int[] getElementLengths() { |  | ||||||
|         return elementLengths; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Set the element lengths for {@link Mode#Hybrid} mesh mode. |  | ||||||
|      *  |  | ||||||
|      * @param elementLengths The element lengths to set |  | ||||||
|      */ |  | ||||||
|     public void setElementLengths(int[] elementLengths) { |  | ||||||
|         this.elementLengths = elementLengths; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Set the mode start indices for {@link Mode#Hybrid} mesh mode. |  | ||||||
|      *  |  | ||||||
|      * @return mode start indices |  | ||||||
|      */ |  | ||||||
|     public int[] getModeStart() { |  | ||||||
|         return modeStart; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get the mode start indices for {@link Mode#Hybrid} mesh mode. |  | ||||||
|      */ |  | ||||||
|     public void setModeStart(int[] modeStart) { |  | ||||||
|         this.modeStart = modeStart; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * Returns the mesh mode |      * Returns the mesh mode | ||||||
|      *  |      *  | ||||||
| @ -899,23 +850,6 @@ public class Mesh implements Savable, Cloneable { | |||||||
|         indices[2] = ib.get(vertIndex+2); |         indices[2] = ib.get(vertIndex+2); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Returns the mesh's VAO ID. Internal use only. |  | ||||||
|      */ |  | ||||||
|     public int getId(){ |  | ||||||
|         return vertexArrayID; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Sets the mesh's VAO ID. Internal use only. |  | ||||||
|      */ |  | ||||||
|     public void setId(int id){ |  | ||||||
|         if (vertexArrayID != -1) |  | ||||||
|             throw new IllegalStateException("ID has already been set."); |  | ||||||
|          |  | ||||||
|         vertexArrayID = id; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * Generates a collision tree for the mesh. |      * Generates a collision tree for the mesh. | ||||||
|      * Called automatically by {@link #collideWith(com.jme3.collision.Collidable,  |      * Called automatically by {@link #collideWith(com.jme3.collision.Collidable,  | ||||||
| @ -1109,9 +1043,6 @@ public class Mesh implements Savable, Cloneable { | |||||||
|      * @return A virtual or wrapped index buffer to read the data as a list |      * @return A virtual or wrapped index buffer to read the data as a list | ||||||
|      */ |      */ | ||||||
|     public IndexBuffer getIndicesAsList(){ |     public IndexBuffer getIndicesAsList(){ | ||||||
|         if (mode == Mode.Hybrid) |  | ||||||
|             throw new UnsupportedOperationException("Hybrid mode not supported"); |  | ||||||
|          |  | ||||||
|         IndexBuffer ib = getIndexBuffer(); |         IndexBuffer ib = getIndexBuffer(); | ||||||
|         if (ib != null) { |         if (ib != null) { | ||||||
|             if (mode.isListMode()) { |             if (mode.isListMode()) { | ||||||
| @ -1385,16 +1316,30 @@ public class Mesh implements Savable, Cloneable { | |||||||
|         return patchVertexCount; |         return patchVertexCount; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public void resetObject() { | ||||||
|  |         id = -1; | ||||||
|  |         setUpdateNeeded(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void deleteObject(Object rendererObject) { | ||||||
|  |         ((Renderer)rendererObject).deleteMesh(this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public NativeObject createDestructableClone() { | ||||||
|  |         return new Mesh(id); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public long getUniqueId() { | ||||||
|  |         return ((long)OBJTYPE_MESH << 32) | ((long)id); | ||||||
|  |     } | ||||||
|  |      | ||||||
|     public void write(JmeExporter ex) throws IOException { |     public void write(JmeExporter ex) throws IOException { | ||||||
|         OutputCapsule out = ex.getCapsule(this); |         OutputCapsule out = ex.getCapsule(this); | ||||||
| 
 | 
 | ||||||
| //        HashMap<String, VertexBuffer> map = new HashMap<String, VertexBuffer>(); |  | ||||||
| //        for (Entry<VertexBuffer> buf : buffers){ |  | ||||||
| //            if (buf.getValue() != null) |  | ||||||
| //                map.put(buf.getKey()+"a", buf.getValue()); |  | ||||||
| //        } |  | ||||||
| //        out.writeStringSavableMap(map, "buffers", null); |  | ||||||
| 
 |  | ||||||
|         out.write(meshBound, "modelBound", null); |         out.write(meshBound, "modelBound", null); | ||||||
|         out.write(vertCount, "vertCount", -1); |         out.write(vertCount, "vertCount", -1); | ||||||
|         out.write(elementCount, "elementCount", -1); |         out.write(elementCount, "elementCount", -1); | ||||||
| @ -1402,8 +1347,6 @@ public class Mesh implements Savable, Cloneable { | |||||||
|         out.write(maxNumWeights, "max_num_weights", -1); |         out.write(maxNumWeights, "max_num_weights", -1); | ||||||
|         out.write(mode, "mode", Mode.Triangles); |         out.write(mode, "mode", Mode.Triangles); | ||||||
|         out.write(collisionTree, "collisionTree", null); |         out.write(collisionTree, "collisionTree", null); | ||||||
|         out.write(elementLengths, "elementLengths", null); |  | ||||||
|         out.write(modeStart, "modeStart", null); |  | ||||||
|         out.write(pointSize, "pointSize", 1f); |         out.write(pointSize, "pointSize", 1f); | ||||||
|          |          | ||||||
|         //Removing HW skinning buffers to not save them |         //Removing HW skinning buffers to not save them | ||||||
| @ -1439,14 +1382,9 @@ public class Mesh implements Savable, Cloneable { | |||||||
|         instanceCount = in.readInt("instanceCount", -1); |         instanceCount = in.readInt("instanceCount", -1); | ||||||
|         maxNumWeights = in.readInt("max_num_weights", -1); |         maxNumWeights = in.readInt("max_num_weights", -1); | ||||||
|         mode = in.readEnum("mode", Mode.class, Mode.Triangles); |         mode = in.readEnum("mode", Mode.class, Mode.Triangles); | ||||||
|         elementLengths = in.readIntArray("elementLengths", null); |  | ||||||
|         modeStart = in.readIntArray("modeStart", null); |  | ||||||
|         collisionTree = (BIHTree) in.readSavable("collisionTree", null); |         collisionTree = (BIHTree) in.readSavable("collisionTree", null); | ||||||
|         elementLengths = in.readIntArray("elementLengths", null); |  | ||||||
|         modeStart = in.readIntArray("modeStart", null); |  | ||||||
|         pointSize = in.readFloat("pointSize", 1f); |         pointSize = in.readFloat("pointSize", 1f); | ||||||
| 
 | 
 | ||||||
| //        in.readStringSavableMap("buffers", null); |  | ||||||
|         buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null); |         buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null); | ||||||
|         for (Entry<VertexBuffer> entry : buffers){ |         for (Entry<VertexBuffer> entry : buffers){ | ||||||
|             buffersList.add(entry.getValue()); |             buffersList.add(entry.getValue()); | ||||||
|  | |||||||
| @ -524,6 +524,17 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { | |||||||
|         this.usage = usage; |         this.usage = usage; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * The size of an element in bytes. | ||||||
|  |      *  | ||||||
|  |      * The number of components multiplied by the size of a component. | ||||||
|  |      *  | ||||||
|  |      * @return size of an element in bytes. | ||||||
|  |      */ | ||||||
|  |     public int getElementSize() { | ||||||
|  |         return componentsLength; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @param normalized Set to true if integer components should be converted |      * @param normalized Set to true if integer components should be converted | ||||||
|      * from their maximal range into the range 0.0 - 1.0 when converted to |      * from their maximal range into the range 0.0 - 1.0 when converted to | ||||||
| @ -976,6 +987,10 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { | |||||||
|      * of the parameters. The buffer will be of the type specified by |      * of the parameters. The buffer will be of the type specified by | ||||||
|      * {@link Format format} and would be able to contain the given number |      * {@link Format format} and would be able to contain the given number | ||||||
|      * of elements with the given number of components in each element. |      * of elements with the given number of components in each element. | ||||||
|  |      * @param format The format of the buffer to create | ||||||
|  |      * @param components The number of components (aka dimensions) | ||||||
|  |      * @param numElements Capacity of the buffer in number of elements. | ||||||
|  |      * @return A buffer satisfying the given requirements. | ||||||
|      */ |      */ | ||||||
|     public static Buffer createBuffer(Format format, int components, int numElements){ |     public static Buffer createBuffer(Format format, int components, int numElements){ | ||||||
|         if (components < 1 || components > 4) |         if (components < 1 || components > 4) | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ public class VirtualIndexBuffer extends IndexBuffer { | |||||||
|             case Triangles: |             case Triangles: | ||||||
|                 numIndices = numVerts; |                 numIndices = numVerts; | ||||||
|                 return; |                 return; | ||||||
|             case Hybrid: |             case Reserved: | ||||||
|                 throw new UnsupportedOperationException(); |                 throw new UnsupportedOperationException(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -303,6 +303,16 @@ public final class Shader extends NativeObject { | |||||||
|         return attrib; |         return attrib; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public boolean isAttributeDefined(VertexBuffer.Type attribType) { | ||||||
|  |         int ordinal = attribType.ordinal(); | ||||||
|  |         Attribute attrib = attribs.get(ordinal); | ||||||
|  |         if (attrib == null){ | ||||||
|  |             return false; | ||||||
|  |         } else { | ||||||
|  |             return attrib.location != -1 && attrib.location != 2; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|     public ListMap<String, Uniform> getUniformMap(){ |     public ListMap<String, Uniform> getUniformMap(){ | ||||||
|         return uniforms; |         return uniforms; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -103,6 +103,10 @@ public class Uniform extends ShaderVariable { | |||||||
|         return value; |         return value; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     public FloatBuffer getMultiData() { | ||||||
|  |         return multiData; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public boolean isSetByCurrentMaterial() { |     public boolean isSetByCurrentMaterial() { | ||||||
|         return setByCurrentMaterial; |         return setByCurrentMaterial; | ||||||
|     } |     } | ||||||
| @ -111,21 +115,6 @@ public class Uniform extends ShaderVariable { | |||||||
|         setByCurrentMaterial = false; |         setByCurrentMaterial = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static void setVector4(Vector4f vec, Object value) { |  | ||||||
|         if (value instanceof ColorRGBA) { |  | ||||||
|             ColorRGBA color = (ColorRGBA) value; |  | ||||||
|             vec.set(color.r, color.g, color.b, color.a); |  | ||||||
|         } else if (value instanceof Quaternion) { |  | ||||||
|             Quaternion quat = (Quaternion) value; |  | ||||||
|             vec.set(quat.getX(), quat.getY(), quat.getZ(), quat.getW()); |  | ||||||
|         } else if (value instanceof Vector4f) { |  | ||||||
|             Vector4f vec4 = (Vector4f) value; |  | ||||||
|             vec.set(vec4); |  | ||||||
|         } else{ |  | ||||||
|             throw new IllegalArgumentException(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     public void clearValue(){ |     public void clearValue(){ | ||||||
|         updateNeeded = true; |         updateNeeded = true; | ||||||
| 
 | 
 | ||||||
| @ -189,20 +178,36 @@ public class Uniform extends ShaderVariable { | |||||||
| 
 | 
 | ||||||
|         switch (type){ |         switch (type){ | ||||||
|             case Matrix3: |             case Matrix3: | ||||||
|  |                 if (value.equals(this.value)) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|                 Matrix3f m3 = (Matrix3f) value; |                 Matrix3f m3 = (Matrix3f) value; | ||||||
|                 if (multiData == null) { |                 if (multiData == null) { | ||||||
|                     multiData = BufferUtils.createFloatBuffer(9); |                     multiData = BufferUtils.createFloatBuffer(9); | ||||||
|                 } |                 } | ||||||
|                 m3.fillFloatBuffer(multiData, true); |                 m3.fillFloatBuffer(multiData, true); | ||||||
|                 multiData.clear(); |                 multiData.clear(); | ||||||
|  |                 if (this.value == null) { | ||||||
|  |                     this.value = new Matrix3f(m3); | ||||||
|  |                 } else { | ||||||
|  |                     ((Matrix3f)this.value).set(m3); | ||||||
|  |                 } | ||||||
|                 break; |                 break; | ||||||
|             case Matrix4: |             case Matrix4: | ||||||
|  |                 if (value.equals(this.value)) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|                 Matrix4f m4 = (Matrix4f) value; |                 Matrix4f m4 = (Matrix4f) value; | ||||||
|                 if (multiData == null) { |                 if (multiData == null) { | ||||||
|                     multiData = BufferUtils.createFloatBuffer(16); |                     multiData = BufferUtils.createFloatBuffer(16); | ||||||
|                 } |                 } | ||||||
|                 m4.fillFloatBuffer(multiData, true); |                 m4.fillFloatBuffer(multiData, true); | ||||||
|                 multiData.clear(); |                 multiData.clear(); | ||||||
|  |                 if (this.value == null) { | ||||||
|  |                     this.value = new Matrix4f(m4); | ||||||
|  |                 } else { | ||||||
|  |                     ((Matrix4f)this.value).copy(m4); | ||||||
|  |                 } | ||||||
|                 break; |                 break; | ||||||
|             case IntArray: |             case IntArray: | ||||||
|                 int[] ia = (int[]) value; |                 int[] ia = (int[]) value; | ||||||
| @ -283,11 +288,32 @@ public class Uniform extends ShaderVariable { | |||||||
|                 } |                 } | ||||||
|                 multiData.clear(); |                 multiData.clear(); | ||||||
|                 break; |                 break; | ||||||
|  |             case Vector4: | ||||||
|  |                 if (value.equals(this.value)) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 if (value instanceof ColorRGBA) { | ||||||
|  |                     if (this.value == null) { | ||||||
|  |                         this.value = new ColorRGBA(); | ||||||
|  |                     } | ||||||
|  |                     ((ColorRGBA) this.value).set((ColorRGBA) value); | ||||||
|  |                 } else if (value instanceof Vector4f) { | ||||||
|  |                     if (this.value == null) { | ||||||
|  |                         this.value = new Vector4f(); | ||||||
|  |                     } | ||||||
|  |                     ((Vector4f) this.value).set((Vector4f) value); | ||||||
|  |                 } else { | ||||||
|  |                     if (this.value == null) { | ||||||
|  |                         this.value = new Quaternion(); | ||||||
|  |                     } | ||||||
|  |                     ((Quaternion) this.value).set((Quaternion) value); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|                 // Only use check if equals optimization for primitive values |                 // Only use check if equals optimization for primitive values | ||||||
|             case Int: |             case Int: | ||||||
|             case Float: |             case Float: | ||||||
|             case Boolean: |             case Boolean: | ||||||
|                 if (this.value != null && this.value.equals(value)) { |                 if (value.equals(this.value)) { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|                 this.value = value; |                 this.value = value; | ||||||
| @ -297,39 +323,38 @@ public class Uniform extends ShaderVariable { | |||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (multiData != null) { | //        if (multiData != null) { | ||||||
|             this.value = multiData; | //            this.value = multiData; | ||||||
|         } | //        } | ||||||
| 
 | 
 | ||||||
|         varType = type; |         varType = type; | ||||||
|         updateNeeded = true; |         updateNeeded = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void setVector4Length(int length){ |     public void setVector4Length(int length){ | ||||||
|         if (location == -1) |         if (location == -1) { | ||||||
|             return; |             return; | ||||||
| 
 |  | ||||||
|         FloatBuffer fb = (FloatBuffer) value; |  | ||||||
|         if (fb == null || fb.capacity() < length * 4) { |  | ||||||
|             value = BufferUtils.createFloatBuffer(length * 4); |  | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         multiData = BufferUtils.ensureLargeEnough(multiData, length * 4); | ||||||
|  |         value = multiData; | ||||||
|         varType = VarType.Vector4Array; |         varType = VarType.Vector4Array; | ||||||
|         updateNeeded = true; |         updateNeeded = true; | ||||||
|         setByCurrentMaterial = true; |         setByCurrentMaterial = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void setVector4InArray(float x, float y, float z, float w, int index){ |     public void setVector4InArray(float x, float y, float z, float w, int index){ | ||||||
|         if (location == -1) |         if (location == -1) { | ||||||
|             return; |             return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (varType != null && varType != VarType.Vector4Array) |         if (varType != null && varType != VarType.Vector4Array) { | ||||||
|             throw new IllegalArgumentException("Expected a " + varType.name() + " value!"); |             throw new IllegalArgumentException("Expected a " + varType.name() + " value!"); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         FloatBuffer fb = (FloatBuffer) value; |         multiData.position(index * 4); | ||||||
|         fb.position(index * 4); |         multiData.put(x).put(y).put(z).put(w); | ||||||
|         fb.put(x).put(y).put(z).put(w); |         multiData.rewind(); | ||||||
|         fb.rewind(); |  | ||||||
|         updateNeeded = true; |         updateNeeded = true; | ||||||
|         setByCurrentMaterial = true; |         setByCurrentMaterial = true; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -164,4 +164,7 @@ public class NullRenderer implements Renderer { | |||||||
|     public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {         |     public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {         | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public void deleteMesh(Mesh mesh) { | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -52,7 +52,8 @@ public abstract class NativeObject implements Cloneable { | |||||||
|                                OBJTYPE_SHADERSOURCE = 5, |                                OBJTYPE_SHADERSOURCE = 5, | ||||||
|                                OBJTYPE_AUDIOBUFFER  = 6, |                                OBJTYPE_AUDIOBUFFER  = 6, | ||||||
|                                OBJTYPE_AUDIOSTREAM  = 7, |                                OBJTYPE_AUDIOSTREAM  = 7, | ||||||
|                                OBJTYPE_FILTER       = 8; |                                OBJTYPE_FILTER       = 8, | ||||||
|  |                                OBJTYPE_MESH         = 9; | ||||||
|      |      | ||||||
|     /** |     /** | ||||||
|      * The object manager to which this NativeObject is registered to. |      * The object manager to which this NativeObject is registered to. | ||||||
|  | |||||||
| @ -0,0 +1,141 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2009-2015 jMonkeyEngine | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are | ||||||
|  |  * met: | ||||||
|  |  * | ||||||
|  |  * * Redistributions of source code must retain the above copyright | ||||||
|  |  *   notice, this list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * * Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *   notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *   documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors | ||||||
|  |  *   may be used to endorse or promote products derived from this software | ||||||
|  |  *   without specific prior written permission. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||||
|  |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | ||||||
|  |  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||||
|  |  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||||||
|  |  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||||||
|  |  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||||||
|  |  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||||||
|  |  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||||||
|  |  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||||
|  |  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | package jme3test.stress; | ||||||
|  | 
 | ||||||
|  | import com.jme3.app.SimpleApplication; | ||||||
|  | import com.jme3.light.DirectionalLight; | ||||||
|  | import com.jme3.material.Material; | ||||||
|  | import com.jme3.math.ColorRGBA; | ||||||
|  | import com.jme3.math.FastMath; | ||||||
|  | import com.jme3.math.Quaternion; | ||||||
|  | import com.jme3.math.Vector3f; | ||||||
|  | import com.jme3.scene.Geometry; | ||||||
|  | import com.jme3.scene.shape.Box; | ||||||
|  | import com.jme3.system.AppSettings; | ||||||
|  | 
 | ||||||
|  | // Let's see if we can render 2500 batches in 60 fps. | ||||||
|  | // We'll use 50 materials with various combinations of textures and colors | ||||||
|  | // to make things wild. | ||||||
|  | public class TestUniqueGeometries extends SimpleApplication { | ||||||
|  | 
 | ||||||
|  |     private Material[] randomMaterials = new Material[50]; | ||||||
|  |      | ||||||
|  |     private String[] textureList = new String[] { | ||||||
|  |         "Blender/2.4x/textures/Concrete_Wall.PNG",  | ||||||
|  |         "Blender/2.4x/textures/Grass_256.png",  | ||||||
|  |         "Blender/2.4x/textures/SandDesert_StartTower.png",  | ||||||
|  |         "Blender/2.4x/textures/Tar_Cracked.png",  | ||||||
|  |         "Blender/2.4x/textures/WarningStrip.png",  | ||||||
|  |         "Blender/2.4x/WoodCrate_lighter.png", | ||||||
|  |         "Interface/Logo/Monkey.jpg",  | ||||||
|  |         "Interface/Logo/Monkey.png",  | ||||||
|  |         "Models/Boat/boat.png",  | ||||||
|  |         "Models/Ninja/Ninja.jpg", | ||||||
|  |         "Models/Tree/BarkColor.jpg",  | ||||||
|  |         "Textures/Terrain/BrickWall/BrickWall.jpg",  | ||||||
|  |         "Textures/Terrain/Pond/Pond.jpg",  | ||||||
|  |         "Textures/Terrain/Pond/Pond_normal.png",  | ||||||
|  |         "Textures/Terrain/Rock/Rock.PNG", | ||||||
|  |         "Textures/Terrain/Rock/Rock_normal.png", | ||||||
|  |         "Textures/Terrain/Rock2/rock.jpg", | ||||||
|  |         "Textures/Terrain/Rocky/RockyNormals.jpg", | ||||||
|  |         "Textures/Terrain/Rocky/RockyTexture.jpg", | ||||||
|  |         "Textures/Terrain/splat/alpha1.png",  | ||||||
|  |         "Textures/Terrain/splat/alpha2.png",  | ||||||
|  |         "Textures/Terrain/splat/alphamap.png",  | ||||||
|  |         "Textures/Terrain/splat/alphamap2.png",  | ||||||
|  |         "Textures/Terrain/splat/dirt.jpg",  | ||||||
|  |         "Textures/Terrain/splat/dirt_normal.png",  | ||||||
|  |         "Textures/Terrain/splat/fortress512.png",  | ||||||
|  |         "Textures/Terrain/splat/grass.jpg",  | ||||||
|  |         "Textures/Terrain/splat/grass_normal.jpg", | ||||||
|  |         "Textures/Terrain/splat/mountains128.png", | ||||||
|  |         "Textures/Terrain/splat/road.jpg", | ||||||
|  |         "Textures/Terrain/splat/road_normal.png", | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |         TestUniqueGeometries app = new TestUniqueGeometries(); | ||||||
|  |         AppSettings settings = new AppSettings(true); | ||||||
|  |         settings.putBoolean("GraphicsTrace", false); | ||||||
|  |         settings.putBoolean("GraphicsTiming", true); | ||||||
|  |         app.setSettings(settings); | ||||||
|  |         app.start(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     private void loadRandomMaterials() { | ||||||
|  |         for (int i = 0; i < randomMaterials.length; i++) { | ||||||
|  |             Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); | ||||||
|  |             mat.setBoolean("VertexLighting", true); | ||||||
|  |             mat.setBoolean("UseMaterialColors", true); | ||||||
|  |             mat.setColor("Ambient", ColorRGBA.Black); | ||||||
|  |             mat.setColor("Diffuse", ColorRGBA.White); | ||||||
|  |             mat.setColor("Specular", ColorRGBA.White); | ||||||
|  |             mat.setFloat("Shininess", 32); | ||||||
|  |             mat.setTexture("DiffuseMap", assetManager.loadTexture(textureList[i % textureList.length])); | ||||||
|  |             randomMaterials[i] = mat; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     @Override | ||||||
|  |     public void simpleInitApp() { | ||||||
|  |         flyCam.setDragToRotate(true); | ||||||
|  |          | ||||||
|  |         cam.setLocation(new Vector3f(22.717342f, 18.366547f, 22.043106f)); | ||||||
|  |         cam.setRotation(new Quaternion(-0.11630201f, 0.8794429f, -0.27703872f, -0.36919326f)); | ||||||
|  |          | ||||||
|  |         DirectionalLight dl = new DirectionalLight(); | ||||||
|  |         dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); | ||||||
|  |         rootNode.addLight(dl); | ||||||
|  |          | ||||||
|  |         flyCam.setMoveSpeed(5); | ||||||
|  |          | ||||||
|  |         loadRandomMaterials(); | ||||||
|  |          | ||||||
|  |         // Box box = new Box(1,1,1); | ||||||
|  |          | ||||||
|  |         for (int y = -25; y < 25; y++) { | ||||||
|  |             for (int x = -25; x < 25; x++) { | ||||||
|  |                 Material mat = randomMaterials[0]; // randomMaterials[FastMath.nextRandomInt(0, randomMaterials.length - 1)]; | ||||||
|  |          | ||||||
|  |                 Box box = new Box(1,1,1); | ||||||
|  |                 Geometry boxClone = new Geometry("box", box); | ||||||
|  |                 boxClone.setMaterial(mat); | ||||||
|  |                  | ||||||
|  |                 boxClone.setLocalTranslation(x * .5f, 0, y * .5f); | ||||||
|  |                 boxClone.setLocalScale(.15f); | ||||||
|  |                 boxClone.setMaterial(mat); | ||||||
|  |                 rootNode.attachChild(boxClone); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,529 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright (c) 2009-2012 jMonkeyEngine |  | ||||||
|  * All rights reserved. |  | ||||||
|  * |  | ||||||
|  * Redistribution and use in source and binary forms, with or without |  | ||||||
|  * modification, are permitted provided that the following conditions are |  | ||||||
|  * met: |  | ||||||
|  * |  | ||||||
|  * * Redistributions of source code must retain the above copyright |  | ||||||
|  *   notice, this list of conditions and the following disclaimer. |  | ||||||
|  * |  | ||||||
|  * * Redistributions in binary form must reproduce the above copyright |  | ||||||
|  *   notice, this list of conditions and the following disclaimer in the |  | ||||||
|  *   documentation and/or other materials provided with the distribution. |  | ||||||
|  * |  | ||||||
|  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors |  | ||||||
|  *   may be used to endorse or promote products derived from this software |  | ||||||
|  *   without specific prior written permission. |  | ||||||
|  * |  | ||||||
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |  | ||||||
|  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |  | ||||||
|  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |  | ||||||
|  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |  | ||||||
|  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |  | ||||||
|  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |  | ||||||
|  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |  | ||||||
|  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |  | ||||||
|  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |  | ||||||
|  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |  | ||||||
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.jme3.renderer.jogl; |  | ||||||
| 
 |  | ||||||
| import com.jme3.renderer.RendererException; |  | ||||||
| import com.jme3.texture.Image; |  | ||||||
| import com.jme3.texture.Image.Format; |  | ||||||
| import com.jme3.texture.image.ColorSpace; |  | ||||||
| 
 |  | ||||||
| import java.nio.ByteBuffer; |  | ||||||
| import java.util.logging.Level; |  | ||||||
| import java.util.logging.Logger; |  | ||||||
| 
 |  | ||||||
| import com.jogamp.opengl.GL; |  | ||||||
| import com.jogamp.opengl.GL2; |  | ||||||
| import com.jogamp.opengl.GL2ES2; |  | ||||||
| import com.jogamp.opengl.GL2ES3; |  | ||||||
| import com.jogamp.opengl.GL2GL3; |  | ||||||
| import com.jogamp.opengl.GLContext; |  | ||||||
| 
 |  | ||||||
| public class TextureUtil { |  | ||||||
|      |  | ||||||
|     private static boolean abgrToRgbaConversionEnabled = false; |  | ||||||
| 
 |  | ||||||
|     public static int convertTextureFormat(Format fmt) { |  | ||||||
|         switch (fmt) { |  | ||||||
|             case Alpha8: |  | ||||||
|                 return GL.GL_ALPHA; |  | ||||||
|             case Luminance8Alpha8: |  | ||||||
|                 return GL.GL_LUMINANCE_ALPHA; |  | ||||||
|             case Luminance8: |  | ||||||
|                 return GL.GL_LUMINANCE; |  | ||||||
|             case BGR8: |  | ||||||
|             case RGB8: |  | ||||||
|             case RGB565: |  | ||||||
|                 return GL.GL_RGB; |  | ||||||
|             case RGB5A1: |  | ||||||
|             case RGBA8: |  | ||||||
|                 return GL.GL_RGBA; |  | ||||||
|             case Depth: |  | ||||||
|                 return GL2ES2.GL_DEPTH_COMPONENT; |  | ||||||
|             default: |  | ||||||
|                 throw new UnsupportedOperationException("Unrecognized format: " + fmt); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     public static class GLImageFormat { |  | ||||||
|          |  | ||||||
|         int internalFormat; |  | ||||||
|         int format; |  | ||||||
|         int dataType; |  | ||||||
|         boolean compressed; |  | ||||||
| 
 |  | ||||||
|         public GLImageFormat(int internalFormat, int format, int dataType, boolean compressed) { |  | ||||||
|             this.internalFormat = internalFormat; |  | ||||||
|             this.format = format; |  | ||||||
|             this.dataType = dataType; |  | ||||||
|             this.compressed = compressed; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     private static final GLImageFormat[] formatToGL = new GLImageFormat[Format.values().length]; |  | ||||||
|      |  | ||||||
|     private static void setFormat(Format format, int glInternalFormat, int glFormat, int glDataType, boolean glCompressed){ |  | ||||||
|         formatToGL[format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, glCompressed); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     static { |  | ||||||
|         // Alpha formats |  | ||||||
|         setFormat(Format.Alpha8,  GL2.GL_ALPHA8,  GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|          |  | ||||||
|         // Luminance formats |  | ||||||
|         setFormat(Format.Luminance8,   GL2.GL_LUMINANCE8,  GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|         setFormat(Format.Luminance16F, GL2.GL_LUMINANCE16F, GL.GL_LUMINANCE, GL.GL_HALF_FLOAT, false); |  | ||||||
|         setFormat(Format.Luminance32F, GL2.GL_LUMINANCE32F, GL.GL_LUMINANCE, GL.GL_FLOAT, false); |  | ||||||
|          |  | ||||||
|         // Luminance alpha formats |  | ||||||
|         setFormat(Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8,  GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|         setFormat(Format.Luminance16FAlpha16F, GL2.GL_LUMINANCE_ALPHA16F, GL.GL_LUMINANCE_ALPHA, GL.GL_HALF_FLOAT, false); |  | ||||||
|          |  | ||||||
|         // Depth formats |  | ||||||
|         setFormat(Format.Depth,    GL2ES2.GL_DEPTH_COMPONENT,    GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|         setFormat(Format.Depth16,  GL.GL_DEPTH_COMPONENT16,  GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_SHORT, false); |  | ||||||
|         setFormat(Format.Depth24,  GL.GL_DEPTH_COMPONENT24,  GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT, false); |  | ||||||
|         setFormat(Format.Depth32,  GL.GL_DEPTH_COMPONENT32,  GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT, false); |  | ||||||
|         setFormat(Format.Depth32F, GL2GL3.GL_DEPTH_COMPONENT32F, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_FLOAT,         false); |  | ||||||
|          |  | ||||||
|         // Depth stencil formats |  | ||||||
|         setFormat(Format.Depth24Stencil8, GL.GL_DEPTH24_STENCIL8, GL.GL_DEPTH_STENCIL, GL.GL_UNSIGNED_INT_24_8, false); |  | ||||||
|          |  | ||||||
|         // RGB formats |  | ||||||
|         setFormat(Format.BGR8,       GL.GL_RGB8,  GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|         setFormat(Format.ARGB8,       GL.GL_RGBA8,  GL.GL_BGRA, GL2.GL_UNSIGNED_INT_8_8_8_8_REV, false); |  | ||||||
|         setFormat(Format.BGRA8,       GL.GL_RGBA8,  GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|         setFormat(Format.RGB8,       GL.GL_RGB8,  GL.GL_RGB,        GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|         setFormat(Format.RGB16F,     GL2ES2.GL_RGB16F, GL.GL_RGB, GL.GL_HALF_FLOAT, false); |  | ||||||
|         setFormat(Format.RGB32F,     GL.GL_RGB32F, GL.GL_RGB, GL.GL_FLOAT, false); |  | ||||||
|          |  | ||||||
|         // Special RGB formats |  | ||||||
|         setFormat(Format.RGB111110F, GL2ES3.GL_R11F_G11F_B10F,    GL.GL_RGB, GL.GL_UNSIGNED_INT_10F_11F_11F_REV, false); |  | ||||||
|         setFormat(Format.RGB9E5,     GL2GL3.GL_RGB9_E5, GL.GL_RGB, GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV, false); |  | ||||||
|         setFormat(Format.RGB16F_to_RGB111110F, GL2ES3.GL_R11F_G11F_B10F,    GL.GL_RGB, GL.GL_HALF_FLOAT, false); |  | ||||||
|         setFormat(Format.RGB16F_to_RGB9E5, GL2.GL_RGB9_E5, GL.GL_RGB, GL.GL_HALF_FLOAT, false); |  | ||||||
|          |  | ||||||
|         // RGBA formats |  | ||||||
|         setFormat(Format.ABGR8,   GL.GL_RGBA8,       GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|         setFormat(Format.RGB5A1,    GL.GL_RGB5_A1,   GL.GL_RGBA,        GL.GL_UNSIGNED_SHORT_5_5_5_1, false); |  | ||||||
|         setFormat(Format.RGBA8,   GL.GL_RGBA8,       GL.GL_RGBA,        GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|         setFormat(Format.RGBA16F, GL2ES2.GL_RGBA16F, GL.GL_RGBA, GL.GL_HALF_FLOAT, false); |  | ||||||
|         setFormat(Format.RGBA32F, GL.GL_RGBA32F, GL.GL_RGBA, GL.GL_FLOAT, false); |  | ||||||
|          |  | ||||||
|         // DXT formats |  | ||||||
|         setFormat(Format.DXT1,  GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL.GL_RGB,   GL.GL_UNSIGNED_BYTE, true); |  | ||||||
|         setFormat(Format.DXT1A, GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |  | ||||||
|         setFormat(Format.DXT3,  GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |  | ||||||
|         setFormat(Format.DXT5,  GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|      //sRGB formats         |  | ||||||
|     private static final GLImageFormat sRGB_RGB8 = new GLImageFormat(GL2.GL_SRGB8,GL.GL_RGB, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|     private static final GLImageFormat sRGB_RGBA8 = new GLImageFormat(GL.GL_SRGB8_ALPHA8,GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|     private static final GLImageFormat sRGB_Luminance8 = new GLImageFormat(GL2.GL_SLUMINANCE8,GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|     private static final GLImageFormat sRGB_LuminanceAlpha8 = new GLImageFormat(GL2.GL_SLUMINANCE8_ALPHA8,GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|     private static final GLImageFormat sRGB_BGR8 = new GLImageFormat(GL2.GL_SRGB8, GL2GL3.GL_BGR,  GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|     private static final GLImageFormat sRGB_ABGR8 = new GLImageFormat(GL2.GL_SRGB8_ALPHA8, GL2.GL_ABGR_EXT,  GL.GL_UNSIGNED_BYTE, false); |  | ||||||
| 
 |  | ||||||
|     //FIXME cannot find GL_COMPRESSED_RGB_S3TC_DXT1,GL_COMPRESSED_RGBA_S3TC_DXT1,GL_COMPRESSED_RGB_S3TC_DXT3,GL_COMPRESSED_RGB_S3TC_DXT5 in JOGL used constants |  | ||||||
|     //GL_COMPRESSED_RGB_S3TC_DXT1 = 33776; |  | ||||||
|     //GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 33777; |  | ||||||
|     //GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 33778; |  | ||||||
|     //GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 33779; |  | ||||||
|     private static final GLImageFormat sRGB_DXT1 = new GLImageFormat(33776, GL.GL_RGB,   GL.GL_UNSIGNED_BYTE, true); |  | ||||||
|     private static final GLImageFormat sRGB_DXT1A = new GLImageFormat( 33777, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |  | ||||||
|     private static final GLImageFormat sRGB_DXT3 = new GLImageFormat(33778, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |  | ||||||
|     private static final GLImageFormat sRGB_DXT5 = new GLImageFormat( 33779, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true); |  | ||||||
| 
 |  | ||||||
|      |  | ||||||
|     public static GLImageFormat getImageFormat(Format fmt, boolean isSrgb){ |  | ||||||
|         GL gl = GLContext.getCurrentGL(); |  | ||||||
|         switch (fmt){ |  | ||||||
|             case ABGR8: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_EXT_abgr") && !abgrToRgbaConversionEnabled) { |  | ||||||
|                     setFormat(Format.ABGR8,   GL.GL_RGBA,        GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false); |  | ||||||
|                     abgrToRgbaConversionEnabled = true; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case BGR8: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_VERSION_1_2") && !gl.isExtensionAvailable("EXT_bgra")){ |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case DXT1: |  | ||||||
|             case DXT1A: |  | ||||||
|             case DXT3: |  | ||||||
|             case DXT5: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_EXT_texture_compression_s3tc")) { |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case Depth: |  | ||||||
|             case Depth16: |  | ||||||
|             case Depth24: |  | ||||||
|             case Depth32: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_VERSION_1_4") && !gl.isExtensionAvailable("ARB_depth_texture")){ |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case Depth24Stencil8: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_VERSION_3_0")){ |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case Luminance16F: |  | ||||||
|             case Luminance16FAlpha16F: |  | ||||||
|             case Luminance32F: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_ARB_texture_float")){ |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case RGB16F: |  | ||||||
|             case RGB32F: |  | ||||||
|             case RGBA16F: |  | ||||||
|             case RGBA32F: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_ARB_texture_float")){ |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case Depth32F: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_NV_depth_buffer_float")){ |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case RGB9E5: |  | ||||||
|             case RGB16F_to_RGB9E5: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_EXT_texture_shared_exponent")){ |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case RGB111110F: |  | ||||||
|             case RGB16F_to_RGB111110F: |  | ||||||
|                 if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_EXT_packed_float")){ |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         if(isSrgb){ |  | ||||||
|             return getSrgbFormat(fmt); |  | ||||||
|         } |  | ||||||
|         return formatToGL[fmt.ordinal()]; |  | ||||||
|     } |  | ||||||
|    |  | ||||||
|     public static GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) { |  | ||||||
|         GLImageFormat glFmt = getImageFormat(fmt, isSrgb); |  | ||||||
|         if (glFmt == null) { |  | ||||||
|             throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware."); |  | ||||||
|         } |  | ||||||
|         return glFmt; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     private static GLImageFormat getSrgbFormat(Format fmt){ |  | ||||||
|         switch (fmt){ |  | ||||||
|             case RGB8 : return sRGB_RGB8; |  | ||||||
|             case RGBA8 : return sRGB_RGBA8; |  | ||||||
|             case BGR8 : return sRGB_BGR8;     |  | ||||||
|             case ABGR8 : return sRGB_ABGR8;     |  | ||||||
|             case Luminance8 : return sRGB_Luminance8; |  | ||||||
|             case Luminance8Alpha8 : return sRGB_LuminanceAlpha8; |  | ||||||
|             case DXT1 : return sRGB_DXT1; |  | ||||||
|             case DXT1A : return sRGB_DXT1A; |  | ||||||
|             case DXT3 : return sRGB_DXT3; |  | ||||||
|             case DXT5 : return sRGB_DXT5; |  | ||||||
|             default : Logger.getLogger(TextureUtil.class.getName()).log(Level.WARNING, "Format {0} has no sRGB equivalent, using linear format.", fmt.toString()); |  | ||||||
|                 return formatToGL[fmt.ordinal()]; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static void uploadTexture(Image image, |  | ||||||
|                                      int target, |  | ||||||
|                                      int index, |  | ||||||
|                                      int border, |  | ||||||
|                                      boolean linearizeSrgb){ |  | ||||||
|         GL gl = GLContext.getCurrentGL(); |  | ||||||
|         Image.Format fmt = image.getFormat(); |  | ||||||
|         GLImageFormat glFmt = getImageFormatWithError(fmt, image.getColorSpace() == ColorSpace.sRGB  && linearizeSrgb); |  | ||||||
| 
 |  | ||||||
|         ByteBuffer data; |  | ||||||
|         if (index >= 0 && image.getData() != null && image.getData().size() > 0){ |  | ||||||
|             data = image.getData(index); |  | ||||||
|         }else{ |  | ||||||
|             data = null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         int width = image.getWidth(); |  | ||||||
|         int height = image.getHeight(); |  | ||||||
|         int depth = image.getDepth(); |  | ||||||
| 
 |  | ||||||
|         if (data != null) { |  | ||||||
|             if (abgrToRgbaConversionEnabled) { |  | ||||||
|                 convertABGRtoRGBA(data); |  | ||||||
|             } |  | ||||||
|             gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         int[] mipSizes = image.getMipMapSizes(); |  | ||||||
|         int pos = 0; |  | ||||||
|         // TODO: Remove unneccessary allocation |  | ||||||
|         if (mipSizes == null){ |  | ||||||
|             if (data != null) |  | ||||||
|                 mipSizes = new int[]{ data.capacity() }; |  | ||||||
|             else |  | ||||||
|                 mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 }; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         boolean subtex = false; |  | ||||||
|         int samples = image.getMultiSamples(); |  | ||||||
| 
 |  | ||||||
|         for (int i = 0; i < mipSizes.length; i++){ |  | ||||||
|             int mipWidth =  Math.max(1, width  >> i); |  | ||||||
|             int mipHeight = Math.max(1, height >> i); |  | ||||||
|             int mipDepth =  Math.max(1, depth  >> i); |  | ||||||
| 
 |  | ||||||
|             if (data != null){ |  | ||||||
|                 data.position(pos); |  | ||||||
|                 data.limit(pos + mipSizes[i]); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             if (glFmt.compressed && data != null){ |  | ||||||
|                 if (target == GL2ES2.GL_TEXTURE_3D){ |  | ||||||
|                     gl.getGL2ES2().glCompressedTexImage3D(target, |  | ||||||
|                                                 i, |  | ||||||
|                                                 glFmt.internalFormat, |  | ||||||
|                                                 mipWidth, |  | ||||||
|                                                 mipHeight, |  | ||||||
|                                                 mipDepth, |  | ||||||
|                                                 data.remaining(), |  | ||||||
|                                                 border, |  | ||||||
|                                                 data); |  | ||||||
|                 }else{ |  | ||||||
|                     //all other targets use 2D: array, cubemap, 2d |  | ||||||
|                     gl.glCompressedTexImage2D(target, |  | ||||||
|                                                 i, |  | ||||||
|                                                 glFmt.internalFormat, |  | ||||||
|                                                 mipWidth, |  | ||||||
|                                                 mipHeight, |  | ||||||
|                                                 data.remaining(), |  | ||||||
|                                                 border, |  | ||||||
|                                                 data); |  | ||||||
|                 } |  | ||||||
|             }else{ |  | ||||||
|                 if (target == GL2ES2.GL_TEXTURE_3D){ |  | ||||||
|                     gl.getGL2ES2().glTexImage3D(target, |  | ||||||
|                                       i, |  | ||||||
|                                       glFmt.internalFormat, |  | ||||||
|                                       mipWidth, |  | ||||||
|                                       mipHeight, |  | ||||||
|                                       mipDepth, |  | ||||||
|                                       border, |  | ||||||
|                                       glFmt.format, |  | ||||||
|                                       glFmt.dataType, |  | ||||||
|                                       data); |  | ||||||
|                 }else if (target == GL2ES3.GL_TEXTURE_2D_ARRAY){ |  | ||||||
|                     // prepare data for 2D array |  | ||||||
|                     // or upload slice |  | ||||||
|                     if (index == -1){ |  | ||||||
|                         gl.getGL2ES2().glTexImage3D(target, |  | ||||||
|                                           0, |  | ||||||
|                                           glFmt.internalFormat, |  | ||||||
|                                           mipWidth, |  | ||||||
|                                           mipHeight, |  | ||||||
|                                           image.getData().size(), //# of slices |  | ||||||
|                                           border, |  | ||||||
|                                           glFmt.format, |  | ||||||
|                                           glFmt.dataType, |  | ||||||
|                                           data); |  | ||||||
|                     }else{ |  | ||||||
|                         gl.getGL2ES2().glTexSubImage3D(target, |  | ||||||
|                                              i, // level |  | ||||||
|                                              0, // xoffset |  | ||||||
|                                              0, // yoffset |  | ||||||
|                                              index, // zoffset |  | ||||||
|                                              width, // width |  | ||||||
|                                              height, // height |  | ||||||
|                                              1, // depth |  | ||||||
|                                              glFmt.format, |  | ||||||
|                                              glFmt.dataType, |  | ||||||
|                                              data); |  | ||||||
|                     } |  | ||||||
|                 }else{ |  | ||||||
|                     if (subtex){ |  | ||||||
|                         if (samples > 1){ |  | ||||||
|                             throw new IllegalStateException("Cannot update multisample textures"); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         gl.glTexSubImage2D(target, |  | ||||||
|                                              i, |  | ||||||
|                                              0, 0, |  | ||||||
|                                              mipWidth, mipHeight, |  | ||||||
|                                              glFmt.format, |  | ||||||
|                                              glFmt.dataType, |  | ||||||
|                                              data); |  | ||||||
|                     }else{ |  | ||||||
|                         if (samples > 1){ |  | ||||||
|                             if (gl.isGL2GL3()) { |  | ||||||
|                                 gl.getGL3().glTexImage2DMultisample(target, |  | ||||||
|                                         samples, |  | ||||||
|                                         glFmt.internalFormat, |  | ||||||
|                                         mipWidth, |  | ||||||
|                                         mipHeight, |  | ||||||
|                                         true); |  | ||||||
|                             } |  | ||||||
|                         } else { |  | ||||||
|                             gl.glTexImage2D(target, |  | ||||||
|                                               i, |  | ||||||
|                                               glFmt.internalFormat, |  | ||||||
|                                               mipWidth, |  | ||||||
|                                               mipHeight, |  | ||||||
|                                               border, |  | ||||||
|                                               glFmt.format, |  | ||||||
|                                               glFmt.dataType, |  | ||||||
|                                               data); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             pos += mipSizes[i]; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     private static void convertABGRtoRGBA(ByteBuffer buffer) { |  | ||||||
| 
 |  | ||||||
|         for (int i = 0; i < buffer.capacity(); i++) { |  | ||||||
| 
 |  | ||||||
|             int a = buffer.get(i++); |  | ||||||
|             int b = buffer.get(i++); |  | ||||||
|             int g = buffer.get(i++); |  | ||||||
|             int r = buffer.get(i); |  | ||||||
| 
 |  | ||||||
|             buffer.put(i - 3, (byte) r); |  | ||||||
|             buffer.put(i - 2, (byte) g); |  | ||||||
|             buffer.put(i - 1, (byte) b); |  | ||||||
|             buffer.put(i, (byte) a); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Update the texture currently bound to target at with data from the given Image at position x and y. The parameter |  | ||||||
|      * index is used as the zoffset in case a 3d texture or texture 2d array is being updated. |  | ||||||
|      * |  | ||||||
|      * @param image Image with the source data (this data will be put into the texture) |  | ||||||
|      * @param target the target texture |  | ||||||
|      * @param index the mipmap level to update |  | ||||||
|      * @param x the x position where to put the image in the texture |  | ||||||
|      * @param y the y position where to put the image in the texture |  | ||||||
|      */ |  | ||||||
|     public static void uploadSubTexture( |  | ||||||
|         Image image, |  | ||||||
|         int target, |  | ||||||
|         int index, |  | ||||||
|         int x, |  | ||||||
|         int y, |  | ||||||
|         boolean linearizeSrgb) { |  | ||||||
|       GL gl = GLContext.getCurrentGL(); |  | ||||||
|       Image.Format fmt = image.getFormat(); |  | ||||||
|       GLImageFormat glFmt = getImageFormatWithError(fmt, image.getColorSpace() == ColorSpace.sRGB  && linearizeSrgb); |  | ||||||
| 
 |  | ||||||
|       ByteBuffer data = null; |  | ||||||
|       if (index >= 0 && image.getData() != null && image.getData().size() > 0) { |  | ||||||
|         data = image.getData(index); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       int width = image.getWidth(); |  | ||||||
|       int height = image.getHeight(); |  | ||||||
|       int depth = image.getDepth(); |  | ||||||
| 
 |  | ||||||
|       if (data != null) { |  | ||||||
|         gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       int[] mipSizes = image.getMipMapSizes(); |  | ||||||
|       int pos = 0; |  | ||||||
| 
 |  | ||||||
|       // TODO: Remove unneccessary allocation |  | ||||||
|       if (mipSizes == null){ |  | ||||||
|         if (data != null) { |  | ||||||
|           mipSizes = new int[]{ data.capacity() }; |  | ||||||
|         } else { |  | ||||||
|           mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 }; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       int samples = image.getMultiSamples(); |  | ||||||
| 
 |  | ||||||
|       for (int i = 0; i < mipSizes.length; i++){ |  | ||||||
|         int mipWidth =  Math.max(1, width  >> i); |  | ||||||
|         int mipHeight = Math.max(1, height >> i); |  | ||||||
|         int mipDepth =  Math.max(1, depth  >> i); |  | ||||||
| 
 |  | ||||||
|         if (data != null){ |  | ||||||
|           data.position(pos); |  | ||||||
|           data.limit(pos + mipSizes[i]); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // to remove the cumbersome if/then/else stuff below we'll update the pos right here and use continue after each |  | ||||||
|         // gl*Image call in an attempt to unclutter things a bit |  | ||||||
|         pos += mipSizes[i]; |  | ||||||
| 
 |  | ||||||
|         int glFmtInternal = glFmt.internalFormat; |  | ||||||
|         int glFmtFormat = glFmt.format; |  | ||||||
|         int glFmtDataType = glFmt.dataType; |  | ||||||
| 
 |  | ||||||
|         if (glFmt.compressed && data != null){ |  | ||||||
|           if (target == GL2ES2.GL_TEXTURE_3D){ |  | ||||||
|             gl.getGL2ES2().glCompressedTexSubImage3D(target, i, x, y, index, mipWidth, mipHeight, mipDepth, glFmtInternal, data.limit(), data); |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           // all other targets use 2D: array, cubemap, 2d |  | ||||||
|           gl.getGL2ES2().glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, glFmtInternal, data.limit(), data); |  | ||||||
|           continue; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (target == GL2ES2.GL_TEXTURE_3D){ |  | ||||||
|           gl.getGL2ES2().glTexSubImage3D(target, i, x, y, index, mipWidth, mipHeight, mipDepth, glFmtFormat, glFmtDataType, data); |  | ||||||
|           continue; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (samples > 1){ |  | ||||||
|           throw new IllegalStateException("Cannot update multisample textures"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         gl.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, glFmtFormat, glFmtDataType, data); |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -54,6 +54,7 @@ import com.jme3.system.AppSettings; | |||||||
| import com.jme3.system.JmeContext; | import com.jme3.system.JmeContext; | ||||||
| import com.jme3.system.NanoTimer; | import com.jme3.system.NanoTimer; | ||||||
| import com.jme3.system.NativeLibraryLoader; | import com.jme3.system.NativeLibraryLoader; | ||||||
|  | import com.jme3.system.NullRenderer; | ||||||
| import com.jme3.system.SystemListener; | import com.jme3.system.SystemListener; | ||||||
| import com.jme3.system.Timer; | import com.jme3.system.Timer; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user