Jump to content

Problem with combining the meshes for a MP machine [SOLVED]


Frepo

Recommended Posts

I'm doing some fixing of the auto-sifter mod, since I love the machine and dev is discontinued, and also to start learning how to mod for the MP-system.
Currently I'm stuck at combining the static shape model with the animated/rotating part. Only the static model shows up in-game. 

The code for the wooden toggle is used as basis for this machine, since they both only connect horizontally ("ns", "we"), and don't have inventories and such.
I've split the model into 3 separate models

autosifter-inventory.json is the complete model (left pic), autosifter-stand.json is the static part (middle), and autosifter-moving.json is the rotating part (right)
image.png.49b9a42c80c0ad053ed57efd58e71998.png image.png.a02f3491a4c5994ee5b5a791d58714b8.png image.png.ab437d6c16cb67e5a9048643e52932a1.png

As far as I understood, the shape is set to the moving one in the json for BlockAutoSifter (at shapeByType)

Spoiler
{
	code: "tieredautosifter",
	class: "BlockAutoSifter",
	entityclass: "AutoSifterBlockEntity",
	behaviors: [{name: "NWOrientable" }],
	entityBehaviors: [{ name: "MPSifter" }],
	creativeinventory: { "general": ["*-ns"],"mechanics": ["*-ns"] },
	variantgroups: [
		{ code:"metal", states: ["steel","meteoriciron","iron","blackbronze","bismuthbronze","tinbronze","empty"]},
		{ code:"orientation", states: ["ns", "we"]},
	],
	blockmaterial: "Metal",
	shapeInventory: { base: "block/autosifter-inventory" },
	shapeByType: {
		"*-ns": { base: "block/autosifter-moving", rotateY: 90 },
		"*-we": { base: "block/autosifter-moving", rotateY: 0 },
	},
	textures: {
		grate: { base: "grate_{metal}" },
		lip: { base: "ingot/{metal}"},
		metalbase: { base: "metalbase"},
		metalalts: { base: "metalalts"},
		axle: { base: "game:block/wood/planks/generic" },
	},
	drops: [
		{ type: "block", code: "tieredautosifter-{metal}-ns", quantity: { avg: 1 }  }
	],
	sidesolid: {
		all: false
	},
	sideopaque: {
		all: false
	},
	attributes: {
		maxblocksByType: {
			"tieredautosifter-steel-*": 7,
			"tieredautosifter-meteoriciron-*": 4,
			"tieredautosifter-iron-*": 3,
			"tieredautosifter-blackbronze-*": 2,
			"tieredautosifter-bismuthbronze-*": 1,
			"tieredautosifter-tinbronze-*": 1
		}
	},
	resistance: 3.5,
	lightAbsorption: 1,
	maxStackSize: 1,
	groundTransform: {
		translation: { x: 0, y: 0, z: 0 },
		rotation: { x: -90, y: 0, z: 0 },
		origin: { x: 0.5, y: 0.45, z: 0.38 },
		scale: 2.7
	},
	guiTransform: {
		rotation: { x: -43, y: 45, z: 1 },
		scale: 0.96
	},
	tpHandTransform: {
		translation: { x: -1.2, y: -0.4, z: -0.6 },
		rotation: { x: -90, y: 11, z: -103 },
		origin: { x: 0.5, y: 0.25, z: 0.5 },
		scale: 0.42
	},
	collisionSelectionBoxByType: {
		"*-we": { x1: 0, y1: 0.34375, z1: 0, x2: 1, y2: 1, z2: 1 },
		"*-ns": { x1: 0, y1: 0.34375, z1: 0, x2: 1, y2: 1, z2: 1, rotateY: 90 },
	},
	sounds: {
		"place": "game:sounds/block/chute",
		"walk": "game:sounds/walk/stone"
	}
}

 

Here is the code for BlockAutoSifter, it's basically ripped off the vanilla BlockToggle:

Spoiler
namespace fregtech
{
    public class BlockAutoSifter : BlockMPBase
    {
        public bool IsOrientedTo(BlockFacing facing)
        {
            string dirs = base.LastCodePart(0);
            return dirs[0] == facing.Code[0] || (dirs.Length > 1 && dirs[1] == facing.Code[0]);

        }


