Created
February 20, 2015 19:06
-
-
Save StillManic/b9d812148620ba7fe7f9 to your computer and use it in GitHub Desktop.
OBJModel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.minecraftforge.client.model.obj; | |
import java.awt.image.BufferedImage; | |
import java.awt.image.Raster; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.nio.ByteBuffer; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.ListIterator; | |
import java.util.Map; | |
import java.util.Set; | |
import javax.vecmath.Matrix4f; | |
import javax.vecmath.Vector2f; | |
import javax.vecmath.Vector3f; | |
import javax.vecmath.Vector4f; | |
import net.minecraft.block.state.IBlockState; | |
import net.minecraft.client.Minecraft; | |
import net.minecraft.client.renderer.block.model.BakedQuad; | |
import net.minecraft.client.renderer.block.model.ItemCameraTransforms; | |
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; | |
import net.minecraft.client.renderer.texture.DynamicTexture; | |
import net.minecraft.client.renderer.texture.TextureAtlasSprite; | |
import net.minecraft.client.renderer.texture.TextureMap; | |
import net.minecraft.client.renderer.texture.TextureUtil; | |
import net.minecraft.client.renderer.vertex.VertexFormat; | |
import net.minecraft.client.renderer.vertex.VertexFormatElement; | |
import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumType; | |
import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumUsage; | |
import net.minecraft.client.resources.IResource; | |
import net.minecraft.client.resources.IResourceManager; | |
import net.minecraft.client.resources.data.AnimationMetadataSection; | |
import net.minecraft.client.resources.model.IBakedModel; | |
import net.minecraft.item.ItemStack; | |
import net.minecraft.util.EnumFacing; | |
import net.minecraft.util.ResourceLocation; | |
import net.minecraftforge.client.GuiIngameForge; | |
import net.minecraftforge.client.event.RenderGameOverlayEvent; | |
import net.minecraftforge.client.event.TextureStitchEvent; | |
import net.minecraftforge.client.model.Attributes; | |
import net.minecraftforge.client.model.IFlexibleBakedModel; | |
import net.minecraftforge.client.model.IModel; | |
import net.minecraftforge.client.model.IModelPart; | |
import net.minecraftforge.client.model.IModelState; | |
import net.minecraftforge.client.model.IPerspectiveAwareModel; | |
import net.minecraftforge.client.model.ISmartBlockModel; | |
import net.minecraftforge.client.model.ISmartItemModel; | |
import net.minecraftforge.client.model.ModelLoader.White; | |
import net.minecraftforge.client.model.ModelLoaderRegistry; | |
import net.minecraftforge.client.model.TRSRTransformation; | |
import net.minecraftforge.fml.common.eventhandler.EventPriority; | |
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; | |
import net.minecraftforge.fml.common.registry.GameRegistry; | |
import org.apache.commons.lang3.tuple.Pair; | |
import org.apache.logging.log4j.Level; | |
import org.apache.logging.log4j.LogManager; | |
import org.apache.logging.log4j.Logger; | |
import org.lwjgl.BufferUtils; | |
import com.google.common.base.Charsets; | |
import com.google.common.base.Function; | |
import com.google.common.collect.ImmutableList; | |
import com.google.common.collect.ImmutableMap; | |
public class OBJModel implements IModel { | |
public static boolean shouldLog = true; | |
public static final Logger logger = LogManager.getLogger(OBJModel.class); | |
OBJFlexibleModel flexModel; | |
MaterialLibrary matLib = new MaterialLibrary(); | |
Material currentMaterial = new Material(); | |
List<Face> faces = new ArrayList<Face>(); | |
List<Vertex> vertices = new ArrayList<Vertex>(); | |
List<TextureCoordinate> texCoords = new ArrayList<TextureCoordinate>(); | |
List<Vector3f> normals = new ArrayList<Vector3f>(); | |
List<Vector3f> colors = new ArrayList<Vector3f>(); | |
List<ResourceLocation> textures = new ArrayList<ResourceLocation>(); | |
private boolean isBlockModel = false; | |
public OBJModel() {} | |
@Override | |
public Collection<ResourceLocation> getDependencies() | |
{ | |
return Collections.emptyList(); | |
} | |
@Override | |
public Collection<ResourceLocation> getTextures() | |
{ | |
return Collections.emptyList(); | |
} | |
@Override | |
public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter) | |
{ | |
//TODO | |
flexModel = new OBJFlexibleModel(bakedTextureGetter, this); | |
return flexModel; | |
} | |
@Override | |
public IModelState getDefaultState() | |
{ | |
return new OBJState(); | |
} | |
public void isBlockModel(boolean isBlockModel) | |
{ | |
this.isBlockModel = isBlockModel; | |
} | |
public static class OBJState implements IModelState | |
{ | |
public OBJState() {} | |
public TRSRTransformation apply(IModelPart part) | |
{ | |
return new TRSRTransformation(new Matrix4f()); | |
} | |
} | |
public static class OBJFlexibleModel implements IFlexibleBakedModel, ISmartBlockModel, ISmartItemModel, IPerspectiveAwareModel | |
{ | |
public Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter; | |
private OBJModel model = null; | |
private ByteBuffer buffer; | |
private VertexFormat format = Attributes.DEFAULT_BAKED_FORMAT; | |
public static final int BYTES_IN_INT = Integer.SIZE / Byte.SIZE; | |
public static final int VERTICES_IN_QUAD = 4; | |
public OBJFlexibleModel(Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter, OBJModel model) | |
{ | |
this.bakedTextureGetter = bakedTextureGetter; | |
this.model = model; | |
this.buffer = BufferUtils.createByteBuffer(VERTICES_IN_QUAD * format.getNextOffset()); | |
} | |
@Override | |
public boolean isAmbientOcclusion() | |
{ | |
return false; | |
} | |
@Override | |
public boolean isGui3d() | |
{ | |
return true; | |
// return model.isBlockModel; | |
} | |
@Override | |
public boolean isBuiltInRenderer() | |
{ | |
return false; | |
} | |
@Override | |
public TextureAtlasSprite getTexture() | |
{ | |
TextureAtlasSprite texture; | |
if (model.currentMaterial.getTexture().toString().equalsIgnoreCase(White.instance.getIconName())) texture = White.instance; | |
else texture = bakedTextureGetter.apply(model.currentMaterial.getTexture()); | |
return texture; | |
} | |
@Override | |
public ItemCameraTransforms getItemCameraTransforms() | |
{ | |
return ItemCameraTransforms.DEFAULT; | |
} | |
@Override | |
public IBakedModel handleItemState(ItemStack stack) | |
{ | |
//TODO: how to handle different states? | |
return this; | |
} | |
@Override | |
public IBakedModel handleBlockState(IBlockState state) | |
{ | |
//TODO: how to handle different states? | |
return this; | |
} | |
@Override | |
public List<BakedQuad> getFaceQuads(EnumFacing side) | |
{ | |
return Collections.emptyList(); | |
} | |
@Override | |
public List<BakedQuad> getGeneralQuads() | |
{ | |
ImmutableList<BakedQuad> quads; | |
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder(); | |
for (Face f : model.faces) | |
{ | |
buffer.clear(); | |
//TODO: figure out how to swap sprites for multiple textures per block | |
Material mat = model.matLib.getMaterial(f); | |
TextureAtlasSprite sprite; | |
if (mat.getTexture().toString().equalsIgnoreCase(White.instance.getIconName())) sprite = White.instance; | |
else sprite = bakedTextureGetter.apply(mat.getTexture()); | |
Vertex[] vertices = f.getVertices(); | |
putVertexData(vertices[0], sprite, 0, f); | |
putVertexData(vertices[1], sprite, 1, f); | |
putVertexData(vertices[2], sprite, 2, f); | |
putVertexData(vertices[3], sprite, 3, f); | |
buffer.flip(); | |
int[] data = new int[VERTICES_IN_QUAD * format.getNextOffset() / BYTES_IN_INT]; | |
buffer.asIntBuffer().get(data); | |
Vector3f normal = f.calcNormal(); | |
builder.add(new BakedQuad(data, -1, EnumFacing.getFacingFromVector(normal.x, normal.y, normal.z))); | |
} | |
quads = builder.build(); | |
return quads; | |
} | |
private void put(VertexFormatElement element, Number... ns) | |
{ | |
Attributes.put(buffer, element, true, 0, ns); | |
} | |
@SuppressWarnings("unchecked") | |
private final void putVertexData(Vertex v, TextureAtlasSprite sprite, int index, Face f) | |
{ | |
int oldPos = buffer.position(); | |
Number[] nums = new Number[16]; | |
for (int i = 0; i < nums.length; i++) nums[i] = 0f; | |
for (VertexFormatElement element : (List<VertexFormatElement>) format.getElements()) | |
{ | |
switch (element.getUsage()) | |
{ | |
case POSITION: | |
put(element, v.getPosition().x, v.getPosition().y, v.getPosition().z, 1); | |
break; | |
case COLOR: | |
//TODO: may change when implementing color-only | |
Material m = this.model.matLib.getMaterial(f); | |
if (m == null) put(element, 1, 1, 1, 0); | |
else | |
{ | |
float r = m.color.x == 1f ? m.color.x : (int) (m.color.x * 255); | |
float g = m.color.y == 1f ? m.color.y : (int) (m.color.y * 255); | |
float b = m.color.z == 1f ? m.color.z : (int) (m.color.z * 255); | |
put(element, r, g, b, 0); | |
} | |
break; | |
case UV: | |
if (element.getIndex() < f.texCoords.length) | |
{ | |
put(element, | |
sprite.getInterpolatedU(f.texCoords[index].coord.x * 16.0f), | |
sprite.getInterpolatedV(f.texCoords[index].coord.y * 16.0f), | |
0, | |
1 | |
); | |
} | |
break; | |
default: | |
break; | |
} | |
} | |
buffer.position(oldPos + format.getNextOffset()); | |
} | |
@Override | |
public VertexFormat getFormat() | |
{ | |
return format; | |
} | |
@Override | |
public Pair<IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType) | |
{ | |
return Pair.of((IBakedModel) this, null); | |
} | |
} | |
public static class Parser | |
{ | |
private IResourceManager manager; | |
private IResource from; | |
private InputStreamReader objStream; | |
private InputStreamReader mtlStream; | |
private BufferedReader objReader; | |
private BufferedReader mtlReader; | |
private List<String> objData = new ArrayList<String>(); | |
private List<String> mtlData = new ArrayList<String>(); | |
OBJModel model = new OBJModel(); | |
public Parser(IResource from, IResourceManager manager) throws IOException | |
{ | |
this.manager = manager; | |
this.from = from; | |
this.objStream = new InputStreamReader(from.getInputStream(), Charsets.UTF_8); | |
this.objReader = new BufferedReader(objStream); | |
} | |
public void readOBJToList() throws IOException | |
{ | |
for (;;) | |
{ | |
String currentLine = objReader.readLine(); | |
if (currentLine == null) break; | |
if (currentLine.isEmpty() || currentLine.startsWith("#")) continue; | |
this.objData.add(currentLine); | |
} | |
} | |
public void readMTLToList(IResource from) throws IOException | |
{ | |
mtlStream = new InputStreamReader(from.getInputStream(), Charsets.UTF_8); | |
mtlReader = new BufferedReader(mtlStream); | |
for (;;) | |
{ | |
String currentLine = mtlReader.readLine(); | |
if (currentLine == null) break; | |
if (currentLine.isEmpty() || currentLine.startsWith("#")) continue; | |
this.mtlData.add(currentLine); | |
} | |
} | |
public List<Face> getFaces() | |
{ | |
List<Face> faces = new ArrayList<Face>(); | |
for (Material m : model.matLib.materials.values()) faces.addAll(m.getFaces()); | |
return faces; | |
} | |
public List<TextureCoordinate> getTextureCoordinates() | |
{ | |
List<TextureCoordinate> coords = new ArrayList<TextureCoordinate>(); | |
List<Face> faces = getFaces(); | |
for (Face f : faces) coords.addAll(Arrays.asList(f.getTextureCoordinates())); | |
return coords; | |
} | |
public OBJModel parse() throws IOException | |
{ | |
readOBJToList(); | |
StringBuilder builder = new StringBuilder(from.getResourceLocation().getResourceDomain() + ":" + from.getResourceLocation().getResourcePath()); | |
int i = builder.lastIndexOf("/"); | |
for (String s : objData) | |
{ | |
if (s.startsWith("mtllib") && mtlData.isEmpty()) | |
{ | |
String mtllibName = s.split(" ", 2)[1]; | |
builder.replace(i + 1, builder.length(), mtllibName); | |
readMTLToList(manager.getResource(new ResourceLocation(builder.toString()))); | |
break; | |
} | |
} | |
model.matLib.buildLibrary(this.mtlData); | |
for (String s : objData) | |
{ | |
if (s.isEmpty() || s.startsWith("#")) continue; | |
String[] fields = s.split(" ", 2); | |
String key = fields[0]; | |
String data = fields[1]; | |
if (key.equalsIgnoreCase("usemtl")) model.currentMaterial = model.matLib.getMaterial(data); | |
else if (key.equalsIgnoreCase("v")) | |
{ | |
String[] posStr = data.split(" ", 6); | |
Vector3f pos = new Vector3f(Float.parseFloat(posStr[0]), Float.parseFloat(posStr[1]), Float.parseFloat(posStr[2])); | |
Vector3f color = new Vector3f(0, 0, 0); | |
if (posStr.length > 4) color = new Vector3f(Float.parseFloat(posStr[3]), Float.parseFloat(posStr[4]), Float.parseFloat(posStr[5])); | |
model.vertices.add(new Vertex(pos, color)); | |
} | |
else if (key.equalsIgnoreCase("vt")) | |
{ | |
String[] posStr = data.split(" ", 3); | |
Vector2f pos = new Vector2f(Float.parseFloat(posStr[0]), Float.parseFloat(posStr[1])); | |
model.texCoords.add(new TextureCoordinate(pos)); | |
} | |
else if (key.equalsIgnoreCase("vn")) | |
{ | |
String[] posStr = data.split(" ", 3); | |
Vector3f pos = new Vector3f(Float.parseFloat(posStr[0]), Float.parseFloat(posStr[1]), Float.parseFloat(posStr[2])); | |
model.normals.add(pos); | |
} | |
else if (key.equalsIgnoreCase("f")) | |
{ | |
String[] points = data.split(" ", 4); | |
List<Vertex> vs = new ArrayList<Vertex>(); | |
List<TextureCoordinate> tcs = new ArrayList<TextureCoordinate>(); | |
List<Vector3f> ns = new ArrayList<Vector3f>(); | |
for (String p : points) | |
{ | |
if (p.contains("//")) | |
{ | |
String[] vnStr = p.split("//", 2); | |
int vert = Integer.parseInt(vnStr[0]); | |
int norm = Integer.parseInt(vnStr[1]); | |
//TODO: handle negative vertex indices! | |
if (vert < 0 || norm < 0) | |
{ | |
logger.printf(Level.WARN, "OBJModel: Negative vertex index refrences for faces aren't supported yet! The model will not render properlly!", new Object[0]); | |
break; | |
} | |
else | |
{ | |
if (vert > 0) vs.add(model.vertices.get(vert - 1)); | |
if (norm > 0) ns.add(model.normals.get(norm - 1)); | |
tcs.add(new TextureCoordinate(0, 1)); | |
tcs.add(new TextureCoordinate(1, 1)); | |
tcs.add(new TextureCoordinate(1, 0)); | |
tcs.add(new TextureCoordinate(0, 0)); | |
} | |
} | |
else if (p.contains("/")) | |
{ | |
String[] vtStr = p.split("/", 3); | |
int vert = Integer.parseInt(vtStr[0]); | |
int texc = Integer.parseInt(vtStr[1]); | |
int norm = vtStr.length > 2 ? Integer.parseInt(vtStr[2]) : 0; | |
//TODO: handle negative vertex indices! | |
if (vert < 0 || texc < 0 || norm < 0) | |
{ | |
logger.printf(Level.WARN, "OBJModel: Negative vertex index refrences for faces aren't supported yet! The model will not render properlly!", new Object[0]); | |
break; | |
} | |
else | |
{ | |
if (vert > 0) vs.add(model.vertices.get(vert - 1)); | |
if (texc > 0) tcs.add(model.texCoords.get(texc - 1)); | |
if (norm > 0) ns.add(model.normals.get(norm - 1)); | |
} | |
} | |
else | |
{ | |
int vert = Integer.parseInt(p); | |
//TODO: handle negative vertex indices! | |
if (vert < 0) | |
{ | |
logger.printf(Level.WARN, "OBJModel: Negative vertex index refrences for faces aren't supported yet! The model will not render properlly!", new Object[0]); | |
break; | |
} | |
else | |
{ | |
if (vert > 0) vs.add(model.vertices.get(vert - 1)); | |
tcs.add(new TextureCoordinate(0, 1)); | |
tcs.add(new TextureCoordinate(1, 1)); | |
tcs.add(new TextureCoordinate(1, 0)); | |
tcs.add(new TextureCoordinate(0, 0)); | |
ns.add(new Vector3f(0, 0, 0)); | |
ns.add(new Vector3f(0, 0, 0)); | |
ns.add(new Vector3f(0, 0, 0)); | |
ns.add(new Vector3f(0, 0, 0)); | |
} | |
} | |
} | |
if (vs.size() == 3) vs.add(vs.get(2)); | |
if (tcs.size() == 3) tcs.add(tcs.get(2)); | |
if (ns.size() == 3) ns.add(ns.get(2)); | |
Face face = new Face(vs.toArray(new Vertex[4]), tcs.toArray(new TextureCoordinate[4]), ns.toArray(new Vector3f[4])); | |
model.currentMaterial.addFace(face); | |
} | |
} | |
model.faces = getFaces(); | |
return model; | |
} | |
} | |
public static class MaterialLibrary | |
{ | |
Map<String, Material> materials = new HashMap<String, Material>(); | |
public MaterialLibrary() | |
{ | |
materials.put("default", new Material()); | |
} | |
public void buildLibrary(List<String> mtlData) | |
{ | |
List<String> mat = new ArrayList<String>(); | |
String[] matArray = null; | |
for (String s : mtlData) | |
{ | |
String[] fields = s.split(" ", 2); | |
String key = fields[0]; | |
String data = fields[1]; | |
if (key.equalsIgnoreCase("newmtl")) | |
{ | |
if (!mat.isEmpty()) | |
{ | |
String name = mat.get(0).split(" ", 2)[1]; | |
matArray = new String[mat.size()]; | |
mat.toArray(matArray); | |
materials.put(name, new Material(matArray)); | |
mat.clear(); | |
mat.add(s); | |
matArray = null; | |
} | |
else mat.add(s); | |
} | |
else mat.add(s); | |
} | |
if (!mat.isEmpty()) | |
{ | |
String name = mat.get(0).split(" ", 2)[1]; | |
matArray = new String[mat.size()]; | |
mat.toArray(matArray); | |
materials.put(name, new Material(matArray)); | |
mat.clear(); | |
matArray = null; | |
} | |
} | |
public Material getMaterial(String name) | |
{ | |
if (name == null || !this.materials.containsKey(name)) return this.materials.get("default"); | |
else return this.materials.get(name); | |
} | |
public Material getMaterial(Face f) | |
{ | |
if (f != null) | |
{ | |
for (Material m : this.materials.values()) if (m.faces.contains(f)) return m; | |
return null; | |
} | |
return null; | |
} | |
public void updateMaterial(String name, Material material) | |
{ | |
if (name != null && !name.equals("default") && this.materials.containsKey(name) && material != null && material != this.materials.get("defualt")) | |
{ | |
this.materials.replace(name, material); | |
} | |
} | |
public List<Face> getFacesContainingVertex(Vertex v) | |
{ | |
List<Face> fcv = new ArrayList<Face>(); | |
for (Material m : materials.values()) for (Face f : m.faces) if (Arrays.asList(f.verts).contains(v)) fcv.add(f); | |
return fcv; | |
} | |
} | |
public static class Material | |
{ | |
private Vector3f kdColor; | |
private Vector3f kaColor; | |
private Vector3f ksColor; | |
private Vector3f color; | |
private ResourceLocation kdLoc; | |
private ResourceLocation kaLoc; | |
private ResourceLocation ksLoc; | |
private ResourceLocation texture; | |
private String name = "default"; | |
private List<Face> faces = new ArrayList<Face>(); | |
public Material() | |
{ | |
this.name = "default"; | |
this.texture = new ResourceLocation("missingno"); | |
this.color = new Vector3f(0, 0, 0); | |
} | |
public Material(String[] matData) | |
{ | |
for (String s : matData) | |
{ | |
String[] fields = s.split(" ", 2); | |
String key = fields[0]; | |
String data = fields[1]; | |
if (key.equalsIgnoreCase("newmtl")) this.name = data; | |
else if (key.equalsIgnoreCase("Kd") || key.equalsIgnoreCase("Ka") || key.equalsIgnoreCase("Ks")) | |
{ | |
String[] rgbStrings = data.split(" ", 3); | |
float[] rgb = new float[] {Float.parseFloat(rgbStrings[0]), Float.parseFloat(rgbStrings[1]), Float.parseFloat(rgbStrings[2])}; | |
kdColor = key.equalsIgnoreCase("Kd") ? new Vector3f(rgb[0], rgb[1], rgb[2]) : kdColor; | |
kaColor = key.equalsIgnoreCase("Ka") ? new Vector3f(rgb[0], rgb[1], rgb[2]) : kaColor; | |
ksColor = key.equalsIgnoreCase("Ks") ? new Vector3f(rgb[0], rgb[1], rgb[2]) : ksColor; | |
} | |
else if (key.equalsIgnoreCase("map_Kd") || key.equalsIgnoreCase("map_Ka") || key.equalsIgnoreCase("map_Ks")) | |
{ | |
String path; | |
if (data.contains(" ")) | |
{ | |
String[] dataSplit = data.split(" "); | |
path = dataSplit[dataSplit.length - 1]; | |
} | |
else path = data; | |
String[] splitPath = path.split(":", 2); | |
ResourceLocation parsedLoc; | |
if (splitPath.length > 1 && splitPath[1] != null && !splitPath[1].isEmpty()) parsedLoc = new ResourceLocation(splitPath[0] + ":" + splitPath[1]); | |
else parsedLoc = new ResourceLocation(path); | |
kdLoc = key.equalsIgnoreCase("map_Kd") ? parsedLoc : kdLoc; | |
kaLoc = key.equalsIgnoreCase("map_Ka") ? parsedLoc : kaLoc; | |
ksLoc = key.equalsIgnoreCase("map_Ks") ? parsedLoc : ksLoc; | |
} | |
} | |
this.texture = kdLoc != null ? kdLoc : kaLoc != null ? kaLoc : ksLoc != null ? ksLoc : new ResourceLocation(White.instance.getIconName()); | |
this.color = kdColor != null ? kdColor : kaColor != null ? kaColor : ksColor != null ? ksColor : this.color; | |
System.out.printf("Texture name is: %s%n", this.texture.toString()); | |
System.out.printf("Color is: r: %s, g: %s, b: %s%n", this.color.x, this.color.y, this.color.z); | |
} | |
public ResourceLocation getTexture() | |
{ | |
return this.texture; | |
} | |
public Vector3f getColor() | |
{ | |
return this.color; | |
} | |
public String getName() | |
{ | |
return this.name; | |
} | |
public void addFace(Face f) | |
{ | |
if (!this.faces.contains(f)) this.faces.add(f); | |
} | |
public List<Face> getFaces() | |
{ | |
return this.faces; | |
} | |
} | |
public static class Face | |
{ | |
private Vertex[] verts = new Vertex[OBJFlexibleModel.VERTICES_IN_QUAD]; | |
private TextureCoordinate[] texCoords = new TextureCoordinate[OBJFlexibleModel.VERTICES_IN_QUAD]; | |
private Vector3f[] normals = new Vector3f[OBJFlexibleModel.VERTICES_IN_QUAD]; | |
public Face(Vertex[] verts, TextureCoordinate[] texCoords, Vector3f[] normals) | |
{ | |
this.verts = verts; | |
this.texCoords = texCoords; | |
this.normals = normals; | |
} | |
public void setVertices(Vertex[] verts) | |
{ | |
this.verts = verts; | |
} | |
public Vertex[] getVertices() | |
{ | |
return this.verts; | |
} | |
public void setTextureCoordinates(TextureCoordinate[] texCoords) | |
{ | |
this.texCoords = texCoords; | |
} | |
public TextureCoordinate[] getTextureCoordinates() | |
{ | |
return this.texCoords; | |
} | |
public void setNormals(Vector3f[] normals) | |
{ | |
this.normals = normals; | |
} | |
public Vector3f[] getNormals() | |
{ | |
return this.normals; | |
} | |
public boolean containsVertex(Vertex vertex) | |
{ | |
return Arrays.asList(verts).contains(vertex); | |
} | |
public Vector3f calcNormal() | |
{ | |
Vector3f normal = new Vector3f(); | |
Vector3f v2sv1 = new Vector3f(); | |
Vector3f v4sv1 = new Vector3f(); | |
Vector3f v2sv3 = new Vector3f(); | |
Vector3f v4sv3 = new Vector3f(); | |
Vector3f tri124 = new Vector3f(); | |
Vector3f tri324 = new Vector3f(); | |
v2sv1.sub(verts[1].getPosition(), verts[0].getPosition()); | |
v4sv1.sub(verts[3].getPosition(), verts[0].getPosition()); | |
tri124.cross(v2sv1, v4sv1); | |
tri124.normalize(); | |
v2sv3.sub(verts[1].getPosition(), verts[2].getPosition()); | |
v4sv3.sub(verts[3].getPosition(), verts[2].getPosition()); | |
tri324.cross(v2sv3, v4sv3); | |
tri324.normalize(); | |
normal.set((tri124.getX() + tri324.getX()) / 2.0f, (tri124.getY() + tri324.getY()) / 2.0f, (tri124.getZ() + tri324.getZ()) / 2.0f); | |
normal.normalize(); | |
return normal; | |
} | |
} | |
public static class Vertex | |
{ | |
private Vector3f position; | |
private Vector3f color; | |
public Vertex(float x, float y, float z) | |
{ | |
this(new Vector3f(x, y, z)); | |
} | |
public Vertex(float x, float y, float z, float r, float g, float b) | |
{ | |
this(new Vector3f(x, y, z), new Vector3f(r, g, b)); | |
} | |
public Vertex(Vector3f position) | |
{ | |
this(position, new Vector3f(-1, -1, -1)); | |
} | |
public Vertex(Vector3f position, Vector3f color) | |
{ | |
this.position = position; | |
this.color = color; | |
} | |
public void setPosition(Vector3f position) | |
{ | |
this.position = position; | |
} | |
public Vector3f getPosition() | |
{ | |
return this.position; | |
} | |
public void setColor(Vector3f color) | |
{ | |
this.color = color; | |
} | |
public Vector3f getColor() | |
{ | |
return this.color; | |
} | |
} | |
public static class TextureCoordinate | |
{ | |
private Vector2f coord; | |
public TextureCoordinate(float u, float v) | |
{ | |
this(new Vector2f(u, v)); | |
} | |
public TextureCoordinate(Vector2f coord) | |
{ | |
this.coord = coord; | |
} | |
public void setCoord(Vector2f coord) | |
{ | |
this.coord = coord; | |
} | |
public Vector2f getCoord() | |
{ | |
return this.coord; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment