From 464b2d4e773a22c3c94a6e0f487e6282c5888dcb Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Thu, 20 Aug 2026 19:50:19 +0800 Subject: [PATCH] ref: remove old OBJ module --- .../cam72cam/mod/model/common/mesh/Model.java | 32 +- .../mod/model/common/util/ModelCache.java | 20 +- .../cam72cam/mod/model/obj/FaceAccessor.java | 216 --------- .../cam72cam/mod/model/obj/MTLParser.java | 96 ---- .../java/cam72cam/mod/model/obj/Material.java | 36 -- .../cam72cam/mod/model/obj/OBJBuilder.java | 172 ------- .../java/cam72cam/mod/model/obj/OBJFace.java | 55 --- .../java/cam72cam/mod/model/obj/OBJGroup.java | 43 -- .../java/cam72cam/mod/model/obj/OBJModel.java | 436 ------------------ .../cam72cam/mod/model/obj/OBJParser.java | 274 ----------- .../mod/model/obj/OBJTexturePacker.java | 358 -------------- .../java/cam72cam/mod/model/obj/Vec2f.java | 30 -- .../cam72cam/mod/model/obj/VertexBuffer.java | 32 -- .../CachedTexture.java} | 6 +- .../mod/render/common/ModelConfig.java | 17 +- .../cam72cam/mod/render/obj/OBJRender.java | 212 --------- 16 files changed, 37 insertions(+), 1998 deletions(-) delete mode 100644 src/main/java/cam72cam/mod/model/obj/FaceAccessor.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/MTLParser.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/Material.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/OBJBuilder.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/OBJFace.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/OBJGroup.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/OBJModel.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/OBJParser.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/OBJTexturePacker.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/Vec2f.java delete mode 100644 src/main/java/cam72cam/mod/model/obj/VertexBuffer.java rename src/main/java/cam72cam/mod/render/{obj/OBJTextureSheet.java => common/CachedTexture.java} (74%) delete mode 100644 src/main/java/cam72cam/mod/render/obj/OBJRender.java diff --git a/src/main/java/cam72cam/mod/model/common/mesh/Model.java b/src/main/java/cam72cam/mod/model/common/mesh/Model.java index 6512cc3db..dd5ae2507 100644 --- a/src/main/java/cam72cam/mod/model/common/mesh/Model.java +++ b/src/main/java/cam72cam/mod/model/common/mesh/Model.java @@ -4,7 +4,7 @@ import cam72cam.mod.model.common.ModelLoader; import cam72cam.mod.model.common.util.FaceAccessor; import cam72cam.mod.render.common.ModelRenderer; -import cam72cam.mod.render.obj.OBJTextureSheet; +import cam72cam.mod.render.common.CachedTexture; import cam72cam.mod.resource.Identifier; import java.util.*; @@ -32,9 +32,9 @@ public class Model { public final int packedTextureHeight; private int defaultLodSize; - private final Map> textures = new HashMap<>(); - private final Map> speculars = new HashMap<>(); - private final Map> normals = new HashMap<>(); + private final Map> textures = new HashMap<>(); + private final Map> speculars = new HashMap<>(); + private final Map> normals = new HashMap<>(); public String hash; @@ -66,9 +66,9 @@ public Model(Identifier location, VAOLayout layout, Supplier vboSupplie * @param spec specular sheets per variant/LOD (may be empty) * @param norm normal sheets per variant/LOD (may be empty) */ - public void linkTextures(Map> tex, - Map> spec, - Map> norm) { + public void linkTextures(Map> tex, + Map> spec, + Map> norm) { this.textures.putAll(tex); this.speculars.putAll(spec); this.normals.putAll(norm); @@ -198,17 +198,17 @@ public float[] getVboData() { } /** @return albedo texture sheets, keyed by variant name then LOD size */ - public Map> getTextures() { + public Map> getTextures() { return textures; } /** @return specular texture sheets, keyed by variant name then LOD size */ - public Map> getSpeculars() { + public Map> getSpeculars() { return speculars; } /** @return normal texture sheets, keyed by variant name then LOD size */ - public Map> getNormals() { + public Map> getNormals() { return normals; } @@ -253,18 +253,18 @@ protected final void shareTexturesWith(Model owner) { } private void deallocTextures() { - for (Map lodMap : textures.values()) { - for (OBJTextureSheet texture : lodMap.values()) { + for (Map lodMap : textures.values()) { + for (CachedTexture texture : lodMap.values()) { texture.dealloc(); } } - for (Map lodMap : speculars.values()) { - for (OBJTextureSheet texture : lodMap.values()) { + for (Map lodMap : speculars.values()) { + for (CachedTexture texture : lodMap.values()) { texture.dealloc(); } } - for (Map lodMap : normals.values()) { - for (OBJTextureSheet texture : lodMap.values()) { + for (Map lodMap : normals.values()) { + for (CachedTexture texture : lodMap.values()) { texture.dealloc(); } } diff --git a/src/main/java/cam72cam/mod/model/common/util/ModelCache.java b/src/main/java/cam72cam/mod/model/common/util/ModelCache.java index c9bc68256..8dfe2f693 100644 --- a/src/main/java/cam72cam/mod/model/common/util/ModelCache.java +++ b/src/main/java/cam72cam/mod/model/common/util/ModelCache.java @@ -5,7 +5,7 @@ import cam72cam.mod.model.common.format.Parser; import cam72cam.mod.model.common.material.TextureRepacker; import cam72cam.mod.model.common.mesh.*; -import cam72cam.mod.render.obj.OBJTextureSheet; +import cam72cam.mod.render.common.CachedTexture; import cam72cam.mod.resource.Identifier; import cam72cam.mod.serialization.ResourceCache; import cam72cam.mod.serialization.ResourceCache.GenericByteBuffer; @@ -113,8 +113,8 @@ public Model buildModel(int cacheSeconds) throws IOException { return model; } - private Map> loadTextures(String suffix, int cacheSeconds) throws IOException { - Map> result = new HashMap<>(); + private Map> loadTextures(String suffix, int cacheSeconds) throws IOException { + Map> result = new HashMap<>(); if (!meta.hasKey("packedTextureWidth")) { return result; } @@ -123,16 +123,16 @@ private Map> loadTextures(String int texSize = Math.max(textureWidth, textureHeight); for (String variant : meta.getList("variants", k -> k.getString("variant"))) { // TreeMap keeps LOD sizes sorted so ModelConfig can use floorEntry/lastEntry - NavigableMap lodMap = new TreeMap<>(); - lodMap.put(texSize, new OBJTextureSheet(textureWidth, textureHeight, - cache.getResource(variant + suffix + ".rgba", bm -> getTextureBytes(bm, variant, suffix, null)), - cacheSeconds)); + NavigableMap lodMap = new TreeMap<>(); + lodMap.put(texSize, new CachedTexture(textureWidth, textureHeight, + cache.getResource(variant + suffix + ".rgba", bm -> getTextureBytes(bm, variant, suffix, null)), + cacheSeconds)); for (Integer lodValue : lodValues) { if (lodValue < texSize) { Pair size = scaleSize(textureWidth, textureHeight, lodValue); - lodMap.put(lodValue, new OBJTextureSheet(size.getLeft(), size.getRight(), - cache.getResource(variant + "_" + lodValue + suffix + ".rgba", bm -> getTextureBytes(bm, variant, suffix, lodValue)), - cacheSeconds)); + lodMap.put(lodValue, new CachedTexture(size.getLeft(), size.getRight(), + cache.getResource(variant + "_" + lodValue + suffix + ".rgba", bm -> getTextureBytes(bm, variant, suffix, lodValue)), + cacheSeconds)); } } result.put(variant, lodMap); diff --git a/src/main/java/cam72cam/mod/model/obj/FaceAccessor.java b/src/main/java/cam72cam/mod/model/obj/FaceAccessor.java deleted file mode 100644 index 77b703e35..000000000 --- a/src/main/java/cam72cam/mod/model/obj/FaceAccessor.java +++ /dev/null @@ -1,216 +0,0 @@ -package cam72cam.mod.model.obj; - -import cam72cam.mod.math.Vec3d; - -import javax.annotation.Nonnull; -import java.util.*; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -/** - * A {@link VertexBuffer} API wrapper, making accessing geometry completely separated from backend. - * @deprecated Use common.util.FaceAccessor for new Model framework, this is kept for compatibility. - */ - -public class FaceAccessor implements Iterable { - private final OBJModel model; - - /** - * Current face's vertex accessors, assuming all faces are triangles. - */ - public VertexAccessor v0; - public VertexAccessor v1; - public VertexAccessor v2; - - private final VertexBuffer vbo; - private final boolean canSplit; - private int currentFaceIndex; - private final int startFace; - private final int endFace; - - /** - * Internal, use OBJModel.getFaceAccessor - */ - protected FaceAccessor(OBJModel model) { - this(model, 0, Integer.MAX_VALUE); - } - - protected FaceAccessor(OBJModel model, int startFace, int endFace) { - this(model, startFace, endFace, true, null); - } - - protected FaceAccessor(OBJModel model, int startFace, int endFace, boolean canSplit, VertexBuffer buffer) { - this.model = model; - this.vbo = buffer == null ? model.vbo.buffer.get() : buffer; - int faceCount = this.vbo.data.length / this.vbo.stride / this.vbo.vertsPerFace; - v0 = new VertexAccessor(0); - v1 = new VertexAccessor(1); - v2 = new VertexAccessor(2); - if (endFace < startFace) { - throw new IllegalStateException(); - } - this.startFace = Math.max(0, startFace); - this.endFace = Math.min(faceCount, endFace); - this.currentFaceIndex = this.startFace; - this.canSplit = canSplit; - } - - /** - * Get another {@link FaceAccessor} of certain group. - * @param groupName Desired group's name - * @return A {@link FaceAccessor} of given group, or {@code null} if group not present or this FaceAccessor is already grouped - */ - public FaceAccessor getSubByGroup(String groupName) { - if (!model.groups.containsKey(groupName) || !canSplit) { - return null; - } - OBJGroup group = model.groups.get(groupName); - // Pass buffer instead of caching it - return new FaceAccessor(model, group.faceStart, group.faceStop + 1, false, vbo); - } - - /** - * Convert current accessing face into an {@link OBJFace}. - * @return OBJFace of current face - */ - public OBJFace asOBJFace() { - OBJFace.Vertex vert0 = new OBJFace.Vertex(v0); - OBJFace.Vertex vert1 = new OBJFace.Vertex(v1); - OBJFace.Vertex vert2 = new OBJFace.Vertex(v2); - return new OBJFace(vert0, vert1, vert2, - vbo.hasNormals ? v0.normAsVec3d() - : vert1.pos.subtract(vert0.pos).crossProduct(vert2.pos.subtract(vert0.pos)).normalize()); - } - - /** - * An iterator designed specifically for for-each loop, use {@code asOBJFace} to create snapshot if you want to store it externally. - * @return Iterator of self, only recommend to use in for-each - */ - @Override - @Nonnull - public Iterator iterator() { - return new FaceIterator(); - } - - private class FaceIterator implements Iterator { - private int iteratorIndex; - - public FaceIterator() { - this.iteratorIndex = startFace; - currentFaceIndex = startFace; - } - - @Override - public boolean hasNext() { - return iteratorIndex < endFace; - } - - @Override - public FaceAccessor next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - currentFaceIndex = iteratorIndex++; - return FaceAccessor.this; - } - - @Override - public void remove() { - Iterator.super.remove(); - } - } - - @Override - public Spliterator spliterator() { - return Spliterators.spliterator(iterator(), (endFace - startFace), Spliterator.SIZED | Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL); - } - - public Stream stream() { - return StreamSupport.stream(spliterator(), false); - } - - /** - * Vertex data accessor. Provides typed access to vertex attributes. - *
- * Use snapshot methods (posAsVec3d, etc.) to store data externally. - */ - public class VertexAccessor { - public final int vertOffset; - - protected VertexAccessor(int vertOffset) { - this.vertOffset = vertOffset; - } - - public Vec3d posAsVec3d() { - return new Vec3d(x(), y(), z()); - } - - public Vec2f uvAsVec2f() { - return new Vec2f(u(), v()); - } - - public Vec3d normAsVec3d() { - if (!vbo.hasNormals) { - return Vec3d.ZERO; - } - return new Vec3d(nx(), ny(), nz()); - } - - public float x() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.vertexOffset]; - } - - public float y() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.vertexOffset + 1]; - } - - public float z() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.vertexOffset + 2]; - } - - public float u() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.textureOffset]; - } - - public float v() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.textureOffset + 1]; - } - - public float r() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.colorOffset]; - } - - public float g() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.colorOffset + 1]; - } - - public float b() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.colorOffset + 2]; - } - - public float a() { - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.colorOffset + 3]; - } - - public float nx() { - if (!vbo.hasNormals) { - return 0; - } - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.normalOffset]; - } - - public float ny() { - if (!vbo.hasNormals) { - return 0; - } - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.normalOffset + 1]; - } - - public float nz() { - if (!vbo.hasNormals) { - return 0; - } - return vbo.data[(currentFaceIndex * 3 + vertOffset) * vbo.stride + vbo.normalOffset + 2]; - } - } -} diff --git a/src/main/java/cam72cam/mod/model/obj/MTLParser.java b/src/main/java/cam72cam/mod/model/obj/MTLParser.java deleted file mode 100644 index 4173b0bbf..000000000 --- a/src/main/java/cam72cam/mod/model/obj/MTLParser.java +++ /dev/null @@ -1,96 +0,0 @@ -package cam72cam.mod.model.obj; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -public class MTLParser { - public static List parse(InputStream stream) throws IOException { - List materials = new ArrayList<>(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { - String line; - String materialName = null; - String map_Kd = null; - String map_Bump = null; - String map_Ns = null; - Float KdR = null; - Float KdG = null; - Float KdB = null; - Float KdA = null; - - while ((line = reader.readLine()) != null) { - if (line.startsWith("#")) { - continue; - } - if (line.length() == 0) { - continue; - } - String[] parts = line.split(" "); - switch (parts[0]) { - case "newmtl": - if (materialName != null) { - materials.add(new Material(materialName, map_Kd, map_Bump, map_Ns, KdR, KdG, KdB, KdA)); - } - materialName = parts[1]; - for (int i = 2; i < parts.length; i++) { - materialName += " " + parts[i]; - } - map_Kd = null; - KdR = null; - KdG = null; - KdB = null; - KdA = null; - break; - case "Ka": - break; - case "Kd": - KdR = Float.parseFloat(parts[1]); - KdG = Float.parseFloat(parts[2]); - KdB = Float.parseFloat(parts[3]); - if (parts.length > 4) { - KdA = Float.parseFloat(parts[4]); - } else { - KdA = 1.0f; - } - break; - case "Ks": - break; - case "map_Kd": - map_Kd = parts[1]; - break; - case "map_Bump": - map_Bump = parts[1]; - break; - case "map_Ns": - map_Ns = parts[1]; - break; - case "Ns": - //Ignore - break; - case "Ke": - //Ignore - break; - case "Ni": - //Ignore - break; - case "d": - //ignore - break; - case "illum": - //ignore - break; - default: - //System.out.println("MTL: ignored line '" + line + "'"); - break; - } - } - if (materialName != null) { - materials.add(new Material(materialName, map_Kd, map_Bump, map_Ns, KdR, KdG, KdB, KdA)); - } - return materials; - } - } -} \ No newline at end of file diff --git a/src/main/java/cam72cam/mod/model/obj/Material.java b/src/main/java/cam72cam/mod/model/obj/Material.java deleted file mode 100644 index 01e69e136..000000000 --- a/src/main/java/cam72cam/mod/model/obj/Material.java +++ /dev/null @@ -1,36 +0,0 @@ -package cam72cam.mod.model.obj; - -public class Material { - public final String name; - - public final String texKd; - public final String texBump; - public final String texNs; - - public final float KdR; - public final float KdG; - public final float KdB; - public final float KdA; - public boolean used; - - public int copiesU; - public int copiesV; - - public Material(String name, String texKd, String texBump, String texNs, Float kdR, Float kdG, Float kdB, Float kdA) { - this.name = name; - this.texKd = texKd; - this.texBump = texBump; - this.texNs = texNs; - KdR = kdR == null ? 1 : kdR; - KdG = kdG == null ? 1 : kdG; - KdB = kdB == null ? 1 : kdB; - KdA = kdA == null ? 1 : kdA; - copiesU = 1; - copiesV = 1; - used = false; - } - - public boolean hasTexture() { - return texKd != null; - } -} diff --git a/src/main/java/cam72cam/mod/model/obj/OBJBuilder.java b/src/main/java/cam72cam/mod/model/obj/OBJBuilder.java deleted file mode 100644 index 8193beb84..000000000 --- a/src/main/java/cam72cam/mod/model/obj/OBJBuilder.java +++ /dev/null @@ -1,172 +0,0 @@ -package cam72cam.mod.model.obj; - -import cam72cam.mod.Config; -import cam72cam.mod.ModCore; -import cam72cam.mod.resource.Identifier; -import cam72cam.mod.serialization.ResourceCache; - -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.*; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -public class OBJBuilder { - - private final VertexBuffer vbo; - private final List groups; - private final OBJTexturePacker packer; - private final int textureWidth; - private final int textureHeight; - private boolean smoothShading; - - public OBJBuilder(Identifier modelLoc, ResourceCache.ResourceProvider input, float scale, float darken, Collection variants) throws IOException { - long start = System.currentTimeMillis(); - if (variants == null) { - variants = Collections.singleton(""); - } - if (variants.isEmpty()) { - variants.add(""); - } - - OBJParser parser = new OBJParser(new ByteArrayInputStream(input.apply(modelLoc)), scale); - this.groups = parser.getGroups(); - this.vbo = parser.getBuffer(); - this.smoothShading = parser.isSmoothShading(); - - if (Config.getMaxTextureSize() <= 0) { - packer = null; - textureWidth = -1; - textureHeight = -1; - return; - } - List materials = new ArrayList<>(); - for (String materialPath : parser.getMaterialLibraries()) { - materials.addAll(MTLParser.parse(new ByteArrayInputStream(input.apply(modelLoc.getRelative(materialPath))))); - } - Map materialLookup = materials.stream().collect(Collectors.toMap(m -> m.name, m -> m)); - - String[] faceMaterials = parser.getFaceMaterials(); - int colorOffset = vbo.colorOffset; - int textureOffset = vbo.textureOffset; - float mult = 1 - darken * 5; - for (String materialName : faceMaterials) { - if (materialName != null) { - Material material = materialLookup.get(materialName); - if (material == null) { - ModCore.warn("Unknown material '%s' in %s", materialName, modelLoc); - colorOffset += vbo.stride * 3; - textureOffset += vbo.stride * 3; - continue; - } - material.used = true; - - float vminU = 0; - float vminV = 0; - float vmaxU = 0; - float vmaxV = 0; - - for (int point = 0; point < 3; point++) { - int pointOffset = point * vbo.stride; - - vbo.data[colorOffset + pointOffset + 0] = material.hasTexture() ? material.KdR * mult : 1; - vbo.data[colorOffset + pointOffset + 1] = material.hasTexture() ? material.KdG * mult : 1; - vbo.data[colorOffset + pointOffset + 2] = material.hasTexture() ? material.KdB * mult : 1; - vbo.data[colorOffset + pointOffset + 3] = material.KdA; - - float u = vbo.data[textureOffset + pointOffset + 0]; - float v = vbo.data[textureOffset + pointOffset + 1]; - - //System.out.println(String.format("u: %s v:%s", u, v)); - if (point == 0) { - vminU = u; - vmaxU = u; - vminV = v; - vmaxV = v; - } else { - vminU = Math.min(vminU, u); - vmaxU = Math.max(vmaxU, u); - vminV = Math.min(vminV, v); - vmaxV = Math.max(vmaxV, v); - } - } - if (material.hasTexture() && vminU != OBJParser.UNSPECIFIED && vminV != OBJParser.UNSPECIFIED) { - int offsetU = (int) Math.floor(vminU); - int offsetV = (int) Math.floor(vminV); - // "Normalize" uv coordinates to start between 0 and 1 and repeat into positive integer space - for (int point = 0; point < 3; point++) { - int pointOffset = point * vbo.stride; - vbo.data[textureOffset + pointOffset + 0] -= offsetU; - vbo.data[textureOffset + pointOffset + 1] -= offsetV; - } - - material.copiesU = Math.max(material.copiesU, (int) Math.ceil(vmaxU - offsetU)); - material.copiesV = Math.max(material.copiesV, (int) Math.ceil(vmaxV - offsetV)); - } else { - for (int point = 0; point < 3; point++) { - int pointOffset = point * vbo.stride; - vbo.data[textureOffset + pointOffset + 0] = 0.5f; - vbo.data[textureOffset + pointOffset + 1] = 0.5f; - } - } - } - colorOffset += vbo.stride * 3; - textureOffset += vbo.stride * 3; - } - - OBJTexturePacker packer = new OBJTexturePacker( - modelLoc, - modelLoc::getRelative, - path -> new ByteArrayInputStream(input.apply(modelLoc.getRelative(path))), - materialLookup.values(), - variants - ); - this.packer = packer; - textureOffset = vbo.textureOffset; - for (String materialName : faceMaterials) { - if (materialName != null) { - OBJTexturePacker.UVConverter converter = packer.converters.get(materialName); - if (converter != null) { - for (int point = 0; point < 3; point++) { - int pointOffset = point * vbo.stride; - vbo.data[textureOffset + pointOffset + 0] = converter.convertU(vbo.data[textureOffset + pointOffset + 0]); - // This is where we flip V - vbo.data[textureOffset + pointOffset + 1] = converter.convertV(vbo.data[textureOffset + pointOffset + 1]); - } - } - } - textureOffset += vbo.stride * 3; - } - this.textureWidth = packer.getWidth(); - this.textureHeight = packer.getHeight(); - ModCore.debug("Building %s took %sms", modelLoc, (System.currentTimeMillis() - start)); - } - - public VertexBuffer vertexBufferObject() { - return vbo; - } - public Map> getTextures() { - return packer != null ? packer.textures : Collections.emptyMap(); - } - public Map> getNormals() { - return packer != null ? packer.normals : Collections.emptyMap(); - } - public Map> getSpeculars() { - return packer != null ? packer.speculars : Collections.emptyMap(); - } - public List getGroups() { - return groups; - } - - public int getTextureWidth() { - return textureWidth; - } - - public int getTextureHeight() { - return textureHeight; - } - public boolean isSmoothShading() { - return smoothShading; - } -} diff --git a/src/main/java/cam72cam/mod/model/obj/OBJFace.java b/src/main/java/cam72cam/mod/model/obj/OBJFace.java deleted file mode 100644 index 27fde9ef7..000000000 --- a/src/main/java/cam72cam/mod/model/obj/OBJFace.java +++ /dev/null @@ -1,55 +0,0 @@ -package cam72cam.mod.model.obj; - -import cam72cam.mod.entity.boundingbox.IBoundingBox; -import cam72cam.mod.math.Vec3d; - -public class OBJFace { - public final Vertex vertex0; - public final Vertex vertex1; - public final Vertex vertex2; - - public final Vec3d normal; - //TODO more accurate one - private IBoundingBox box; - - public OBJFace(Vertex vertex0, Vertex vertex1, Vertex vertex2, Vec3d normal) { - this.vertex0 = vertex0; - this.vertex1 = vertex1; - this.vertex2 = vertex2; - this.normal = normal; - } - - public IBoundingBox getBoundingBox() { - if (box == null) { - //Uses AABB for now but we may want OBB or something more accurate in the future - Vec3d min = vertex0.pos.min(vertex1.pos.min(vertex2.pos)); - Vec3d max = vertex0.pos.max(vertex1.pos.max(vertex2.pos)); - box = IBoundingBox.from(min, max); - } - return box; - } - - public OBJFace scale(double factor) { - return new OBJFace(vertex0.scale(factor), vertex1.scale(factor), vertex2.scale(factor), normal); - } - - public static class Vertex { - public final Vec3d pos; - public final Vec2f uv; - - public Vertex(Vec3d vertex, Vec2f uv) { - this.pos = vertex; - this.uv = uv; - } - - public Vertex(FaceAccessor.VertexAccessor accessor) { - this.pos = accessor.posAsVec3d(); - this.uv = accessor.uvAsVec2f(); - } - - public Vertex scale(double factor) { - Vec3d newPos = pos.scale(factor); - return new Vertex(newPos, uv); - } - } -} diff --git a/src/main/java/cam72cam/mod/model/obj/OBJGroup.java b/src/main/java/cam72cam/mod/model/obj/OBJGroup.java deleted file mode 100644 index 006965c00..000000000 --- a/src/main/java/cam72cam/mod/model/obj/OBJGroup.java +++ /dev/null @@ -1,43 +0,0 @@ -package cam72cam.mod.model.obj; - -import cam72cam.mod.math.Vec3d; -import cam72cam.mod.serialization.TagCompound; - -public class OBJGroup { - public final String name; - public final int faceStart; - public final int faceStop; - public final Vec3d min; - public final Vec3d max; - public final Vec3d normal; - - OBJGroup(String name, int faceStart, int faceStop, Vec3d min, Vec3d max, Vec3d normal) { - this.name = name; - this.faceStart = faceStart; - this.faceStop = faceStop; - this.min = min; - this.max = max; - this.normal = normal; - } - - OBJGroup(TagCompound d) { - this( - d.getString("name"), - d.getInteger("faceStart"), - d.getInteger("faceStop"), - d.getVec3d("min"), - d.getVec3d("max"), - d.getVec3d("normal") - ); - } - - TagCompound toTag() { - return new TagCompound() - .setString("name", name) - .setInteger("faceStart", faceStart) - .setInteger("faceStop", faceStop) - .setVec3d("min", min) - .setVec3d("max", max) - .setVec3d("normal", normal); - } -} diff --git a/src/main/java/cam72cam/mod/model/obj/OBJModel.java b/src/main/java/cam72cam/mod/model/obj/OBJModel.java deleted file mode 100644 index 5fa84df3e..000000000 --- a/src/main/java/cam72cam/mod/model/obj/OBJModel.java +++ /dev/null @@ -1,436 +0,0 @@ -package cam72cam.mod.model.obj; - -import cam72cam.mod.Config; -import cam72cam.mod.ModCore; -import cam72cam.mod.math.Vec3d; -import cam72cam.mod.render.obj.OBJTextureSheet; -import cam72cam.mod.render.obj.OBJRender; -import cam72cam.mod.render.opengl.CustomTexture; -import cam72cam.mod.render.opengl.RenderState; -import cam72cam.mod.resource.Identifier; -import cam72cam.mod.serialization.*; -import cam72cam.mod.serialization.ResourceCache.GenericByteBuffer; -import org.apache.commons.lang3.tuple.Pair; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static cam72cam.mod.model.common.util.ImageUtils.*; - -/** - * @deprecated Use {@link cam72cam.mod.model.common.ModelLoader#load} and {@link cam72cam.mod.model.common.mesh.Model} - */ -public class OBJModel { - private static final OBJTextureSheet defTex = new OBJTextureSheet(1, 1, () -> new ResourceCache.GenericByteBuffer(new int[] { 0x0000FF }), Integer.MAX_VALUE/2); - public final OBJRender vbo; - public final int textureWidth; - public final int textureHeight; - public final int defaultLodSize; - public final Map> textures = new HashMap<>(); - public final Map> normals = new HashMap<>(); - public final Map> speculars = new HashMap<>(); - public final LinkedHashMap groups; //Order by vertex start/stop - public final boolean isSmoothShading; - - public String hash; - - public OBJModel(Identifier modelLoc, float darken) throws Exception { - this(modelLoc, darken, 1, null, 30, null); - } - - public OBJModel(Identifier modelLoc, float darken, Collection variants) throws Exception { - this(modelLoc, darken, 1, variants, 30, null); - } - - public OBJModel(Identifier modelLoc, float darken, double scale) throws Exception { - this(modelLoc, darken, scale, null, 30, null); - } - - public OBJModel(Identifier modelLoc, float darken, double scale, Collection variants, int cacheSeconds, Function> lodSizes) throws Exception { - ModCore.debug("Start obj model " + modelLoc); - List lodValues; - if (lodSizes != null) { - lodValues = lodSizes.apply(Config.getMaxTextureSize()); - } else { - lodValues = new ArrayList<>(); - lodValues.add(Config.getMaxTextureSize()); - } - - String settings = Arrays.toString(new Object[]{ - "v1", - scale, - darken, - variants == null ? "[]" : String.join(":", variants), - lodValues.stream().map(Object::toString).collect(Collectors.joining("-")) - }); - if (Config.DebugTextureSheets) { - settings += "-debug"; - } - ResourceCache cache = new ResourceCache<>( - new Identifier(modelLoc.getDomain(), modelLoc.getPath() + "_" + settings.hashCode()), - provider -> new OBJBuilder(modelLoc, provider, (float)scale, darken, variants) - ); - - Supplier vboData = cache.getResource( - "model.bin", - builder -> new GenericByteBuffer(builder.vertexBufferObject().data) - ); - TagCompound meta = new TagCompound(cache.getResource( - "meta.nbt", - builder -> { - TagCompound data = new TagCompound(); - data.setBoolean("hasVertexNormals", builder.vertexBufferObject().hasNormals); - data.setBoolean("isSmoothShading", builder.isSmoothShading()); - if (Config.getMaxTextureSize() > 0) { - data.setInteger("textureWidth", builder.getTextureWidth()); - data.setInteger("textureHeight", builder.getTextureHeight()); - data.setList("variants", new ArrayList<>(builder.getTextures().keySet()), k -> new TagCompound().setString("variant", k)); - } - data.setBoolean("hasNormals", !builder.getNormals().isEmpty()); - data.setBoolean("hasSpeculars", !builder.getSpeculars().isEmpty()); - data.setList("groups", builder.getGroups(), OBJGroup::toTag); - try { - return new GenericByteBuffer(data.toBytes()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - ).get().bytes()); - - boolean hasVertexNormals = meta.getBoolean("hasVertexNormals"); - this.isSmoothShading = meta.hasKey("isSmoothShading") && meta.getBoolean("isSmoothShading"); - if (Config.getMaxTextureSize() > 0) { - this.textureWidth = meta.getInteger("textureWidth"); - this.textureHeight = meta.getInteger("textureHeight"); - - boolean hasNormals = meta.getBoolean("hasNormals"); - boolean hasSpeculars = meta.getBoolean("hasSpeculars"); - - - for (String variant : meta.getList("variants", k -> k.getString("variant"))) { - ModCore.debug("%s : tex %s", modelLoc, variant); - Map baseTexLodMap = new HashMap<>(); - - int texSize = Math.max(textureWidth, textureHeight); - Supplier texData = cache.getResource(variant + ".rgba", builder -> { - BufferedImage img = builder.getTextures().get(variant).get(); - if (Config.DebugTextureSheets) { - try { - File cacheFile = ModCore.cacheFile(new Identifier(modelLoc.getDomain() + "debug", modelLoc.getPath() + "_" + variant + ".png")); - ModCore.info("Writing debug to " + cacheFile); - ImageIO.write(img, "png", cacheFile); - } catch (IOException e) { - ModCore.catching(e); - } - } - return new GenericByteBuffer(toRGBA(img)); - }); - baseTexLodMap.put(texSize, new OBJTextureSheet(textureWidth, textureHeight, texData, cacheSeconds)); - - for (Integer lodValue : lodValues) { - if (lodValue < texSize) { - Pair size = scaleSize(textureWidth, textureHeight, lodValue); - Supplier lodData = cache.getResource(variant + String.format("_%s.rgba", lodValue), - builder -> new GenericByteBuffer(toRGBA(scaleImage(builder.getTextures().get(variant).get(), lodValue))) - ); - baseTexLodMap.put(lodValue, new OBJTextureSheet(size.getLeft(), size.getRight(), lodData, cacheSeconds)); - } - } - this.textures.put(variant, baseTexLodMap); - - if (hasNormals) { - Map normalTexLodMap = new HashMap<>(); - try { - Supplier normData = cache.getResource(variant + ".norm", builder -> { - BufferedImage img = builder.getNormals().get(variant).get(); - if (Config.DebugTextureSheets) { - try { - File cacheFile = ModCore.cacheFile(new Identifier(modelLoc.getDomain() + "debug", modelLoc.getPath() + "_" + variant + "_norm.png")); - ModCore.info("Writing debug normal to " + cacheFile); - ImageIO.write(img, "png", cacheFile); - } catch (IOException e) { - ModCore.catching(e); - } - } - return new GenericByteBuffer(toRGBA(img)); - }); - normalTexLodMap.put(texSize, new OBJTextureSheet(textureWidth, textureHeight, normData, cacheSeconds)); - - for (Integer lodValue : lodValues) { - if (lodValue < texSize) { - Pair size = scaleSize(textureWidth, textureHeight, lodValue); - Supplier lodNormData = cache.getResource(variant + "_" + lodValue + ".norm", - builder -> new GenericByteBuffer(toRGBA(scaleImage(builder.getNormals().get(variant).get(), lodValue)))); - normalTexLodMap.put(lodValue, new OBJTextureSheet(size.getLeft(), size.getRight(), lodNormData, cacheSeconds)); - } - } - this.normals.put(variant, normalTexLodMap); - } catch (Exception ex) { - ModCore.warn("Unable to load normal map for %s, %s", modelLoc, ex); - } - } - - if (hasSpeculars) { - Map specularTexLodMap = new HashMap<>(); - try { - Supplier specData = cache.getResource(variant + ".spec", builder -> { - BufferedImage img = builder.getSpeculars().get(variant).get(); - if (Config.DebugTextureSheets) { - try { - File cacheFile = ModCore.cacheFile(new Identifier(modelLoc.getDomain() + "debug", modelLoc.getPath() + "_" + variant + "_spec.png")); - ModCore.info("Writing debug specular to " + cacheFile); - ImageIO.write(img, "png", cacheFile); - } catch (IOException e) { - ModCore.catching(e); - } - } - return new GenericByteBuffer(toRGBA(img)); - }); - specularTexLodMap.put(texSize, new OBJTextureSheet(textureWidth, textureHeight, specData, cacheSeconds)); - - for (Integer lodValue : lodValues) { - if (lodValue < texSize) { - Pair size = scaleSize(textureWidth, textureHeight, lodValue); - Supplier lodSpecData = cache.getResource(variant + "_" + lodValue + ".spec", - builder -> new GenericByteBuffer(toRGBA(scaleImage(builder.getSpeculars().get(variant).get(), lodValue)))); - specularTexLodMap.put(lodValue, new OBJTextureSheet(size.getLeft(), size.getRight(), lodSpecData, cacheSeconds)); - } - } - this.speculars.put(variant, specularTexLodMap); - } catch (Exception ex) { - ModCore.warn("Unable to load specular map for %s, %s", modelLoc, ex); - } - } - } - defaultLodSize = textures.get("").keySet().stream().mapToInt(i -> i).max().getAsInt(); - } else { - defaultLodSize = -1; - this.textureWidth = -1; - this.textureHeight = -1; - } - - this.groups = meta.getList("groups", OBJGroup::new).stream().collect(Collectors.toMap(k -> k.name, v -> v, (x, y) -> y, LinkedHashMap::new)); - - this.vbo = new OBJRender(this, () -> new VertexBuffer(vboData.get().floats(), hasVertexNormals)); - - this.hash = cache.close(); - - ModCore.debug("End obj model " + modelLoc); - } - - public Set groups() { - return groups.keySet(); - } - - public FaceAccessor getFaceAccessor() { - return new FaceAccessor(this); - } - - public Vec3d minOfGroup(Iterable groupNames) { - Vec3d min = null; - for (String group : groupNames) { - Vec3d gmin = groups.get(group).min; - if (min == null) { - min = gmin; - } else { - min = min.min(gmin); - } - } - return min; - } - - public Vec3d maxOfGroup(Iterable groupNames) { - Vec3d max = null; - for (String group : groupNames) { - Vec3d gmax = groups.get(group).max; - if (max == null) { - max = gmax; - } else { - max = max.max(gmax); - } - } - return max; - } - - public Vec3d centerOfGroups(Iterable groupNames) { - Vec3d min = minOfGroup(groupNames); - Vec3d max = maxOfGroup(groupNames); - return new Vec3d((min.x + max.x) / 2, (min.y + max.y) / 2, (min.z + max.z) / 2); - } - - public double heightOfGroups(Iterable groupNames) { - Vec3d min = minOfGroup(groupNames); - Vec3d max = maxOfGroup(groupNames); - return max.y - min.y; - } - - public double lengthOfGroups(Iterable groupNames) { - Vec3d min = minOfGroup(groupNames); - Vec3d max = maxOfGroup(groupNames); - return max.x - min.x; - } - - public double widthOfGroups(Iterable groupNames) { - Vec3d min = minOfGroup(groupNames); - Vec3d max = maxOfGroup(groupNames); - return max.z - min.z; - } - - /** WARNING This is a very slow function and should be used for debug only */ - public List points(OBJGroup group) { - List points = new ArrayList<>(); - VertexBuffer vbo = this.vbo.buffer.get(); - for (int face = group.faceStart; face <= group.faceStop; face++) { - for (int point = 0; point < 3; point++) { - int idx = (face * 3 + point) * vbo.stride + vbo.vertexOffset; - points.add(new Vec3d(vbo.data[idx], vbo.data[idx+1], vbo.data[idx+2])); - } - } - return points; - } - - public Binder binder() { - return new Binder(); - } - - public class Binder { - private boolean wait = false; - private int lodSize = Config.MaxTextureSize; - private String texName = ""; - - private Binder() { - - } - - public Binder synchronous() { - this.wait = true; - return this; - } - - public Binder lod(int lodSize) { - this.lodSize = lodSize; - return this; - } - - public Binder texture(String texName) { - this.texName = texName; - return this; - } - - public void apply(RenderState state) { - if (OBJModel.this.textures.get(texName) == null) { - texName = ""; // Default - } - - OBJTextureSheet tex = OBJModel.this.textures.get(texName).get(lodSize); - if (tex == null) { - tex = OBJModel.this.textures.get(texName).get(defaultLodSize); - } - if (wait) { - state.texture(tex.synchronous(true)); - } else { - // Start load even if not loaded - tex.getId(); - - if (!tex.isLoaded()) { - // Try to find a loaded LOD, with a sane default - tex = OBJModel.this.textures.get(texName).values().stream() - .filter(CustomTexture::isLoaded) - .findAny().orElse(null); - } - if (tex != null) { - state.texture(tex); - } else { - state.texture(defTex.synchronous(true)); - } - } - - OBJTextureSheet normTex = null; - if (OBJModel.this.normals.containsKey(texName)) { - Map normLodMap = OBJModel.this.normals.get(texName); - normTex = normLodMap.get(lodSize); - if (normTex == null) { - normTex = normLodMap.get(defaultLodSize); - } - } - if (normTex != null) { - if (wait) { - state.normals(normTex.synchronous(true)); - } else { - normTex.getId(); - if (!normTex.isLoaded()) { - normTex = OBJModel.this.normals.get(texName).values().stream() - .filter(CustomTexture::isLoaded) - .findAny().orElse(null); - } - if (normTex != null) { - state.normals(normTex); - } else { - state.normals(defTex); - } - } - } else { - state.normals(defTex); - } - - OBJTextureSheet specTex = null; - if (OBJModel.this.speculars.containsKey(texName)) { - Map specLodMap = OBJModel.this.speculars.get(texName); - specTex = specLodMap.get(lodSize); - if (specTex == null) { - specTex = specLodMap.get(defaultLodSize); - } - } - if (specTex != null) { - if (wait) { - state.specular(specTex.synchronous(true)); - } else { - specTex.getId(); - if (!specTex.isLoaded()) { - specTex = OBJModel.this.speculars.get(texName).values().stream() - .filter(CustomTexture::isLoaded) - .findAny().orElse(null); - } - if (specTex != null) { - state.specular(specTex); - } else { - state.specular(defTex); - } - } - } else { - state.specular(defTex); - } - - state.smooth_shading(OBJModel.this.isSmoothShading); - } - - public OBJRender.Binding bind(RenderState state) { - return bind(state, false); - } - - public OBJRender.Binding bind(RenderState state, boolean waitForLoad) { - state = state.clone(); - apply(state); - return vbo.bind(state, waitForLoad); - } - - public OBJRender.Builder builder() { - return vbo.subModel(this::apply); - } - } - - public void free() { - for (Map lodMap : textures.values()) { - for (OBJTextureSheet texture : lodMap.values()) { - texture.dealloc(); - } - } - vbo.free(); - } -} diff --git a/src/main/java/cam72cam/mod/model/obj/OBJParser.java b/src/main/java/cam72cam/mod/model/obj/OBJParser.java deleted file mode 100644 index 533f459d3..000000000 --- a/src/main/java/cam72cam/mod/model/obj/OBJParser.java +++ /dev/null @@ -1,274 +0,0 @@ -package cam72cam.mod.model.obj; - -import cam72cam.mod.math.Vec3d; -import cam72cam.mod.model.common.util.Buffers.*; - -import java.io.*; -import java.util.*; -import java.util.stream.Collectors; - -public class OBJParser { - public static final float UNSPECIFIED = Float.MIN_VALUE; - - // 3 floats per vertex (x, y, z) - private final FloatBuffer vertices = new FloatBuffer(1024); - // 3 floats per normal (x, y, z) - private final FloatBuffer vertexNormals = new FloatBuffer(1024); - // 2 floats per normal (u, v) - private final FloatBuffer vertexTextures = new FloatBuffer(1024); - // 3 ints per vert, 3 verts per face (v1, vt1, vn1, v2, vt2, vn2, v3, vt3, vn3) - private final IntBuffer faceVerts = new IntBuffer(1024); - // 1 int per face, which face to use (mtlLookup) - private final List faceMaterials = new ArrayList<>(); - // List of material files to load as part of this obj - private final List materialLibraries = new ArrayList<>(); - // Group -> Face # ranges - private final List groups = new ArrayList<>(); - - private final float scale; - - private final List correctedGroups; - private final VertexBuffer buffer; - private final String[] correctedFaceMaterials; - private boolean smoothShading = false; - - private String currentMaterial = null; - private int currentGroupStart = 0; - private String currentGroupName = "defaultName"; - private boolean hasNormals = true; - - public OBJParser(InputStream stream, float scale) throws IOException { - this.scale = scale; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { - String line; - while ((line = reader.readLine()) != null) { - if (line.startsWith("#")) { - continue; - } - if (line.length() == 0) { - continue; - } - String[] args = line.split(" "); - String cmd = args[0]; - switch (cmd) { - case "mtllib": - addMaterialLibrary(args[1]); - break; - case "usemtl": - if (args.length >= 2) { - String mtlName = args[1]; - for (int i = 2; i < args.length; i++) { - mtlName += " " + args[i]; - } - setCurrentMTL(mtlName); - } else { - setCurrentMTL("undefined"); - } - break; - case "o": - case "g": - String groupName = args[1]; - for (int i = 2; i < args.length; i++) { - groupName += " " + args[i]; - } - addGroup(groupName); - break; - case "v": - addVertex(args[1], args[2], args[3]); - break; - case "vn": - addVertexNormal(args[1], args[2], args[3]); - break; - case "vt": - addVertexTexture(args[1], args[2]); - break; - case "f": - if (args.length == 4) { - addFace(args[1], args[2], args[3]); - } else if (args.length == 5) { - addFace(args[1], args[2], args[3]); - addFace(args[3], args[4], args[1]); - } else { - for (int i = 2; i < args.length - 1; i++) { - addFace(args[1], args[i], args[i + 1]); - } - } - break; - case "s": - if (args.length == 2 && args[1].equals("1")) { - // Technically this should be for every group, but this is a close enough approximation - this.smoothShading = true; - } - break; - case "l": - // Ignore - // TODO might be able to use this for details - break; - default: - //System.out.println("OBJ: ignored line '" + line + "'"); - break; - } - } - addGroup(null); // Finalize last group - } - - groups.sort(Comparator.comparing(a -> a.name)); - this.correctedGroups = new ArrayList<>(); - - float[] vertices = this.vertices.array(); - float[] vertexNormals = this.vertexNormals.array(); - float[] vertexTextures = this.vertexTextures.array(); - int[] faceVerts = this.faceVerts.array(); - this.correctedFaceMaterials = new String[faceMaterials.size()]; - - this.buffer = new VertexBuffer(faceMaterials.size(), hasNormals); - - int faceCount = 0; - int vertexOffset = buffer.vertexOffset; - int normalOffset = buffer.normalOffset; - int textureOffset = buffer.textureOffset; - - for (OBJGroup group : groups) { - int startFace = faceCount; - List points = new ArrayList<>(); - // primitive array here only takes up maybe 1-2MB at worst - boolean[] usedVerts = new boolean[this.vertices.size()/3]; - for (int face = group.faceStart; face <= group.faceStop; face++) { - correctedFaceMaterials[faceCount] = faceMaterials.get(face); - for (int point = 0; point < 3; point++) { - int faceVertexIdx = face * 3 * 3 + point * 3; - - int vertex = faceVerts[faceVertexIdx+0] * 3; - float x = vertices[vertex+0]; - float y = vertices[vertex+1]; - float z = vertices[vertex+2]; - buffer.data[vertexOffset+0] = x; - buffer.data[vertexOffset+1] = y; - buffer.data[vertexOffset+2] = z; - vertexOffset += buffer.stride; - - if (!usedVerts[vertex/3]) { - usedVerts[vertex/3] = true; - points.add(new Vec3d(x, y, z)); - } - - int texture = faceVerts[faceVertexIdx+1] * 2; - if (texture >= 0) { - buffer.data[textureOffset+0] = vertexTextures[texture+0]; - buffer.data[textureOffset+1] = vertexTextures[texture+1]; - } else { - buffer.data[textureOffset+0] = UNSPECIFIED; - buffer.data[textureOffset+1] = UNSPECIFIED; - } - textureOffset += buffer.stride; - - if (hasNormals) { - int normal = faceVerts[faceVertexIdx+2] * 3; - buffer.data[normalOffset+0] = vertexNormals[normal+0]; - buffer.data[normalOffset+1] = vertexNormals[normal+1]; - buffer.data[normalOffset+2] = vertexNormals[normal+2]; - normalOffset += buffer.stride; - } - } - faceCount++; - } - - Vec3d first = points.get(0); - Vec3d groupMin = points.stream().reduce(first, Vec3d::min); - Vec3d groupMax = points.stream().reduce(first, Vec3d::max); - Vec3d center = groupMax.add(groupMin).scale(0.5); - - Vec3d min = first; - Vec3d max = first; - // Furthest from center - for (Vec3d point : points) { - if (max.distanceToSquared(center) < point.distanceToSquared(center)) { - max = point; - } - } - // - for (Vec3d point : points) { - if (min.distanceToSquared(max) < point.distanceToSquared(max)) { - min = point; - } - } - Vec3d finalMin = min.lengthSquared() < max.lengthSquared() ? min : max; - Vec3d finalMax = min.lengthSquared() < max.lengthSquared() ? max : min; - List minG = points.stream().filter(p -> p.distanceToSquared(finalMin) < p.distanceToSquared(finalMax)).collect(Collectors.toList()); - List maxG = points.stream().filter(p -> p.distanceToSquared(finalMin) > p.distanceToSquared(finalMax)).collect(Collectors.toList()); - Vec3d minN = minG.stream().reduce(Vec3d.ZERO, Vec3d::add).scale(1. / minG.size()); - Vec3d maxN = maxG.stream().reduce(Vec3d.ZERO, Vec3d::add).scale(1. / maxG.size()); - Vec3d normal = maxN.subtract(minN).normalize(); - - correctedGroups.add(new OBJGroup(group.name, startFace, faceCount-1, groupMin, groupMax, normal)); - } - } - public VertexBuffer getBuffer() { - return buffer; - } - public List getGroups() { - return correctedGroups; - } - public List getMaterialLibraries() { - return materialLibraries; - } - public String[] getFaceMaterials() { - return correctedFaceMaterials; - } - public boolean isSmoothShading() { - return smoothShading; - } - - private void addMaterialLibrary(String lib) { - materialLibraries.add(lib); - } - - private void setCurrentMTL(String name) { - currentMaterial = name.intern(); - } - - private void addGroup(String name) { - if (currentGroupStart != faceMaterials.size()) { - groups.add(new OBJGroup(currentGroupName, currentGroupStart, faceMaterials.size() - 1, null, null, null)); - } - currentGroupName = name; - currentGroupStart = faceMaterials.size(); - } - - private void addVertex(String x, String y, String z) { - vertices.add(Float.parseFloat(x) * scale); - vertices.add(Float.parseFloat(y) * scale); - vertices.add(Float.parseFloat(z) * scale); - } - private void addVertexTexture(String u, String v) { - vertexTextures.add(Float.parseFloat(u)); - vertexTextures.add(Float.parseFloat(v)); - } - private void addVertexNormal(String x, String y, String z) { - vertexNormals.add(Float.parseFloat(x)); - vertexNormals.add(Float.parseFloat(y)); - vertexNormals.add(Float.parseFloat(z)); - } - - private void addFace(String a, String b, String c) { - parsePoint(a); - parsePoint(b); - parsePoint(c); - faceMaterials.add(currentMaterial); - } - - private void parsePoint(String point) { - String[] sp = point.split("/"); - for (int i = 0; i < 3; i++) { - if (i < sp.length && !sp[i].equals("")) { - faceVerts.add(Integer.parseInt(sp[i]) - 1); - } else { - faceVerts.add(-1); - if (i == 2) { - //VN - this.hasNormals = false; - } - } - } - } -} diff --git a/src/main/java/cam72cam/mod/model/obj/OBJTexturePacker.java b/src/main/java/cam72cam/mod/model/obj/OBJTexturePacker.java deleted file mode 100644 index 7c5987710..000000000 --- a/src/main/java/cam72cam/mod/model/obj/OBJTexturePacker.java +++ /dev/null @@ -1,358 +0,0 @@ -package cam72cam.mod.model.obj; - -import cam72cam.mod.Config; -import cam72cam.mod.ModCore; -import cam72cam.mod.model.common.util.ImageUtils; -import cam72cam.mod.resource.Identifier; -import org.apache.commons.lang3.tuple.Pair; - -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.stream.ImageInputStream; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.List; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static cam72cam.mod.model.common.util.ImageUtils.scaleImage; - -/* primer: https://codeincomplete.com/articles/bin-packing/ */ -public class OBJTexturePacker { - private int width = 0; - private int height = 0; - private int scaledWidth = 0; - private int scaledHeight = 0; - private Function paths; - private Function lookup; - - public final Map converters = new HashMap<>(); - public final Map> textures = new HashMap<>(); - public final Map> normals = new HashMap<>(); - public final Map> speculars = new HashMap<>(); - - private final Map imageCache = new HashMap<>(); - - private BufferedImage getCachedImage(String origPath, String variant) { - if (variant != null && !variant.isEmpty()) { - try { - // Variants should only be read once - String path = origPath; - String[] sp = path.split("/"); - String fname = sp[sp.length - 1]; - path = path.replaceAll(fname, variant + "/" + fname); - return ImageIO.read(lookup.apply(path)); - } catch (Exception e) { - //Fallback - return getCachedImage(origPath, null); - } - } - - // Base image should be cached and re-used when applicable - return imageCache.computeIfAbsent(origPath, path -> { - try { - return ImageIO.read(lookup.apply(origPath)); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } - - class Node { - Dimension size; - List materials; - Material texture; - int width; - int height; - Node down; - Node right; - - public Node(List materials) { - this.materials = materials; - this.width = this.height = 32; - this.texture = null; // Textured images will be color only if the image fails to load. - - size = new Dimension(width, height); - - if (materials.get(0).hasTexture()) { - try { - BufferedImage image = getCachedImage(materials.get(0).texKd, null); - size = new Dimension(image.getWidth(), image.getHeight()); - this.width = materials.stream().mapToInt(x -> x.copiesU).max().getAsInt() * size.width; - this.height = materials.stream().mapToInt(x -> x.copiesV).max().getAsInt() * size.height; - this.texture = materials.get(0); - } catch (Exception e) { - ModCore.catching(e, "Unable to load image %s", paths.apply(materials.get(0).texKd)); - } - } - } - - public Node(int width, int height) { - this.width = width; - this.height = height; - materials = null; - } - - private boolean canFit(Node node) { - return materials == null && this.width >= node.width && this.height >= node.height; - } - - public boolean addNode(Node node) { - if (this.right != null) { - if (this.right.canFit(node)) { - node.right = new Node(this.right.width - node.width, this.right.height); - node.down = new Node(node.width, this.right.height - node.height); - node.right = node.right.width == 0 ? null : node.right; - node.down = node.down.height == 0 ? null : node.down; - this.right = node; - return true; - } else { - boolean recursed = this.right.addNode(node); - if (recursed) { - return true; - } - } - } - if (this.down != null) { - if (this.down.canFit(node)) { - node.right = new Node(this.down.width - node.width, node.height); - node.down = new Node(this.down.width, this.down.height - node.height); - node.right = node.right.width == 0 ? null : node.right; - node.down = node.down.height == 0 ? null : node.down; - this.down = node; - return true; - } else { - boolean recursed = this.down.addNode(node); - if (recursed) { - return true; - } - } - } - return false; - } - - public int getFullWidth() { - return width + (this.right != null ? this.right.getFullWidth() : 0); - } - public int getFullHeight() { - return height + (this.down != null ? this.down.getFullHeight() : 0); - } - - public Node getFurthestRight() { - return right != null ? right.getFurthestRight() : this; - } - - public Node getFurthestDown() { - return down != null ? down.getFurthestDown() : this; - } - - public void converters(int x, int y) { - if (materials != null) { - int copiesU = materials.stream().mapToInt(m -> m.copiesU).max().getAsInt(); - int copiesV = materials.stream().mapToInt(m -> m.copiesV).max().getAsInt(); - UVConverter converter = new UVConverter( - x, y, - size.width, size.height, - copiesU, copiesV, - OBJTexturePacker.this.width, OBJTexturePacker.this.height - ); - for (Material material : materials) { - converters.put(material.name, converter); - } - if (right != null) { - right.converters(x + width, y); - } - if (down != null) { - down.converters(x, y + height); - } - } - } - - public void draw(int x, int y, String variant, Graphics2D graphics, Function texlu) { - if (materials == null) { - graphics.setColor(Color.BLACK); - graphics.fillRect(x, y, width, height); - return; - } - - BufferedImage image; - if (texture != null) { - String origPath = texlu.apply(texture); - image = getCachedImage(origPath, variant); - } else { - Material mat = materials.get(0); - int r = (int) (Math.max(0, mat.KdR) * 255); - int g = (int) (Math.max(0, mat.KdG) * 255); - int b = (int) (Math.max(0, mat.KdB) * 255); - int a = (int) (mat.KdA * 255); - int cint = (a << 24) | (r << 16) | (g << 8) | b; - image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_ARGB); - for (int px = 0; px < this.width; px++) { - for (int py = 0; py < this.height; py++) { - image.setRGB(px, py, cint); - } - } - } - - int copiesU = materials.stream().mapToInt(m -> m.copiesU).max().getAsInt(); - int copiesV = materials.stream().mapToInt(m -> m.copiesV).max().getAsInt(); - - for (int cU = 0; cU < copiesU; cU++) { - for (int cV = 0; cV < copiesV; cV++) { - int offX = x + image.getWidth() * cU; - int offY = y + image.getHeight() * cV; - graphics.drawImage(image, null, offX, offY); - } - } - if (right != null) { - right.draw(x + width, y, variant, graphics, texlu); - } - if (down != null) { - down.draw(x, y + height, variant, graphics, texlu); - } - } - } - - public static class UVConverter { - private final int x; - private final int y; - private final int width; - private final int height; - private final int copiesU; - private final int copiesV; - private final int sheetWidth; - private final int sheetHeight; - - public UVConverter(int x, int y, int width, int height, int copiesU, int copiesV, int sheetWidth, int sheetHeight) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - this.copiesU = copiesU; - this.copiesV = copiesV; - this.sheetWidth = sheetWidth; - this.sheetHeight = sheetHeight; - } - - public float convertU(float u) { - float originU = x / (float) sheetWidth; - float offsetU = u * (float) this.width / sheetWidth; - return originU + offsetU; - } - - public float convertV(float v) { - float originV = 1 - ((y+height*copiesV) / (float) sheetHeight); - float offsetV = v * ((float) this.height / sheetHeight); - return 1-(originV + offsetV); - } - } - - public OBJTexturePacker(Identifier ident, Function paths, Function lookup, Collection materials, Collection variants) { - ImageIO.setUseCache(false); - if (materials.isEmpty()) { - return; - } - - this.paths = paths; - this.lookup = lookup; - - List inputNodes = materials.stream() - .filter(m -> m.used) - .collect(Collectors.groupingBy(k -> k.texKd == null ? k.name : k.texKd)).values().stream() - .map(Node::new) - .sorted(Comparator.comparingInt(x -> -10000 * x.height + x.width)) - .collect(Collectors.toList()); - - Node rootNode = inputNodes.remove(0); - for (Node node : inputNodes) { - if (!rootNode.addNode(node)) { - boolean fitsRight = rootNode.getFullHeight() >= node.height; - boolean fitsDown = rootNode.getFullWidth() >= node.width; - boolean betterFitRight = rootNode.getFullWidth() + node.width < rootNode.getFullHeight() + node.height; - if (fitsRight && (!fitsDown || betterFitRight)) { - // Expand right - rootNode.getFurthestRight().right = new Node(node.width, rootNode.getFullHeight()); - } else if (fitsDown) { - // Expand down - rootNode.getFurthestDown().down = new Node(rootNode.getFullWidth(), node.height); - } else { - throw new RuntimeException("Impossible!!!!"); - } - rootNode.addNode(node); - } - } - - this.width = rootNode.getFullWidth(); - this.height = rootNode.getFullHeight(); - if (needsScaling()) { - Pair size = ImageUtils.scaleSize(width, height, Config.getMaxTextureSize()); - this.scaledWidth = size.getLeft(); - this.scaledHeight = size.getRight(); - } else { - this.scaledWidth = width; - this.scaledHeight = height; - } - rootNode.converters(0, 0); - - for (String variant : variants) { - textures.put(variant, () -> { - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = image.createGraphics(); - rootNode.draw(0, 0, variant, graphics, m -> m.texKd); - if (needsScaling()) { - int originalWidth = image.getWidth(); - int originalHeight = image.getHeight(); - image = scaleImage(image, Config.getMaxTextureSize()); - ModCore.warn("Scaling texture '%s' for %s from (%s x %s) to (%s x %s)", variant, ident, originalWidth, originalHeight, image.getWidth(), image.getHeight()); - } - return image; - }); - - if (materials.stream().anyMatch(x -> x.texBump != null)) { - normals.put(variant, () -> { - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = image.createGraphics(); - rootNode.draw(0, 0, variant, graphics, m -> m.texBump); - if (needsScaling()) { - int originalWidth = image.getWidth(); - int originalHeight = image.getHeight(); - image = scaleImage(image, Config.getMaxTextureSize()); - ModCore.warn("Scaling texture '%s' for %s from (%s x %s) to (%s x %s)", variant, ident, originalWidth, originalHeight, image.getWidth(), image.getHeight()); - } - return image; - }); - } - - if (materials.stream().anyMatch(x -> x.texNs != null)) { - speculars.put(variant, () -> { - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = image.createGraphics(); - rootNode.draw(0, 0, variant, graphics, m -> m.texNs); - if (needsScaling()) { - int originalWidth = image.getWidth(); - int originalHeight = image.getHeight(); - image = scaleImage(image, Config.getMaxTextureSize()); - ModCore.warn("Scaling texture '%s' for %s from (%s x %s) to (%s x %s)", variant, ident, originalWidth, originalHeight, image.getWidth(), image.getHeight()); - } - return image; - }); - } - } - imageCache.clear(); - } - - private boolean needsScaling() { - return width > Config.getMaxTextureSize() || height > Config.getMaxTextureSize(); - } - - public int getWidth() { - return scaledWidth; - } - public int getHeight() { - return scaledHeight; - } -} diff --git a/src/main/java/cam72cam/mod/model/obj/Vec2f.java b/src/main/java/cam72cam/mod/model/obj/Vec2f.java deleted file mode 100644 index 604ab6ee6..000000000 --- a/src/main/java/cam72cam/mod/model/obj/Vec2f.java +++ /dev/null @@ -1,30 +0,0 @@ -package cam72cam.mod.model.obj; - -public class Vec2f { - - public static final Vec2f ZERO = new Vec2f(0, 0); - - public final float x; - public final float y; - - public Vec2f(float x, float y) { - this.x = x; - this.y = y; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Vec2f)) { - return false; - } else { - Vec2f v = (Vec2f) other; - return v.x == x && v.y == y; - } - } - - @Override - public int hashCode() { - return Float.hashCode(x) * 31 + Float.hashCode(y); - } - -} diff --git a/src/main/java/cam72cam/mod/model/obj/VertexBuffer.java b/src/main/java/cam72cam/mod/model/obj/VertexBuffer.java deleted file mode 100644 index d83d32318..000000000 --- a/src/main/java/cam72cam/mod/model/obj/VertexBuffer.java +++ /dev/null @@ -1,32 +0,0 @@ -package cam72cam.mod.model.obj; - -public class VertexBuffer { - public final boolean hasNormals; - public final int vertsPerFace; - //(vx, vy, vz, u, v, r, g, b, a, nx, ny, nz) - public final int stride; - public final float[] data; - public final int vertexOffset; - public final int textureOffset; - public final int colorOffset; - public final int normalOffset; - - private VertexBuffer(float[] data, int faces, boolean hasNormals) { - this.hasNormals = hasNormals; - this.vertsPerFace = 3; - this.vertexOffset = 0; - this.textureOffset = vertexOffset + 3; - this.colorOffset = textureOffset + 2; - this.normalOffset = hasNormals ? colorOffset + 4 : Integer.MIN_VALUE; - this.stride = hasNormals ? normalOffset + 3 : colorOffset + 4; - - this.data = data != null ? data : new float[faces * vertsPerFace * stride]; - } - - public VertexBuffer(int faces, boolean hasNormals) { - this(null, faces, hasNormals); - } - public VertexBuffer(float[] data, boolean hasNormals) { - this(data, 0, hasNormals); - } -} diff --git a/src/main/java/cam72cam/mod/render/obj/OBJTextureSheet.java b/src/main/java/cam72cam/mod/render/common/CachedTexture.java similarity index 74% rename from src/main/java/cam72cam/mod/render/obj/OBJTextureSheet.java rename to src/main/java/cam72cam/mod/render/common/CachedTexture.java index ca7dac5f1..6c0b7ca2b 100644 --- a/src/main/java/cam72cam/mod/render/obj/OBJTextureSheet.java +++ b/src/main/java/cam72cam/mod/render/common/CachedTexture.java @@ -1,4 +1,4 @@ -package cam72cam.mod.render.obj; +package cam72cam.mod.render.common; import cam72cam.mod.render.opengl.CustomTexture; import cam72cam.mod.serialization.ResourceCache; @@ -7,10 +7,10 @@ import java.nio.ByteBuffer; import java.util.function.Supplier; -public class OBJTextureSheet extends CustomTexture { +public class CachedTexture extends CustomTexture { private final Supplier data; - public OBJTextureSheet(int width, int height, Supplier data, int cacheSeconds) { + public CachedTexture(int width, int height, Supplier data, int cacheSeconds) { super(width, height, cacheSeconds); this.data = data; } diff --git a/src/main/java/cam72cam/mod/render/common/ModelConfig.java b/src/main/java/cam72cam/mod/render/common/ModelConfig.java index 5bf40c9f5..1bfb9caf6 100644 --- a/src/main/java/cam72cam/mod/render/common/ModelConfig.java +++ b/src/main/java/cam72cam/mod/render/common/ModelConfig.java @@ -2,7 +2,6 @@ import cam72cam.mod.Config; import cam72cam.mod.model.common.mesh.Model; -import cam72cam.mod.render.obj.OBJTextureSheet; import cam72cam.mod.render.opengl.CustomTexture; import cam72cam.mod.render.opengl.RenderState; import cam72cam.mod.render.opengl.Texture; @@ -16,9 +15,9 @@ * Configures texture variant, LoD size, and whether texture loading should block synchronously. */ public class ModelConfig { - private static final OBJTextureSheet defTex = new OBJTextureSheet(1, 1, () -> new ResourceCache.GenericByteBuffer(new int[] {0x0000FF}), Integer.MAX_VALUE/2); - private static final OBJTextureSheet defSpecTex = new OBJTextureSheet(1, 1, () -> new ResourceCache.GenericByteBuffer(new int[]{0x000000}), Integer.MAX_VALUE/2); - private static final OBJTextureSheet defNormTex = new OBJTextureSheet(1, 1, () -> new ResourceCache.GenericByteBuffer(new int[]{0x8080FF}), Integer.MAX_VALUE/2); + private static final CachedTexture defTex = new CachedTexture(1, 1, () -> new ResourceCache.GenericByteBuffer(new int[] {0x0000FF}), Integer.MAX_VALUE/2); + private static final CachedTexture defSpecTex = new CachedTexture(1, 1, () -> new ResourceCache.GenericByteBuffer(new int[]{0x000000}), Integer.MAX_VALUE/2); + private static final CachedTexture defNormTex = new CachedTexture(1, 1, () -> new ResourceCache.GenericByteBuffer(new int[]{0x8080FF}), Integer.MAX_VALUE/2); private int lod = Config.getMaxTextureSize(); private String variant = ""; @@ -72,18 +71,18 @@ void apply(RenderState state, Model model) { state.smooth_shading(model.isSmoothShading); } - private Texture pickLodSheet(Map> variants, OBJTextureSheet fallback) { - NavigableMap map = variants.get(variant); - NavigableMap lodMap = map != null ? map : variants.get(""); + private Texture pickLodSheet(Map> variants, CachedTexture fallback) { + NavigableMap map = variants.get(variant); + NavigableMap lodMap = map != null ? map : variants.get(""); if (lodMap == null || lodMap.isEmpty()) { return null; } - OBJTextureSheet tex; + CachedTexture tex; if (lod <= 0) { tex = lodMap.lastEntry().getValue(); } else { - Map.Entry entry = lodMap.floorEntry(lod); + Map.Entry entry = lodMap.floorEntry(lod); tex = (entry != null ? entry : lodMap.firstEntry()).getValue(); } diff --git a/src/main/java/cam72cam/mod/render/obj/OBJRender.java b/src/main/java/cam72cam/mod/render/obj/OBJRender.java deleted file mode 100644 index bd22e6f76..000000000 --- a/src/main/java/cam72cam/mod/render/obj/OBJRender.java +++ /dev/null @@ -1,212 +0,0 @@ -package cam72cam.mod.render.obj; - -import cam72cam.mod.model.obj.OBJGroup; -import cam72cam.mod.model.obj.OBJModel; -import cam72cam.mod.model.obj.VertexBuffer; -import cam72cam.mod.util.With; -import cam72cam.mod.render.opengl.VBO; -import cam72cam.mod.render.opengl.RenderState; -import org.lwjgl.opengl.GL11; -import util.Matrix4; - -import javax.vecmath.Matrix3f; -import javax.vecmath.SingularMatrixException; -import javax.vecmath.Vector3f; -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class OBJRender extends VBO { - public final OBJModel model; - public final Supplier buffer; - - public OBJRender(OBJModel model, Supplier buffer) { - super(buffer, s -> {}); - this.model = model; - this.buffer = buffer; - } - - public Binding bind(RenderState state) { - return bind(state, false); - } - public Binding bind(RenderState state, boolean waitForLoad) { - return new Binding(state, waitForLoad); - } - - public class Binding extends VBO.Binding { - protected Binding(RenderState state, boolean wait) { - super(state, wait); - } - - public void draw(Collection groups, Consumer mod) { - if (!isLoaded()) { - return; - } - try (With pus = super.push(mod)) { - draw(groups); - } - } - - /** - * Draw these groups in the VB - */ - public void draw(Collection groups) { - if (!isLoaded()) { - return; - } - List sorted = new ArrayList<>(groups); - sorted.sort(Comparator.naturalOrder()); - int start = -1; - int stop = -1; - for (String group : sorted) { - OBJGroup info = model.groups.get(group); - if (start == stop) { - start = info.faceStart; - stop = info.faceStop + 1; - } else if (info.faceStart == stop) { - stop = info.faceStop + 1; - } else { - GL11.glDrawArrays(GL11.GL_TRIANGLES, start * 3, (stop - start) * 3); - start = info.faceStart; - stop = info.faceStop + 1; - } - } - if (start != stop) { - GL11.glDrawArrays(GL11.GL_TRIANGLES, start * 3, (stop - start) * 3); - } - } - } - - public class Builder { - private final Consumer settings; - private final List> actions = new ArrayList<>(); - - private Builder(Consumer settings) { - this.settings = settings; - } - - private class Buffer { - private VertexBuffer vb; - private float[] built; - private int builtIdx; - - private Buffer() { - this.vb = buffer.get(); - this.built = new float[vb.data.length]; - this.builtIdx = 0; - } - - private void require(int size) { - while (built.length <= builtIdx + size) { - float[] tmp = new float[built.length * 2]; - System.arraycopy(built, 0, tmp, 0, builtIdx); - built = tmp; - } - } - - private void add(float[] buff, Matrix4 m) { - require(buff.length); - - if (m != null) { - Matrix3f normalMat = new Matrix3f( - (float) m.m00, (float) m.m01, (float) m.m02, - (float) m.m10, (float) m.m11, (float) m.m12, - (float) m.m20, (float) m.m21, (float) m.m22 - ); - try { - normalMat.invert(); - } catch (SingularMatrixException ignore) { - //Nothing to do here - } - normalMat.transpose(); - for (int i = 0; i < buff.length; i += vb.stride) { - float x = buff[i+0]; - float y = buff[i+1]; - float z = buff[i+2]; - Vector3f v = new Vector3f(x, y, z); - m.apply(v); - buff[i+0] = v.x; - buff[i+1] = v.y; - buff[i+2] = v.z; - - if (vb.hasNormals) { - float nx = buff[i+0+vb.normalOffset]; - float ny = buff[i+1+vb.normalOffset]; - float nz = buff[i+2+vb.normalOffset]; - Vector3f n = new Vector3f(nx, ny, nz); - normalMat.transform(n); - n.normalize(); - buff[i+0+vb.normalOffset] = n.x; - buff[i+1+vb.normalOffset] = n.y; - buff[i+2+vb.normalOffset] = n.z; - } - } - } - - System.arraycopy(buff, 0, built, builtIdx, buff.length); - builtIdx += buff.length; - } - - public void draw(Matrix4 m) { - if (m == null) { - add(vb.data, null); - } else { - float[] buff = new float[vb.data.length]; - System.arraycopy(vb.data, 0, buff, 0, vb.data.length); - add(buff, m); - } - } - - public void draw(Collection groups, Matrix4 m) { - for (String group : groups) { - OBJGroup info = model.groups.get(group); - - int start = info.faceStart * vb.vertsPerFace * vb.stride; - int stop = (info.faceStop + 1) * vb.vertsPerFace * vb.stride; - - float[] buff = new float[stop - start]; - System.arraycopy(vb.data, start, buff, 0, stop - start); - add(buff, m); - } - } - - public VertexBuffer build() { - float[] out = new float[builtIdx]; - System.arraycopy(built, 0, out, 0, builtIdx); - boolean hasNormals = vb.hasNormals; - vb = null; - built = null; - return new VertexBuffer(out, hasNormals); - } - } - - public void draw() { - draw((Matrix4) null); - } - - public void draw(Matrix4 m) { - actions.add(b -> b.draw(m)); - } - - public void draw(Collection groups) { - draw(groups, null); - } - - public void draw(Collection groups, Matrix4 m) { - actions.add(b -> b.draw(groups, m)); - } - - public VBO build() { - List> actions = new ArrayList<>(this.actions); // Snapshot - return new VBO(() -> { - Buffer buff = new Buffer(); - actions.forEach(c -> c.accept(buff)); - return buff.build(); - }, settings); - } - } - - public Builder subModel(Consumer settings) { - return new Builder(settings); - } -}