Skip to content

Commit 3f5d840

Browse files
committed
OBJLoader enhancement: named groups support
1 parent 8aa50e9 commit 3f5d840

10 files changed

Lines changed: 354 additions & 29 deletions

File tree

jme3-core/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ sourceSets {
1717
}
1818
}
1919

20+
dependencies {
21+
testCompile project(':jme3-testdata')
22+
}
23+
2024
task updateVersionPropertiesFile {
2125
def versionFile = file('src/main/resources/com/jme3/system/version.properties')
2226
def versionFileText = "# THIS IS AN AUTO-GENERATED FILE..\n" +

jme3-core/src/plugins/java/com/jme3/scene/plugins/MTLLoader.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ protected void createMaterial(){
152152
material.setFloat("AlphaDiscardThreshold", 0.01f);
153153
}
154154

155+
material.setName(matName);
155156
matList.put(matName, material);
156157
}
157158

jme3-core/src/plugins/java/com/jme3/scene/plugins/OBJLoader.java

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,9 @@ public final class OBJLoader implements AssetLoader {
6666
protected final ArrayList<Vector3f> verts = new ArrayList<Vector3f>();
6767
protected final ArrayList<Vector2f> texCoords = new ArrayList<Vector2f>();
6868
protected final ArrayList<Vector3f> norms = new ArrayList<Vector3f>();
69-
70-
protected final ArrayList<Face> faces = new ArrayList<Face>();
71-
protected final HashMap<String, ArrayList<Face>> matFaces = new HashMap<String, ArrayList<Face>>();
72-
69+
70+
private final ArrayList<Group> groups = new ArrayList<Group>();
71+
7372
protected String currentMatName;
7473
protected String currentObjectName;
7574

@@ -87,6 +86,16 @@ public final class OBJLoader implements AssetLoader {
8786
protected String objName;
8887
protected Node objNode;
8988

89+
private static class Group {
90+
private String name;
91+
private final ArrayList<Face> faces = new ArrayList<Face>();
92+
private final HashMap<String, ArrayList<Face>> matFaces = new HashMap<String, ArrayList<Face>>();
93+
94+
public Group(final String name) {
95+
this.name = name;
96+
}
97+
}
98+
9099
protected static class Vertex {
91100

92101
Vector3f v;
@@ -164,8 +173,7 @@ public void reset(){
164173
verts.clear();
165174
texCoords.clear();
166175
norms.clear();
167-
faces.clear();
168-
matFaces.clear();
176+
groups.clear();
169177

170178
vertIndexMap.clear();
171179
indexVertMap.clear();
@@ -289,10 +297,17 @@ protected void readFace(){
289297
f.verticies[i] = vertList.get(i);
290298
}
291299

292-
if (matList != null && matFaces.containsKey(currentMatName)){
293-
matFaces.get(currentMatName).add(f);
300+
Group group = groups.get(groups.size() - 1);
301+
302+
if (currentMatName != null && matList != null && matList.containsKey(currentMatName)){
303+
ArrayList<Face> matFaces = group.matFaces.get(currentMatName);
304+
if (matFaces == null) {
305+
matFaces = new ArrayList<Face>();
306+
group.matFaces.put(currentMatName, matFaces);
307+
}
308+
matFaces.add(f);
294309
}else{
295-
faces.add(f); // faces that belong to the default material
310+
group.faces.add(f); // faces that belong to the default material
296311
}
297312
}
298313

@@ -337,13 +352,6 @@ protected void loadMtlLib(String name) throws IOException{
337352
} catch (AssetNotFoundException ex){
338353
logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{name, key});
339354
}
340-
341-
if (matList != null){
342-
// create face lists for every material
343-
for (String matName : matList.keySet()){
344-
matFaces.put(matName, new ArrayList<Face>());
345-
}
346-
}
347355
}
348356

349357
protected boolean nextStatement(){
@@ -387,8 +395,10 @@ protected boolean readLine() throws IOException{
387395
// specify MTL lib to use for this OBJ file
388396
String mtllib = scan.nextLine().trim();
389397
loadMtlLib(mtllib);
390-
}else if (cmd.equals("s") || cmd.equals("g")){
398+
}else if (cmd.equals("s")) {
391399
return nextStatement();
400+
}else if (cmd.equals("g")) {
401+
groups.add(new Group(scan.nextLine().trim()));
392402
}else{
393403
// skip entire command until next line
394404
logger.log(Level.WARNING, "Unknown statement in OBJ! {0}", cmd);
@@ -566,10 +576,12 @@ public Object load(AssetInfo info) throws IOException{
566576

567577
objNode = new Node(objName + "-objnode");
568578

579+
groups.add(new Group("DefaultGroup"));
580+
569581
if (!(info.getKey() instanceof ModelKey))
570582
throw new IllegalArgumentException("Model assets must be loaded using a ModelKey");
571583

572-
InputStream in = null;
584+
InputStream in = null;
573585
try {
574586
in = info.openStream();
575587

@@ -583,23 +595,33 @@ public Object load(AssetInfo info) throws IOException{
583595
}
584596
}
585597

586-
if (matFaces.size() > 0){
587-
for (Entry<String, ArrayList<Face>> entry : matFaces.entrySet()){
588-
ArrayList<Face> materialFaces = entry.getValue();
589-
if (materialFaces.size() > 0){
590-
Geometry geom = createGeometry(materialFaces, entry.getKey());
591-
objNode.attachChild(geom);
598+
for (final Group group : groups) {
599+
final Node groupNode = new Node(group.name);
600+
if (group.matFaces.size() > 0) {
601+
for (Entry<String, ArrayList<Face>> entry : group.matFaces.entrySet()){
602+
ArrayList<Face> materialFaces = entry.getValue();
603+
if (materialFaces.size() > 0){
604+
Geometry geom = createGeometry(materialFaces, entry.getKey());
605+
groupNode.attachChild(geom);
606+
}
592607
}
608+
} else if (group.faces.size() > 0) {
609+
// generate final geometry
610+
Geometry geom = createGeometry(group.faces, null);
611+
groupNode.attachChild(geom);
612+
}
613+
if (groupNode.getQuantity() == 1) {
614+
Spatial geom = groupNode.getChild(0);
615+
geom.setName(groupNode.getName());
616+
objNode.attachChild(geom);
617+
} else if (groupNode.getQuantity() > 1) {
618+
objNode.attachChild(groupNode);
593619
}
594-
}else if (faces.size() > 0){
595-
// generate final geometry
596-
Geometry geom = createGeometry(faces, null);
597-
objNode.attachChild(geom);
598620
}
599621

600622
if (objNode.getQuantity() == 1)
601623
// only 1 geometry, so no need to send node
602-
return objNode.getChild(0);
624+
return objNode.getChild(0);
603625
else
604626
return objNode;
605627
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.jme3.scene.plugins;
2+
3+
import com.jme3.asset.AssetInfo;
4+
import com.jme3.asset.AssetLoader;
5+
import com.jme3.asset.AssetManager;
6+
import com.jme3.asset.ModelKey;
7+
import com.jme3.scene.Geometry;
8+
import com.jme3.scene.Node;
9+
import com.jme3.system.TestUtil;
10+
import com.jme3.texture.Image;
11+
import org.junit.Before;
12+
import org.junit.Test;
13+
14+
import java.io.IOException;
15+
16+
import static org.junit.Assert.assertEquals;
17+
18+
public class OBJLoaderTest {
19+
private AssetManager assetManager;
20+
21+
@Before
22+
public void init() {
23+
assetManager = TestUtil.createAssetManager();
24+
// texture loaders are outside of core, so creating stub
25+
assetManager.registerLoader(PngLoaderStub.class, "png");
26+
27+
}
28+
29+
@Test
30+
public void testHappyPath() {
31+
Node scene = (Node) assetManager.loadModel(new ModelKey("OBJLoaderTest/TwoChairs.obj"));
32+
assertEquals(3, scene.getQuantity());
33+
{
34+
Geometry pillow2 = (Geometry) scene.getChild(0);
35+
assertEquals("Pillow 2", pillow2.getName());
36+
assertEquals("dot_red", pillow2.getMaterial().getName());
37+
38+
Node chair1 = (Node) scene.getChild(1);
39+
assertEquals("Chair 1", chair1.getName());
40+
assertEquals(2, chair1.getQuantity());
41+
{
42+
Geometry chairMat1 = (Geometry) chair1.getChild(0);
43+
assertEquals("dot_green", chairMat1.getMaterial().getName());
44+
45+
Geometry chairMat2 = (Geometry) chair1.getChild(1);
46+
assertEquals("dot_purple", chairMat2.getMaterial().getName());
47+
}
48+
Geometry chair2 = (Geometry) scene.getChild(2);
49+
assertEquals("Chair 2", chair2.getName());
50+
assertEquals("dot_purple", chair2.getMaterial().getName());
51+
}
52+
}
53+
54+
public static class PngLoaderStub implements AssetLoader {
55+
@Override
56+
public Object load(final AssetInfo assetInfo) {
57+
return new Image();
58+
}
59+
}
60+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package jme3test.model;
2+
3+
import com.jme3.app.SimpleApplication;
4+
import com.jme3.asset.ModelKey;
5+
import com.jme3.collision.CollisionResult;
6+
import com.jme3.collision.CollisionResults;
7+
import com.jme3.font.BitmapFont;
8+
import com.jme3.font.BitmapText;
9+
import com.jme3.font.Rectangle;
10+
import com.jme3.light.AmbientLight;
11+
import com.jme3.math.ColorRGBA;
12+
import com.jme3.math.Ray;
13+
import com.jme3.math.Vector3f;
14+
import com.jme3.scene.Spatial;
15+
16+
public class TestObjGroupsLoading extends SimpleApplication {
17+
18+
public static void main(String[] args) {
19+
TestObjGroupsLoading app = new TestObjGroupsLoading();
20+
app.start();
21+
}
22+
23+
private BitmapText pointerDisplay;
24+
25+
@Override
26+
public void simpleInitApp() {
27+
28+
// load scene with objects organized in 3 groups: Chair 1, Chair 2, Pillow 2
29+
Spatial scene = assetManager.loadModel(new ModelKey("OBJLoaderTest/TwoChairs.obj"));
30+
// add light to make it visible
31+
scene.addLight(new AmbientLight(ColorRGBA.White));
32+
// attach scene to the root
33+
rootNode.attachChild(scene);
34+
35+
// configure camera for best scene viewing
36+
cam.setLocation(new Vector3f(0, 2, -4));
37+
cam.lookAt(new Vector3f(0, 2, 0), Vector3f.UNIT_Y);
38+
flyCam.setMoveSpeed(10);
39+
40+
// create display to indicate pointed geometry name
41+
pointerDisplay = new BitmapText(guiFont);
42+
pointerDisplay.setBox(new Rectangle(0, settings.getHeight(), settings.getWidth(), settings.getHeight()));
43+
pointerDisplay.setAlignment(BitmapFont.Align.Center);
44+
pointerDisplay.setVerticalAlignment(BitmapFont.VAlign.Center);
45+
guiNode.attachChild(pointerDisplay);
46+
}
47+
48+
@Override
49+
public void simpleUpdate(final float tpf) {
50+
51+
// ray to the center of the screen from the camera
52+
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
53+
54+
// find object at the center of the screen
55+
56+
final CollisionResults results = new CollisionResults();
57+
rootNode.collideWith(ray, results);
58+
59+
CollisionResult result = results.getClosestCollision();
60+
if (result == null) {
61+
pointerDisplay.setText("");
62+
} else {
63+
// display pointed geometry and it's parents names
64+
StringBuilder sb = new StringBuilder();
65+
for (Spatial node = result.getGeometry(); node != null; node = node.getParent()) {
66+
if (sb.length() > 0) {
67+
sb.append(" < ");
68+
}
69+
sb.append(node.getName());
70+
}
71+
pointerDisplay.setText(sb);
72+
}
73+
}
74+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
newmtl dot_purple
2+
map_Kd -o 0 0 -s 1 1 dot_purple.png
3+
Kd 1 1 1
4+
d 1
5+
6+
newmtl dot_green
7+
map_Kd -o 0 0 -s 1 1 dot_green.png
8+
Kd 1 1 1
9+
d 1
10+
11+
newmtl dot_red
12+
map_Kd -o 0 0 -s 1 1 dot_red.png
13+
Kd 1 1 1
14+
d 1
15+

0 commit comments

Comments
 (0)