Merge remote-tracking branch 'origin/master' into in-pass-shadows
This commit is contained in:
commit
59c85d58c8
@ -1980,7 +1980,13 @@ public final class GLRenderer implements Renderer {
|
||||
@SuppressWarnings("fallthrough")
|
||||
private void setupTextureParams(int unit, Texture tex) {
|
||||
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;
|
||||
if (image != null) {
|
||||
@ -2183,45 +2189,46 @@ public final class GLRenderer implements Renderer {
|
||||
int target = convertTextureType(type, img.getMultiSamples(), -1);
|
||||
bindTextureAndUnit(target, img, unit);
|
||||
|
||||
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
|
||||
// Image does not have mipmaps, but they are required.
|
||||
// Generate from base level.
|
||||
int imageSamples = img.getMultiSamples();
|
||||
|
||||
if (!caps.contains(Caps.FrameBuffer) && gl2 != null) {
|
||||
gl2.glTexParameteri(target, GL2.GL_GENERATE_MIPMAP, GL.GL_TRUE);
|
||||
img.setMipmapsGenerated(true);
|
||||
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 {
|
||||
// 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);
|
||||
// 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 {
|
||||
// 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);
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
int imageSamples = img.getMultiSamples();
|
||||
if (imageSamples > 1) {
|
||||
if (img.getFormat().isDepthFormat()) {
|
||||
img.setMultiSamples(Math.min(limits.get(Limits.DepthTextureSamples), imageSamples));
|
||||
} else {
|
||||
img.setMultiSamples(Math.min(limits.get(Limits.ColorTextureSamples), imageSamples));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if graphics card doesn't support multisample textures
|
||||
if (!caps.contains(Caps.TextureMultisample)) {
|
||||
if (img.getMultiSamples() > 1) {
|
||||
throw new RendererException("Multisample textures are not supported by the video hardware");
|
||||
}
|
||||
scaleToPot = false;
|
||||
}
|
||||
|
||||
// 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 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.
|
||||
*
|
||||
* TODO normal or tangents or both looks a bit weird
|
||||
* <p>
|
||||
* TODO more automagic (batch when needed in the updateLogicalState)
|
||||
*
|
||||
* @author Nehon
|
||||
*/
|
||||
public class BatchNode extends GeometryGroupNode {
|
||||
@ -108,7 +108,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
public void onMaterialChange(Geometry geom) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot set the material of a batched geometry, "
|
||||
+ "change the material of the parent BatchNode.");
|
||||
+ "change the material of the parent BatchNode.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -122,7 +122,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
setNeedsFullRebatch(true);
|
||||
}
|
||||
|
||||
protected Matrix4f getTransformMatrix(Geometry g){
|
||||
protected Matrix4f getTransformMatrix(Geometry g) {
|
||||
return g.cachedWorldMat;
|
||||
}
|
||||
|
||||
@ -133,35 +133,44 @@ public class BatchNode extends GeometryGroupNode {
|
||||
Mesh origMesh = bg.getMesh();
|
||||
|
||||
VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position);
|
||||
FloatBuffer posBuf = (FloatBuffer) pvb.getData();
|
||||
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);
|
||||
FloatBuffer oposBuf = (FloatBuffer) opvb.getData();
|
||||
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);
|
||||
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);
|
||||
nvb.updateData(normBuf);
|
||||
|
||||
if (nvb != null) {
|
||||
nvb.updateData(normBuf);
|
||||
}
|
||||
if (tvb != null) {
|
||||
tvb.updateData(tanBuf);
|
||||
}
|
||||
|
||||
batch.geometry.updateModelBound();
|
||||
}
|
||||
}
|
||||
|
||||
private FloatBuffer getFloatBuffer(VertexBuffer vb) {
|
||||
if (vb == null) {
|
||||
return null;
|
||||
}
|
||||
return (FloatBuffer) vb.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -234,7 +243,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
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.
|
||||
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.
|
||||
//init temp float arrays
|
||||
tmpFloat = new float[maxVertCount * 3];
|
||||
@ -257,6 +266,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
|
||||
/**
|
||||
* recursively visit the subgraph and unbatch geometries
|
||||
*
|
||||
* @param 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
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
*
|
||||
* @see #setMaterial(com.jme3.material.Material)
|
||||
*/
|
||||
public Material getMaterial() {
|
||||
@ -428,14 +437,6 @@ public class BatchNode extends GeometryGroupNode {
|
||||
+ " primitive types: " + 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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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) {
|
||||
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;
|
||||
@ -588,60 +543,76 @@ public class BatchNode extends GeometryGroupNode {
|
||||
int offset = start * 3;
|
||||
int tanOffset = start * 4;
|
||||
|
||||
|
||||
bindBufPos.rewind();
|
||||
bindBufNorm.rewind();
|
||||
bindBufTangents.rewind();
|
||||
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 tanIndex = 0;
|
||||
int index1, index2, tanIndex1, tanIndex2;
|
||||
|
||||
while (index < length) {
|
||||
index1 = index + 1;
|
||||
index2 = index + 2;
|
||||
|
||||
pos.x = tmpFloat[index];
|
||||
norm.x = tmpFloatN[index++];
|
||||
pos.y = tmpFloat[index];
|
||||
norm.y = tmpFloatN[index++];
|
||||
pos.z = tmpFloat[index];
|
||||
norm.z = tmpFloatN[index];
|
||||
|
||||
tan.x = tmpFloatT[tanIndex++];
|
||||
tan.y = tmpFloatT[tanIndex++];
|
||||
tan.z = tmpFloatT[tanIndex++];
|
||||
|
||||
pos.y = tmpFloat[index1];
|
||||
pos.z = tmpFloat[index2];
|
||||
transform.mult(pos, pos);
|
||||
transform.multNormal(norm, norm);
|
||||
transform.multNormal(tan, tan);
|
||||
|
||||
index -= 2;
|
||||
tanIndex -= 3;
|
||||
|
||||
tmpFloat[index] = pos.x;
|
||||
tmpFloatN[index++] = norm.x;
|
||||
tmpFloat[index] = pos.y;
|
||||
tmpFloatN[index++] = norm.y;
|
||||
tmpFloat[index] = pos.z;
|
||||
tmpFloatN[index++] = norm.z;
|
||||
tmpFloat[index1] = pos.y;
|
||||
tmpFloat[index2] = pos.z;
|
||||
|
||||
tmpFloatT[tanIndex++] = tan.x;
|
||||
tmpFloatT[tanIndex++] = tan.y;
|
||||
tmpFloatT[tanIndex++] = tan.z;
|
||||
if (bindBufNorm != null) {
|
||||
norm.x = tmpFloatN[index];
|
||||
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)
|
||||
tanIndex++;
|
||||
index += 3;
|
||||
|
||||
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();
|
||||
|
||||
//using bulk put as it's faster
|
||||
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);
|
||||
bufTangents.position(tanOffset);
|
||||
//using bulk put as it's faster
|
||||
bufTangents.put(tmpFloatT, 0, tanLength);
|
||||
|
||||
if (bindBufNorm != null) {
|
||||
bufNorm.position(offset);
|
||||
bufNorm.put(tmpFloatN, 0, length);
|
||||
}
|
||||
|
||||
if (bindBufTangents != null) {
|
||||
bufTangents.position(tanOffset);
|
||||
bufTangents.put(tmpFloatT, 0, tanLength);
|
||||
}
|
||||
}
|
||||
|
||||
private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf, int componentSize) {
|
||||
@ -653,11 +624,11 @@ public class BatchNode extends GeometryGroupNode {
|
||||
offset *= componentSize;
|
||||
|
||||
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.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 + 2, pos.z);
|
||||
}
|
||||
@ -667,6 +638,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
protected class Batch implements JmeCloneable {
|
||||
/**
|
||||
* update the batchesByGeom map for this batch with the given List of geometries
|
||||
*
|
||||
* @param list
|
||||
*/
|
||||
void updateGeomList(List<Geometry> list) {
|
||||
@ -676,6 +648,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Geometry geometry;
|
||||
|
||||
public final Geometry getGeometry() {
|
||||
@ -685,14 +658,14 @@ public class BatchNode extends GeometryGroupNode {
|
||||
@Override
|
||||
public Batch jmeClone() {
|
||||
try {
|
||||
return (Batch)super.clone();
|
||||
return (Batch) super.clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cloneFields( Cloner cloner, Object original ) {
|
||||
public void cloneFields(Cloner cloner, Object original) {
|
||||
this.geometry = cloner.clone(geometry);
|
||||
}
|
||||
|
||||
@ -704,11 +677,11 @@ public class BatchNode extends GeometryGroupNode {
|
||||
|
||||
@Override
|
||||
public Node clone(boolean cloneMaterials) {
|
||||
BatchNode clone = (BatchNode)super.clone(cloneMaterials);
|
||||
if ( batches.size() > 0) {
|
||||
for ( Batch b : batches ) {
|
||||
for ( int i =0; i < clone.children.size(); i++ ) {
|
||||
if ( clone.children.get(i).getName().equals(b.geometry.getName())) {
|
||||
BatchNode clone = (BatchNode) super.clone(cloneMaterials);
|
||||
if (batches.size() > 0) {
|
||||
for (Batch b : batches) {
|
||||
for (int i = 0; i < clone.children.size(); i++) {
|
||||
if (clone.children.get(i).getName().equals(b.geometry.getName())) {
|
||||
clone.children.remove(i);
|
||||
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
|
||||
public void cloneFields( Cloner cloner, Object original ) {
|
||||
public void cloneFields(Cloner cloner, Object original) {
|
||||
super.cloneFields(cloner, original);
|
||||
|
||||
this.batches = cloner.clone(batches);
|
||||
@ -736,7 +709,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
|
||||
|
||||
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()));
|
||||
}
|
||||
this.batchesByGeom = newBatchesByGeom;
|
||||
@ -745,7 +718,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
@Override
|
||||
public int collideWith(Collidable other, CollisionResults results) {
|
||||
int total = 0;
|
||||
for (Spatial child : children.getArray()){
|
||||
for (Spatial child : children.getArray()) {
|
||||
if (!isBatch(child)) {
|
||||
total += child.collideWith(other, results);
|
||||
}
|
||||
|
@ -1013,6 +1013,18 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
BoundingVolume worldBound,
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ MaterialDef Sky Plane {
|
||||
WorldParameters {
|
||||
ViewMatrix
|
||||
ProjectionMatrix
|
||||
WorldMatrix
|
||||
WorldMatrixInverse
|
||||
}
|
||||
|
||||
Defines {
|
||||
|
@ -1,7 +1,7 @@
|
||||
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||
uniform mat4 g_ViewMatrix;
|
||||
uniform mat4 g_ProjectionMatrix;
|
||||
uniform mat4 g_WorldMatrix;
|
||||
uniform mat4 g_WorldMatrixInverse;
|
||||
|
||||
uniform vec3 m_NormalScale;
|
||||
|
||||
@ -22,5 +22,5 @@ void main(){
|
||||
gl_Position = g_ProjectionMatrix * pos;
|
||||
|
||||
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.jme3.asset.AssetLoadException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
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);
|
||||
output = readExtras(name, el, 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");
|
||||
if (extensions == null) {
|
||||
return input;
|
||||
|
@ -2,6 +2,8 @@ package com.jme3.scene.plugins.gltf;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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
|
||||
public Object load(AssetInfo assetInfo) throws IOException {
|
||||
return loadFromStream(assetInfo, assetInfo.openStream());
|
||||
}
|
||||
|
||||
|
||||
protected Object loadFromStream(AssetInfo assetInfo, InputStream stream) throws IOException {
|
||||
try {
|
||||
dataCache.clear();
|
||||
info = assetInfo;
|
||||
@ -87,7 +92,7 @@ public class GltfLoader implements AssetLoader {
|
||||
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();
|
||||
String generator = getAsString(asset, "generator");
|
||||
@ -455,7 +460,7 @@ public class GltfLoader implements AssetLoader {
|
||||
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();
|
||||
Integer bufferIndex = getAsInteger(bufferView, "buffer");
|
||||
@ -473,8 +478,17 @@ public class GltfLoader implements AssetLoader {
|
||||
|
||||
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);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
public byte[] readData(int bufferIndex) throws IOException {
|
||||
@ -489,6 +503,17 @@ public class GltfLoader implements AssetLoader {
|
||||
if (data != null) {
|
||||
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.startsWith("data:")) {
|
||||
//base 64 embed data
|
||||
@ -505,19 +530,13 @@ public class GltfLoader implements AssetLoader {
|
||||
input.read(data);
|
||||
}
|
||||
} else {
|
||||
//no URI we are in a binary file so the data is in the 2nd chunk
|
||||
//TODO handle binary GLTF (GLB)
|
||||
throw new AssetLoadException("Binary gltf is not supported yet");
|
||||
//no URI this should not happen in a gltf file, only in glb files.
|
||||
throw new AssetLoadException("Buffer " + bufferIndex + " has no uri");
|
||||
}
|
||||
|
||||
data = customContentManager.readExtensionAndExtras("buffer", buffer, data);
|
||||
|
||||
addToCache("buffers", bufferIndex, data, buffers.size());
|
||||
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");
|
||||
|
||||
JsonObject matData = materials.get(materialIndex).getAsJsonObject();
|
||||
@ -571,7 +590,7 @@ public class GltfLoader implements AssetLoader {
|
||||
return adapter.getMaterial();
|
||||
}
|
||||
|
||||
public void readCameras() {
|
||||
public void readCameras() throws IOException {
|
||||
if (cameras == null) {
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
public Texture2D readTexture(JsonObject texture, boolean flip) {
|
||||
public Texture2D readTexture(JsonObject texture, boolean flip) throws IOException {
|
||||
if (texture == null) {
|
||||
return null;
|
||||
}
|
||||
@ -646,18 +665,24 @@ public class GltfLoader implements AssetLoader {
|
||||
return texture2d;
|
||||
}
|
||||
|
||||
public Texture2D readImage(int sourceIndex, boolean flip) {
|
||||
public Texture2D readImage(int sourceIndex, boolean flip) throws IOException {
|
||||
if (images == null) {
|
||||
throw new AssetLoadException("No image defined");
|
||||
}
|
||||
|
||||
JsonObject image = images.get(sourceIndex).getAsJsonObject();
|
||||
String uri = getAsString(image, "uri");
|
||||
Integer bufferView = getAsInteger(image, "bufferView");
|
||||
String mimeType = getAsString(image, "mimeType");
|
||||
Texture2D result;
|
||||
if (uri == null) {
|
||||
//Image is embed in a buffer not supported yet
|
||||
//TODO support images embed in a buffer
|
||||
throw new AssetLoadException("Images embed in a buffer are not supported yet");
|
||||
assertNotNull(bufferView, "Image " + sourceIndex + " should either have an uri or a bufferView");
|
||||
assertNotNull(mimeType, "Image " + sourceIndex + " should have a mimeType");
|
||||
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:")) {
|
||||
//base64 encoded image
|
||||
String[] uriInfo = uri.split(",");
|
||||
@ -672,11 +697,7 @@ public class GltfLoader implements AssetLoader {
|
||||
Texture tex = info.getManager().loadTexture(key);
|
||||
result = (Texture2D) tex;
|
||||
}
|
||||
|
||||
result = customContentManager.readExtensionAndExtras("image", image, result);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
throw new AssetLoadException("No samplers defined");
|
||||
}
|
||||
@ -1225,6 +1246,10 @@ public class GltfLoader implements AssetLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private class TextureData {
|
||||
byte[] data;
|
||||
}
|
||||
|
||||
private interface Populator<T> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -253,10 +253,11 @@ public class GltfUtils {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
} else
|
||||
if (store instanceof float[]) {
|
||||
} else if (store instanceof float[]) {
|
||||
populateFloatArray((float[]) store, stream, count, byteOffset, byteStride, numComponents, format);
|
||||
} else if (store instanceof Vector3f[]) {
|
||||
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 {
|
||||
int componentSize = format.getComponentSize();
|
||||
int index = byteOffset;
|
||||
|
@ -3,6 +3,8 @@ package com.jme3.scene.plugins.gltf;
|
||||
import com.google.gson.JsonElement;
|
||||
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.getAsFloat;
|
||||
|
||||
@ -15,7 +17,7 @@ public class PBRSpecGlossExtensionLoader implements ExtensionLoader {
|
||||
private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter();
|
||||
|
||||
@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;
|
||||
AssetKey key = loader.getInfo().getKey();
|
||||
//check for a custom adapter for spec/gloss pipeline
|
||||
|
Loading…
x
Reference in New Issue
Block a user