Skip to content

Commit da04ee0

Browse files
committed
Add support for NeoForge loader.
1 parent b710115 commit da04ee0

File tree

87 files changed

+2735
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+2735
-1
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
org.gradle.jvmargs=-Xmx4G
22
org.gradle.daemon=false
33

4-
enabledPlatforms=fabric,forge
4+
enabledPlatforms=fabric,forge,neoforge
55

66
modId=tis3d
77
mavenGroup=li.cil.tis3d

neoforge/build.gradle.kts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
val modId: String by project
2+
val minecraftVersion: String = libs.versions.minecraft.get()
3+
val neoforgeVersion: String = libs.versions.neoforge.platform.get()
4+
val neoforgeLoaderVersion: String = libs.versions.neoforge.loader.get()
5+
val architecturyVersion: String = libs.versions.architectury.get()
6+
val manualVersion: String = libs.versions.manual.get()
7+
8+
loom {
9+
accessWidenerPath.set(project(":common").loom.accessWidenerPath)
10+
11+
runs {
12+
create("data") {
13+
data()
14+
programArgs("--all")
15+
programArgs("--mod", modId)
16+
programArgs("--output", file("src/generated/resources/").absolutePath)
17+
programArgs("--existing", project(":common").file("src/main/resources").absolutePath)
18+
programArgs("--existing", file("src/main/resources").absolutePath)
19+
}
20+
}
21+
}
22+
23+
repositories {
24+
maven("https://maven.neoforged.net/releases")
25+
}
26+
27+
dependencies {
28+
neoForge(libs.neoforge.platform)
29+
modImplementation(libs.neoforge.architectury)
30+
31+
// modImplementation(libs.neoforge.manual)
32+
}
33+
34+
tasks {
35+
processResources {
36+
val properties = mapOf(
37+
"version" to project.version,
38+
"minecraftVersion" to minecraftVersion,
39+
"loaderVersion" to neoforgeLoaderVersion,
40+
"neoforgeVersion" to neoforgeVersion,
41+
"architecturyVersion" to architecturyVersion,
42+
"manualVersion" to manualVersion
43+
)
44+
inputs.properties(properties)
45+
filesMatching("META-INF/mods.toml") {
46+
expand(properties)
47+
}
48+
}
49+
50+
remapJar {
51+
atAccessWideners.add("${modId}.accesswidener")
52+
}
53+
}

