Skip to content

Commit

Permalink
Add support for replacing textures in ModelCluster
Browse files Browse the repository at this point in the history
  • Loading branch information
zbx1425 committed Aug 23, 2023
1 parent 28ca633 commit 4760dfb
Show file tree
Hide file tree
Showing 16 changed files with 228 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ public static void init(ResourceManager resourceManager) {
Main.LOGGER.info("MTR-NTE: " + MainClient.modelManager.loadedRawModels.size() + " models loaded, "
+ MainClient.modelManager.uploadedVertArraysCount + " VAOs uploaded.");

/*
Path outputPath = Minecraft.getInstance().gameDirectory.toPath().resolve("mtr-nte-models");
try {
Files.createDirectories(outputPath);
} catch (IOException ignored) {

}
// Debug.saveAllBuiltinModels(outputPath);
} catch (IOException ignored) { }
Debug.saveAllBuiltinModels(outputPath);
*/

mtr.client.TrainClientRegistry.register("dk3", new TrainProperties(
"train_20_2", Text.translatable("train.mtrsteamloco.dk3"),
Expand Down
47 changes: 11 additions & 36 deletions common/src/main/java/cn/zbx1425/mtrsteamloco/Debug.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import mtr.mappings.Text;
import mtr.mappings.Utilities;
import mtr.mappings.UtilitiesClient;
import mtr.model.ModelSimpleTrainBase;
import mtr.render.JonModelTrainRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
Expand All @@ -31,49 +32,31 @@ public class Debug {
public static void saveAllBuiltinModels(Path outputDir) {
mtr.client.TrainClientRegistry.forEach(TransportMode.TRAIN, (trainId, trainProperties) -> {
if (trainProperties.renderer instanceof JonModelTrainRenderer renderer
&& renderer.model != null && renderer.textureId != null) {
&& renderer.model != null && renderer.textureId != null && renderer.model instanceof ModelSimpleTrainBase<?>) {
try {
String textureName = FilenameUtils.getBaseName(renderer.textureId);
RawModel[] modelHead0 = TrainModelCapture.captureModels(
renderer.model, new ResourceLocation(renderer.textureId + ".png"),
1, 3);
ObjModelLoader.saveModels(nameModels(modelHead0),
outputDir.resolve(trainId + "_head0.obj"),
outputDir.resolve(textureName + ".mtl"), false);
RawModel[] modelHead1 = TrainModelCapture.captureModels(
renderer.model, new ResourceLocation(renderer.textureId + ".png"),
0, 3);
ObjModelLoader.saveModels(nameModels(modelHead1),
outputDir.resolve(trainId + "_head1.obj"),
outputDir.resolve(textureName + ".mtl"), false);
RawModel[] modelHead2 = TrainModelCapture.captureModels(
renderer.model, new ResourceLocation(renderer.textureId + ".png"),
2, 3);
ObjModelLoader.saveModels(nameModels(modelHead2),
outputDir.resolve(trainId + "_head2.obj"),
outputDir.resolve(textureName + ".mtl"), false);
RawModel[] modelHead12 = TrainModelCapture.captureModels(
renderer.model, new ResourceLocation(renderer.textureId + ".png"),
0, 1);
ObjModelLoader.saveModels(nameModels(modelHead12),
outputDir.resolve(trainId + "_head12.obj"),
TrainModelCapture.CaptureResult result = TrainModelCapture.captureModels(
renderer.model, new ResourceLocation(renderer.textureId + ".png"));
result.getNamedModels().values().forEach(RawModel::distinct);
ObjModelLoader.saveModels(result.getNamedModels(),
outputDir.resolve(trainId + ".obj"),
outputDir.resolve(textureName + ".mtl"), false);

if (!Files.exists(outputDir.resolve(textureName + ".png"))) {
final List<Resource> resources = UtilitiesClient.getResources(Minecraft.getInstance().getResourceManager(),
new ResourceLocation(renderer.textureId + ".png"));
if (resources.size() > 0) {
if (!resources.isEmpty()) {
try {
try (InputStream is = Utilities.getInputStream(resources.get(0))) {
Files.copy(is, outputDir.resolve(textureName + ".png"));
}
} catch (IOException e) {
e.printStackTrace();
} catch (IOException ex) {
Main.LOGGER.warn("Failed to save texture for " + trainId + ": ", ex);
}
}
}
} catch (IOException ex) {
ex.printStackTrace();
Main.LOGGER.warn("Failed to save model for " + trainId, ex);
}
}
});
Expand All @@ -99,12 +82,4 @@ public static void registerAllModelsAsEyeCandy() {
EyeCandyRegistry.register(key, new EyeCandyProperties(Text.literal(key), entry.getValue()));
}
}

private static Map<String, RawModel> nameModels(RawModel[] capturedModels) {
return Map.of(
"body", capturedModels[0],
"doorXNZN", capturedModels[1], "doorXNZP", capturedModels[2],
"doorXPZN", capturedModels[3], "doorXPZP", capturedModels[4]
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package cn.zbx1425.mtrsteamloco.mixin;

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import mtr.model.ModelSimpleTrainBase;
import mtr.model.ModelTrainBase;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;

@Mixin(ModelSimpleTrainBase.class)
public interface ModelSimpleTrainBaseAccessor {

@Invoker
void invokeRender(PoseStack matrices, VertexConsumer vertices, ModelTrainBase.RenderStage renderStage, int light, float doorLeftX, float doorRightX, float doorLeftZ, float doorRightZ, int currentCar, int trainCars, boolean head1IsFront, boolean renderDetails);

@Invoker
void invokeRenderWindowPositions(PoseStack matrices, VertexConsumer vertices, ModelTrainBase.RenderStage renderStage, int light, int position, boolean renderDetails, float doorLeftX, float doorRightX, float doorLeftZ, float doorRightZ, boolean isEnd1Head, boolean isEnd2Head);

@Invoker
void invokeRenderDoorPositions(PoseStack matrices, VertexConsumer vertices, ModelTrainBase.RenderStage renderStage, int light, int position, boolean renderDetails, float doorLeftX, float doorRightX, float doorLeftZ, float doorRightZ, boolean isEnd1Head, boolean isEnd2Head);

@Invoker
void invokeRenderHeadPosition1(PoseStack matrices, VertexConsumer vertices, ModelTrainBase.RenderStage renderStage, int light, int position, boolean renderDetails, float doorLeftX, float doorRightX, float doorLeftZ, float doorRightZ, boolean useHeadlights);

@Invoker
void invokeRenderHeadPosition2(PoseStack matrices, VertexConsumer vertices, ModelTrainBase.RenderStage renderStage, int light, int position, boolean renderDetails, float doorLeftX, float doorRightX, float doorLeftZ, float doorRightZ, boolean useHeadlights);

@Invoker
void invokeRenderEndPosition1(PoseStack matrices, VertexConsumer vertices, ModelTrainBase.RenderStage renderStage, int light, int position, boolean renderDetails, float doorLeftX, float doorRightX, float doorLeftZ, float doorRightZ);

@Invoker
void invokeRenderEndPosition2(PoseStack matrices, VertexConsumer vertices, ModelTrainBase.RenderStage renderStage, int light, int position, boolean renderDetails, float doorLeftX, float doorRightX, float doorLeftZ, float doorRightZ);

@Invoker(remap = false)
int[] invokeGetWindowPositions();

@Invoker(remap = false)
int[] invokeGetDoorPositions();

@Invoker(remap = false)
int[] invokeGetEndPositions();
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.stream.IntStream;

public class CapturingVertexConsumer implements VertexConsumer {

Expand Down Expand Up @@ -49,6 +50,12 @@ public void beginStage(ResourceLocation texture, ModelTrainBase.RenderStage stag
}
}

public void reset() {
Arrays.setAll(models, ignored -> new RawModel());
Arrays.fill(buildingMeshes, null);
buildingVertex = new Vertex();
}

@Override
public @NotNull VertexConsumer vertex(double x, double y, double z) {
buildingVertex.position = new Vector3f((float) x, (float) y, (float) z);
Expand Down Expand Up @@ -102,8 +109,8 @@ public void endVertex() {
RawMesh buildingMesh = buildingMeshes[meshToUse];
buildingMesh.vertices.add(buildingVertex);
if (buildingMesh.vertices.size() % 4 == 0) {
buildingMesh.faces.addAll(Face.triangulate(
buildingMesh.vertices.size() - 4, buildingMesh.vertices.size() - 1, false));
buildingMesh.faces.add(new Face(
IntStream.range(buildingMesh.vertices.size() - 4, buildingMesh.vertices.size()).toArray()));
}
buildingVertex = new Vertex();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,93 @@
package cn.zbx1425.mtrsteamloco.render.integration;

import cn.zbx1425.mtrsteamloco.mixin.ModelTrainBaseAccessor;
import cn.zbx1425.mtrsteamloco.mixin.ModelSimpleTrainBaseAccessor;
import cn.zbx1425.sowcer.math.Vector3f;
import cn.zbx1425.sowcerext.model.RawModel;
import com.mojang.blaze3d.vertex.PoseStack;
import mtr.model.ModelTrainBase;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.resources.ResourceLocation;

import java.util.Arrays;
import java.util.Map;

public class TrainModelCapture {

public static final float DOOR_OFFSET = 2048;

public static RawModel[] captureModels(ModelTrainBase mtrModel, ResourceLocation texture, int currentCar, int trainCars) {
CapturingVertexConsumer vertexConsumer = new CapturingVertexConsumer();
PoseStack emptyPose = new PoseStack();
for (ModelTrainBase.RenderStage stage : ModelTrainBase.RenderStage.values()) {
vertexConsumer.beginStage(texture, stage);
((ModelTrainBaseAccessor)mtrModel).invokeRender(
emptyPose, vertexConsumer, stage, LightTexture.FULL_BRIGHT,
0, 0, DOOR_OFFSET * 16, DOOR_OFFSET * 16,
currentCar, trainCars, true, true
);
public static CaptureResult captureModels(ModelTrainBase mtrModel, ResourceLocation texture) {
CaptureResult result = new CaptureResult();

CapturingVertexConsumer vertices = new CapturingVertexConsumer();
PoseStack matrices = new PoseStack();
int light = LightTexture.FULL_BRIGHT;
float doorLeftX = 0, doorRightX = 0, doorLeftZ = DOOR_OFFSET * 16, doorRightZ = DOOR_OFFSET * 16;
boolean renderDetails = true, isEnd1Head = false, isEnd2Head = false, head1IsFront = true;

vertices.reset();
for (ModelTrainBase.RenderStage renderStage : ModelTrainBase.RenderStage.values()) {
vertices.beginStage(texture, renderStage);
for (final int position : ((ModelSimpleTrainBaseAccessor) mtrModel).invokeGetWindowPositions()) {
((ModelSimpleTrainBaseAccessor) mtrModel).invokeRenderWindowPositions(matrices, vertices, renderStage, light, position, renderDetails, doorLeftX, doorRightX, doorLeftZ, doorRightZ, isEnd1Head, isEnd2Head);
}
for (final int position : ((ModelSimpleTrainBaseAccessor) mtrModel).invokeGetDoorPositions()) {
((ModelSimpleTrainBaseAccessor) mtrModel).invokeRenderDoorPositions(matrices, vertices, renderStage, light, position, renderDetails, doorLeftX, doorRightX, doorLeftZ, doorRightZ, isEnd1Head, isEnd2Head);
}
}
for (RawModel model : vertexConsumer.models) {
model.applyRotation(Vector3f.XP, 180);
for (RawModel model : vertices.models) model.applyRotation(Vector3f.XP, 180);
result.modelBody = vertices.models[0];
result.modelDoors = Arrays.copyOfRange(vertices.models, 1, 5);

vertices.reset();
for (ModelTrainBase.RenderStage renderStage : ModelTrainBase.RenderStage.values()) {
if (renderStage == ModelTrainBase.RenderStage.ALWAYS_ON_LIGHTS) continue;
vertices.beginStage(texture, renderStage);
((ModelSimpleTrainBaseAccessor)mtrModel).invokeRenderHeadPosition2(matrices, vertices, renderStage, light, ((ModelSimpleTrainBaseAccessor) mtrModel).invokeGetEndPositions()[1], renderDetails, doorLeftX, doorRightX, doorLeftZ, doorRightZ, head1IsFront);
}
for (RawModel model : vertices.models) model.applyRotation(Vector3f.XP, 180);
result.modelHead = vertices.models[0];

vertices.reset();
for (ModelTrainBase.RenderStage renderStage : ModelTrainBase.RenderStage.values()) {
if (renderStage == ModelTrainBase.RenderStage.ALWAYS_ON_LIGHTS) continue;
vertices.beginStage(texture, renderStage);
((ModelSimpleTrainBaseAccessor)mtrModel).invokeRenderEndPosition1(matrices, vertices, renderStage, light, ((ModelSimpleTrainBaseAccessor) mtrModel).invokeGetEndPositions()[0], renderDetails, doorLeftX, doorRightX, doorLeftZ, doorRightZ);
}
return vertexConsumer.models;
for (RawModel model : vertices.models) model.applyRotation(Vector3f.XP, 180);
result.modelEnd = vertices.models[0];

vertices.reset();
vertices.beginStage(texture, ModelTrainBase.RenderStage.ALWAYS_ON_LIGHTS);
((ModelSimpleTrainBaseAccessor)mtrModel).invokeRenderHeadPosition2(matrices, vertices, ModelTrainBase.RenderStage.ALWAYS_ON_LIGHTS, light, ((ModelSimpleTrainBaseAccessor) mtrModel).invokeGetEndPositions()[1], renderDetails, doorLeftX, doorRightX, doorLeftZ, doorRightZ, true);
for (RawModel model : vertices.models) model.applyRotation(Vector3f.XP, 180);
result.modelHeadlight = vertices.models[0];

vertices.reset();
vertices.beginStage(texture, ModelTrainBase.RenderStage.ALWAYS_ON_LIGHTS);
((ModelSimpleTrainBaseAccessor)mtrModel).invokeRenderHeadPosition2(matrices, vertices, ModelTrainBase.RenderStage.ALWAYS_ON_LIGHTS, light, ((ModelSimpleTrainBaseAccessor) mtrModel).invokeGetEndPositions()[1], renderDetails, doorLeftX, doorRightX, doorLeftZ, doorRightZ, false);
for (RawModel model : vertices.models) model.applyRotation(Vector3f.XP, 180);
result.modelTaillight = vertices.models[0];

return result;
}

public static ModelTrainBase getBuiltinTrainModel(String modelName) throws ReflectiveOperationException {
return (ModelTrainBase)Class.forName("mtr.model." + modelName).getConstructor().newInstance();
public static class CaptureResult {

public RawModel modelBody;
public RawModel[] modelDoors;
public RawModel modelHead;
public RawModel modelEnd;
public RawModel modelHeadlight;
public RawModel modelTaillight;

public Map<String, RawModel> getNamedModels() {
return Map.of(
"body", modelBody,
"doorXPZN", modelDoors[0], "doorXPZP", modelDoors[1],
"doorXNZN", modelDoors[2], "doorXNZP", modelDoors[3],
"head", modelHead, "end", modelEnd,
"headlight", modelHeadlight, "taillight", modelTaillight
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ public void load(Map<ResourceLocation, String> scripts) {
scope.put("CycleTracker", scope, new NativeJavaClass(scope, CycleTracker.class));
scope.put("RateLimit", scope, new NativeJavaClass(scope, RateLimit.class));

scope.put("Color", scope, new NativeJavaClass(scope, Color.class));
scope.put("RawModel", scope, new NativeJavaClass(scope, RawModel.class));
scope.put("RawMesh", scope, new NativeJavaClass(scope, RawMesh.class));
scope.put("Matrices", scope, new NativeJavaClass(scope, Matrices.class));
Expand Down Expand Up @@ -80,11 +79,12 @@ public void load(Map<ResourceLocation, String> scripts) {
Map.Entry<ResourceLocation, String> entry = ScriptResourceUtil.scriptsToExecute.get(i);
ScriptResourceUtil.relativeBase = entry.getKey();
rhinoCtx.evaluateString(scope, entry.getValue(), entry.getKey().toString(), 1, null);
ScriptResourceUtil.relativeBase = null;
}

isActive = true;
} catch (Exception ex) {
Main.LOGGER.error("Script", ex);
Main.LOGGER.error("Error in NTE Resource Pack JavaScript", ex);
} finally {
Context.exit();
}
Expand All @@ -105,7 +105,7 @@ public Future<?> callTrainFunction(String function, TrainScriptContext trainCtx)
trainCtx.scriptFinished();
}
} catch (Exception ex) {
Main.LOGGER.error("Script", ex);
Main.LOGGER.error("Error in NTE Resource Pack JavaScript", ex);
isActive = false;
} finally {
Context.exit();
Expand All @@ -128,7 +128,7 @@ public Future<?> callEyeCandyFunction(String function, EyeCandyScriptContext eye
eyeCandyCtx.scriptFinished();
}
} catch (Exception ex) {
Main.LOGGER.error("Script", ex);
Main.LOGGER.error("Error in NTE Resource Pack JavaScript", ex);
isActive = false;
} finally {
Context.exit();
Expand Down
Loading

0 comments on commit 4760dfb

Please sign in to comment.