Skip to content

Commit

Permalink
Add support for NeoForge loader.
Browse files Browse the repository at this point in the history
  • Loading branch information
fnuecke committed Apr 15, 2024
1 parent b710115 commit da04ee0
Show file tree
Hide file tree
Showing 87 changed files with 2,735 additions and 1 deletion.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
org.gradle.jvmargs=-Xmx4G
org.gradle.daemon=false

enabledPlatforms=fabric,forge
enabledPlatforms=fabric,forge,neoforge

modId=tis3d
mavenGroup=li.cil.tis3d
Expand Down
53 changes: 53 additions & 0 deletions neoforge/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
val modId: String by project
val minecraftVersion: String = libs.versions.minecraft.get()
val neoforgeVersion: String = libs.versions.neoforge.platform.get()
val neoforgeLoaderVersion: String = libs.versions.neoforge.loader.get()
val architecturyVersion: String = libs.versions.architectury.get()
val manualVersion: String = libs.versions.manual.get()

loom {
accessWidenerPath.set(project(":common").loom.accessWidenerPath)

runs {
create("data") {
data()
programArgs("--all")
programArgs("--mod", modId)
programArgs("--output", file("src/generated/resources/").absolutePath)
programArgs("--existing", project(":common").file("src/main/resources").absolutePath)
programArgs("--existing", file("src/main/resources").absolutePath)
}
}
}

repositories {
maven("https://maven.neoforged.net/releases")
}

dependencies {
neoForge(libs.neoforge.platform)
modImplementation(libs.neoforge.architectury)

// modImplementation(libs.neoforge.manual)
}

tasks {
processResources {
val properties = mapOf(
"version" to project.version,
"minecraftVersion" to minecraftVersion,
"loaderVersion" to neoforgeLoaderVersion,
"neoforgeVersion" to neoforgeVersion,
"architecturyVersion" to architecturyVersion,
"manualVersion" to manualVersion
)
inputs.properties(properties)
filesMatching("META-INF/mods.toml") {
expand(properties)
}
}

remapJar {
atAccessWideners.add("${modId}.accesswidener")
}
}
1 change: 1 addition & 0 deletions neoforge/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
loom.platform = neoforge
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package li.cil.tis3d.api.module.traits.neoforge;

import li.cil.tis3d.api.module.traits.ModuleWithBakedModel;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.client.ChunkRenderTypeSet;
import net.neoforged.neoforge.client.model.data.ModelData;

import javax.annotation.Nullable;
import java.util.List;

