If I am not mistaken, ShadowedRenderPass requires occluders to be closed meshes. Closed as in no holes. As in all edges are shared by no less than two triangles. Try to use a Quad as occluder in the simple scene and see if you get the same error.
There could very well be bugs as well though, like stencil values that are invalid for your card or something like that. We are not doing shadowing right now (yet) so I can't say with confidence that that code is heavily tested under lots of situations.
The quads are casting shadows successfully. I think I can get around it… If I use a sphere as an occluder, it doesn't have to exist in the scene right? So I could just represent my high poly model as a sphere as far as shadows go and call it a day.
Do you create any terrain?
I experienced a similar crash when I tested the ShadowRenderPass class and created terrain. Below is my stack trace; i think it's identical to yours.
Oct 27, 2007 2:33:17 PM com.jmex.game.DefaultUncaughtExceptionHandler uncaughtException
SEVERE: Main game loop broken by uncaught exception
org.lwjgl.opengl.OpenGLException: Invalid value (1281)
at org.lwjgl.opengl.Util.checkGLError(Util.java:53)
at org.lwjgl.opengl.Display.swapBuffers(Display.java:591)
at org.lwjgl.opengl.Display.update(Display.java:609)
at com.jme.renderer.lwjgl.LWJGLRenderer.displayBackBuffer(LWJGLRenderer.java:518)
at com.jmex.game.StandardGame.run(StandardGame.java:190)
at java.lang.Thread.run(Thread.java:619)
This piece of code caused the error.
void createHills() {
FaultFractalHeightMap heightMap = new FaultFractalHeightMap(257, 32, 0,
255, 0.55f);
Vector3f terrainScale = new Vector3f(1, 0.01f, 1);
page = new TerrainPage("Terrain", 33, heightMap.getSize(),
terrainScale, heightMap.getHeightMap(), false);
page.setDetailTexture(1, 16);
rootNode.attachChild(page);
...
The culprit was: Vector3f terrainScale = new Vector3f(1, 0.01f, 1);
If I specify e.g. Vector3f(10, 0.01f, 10); instead, the error is gone! In fact, as long as the X and Z component of the terrain scale vector are >= 8 or something, all is fine --- but when e.g. X is <= 7, OpenGL crash (with the stack trace included above).
It was driving me nuts for a while, until I did a diff between my code and jME's example shadow code, and noticed that I used another terrain scale. That was the only difference. It must be virtually impossible to identify this error, unless you are lucky and can run a diff between your code and some other almost identical code that doesn't have this problem.
I guess this has something to do with the terrain generated being too sharp or edgy in some way that jME's shadow module doesn't handle properly, and this is a jME bug?
This seems to be an issue with LWJGL, try updating the code from CVS, and see if it corrects it.
I have a hard time seeing how this would be a LWJGL issue. The change of that parameter changes some behavior in FaultFractalHeightMap or TerrainPage which causes it to make an opengl call with an invalid value.
Sorry, I just assumed (apparently wrongly) that the errors concerning invalid values meant there was an internal error in LWJGL… my mistake.
Hello,
I am just struggeling with the same issue. The ShadowedRenderPass crashes when a occluder's mesh is not closed.
I use downloaded free models so I cannot easily change the meshes easily.
Two questions:
- How difficult is changing ShadowedRenderPass so that it can deal with non-closed meshes?
- Is there any workarround? I think of some little converter, that takes meshes and closes them by adding triangles on the back sides of the unclosed meshes. So in the worst case the number of triangles is doubled, but it at least works untill the ShadowedRenderPass supports non-closed occluders.
Any ideas?
Thanks in advance!
sven
I have just written a class to close the meshes of TriMesh objects. This is a workarround with performance drawbacks. But it works for me at the moment, so at least i get rid of the OpenGL errors.
Here is the code, perhaps it is of use to anyone:
public class TriMeshCloser {
private static final Logger logger = Logger.getLogger(TriMeshCloser.class
.getName());
public boolean closeMeshes(Spatial spatial) {
boolean result = false;
if (spatial instanceof Node) {
Node node = (Node) spatial;
for (int i = 0; i < node.getQuantity(); i++) {
result |= closeMeshes(node.getChild(i));
}
} else if (spatial instanceof TriMesh) {
TriMesh mesh = (TriMesh) spatial;
for (int i = 0; i < mesh.getBatchCount(); i++) {
GeomBatch gb = mesh.getBatch(i);
if (gb.isEnabled() && gb instanceof TriangleBatch) {
result |= closeOrCheckBatch((TriangleBatch) gb, false);
}
}
} else {
throw new RuntimeException(
"could not close meshes of node. only TriMesh and Node can be closed: "
+ spatial);
}
return result;
}
public boolean checkMeshes(Spatial spatial) {
if (spatial instanceof Node) {
Node node = (Node) spatial;
for (int i = 0; i < node.getQuantity(); i++) {
if (checkMeshes(node.getChild(i))) {
return true;
}
}
return false;
} else if (spatial instanceof TriMesh) {
TriMesh mesh = (TriMesh) spatial;
for (int i = 0; i < mesh.getBatchCount(); i++) {
GeomBatch gb = mesh.getBatch(i);
if (gb.isEnabled() && gb instanceof TriangleBatch) {
if (closeOrCheckBatch((TriangleBatch) gb, true)) {
return true;
}
}
}
return false;
} else {
throw new RuntimeException(
"could not close meshes of node. only TriMesh and Node can be closed: "
+ spatial);
}
}
private boolean closeOrCheckBatch(TriangleBatch batch, boolean justCheck) {
Vector3f[] vertices = BufferUtils.getVector3Array(batch.getVertexBuffer());
int triCount = batch.getTriangleCount();
int[][] triangles = new int[triCount][3];
for (int i = 0; i < triCount; i++) {
batch.getTriangle(i, triangles[i]);
}
// ecken suchen, die nur in einem dreieck vorkommen
int[] wieoft = new int[vertices.length];
int[] wo = new int[vertices.length];
for (int i = 0; i < triangles.length; i++) {
for (int j = 0; j < 3; j++) {
wieoft[triangles[i][j]]++;
wo[triangles[i][j]] = i;
}
}
int countEdges = 0;
for (int i = 0; i < vertices.length; i++) {
if (wieoft[i] == 1) {
countEdges++;
}
}
if (countEdges == 0) {
return false;
}
if (justCheck) {
return true;
}
ArrayList<int[]> newTriangles = new ArrayList<int[]>(countEdges/2);
for (int i = 0; i < vertices.length; i++) {
if (wieoft[i] != 1)
continue;
// hier haben wir die nicht abgeschlossenen Punkte
int[] triangle = triangles[wo[i]];
int[] newTriangle = new int[3];
// neues Dreieck definieren
newTriangle[0] = triangle[0];
newTriangle[1] = triangle[2];
newTriangle[2] = triangle[1];
// ...und gleich die Punkte kenntzeichnen, damit nicht weitere Dreiecke
// entstehen
for (int j = 0; j < 3; j++) {
wieoft[newTriangle[j]]++;
wo[newTriangle[j]] = i;
}
newTriangles.add(newTriangle);
logger.info("triangle added: (" + newTriangle[0] + "," + newTriangle[1]
+ "," + newTriangle[2] + ")");
}
// Alte und neue Dreiecke zu array zusammenfuegen
int[] allTriangles = new int[(triCount + newTriangles.size()) * 3];
for (int i = 0; i < triCount; i++) {
allTriangles[3 * i] = triangles[i][0];
allTriangles[3 * i + 1] = triangles[i][1];
allTriangles[3 * i + 2] = triangles[i][2];
}
for (int i = 0; i < newTriangles.size(); i++) {
allTriangles[3 * (triCount + i)] = newTriangles.get(i)[0];
allTriangles[3 * (triCount + i) + 1] = newTriangles.get(i)[1];
allTriangles[3 * (triCount + i) + 2] = newTriangles.get(i)[2];
}
batch.setIndexBuffer(BufferUtils.createIntBuffer(allTriangles));
return true;
}
}
Perhaps anybody can still help me with information on if this will be fixed soon.