        public override bool HasMechPowerConnectorAt(IWorldAccessor world, BlockPos pos, BlockFacing face)
        {
            return this.IsOrientedTo(face);
        }


        public override bool TryPlaceBlock(IWorldAccessor world, IPlayer byPlayer, ItemStack itemstack, BlockSelection blockSel, ref string failureCode)
        {

            if (!this.CanPlaceBlock(world, byPlayer, blockSel, ref failureCode))
            {
                return false;
            }

            foreach (BlockFacing face in BlockFacing.HORIZONTALS)
            {
                BlockPos pos = blockSel.Position.AddCopy(face);
                IMechanicalPowerBlock block = world.BlockAccessor.GetBlock(pos) as IMechanicalPowerBlock;
                if (block != null && block.HasMechPowerConnectorAt(world, pos, face.Opposite))
                {
                    AssetLocation loc = new AssetLocation(this.Code.Domain, base.FirstCodePart(0) + "-" + base.FirstCodePart(1) + "-" + face.Opposite.Code[0].ToString() + face.Code[0].ToString());
                    Block toPlaceBlock = world.GetBlock(loc);
                    if (toPlaceBlock == null)
                    {
                        loc = new AssetLocation(this.Code.Domain, base.FirstCodePart(0) + "-" + base.FirstCodePart(1) + "-" + face.Code[0].ToString() + face.Opposite.Code[0].ToString());
                        toPlaceBlock = world.GetBlock(loc);
                    }

                    if (toPlaceBlock != null && toPlaceBlock.DoPlaceBlock(world, byPlayer, blockSel, itemstack))
                    {
                        block.DidConnectAt(world, pos, face.Opposite);
                        this.WasPlaced(world, blockSel.Position, face);
                        return true;
                    }
                }
            }

            if (base.TryPlaceBlock(world, byPlayer, itemstack, blockSel, ref failureCode))
            {
                this.WasPlaced(world, blockSel.Position, null);
                return true;
            }
            return false;
        }


        public override void OnNeighbourBlockChange(IWorldAccessor world, BlockPos pos, BlockPos neibpos)
        {
            BlockEntity blockEntity = world.BlockAccessor.GetBlockEntity(pos);
            BEBehaviorMPSifter bempsifter = (blockEntity != null) ? blockEntity.GetBehavior<BEBehaviorMPSifter>() : null;
            if (bempsifter != null && !bempsifter.IsAttachedToBlock())
            {
                foreach (BlockFacing face in BlockFacing.HORIZONTALS)
                {
                    BlockPos npos = pos.AddCopy(face);
                    BlockAngledGears blockagears = world.BlockAccessor.GetBlock(npos) as BlockAngledGears;
                    if (blockagears != null && blockagears.Facings.Contains(face.Opposite) && blockagears.Facings.Length == 1)
                    {
                        world.BlockAccessor.BreakBlock(npos, null, 1f);
                    }
                }
            }
            base.OnNeighbourBlockChange(world, pos, neibpos);
        }

        public override void DidConnectAt(IWorldAccessor world, BlockPos pos, BlockFacing face)
        {
        }
    }
}

 

Here is the code for the BEBehaviorMPSifter, which is mostly ripped off the BEBehaviorMPToggle. Here in lies the problem, I think, probably in the getStandMesh and/or OnTesselation methods. Instead of adding the autosifter-stand model to the autosifter-moving model, it replaces it. And I have no idea why.
 

Spoiler
namespace fregtech
{
    public class BEBehaviorMPSifter : BEBehaviorMPConsumer
    {
        public BEBehaviorMPSifter(BlockEntity blockentity) : base(blockentity)
        {
        }