/**
* Forge specific specialization of the {@link ModuleWithBakedModel} interface. Use this when using Forge
* to emit custom quads for a module.
*/
public interface ModuleWithBakedModelNeoForge extends ModuleWithBakedModel {
/**
* Collect model data that is needed to render the quads returned by this module.
* <p>
* The returned value will be passed back into {@link #getQuads(BlockState, Direction, RandomSource, ModelData, RenderType)}.
*
* @param level the render-thread safe world access.
* @param pos the position of the casing.
* @param state the current block-state of the casing.
* @param data the incoming model data to wrap/expand.
* @return model data needed for rendering.
*/
@OnlyIn(Dist.CLIENT)
default ModelData getModelData(final BlockAndTintGetter level, final BlockPos pos, final BlockState state, final ModelData data) {
return data;
}

/**
* Called to obtain quads to use for the specified side instead of the casing's default ones. Will be called
* directly from the casing's {@link net.minecraft.client.resources.model.BakedModel#getQuads(BlockState, Direction, RandomSource, ModelData, RenderType)}
* logic.
*
* @param state the casing's block state.
* @param face the side to obtain replacement quads for.
* @param random the random seed to use for the quad generation.
* @param renderType the render type.
* @return the list of replacement quads, or <c>null</c> to use the default casing quads.
*/
@OnlyIn(Dist.CLIENT)
List<BakedQuad> getQuads(final @Nullable BlockState state, @Nullable final Direction face, final RandomSource random, final ModelData data, @Nullable final RenderType renderType);

/**
* Returns the render types required by the underlying model.
*
* @param random the random seed to use for the quad generation.
* @param data the model data for the underlying model.
* @return the render layers needed by the underlying model.
*/
@OnlyIn(Dist.CLIENT)
ChunkRenderTypeSet getRenderTypes(final RandomSource random, final ModelData data);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.tis3d.api.module.traits.neoforge;

import net.minecraft.MethodsReturnNonnullByDefault;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package li.cil.tis3d.client.neoforge;

import li.cil.tis3d.api.API;
import li.cil.tis3d.client.ClientSetup;
import li.cil.tis3d.client.gui.TerminalModuleScreen;
import li.cil.tis3d.client.renderer.block.neoforge.ModuleModelLoader;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.neoforge.client.event.ModelEvent;
import net.neoforged.neoforge.client.event.RenderGuiOverlayEvent;
import net.neoforged.neoforge.common.NeoForge;

@Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD, modid = API.MOD_ID)
public final class ClientSetupNeoForge {
@SubscribeEvent
public static void handleClientSetup(final FMLClientSetupEvent ignoredEvent) {
ClientSetup.run();

NeoForge.EVENT_BUS.addListener((RenderGuiOverlayEvent.Pre event) -> {
if (Minecraft.getInstance().screen instanceof TerminalModuleScreen) {
event.setCanceled(true);
}
});
}

@SubscribeEvent
public static void handleModelRegistryEvent(ModelEvent.RegisterGeometryLoaders event) {
event.register(new ResourceLocation(API.MOD_ID, "module"), new ModuleModelLoader());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.tis3d.client.neoforge;

import net.minecraft.MethodsReturnNonnullByDefault;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package li.cil.tis3d.client.renderer.block.neoforge;

import li.cil.tis3d.api.machine.Face;
import li.cil.tis3d.api.module.traits.neoforge.ModuleWithBakedModelNeoForge;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.client.ChunkRenderTypeSet;
import net.neoforged.neoforge.client.model.IDynamicBakedModel;
import net.neoforged.neoforge.client.model.data.ModelData;
import net.neoforged.neoforge.client.model.data.ModelProperty;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class ModuleBakedModel implements IDynamicBakedModel {
private final BakedModel proxy;

// --------------------------------------------------------------------- //

ModuleBakedModel(final BakedModel proxy) {
this.proxy = proxy;
}

// --------------------------------------------------------------------- //
// IBakedModel

@Override
public @NotNull List<BakedQuad> getQuads(@Nullable final BlockState state, @Nullable final Direction side, final RandomSource random, final ModelData data, @Nullable final RenderType renderType) {
final CasingModules modules = data.get(CasingModules.CASING_MODULES_PROPERTY);
if (side != null) {
if (modules != null) {
final Face face = Face.fromDirection(side);
final ModuleWithBakedModelNeoForge module = modules.getModule(face);
if (module != null && module.hasModel()) {
final ModelData moduleData = modules.getModuleData(face);
return module.getQuads(state, side, random, moduleData, renderType);
}
}

if (renderType != null && renderType.equals(RenderType.solid())) {
return proxy.getQuads(state, side, random, data, renderType);
} else {
return Collections.emptyList();
}
} else {
final ArrayList<BakedQuad> quads = new ArrayList<>();

if (modules != null) {
for (final Face face : Face.VALUES) {
final ModuleWithBakedModelNeoForge module = modules.getModule(face);
if (module != null && module.hasModel()) {
final ModelData moduleData = modules.getModuleData(face);
quads.addAll(module.getQuads(state, null, random, moduleData, renderType));
}
}
}

if (renderType != null && renderType.equals(RenderType.solid())) {
quads.addAll(proxy.getQuads(state, null, random, data, renderType));
}

return quads;
}
}

@Override
public boolean useAmbientOcclusion() {
return proxy.useAmbientOcclusion();
}

@Override
public boolean isGui3d() {
return proxy.isGui3d();
}

@Override
public boolean usesBlockLight() {
return proxy.usesBlockLight();
}

@Override
public boolean isCustomRenderer() {
return proxy.isCustomRenderer();
}

@SuppressWarnings("deprecation")
@Override
public TextureAtlasSprite getParticleIcon() {
return proxy.getParticleIcon();
}

@Override
public ItemOverrides getOverrides() {
return proxy.getOverrides();
}

@Override
public ChunkRenderTypeSet getRenderTypes(@NotNull final BlockState state, @NotNull final RandomSource random, @NotNull final ModelData data) {
ChunkRenderTypeSet set = proxy.getRenderTypes(state, random, data);
final CasingModules modules = data.get(CasingModules.CASING_MODULES_PROPERTY);
if (modules != null) {
for (final Face face : Face.VALUES) {
final ModuleWithBakedModelNeoForge module = modules.getModule(face);
if (module != null && module.hasModel()) {
final ModelData moduleData = modules.getModuleData(face);
set = ChunkRenderTypeSet.union(set, module.getRenderTypes(random, moduleData));
}
}
}
return set;
}

// --------------------------------------------------------------------- //

public static final class CasingModules {
public static final ModelProperty<CasingModules> CASING_MODULES_PROPERTY = new ModelProperty<>();

private final ModuleWithBakedModelNeoForge[] modules = new ModuleWithBakedModelNeoForge[Face.VALUES.length];
private final ModelData[] moduleData = new ModelData[Face.VALUES.length];

public boolean isEmpty() {
for (final ModuleWithBakedModelNeoForge module : modules) {
if (module != null) {
return false;
}
}

return true;
}

public void setModule(final Face face, final ModuleWithBakedModelNeoForge module, final ModelData data) {
modules[face.ordinal()] = module;
moduleData[face.ordinal()] = data;
}

@Nullable
public ModuleWithBakedModelNeoForge getModule(final Face face) {
return modules[face.ordinal()];
}

public ModelData getModuleData(final Face face) {
return moduleData[face.ordinal()];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package li.cil.tis3d.client.renderer.block.neoforge;

import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext;
import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry;

import java.util.Set;
import java.util.function.Function;

public final class ModuleModel implements IUnbakedGeometry<ModuleModel> {
private final IUnbakedGeometry<?> proxy;

// --------------------------------------------------------------------- //

ModuleModel(final IUnbakedGeometry<?> proxy) {
this.proxy = proxy;
}

// --------------------------------------------------------------------- //
// IModelGeometry

@Override
public BakedModel bake(final IGeometryBakingContext context, final ModelBaker baker, final Function<Material, TextureAtlasSprite> spriteGetter, final ModelState modelState, final ItemOverrides overrides, final ResourceLocation modelLocation) {
return new ModuleBakedModel(proxy.bake(context, baker, spriteGetter, modelState, overrides, modelLocation));
}

@Override
public void resolveParents(final Function<ResourceLocation, UnbakedModel> modelGetter, final IGeometryBakingContext context) {
proxy.resolveParents(modelGetter, context);
}

@Override
public Set<String> getConfigurableComponentNames() {
return proxy.getConfigurableComponentNames();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package li.cil.tis3d.client.renderer.block.neoforge;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.neoforged.neoforge.client.model.ElementsModel;
import net.neoforged.neoforge.client.model.geometry.IGeometryLoader;

public final class ModuleModelLoader implements IGeometryLoader<ModuleModel> {
// --------------------------------------------------------------------- //
// IGeometryLoader

@Override
public ModuleModel read(final JsonObject modelContents, final JsonDeserializationContext context) throws JsonParseException {
return new ModuleModel(ElementsModel.Loader.INSTANCE.read(modelContents, context));
}
}
Loading

0 comments on commit da04ee0

Please sign in to comment.