OBJ loader: fixed another whitespace-related issue; removed unused "modifyUVs" property for now; added the "flip-v" property to switch between OpenGL-style and DirextX-style model UVs; fixed normals - they are now correct in-world, still a bit strange for the items; fixed normals a little bit for B3D models too.

This commit is contained in:
RainWarrior 2015-10-29 22:50:45 +03:00
parent c3b15e3f6c
commit 12e6fe9db7
3 changed files with 255 additions and 286 deletions

View file

@ -582,11 +582,11 @@ public class B3DLoader implements ICustomModelLoader
case NORMAL: case NORMAL:
if(v.getNormal() != null) if(v.getNormal() != null)
{ {
builder.put(e, v.getNormal().x, v.getNormal().y, v.getNormal().z, 1); builder.put(e, v.getNormal().x, v.getNormal().y, v.getNormal().z, 0);
} }
else else
{ {
builder.put(e, faceNormal.x, faceNormal.y, faceNormal.z, 1); builder.put(e, faceNormal.x, faceNormal.y, faceNormal.z, 0);
} }
break; break;
default: default:

View file

@ -17,6 +17,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.vecmath.Matrix3f;
import javax.vecmath.Matrix4f; import javax.vecmath.Matrix4f;
import javax.vecmath.Quat4f; import javax.vecmath.Quat4f;
import javax.vecmath.Vector2f; import javax.vecmath.Vector2f;
@ -625,7 +626,8 @@ public class B3DModel
bm.mul(bone.getLeft()); bm.mul(bone.getLeft());
t.add(bm); t.add(bm);
} }
if(totalWeight != 0) t.mul(1f / totalWeight); if(Math.abs(totalWeight) > 1e-4) t.mul(1f / totalWeight);
else t.setIdentity();
} }
// pos // pos
@ -635,12 +637,12 @@ public class B3DModel
Vector3f rPos = new Vector3f(newPos.x / newPos.w, newPos.y / newPos.w, newPos.z / newPos.w); Vector3f rPos = new Vector3f(newPos.x / newPos.w, newPos.y / newPos.w, newPos.z / newPos.w);
// normal // normal
t.invert(); Matrix3f tm = new Matrix3f();
t.transpose(); t.getRotationScale(tm);
Vector4f normal = new Vector4f(this.normal), newNormal = new Vector4f(); tm.invert();
normal.w = 1; tm.transpose();
t.transform(normal, newNormal); Vector3f normal = new Vector3f(this.normal), rNormal = new Vector3f();
Vector3f rNormal = new Vector3f(newNormal.x / newNormal.w, newNormal.y / newNormal.w, newNormal.z / newNormal.w); tm.transform(normal, rNormal);
rNormal.normalize(); rNormal.normalize();
// texCoords TODO // texCoords TODO

View file

