Merge branch 'renderer-improvements' into experimental

experimental
Kirill Vainer 9 years ago
commit ff6b1be725
  1. 5
      jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
  2. 6
      jme3-core/src/main/java/com/jme3/renderer/Renderer.java
  3. 362
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  4. 144
      jme3-core/src/main/java/com/jme3/scene/Mesh.java
  5. 17
      jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java
  6. 2
      jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java
  7. 10
      jme3-core/src/main/java/com/jme3/shader/Shader.java
  8. 87
      jme3-core/src/main/java/com/jme3/shader/Uniform.java
  9. 3
      jme3-core/src/main/java/com/jme3/system/NullRenderer.java
  10. 3
      jme3-core/src/main/java/com/jme3/util/NativeObject.java
  11. 141
      jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java
  12. 529
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java
  13. 1
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java

@ -139,6 +139,11 @@ class BitmapTextPage extends Geometry {
int vertCount = pageQuads.size() * 4;
int triCount = pageQuads.size() * 2;
if (vertCount > m.getVertexCount() ||
triCount > m.getTriangleCount()) {
m.setUpdateNeeded();
}
VertexBuffer pb = m.getBuffer(Type.Position);
VertexBuffer tb = m.getBuffer(Type.TexCoord);
VertexBuffer ib = m.getBuffer(Type.Index);

@ -284,6 +284,12 @@ public interface Renderer {
*/
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.
* 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)) {
// Core Profile requires VAO to be bound.
gl3.glGenVertexArrays(intBuf16);
int vaoId = intBuf16.get(0);
gl3.glBindVertexArray(vaoId);
// gl3.glGenVertexArrays(intBuf16);
// int vaoId = intBuf16.get(0);
// gl3.glBindVertexArray(vaoId);
}
if (gl2 != null) {
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);
break;
case Matrix3:
fb = (FloatBuffer) uniform.getValue();
fb = uniform.getMultiData();
assert fb.remaining() == 9;
gl.glUniformMatrix3(loc, false, fb);
break;
case Matrix4:
fb = (FloatBuffer) uniform.getValue();
fb = uniform.getMultiData();
assert fb.remaining() == 16;
gl.glUniformMatrix4(loc, false, fb);
break;
@ -985,23 +985,23 @@ public final class GLRenderer implements Renderer {
gl.glUniform1(loc, ib);
break;
case FloatArray:
fb = (FloatBuffer) uniform.getValue();
fb = uniform.getMultiData();
gl.glUniform1(loc, fb);
break;
case Vector2Array:
fb = (FloatBuffer) uniform.getValue();
fb = uniform.getMultiData();
gl.glUniform2(loc, fb);
break;
case Vector3Array:
fb = (FloatBuffer) uniform.getValue();
fb = uniform.getMultiData();
gl.glUniform3(loc, fb);
break;
case Vector4Array:
fb = (FloatBuffer) uniform.getValue();
fb = uniform.getMultiData();
gl.glUniform4(loc, fb);
break;
case Matrix4Array:
fb = (FloatBuffer) uniform.getValue();
fb = uniform.getMultiData();
gl.glUniformMatrix4(loc, false, fb);
break;
case Int:
@ -2048,6 +2048,7 @@ public final class GLRenderer implements Renderer {
// bind texture
int target = convertTextureType(type, img.getMultiSamples(), -1);
bindTextureAndUnit(target, img, unit);
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
@ -2334,32 +2335,37 @@ public final class GLRenderer implements Renderer {
context.attribIndexList.copyNewToOld();
}
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");
}
if (context.boundShaderProgram <= 0) {
throw new IllegalStateException("Cannot render mesh without shader bound");
}
Attribute attrib = context.boundShader.getAttribute(vb.getBufferType());
private int updateAttributeLocation(Shader shader, VertexBuffer.Type attribType) {
Attribute attrib = shader.getAttribute(attribType);
int loc = attrib.getLocation();
if (loc == -1) {
return; // not defined
return -1; // not defined
}
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
// the internal name of the enum (Position).
if (loc < 0) {
attrib.setLocation(-1);
return; // not available in shader.
return -1; // not available in shader.
} else {
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 (!caps.contains(Caps.MeshInstancing)) {
@ -2383,11 +2389,11 @@ public final class GLRenderer implements Renderer {
VertexBuffer[] attribs = context.boundAttribs;
for (int i = 0; i < slotsRequired; i++) {
if (!context.attribIndexList.moveToNew(loc + i)) {
gl.glEnableVertexAttribArray(loc + i);
if (!context.attribIndexList.moveToNew(location + i)) {
gl.glEnableVertexAttribArray(location + i);
}
}
if (attribs[loc] != vb) {
if (attribs[location] != vb) {
// NOTE: Use id from interleaved buffer if specified
int bufId = idb != null ? idb.getId() : vb.getId();
assert bufId != -1;
@ -2400,7 +2406,7 @@ public final class GLRenderer implements Renderer {
}
if (slotsRequired == 1) {
gl.glVertexAttribPointer(loc,
gl.glVertexAttribPointer(location,
vb.getNumComponents(),
convertFormat(vb.getFormat()),
vb.isNormalized(),
@ -2416,7 +2422,7 @@ public final class GLRenderer implements Renderer {
// 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(loc + i,
gl.glVertexAttribPointer(location + i,
4,
convertFormat(vb.getFormat()),
vb.isNormalized(),
@ -2426,7 +2432,7 @@ public final class GLRenderer implements Renderer {
}
for (int i = 0; i < slotsRequired; i++) {
int slot = loc + i;
int slot = location + i;
if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) {
// non-instanced -> instanced
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) {
setVertexAttrib(vb, null);
}
@ -2488,44 +2580,8 @@ public final class GLRenderer implements Renderer {
}
int vertCount = mesh.getVertexCount();
if (mesh.getMode() == Mode.Hybrid) {
int[] modeStart = mesh.getModeStart();
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) {
boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
if (useInstancing) {
glext.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
indexBuf.getData().limit(),
convertFormat(indexBuf.getFormat()),
@ -2540,7 +2596,6 @@ public final class GLRenderer implements Renderer {
0);
}
}
}
/*********************************************************************\
|* Render Calls *|
@ -2568,27 +2623,16 @@ public final class GLRenderer implements Renderer {
}
}
public void updateVertexArray(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;
}
private void setupVertexBuffersLegacy(Mesh mesh, VertexBuffer[] instanceData) {
VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
if (interleavedData != null && interleavedData.isUpdateNeeded()) {
updateBufferData(interleavedData);
}
if (instanceData != null) {
setVertexAttrib(instanceData, null);
for (VertexBuffer vb : instanceData) {
setVertexAttrib(vb, null);
}
}
for (VertexBuffer vb : mesh.getBufferList().getArray()) {
@ -2608,73 +2652,124 @@ public final class GLRenderer implements Renderer {
}
}
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
private void setupVertexBuffers(Mesh mesh, VertexBuffer[] instanceData) {
VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
if (instanceData != null) {
for (VertexBuffer vb : instanceData) {
setVertexAttribVAO(vb, null);
}
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);
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 (indices != null) {
drawTriangleList(indices, mesh, count);
if (vb.getStride() == 0) {
// not interleaved
setVertexAttribVAO(vb, null);
} else {
drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
// interleaved
setVertexAttribVAO(vb, interleavedData);
}
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);
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;
}
if (instanceData != null) {
for (VertexBuffer vb : instanceData) {
setVertexAttrib(vb, null);
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;
}
}
for (VertexBuffer vb : mesh.getBufferList().getArray()) {
if (vb.getBufferType() == Type.InterleavedData
|| vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
|| vb.getBufferType() == Type.Index) {
continue;
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 (vb.getStride() == 0) {
// not interleaved
setVertexAttrib(vb);
if (indices != null) {
if (indices.isUpdateNeeded()) {
updateBufferData(indices);
}
drawTriangleList(indices, mesh, count);
context.boundElementArrayVBO = 0;
} else {
// interleaved
setVertexAttrib(vb, interleavedData);
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();
if (indices != null) {
@ -2682,6 +2777,7 @@ public final class GLRenderer implements Renderer {
} else {
drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
}
}
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)) {
gl4.glPatchParameter(mesh.getPatchVertexCount());
}
statistics.onMeshDrawn(mesh, lod, count);
// if (ctxCaps.GL_ARB_vertex_array_object){
// renderMeshVertexArray(mesh, lod, count);
// }else{
// Here while count is still passed in. Can be removed when/if
// the method is collapsed again. -pspeed
count = Math.max(mesh.getInstanceCount(), count);
// if (caps.contains(Caps.VertexBufferArray)) {
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) {

@ -42,6 +42,7 @@ import com.jme3.math.Matrix4f;
import com.jme3.math.Triangle;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Renderer;
import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
@ -49,6 +50,7 @@ import com.jme3.scene.mesh.*;
import com.jme3.util.BufferUtils;
import com.jme3.util.IntMap;
import com.jme3.util.IntMap.Entry;
import com.jme3.util.NativeObject;
import com.jme3.util.SafeArrayList;
import java.io.IOException;
import java.nio.*;
@ -71,7 +73,7 @@ import java.util.ArrayList;
*
* @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
@ -127,19 +129,14 @@ public class Mesh implements Savable, Cloneable {
*/
TriangleFan(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),
Reserved(false),
/**
* Used for Tesselation only. Requires to set the number of vertices
* for each patch (default is 3 for triangle tesselation)
*/
Patch(true);
private boolean listMode = false;
private Mode(boolean listMode){
@ -182,9 +179,6 @@ public class Mesh implements Savable, Cloneable {
private int patchVertexCount=3; //only used for tesselation
private int maxNumWeights = -1; // only if using skeletal animation
private int[] elementLengths;
private int[] modeStart;
private Mode mode = Mode.Triangles;
/**
@ -193,6 +187,10 @@ public class Mesh implements Savable, Cloneable {
public Mesh(){
}
protected Mesh(int id) {
super(id);
}
/**
* Create a shallow clone of this Mesh. The {@link VertexBuffer vertex
* buffers} are shared between this and the clone mesh, the rest
@ -202,23 +200,12 @@ public class Mesh implements Savable, Cloneable {
*/
@Override
public Mesh clone() {
try {
Mesh clone = (Mesh) super.clone();
clone.meshBound = meshBound.clone();
clone.collisionTree = collisionTree != null ? collisionTree : null;
clone.buffers = buffers.clone();
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;
} catch (CloneNotSupportedException ex) {
throw new AssertionError();
}
}
/**
@ -229,7 +216,6 @@ public class Mesh implements Savable, Cloneable {
* @return a deep clone of this mesh.
*/
public Mesh deepClone(){
try{
Mesh clone = (Mesh) super.clone();
clone.meshBound = meshBound != null ? meshBound.clone() : null;
@ -245,7 +231,6 @@ public class Mesh implements Savable, Cloneable {
clone.buffersList.add(bufClone);
}
clone.vertexArrayID = -1;
clone.vertCount = vertCount;
clone.elementCount = elementCount;
clone.instanceCount = instanceCount;
@ -254,12 +239,7 @@ public class Mesh implements Savable, Cloneable {
// if the bone weight/index buffers are modified
clone.maxNumWeights = maxNumWeights;
clone.elementLengths = elementLengths != null ? elementLengths.clone() : null;
clone.modeStart = modeStart != null ? modeStart.clone() : null;
return clone;
}catch (CloneNotSupportedException ex){
throw new AssertionError();
}
}
/**
@ -374,11 +354,16 @@ public class Mesh implements Savable, Cloneable {
// convert weights on the heap
VertexBuffer weights = getBuffer(Type.BoneWeight);
if (!weights.getData().hasArray()) {
if (weights.getFormat() == Format.Float) {
FloatBuffer originalWeight = (FloatBuffer) weights.getData();
FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());
originalWeight.clear();
arrayWeight.put(originalWeight);
weights.updateData(arrayWeight);
} else {
// UByte to Float conversion
throw new UnsupportedOperationException("Not yet supported");
}
}
weights.setUsage(Usage.CpuOnly);
// position, normal, and tanget buffers to be in "Stream" mode
@ -475,40 +460,6 @@ public class Mesh implements Savable, Cloneable {
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
*
@ -899,23 +850,6 @@ public class Mesh implements Savable, Cloneable {
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.
* Called automatically by {@link #collideWith(com.jme3.collision.Collidable,
@ -1109,20 +1043,17 @@ public class Mesh implements Savable, Cloneable {
* @return A virtual or wrapped index buffer to read the data as a list
*/
public IndexBuffer getIndicesAsList(){
if (mode == Mode.Hybrid)
throw new UnsupportedOperationException("Hybrid mode not supported");
IndexBuffer ib = getIndexBuffer();
if (ib != null){
if (mode.isListMode()){
if (ib != null) {
if (mode.isListMode()) {
// already in list mode
return ib;
}else{
} else {
// not in list mode but it does have an index buffer
// wrap it so the data is converted to list format
return new WrappedIndexBuffer(this);
}
}else{
} else {
// return a virtual index buffer that will supply
// "fake" indices in list format
return new VirtualIndexBuffer(vertCount, mode);
@ -1385,16 +1316,30 @@ public class Mesh implements Savable, Cloneable {
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 {
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(vertCount, "vertCount", -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(mode, "mode", Mode.Triangles);
out.write(collisionTree, "collisionTree", null);
out.write(elementLengths, "elementLengths", null);
out.write(modeStart, "modeStart", null);
out.write(pointSize, "pointSize", 1f);
//Removing HW skinning buffers to not save them
@ -1439,21 +1382,16 @@ public class Mesh implements Savable, Cloneable {
instanceCount = in.readInt("instanceCount", -1);
maxNumWeights = in.readInt("max_num_weights", -1);
mode = in.readEnum("mode", Mode.class, Mode.Triangles);
elementLengths = in.readIntArray("elementLengths", null);
modeStart = in.readIntArray("modeStart", null);
collisionTree = (BIHTree) in.readSavable("collisionTree", null);
elementLengths = in.readIntArray("elementLengths", null);
modeStart = in.readIntArray("modeStart", null);
pointSize = in.readFloat("pointSize", 1f);
// in.readStringSavableMap("buffers", null);
buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null);
for (Entry<VertexBuffer> entry : buffers){
buffersList.add(entry.getValue());
}
//creating hw animation buffers empty so that they are put in the cache
if(isAnimated()){
if (isAnimated()) {
VertexBuffer hwBoneIndex = new VertexBuffer(Type.HWBoneIndex);
hwBoneIndex.setUsage(Usage.CpuOnly);
setBuffer(hwBoneIndex);

@ -524,6 +524,17 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
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
* 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
* {@link Format format} and would be able to contain the given number
* 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){
if (components < 1 || components > 4)
@ -1010,7 +1025,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
* @return Deep clone of this buffer
*/
@Override
public VertexBuffer clone(){
public VertexBuffer clone() {
// NOTE: Superclass GLObject automatically creates shallow clone
// e.g re-use ID.
VertexBuffer vb = (VertexBuffer) super.clone();

@ -81,7 +81,7 @@ public class VirtualIndexBuffer extends IndexBuffer {
case Triangles:
numIndices = numVerts;
return;
case Hybrid:
case Reserved:
throw new UnsupportedOperationException();
}
}

@ -303,6 +303,16 @@ public final class Shader extends NativeObject {
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(){
return uniforms;
}

@ -103,6 +103,10 @@ public class Uniform extends ShaderVariable {
return value;
}
public FloatBuffer getMultiData() {
return multiData;
}
public boolean isSetByCurrentMaterial() {
return setByCurrentMaterial;
}
@ -111,21 +115,6 @@ public class Uniform extends ShaderVariable {
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(){
updateNeeded = true;
@ -189,20 +178,36 @@ public class Uniform extends ShaderVariable {
switch (type){
case Matrix3:
if (value.equals(this.value)) {
return;
}
Matrix3f m3 = (Matrix3f) value;
if (multiData == null) {
multiData = BufferUtils.createFloatBuffer(9);
}
m3.fillFloatBuffer(multiData, true);
multiData.clear();
if (this.value == null) {
this.value = new Matrix3f(m3);
} else {
((Matrix3f)this.value).set(m3);
}
break;
case Matrix4:
if (value.equals(this.value)) {
return;
}
Matrix4f m4 = (Matrix4f) value;
if (multiData == null) {
multiData = BufferUtils.createFloatBuffer(16);
}
m4.fillFloatBuffer(multiData, true);
multiData.clear();
if (this.value == null) {
this.value = new Matrix4f(m4);
} else {
((Matrix4f)this.value).copy(m4);
}
break;
case IntArray:
int[] ia = (int[]) value;
@ -283,11 +288,32 @@ public class Uniform extends ShaderVariable {
}
multiData.clear();
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
case Int:
case Float:
case Boolean:
if (this.value != null && this.value.equals(value)) {
if (value.equals(this.value)) {
return;
}
this.value = value;
@ -297,39 +323,38 @@ public class Uniform extends ShaderVariable {
break;
}
if (multiData != null) {
this.value = multiData;
}
// if (multiData != null) {
// this.value = multiData;
// }
varType = type;
updateNeeded = true;
}
public void setVector4Length(int length){
if (location == -1)
if (location == -1) {
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;
updateNeeded = true;
setByCurrentMaterial = true;
}
public void setVector4InArray(float x, float y, float z, float w, int index){
if (location == -1)
if (location == -1) {
return;
}
if (varType != null && varType != VarType.Vector4Array)
throw new IllegalArgumentException("Expected a "+varType.name()+" value!");
if (varType != null && varType != VarType.Vector4Array) {
throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
}
FloatBuffer fb = (FloatBuffer) value;
fb.position(index * 4);
fb.put(x).put(y).put(z).put(w);
fb.rewind();
multiData.position(index * 4);
multiData.put(x).put(y).put(z).put(w);
multiData.rewind();
updateNeeded = true;
setByCurrentMaterial = true;
}

@ -164,4 +164,7 @@ public class NullRenderer implements Renderer {
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_AUDIOBUFFER = 6,
OBJTYPE_AUDIOSTREAM = 7,
OBJTYPE_FILTER = 8;
OBJTYPE_FILTER = 8,
OBJTYPE_MESH = 9;
/**
* 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.NanoTimer;
import com.jme3.system.NativeLibraryLoader;
import com.jme3.system.NullRenderer;
import com.jme3.system.SystemListener;
import com.jme3.system.Timer;

Loading…
Cancel
Save