neoforge/gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
loom.platform = neoforge
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package li.cil.tis3d.api.module.traits.neoforge;
2+
3+
import li.cil.tis3d.api.module.traits.ModuleWithBakedModel;
4+
import net.minecraft.client.renderer.RenderType;
5+
import net.minecraft.client.renderer.block.model.BakedQuad;
6+
import net.minecraft.core.BlockPos;
7+
import net.minecraft.core.Direction;
8+
import net.minecraft.util.RandomSource;
9+
import net.minecraft.world.level.BlockAndTintGetter;
10+
import net.minecraft.world.level.block.state.BlockState;
11+
import net.neoforged.api.distmarker.Dist;
12+
import net.neoforged.api.distmarker.OnlyIn;
13+
import net.neoforged.neoforge.client.ChunkRenderTypeSet;
14+
import net.neoforged.neoforge.client.model.data.ModelData;
15+
16+
import javax.annotation.Nullable;
17+
import java.util.List;
18+
19+
/**
20+
* Forge specific specialization of the {@link ModuleWithBakedModel} interface. Use this when using Forge
21+
* to emit custom quads for a module.
22+
*/
23+
public interface ModuleWithBakedModelNeoForge extends ModuleWithBakedModel {
24+
/**
25+
* Collect model data that is needed to render the quads returned by this module.
26+
* <p>
27+
* The returned value will be passed back into {@link #getQuads(BlockState, Direction, RandomSource, ModelData, RenderType)}.
28+
*
29+
* @param level the render-thread safe world access.
30+
* @param pos the position of the casing.
31+
* @param state the current block-state of the casing.
32+
* @param data the incoming model data to wrap/expand.
33+
* @return model data needed for rendering.
34+
*/
35+
@OnlyIn(Dist.CLIENT)
36+
default ModelData getModelData(final BlockAndTintGetter level, final BlockPos pos, final BlockState state, final ModelData data) {
37+
return data;
38+
}
39+
40+
/**
41+
* Called to obtain quads to use for the specified side instead of the casing's default ones. Will be called
42+
* directly from the casing's {@link net.minecraft.client.resources.model.BakedModel#getQuads(BlockState, Direction, RandomSource, ModelData, RenderType)}
43+
* logic.
44+
*
45+
* @param state the casing's block state.
46+
* @param face the side to obtain replacement quads for.
47+
* @param random the random seed to use for the quad generation.
48+
* @param renderType the render type.
49+
* @return the list of replacement quads, or <c>null</c> to use the default casing quads.
50+
*/
51+
@OnlyIn(Dist.CLIENT)
52+
List<BakedQuad> getQuads(final @Nullable BlockState state, @Nullable final Direction face, final RandomSource random, final ModelData data, @Nullable final RenderType renderType);
53+
54+
/**
55+
* Returns the render types required by the underlying model.
56+
*
57+
* @param random the random seed to use for the quad generation.
58+
* @param data the model data for the underlying model.
59+
* @return the render layers needed by the underlying model.
60+
*/
61+
@OnlyIn(Dist.CLIENT)
62+
ChunkRenderTypeSet getRenderTypes(final RandomSource random, final ModelData data);
63+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@ParametersAreNonnullByDefault
2+
@MethodsReturnNonnullByDefault
3+
package li.cil.tis3d.api.module.traits.neoforge;
4+
5+
import net.minecraft.MethodsReturnNonnullByDefault;
6+
7+
import javax.annotation.ParametersAreNonnullByDefault;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package li.cil.tis3d.client.neoforge;
2+
3+
import li.cil.tis3d.api.API;
4+
import li.cil.tis3d.client.ClientSetup;
5+
import li.cil.tis3d.client.gui.TerminalModuleScreen;
6+
import li.cil.tis3d.client.renderer.block.neoforge.ModuleModelLoader;
7+
import net.minecraft.client.Minecraft;
8+
import net.minecraft.resources.ResourceLocation;
9+
import net.neoforged.api.distmarker.Dist;
10+
import net.neoforged.bus.api.SubscribeEvent;
11+
import net.neoforged.fml.common.Mod;
12+
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
13+
import net.neoforged.neoforge.client.event.ModelEvent;
14+
import net.neoforged.neoforge.client.event.RenderGuiOverlayEvent;
15+
import net.neoforged.neoforge.common.NeoForge;
16+
17+
@Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD, modid = API.MOD_ID)
18+
public final class ClientSetupNeoForge {
19+
@SubscribeEvent
20+
public static void handleClientSetup(final FMLClientSetupEvent ignoredEvent) {
21+
ClientSetup.run();
22+
23+
NeoForge.EVENT_BUS.addListener((RenderGuiOverlayEvent.Pre event) -> {
24+
if (Minecraft.getInstance().screen instanceof TerminalModuleScreen) {
25+
event.setCanceled(true);
26+
}
27+
});
28+
}
29+
30+
@SubscribeEvent
31+
public static void handleModelRegistryEvent(ModelEvent.RegisterGeometryLoaders event) {
32+
event.register(new ResourceLocation(API.MOD_ID, "module"), new ModuleModelLoader());
33+
}
34+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@ParametersAreNonnullByDefault
2+
@MethodsReturnNonnullByDefault
3+
package li.cil.tis3d.client.neoforge;
4+
5+
import net.minecraft.MethodsReturnNonnullByDefault;
6+
7+
import javax.annotation.ParametersAreNonnullByDefault;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package li.cil.tis3d.client.renderer.block.neoforge;
2+
3+
import li.cil.tis3d.api.machine.Face;
4+
import li.cil.tis3d.api.module.traits.neoforge.ModuleWithBakedModelNeoForge;
5+
import net.minecraft.client.renderer.RenderType;
6+
import net.minecraft.client.renderer.block.model.BakedQuad;
7+
import net.minecraft.client.renderer.block.model.ItemOverrides;
8+
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
9+
import net.minecraft.client.resources.model.BakedModel;
10+
import net.minecraft.core.Direction;
11+
import net.minecraft.util.RandomSource;
12+
import net.minecraft.world.level.block.state.BlockState;
13+
import net.neoforged.neoforge.client.ChunkRenderTypeSet;
14+
import net.neoforged.neoforge.client.model.IDynamicBakedModel;
15+
import net.neoforged.neoforge.client.model.data.ModelData;
16+
import net.neoforged.neoforge.client.model.data.ModelProperty;
17+
import org.jetbrains.annotations.NotNull;
18+
19+
import javax.annotation.Nullable;
20+
import java.util.ArrayList;
21+
import java.util.Collections;
22+
import java.util.List;
23+
24+
public final class ModuleBakedModel implements IDynamicBakedModel {
25+
private final BakedModel proxy;
26+
27+
// --------------------------------------------------------------------- //
28+
29+
ModuleBakedModel(final BakedModel proxy) {
30+
this.proxy = proxy;
31+
}
32+
33+
// --------------------------------------------------------------------- //
34+
// IBakedModel
35+
36+
@Override
37+
public @NotNull List<BakedQuad> getQuads(@Nullable final BlockState state, @Nullable final Direction side, final RandomSource random, final ModelData data, @Nullable final RenderType renderType) {
38+
final CasingModules modules = data.get(CasingModules.CASING_MODULES_PROPERTY);
39+
if (side != null) {
40+
if (modules != null) {
41+
final Face face = Face.fromDirection(side);
42+
final ModuleWithBakedModelNeoForge module = modules.getModule(face);
43+
if (module != null && module.hasModel()) {
44+
final ModelData moduleData = modules.getModuleData(face);
45+
return module.getQuads(state, side, random, moduleData, renderType);
46+
}
47+
}
48+
49+
if (renderType != null && renderType.equals(RenderType.solid())) {
50+
return proxy.getQuads(state, side, random, data, renderType);
51+
} else {
52+
return Collections.emptyList();
53+
}
54+
} else {
55+
final ArrayList<BakedQuad> quads = new ArrayList<>();
56+
57+
if (modules != null) {
58+
for (final Face face : Face.VALUES) {
59+
final ModuleWithBakedModelNeoForge module = modules.getModule(face);
60+
if (module != null && module.hasModel()) {
61+
final ModelData moduleData = modules.getModuleData(face);
62+
quads.addAll(module.getQuads(state, null, random, moduleData, renderType));
63+
}
64+
}
65+
}
66+
67+
if (renderType != null && renderType.equals(RenderType.solid())) {
68+
quads.addAll(proxy.getQuads(state, null, random, data, renderType));
69+
}
70+
71+
return quads;
72+
}
73+
}
74+
75+
@Override
76+
public boolean useAmbientOcclusion() {
77+
return proxy.useAmbientOcclusion();
78+
}
79+
80+
@Override
81+
public boolean isGui3d() {
82+
return proxy.isGui3d();
83+
}
84+
85+
@Override
86+
public boolean usesBlockLight() {
87+
return proxy.usesBlockLight();
88+
}
89+
90+
@Override
91+
public boolean isCustomRenderer() {
92+
return proxy.isCustomRenderer();
93+
}
94+
95+
@SuppressWarnings("deprecation")
96+
@Override
97+
public TextureAtlasSprite getParticleIcon() {
98+
return proxy.getParticleIcon();
99+
}
100+
101+
@Override
102+
public ItemOverrides getOverrides() {
103+
return proxy.getOverrides();
104+
}
105+
106+
@Override
107+
public ChunkRenderTypeSet getRenderTypes(@NotNull final BlockState state, @NotNull final RandomSource random, @NotNull final ModelData data) {
108+
ChunkRenderTypeSet set = proxy.getRenderTypes(state, random, data);
109+
final CasingModules modules = data.get(CasingModules.CASING_MODULES_PROPERTY);
110+
if (modules != null) {
111+
for (final Face face : Face.VALUES) {
112+
final ModuleWithBakedModelNeoForge module = modules.getModule(face);
113+
if (module != null && module.hasModel()) {
114+
final ModelData moduleData = modules.getModuleData(face);
115+
set = ChunkRenderTypeSet.union(set, module.getRenderTypes(random, moduleData));
116+
}
117+
}
118+
}
119+
return set;
120+
}
121+
122+
// --------------------------------------------------------------------- //
123+
124+
public static final class CasingModules {
125+
public static final ModelProperty<CasingModules> CASING_MODULES_PROPERTY = new ModelProperty<>();
126+
127+
private final ModuleWithBakedModelNeoForge[] modules = new ModuleWithBakedModelNeoForge[Face.VALUES.length];
128+
private final ModelData[] moduleData = new ModelData[Face.VALUES.length];
129+
130+
public boolean isEmpty() {
131+
for (final ModuleWithBakedModelNeoForge module : modules) {
132+
if (module != null) {
133+
return false;
134+
}
135+
}
136+
137+
return true;
138+
}
139+
140+
public void setModule(final Face face, final ModuleWithBakedModelNeoForge module, final ModelData data) {
141+
modules[face.ordinal()] = module;
142+
moduleData[face.ordinal()] = data;
143+
}
144+
145+
@Nullable
146+
public ModuleWithBakedModelNeoForge getModule(final Face face) {
147+
return modules[face.ordinal()];
148+
}
149+
150+
public ModelData getModuleData(final Face face) {
151+
return moduleData[face.ordinal()];
152+
}
153+
}
154+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package li.cil.tis3d.client.renderer.block.neoforge;
2+
3+
import net.minecraft.client.renderer.block.model.ItemOverrides;
4+
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
5+
import net.minecraft.client.resources.model.*;
6+
import net.minecraft.resources.ResourceLocation;
7+
import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext;
8+
import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry;
9+
10+
import java.util.Set;
11+
import java.util.function.Function;
12+
13+
public final class ModuleModel implements IUnbakedGeometry<ModuleModel> {
14+
private final IUnbakedGeometry<?> proxy;
15+
16+
// --------------------------------------------------------------------- //
17+
18+
ModuleModel(final IUnbakedGeometry<?> proxy) {
19+
this.proxy = proxy;
20+
}
21+
22+
// --------------------------------------------------------------------- //
23+
// IModelGeometry
24+
25+
@Override
26+
public BakedModel bake(final IGeometryBakingContext context, final ModelBaker baker, final Function<Material, TextureAtlasSprite> spriteGetter, final ModelState modelState, final ItemOverrides overrides, final ResourceLocation modelLocation) {
27+
return new ModuleBakedModel(proxy.bake(context, baker, spriteGetter, modelState, overrides, modelLocation));
28+
}
29+
30+
@Override
31+
public void resolveParents(final Function<ResourceLocation, UnbakedModel> modelGetter, final IGeometryBakingContext context) {
32+
proxy.resolveParents(modelGetter, context);
33+
}
34+
35+
@Override
36+
public Set<String> getConfigurableComponentNames() {
37+
return proxy.getConfigurableComponentNames();
38+
}
39+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package li.cil.tis3d.client.renderer.block.neoforge;
2+
3+
import com.google.gson.JsonDeserializationContext;
4+
import com.google.gson.JsonObject;
5+
import com.google.gson.JsonParseException;
6+
import net.neoforged.neoforge.client.model.ElementsModel;
7+
import net.neoforged.neoforge.client.model.geometry.IGeometryLoader;
8+
9+
public final class ModuleModelLoader implements IGeometryLoader<ModuleModel> {
10+
// --------------------------------------------------------------------- //
11+
// IGeometryLoader
12+
13+
@Override
14+
public ModuleModel read(final JsonObject modelContents, final JsonDeserializationContext context) throws JsonParseException {
15+
return new ModuleModel(ElementsModel.Loader.INSTANCE.read(modelContents, context));
16+
}
17+
}

0 commit comments

Comments
 (0)