Jump to content

[1.8.9] Multiblocks structures and OBJ models: is it possible?


Recommended Posts

Posted

 

Hi!

 

I'm working on a mod and I like very much big multiblock machines and structures but I'd like to make them look a bit better than a Borg cube, something like an Arc Furnace from Immersive Engineering instead of a Steam Boiler from Railcraft to give you an idea :)

 

What I'd like to do is make a 3D model for the whole multiblock in Blender or 3DS MAX and use the resulting OBJ model to render the assembled multiblock in game

 

The question is how to do that :D

 

 

So far I came up with the following ideas / concepts:

 

 

1) use TESR to render the whole model by delegating the job to one of the TEs of the multiblock structure

 

This can turn ugly performance-wise and I don't know if the Minecraft rendering engine support models much bigger than 1 or 2 block spaces

 

 

2) cut the 3D model in pieces and generate an OBJ for each of the blocks in the multiblock structure

 

Aside from the cutting nightmare, there will probably be problems regarding the orientation of the multiblock

 

 

What do you think guys?

 

Posted

Not my profession for a long time now, but in past I think I stumbled upon something like this.

Design was pretty much: Place one block (center) which renders whole thing and fill air around with special invisible blocks.

I am unsure if it was in Entity or Block class, but I remember there is a method that allows you to change rendering distance hit-box (meaning you start seeing (render) block before you actually see central-block). But as said - this might have been method for Entity (that was like a year ago, idk).

 

Note that you will need some pretty big recursion to break such a structure. Way I would do it is to make that one of those structures (when trying to place them) cannot ever be in 1-block contact with other. Then (since "invisible block" is of your design) you can NOT care about checking whether your recursion is removing structure wanted or passed onto another one next to it, and just make tail call. Just an idea.

 

Hope anything here is helpful, but you got to do the actual research (or wait for someone else). :D

1.7.10 is no longer supported by forge, you are on your own.

Posted

First of all thank you for your reply :)

 

I'm not sure if I got it right, are the invisible blocks to be placed around the whole structure (for example, around the 5x5x5 cube that form my multiblock) or around the central block?

 

Thank you!

Z

Posted

The invisible blocks are every real block in your multiblock structure.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

Oh, I've done this recently!

Basically, in your TESR and Block classes, in the rendering functions(renderTileEntityAt, isOpaqueCube, shouldSideBeRendered,etc) check the tile entity for a boolean method called isMultiblock or something. This will use worldObj.getBlock to check if the required blocks are not null, and the right blocks. Then, return if it is.

Make sure you don't render the obj if it isn't multiblock, and you do render the blocks.

Remember also to translate the model!

Don't forget to also update the fake blocks. I'd suggest doing something along the lines of:

if(te.isMultiblock() || te.isToldMultiblock)
//don't render sides

if(isMultiblock())
//render OBJ

if(multiblockcode)
//set blocks isToldMulitblock()

 

And the model can be huge! But be careful on scale and don't go overboard with the vertices

[shadow=gray,left][glow=red,2,300]KEEGAN[/glow][/shadow]

Posted

Sorry guys for the late reply and thank you for your help!

 

I was able to make this work using the standard blockstate tying it into my multiblock controller with getActualState and shouldSideBeRendered and delegating the "you should render the model" state to one of the block (selected when then whole machine became fully assembled)

 

PNJdonr.png

 

J3wMgn6.png

(no texture on the model right now)

 

 

In the tests so far I dont' see any performance drops so all good. The next step is to animate things a bit :)

 

Posted

Your invisible blocks should also be transparent, so the model gets light.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

  • 2 months later...
Posted

Hey so I'm pretty good at coding and I had a mod in 1.7.10 but updating messed it all up and I don't want to restart so would you be willing to partner with me?

Posted

You got the model to work? How did you do this!?

 

Yup, with plain vanilla blockstate :)

 

 

All the blocks use the same blockstate:

 

{
    "variants": {
        "state=single": { "model": "sand" },
	"state=invisible": { "model": "thedarkkin:transparentBlock" },
	"state=renderer": { "model": "thedarkkin:mblock1/multib1.obj" }
    }
}

 

 

 

"single" is for blocks that are not part of a formed multiblock (I used the vanilla sand block model because this was a proof of concept rather then an actual machine)

 

"invisible" is for blocks that are part of a formed mutiblock but are not the mutiblock render delegate. they are actualy invisible in game

 

"renderer" is for the block selected to be the render delegate of the formed multiblock

 

 

Basically when the multiblock form itself, I select one block and delegate to it the job of rendering the 3D model (using the "render" blockstate and it's OBJ model). All the other block will render as an invisible cube.

 

You must create the 3D model keeping in mind where the render delegate of your choice is in the whole structure. Mine was in the center of the bottom layer

 

 

The block class override getActualState() and return one of the three possibile states according to the state of the multiblock (how to actualy do that depends on how you implement your multiblock logic)

 

 

 

 

 

Posted

Thanks, I don't actually want a multiblock structure, but for what I understood I can simply "load" the obj file with the json, is that really everything to it? Seems way too easy, I've found three tutorials with overly complicated ways to load obj.

Posted

Hi, glad it worked for you too ;)

 

You need to export the material library (.MTL) along with the OBJ file and place it in the same directory of the OBJ file

 

Then, open the MTL file in a text editor and change the path of your texture files using the same format as in all the other resource locations you use in your mod (basically, yourmodid:path_to_texture)

 

Hope that helps :)

 

Z

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
    • Do you use features of inventory profiles next (ipnext) or is there a change without it?
    • Remove rubidium - you are already using embeddium, which is a fork of rubidium
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.