@ -14,6 +14,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.vecmath.Matrix3f;
import javax.vecmath.Matrix4f; import javax.vecmath.Matrix4f;
import javax.vecmath.Vector2f; import javax.vecmath.Vector2f;
import javax.vecmath.Vector3f; import javax.vecmath.Vector3f;
@ -69,7 +70,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
public OBJModel(MaterialLibrary matLib, ResourceLocation modelLocation) public OBJModel(MaterialLibrary matLib, ResourceLocation modelLocation)
{ {
this(matLib, modelLocation, null); this(matLib, modelLocation, new CustomData());
} }
public OBJModel(MaterialLibrary matLib, ResourceLocation modelLocation, CustomData customData) public OBJModel(MaterialLibrary matLib, ResourceLocation modelLocation, CustomData customData)
@ -132,15 +133,10 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
return this.matLib; return this.matLib;
} }
public boolean hasCustomData()
{
return this.customData != null;
}
@Override @Override
public IModel process(ImmutableMap<String, String> customData) public IModel process(ImmutableMap<String, String> customData)
{ {
OBJModel ret = new OBJModel(this.matLib, this.modelLocation, new CustomData(customData)); OBJModel ret = new OBJModel(this.matLib, this.modelLocation, new CustomData(this.customData, customData));
return ret; return ret;
} }
@ -151,17 +147,24 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
return ret; return ret;
} }
public static class CustomData static class CustomData
{ {
public boolean ambientOcclusion = true; public boolean ambientOcclusion = true;
public boolean gui3d = true; public boolean gui3d = true;
public boolean modifyUVs = false; // should be an enum, TODO
//public boolean modifyUVs = false;
public boolean flipV = false;
public CustomData(ImmutableMap<String, String> customData) public CustomData(CustomData parent, ImmutableMap<String, String> customData)
{ {
this.ambientOcclusion = parent.ambientOcclusion;
this.gui3d = parent.gui3d;
this.flipV = parent.flipV;
this.process(customData); this.process(customData);
} }
public CustomData() {}
public void process(ImmutableMap<String, String> customData) public void process(ImmutableMap<String, String> customData)
{ {
for (Map.Entry<String, String> e : customData.entrySet()) for (Map.Entry<String, String> e : customData.entrySet())
@ -170,8 +173,10 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
this.ambientOcclusion = Boolean.valueOf(e.getValue()); this.ambientOcclusion = Boolean.valueOf(e.getValue());
else if (e.getKey().equals("gui3d")) else if (e.getKey().equals("gui3d"))
this.gui3d = Boolean.valueOf(e.getValue()); this.gui3d = Boolean.valueOf(e.getValue());
else if (e.getKey().equals("modifyUVs")) /*else if (e.getKey().equals("modifyUVs"))
this.modifyUVs = Boolean.valueOf(e.getValue()); this.modifyUVs = Boolean.valueOf(e.getValue());*/
else if (e.getKey().equals("flip-v"))
this.flipV = Boolean.valueOf(e.getValue());
} }
} }
} }
@ -217,6 +222,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
{ {
currentLine = objReader.readLine(); currentLine = objReader.readLine();
if (currentLine == null) break; if (currentLine == null) break;
currentLine.trim();
if (currentLine.isEmpty() || currentLine.startsWith("#")) continue; if (currentLine.isEmpty() || currentLine.startsWith("#")) continue;
String[] fields = WHITE_SPACE.split(currentLine, 2); String[] fields = WHITE_SPACE.split(currentLine, 2);
@ -402,7 +408,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
} }
} }
OBJModel model = new OBJModel(this.materialLibrary, this.objFrom, null); OBJModel model = new OBJModel(this.materialLibrary, this.objFrom);
// model.getMatLib().setUVBounds(minUVBounds[0], maxUVBounds[0], minUVBounds[1], maxUVBounds[1]); // model.getMatLib().setUVBounds(minUVBounds[0], maxUVBounds[0], minUVBounds[1], maxUVBounds[1]);
return model; return model;
} }
@ -528,6 +534,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
{ {
currentLine = mtlReader.readLine(); currentLine = mtlReader.readLine();
if (currentLine == null) break; if (currentLine == null) break;
currentLine.trim();
if (currentLine.isEmpty() || currentLine.startsWith("#")) continue; if (currentLine.isEmpty() || currentLine.startsWith("#")) continue;
String[] fields = WHITE_SPACE.split(currentLine, 2); String[] fields = WHITE_SPACE.split(currentLine, 2);
@ -849,13 +856,13 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
public Face bake(TRSRTransformation transform) public Face bake(TRSRTransformation transform)
{ {
Matrix4f m = transform.getMatrix(); Matrix4f m = transform.getMatrix();
Matrix3f mn = null;
Vertex[] vertices = new Vertex[verts.length]; Vertex[] vertices = new Vertex[verts.length];
// Normal[] normals = norms != null ? new Normal[norms.length] : null; // Normal[] normals = norms != null ? new Normal[norms.length] : null;
// TextureCoordinate[] textureCoords = texCoords != null ? new TextureCoordinate[texCoords.length] : null; // TextureCoordinate[] textureCoords = texCoords != null ? new TextureCoordinate[texCoords.length] : null;
for (int i = 0; i < verts.length; i++) for (int i = 0; i < verts.length; i++)
{ {
m = transform.getMatrix();
Vertex v = verts[i]; Vertex v = verts[i];
// Normal n = norms != null ? norms[i] : null; // Normal n = norms != null ? norms[i] : null;
// TextureCoordinate t = texCoords != null ? texCoords[i] : null; // TextureCoordinate t = texCoords != null ? texCoords[i] : null;
@ -867,31 +874,22 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
if (v.hasNormal()) if (v.hasNormal())
{ {
m.invert(); if(mn == null)
Vector4f normal = new Vector4f(v.getNormal().getData()), newNormal = new Vector4f(); {
normal.w = 1.0f; mn = new Matrix3f();
m.transform(normal, newNormal); m.getRotationScale(mn);
Vector3f rNormal = new Vector3f(newNormal.x / newNormal.w, newNormal.y / newNormal.w, newNormal.z / newNormal.w); mn.invert();
rNormal.normalize(); mn.transpose();
vertices[i].setNormal(new Normal(rNormal)); }
Vector3f normal = new Vector3f(v.getNormal().getData()), newNormal = new Vector3f();
mn.transform(normal, newNormal);
newNormal.normalize();
vertices[i].setNormal(new Normal(newNormal));
} }
else v.setNormal(new Normal(0.0f, 1.0f, 0.0f));
if (v.hasTextureCoordinate()) vertices[i].setTextureCoordinate(v.getTextureCoordinate()); if (v.hasTextureCoordinate()) vertices[i].setTextureCoordinate(v.getTextureCoordinate());
else v.setTextureCoordinate(TextureCoordinate.getDefaultUVs()[i]); else v.setTextureCoordinate(TextureCoordinate.getDefaultUVs()[i]);
// if (n != null)
// {
// m.invert();
// m.transpose();
// Vector4f normal = new Vector4f(n.getData()), newNormal = new Vector4f();
// normal.w = 1;
// m.transform(normal, newNormal);
// Vector3f rNormal = new Vector3f(newNormal.x / newNormal.w, newNormal.y / newNormal.w, newNormal.z / newNormal.w);
// rNormal.normalize();
// normals[i] = new Normal(rNormal);
// }
//texCoords TODO //texCoords TODO
// if (t != null) textureCoords[i] = t; // if (t != null) textureCoords[i] = t;
} }
@ -900,40 +898,13 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
public Normal getNormal() public Normal getNormal()
{ {
for (Vertex v : this.verts) Vector3f a = this.verts[2].getPos3();
{ a.sub(this.verts[0].getPos3());
if (!v.hasNormal()) Vector3f b = this.verts[3].getPos3();
{ b.sub(this.verts[1].getPos3());
Vector3f vPos = v.getPos3(); a.cross(a, b);
vPos.normalize(); a.normalize();
v.setNormal(new Normal(vPos)); return new Normal(a);
}
}
Vector3f a = this.verts[1].getNormal().getData();
a.sub(this.verts[0].getNormal().getData());
Vector3f b = this.verts[2].getNormal().getData();
b.sub(this.verts[0].getNormal().getData());
Vector3f c = new Vector3f();
c.cross(a, b);
c.normalize();
if (this.isTri) return new Normal(c);
else
{
a = this.verts[1].getNormal().getData();
a.sub(this.verts[3].getNormal().getData());
b = this.verts[2].getNormal().getData();
b.sub(this.verts[3].getNormal().getData());
Vector3f c1 = new Vector3f();
c1.cross(a, b);
c1.normalize();
Vector3f normal = new Vector3f();
normal.add(c, c1);
normal.scale(0.5f);
normal.normalize();
return new Normal(normal);
}
} }
} }
@ -1340,9 +1311,9 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
if (this.state instanceof OBJState) if (this.state instanceof OBJState)
{ {
OBJState state = (OBJState) this.state; OBJState state = (OBJState) this.state;
if (state.parent != null && state.parent instanceof TRSRTransformation) if (state.parent != null)
{ {
transform = (TRSRTransformation) state.parent; transform = state.parent.apply(model);
} }
//TODO: can this be replaced by updateStateVisibilityMap(OBJState)? //TODO: can this be replaced by updateStateVisibilityMap(OBJState)?
if (state.getGroupNamesFromMap().contains(Group.ALL)) if (state.getGroupNamesFromMap().contains(Group.ALL))
@ -1377,14 +1348,9 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
faces.addAll(g.applyTransform(transform)); faces.addAll(g.applyTransform(transform));
} }
} }
else if (this.state instanceof TRSRTransformation)
{
transform = (TRSRTransformation) this.state;
faces.addAll(g.applyTransform(transform));
}
else else
{ {
transform = TRSRTransformation.identity(); transform = state.apply(model);
faces.addAll(g.applyTransform(transform)); faces.addAll(g.applyTransform(transform));
} }
} }
@ -1404,10 +1370,11 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format); UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z)); builder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z));
builder.setQuadColored(); builder.setQuadColored();
putVertexData(builder, f.verts[0], f.getNormal(), TextureCoordinate.getDefaultUVs()[0], sprite); Normal faceNormal = f.getNormal();
putVertexData(builder, f.verts[1], f.getNormal(), TextureCoordinate.getDefaultUVs()[1], sprite); putVertexData(builder, f.verts[0], faceNormal, TextureCoordinate.getDefaultUVs()[0], sprite);
putVertexData(builder, f.verts[2], f.getNormal(), TextureCoordinate.getDefaultUVs()[2], sprite); putVertexData(builder, f.verts[1], faceNormal, TextureCoordinate.getDefaultUVs()[1], sprite);
putVertexData(builder, f.verts[3], f.getNormal(), TextureCoordinate.getDefaultUVs()[3], sprite); putVertexData(builder, f.verts[2], faceNormal, TextureCoordinate.getDefaultUVs()[2], sprite);
putVertexData(builder, f.verts[3], faceNormal, TextureCoordinate.getDefaultUVs()[3], sprite);
quads.add(builder.build()); quads.add(builder.build());
} }
} }
@ -1444,19 +1411,19 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
if (!v.hasTextureCoordinate()) if (!v.hasTextureCoordinate())
builder.put(e, builder.put(e,
sprite.getInterpolatedU(defUV.u * 16), sprite.getInterpolatedU(defUV.u * 16),
sprite.getInterpolatedV(defUV.v * 16), sprite.getInterpolatedV((model.customData.flipV ? 1 - defUV.v: defUV.v) * 16),
0, 1); 0, 1);
else else
builder.put(e, builder.put(e,
sprite.getInterpolatedU(v.getTextureCoordinate().u * 16), sprite.getInterpolatedU(v.getTextureCoordinate().u * 16),
sprite.getInterpolatedV(v.getTextureCoordinate().v * 16), sprite.getInterpolatedV((model.customData.flipV ? 1 - v.getTextureCoordinate().v : v.getTextureCoordinate().v) * 16),
0, 1); 0, 1);
break; break;
case NORMAL: case NORMAL:
if (!v.hasNormal()) if (!v.hasNormal())
builder.put(e, faceNormal.x, faceNormal.y, faceNormal.z, 1); builder.put(e, faceNormal.x, faceNormal.y, faceNormal.z, 0);
else else
builder.put(e, v.getNormal().x, v.getNormal().y, v.getNormal().z, 1); builder.put(e, v.getNormal().x, v.getNormal().y, v.getNormal().z, 0);
break; break;
default: default:
builder.put(e); builder.put(e);
@ -1467,13 +1434,13 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
@Override @Override
public boolean isAmbientOcclusion() public boolean isAmbientOcclusion()
{ {
return model != null && model.hasCustomData() ? model.customData.ambientOcclusion : true; return model != null ? model.customData.ambientOcclusion : true;
} }
@Override @Override
public boolean isGui3d() public boolean isGui3d()
{ {
return model != null && model.hasCustomData() ? model.customData.gui3d : true; return model != null ? model.customData.gui3d : true;
} }
@Override @Override