OBJ model format has markup element to organize objects in groups (without nesting). Currently, this markup is ignored by jME’s OBJLoader - only shape and material is loaded. This PR implements loading of groups as dedicated children of resulting Spatial.
Justification
In current verison there is no legitimate way of transfering any “tags” from model editor to game code with OBJ format models. This feature is vital for loading interactive models from OBJ format. Though OBJ format may be too primitive for serious modern character models, it’s easy to support, it’s tools-friendly, it’s human-friendly, it’s still supported by many editors and its capabilities are enough for static geometry modeling such as game areas for non-high-end games.
Example of use
in level editor
Object names are in “Hierarchy view” by left-top
sorry for referencing competitors - there just a nice level-editing plugin…
in OBJ file
groups defined by “g” command:
mtllib ./TwoChairs.mtl
o TwoChairs
g Pillow 2
v -2.791 0.8468804 0.04495001
v -3.791 0.8468804 0.04495001
v -2.791 0.8468804 -0.95505
v -3.791 0.8468804 -0.95505
vt 0 0
vt 1 0
vt 0 -1
vt 1 -1
vn 0 1 0
usemtl dot_red
f 3/3/1 4/4/1 2/2/1 1/1/1
g Chair 1
v -0.7912173 -0.1531196 0.29495
v -1.791217 -0.1531196 0.29495
v -0.7912173 0.8468804 0.29495
v -1.791217 0.8468804 0.29495
v -1.791217 -0.1531196 0.04495001
v -1.791217 -0.1531196 -0.95505
v -1.791217 0.8468804 0.04495001
v -1.791217 0.8468804 -0.95505
v -0.7912173 -0.1531196 -0.95505
v -0.7912173 0.8468804 -0.95505
v -0.7912173 -0.1531196 0.04495001
v -0.7912173 0.8468804 0.04495001
v -1.791217 1.84688 0.04495001
v -0.7912173 1.84688 0.04495001
v -1.791217 1.84688 0.29495
v -0.7912173 1.84688 0.29495
vt 0 0
vt -1 0
vt 0 1
vt -1 1
vt 1 0
vt 1 1
vt 0 -1
vt -1 -1
vt 0 0.25
vt -1 0.25
vt -0.25 1
vt -0.25 0
vt 0.25 0
vt 0.25 1
vt 1 0.25
vt 1 2
vt 0 2
vt 0.25 2
vt -0.25 2
vt -1 2
vt 1 -1
vn 0 0 1
vn -1 0 0
vn 0 0 -1
vn 1 0 0
vn 0 -1 0
vn 0 1 0
usemtl dot_black
f 7/7/2 8/8/2 6/6/2 5/5/2
f 11/7/3 12/8/3 10/6/3 9/5/3
f 12/10/4 14/7/4 13/5/4 10/9/4
f 14/10/5 16/7/5 15/5/5 13/9/5
f 15/5/6 9/6/6 10/12/6 13/11/6
f 5/13/6 6/14/6 9/6/6 15/5/6
f 7/15/5 5/16/5 15/5/5 16/7/5
f 6/17/3 8/18/3 11/7/3 9/5/3
f 19/19/7 20/13/7 18/5/7 17/9/7
f 17/20/4 18/21/4 16/7/4 11/10/4
f 19/22/3 17/21/3 11/7/3 8/18/3
f 18/21/5 20/23/5 7/15/5 16/7/5
f 20/21/2 19/24/2 8/8/2 7/7/2
usemtl dot_green
f 14/11/7 12/25/7 11/9/7 16/5/7
g Chair 2
v -2.791 -0.1531196 0.29495
v -3.791 -0.1531196 0.29495
v -2.791 0.8468804 0.29495
v -3.791 0.8468804 0.29495
v -3.791 -0.1531196 0.04495001
v -3.791 -0.1531196 -0.95505
v -3.791 0.8468804 0.04495001
v -3.791 0.8468804 -0.95505
v -2.791 -0.1531196 -0.95505
v -2.791 0.8468804 -0.95505
v -2.791 -0.1531196 0.04495001
v -2.791 0.8468804 0.04495001
v -3.791 1.84688 0.04495001
v -2.791 1.84688 0.04495001
v -3.791 1.84688 0.29495
v -2.791 1.84688 0.29495
vt 0 0
vt -1 0
vt 0 1
vt -1 1
vt 1 0
vt 1 1
vt 0 -1
vt -1 -1
vt 0 0.25
vt -1 0.25
vt -0.25 1
vt -0.25 0
vt 0.25 0
vt 0.25 1
vt 1 0.25
vt 1 2
vt 0 2
vt 0.25 2
vt -0.25 2
vt -1 2
vn 0 0 1
vn -1 0 0
vn 0 0 -1
vn 1 0 0
vn 0 -1 0
vn 0 1 0
usemtl dot_black
f 23/28/8 24/29/8 22/27/8 21/26/8
f 27/28/9 28/29/9 26/27/9 25/26/9
f 28/31/10 30/28/10 29/26/10 26/30/10
f 30/31/11 32/28/11 31/26/11 29/30/11
f 31/26/12 25/27/12 26/33/12 29/32/12
f 21/34/12 22/35/12 25/27/12 31/26/12
f 23/36/11 21/37/11 31/26/11 32/28/11
f 22/38/9 24/39/9 27/28/9 25/26/9
f 35/40/13 36/34/13 34/26/13 33/30/13
f 33/41/10 34/42/10 32/28/10 27/31/10
f 35/43/9 33/42/9 27/28/9 24/39/9
f 34/42/11 36/44/11 23/36/11 32/28/11
f 36/42/8 35/45/8 24/29/8 23/28/8
Smoothing groups, merge groups and (plain) groups shouldn’t be managed the same way. Before your change, lines starting with s or g were skipped and the parser went to the next statement. Now, you manage the (plain) groups. I advise you to use other importers written in Java as sources of inspiration when their respective licenses are compatible with JMonkeyEngine’s license to avoid reinventing the wheel in worse.
The goal of my change is to extend it at the minimal sufficient extent to support necessary features.
Such minimalistic approach gives us two benefits:
risk of break is minimal
test coverage is maximum
And, as I see, current OBJLoader implementation developed with the same attitude, so such approach doesn’t seem bad in this case.
But, taking your notice in attention, I think I need to reimplement my solution to take into account plain groups only.
@mitm Maybe I missed something obvious, are the limitations of this importer documented?
@NikolayPlekhanov Do as you wish but in my humble opinion, smoothing groups should be parsed into a separate block with a comment explaining that they are unfortunately not smoothed by the engine.
as I understand, j3o is internal format used by jME SDK to store models imported from another formats. But I do not use jME SDK. What are the benefits of this double conversion for cases when developer is ok to work without SDK?
List of options above are targeted only to the first question. Let’s do not mix them. at least now.
About the smooth and merge groups
@gouessej
Ok, I’ll add informative warning if these statements detected in OBJ file.
About the place where new code should be
@pspeed, @mitm
Now I understand. j3o is optimized for jME and it follows the idea to avoid unnecessary dependencies in final distribution.
Then I’d like to apply my changes to the place where it’s available for JmeConvert and JME SDK.
After examination of sources of SDK and JmeConvert I discovered that they both use jme3-core’s OBJLoader to read OBJ files during import (indirectly via DesktopAssetManager). So, looks like proposed solution is already compliant with your idea to use only j3o in runtime.
@NikolayPlekhanov At first, I don’t know whether it’s in the spec but a group can have no name. A smoothing group is different, “off” means 0, otherwise you should find a smoothing value. You have to use this information when removing duplicate vertices, the candidates must have the same texture (UV) coordinates AND come from the same smoothing group.
I agree with pspeed, the format of the engine should be used at runtime most of the time except if the purpose of the software is to perform conversions like MeshLab and jmeConvert. For long term storage, I use an exchange format (Collada in my case but GLTF is fine too, OBJ is enough for basic models without animations and is supported by tons of modelers).
I’d prefer avoid increasing spec compliance of OBJLoader more, because:
it will make code more complex and thus more expensive in support.
it is not demanded by anyone.
Proposed implementation doesn’t make such enhancement more difficult in future.
as I see from this spec (I’m not sure it’s official, but looks very serious), s statements always used in conjunction with g statement. Anyway, if not - current implementation just ignores it. So it’s safe to continue ignore it and it will not make spec compiance less than before.
I agree too. And proposed solution supports such practice.
The example uses s in conjunction with g but it’s not mandatory. Yes you can go on ignoring them but in this case, don’t create a group for each smoothing group, just skip them and add a comment into your source code to state that smoothing groups aren’t taken into account.
Just handle it as before. You’re right, my reply was inaccurate. “off” or a long isn’t a group name, it makes no sense to make a plain group for a smoothing group, especially if you don’t perform smoothing later. Those are two distinct notions.
To be honest, I care so little about the OBJ format other than “we should have it” that I’m actually fine with the way it already is. It’s an ancient format that is as simple as a format gets… and our current implementation is as simple as a format reader gets.
I would be willing to bet that within the next year or two, even you will have abandoned this format for something better.
To complicate it to support a feature that probably only you will use… and then only for a while… I just don’t know.
@gouessej
Could you please clarify how “s” statement works. As I see in Wavefront .obj file - Wikipedia, it just switches on/off smoothing and sounds like it doesn’t clash with “g” command. so we can smooth part of named group. Is my understadning right?
thanks for reference. actually text there confirms by assumption that s works in parallel with g. It means that my implementation already fit @gouessej’s claim.
Actually, I hope to abandon OBJ format in a couple of months, because I do not plan to create a lot of demo levels.
But from another side, this change is very simple and improves format support so much. And there are lot of tools that supports OBJ export. So I thought it worth to be improved even for rare cases, especially counting on growth of jME auditory.