Working mtl obj rendering
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
package org.saturnclient.cosmetics.hat;
|
package org.saturnclient.cosmetics.hat;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.saturnclient.cosmetics.obj.ObjRenderer;
|
import org.saturnclient.cosmetics.obj.ObjRenderer;
|
||||||
|
|
||||||
|
import de.javagl.obj.Mtl;
|
||||||
import de.javagl.obj.Obj;
|
import de.javagl.obj.Obj;
|
||||||
import net.minecraft.client.render.VertexConsumerProvider;
|
import net.minecraft.client.render.VertexConsumerProvider;
|
||||||
import net.minecraft.client.render.entity.feature.FeatureRenderer;
|
import net.minecraft.client.render.entity.feature.FeatureRenderer;
|
||||||
@@ -15,12 +17,15 @@ import net.minecraft.util.Identifier;
|
|||||||
|
|
||||||
public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState, PlayerEntityModel> {
|
public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState, PlayerEntityModel> {
|
||||||
static Identifier HAT = Identifier.of("saturnclient", "models/item/horns/horns.obj");
|
static Identifier HAT = Identifier.of("saturnclient", "models/item/horns/horns.obj");
|
||||||
|
static Identifier BLACK_MTL = Identifier.of("saturnclient", "models/item/black.mtl");
|
||||||
Obj obj;
|
Obj obj;
|
||||||
|
Map<String, Mtl> mtl;
|
||||||
|
|
||||||
public HatFeatureRenderer(FeatureRendererContext<PlayerEntityRenderState, PlayerEntityModel> context) {
|
public HatFeatureRenderer(FeatureRendererContext<PlayerEntityRenderState, PlayerEntityModel> context) {
|
||||||
super(context);
|
super(context);
|
||||||
try {
|
try {
|
||||||
this.obj = ObjRenderer.loadObj(HAT);
|
this.obj = ObjRenderer.loadObj(HAT);
|
||||||
|
this.mtl = ObjRenderer.loadMtl(BLACK_MTL);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -29,6 +34,6 @@ public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState,
|
|||||||
@Override
|
@Override
|
||||||
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light,
|
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light,
|
||||||
PlayerEntityRenderState state, float limbAngle, float limbDistance) {
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import de.javagl.obj.FloatTuple;
|
import de.javagl.obj.*;
|
||||||
import de.javagl.obj.Obj;
|
|
||||||
import de.javagl.obj.ObjFace;
|
|
||||||
import de.javagl.obj.ObjReader;
|
|
||||||
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.render.RenderLayer;
|
import net.minecraft.client.render.RenderLayer;
|
||||||
@@ -16,6 +15,7 @@ import net.minecraft.client.util.math.MatrixStack;
|
|||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
public class ObjRenderer {
|
public class ObjRenderer {
|
||||||
|
|
||||||
public static Obj loadObj(Identifier objId) throws IOException {
|
public static Obj loadObj(Identifier objId) throws IOException {
|
||||||
try (InputStream is = MinecraftClient.getInstance().getResourceManager()
|
try (InputStream is = MinecraftClient.getInstance().getResourceManager()
|
||||||
.getResource(objId).get().getInputStream()) {
|
.getResource(objId).get().getInputStream()) {
|
||||||
@@ -23,42 +23,83 @@ public class ObjRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void renderObj(Obj obj, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light,
|
public static Map<String, Mtl> loadMtl(Identifier mtlId) throws IOException {
|
||||||
int overlay) {
|
try (InputStream is = MinecraftClient.getInstance().getResourceManager()
|
||||||
VertexConsumer consumer = vertexConsumers.getBuffer(
|
.getResource(mtlId).get().getInputStream()) {
|
||||||
RenderLayer.getEntityAlpha(Identifier.of("textures/misc/white.png")));
|
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();
|
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++) {
|
Mtl mtl = mtlMap != null ? mtlMap.get(materialName) : null;
|
||||||
ObjFace face = obj.getFace(f);
|
|
||||||
int vertices = face.getNumVertices();
|
|
||||||
|
|
||||||
// Reverse winding order: OBJ is CCW, Minecraft expects CW
|
// Resolve texture identifier using same logic as ObjUnbakedModelModel
|
||||||
for (int i = vertices - 1; i >= 0; i--) {
|
Identifier texture = Identifier.of("minecraft:textures/misc/white.png");
|
||||||
writeVertex(obj, face, i, consumer, entry, light, overlay);
|
|
||||||
|
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,
|
||||||
private static void writeVertex(Obj obj, ObjFace face, int index, VertexConsumer consumer, MatrixStack.Entry matrix,
|
MatrixStack.Entry matrix, int light, int overlay,
|
||||||
int light, int overlay) {
|
float r, float g, float b) {
|
||||||
|
|
||||||
FloatTuple pos = obj.getVertex(face.getVertexIndex(index));
|
FloatTuple pos = obj.getVertex(face.getVertexIndex(index));
|
||||||
float x = pos.getX();
|
float x = pos.getX();
|
||||||
float y = pos.getY();
|
float y = pos.getY();
|
||||||
float z = pos.getZ();
|
float z = pos.getZ();
|
||||||
|
|
||||||
// Default normal
|
float nx = 0f, ny = 1f, nz = 0f;
|
||||||
float nx = 0f;
|
|
||||||
float ny = 1f;
|
|
||||||
float nz = 0f;
|
|
||||||
|
|
||||||
// Use OBJ normal if available
|
|
||||||
int normalIndex = face.getNormalIndex(index);
|
int normalIndex = face.getNormalIndex(index);
|
||||||
if (normalIndex >= 0) {
|
if (normalIndex >= 0) {
|
||||||
FloatTuple n = obj.getNormal(normalIndex);
|
FloatTuple n = obj.getNormal(normalIndex);
|
||||||
@@ -67,9 +108,17 @@ public class ObjRenderer {
|
|||||||
nz = n.getZ();
|
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)
|
consumer.vertex(matrix, x, y, z)
|
||||||
.color(255, 255, 255, 255)
|
.color(r, g, b, 1f)
|
||||||
.texture(0f, 0f)
|
.texture(u, v)
|
||||||
.overlay(overlay)
|
.overlay(overlay)
|
||||||
.light(light)
|
.light(light)
|
||||||
.normal(matrix, nx, ny, nz);
|
.normal(matrix, nx, ny, nz);
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ Ke 0.000000 0.000000 0.000000
|
|||||||
Ni 1.450000
|
Ni 1.450000
|
||||||
d 1.000000
|
d 1.000000
|
||||||
illum 2
|
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
|
Ni 1.450000
|
||||||
d 1.000000
|
d 1.000000
|
||||||
illum 2
|
illum 2
|
||||||
map_Kd saturnclient:item/halo
|
map_Kd saturnclient:textures/item/halo.png
|
||||||
|
|||||||
Reference in New Issue
Block a user