Skip to content

Commit 67d3edf

Browse files
authored
solve issue #1919 (underflow while generating tangents) (#1920)
* jme-core: add a test for issue #1919 (underflow generating tangents) * MikktspaceTangentGenerator: solve #1919 (underflow generating tangents)
1 parent c5405ea commit 67d3edf

File tree

2 files changed

+213
-7
lines changed

2 files changed

+213
-7
lines changed

jme3-core/src/main/java/com/jme3/util/mikktspace/MikktspaceTangentGenerator.java

+33-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2021 jMonkeyEngine
2+
* Copyright (c) 2009-2023 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -112,13 +112,39 @@ public static void generate(Spatial s){
112112
for (Spatial child : n.getChildren()) {
113113
generate(child);
114114
}
115-
} else if (s instanceof Geometry){
116-
Geometry g = (Geometry)s;
117-
MikkTSpaceImpl context = new MikkTSpaceImpl(g.getMesh());
118-
if(!genTangSpaceDefault(context)){
119-
logger.log(Level.SEVERE, "Failed to generate tangents for geometry {0}", g.getName());
115+
116+
} else if (s instanceof Geometry) {
117+
Geometry g = (Geometry) s;
118+
Mesh mesh = g.getMesh();
119+
120+
Mesh.Mode mode = mesh.getMode();
121+
boolean hasTriangles;
122+
switch (mode) {
123+
case Points:
124+
case Lines:
125+
case LineStrip:
126+
case LineLoop:
127+
hasTriangles = false; // skip this mesh
128+
break;
129+
130+
case Triangles:
131+
case TriangleFan:
132+
case TriangleStrip:
133+
hasTriangles = true;
134+
break;
135+
136+
default:
137+
String message = "Tangent generation isn't implemented for mode=" + mode;
138+
throw new UnsupportedOperationException(message);
139+
}
140+
141+
if (hasTriangles) {
142+
MikkTSpaceImpl context = new MikkTSpaceImpl(mesh);
143+
if (!genTangSpaceDefault(context)) {
144+
logger.log(Level.SEVERE, "Failed to generate tangents for geometry {0}", g.getName());
145+
}
146+
TangentUtils.generateBindPoseTangentsIfNecessary(mesh);
120147
}
121-
TangentUtils.generateBindPoseTangentsIfNecessary(g.getMesh());
122148
}
123149
}
124150

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* Copyright (c) 2023 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.util;
33+
34+
import com.jme3.scene.Geometry;
35+
import com.jme3.scene.Mesh;
36+
import com.jme3.scene.VertexBuffer;
37+
import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
38+
import java.nio.FloatBuffer;
39+
import org.junit.Assert;
40+
import org.junit.Test;
41+
42+
/**
43+
* Verifies how MikktspaceTangentGenerator handles various mesh modes. This was
44+
* issue #1919 at GitHub.
45+
*
46+
* @author Stephen Gold
47+
*/
48+
public class TestIssue1919 {
49+
/**
50+
* The number of axes in a vector.
51+
*/
52+
private static final int numAxes = 3;
53+
54+
/**
55+
* Tests a Hybrid-mode mesh.
56+
*/
57+
@Test(expected = UnsupportedOperationException.class)
58+
public void testHybrid() {
59+
Geometry testGeometry = createGeometry(Mesh.Mode.Hybrid);
60+
MikktspaceTangentGenerator.generate(testGeometry);
61+
}
62+
63+
/**
64+
* Tests a LineLoop-mode mesh.
65+
*/
66+
@Test
67+
public void testLineLoop() {
68+
Geometry testGeometry = createGeometry(Mesh.Mode.LineLoop);
69+
MikktspaceTangentGenerator.generate(testGeometry);
70+
71+
Mesh mesh = testGeometry.getMesh();
72+
VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent);
73+
Assert.assertNull(tangents); /// skipped this mesh
74+
}
75+
76+
/**
77+
* Tests a LineStrip-mode mesh.
78+
*/
79+
@Test
80+
public void testLineStrip() {
81+
Geometry testGeometry = createGeometry(Mesh.Mode.LineStrip);
82+
MikktspaceTangentGenerator.generate(testGeometry);
83+
84+
Mesh mesh = testGeometry.getMesh();
85+
VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent);
86+
Assert.assertNull(tangents); /// skipped this mesh
87+
}
88+
89+
/**
90+
* Tests a Lines-mode mesh.
91+
*/
92+
@Test
93+
public void testLines() {
94+
Geometry testGeometry = createGeometry(Mesh.Mode.Lines);
95+
MikktspaceTangentGenerator.generate(testGeometry);
96+
97+
Mesh mesh = testGeometry.getMesh();
98+
VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent);
99+
Assert.assertNull(tangents); // skipped this mesh
100+
}
101+
102+
/**
103+
* Tests a Patch-mode mesh.
104+
*/
105+
@Test(expected = UnsupportedOperationException.class)
106+
public void testPatch() {
107+
Geometry testGeometry = createGeometry(Mesh.Mode.Patch);
108+
MikktspaceTangentGenerator.generate(testGeometry);
109+
}
110+
111+
/**
112+
* Tests a Points-mode mesh.
113+
*/
114+
@Test
115+
public void testPoints() {
116+
Geometry testGeometry = createGeometry(Mesh.Mode.Points);
117+
MikktspaceTangentGenerator.generate(testGeometry);
118+
119+
Mesh mesh = testGeometry.getMesh();
120+
VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent);
121+
Assert.assertNull(tangents); // skipped this mesh
122+
}
123+
124+
/**
125+
* Tests a Triangles-mode mesh.
126+
*/
127+
@Test
128+
public void testTriangles() {
129+
Geometry testGeometry = createGeometry(Mesh.Mode.Triangles);
130+
MikktspaceTangentGenerator.generate(testGeometry);
131+
132+
Mesh mesh = testGeometry.getMesh();
133+
VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent);
134+
Assert.assertNotNull(tangents); // generated tangents
135+
}
136+
137+
/**
138+
* Generates a geometry in the X-Z plane with the specified mesh mode.
139+
*
140+
* @param mode the desired mode (not null)
141+
* @return a new geometry
142+
*/
143+
private Geometry createGeometry(Mesh.Mode mode) {
144+
FloatBuffer normals = BufferUtils.createFloatBuffer(
145+
0f, 1f, 0f,
146+
0f, 1f, 0f,
147+
0f, 1f, 0f,
148+
0f, 1f, 0f,
149+
0f, 1f, 0f,
150+
0f, 1f, 0f
151+
);
152+
float uvDiameter = 5f;
153+
FloatBuffer uvs = BufferUtils.createFloatBuffer(
154+
uvDiameter, uvDiameter,
155+
0f, 0f,
156+
uvDiameter, 0f,
157+
uvDiameter, uvDiameter,
158+
0f, uvDiameter,
159+
0f, 0f
160+
);
161+
float posRadius = 500f;
162+
FloatBuffer positions = BufferUtils.createFloatBuffer(
163+
+posRadius, 0f, +posRadius,
164+
-posRadius, 0f, -posRadius,
165+
-posRadius, 0f, +posRadius,
166+
+posRadius, 0f, +posRadius,
167+
+posRadius, 0f, -posRadius,
168+
-posRadius, 0f, -posRadius
169+
);
170+
Mesh mesh = new Mesh();
171+
mesh.setMode(mode);
172+
mesh.setBuffer(VertexBuffer.Type.Normal, numAxes, normals);
173+
mesh.setBuffer(VertexBuffer.Type.Position, numAxes, positions);
174+
mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, uvs);
175+
mesh.updateBound();
176+
177+
Geometry result = new Geometry("testGeometry" + mode, mesh);
178+
return result;
179+
}
180+
}

0 commit comments

Comments
 (0)