        public override void Initialize(ICoreAPI api, JsonObject properties)
        {
            base.Initialize(api, properties);

            string domainAndPath = "block/autosifter-stand.json";
            AssetLocation code = base.Block.Code;
            this.sifterStandLoc = AssetLocation.Create(domainAndPath, (code != null) ? code.Domain : "fregtech");
            JsonObject attributes = base.Block.Attributes;
            if (attributes != null && attributes["sifterStandLoc"].Exists)
            {
                JsonObject attributes2 = base.Block.Attributes;
                this.sifterStandLoc = ((attributes2 != null) ? attributes2["sifterStandLoc"].AsObject<AssetLocation>(null) : null);
            }
            this.sifterStandLoc.WithPathPrefixOnce("shapes/").WithPathAppendixOnce(".json");

            if (api.Side == EnumAppSide.Client)
            {
                this.capi = (api as ICoreClientAPI);
            }
            this.orientations = this.Block.Variant["orientation"];
            string a = this.orientations;
            if (a == "ns")
            {
                this.AxisSign = new int[] { 0, 0, -1 };
                this.orients[0] = BlockFacing.NORTH;
                this.orients[1] = BlockFacing.SOUTH;
                this.sides[0] = this.Position.AddCopy(BlockFacing.WEST);
                this.sides[1] = this.Position.AddCopy(BlockFacing.EAST);
                return;
            }
            if (!(a == "we"))
            {
                return;
            }
            int[] array = new int[3];
            array[0] = -1;
            this.AxisSign = array;
            this.orients[0] = BlockFacing.WEST;
            this.orients[1] = BlockFacing.EAST;
            this.sides[0] = this.Position.AddCopy(BlockFacing.NORTH);
            this.sides[1] = this.Position.AddCopy(BlockFacing.SOUTH);
        }

        public override float GetResistance()
        {
            this.Api.World.BlockAccessor.GetBlockEntity(this.Position);
            float speed = (this.network == null) ? 0f : Math.Abs(this.network.Speed * base.GearedRatio);
            float speedLimiter = 5f * (float)Math.Exp((double)speed * 2.8 - 5.0);
            Block blockabove = this.Api.World.BlockAccessor.GetBlock(this.Position.AddCopy(0, 1, 0));
            if (blockabove.Attributes != null && blockabove.Attributes.IsTrue("pannable"))
            {
                return 0.125f + speedLimiter;
            }
            return (0.125f + speedLimiter) * 0.5f;
        }

        public override void JoinNetwork(MechanicalNetwork network)
        {
            base.JoinNetwork(network);
            float speed = (network == null) ? 0f : (Math.Abs(network.Speed * base.GearedRatio) * 1.6f);
            if (speed > 1f)
            {
                network.Speed /= speed;
                network.clientSpeed /= speed;
            }
        }

        public bool IsAttachedToBlock()
        {
            return (this.orientations == "ns" || this.orientations == "we") && (this.Api.World.BlockAccessor.IsSideSolid(this.Position.X, this.Position.Y - 1, this.Position.Z, BlockFacing.UP) || this.Api.World.BlockAccessor.IsSideSolid(this.Position.X, this.Position.Y + 1, this.Position.Z, BlockFacing.DOWN));
        }

        private MeshData getStandMesh(string orient)
        {
            ICoreAPI api = this.Api;
            AssetLocation code = base.Block.Code;
            return ObjectCacheUtil.GetOrCreate<MeshData>(api, ((code != null) ? code.ToString() : null) + "-" + orient + "-stand", delegate
            {
                Shape shape = Vintagestory.API.Common.Shape.TryGet(this.capi, this.sifterStandLoc);
                MeshData mesh;
                this.capi.Tesselator.TesselateShape(this.Block, shape, out mesh, null, null, null);
                if (orient == "ns")
                {
                    mesh.Rotate(new Vec3f(0.5f, 0.5f, 0.5f), 0f, 1.5707964f, 0f);
                }
                return mesh;
            });
        }

        public override bool OnTesselation(ITerrainMeshPool mesher, ITesselatorAPI tesselator)
        {
            MeshData mesh = this.getStandMesh(base.Block.Variant["orientation"]);
            mesher.AddMeshData(mesh, 1);
            return base.OnTesselation(mesher, tesselator);
        }

        protected readonly BlockFacing[] orients = new BlockFacing[2];

        protected readonly BlockPos[] sides = new BlockPos[2];

        private ICoreClientAPI capi;

        private string orientations;

