Working mtl obj rendering
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package org.saturnclient.cosmetics.hat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.saturnclient.cosmetics.obj.ObjRenderer;
|
||||
|
||||
import de.javagl.obj.Mtl;
|
||||
import de.javagl.obj.Obj;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.feature.FeatureRenderer;
|
||||
@@ -15,12 +17,15 @@ import net.minecraft.util.Identifier;
|
||||
|
||||
public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState, PlayerEntityModel> {
|
||||
static Identifier HAT = Identifier.of("saturnclient", "models/item/horns/horns.obj");
|
||||
static Identifier BLACK_MTL = Identifier.of("saturnclient", "models/item/black.mtl");
|
||||
Obj obj;
|
||||
Map<String, Mtl> mtl;
|
||||
|
||||
public HatFeatureRenderer(FeatureRendererContext<PlayerEntityRenderState, PlayerEntityModel> context) {
|
||||
super(context);
|
||||
try {
|
||||
this.obj = ObjRenderer.loadObj(HAT);
|
||||
this.mtl = ObjRenderer.loadMtl(BLACK_MTL);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -29,6 +34,6 @@ public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState,
|
||||
@Override
|
||||
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light,
|
||||
PlayerEntityRenderState state, float limbAngle, float limbDistance) {
|
||||
ObjRenderer.renderObj(this.obj, matrices, vertexConsumers, light, 0);
|
||||
ObjRenderer.renderObj(this.obj, this.mtl, matrices, vertexConsumers, light, 0);
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,10 @@ package org.saturnclient.cosmetics.obj;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import de.javagl.obj.FloatTuple;
|
||||
import de.javagl.obj.Obj;
|
||||
import de.javagl.obj.ObjFace;
|
||||
import de.javagl.obj.ObjReader;
|
||||
import de.javagl.obj.*;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
@@ -16,6 +15,7 @@ import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class ObjRenderer {
|
||||
|
||||
public static Obj loadObj(Identifier objId) throws IOException {
|
||||
try (InputStream is = MinecraftClient.getInstance().getResourceManager()
|
||||
.getResource(objId).get().getInputStream()) {
|
||||
@@ -23,42 +23,83 @@ public class ObjRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
public static void renderObj(Obj obj, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light,
|
||||
int overlay) {
|
||||
VertexConsumer consumer = vertexConsumers.getBuffer(
|
||||
RenderLayer.getEntityAlpha(Identifier.of("textures/misc/white.png")));
|
||||
public static Map<String, Mtl> loadMtl(Identifier mtlId) throws IOException {
|
||||
try (InputStream is = MinecraftClient.getInstance().getResourceManager()
|
||||
.getResource(mtlId).get().getInputStream()) {
|
||||
List<Mtl> mtlList = MtlReader.read(is);
|
||||
Map<String, Mtl> mtlMap = new java.util.LinkedHashMap<>();
|
||||
for (Mtl mtl : mtlList) {
|
||||
mtlMap.put(mtl.getName(), mtl);
|
||||
}
|
||||
return mtlMap;
|
||||
}
|
||||
}
|
||||
|
||||
public static void renderObj(Obj obj, Map<String, Mtl> mtlMap, MatrixStack matrices,
|
||||
VertexConsumerProvider vertexConsumers, int light, int overlay) {
|
||||
|
||||
MatrixStack.Entry entry = matrices.peek();
|
||||
|
||||
int faces = obj.getNumFaces();
|
||||
Map<String, Obj> materialGroups = ObjSplitting.splitByMaterialGroups(obj);
|
||||
|
||||
System.out.println("Faces " + faces + ", vertices " + obj.getNumVertices());
|
||||
for (Map.Entry<String, Obj> group : materialGroups.entrySet()) {
|
||||
String materialName = group.getKey();
|
||||
Obj groupObj = group.getValue();
|
||||
|
||||
for (int f = 0; f < faces; f++) {
|
||||
ObjFace face = obj.getFace(f);
|
||||
int vertices = face.getNumVertices();
|
||||
Mtl mtl = mtlMap != null ? mtlMap.get(materialName) : null;
|
||||
|
||||
// Reverse winding order: OBJ is CCW, Minecraft expects CW
|
||||
for (int i = vertices - 1; i >= 0; i--) {
|
||||
writeVertex(obj, face, i, consumer, entry, light, overlay);
|
||||
// Resolve texture identifier using same logic as ObjUnbakedModelModel
|
||||
Identifier texture = Identifier.of("minecraft:textures/misc/white.png");
|
||||
|
||||
if (mtl != null) {
|
||||
String mapKd = mtl.getMapKd();
|
||||
if (mapKd != null) {
|
||||
// "#key" references are not resolvable at runtime without a texture map,
|
||||
// so we only support direct resource location strings here
|
||||
if (!mapKd.startsWith("#")) {
|
||||
texture = Identifier.of(mapKd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Diffuse color (Kd)
|
||||
float r = 1f, g = 1f, b = 1f;
|
||||
if (mtl != null) {
|
||||
FloatTuple kd = mtl.getKd();
|
||||
if (kd != null) {
|
||||
r = kd.getX();
|
||||
g = kd.getY();
|
||||
b = kd.getZ();
|
||||
}
|
||||
}
|
||||
|
||||
VertexConsumer consumer = vertexConsumers.getBuffer(RenderLayer.getEntityAlpha(texture));
|
||||
|
||||
int faces = groupObj.getNumFaces();
|
||||
final float fr = r, fg = g, fb = b;
|
||||
|
||||
for (int f = 0; f < faces; f++) {
|
||||
ObjFace face = groupObj.getFace(f);
|
||||
int verts = face.getNumVertices();
|
||||
|
||||
// Your original winding order logic, untouched
|
||||
for (int i = verts - 1; i >= 0; i--) {
|
||||
writeVertex(groupObj, face, i, consumer, entry, light, overlay, fr, fg, fb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Write a single vertex to the VertexConsumer using the OBJ FloatTuple */
|
||||
private static void writeVertex(Obj obj, ObjFace face, int index, VertexConsumer consumer, MatrixStack.Entry matrix,
|
||||
int light, int overlay) {
|
||||
private static void writeVertex(Obj obj, ObjFace face, int index, VertexConsumer consumer,
|
||||
MatrixStack.Entry matrix, int light, int overlay,
|
||||
float r, float g, float b) {
|
||||
|
||||
FloatTuple pos = obj.getVertex(face.getVertexIndex(index));
|
||||
float x = pos.getX();
|
||||
float y = pos.getY();
|
||||
float z = pos.getZ();
|
||||
|
||||
// Default normal
|
||||
float nx = 0f;
|
||||
float ny = 1f;
|
||||
float nz = 0f;
|
||||
|
||||
// Use OBJ normal if available
|
||||
float nx = 0f, ny = 1f, nz = 0f;
|
||||
int normalIndex = face.getNormalIndex(index);
|
||||
if (normalIndex >= 0) {
|
||||
FloatTuple n = obj.getNormal(normalIndex);
|
||||
@@ -67,9 +108,17 @@ public class ObjRenderer {
|
||||
nz = n.getZ();
|
||||
}
|
||||
|
||||
float u = 0f, v = 0f;
|
||||
int texIndex = face.getTexCoordIndex(index);
|
||||
if (texIndex >= 0) {
|
||||
FloatTuple tc = obj.getTexCoord(texIndex);
|
||||
u = tc.getX();
|
||||
v = 1f - tc.getY(); // Flip V: OBJ bottom-left origin -> Minecraft top-left
|
||||
}
|
||||
|
||||
consumer.vertex(matrix, x, y, z)
|
||||
.color(255, 255, 255, 255)
|
||||
.texture(0f, 0f)
|
||||
.color(r, g, b, 1f)
|
||||
.texture(u, v)
|
||||
.overlay(overlay)
|
||||
.light(light)
|
||||
.normal(matrix, nx, ny, nz);
|
||||
|
||||
@@ -10,4 +10,4 @@ Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd saturnclient:item/halo_black
|
||||
map_Kd saturnclient:textures/item/halo_black.png
|
||||
|
||||
@@ -10,4 +10,4 @@ Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd saturnclient:item/halo
|
||||
map_Kd saturnclient:textures/item/halo.png
|
||||
|
||||
Reference in New Issue
Block a user