Merge remote-tracking branch 'origin/master' into opengles2-fixes
This commit is contained in:
commit
7326c45ee8
@ -1980,7 +1980,13 @@ public final class GLRenderer implements Renderer {
|
|||||||
@SuppressWarnings("fallthrough")
|
@SuppressWarnings("fallthrough")
|
||||||
private void setupTextureParams(int unit, Texture tex) {
|
private void setupTextureParams(int unit, Texture tex) {
|
||||||
Image image = tex.getImage();
|
Image image = tex.getImage();
|
||||||
int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1);
|
int samples = image != null ? image.getMultiSamples() : 1;
|
||||||
|
int target = convertTextureType(tex.getType(), samples, -1);
|
||||||
|
|
||||||
|
if (samples > 1) {
|
||||||
|
bindTextureOnly(target, image, unit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean haveMips = true;
|
boolean haveMips = true;
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
@ -2183,45 +2189,51 @@ public final class GLRenderer implements Renderer {
|
|||||||
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()) {
|
|
||||||
// Image does not have mipmaps, but they are required.
|
|
||||||
// Generate from base level.
|
|
||||||
|
|
||||||
if (!caps.contains(Caps.FrameBuffer) && gl2 != null) {
|
|
||||||
gl2.glTexParameteri(target, GL2.GL_GENERATE_MIPMAP, GL.GL_TRUE);
|
|
||||||
img.setMipmapsGenerated(true);
|
|
||||||
} else {
|
|
||||||
// For OpenGL3 and up.
|
|
||||||
// We'll generate mipmaps via glGenerateMipmapEXT (see below)
|
|
||||||
}
|
|
||||||
} else if (img.hasMipmaps()) {
|
|
||||||
// Image already has mipmaps, set the max level based on the
|
|
||||||
// number of mipmaps we have.
|
|
||||||
if (caps.contains(Caps.OpenGL20)) {
|
|
||||||
gl.glTexParameteri(target, GL2.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Image does not have mipmaps and they are not required.
|
|
||||||
// Specify that that the texture has no mipmaps.
|
|
||||||
if (caps.contains(Caps.OpenGL20)) {
|
|
||||||
gl.glTexParameteri(target, GL2.GL_TEXTURE_MAX_LEVEL, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int imageSamples = img.getMultiSamples();
|
int imageSamples = img.getMultiSamples();
|
||||||
if (imageSamples > 1) {
|
|
||||||
|
if (imageSamples <= 1) {
|
||||||
|
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
|
||||||
|
// Image does not have mipmaps, but they are required.
|
||||||
|
// Generate from base level.
|
||||||
|
|
||||||
|
if (!caps.contains(Caps.FrameBuffer) && gl2 != null) {
|
||||||
|
gl2.glTexParameteri(target, GL2.GL_GENERATE_MIPMAP, GL.GL_TRUE);
|
||||||
|
img.setMipmapsGenerated(true);
|
||||||
|
} else {
|
||||||
|
// For OpenGL3 and up.
|
||||||
|
// We'll generate mipmaps via glGenerateMipmapEXT (see below)
|
||||||
|
}
|
||||||
|
} else if (img.hasMipmaps()) {
|
||||||
|
// Image already has mipmaps, set the max level based on the
|
||||||
|
// number of mipmaps we have.
|
||||||
|
gl.glTexParameteri(target, GL.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length - 1);
|
||||||
|
} else {
|
||||||
|
// Image does not have mipmaps and they are not required.
|
||||||
|
// Specify that that the texture has no mipmaps.
|
||||||
|
gl.glTexParameteri(target, GL.GL_TEXTURE_MAX_LEVEL, 0);
|
||||||
|
}
|
||||||
|
(??) } else if (img.hasMipmaps()) {
|
||||||
|
(??) // Image already has mipmaps, set the max level based on the
|
||||||
|
(??) // number of mipmaps we have.
|
||||||
|
(??) gl.glTexParameteri(target, GL.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length - 1);
|
||||||
|
} else {
|
||||||
|
// Check if graphics card doesn't support multisample textures
|
||||||
|
if (!caps.contains(Caps.TextureMultisample)) {
|
||||||
|
throw new RendererException("Multisample textures are not supported by the video hardware");
|
||||||
|
}
|
||||||
|
(??)
|
||||||
|
|
||||||
|
if (img.isGeneratedMipmapsRequired() || img.hasMipmaps()) {
|
||||||
|
throw new RendererException("Multisample textures do not support mipmaps");
|
||||||
|
}
|
||||||
|
|
||||||
if (img.getFormat().isDepthFormat()) {
|
if (img.getFormat().isDepthFormat()) {
|
||||||
img.setMultiSamples(Math.min(limits.get(Limits.DepthTextureSamples), imageSamples));
|
img.setMultiSamples(Math.min(limits.get(Limits.DepthTextureSamples), imageSamples));
|
||||||
} else {
|
} else {
|
||||||
img.setMultiSamples(Math.min(limits.get(Limits.ColorTextureSamples), imageSamples));
|
img.setMultiSamples(Math.min(limits.get(Limits.ColorTextureSamples), imageSamples));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check if graphics card doesn't support multisample textures
|
scaleToPot = false;
|
||||||
if (!caps.contains(Caps.TextureMultisample)) {
|
|
||||||
if (img.getMultiSamples() > 1) {
|
|
||||||
throw new RendererException("Multisample textures are not supported by the video hardware");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if graphics card doesn't support depth textures
|
// Check if graphics card doesn't support depth textures
|
||||||
|
@ -62,9 +62,9 @@ import com.jme3.util.clone.JmeCloneable;
|
|||||||
* Sub geoms can be removed but it may be slower than the normal spatial removing
|
* Sub geoms can be removed but it may be slower than the normal spatial removing
|
||||||
* Sub geoms can be added after the batch() method has been called but won't be batched and will just be rendered as normal geometries.
|
* Sub geoms can be added after the batch() method has been called but won't be batched and will just be rendered as normal geometries.
|
||||||
* To integrate them in the batch you have to call the batch() method again on the batchNode.
|
* To integrate them in the batch you have to call the batch() method again on the batchNode.
|
||||||
*
|
* <p>
|
||||||
* TODO normal or tangents or both looks a bit weird
|
|
||||||
* TODO more automagic (batch when needed in the updateLogicalState)
|
* TODO more automagic (batch when needed in the updateLogicalState)
|
||||||
|
*
|
||||||
* @author Nehon
|
* @author Nehon
|
||||||
*/
|
*/
|
||||||
public class BatchNode extends GeometryGroupNode {
|
public class BatchNode extends GeometryGroupNode {
|
||||||
@ -108,7 +108,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
public void onMaterialChange(Geometry geom) {
|
public void onMaterialChange(Geometry geom) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Cannot set the material of a batched geometry, "
|
"Cannot set the material of a batched geometry, "
|
||||||
+ "change the material of the parent BatchNode.");
|
+ "change the material of the parent BatchNode.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -122,7 +122,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
setNeedsFullRebatch(true);
|
setNeedsFullRebatch(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Matrix4f getTransformMatrix(Geometry g){
|
protected Matrix4f getTransformMatrix(Geometry g) {
|
||||||
return g.cachedWorldMat;
|
return g.cachedWorldMat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,35 +133,44 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
Mesh origMesh = bg.getMesh();
|
Mesh origMesh = bg.getMesh();
|
||||||
|
|
||||||
VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position);
|
VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position);
|
||||||
FloatBuffer posBuf = (FloatBuffer) pvb.getData();
|
|
||||||
VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal);
|
VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal);
|
||||||
FloatBuffer normBuf = (FloatBuffer) nvb.getData();
|
VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent);
|
||||||
|
|
||||||
VertexBuffer opvb = origMesh.getBuffer(VertexBuffer.Type.Position);
|
VertexBuffer opvb = origMesh.getBuffer(VertexBuffer.Type.Position);
|
||||||
FloatBuffer oposBuf = (FloatBuffer) opvb.getData();
|
|
||||||
VertexBuffer onvb = origMesh.getBuffer(VertexBuffer.Type.Normal);
|
VertexBuffer onvb = origMesh.getBuffer(VertexBuffer.Type.Normal);
|
||||||
FloatBuffer onormBuf = (FloatBuffer) onvb.getData();
|
VertexBuffer otvb = origMesh.getBuffer(VertexBuffer.Type.Tangent);
|
||||||
|
|
||||||
|
FloatBuffer posBuf = getFloatBuffer(pvb);
|
||||||
|
FloatBuffer normBuf = getFloatBuffer(nvb);
|
||||||
|
FloatBuffer tanBuf = getFloatBuffer(tvb);
|
||||||
|
|
||||||
|
FloatBuffer oposBuf = getFloatBuffer(opvb);
|
||||||
|
FloatBuffer onormBuf = getFloatBuffer(onvb);
|
||||||
|
FloatBuffer otanBuf = getFloatBuffer(otvb);
|
||||||
|
|
||||||
Matrix4f transformMat = getTransformMatrix(bg);
|
Matrix4f transformMat = getTransformMatrix(bg);
|
||||||
|
doTransforms(oposBuf, onormBuf, otanBuf, posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), transformMat);
|
||||||
|
|
||||||
if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) {
|
|
||||||
|
|
||||||
VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent);
|
|
||||||
FloatBuffer tanBuf = (FloatBuffer) tvb.getData();
|
|
||||||
VertexBuffer otvb = origMesh.getBuffer(VertexBuffer.Type.Tangent);
|
|
||||||
FloatBuffer otanBuf = (FloatBuffer) otvb.getData();
|
|
||||||
doTransformsTangents(oposBuf, onormBuf, otanBuf, posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), transformMat);
|
|
||||||
tvb.updateData(tanBuf);
|
|
||||||
} else {
|
|
||||||
doTransforms(oposBuf, onormBuf, posBuf, normBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), transformMat);
|
|
||||||
}
|
|
||||||
pvb.updateData(posBuf);
|
pvb.updateData(posBuf);
|
||||||
nvb.updateData(normBuf);
|
|
||||||
|
|
||||||
|
if (nvb != null) {
|
||||||
|
nvb.updateData(normBuf);
|
||||||
|
}
|
||||||
|
if (tvb != null) {
|
||||||
|
tvb.updateData(tanBuf);
|
||||||
|
}
|
||||||
|
|
||||||
batch.geometry.updateModelBound();
|
batch.geometry.updateModelBound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FloatBuffer getFloatBuffer(VertexBuffer vb) {
|
||||||
|
if (vb == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (FloatBuffer) vb.getData();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Batch this batchNode
|
* Batch this batchNode
|
||||||
* every geometry of the sub scene graph of this node will be batched into a single mesh that will be rendered in one call
|
* every geometry of the sub scene graph of this node will be batched into a single mesh that will be rendered in one call
|
||||||
@ -234,7 +243,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
logger.log(Level.FINE, "Batched {0} geometries in {1} batches.", new Object[]{nbGeoms, batches.size()});
|
logger.log(Level.FINE, "Batched {0} geometries in {1} batches.", new Object[]{nbGeoms, batches.size()});
|
||||||
|
|
||||||
//init the temp arrays if something has been batched only.
|
//init the temp arrays if something has been batched only.
|
||||||
if(matMap.size()>0){
|
if (matMap.size() > 0) {
|
||||||
//TODO these arrays should be allocated by chunk instead to avoid recreating them each time the batch is changed.
|
//TODO these arrays should be allocated by chunk instead to avoid recreating them each time the batch is changed.
|
||||||
//init temp float arrays
|
//init temp float arrays
|
||||||
tmpFloat = new float[maxVertCount * 3];
|
tmpFloat = new float[maxVertCount * 3];
|
||||||
@ -257,6 +266,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* recursively visit the subgraph and unbatch geometries
|
* recursively visit the subgraph and unbatch geometries
|
||||||
|
*
|
||||||
* @param s
|
* @param s
|
||||||
*/
|
*/
|
||||||
private void unbatchSubGraph(Spatial s) {
|
private void unbatchSubGraph(Spatial s) {
|
||||||
@ -343,11 +353,10 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the material that is used for the first batch of this BatchNode
|
* Returns the material that is used for the first batch of this BatchNode
|
||||||
*
|
* <p>
|
||||||
* use getMaterial(Material material,int batchIndex) to get a material from a specific batch
|
* use getMaterial(Material material,int batchIndex) to get a material from a specific batch
|
||||||
*
|
*
|
||||||
* @return the material that is used for the first batch of this BatchNode
|
* @return the material that is used for the first batch of this BatchNode
|
||||||
*
|
|
||||||
* @see #setMaterial(com.jme3.material.Material)
|
* @see #setMaterial(com.jme3.material.Material)
|
||||||
*/
|
*/
|
||||||
public Material getMaterial() {
|
public Material getMaterial() {
|
||||||
@ -428,14 +437,6 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
+ " primitive types: " + mode + " != " + listMode);
|
+ " primitive types: " + mode + " != " + listMode);
|
||||||
}
|
}
|
||||||
mode = listMode;
|
mode = listMode;
|
||||||
//Not needed anymore as lineWidth is now in RenderState and will be taken into account when merging according to the material
|
|
||||||
// if (mode == Mesh.Mode.Lines) {
|
|
||||||
// if (lineWidth != 1f && listLineWidth != lineWidth) {
|
|
||||||
// throw new UnsupportedOperationException("When using Mesh Line mode, cannot combine meshes with different line width "
|
|
||||||
// + lineWidth + " != " + listLineWidth);
|
|
||||||
// }
|
|
||||||
// lineWidth = listLineWidth;
|
|
||||||
// }
|
|
||||||
compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
|
compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,53 +529,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doTransforms(FloatBuffer bindBufPos, FloatBuffer bindBufNorm, FloatBuffer bufPos, FloatBuffer bufNorm, int start, int end, Matrix4f transform) {
|
private void doTransforms(FloatBuffer bindBufPos, FloatBuffer bindBufNorm, FloatBuffer bindBufTangents, FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) {
|
||||||
TempVars vars = TempVars.get();
|
|
||||||
Vector3f pos = vars.vect1;
|
|
||||||
Vector3f norm = vars.vect2;
|
|
||||||
|
|
||||||
int length = (end - start) * 3;
|
|
||||||
|
|
||||||
// offset is given in element units
|
|
||||||
// convert to be in component units
|
|
||||||
int offset = start * 3;
|
|
||||||
bindBufPos.rewind();
|
|
||||||
bindBufNorm.rewind();
|
|
||||||
//bufPos.position(offset);
|
|
||||||
//bufNorm.position(offset);
|
|
||||||
bindBufPos.get(tmpFloat, 0, length);
|
|
||||||
bindBufNorm.get(tmpFloatN, 0, length);
|
|
||||||
int index = 0;
|
|
||||||
while (index < length) {
|
|
||||||
pos.x = tmpFloat[index];
|
|
||||||
norm.x = tmpFloatN[index++];
|
|
||||||
pos.y = tmpFloat[index];
|
|
||||||
norm.y = tmpFloatN[index++];
|
|
||||||
pos.z = tmpFloat[index];
|
|
||||||
norm.z = tmpFloatN[index];
|
|
||||||
|
|
||||||
transform.mult(pos, pos);
|
|
||||||
transform.multNormal(norm, norm);
|
|
||||||
|
|
||||||
index -= 2;
|
|
||||||
tmpFloat[index] = pos.x;
|
|
||||||
tmpFloatN[index++] = norm.x;
|
|
||||||
tmpFloat[index] = pos.y;
|
|
||||||
tmpFloatN[index++] = norm.y;
|
|
||||||
tmpFloat[index] = pos.z;
|
|
||||||
tmpFloatN[index++] = norm.z;
|
|
||||||
|
|
||||||
}
|
|
||||||
vars.release();
|
|
||||||
bufPos.position(offset);
|
|
||||||
//using bulk put as it's faster
|
|
||||||
bufPos.put(tmpFloat, 0, length);
|
|
||||||
bufNorm.position(offset);
|
|
||||||
//using bulk put as it's faster
|
|
||||||
bufNorm.put(tmpFloatN, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doTransformsTangents(FloatBuffer bindBufPos, FloatBuffer bindBufNorm, FloatBuffer bindBufTangents,FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) {
|
|
||||||
TempVars vars = TempVars.get();
|
TempVars vars = TempVars.get();
|
||||||
Vector3f pos = vars.vect1;
|
Vector3f pos = vars.vect1;
|
||||||
Vector3f norm = vars.vect2;
|
Vector3f norm = vars.vect2;
|
||||||
@ -588,60 +543,76 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
int offset = start * 3;
|
int offset = start * 3;
|
||||||
int tanOffset = start * 4;
|
int tanOffset = start * 4;
|
||||||
|
|
||||||
|
|
||||||
bindBufPos.rewind();
|
bindBufPos.rewind();
|
||||||
bindBufNorm.rewind();
|
|
||||||
bindBufTangents.rewind();
|
|
||||||
bindBufPos.get(tmpFloat, 0, length);
|
bindBufPos.get(tmpFloat, 0, length);
|
||||||
bindBufNorm.get(tmpFloatN, 0, length);
|
|
||||||
bindBufTangents.get(tmpFloatT, 0, tanLength);
|
if (bindBufNorm != null) {
|
||||||
|
bindBufNorm.rewind();
|
||||||
|
bindBufNorm.get(tmpFloatN, 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bindBufTangents != null) {
|
||||||
|
bindBufTangents.rewind();
|
||||||
|
bindBufTangents.get(tmpFloatT, 0, tanLength);
|
||||||
|
}
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
int tanIndex = 0;
|
int tanIndex = 0;
|
||||||
|
int index1, index2, tanIndex1, tanIndex2;
|
||||||
|
|
||||||
while (index < length) {
|
while (index < length) {
|
||||||
|
index1 = index + 1;
|
||||||
|
index2 = index + 2;
|
||||||
|
|
||||||
pos.x = tmpFloat[index];
|
pos.x = tmpFloat[index];
|
||||||
norm.x = tmpFloatN[index++];
|
pos.y = tmpFloat[index1];
|
||||||
pos.y = tmpFloat[index];
|
pos.z = tmpFloat[index2];
|
||||||
norm.y = tmpFloatN[index++];
|
|
||||||
pos.z = tmpFloat[index];
|
|
||||||
norm.z = tmpFloatN[index];
|
|
||||||
|
|
||||||
tan.x = tmpFloatT[tanIndex++];
|
|
||||||
tan.y = tmpFloatT[tanIndex++];
|
|
||||||
tan.z = tmpFloatT[tanIndex++];
|
|
||||||
|
|
||||||
transform.mult(pos, pos);
|
transform.mult(pos, pos);
|
||||||
transform.multNormal(norm, norm);
|
|
||||||
transform.multNormal(tan, tan);
|
|
||||||
|
|
||||||
index -= 2;
|
|
||||||
tanIndex -= 3;
|
|
||||||
|
|
||||||
tmpFloat[index] = pos.x;
|
tmpFloat[index] = pos.x;
|
||||||
tmpFloatN[index++] = norm.x;
|
tmpFloat[index1] = pos.y;
|
||||||
tmpFloat[index] = pos.y;
|
tmpFloat[index2] = pos.z;
|
||||||
tmpFloatN[index++] = norm.y;
|
|
||||||
tmpFloat[index] = pos.z;
|
|
||||||
tmpFloatN[index++] = norm.z;
|
|
||||||
|
|
||||||
tmpFloatT[tanIndex++] = tan.x;
|
if (bindBufNorm != null) {
|
||||||
tmpFloatT[tanIndex++] = tan.y;
|
norm.x = tmpFloatN[index];
|
||||||
tmpFloatT[tanIndex++] = tan.z;
|
norm.y = tmpFloatN[index1];
|
||||||
|
norm.z = tmpFloatN[index2];
|
||||||
|
transform.multNormal(norm, norm);
|
||||||
|
tmpFloatN[index] = norm.x;
|
||||||
|
tmpFloatN[index1] = norm.y;
|
||||||
|
tmpFloatN[index2] = norm.z;
|
||||||
|
}
|
||||||
|
|
||||||
//Skipping 4th element of tangent buffer (handedness)
|
index += 3;
|
||||||
tanIndex++;
|
|
||||||
|
if (bindBufTangents != null) {
|
||||||
|
tanIndex1 = tanIndex + 1;
|
||||||
|
tanIndex2 = tanIndex + 2;
|
||||||
|
tan.x = tmpFloatT[tanIndex];
|
||||||
|
tan.y = tmpFloatT[tanIndex1];
|
||||||
|
tan.z = tmpFloatT[tanIndex2];
|
||||||
|
transform.multNormal(tan, tan);
|
||||||
|
tmpFloatT[tanIndex] = tan.x;
|
||||||
|
tmpFloatT[tanIndex1] = tan.y;
|
||||||
|
tmpFloatT[tanIndex2] = tan.z;
|
||||||
|
tanIndex += 4;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
vars.release();
|
vars.release();
|
||||||
|
|
||||||
|
//using bulk put as it's faster
|
||||||
bufPos.position(offset);
|
bufPos.position(offset);
|
||||||
//using bulk put as it's faster
|
|
||||||
bufPos.put(tmpFloat, 0, length);
|
bufPos.put(tmpFloat, 0, length);
|
||||||
bufNorm.position(offset);
|
|
||||||
//using bulk put as it's faster
|
if (bindBufNorm != null) {
|
||||||
bufNorm.put(tmpFloatN, 0, length);
|
bufNorm.position(offset);
|
||||||
bufTangents.position(tanOffset);
|
bufNorm.put(tmpFloatN, 0, length);
|
||||||
//using bulk put as it's faster
|
}
|
||||||
bufTangents.put(tmpFloatT, 0, tanLength);
|
|
||||||
|
if (bindBufTangents != null) {
|
||||||
|
bufTangents.position(tanOffset);
|
||||||
|
bufTangents.put(tmpFloatT, 0, tanLength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf, int componentSize) {
|
private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf, int componentSize) {
|
||||||
@ -653,11 +624,11 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
offset *= componentSize;
|
offset *= componentSize;
|
||||||
|
|
||||||
for (int i = 0; i < inBuf.limit() / componentSize; i++) {
|
for (int i = 0; i < inBuf.limit() / componentSize; i++) {
|
||||||
pos.x = inBuf.get(i * componentSize + 0);
|
pos.x = inBuf.get(i * componentSize);
|
||||||
pos.y = inBuf.get(i * componentSize + 1);
|
pos.y = inBuf.get(i * componentSize + 1);
|
||||||
pos.z = inBuf.get(i * componentSize + 2);
|
pos.z = inBuf.get(i * componentSize + 2);
|
||||||
|
|
||||||
outBuf.put(offset + i * componentSize + 0, pos.x);
|
outBuf.put(offset + i * componentSize, pos.x);
|
||||||
outBuf.put(offset + i * componentSize + 1, pos.y);
|
outBuf.put(offset + i * componentSize + 1, pos.y);
|
||||||
outBuf.put(offset + i * componentSize + 2, pos.z);
|
outBuf.put(offset + i * componentSize + 2, pos.z);
|
||||||
}
|
}
|
||||||
@ -667,6 +638,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
protected class Batch implements JmeCloneable {
|
protected class Batch implements JmeCloneable {
|
||||||
/**
|
/**
|
||||||
* update the batchesByGeom map for this batch with the given List of geometries
|
* update the batchesByGeom map for this batch with the given List of geometries
|
||||||
|
*
|
||||||
* @param list
|
* @param list
|
||||||
*/
|
*/
|
||||||
void updateGeomList(List<Geometry> list) {
|
void updateGeomList(List<Geometry> list) {
|
||||||
@ -676,6 +648,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Geometry geometry;
|
Geometry geometry;
|
||||||
|
|
||||||
public final Geometry getGeometry() {
|
public final Geometry getGeometry() {
|
||||||
@ -685,14 +658,14 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
@Override
|
@Override
|
||||||
public Batch jmeClone() {
|
public Batch jmeClone() {
|
||||||
try {
|
try {
|
||||||
return (Batch)super.clone();
|
return (Batch) super.clone();
|
||||||
} catch (CloneNotSupportedException ex) {
|
} catch (CloneNotSupportedException ex) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cloneFields( Cloner cloner, Object original ) {
|
public void cloneFields(Cloner cloner, Object original) {
|
||||||
this.geometry = cloner.clone(geometry);
|
this.geometry = cloner.clone(geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,11 +677,11 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node clone(boolean cloneMaterials) {
|
public Node clone(boolean cloneMaterials) {
|
||||||
BatchNode clone = (BatchNode)super.clone(cloneMaterials);
|
BatchNode clone = (BatchNode) super.clone(cloneMaterials);
|
||||||
if ( batches.size() > 0) {
|
if (batches.size() > 0) {
|
||||||
for ( Batch b : batches ) {
|
for (Batch b : batches) {
|
||||||
for ( int i =0; i < clone.children.size(); i++ ) {
|
for (int i = 0; i < clone.children.size(); i++) {
|
||||||
if ( clone.children.get(i).getName().equals(b.geometry.getName())) {
|
if (clone.children.get(i).getName().equals(b.geometry.getName())) {
|
||||||
clone.children.remove(i);
|
clone.children.remove(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -723,10 +696,10 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void cloneFields( Cloner cloner, Object original ) {
|
public void cloneFields(Cloner cloner, Object original) {
|
||||||
super.cloneFields(cloner, original);
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
this.batches = cloner.clone(batches);
|
this.batches = cloner.clone(batches);
|
||||||
@ -736,7 +709,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
|
|
||||||
|
|
||||||
HashMap<Geometry, Batch> newBatchesByGeom = new HashMap<Geometry, Batch>();
|
HashMap<Geometry, Batch> newBatchesByGeom = new HashMap<Geometry, Batch>();
|
||||||
for( Map.Entry<Geometry, Batch> e : batchesByGeom.entrySet() ) {
|
for (Map.Entry<Geometry, Batch> e : batchesByGeom.entrySet()) {
|
||||||
newBatchesByGeom.put(cloner.clone(e.getKey()), cloner.clone(e.getValue()));
|
newBatchesByGeom.put(cloner.clone(e.getKey()), cloner.clone(e.getValue()));
|
||||||
}
|
}
|
||||||
this.batchesByGeom = newBatchesByGeom;
|
this.batchesByGeom = newBatchesByGeom;
|
||||||
@ -745,7 +718,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
@Override
|
@Override
|
||||||
public int collideWith(Collidable other, CollisionResults results) {
|
public int collideWith(Collidable other, CollisionResults results) {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (Spatial child : children.getArray()){
|
for (Spatial child : children.getArray()) {
|
||||||
if (!isBatch(child)) {
|
if (!isBatch(child)) {
|
||||||
total += child.collideWith(other, results);
|
total += child.collideWith(other, results);
|
||||||
}
|
}
|
||||||
|
@ -1013,6 +1013,18 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
BoundingVolume worldBound,
|
BoundingVolume worldBound,
|
||||||
CollisionResults results){
|
CollisionResults results){
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case Points:
|
||||||
|
case Lines:
|
||||||
|
case LineStrip:
|
||||||
|
case LineLoop:
|
||||||
|
/*
|
||||||
|
* Collisions can be detected only with triangles,
|
||||||
|
* and there are no triangles in this mesh.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (getVertexCount() == 0) {
|
if (getVertexCount() == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ MaterialDef Sky Plane {
|
|||||||
WorldParameters {
|
WorldParameters {
|
||||||
ViewMatrix
|
ViewMatrix
|
||||||
ProjectionMatrix
|
ProjectionMatrix
|
||||||
WorldMatrix
|
WorldMatrixInverse
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||||
uniform mat4 g_ViewMatrix;
|
uniform mat4 g_ViewMatrix;
|
||||||
uniform mat4 g_ProjectionMatrix;
|
uniform mat4 g_ProjectionMatrix;
|
||||||
uniform mat4 g_WorldMatrix;
|
uniform mat4 g_WorldMatrixInverse;
|
||||||
|
|
||||||
uniform vec3 m_NormalScale;
|
uniform vec3 m_NormalScale;
|
||||||
|
|
||||||
@ -22,5 +22,5 @@ void main(){
|
|||||||
gl_Position = g_ProjectionMatrix * pos;
|
gl_Position = g_ProjectionMatrix * pos;
|
||||||
|
|
||||||
vec4 normal = vec4(inNormal * m_NormalScale, 0.0);
|
vec4 normal = vec4(inNormal * m_NormalScale, 0.0);
|
||||||
direction = (g_WorldMatrix * normal).xyz;
|
direction = (g_WorldMatrixInverse * normal).xyz;
|
||||||
}
|
}
|
||||||
|
128
jme3-core/src/test/java/com/jme3/scene/PhantomTrianglesTest.java
Normal file
128
jme3-core/src/test/java/com/jme3/scene/PhantomTrianglesTest.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 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.scene;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.asset.DesktopAssetManager;
|
||||||
|
import com.jme3.asset.plugins.ClasspathLocator;
|
||||||
|
import com.jme3.collision.CollisionResult;
|
||||||
|
import com.jme3.collision.CollisionResults;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.material.plugins.J3MLoader;
|
||||||
|
import com.jme3.math.Ray;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.shape.Quad;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that collideWith() doesn't reports collisions with phantom triangles.
|
||||||
|
* This was issue #710 at GitHub.
|
||||||
|
*
|
||||||
|
* @author Stephen Gold
|
||||||
|
*/
|
||||||
|
public class PhantomTrianglesTest {
|
||||||
|
|
||||||
|
AssetManager assetManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ray in the -Z direction, starting from (0.1, 0.2, 10)
|
||||||
|
*/
|
||||||
|
final private Ray ray = new Ray(/* origin */new Vector3f(0.1f, 0.2f, 10f),
|
||||||
|
/* direction */ new Vector3f(0f, 0f, -1f));
|
||||||
|
Node rootNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast a ray at the geometries and report all collisions.
|
||||||
|
*/
|
||||||
|
void castRay() {
|
||||||
|
CollisionResults results = new CollisionResults();
|
||||||
|
rootNode.collideWith(ray, results);
|
||||||
|
int numResults = results.size();
|
||||||
|
for (int resultI = 0; resultI < numResults; resultI++) {
|
||||||
|
CollisionResult result = results.getCollision(resultI);
|
||||||
|
Geometry geometry = result.getGeometry();
|
||||||
|
String name = geometry.getName();
|
||||||
|
if (name.equals("white lines")) {
|
||||||
|
assert false; // phantom triangle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach a red square in the XY plane with its lower left corner at (0, 0,
|
||||||
|
* 0). It is composed of 2 triangles.
|
||||||
|
*/
|
||||||
|
void createRedSquare() {
|
||||||
|
Mesh quadMesh = new Quad(1f, 1f);
|
||||||
|
Geometry redSquare = new Geometry("red square", quadMesh);
|
||||||
|
Material red = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
|
||||||
|
redSquare.setMaterial(red);
|
||||||
|
rootNode.attachChild(redSquare);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach a pair of parallel white lines in the z=1 plane.
|
||||||
|
*/
|
||||||
|
void createWhiteLines() {
|
||||||
|
Mesh lineMesh = new Mesh();
|
||||||
|
lineMesh.setMode(Mesh.Mode.Lines);
|
||||||
|
float[] corners = new float[]{
|
||||||
|
-1f, -1f, 0f,
|
||||||
|
-1f, 1f, 0f,
|
||||||
|
1f, 1f, 0f,
|
||||||
|
1f, -1f, 0f
|
||||||
|
};
|
||||||
|
lineMesh.setBuffer(VertexBuffer.Type.Position, 3, corners);
|
||||||
|
short[] indices = new short[]{0, 1, 2, 3};
|
||||||
|
lineMesh.setBuffer(VertexBuffer.Type.Index, 2, indices);
|
||||||
|
lineMesh.updateBound();
|
||||||
|
Geometry whiteLines = new Geometry("white lines", lineMesh);
|
||||||
|
Material white = assetManager.loadMaterial("Common/Materials/WhiteColor.j3m");
|
||||||
|
whiteLines.setMaterial(white);
|
||||||
|
whiteLines.move(0f, 0f, 1f);
|
||||||
|
rootNode.attachChild(whiteLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPhantomTriangles() {
|
||||||
|
assetManager = new DesktopAssetManager();
|
||||||
|
assetManager.registerLocator(null, ClasspathLocator.class);
|
||||||
|
assetManager.registerLoader(J3MLoader.class, "j3m", "j3md");
|
||||||
|
rootNode = new Node();
|
||||||
|
|
||||||
|
createRedSquare();
|
||||||
|
createWhiteLines();
|
||||||
|
rootNode.updateLogicalState(0.01f);
|
||||||
|
rootNode.updateGeometricState();
|
||||||
|
castRay();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 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.texture;
|
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.input.KeyInput;
|
||||||
|
import com.jme3.input.controls.ActionListener;
|
||||||
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.util.SkyFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple application to test sky rotation with a cube-mapped sky.
|
||||||
|
*
|
||||||
|
* Press "T" to rotate the sky and floor to the camera's left. Press "Y" to
|
||||||
|
* rotate the sky and floor to the camera's right. Both should appear to move by
|
||||||
|
* the same amount in the same direction.
|
||||||
|
*
|
||||||
|
* See issue #651 for further information.
|
||||||
|
*
|
||||||
|
* @author Stephen Gold
|
||||||
|
*/
|
||||||
|
public class TestSkyRotation extends SimpleApplication implements ActionListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* objects visible in the scene
|
||||||
|
*/
|
||||||
|
private Spatial floor, sky;
|
||||||
|
/**
|
||||||
|
* Y-axis rotation angle in radians
|
||||||
|
*/
|
||||||
|
private float angle = 0f;
|
||||||
|
|
||||||
|
public static void main(String[] arguments) {
|
||||||
|
TestSkyRotation application = new TestSkyRotation();
|
||||||
|
application.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
/*
|
||||||
|
* Configure the camera.
|
||||||
|
*/
|
||||||
|
flyCam.setEnabled(false);
|
||||||
|
Vector3f location = new Vector3f(-7f, 4f, 8f);
|
||||||
|
cam.setLocation(location);
|
||||||
|
Quaternion orientation;
|
||||||
|
orientation = new Quaternion(0.0037f, 0.944684f, -0.01067f, 0.327789f);
|
||||||
|
assert FastMath.approximateEquals(orientation.norm(), 1f);
|
||||||
|
cam.setRotation(orientation);
|
||||||
|
/*
|
||||||
|
* Attach a cube-mapped sky to the scene graph.
|
||||||
|
*/
|
||||||
|
sky = SkyFactory.createSky(assetManager,
|
||||||
|
"Scenes/Beach/FullskiesSunset0068.dds",
|
||||||
|
SkyFactory.EnvMapType.CubeMap);
|
||||||
|
rootNode.attachChild(sky);
|
||||||
|
/*
|
||||||
|
* Attach a "floor" geometry to the scene graph.
|
||||||
|
*/
|
||||||
|
Mesh floorMesh = new Box(10f, 0.1f, 10f);
|
||||||
|
floor = new Geometry("floor", floorMesh);
|
||||||
|
Material floorMaterial = new Material(assetManager,
|
||||||
|
"Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
floorMaterial.setTexture("ColorMap",
|
||||||
|
assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
|
||||||
|
floor.setMaterial(floorMaterial);
|
||||||
|
rootNode.attachChild(floor);
|
||||||
|
/*
|
||||||
|
* Configure mappings and listeners for keyboard input.
|
||||||
|
*/
|
||||||
|
inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_T));
|
||||||
|
inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_Y));
|
||||||
|
inputManager.addListener(this, "left");
|
||||||
|
inputManager.addListener(this, "right");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an input action from the user.
|
||||||
|
*
|
||||||
|
* @param name the name of the action
|
||||||
|
* @param ongoing true→depress key, false→release key
|
||||||
|
* @param ignored
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onAction(String name, boolean ongoing, float ignored) {
|
||||||
|
if (!ongoing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Update the Y-axis rotation angle based on which key was pressed.
|
||||||
|
*/
|
||||||
|
if (name.equals("left")) {
|
||||||
|
angle += 0.1f; // radians
|
||||||
|
System.out.print("rotate floor and sky leftward ...");
|
||||||
|
} else if (name.equals("right")) {
|
||||||
|
angle -= 0.1f; // radians
|
||||||
|
System.out.printf("rotate floor and sky spatials rightward ...");
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Update the local rotations of both objects based on the angle.
|
||||||
|
*/
|
||||||
|
System.out.printf(" to %.1f radians left of start%n", angle);
|
||||||
|
Quaternion rotation = new Quaternion();
|
||||||
|
rotation.fromAngleNormalAxis(angle, Vector3f.UNIT_Y);
|
||||||
|
floor.setLocalRotation(rotation);
|
||||||
|
sky.setLocalRotation(rotation);
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import com.google.gson.JsonArray;
|
|||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.jme3.asset.AssetLoadException;
|
import com.jme3.asset.AssetLoadException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -56,13 +57,13 @@ public class CustomContentManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T readExtensionAndExtras(String name, JsonElement el, T input) throws AssetLoadException {
|
public <T> T readExtensionAndExtras(String name, JsonElement el, T input) throws AssetLoadException, IOException {
|
||||||
T output = readExtension(name, el, input);
|
T output = readExtension(name, el, input);
|
||||||
output = readExtras(name, el, output);
|
output = readExtras(name, el, output);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T readExtension(String name, JsonElement el, T input) throws AssetLoadException {
|
private <T> T readExtension(String name, JsonElement el, T input) throws AssetLoadException, IOException {
|
||||||
JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions");
|
JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions");
|
||||||
if (extensions == null) {
|
if (extensions == null) {
|
||||||
return input;
|
return input;
|
||||||
|
@ -2,6 +2,8 @@ package com.jme3.scene.plugins.gltf;
|
|||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Interface for extension loading implementation.
|
* Base Interface for extension loading implementation.
|
||||||
*
|
*
|
||||||
@ -19,6 +21,6 @@ public interface ExtensionLoader {
|
|||||||
* @param input an object containing already loaded data from the element, this is most probably a JME object
|
* @param input an object containing already loaded data from the element, this is most probably a JME object
|
||||||
* @return An object of the same type as input, containing the data from the input object and the eventual additional data read from the extension
|
* @return An object of the same type as input, containing the data from the input object and the eventual additional data read from the extension
|
||||||
*/
|
*/
|
||||||
Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input);
|
Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package com.jme3.scene.plugins.gltf;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetInfo;
|
||||||
|
import com.jme3.util.LittleEndien;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Nehon on 12/09/2017.
|
||||||
|
*/
|
||||||
|
public class GlbLoader extends GltfLoader {
|
||||||
|
|
||||||
|
private static final int GLTF_MAGIC = 0x46546C67;
|
||||||
|
private static final int JSON_TYPE = 0x4E4F534A;
|
||||||
|
private static final int BIN_TYPE = 0x004E4942;
|
||||||
|
private ArrayList<byte[]> data = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object load(AssetInfo assetInfo) throws IOException {
|
||||||
|
LittleEndien stream = new LittleEndien(new DataInputStream(assetInfo.openStream()));
|
||||||
|
int magic = stream.readInt();
|
||||||
|
int version = stream.readInt();
|
||||||
|
int length = stream.readInt();
|
||||||
|
System.err.println(magic == GLTF_MAGIC ? "gltf" : "no no no");
|
||||||
|
System.err.println(version);
|
||||||
|
System.err.println(length);
|
||||||
|
|
||||||
|
byte[] json = null;
|
||||||
|
|
||||||
|
//length is the total size, we have to remove the header size (3 integers = 12 bytes).
|
||||||
|
length -= 12;
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
int chunkLength = stream.readInt();
|
||||||
|
int chunkType = stream.readInt();
|
||||||
|
if (chunkType == JSON_TYPE) {
|
||||||
|
json = new byte[chunkLength];
|
||||||
|
stream.read(json);
|
||||||
|
System.err.println(new String(json));
|
||||||
|
} else {
|
||||||
|
byte[] bin = new byte[chunkLength];
|
||||||
|
stream.read(bin);
|
||||||
|
data.add(bin);
|
||||||
|
}
|
||||||
|
//8 is the byte size of the 2 ints chunkLength and chunkType.
|
||||||
|
length -= chunkLength + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadFromStream(assetInfo, new ByteArrayInputStream(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] getBytes(int bufferIndex, String uri, Integer bufferLength) throws IOException {
|
||||||
|
return data.get(bufferIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -75,6 +75,11 @@ public class GltfLoader implements AssetLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object load(AssetInfo assetInfo) throws IOException {
|
public Object load(AssetInfo assetInfo) throws IOException {
|
||||||
|
return loadFromStream(assetInfo, assetInfo.openStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Object loadFromStream(AssetInfo assetInfo, InputStream stream) throws IOException {
|
||||||
try {
|
try {
|
||||||
dataCache.clear();
|
dataCache.clear();
|
||||||
info = assetInfo;
|
info = assetInfo;
|
||||||
@ -87,7 +92,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
defaultMat.setFloat("Roughness", 1f);
|
defaultMat.setFloat("Roughness", 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
docRoot = new JsonParser().parse(new JsonReader(new InputStreamReader(assetInfo.openStream()))).getAsJsonObject();
|
docRoot = new JsonParser().parse(new JsonReader(new InputStreamReader(stream))).getAsJsonObject();
|
||||||
|
|
||||||
JsonObject asset = docRoot.getAsJsonObject().get("asset").getAsJsonObject();
|
JsonObject asset = docRoot.getAsJsonObject().get("asset").getAsJsonObject();
|
||||||
String generator = getAsString(asset, "generator");
|
String generator = getAsString(asset, "generator");
|
||||||
@ -455,7 +460,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readBuffer(Integer bufferViewIndex, int byteOffset, int count, Object store, int numComponents, VertexBuffer.Format format) throws IOException {
|
public Object readBuffer(Integer bufferViewIndex, int byteOffset, int count, Object store, int numComponents, VertexBuffer.Format format) throws IOException {
|
||||||
|
|
||||||
JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject();
|
JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject();
|
||||||
Integer bufferIndex = getAsInteger(bufferView, "buffer");
|
Integer bufferIndex = getAsInteger(bufferView, "buffer");
|
||||||
@ -473,8 +478,17 @@ public class GltfLoader implements AssetLoader {
|
|||||||
|
|
||||||
data = customContentManager.readExtensionAndExtras("bufferView", bufferView, data);
|
data = customContentManager.readExtensionAndExtras("bufferView", bufferView, data);
|
||||||
|
|
||||||
|
if (store == null) {
|
||||||
|
store = new byte[byteLength];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == -1) {
|
||||||
|
count = byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
populateBuffer(store, data, count, byteOffset + bvByteOffset, byteStride, numComponents, format);
|
populateBuffer(store, data, count, byteOffset + bvByteOffset, byteStride, numComponents, format);
|
||||||
|
|
||||||
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] readData(int bufferIndex) throws IOException {
|
public byte[] readData(int bufferIndex) throws IOException {
|
||||||
@ -489,6 +503,17 @@ public class GltfLoader implements AssetLoader {
|
|||||||
if (data != null) {
|
if (data != null) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
data = getBytes(bufferIndex, uri, bufferLength);
|
||||||
|
|
||||||
|
data = customContentManager.readExtensionAndExtras("buffer", buffer, data);
|
||||||
|
|
||||||
|
addToCache("buffers", bufferIndex, data, buffers.size());
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte[] getBytes(int bufferIndex, String uri, Integer bufferLength) throws IOException {
|
||||||
|
byte[] data;
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
if (uri.startsWith("data:")) {
|
if (uri.startsWith("data:")) {
|
||||||
//base 64 embed data
|
//base 64 embed data
|
||||||
@ -505,19 +530,13 @@ public class GltfLoader implements AssetLoader {
|
|||||||
input.read(data);
|
input.read(data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//no URI we are in a binary file so the data is in the 2nd chunk
|
//no URI this should not happen in a gltf file, only in glb files.
|
||||||
//TODO handle binary GLTF (GLB)
|
throw new AssetLoadException("Buffer " + bufferIndex + " has no uri");
|
||||||
throw new AssetLoadException("Binary gltf is not supported yet");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data = customContentManager.readExtensionAndExtras("buffer", buffer, data);
|
|
||||||
|
|
||||||
addToCache("buffers", bufferIndex, data, buffers.size());
|
|
||||||
return data;
|
return data;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Material readMaterial(int materialIndex) {
|
public Material readMaterial(int materialIndex) throws IOException {
|
||||||
assertNotNull(materials, "There is no material defined yet a mesh references one");
|
assertNotNull(materials, "There is no material defined yet a mesh references one");
|
||||||
|
|
||||||
JsonObject matData = materials.get(materialIndex).getAsJsonObject();
|
JsonObject matData = materials.get(materialIndex).getAsJsonObject();
|
||||||
@ -571,7 +590,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
return adapter.getMaterial();
|
return adapter.getMaterial();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readCameras() {
|
public void readCameras() throws IOException {
|
||||||
if (cameras == null) {
|
if (cameras == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -616,12 +635,12 @@ public class GltfLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Texture2D readTexture(JsonObject texture) {
|
public Texture2D readTexture(JsonObject texture) throws IOException {
|
||||||
return readTexture(texture, false);
|
return readTexture(texture, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Texture2D readTexture(JsonObject texture, boolean flip) {
|
public Texture2D readTexture(JsonObject texture, boolean flip) throws IOException {
|
||||||
if (texture == null) {
|
if (texture == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -646,18 +665,24 @@ public class GltfLoader implements AssetLoader {
|
|||||||
return texture2d;
|
return texture2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Texture2D readImage(int sourceIndex, boolean flip) {
|
public Texture2D readImage(int sourceIndex, boolean flip) throws IOException {
|
||||||
if (images == null) {
|
if (images == null) {
|
||||||
throw new AssetLoadException("No image defined");
|
throw new AssetLoadException("No image defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject image = images.get(sourceIndex).getAsJsonObject();
|
JsonObject image = images.get(sourceIndex).getAsJsonObject();
|
||||||
String uri = getAsString(image, "uri");
|
String uri = getAsString(image, "uri");
|
||||||
|
Integer bufferView = getAsInteger(image, "bufferView");
|
||||||
|
String mimeType = getAsString(image, "mimeType");
|
||||||
Texture2D result;
|
Texture2D result;
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
//Image is embed in a buffer not supported yet
|
assertNotNull(bufferView, "Image " + sourceIndex + " should either have an uri or a bufferView");
|
||||||
//TODO support images embed in a buffer
|
assertNotNull(mimeType, "Image " + sourceIndex + " should have a mimeType");
|
||||||
throw new AssetLoadException("Images embed in a buffer are not supported yet");
|
byte[] data = (byte[]) readBuffer(bufferView, 0, -1, null, 1, VertexBuffer.Format.Byte);
|
||||||
|
String extension = mimeType.split("/")[1];
|
||||||
|
TextureKey key = new TextureKey("image" + sourceIndex + "." + extension, flip);
|
||||||
|
result = (Texture2D) info.getManager().loadAssetFromStream(key, new ByteArrayInputStream(data));
|
||||||
|
|
||||||
} else if (uri.startsWith("data:")) {
|
} else if (uri.startsWith("data:")) {
|
||||||
//base64 encoded image
|
//base64 encoded image
|
||||||
String[] uriInfo = uri.split(",");
|
String[] uriInfo = uri.split(",");
|
||||||
@ -672,11 +697,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
Texture tex = info.getManager().loadTexture(key);
|
Texture tex = info.getManager().loadTexture(key);
|
||||||
result = (Texture2D) tex;
|
result = (Texture2D) tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = customContentManager.readExtensionAndExtras("image", image, result);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readAnimation(int animationIndex) throws IOException {
|
public void readAnimation(int animationIndex) throws IOException {
|
||||||
@ -844,7 +865,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Texture2D readSampler(int samplerIndex, Texture2D texture) {
|
public Texture2D readSampler(int samplerIndex, Texture2D texture) throws IOException {
|
||||||
if (samplers == null) {
|
if (samplers == null) {
|
||||||
throw new AssetLoadException("No samplers defined");
|
throw new AssetLoadException("No samplers defined");
|
||||||
}
|
}
|
||||||
@ -1225,6 +1246,10 @@ public class GltfLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TextureData {
|
||||||
|
byte[] data;
|
||||||
|
}
|
||||||
|
|
||||||
private interface Populator<T> {
|
private interface Populator<T> {
|
||||||
T populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException;
|
T populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException;
|
||||||
}
|
}
|
||||||
@ -1380,5 +1405,6 @@ public class GltfLoader implements AssetLoader {
|
|||||||
return new SkinBuffers(data, format.getComponentSize());
|
return new SkinBuffers(data, format.getComponentSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,10 +253,11 @@ public class GltfUtils {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LittleEndien stream = getStream(source);
|
LittleEndien stream = getStream(source);
|
||||||
if (store instanceof short[]) {
|
if (store instanceof byte[]) {
|
||||||
|
populateByteArray((byte[]) store, stream, count, byteOffset, byteStride, numComponents, format);
|
||||||
|
} else if (store instanceof short[]) {
|
||||||
populateShortArray((short[]) store, stream, count, byteOffset, byteStride, numComponents, format);
|
populateShortArray((short[]) store, stream, count, byteOffset, byteStride, numComponents, format);
|
||||||
} else
|
} else if (store instanceof float[]) {
|
||||||
if (store instanceof float[]) {
|
|
||||||
populateFloatArray((float[]) store, stream, count, byteOffset, byteStride, numComponents, format);
|
populateFloatArray((float[]) store, stream, count, byteOffset, byteStride, numComponents, format);
|
||||||
} else if (store instanceof Vector3f[]) {
|
} else if (store instanceof Vector3f[]) {
|
||||||
populateVector3fArray((Vector3f[]) store, stream, count, byteOffset, byteStride, numComponents, format);
|
populateVector3fArray((Vector3f[]) store, stream, count, byteOffset, byteStride, numComponents, format);
|
||||||
@ -367,6 +368,26 @@ public class GltfUtils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void populateByteArray(byte[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException {
|
||||||
|
int componentSize = format.getComponentSize();
|
||||||
|
int index = byteOffset;
|
||||||
|
int dataLength = componentSize * numComponents;
|
||||||
|
int stride = Math.max(dataLength, byteStride);
|
||||||
|
int end = count * stride + byteOffset;
|
||||||
|
stream.skipBytes(byteOffset);
|
||||||
|
int arrayIndex = 0;
|
||||||
|
while (index < end) {
|
||||||
|
for (int i = 0; i < numComponents; i++) {
|
||||||
|
array[arrayIndex] = stream.readByte();
|
||||||
|
arrayIndex++;
|
||||||
|
}
|
||||||
|
if (dataLength < stride) {
|
||||||
|
stream.skipBytes(stride - dataLength);
|
||||||
|
}
|
||||||
|
index += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void populateShortArray(short[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException {
|
private static void populateShortArray(short[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException {
|
||||||
int componentSize = format.getComponentSize();
|
int componentSize = format.getComponentSize();
|
||||||
int index = byteOffset;
|
int index = byteOffset;
|
||||||
|
@ -3,6 +3,8 @@ package com.jme3.scene.plugins.gltf;
|
|||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor;
|
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor;
|
||||||
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat;
|
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat;
|
||||||
|
|
||||||
@ -15,7 +17,7 @@ public class PBRSpecGlossExtensionLoader implements ExtensionLoader {
|
|||||||
private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter();
|
private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) {
|
public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException {
|
||||||
MaterialAdapter adapter = materialAdapter;
|
MaterialAdapter adapter = materialAdapter;
|
||||||
AssetKey key = loader.getInfo().getKey();
|
AssetKey key = loader.getInfo().getKey();
|
||||||
//check for a custom adapter for spec/gloss pipeline
|
//check for a custom adapter for spec/gloss pipeline
|
||||||
|
Loading…
x
Reference in New Issue
Block a user