        private AssetLocation sifterStandLoc;
    }
}

 

So this is the end result... the machine functions as intended (guess the BlockEntity code is OK), but the wooden axle is nowhere to be seen.
image.png.64856da4b09066de91f9120f2db6b3a0.png

Edited by Frepo
Link to comment
Share on other sites

You *might* also look at my passthrough axle, which has plenty of outstanding issues but rendering was not one of them.  It is based on the vanilla axle.

It has two unique blocks - axlepassthroughfull.json and axlepassthrough.json
https://github.com/SpearAndFang/millwright/tree/main/assets/millwright/blocktypes/mechanics
and three shapes
https://github.com/SpearAndFang/millwright/tree/main/assets/millwright/shapes/block/wood/mechanics

The block axlepassthoughfull is the entire gizmo - all of the components are static and in a single shape: axle-passthrough-full.json - it's the one you craft and carry around with you. 

When you place it, I swap it to the other block: axlepassthough - and the shape defined in that block is the moving part only.  axle-passthrough.json

The non-moving part (the shape axle-plate.json) is initialized and added to the mix entirely in the behavior and just like you describe above
https://github.com/SpearAndFang/millwright/blob/main/ModSystem/bebehavior/bebehavioraxlepassthrough.cs

- Initialize
- GetPlateMesh
- OnTesselation

Your code looks about right to me and very similar to what I'm doing.  Maybe this helps.

  • Like 2
Link to comment
Share on other sites

Yeah pretty much why the thing doesn't animate in the original release. You've got my blessing for the project if you can figure it out.
If I had to wager a guess, I would say there's some low level rendering function which is being overwritten or not getting expected values.

Edited by Omega Haxors
  • Like 1
Link to comment
Share on other sites

@Spear and Fang Have you made any machines that uses behaviors based on the BEBehaviorMPConsumer?

Where the autosifter differ from the toggle is that it's behavior is BEBehaviorMPConsumer, where the toggle is just BEBehaviorMPBase, just like your passthrough axle. 
In the Initialize method ( in BEBehaviorMPConsumer) there's a line that caught my eye;

this.Shape = properties["mechPartShape"].AsObject<CompositeShape>(null);

If don't know if this property is set anywhere, can't find anything when I search the word. So I was wondering if you know where this is set, or if I need to set this myself somewhere.

Link to comment
Share on other sites

10 hours ago, Frepo said:

@Spear and Fang Have you made any machines that uses behaviors based on the BEBehaviorMPConsumer?

Where the autosifter differ from the toggle is that it's behavior is BEBehaviorMPConsumer, where the toggle is just BEBehaviorMPBase, just like your passthrough axle. 
In the Initialize method ( in BEBehaviorMPConsumer) there's a line that caught my eye;

this.Shape = properties["mechPartShape"].AsObject<CompositeShape>(null);

If don't know if this property is set anywhere, can't find anything when I search the word. So I was wondering if you know where this is set, or if I need to set this myself somewhere.

I *think* that "mechPartShape" is an arbitrary name that is required to initialize that object, and is entirely unused.

But I believe that BEBehaviorMPConsumer is the wrong thing to use in your situation (and no I have not used it).  It's used for the top of the quern only, which is pretty far removed from what you are trying to do.  And it's initialize method is problematic.  It wouldn't be much of a stretch to just use BEBehaviorMPBase instead and add any methods from BEBehaviorMPConsumer that you *think* you might need. There's not much to it.

Try this autosift.zip mod.  I quickly threw it together so it's still got some issues, but it certainly works (the axle turns inside the sifter when connected to mechanical power, passes power out the other side), and is based on what I was saying above.

autosift.zip source.zip

Edited by Spear and Fang
  • Like 1
Link to comment
Share on other sites

OMG it was the consumer class! Started working perfectly after changing to MPBase. Just had to copy in the Join-/LeaveNetwork methods, and OnConnected and OnDisConnected actions. What a relief to finally be able to move on from this.

🤩 THANK YOU!!! 🤩

  • Like 2
Link to comment
Share on other sites

  • Frepo changed the title to Problem with combining the meshes for a MP machine [SOLVED]
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.