diff --git a/data/specialrenderers.json b/data/specialrenderers.json
index 3758dad..2ed4540 100644
--- a/data/specialrenderers.json
+++ b/data/specialrenderers.json
@@ -764,6 +764,18 @@
]
}
]
+ },
+ {
+ "ClassName": "InvisiblePolygonObj",
+ "RendererType": "CollisionOnly"
+ },
+ {
+ "ClassName": "InvisiblePolygonObjGCapture",
+ "RendererType": "CollisionOnly"
+ },
+ {
+ "ClassName": "TransparentWall",
+ "RendererType": "CollisionOnly"
}
]
}
\ No newline at end of file
diff --git a/src/whitehole/db/SpecialRenderers.java b/src/whitehole/db/SpecialRenderers.java
index 00b6a6b..eff6958 100644
--- a/src/whitehole/db/SpecialRenderers.java
+++ b/src/whitehole/db/SpecialRenderers.java
@@ -22,6 +22,7 @@
import whitehole.Whitehole;
import whitehole.rendering.BmdRenderer;
import whitehole.rendering.GLRenderer;
+import whitehole.rendering.KclRenderer;
import static whitehole.rendering.RendererFactory.createDummyCubeRenderer;
import whitehole.rendering.special.*;
import whitehole.smg.object.AbstractObj;
@@ -52,6 +53,8 @@ public String tryGetAdditiveCacheKey(String objModelName, AbstractObj obj)
return ShapeModelRenderer.getAdditiveCacheKey(obj, renderinfo.rendererParams);
case "ModelByProperty":
return ModelByPropertyRenderer.getAdditiveCacheKey(obj, objModelName, renderinfo.rendererParams);
+ case "CollisionOnly":
+ return KclRenderer.getAdditiveCacheKey(obj, renderinfo.rendererParams);
case "PowerStar":
return PowerStarRenderer.getAdditiveCacheKey(obj, (Integer)renderinfo.getRenderParamByName("DefaultFrame"));
@@ -85,6 +88,9 @@ public GLRenderer tryGetSpecialRenderer(GLRenderer.RenderInfo info, String objMo
case "ModelByProperty":
result = new ModelByPropertyRenderer(info, objModelName, obj, renderinfo.rendererParams);
break;
+ case "CollisionOnly":
+ result = new KclRenderer(info, objModelName);
+ break;
case "PowerStar":
result = new PowerStarRenderer(info, objModelName, obj,
diff --git a/src/whitehole/rendering/KclRenderer.java b/src/whitehole/rendering/KclRenderer.java
new file mode 100644
index 0000000..0791f78
--- /dev/null
+++ b/src/whitehole/rendering/KclRenderer.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2024 Whitehole Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package whitehole.rendering;
+
+import com.jogamp.opengl.GL2;
+import com.jogamp.opengl.GLException;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Locale;
+import whitehole.Settings;
+import whitehole.Whitehole;
+import whitehole.io.ExternalFile;
+import whitehole.io.FileBase;
+import whitehole.io.RarcFile;
+import whitehole.math.Vec3f;
+import static whitehole.rendering.GLRenderer.DEFAULT_ROTATION;
+import static whitehole.rendering.GLRenderer.DEFAULT_SCALE;
+import static whitehole.rendering.GLRenderer.DEFAULT_TRANSLATION;
+import whitehole.smg.Bcsv;
+import whitehole.smg.Kcl;
+import whitehole.smg.object.AbstractObj;
+import whitehole.util.SuperFastHash;
+
+/**
+ *
+ * @author Hackio
+ */
+public class KclRenderer extends GLRenderer {
+
+ private int shaderHash(int groupId)
+ {
+ byte[] sigarray = new byte[0x10];
+ ByteBuffer sig = ByteBuffer.wrap(sigarray);
+
+ // In order to prevent the Renderer from confusing Shaders for KCL and Shaders for BMD, we'll sign these with some M A G I C
+ sig.putChar('k');
+ sig.putChar('c');
+ sig.putChar('l');
+
+ //sig.putInt(groupId);
+
+ return(int) SuperFastHash.calculate(sigarray, 0, 0, sig.position());
+ }
+
+ public void generateShaders(GL2 gl, int matid) throws GLException {
+ if (model == null)
+ return;
+
+ if(shaders == null)
+ shaders = new Shader[1];
+
+ shaders[matid] = new Shader();
+
+ int hash = shaderHash(matid);
+ shaders[matid].cacheKey = hash;
+
+ if(ShaderCache.containsEntry(hash))
+ {
+ ShaderCache.CacheEntry entry = ShaderCache.getEntry(hash);
+ shaders[matid].vertexShader = entry.vertexID;
+ shaders[matid].fragmentShader = entry.fragmentID;
+ shaders[matid].program = entry.programID;
+
+ return;
+ }
+
+ Locale usa = new Locale("en-US");
+
+ StringBuilder vert = new StringBuilder();
+ vert.append("#version 120\n");
+ vert.append("\n");
+ vert.append("varying vec3 vertNormal;\n");
+ vert.append("varying vec3 lightVector;\n");
+ vert.append("\n");
+ vert.append("void main()\n");
+ vert.append("{\n");
+ vert.append(" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n");
+ vert.append(" mat3 TEMP = gl_NormalMatrix;\n");
+ vert.append(" vec4 TMP2 = vec4(TEMP * gl_Normal, 0.0);\n");
+ vert.append(" vec4 normal = vec4(((TMP2)*0.0001).xyz, 1);\n");
+ vert.append(" gl_FrontColor = gl_Color;\n");
+ vert.append(" gl_FrontSecondaryColor = gl_SecondaryColor;\n");
+ vert.append(" vertNormal = (gl_ModelViewMatrix * vec4(gl_Normal.xyz, 0)).xyz;\n");
+ vert.append(" lightVector = -(gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz + vec3(0,500,0), 1.0)).xyz;\n");
+ vert.append("}\n");
+
+ int vertid = gl.glCreateShader(gl.GL_VERTEX_SHADER);
+ (this.shaders[matid]).vertexShader = vertid;
+ gl.glShaderSource(vertid, 1, new String[] { vert.toString() }, new int[] { vert.length() }, 0);
+ gl.glCompileShader(vertid);
+ int[] sillyarray = new int[1];
+ gl.glGetShaderiv(vertid, 35713, sillyarray, 0);
+ int success = sillyarray[0];
+ if (success == 0) {
+ CharBuffer charBuffer;
+ gl.glGetShaderiv(vertid, 35716, sillyarray, 0);
+ int loglength = sillyarray[0];
+ byte[] _log = new byte[loglength];
+ gl.glGetShaderInfoLog(vertid, loglength, sillyarray, 0, _log, 0);
+ try {
+ charBuffer = Charset.forName("ASCII").newDecoder().decode(ByteBuffer.wrap(_log));
+ } catch (Exception ex) {
+ charBuffer = CharBuffer.wrap("lolfail");
+ }
+ throw new GLException("!Failed to compile KCL vertex shader: " + charBuffer.toString() + "\n" + vert.toString());
+ }
+ // The above shouldn't ever happen in production...
+
+
+
+
+
+
+ StringBuilder frag = new StringBuilder();
+ frag.append("#version 120\n");
+ frag.append("\n");
+ frag.append("varying vec3 vertNormal;\n");
+ frag.append("varying vec3 lightVector;\n");
+ frag.append("\n");
+
+ frag.append("void main()\n");
+ frag.append("{\n");
+ frag.append("\n");
+ frag.append(" float dot_product = max(dot(normalize(lightVector), normalize(vertNormal)), 0.6);\n");
+ frag.append(" dot_product = dot_product - 0.1;\n");
+ frag.append(" gl_FragColor.rgb = dot_product * gl_Color.rgb;\n");
+ frag.append(" gl_FragColor.a = 1.0;\n");
+ frag.append("\n");
+ frag.append("}\n");
+
+ int fragid = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER);
+ shaders[matid].fragmentShader = fragid;
+ gl.glShaderSource(fragid, 1, new String[] { frag.toString() }, new int[] { frag.length()}, 0);
+ gl.glCompileShader(fragid);
+
+ gl.glGetShaderiv(fragid, GL2.GL_COMPILE_STATUS, sillyarray, 0);
+ success = sillyarray[0];
+ if(success == 0)
+ {
+ //string log = gl.glGetShaderInfoLog(fragid);
+ gl.glGetShaderiv(fragid, GL2.GL_INFO_LOG_LENGTH, sillyarray, 0);
+ int loglength = sillyarray[0];
+ byte[] _log = new byte[loglength];
+ gl.glGetShaderInfoLog(fragid, loglength, sillyarray, 0, _log, 0);
+ CharBuffer log;
+ try {
+ log = Charset.forName("ASCII").newDecoder().decode(ByteBuffer.wrap(_log));
+ } catch(Exception ex) {
+ log = CharBuffer.wrap("lolfail");
+ }
+ throw new GLException("!Failed to compile fragment shader: " + log.toString() + "\n" + frag.toString());
+ // TODO: better error reporting/logging?
+ }
+
+ int sid = gl.glCreateProgram();
+ shaders[matid].program = sid;
+
+ gl.glAttachShader(sid, vertid);
+ gl.glAttachShader(sid, fragid);
+
+ gl.glLinkProgram(sid);
+ gl.glGetProgramiv(sid, GL2.GL_LINK_STATUS, sillyarray, 0);
+ success = sillyarray[0];
+ if(success == 0)
+ {
+ //String log = gl.glGetProgramInfoLog(sid);
+ String log = "TODO: port this excrement from C#";
+ throw new GLException("!Failed to link shader program: " + log);
+ // TODO: better error reporting/logging?
+ }
+
+ ShaderCache.addEntry(hash, vertid, fragid, sid);
+ }
+
+ // -------------------------------------------------------------------------------------------------------------------------
+
+ public KclRenderer() {
+
+ }
+
+ /**
+ * Attempt to load model from {@code modelname}.
+ * @param info
+ * @param modelName
+ * @throws GLException
+ */
+ public KclRenderer(RenderInfo info, String modelName) throws GLException {
+ initModel(info, modelName);
+ }
+
+ public final boolean isValidModel() {
+ return model != null && data != null;
+ }
+ //=====================================================================
+ // These functions are to be called in the Ctor for custom renderers
+
+ protected void initModel(RenderInfo info, String modelName) throws GLException
+ {
+ ctor_doNonSpecialModelLoad(info, modelName);
+ }
+
+ /**
+ * The default sequence to load a model.
+ * @param modelName
+ */
+ protected final void ctor_doNonSpecialModelLoad(RenderInfo info, String modelName) {
+ try
+ {
+ archive = ctor_loadArchive(modelName);
+ }
+ catch(Exception ex)
+ {
+ return;
+ }
+
+ if (archive == null)
+ return; //No archive bruh
+
+ model = ctor_loadModel(modelName, archive);
+ data = ctor_loadData(modelName, archive);
+
+ if (!isValidModel())
+ {
+ try{
+ archive.close();
+ }
+ catch(Exception ex)
+ {
+
+ }
+ return;
+ }
+ ctor_uploadData(info);
+ }
+
+ /**
+ * Load BMD/BDL from ARC using {@code modelname}.
+ * NOTE: {@code modelname} is first run through Substitutor.
+ * @param modelName the name of the object
+ * @param archive a
+ * @throws GLException
+ */
+ protected final Kcl ctor_loadModel(String modelName, RarcFile archive) throws GLException {
+ // Load the KCL file
+ try
+ {
+ if (archive.fileExists("/" + modelName + "/" + modelName + ".kcl"))
+ return new Kcl(archive.openFile("/" + modelName + "/" + modelName + ".kcl"));
+ }
+ catch(IOException up)
+ {
+ }
+ return null;
+ }
+
+ protected final Bcsv ctor_loadData(String modelName, RarcFile archive) {
+ // Load the PA file
+ try
+ {
+ if (archive.fileExists("/" + modelName + "/" + modelName + ".pa"))
+ return new Bcsv(archive.openFile("/" + modelName + "/" + modelName + ".pa"));
+ }
+ catch(IOException up)
+ {
+ }
+ return null;
+ }
+
+ /**
+ * Load the Archive for the model (either from the current or base directories)
+ * @param modelName The name of the model to try to load the archive for
+ * @throws IOException
+ */
+ protected final RarcFile ctor_loadArchive(String modelName) throws IOException {
+ String arcPath = Whitehole.createResourceArcPath(modelName);
+
+ boolean UseAbsolutePath = false;
+ if (arcPath == null) {
+ //If a model is not found, we can try looking in the base directory instead
+ //We will only check ObjectData, as a vanilla game will not have models elsewhere
+ String base = Settings.getBaseGameDir();
+ if (base == null || base.length() == 0)
+ return null; //No base game path set
+
+ arcPath = String.format("%s/%s/%s.arc", base, "ObjectData", modelName);
+ UseAbsolutePath = true;
+ File fi = new File(arcPath);
+ if (!fi.exists())
+ return null;
+ }
+
+ FileBase fi = UseAbsolutePath ? new ExternalFile(arcPath) : Whitehole.getCurrentGameFileSystem().openFile(arcPath);
+ return new RarcFile(fi);
+ }
+
+ protected final void ctor_uploadData(RenderInfo info) throws GLException {
+ if(model == null) {
+ return;
+ }
+
+ GL2 gl = info.drawable.getGL().getGL2();
+
+ String extensions = gl.glGetString(GL2.GL_EXTENSIONS);
+ hasShaders = extensions.contains("GL_ARB_shading_language_100") &&
+ extensions.contains("GL_ARB_shader_objects") &&
+ extensions.contains("GL_ARB_vertex_shader") &&
+ extensions.contains("GL_ARB_fragment_shader");
+
+ if(hasShaders) {
+ shaders = new Shader[1];
+
+ for(int i = 0; i < 1; i++) {
+ try {
+ generateShaders(gl, i);
+ }
+ catch(GLException ex) {
+ // really ugly hack
+ if(ex.getMessage().charAt(0) == '!') {
+ //StringBuilder src = new StringBuilder(10000);
+ //int lolz;
+ //gl.glGetShaderSource(shaders[i].FragmentShader, 10000 out, lolz, src);
+ //System.Windows.Forms.MessageBox.Show(ex.Message + "\n" + src.ToString());
+ throw ex;
+ }
+
+ shaders[i].program = 0;
+ } catch(Exception ex) {
+ //hope it continues?
+ }
+ }
+ }
+ }
+
+ protected Bcsv.Entry getDataFromGroupIdx(int idx)
+ {
+ return data.entries.get(idx);
+ }
+
+ @Override
+ public void close(RenderInfo info) throws GLException {
+ if(model == null)
+ return;
+
+ GL2 gl = info.drawable.getGL().getGL2();
+
+ if(hasShaders) {
+ for(Shader shader : shaders) {
+ if(!ShaderCache.removeEntry(shader.cacheKey))
+ continue;
+
+ if(shader.vertexShader > 0) {
+ gl.glDetachShader(shader.program, shader.vertexShader);
+ gl.glDeleteShader(shader.vertexShader);
+ }
+
+ if(shader.fragmentShader > 0) {
+ gl.glDetachShader(shader.program, shader.fragmentShader);
+ gl.glDeleteShader(shader.fragmentShader);
+ }
+
+ if(shader.program > 0)
+ gl.glDeleteProgram(shader.program);
+ }
+ }
+
+ if(model != null) {
+ try { model.close(); archive.close(); }
+ catch(IOException ex) {}
+ }
+ }
+
+ @Override
+ public void releaseStorage() {
+ try
+ {
+ if (model != null)
+ model.close();
+
+ if (archive != null)
+ archive.close();
+
+ model = null;
+ archive = null;
+ }
+ catch(Exception ex)
+ {
+
+ }
+ }
+
+ @Override
+ public boolean gottaRender(RenderInfo info) throws GLException {
+ return info.renderMode != RenderMode.TRANSLUCENT;
+ }
+
+ @Override
+ public void render(RenderInfo info) throws GLException {
+ GL2 gl = info.drawable.getGL().getGL2();
+
+ if(info.renderMode != RenderMode.PICKING && info.renderMode != RenderMode.HIGHLIGHT)
+ gl.glColor4f(1f, 1f, 1f, 1f);
+
+ if(model == null)
+ return;
+
+ gl.glPushMatrix();
+
+ gl.glTranslatef(translation.x, translation.y, translation.z);
+ gl.glRotatef(rotation.x, 0f, 0f, 1f);
+ gl.glRotatef(rotation.y, 0f, 1f, 0f);
+ gl.glRotatef(rotation.z, 1f, 0f, 0f);
+ gl.glScalef(scale.x, scale.y, scale.z);
+
+
+ if(info.renderMode != RenderMode.PICKING && info.renderMode != RenderMode.HIGHLIGHT)
+ if(gl.isFunctionAvailable("glUseProgram"))
+ gl.glUseProgram(shaders[0].program); // TEMPORARY
+
+ gl.glEnable(GL2.GL_CULL_FACE);
+ gl.glCullFace(GL2.GL_BACK);
+ gl.glEnable(GL2.GL_DEPTH_TEST);
+ gl.glDepthFunc(GL2.GL_LESS);
+ gl.glDepthMask(true);
+ gl.glBindTexture(GL2.GL_TEXTURE_2D, 0);
+ gl.glDisable(GL2.GL_TEXTURE_2D);
+ gl.glBegin(GL2.GL_TRIANGLES);
+
+ for(Kcl.Primitive prim : model.triangles)
+ {
+
+ // Create triangle
+ Vec3f Dir = model.normalArray[prim.directionIndex];
+ Vec3f A = model.positionArray[prim.positionIndex];
+ Vec3f CrossA = new Vec3f(), CrossB = new Vec3f();
+ Vec3f.cross(model.normalArray[prim.normalAIndex], Dir, CrossA);
+ Vec3f.cross(model.normalArray[prim.normalBIndex], Dir, CrossB);
+ Vec3f B = new Vec3f(), C = new Vec3f(), N = new Vec3f();
+ Vec3f.add(A, CrossB.multiplyScalar(prim.Length / Vec3f.dot(CrossB, model.normalArray[prim.normalCIndex])), B);
+ Vec3f.add(A, CrossA.multiplyScalar(prim.Length / Vec3f.dot(CrossA, model.normalArray[prim.normalCIndex])), C);
+ Vec3f V = new Vec3f(), V2 = new Vec3f();
+ Vec3f.subtract(B, A, V);
+ Vec3f.subtract(C, A, V2);
+ Vec3f.cross(V, V2, N);
+ Vec3f.normalize(N, N);
+
+ Vec3f[] pp = new Vec3f[3];
+ pp[0] = C;
+ pp[1] = B;
+ pp[2] = A;
+
+ for(Vec3f current : pp)
+ {
+ if(info.renderMode != RenderMode.PICKING && info.renderMode != RenderMode.HIGHLIGHT)
+ {
+ Bcsv.Entry entry = getDataFromGroupIdx(prim.groupIndex);
+ Integer FloorCode = entry.getInt("Floor_code");
+ Integer Color = colorTable.getOrDefault(FloorCode, colorTable.get(0));
+ int Hash = Color;
+ float Red = Math.abs((Hash >> 24) & 0xFF) / 255f;
+ float Green = Math.abs((Hash >> 16) & 0xFF) / 255f;
+ float Blue = Math.abs((Hash >> 8) & 0xFF) / 255f;
+ gl.glColor4f(Red, Green, Blue, 1.0f);
+ gl.glSecondaryColor3f(Red, Green, Blue);
+ }
+
+ gl.glNormal3f(-N.x, -N.y, -N.z);
+ gl.glVertex3f(current.x, current.y, current.z);
+ }
+ }
+
+ gl.glEnd();
+ gl.glPopMatrix();
+ }
+
+
+ public static String getAdditiveCacheKey(AbstractObj obj, HashMap params) {
+ return "_KCL_";
+ }
+
+ protected RarcFile archive = null;
+ protected Kcl model = null;
+ protected Bcsv data = null;
+ protected Shader[] shaders = null;
+ protected boolean hasShaders = false;
+ protected Vec3f translation = DEFAULT_TRANSLATION;
+ protected Vec3f rotation = DEFAULT_ROTATION;
+ protected Vec3f scale = DEFAULT_SCALE;
+
+ protected class Shader {
+ public int program, vertexShader, fragmentShader, cacheKey;
+ }
+
+ // Maybe move this to a JSON later? Idk...
+ public static final HashMap colorTable = new HashMap<>();
+
+ // First time ever using this though.
+ static
+ {
+ colorTable.put(0, 0xFFFFFFFF);
+ colorTable.put(1, 0x890000FF);
+ colorTable.put(2, 0xFFC6D6FF);
+ colorTable.put(3, 0xC3FFBCFF);
+ colorTable.put(4, 0xFF0000FF);
+ colorTable.put(5, 0xA9F2FFFF);
+ colorTable.put(6, 0xDBB1FFFF);
+ colorTable.put(7, 0xC076FFFF);
+ colorTable.put(8, 0xAC4BFFFF);
+ colorTable.put(9, 0xEBFFA3FF);
+ colorTable.put(10, 0xFF8800FF);
+ colorTable.put(11, 0xC781FFFF);
+ colorTable.put(12, 0xFF004EFF);
+ colorTable.put(13, 0xFFC032FF);
+ colorTable.put(14, 0xD6FCFFFF);
+ colorTable.put(15, 0xAC9D0EFF);
+
+ colorTable.put(32, 0x7E5600FF);
+ }
+}
diff --git a/src/whitehole/rendering/RendererFactory.java b/src/whitehole/rendering/RendererFactory.java
index 932077c..0ffb08d 100644
--- a/src/whitehole/rendering/RendererFactory.java
+++ b/src/whitehole/rendering/RendererFactory.java
@@ -54,6 +54,13 @@ public static GLRenderer tryCreateBmdRenderer(GLRenderer.RenderInfo info, String
return bmdRenderer;
return createDummyCubeRenderer();
+
+// KclRenderer bmdRenderer = new KclRenderer(info, objModelName);
+//
+// if (bmdRenderer.isValidModel())
+// return bmdRenderer;
+//
+// return createDummyCubeRenderer();
}
public static GLRenderer tryCreateBtiRenderer(GLRenderer.RenderInfo info, String objModelName, Vec3f pt1, Vec3f pt2, boolean vertical) {
diff --git a/src/whitehole/smg/Kcl.java b/src/whitehole/smg/Kcl.java
new file mode 100644
index 0000000..fcf1176
--- /dev/null
+++ b/src/whitehole/smg/Kcl.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 Whitehole Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package whitehole.smg;
+
+import java.io.IOException;
+import whitehole.io.FileBase;
+import whitehole.math.Vec3f;
+import whitehole.util.MathUtil;
+
+/**
+ *
+ * @author Hackio
+ */
+public class Kcl {
+ public Kcl(FileBase _file) throws IOException
+ {
+ file = _file;
+ file.setBigEndian(true);
+
+ int PositionsOffset = file.readInt();
+ int NormalsOffset = file.readInt();
+ int TrianglesOffset = file.readInt() + 0x10; // Still don't know why there's an additional thing needed...
+ int OctreeOffset = file.readInt(); //We're probably not gonna need this for rendering...
+
+ thickness = file.readFloat(); //Might actually use this (Probably not...)
+ Vec3f MinCoords = new Vec3f(file.readFloat(), file.readFloat(), file.readFloat());
+ int MaskX = file.readInt();
+ int MaskY = file.readInt();
+ int MaskZ = file.readInt();
+ int ShiftX = file.readInt();
+ int ShiftY = file.readInt();
+ int ShiftZ = file.readInt();
+
+ int PositionCount = (NormalsOffset - PositionsOffset) / 12;
+ int NormalCount = (TrianglesOffset - NormalsOffset) / 12;
+ int TriangleCount = (OctreeOffset - TrianglesOffset) / 16;
+ int OctreeNodeCount = ((~(int)MaskX >> (int)ShiftX) + 1) * ((~(int)MaskY >> (int)ShiftX) + 1) * ((~(int)MaskZ >> (int)ShiftX) + 1);
+
+ file.position(PositionsOffset);
+ positionArray = new Vec3f[PositionCount];
+ for (int i = 0; i < positionArray.length; i++) {
+ Vec3f newVec = new Vec3f(file.readFloat(), file.readFloat(), file.readFloat());
+ //TEST
+ for (int j = 0; j < i; j++) {
+ float dist = MathUtil.distance(newVec, positionArray[j]);
+ if (dist < 1f)
+ newVec = positionArray[j];
+ }
+ positionArray[i] = newVec;
+ }
+
+ file.position(NormalsOffset);
+ normalArray = new Vec3f[NormalCount];
+ for (int i = 0; i < normalArray.length; i++) {
+ Vec3f newVec = new Vec3f(file.readFloat(), file.readFloat(), file.readFloat());
+ normalArray[i] = newVec;
+ }
+
+ file.position(TrianglesOffset);
+ triangles = new Primitive[TriangleCount];
+ for (int i = 0; i < triangles.length; i++) {
+ Primitive p = new Primitive();
+ p.Length = file.readFloat();
+ p.positionIndex = file.readShort() & 0x0000FFFF;
+ p.directionIndex = file.readShort() & 0x0000FFFF;
+ p.normalAIndex = file.readShort() & 0x0000FFFF;
+ p.normalBIndex = file.readShort() & 0x0000FFFF;
+ p.normalCIndex = file.readShort() & 0x0000FFFF;
+ p.groupIndex = file.readShort() & 0x0000FFFF;
+ triangles[i] = p;
+ }
+
+ // Currently not going to bother adding Octree support...
+ }
+
+ public void save() throws IOException
+ {
+ file.save();
+ }
+
+ public void close() throws IOException
+ {
+ file.close();
+ }
+
+ private FileBase file;
+
+ public final float thickness;
+ public Vec3f[] positionArray;
+ public Vec3f[] normalArray;
+
+ public Primitive[] triangles;
+
+ // Similar to BMD's Primitive class
+ public class Primitive
+ {
+ public float Length;
+
+ public int positionIndex;
+ public int directionIndex;
+ public int normalAIndex;
+ public int normalBIndex;
+ public int normalCIndex;
+ public int groupIndex; //This is the .PA index
+ }
+}
diff --git a/src/whitehole/util/MathUtil.java b/src/whitehole/util/MathUtil.java
index 79acab4..7f852bd 100644
--- a/src/whitehole/util/MathUtil.java
+++ b/src/whitehole/util/MathUtil.java
@@ -39,12 +39,18 @@ public static void scaleAndAdd(Vec3f dst, Vec3f a, Vec3f b, float scale)
public static boolean isNearZero(Vec3f v, float min)
{
- boolean r = v.x > -min && v.x < min &&
+ boolean r =
+ v.x > -min && v.x < min &&
v.y > -min && v.y < min &&
v.z > -min && v.z < min;
return r;
}
+ public static float distance(Vec3f vec1, Vec3f vec2)
+ {
+ return (float)Math.sqrt((vec2.x - vec1.x) * (vec2.x - vec1.x) + (vec2.y - vec1.y) * (vec2.y - vec1.y) + (vec2.z - vec1.z) * (vec2.z - vec1.z));
+ }
+
public static void makeAxisVerticalZX(Vec3f axisRight, Vec3f front)
{
vecKillElement(axisRight, Vec3f.unitZ(), front);