Jump to content

[1.8] ISBM Blockstate Rotation


Drakmyth

Recommended Posts

I converted my conveyors from using the vanilla block rendering to using an ISmartBlockModel to change the block by loading JSON sub-models depending on its neighbors. That logic works perfectly. The problem I'm running into is that the blockstates file defines the rotation depending on facing, and it is no longer respected. I assume this is because I'm using a custom model loader, but I'm having trouble figuring out how to either use the vanilla loader to load my ISBM (which I don't think is possible) or getting my loader to respect the rotation.

 

I developed my ISBM implementation following MBE05 though I admit I'm having a bit of a hard time following the logic through the different model classes. I'm not sure what's actually necessary and what's just abstraction for the sake of the tutorial.

 

Relevant files below. I'm not sure I like the idea of implementing a custom model loader for smart models that explicitly checks for each model, so if there is a better or more generic way of doing this already built into Forge please enlighten me!

 

blockstates/conveyor.json

 

{
"variants": {
	"normal": {
		"model": "manufactory:smartmodel/conveyor"
	},
	"facing=north": {
		"model": "manufactory:smartmodel/conveyor"
	},
	"facing=west": {
		"model": "manufactory:smartmodel/conveyor",
		"y": 90
	},
	"facing=south": {
		"model": "manufactory:smartmodel/conveyor",
		"y": 180
	},
	"facing=east": {
		"model": "manufactory:smartmodel/conveyor",
		"y": 270
	}
}
}

 

 

ClientProxy.java

 

public class ClientProxy extends CommonProxy {

@Override
public void preInit(final FMLPreInitializationEvent event) {

	super.preInit(event);

	final StateMapperBase ignoreState = new StateMapperBase() {
		@Override
		protected ModelResourceLocation getModelResourceLocation(final IBlockState state) {

			return new ModelResourceLocation("manufactory:conveyor");
		}
	};
	ModelLoader.setCustomStateMapper(CommonProxy.blockConveyor, ignoreState);

	ModelLoaderRegistry.registerLoader(new SmartModelLoader());
}

@Override
public void init(final FMLInitializationEvent event) {

	super.init(event);

	final Item itemBlockConveyor = GameRegistry.findItem(Constants.MODID, Constants.BlockNames.CONVEYOR_BELT);
	final ModelResourceLocation itemModelResourceLocation = new ModelResourceLocation(Constants.MODID + ":" + Constants.BlockModels.CONVEYOR_BELT, "inventory");
	Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(itemBlockConveyor, 0, itemModelResourceLocation);

	ClientRegistry.bindTileEntitySpecialRenderer(TileEntityConveyor.class, new TileEntityConveyorRenderer());
}
}

 

 

SmartModelLoader.java

 

public class SmartModelLoader implements ICustomModelLoader {

private static final String SMART_MODEL_RESOURCE_LOCATION = "models/block/smartmodel/";
private IResourceManager resourceManager;

@Override
public boolean accepts(final ResourceLocation resourceLocation) {

	return resourceLocation.getResourceDomain().equals("manufactory") && resourceLocation.getResourcePath().startsWith(SMART_MODEL_RESOURCE_LOCATION);
}

@Override
public IModel loadModel(final ResourceLocation resourceLocation) {

	final String resourcePath = resourceLocation.getResourcePath();
	if (!resourcePath.startsWith(SMART_MODEL_RESOURCE_LOCATION)) {
		assert false : "loadModel expected " + SMART_MODEL_RESOURCE_LOCATION + " but found " + resourcePath;
	}
	final String modelName = resourcePath.substring(SMART_MODEL_RESOURCE_LOCATION.length());

	if (modelName.equals("conveyor")) {
		return new ConveyorModel();
	} else {
		return ModelLoaderRegistry.getMissingModel();
	}
}

@Override
public void onResourceManagerReload(final IResourceManager resourceManager) {

	this.resourceManager = resourceManager;
}
}

 

 

ConveyorModel.java

 

public class ConveyorModel implements IModel {

public static final ResourceLocation TEXTURE_SHEET = new ResourceLocation("manufactory:blocks/conveyor_belt");

public static final ModelResourceLocation MODEL_CORE = new ModelResourceLocation("manufactory:block/conveyor");
public static final ModelResourceLocation MODEL_LEFT_WALL = new ModelResourceLocation("manufactory:block/conveyor_left_wall");
public static final ModelResourceLocation MODEL_RIGHT_WALL = new ModelResourceLocation("manufactory:block/conveyor_right_wall");

@Override
public Collection<ResourceLocation> getDependencies() {

	return ImmutableList.copyOf(new ResourceLocation[] { MODEL_CORE, MODEL_LEFT_WALL, MODEL_RIGHT_WALL });
}

@Override
public Collection<ResourceLocation> getTextures() {

	return ImmutableList.copyOf(new ResourceLocation[] { TEXTURE_SHEET });
}

@Override
public IFlexibleBakedModel bake(final IModelState state, final VertexFormat format, final Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter) {

	try {
		IModel subComponent = ModelLoaderRegistry.getModel(MODEL_CORE);
		final IFlexibleBakedModel bakedModelCore = subComponent.bake(state, format, bakedTextureGetter);

		subComponent = ModelLoaderRegistry.getModel(MODEL_LEFT_WALL);
		final IFlexibleBakedModel bakedModelLeft = subComponent.bake(state, format, bakedTextureGetter);

		subComponent = ModelLoaderRegistry.getModel(MODEL_RIGHT_WALL);
		final IFlexibleBakedModel bakedModelRight = subComponent.bake(state, format, bakedTextureGetter);

		return new CompositeModel(bakedModelCore, bakedModelLeft, bakedModelRight);
	} catch (IOException e) {
		e.printStackTrace();
	}
	return null;
}

@Override
public IModelState getDefaultState() {

	return null;
}
}

 

 

CompositeModel.java

 

public class CompositeModel implements IFlexibleBakedModel, ISmartBlockModel {

private final IFlexibleBakedModel _modelCore;
private final IFlexibleBakedModel _modelLeft;
private final IFlexibleBakedModel _modelRight;

public CompositeModel(final IFlexibleBakedModel modelCore, final IFlexibleBakedModel modelLeft, final IFlexibleBakedModel modelRight) {

	_modelCore = modelCore;
	_modelLeft = modelLeft;
	_modelRight = modelRight;
}

@Override
public List<BakedQuad> getFaceQuads(final EnumFacing side) {

	throw new UnsupportedOperationException();
}

@Override
public List<BakedQuad> getGeneralQuads() {

	throw new UnsupportedOperationException();
}

@Override
public boolean isAmbientOcclusion() {

	return _modelCore.isAmbientOcclusion();
}

@Override
public boolean isGui3d() {

	return _modelCore.isGui3d();
}

@Override
public boolean isBuiltInRenderer() {

	return false;
}

@Override
public TextureAtlasSprite getTexture() {

	return _modelCore.getTexture();
}

@Override
public ItemCameraTransforms getItemCameraTransforms() {

	return _modelCore.getItemCameraTransforms();
}

@Override
public VertexFormat getFormat() {

	return Attributes.DEFAULT_BAKED_FORMAT;
}

@Override
public IBakedModel handleBlockState(final IBlockState state) {

	if (state instanceof IExtendedBlockState) {
		return new AssembledBakedModel((IExtendedBlockState)state, _modelCore, _modelLeft, _modelRight);
	}
	return new AssembledBakedModel(_modelCore, _modelLeft, _modelRight);
}
}

 

 

AssembledBakedModel.java

 

public class AssembledBakedModel implements IFlexibleBakedModel {

private final boolean _left;
private final boolean _right;
private final EnumFacing _facing;
private final IFlexibleBakedModel _modelCore;
private final IFlexibleBakedModel _modelLeft;
private final IFlexibleBakedModel _modelRight;

public AssembledBakedModel(final IFlexibleBakedModel modelCore, final IFlexibleBakedModel modelLeft, final IFlexibleBakedModel modelRight) {

	_left = false;
	_right = false;
	_facing = EnumFacing.NORTH;

	_modelCore = modelCore;
	_modelLeft = modelLeft;
	_modelRight = modelRight;
}

public AssembledBakedModel(final IExtendedBlockState state, final IFlexibleBakedModel modelCore, final IFlexibleBakedModel modelLeft, final IFlexibleBakedModel modelRight) {

	final Boolean sourceLeft = state.getValue(BlockConveyor.SOURCE_LEFT);
	_left = sourceLeft != null ? sourceLeft : false;

	final Boolean sourceRight = state.getValue(BlockConveyor.SOURCE_RIGHT);
	_right = sourceRight != null ? sourceRight : false;

	final EnumFacing facing = (EnumFacing)state.getValue(Constants.BlockStateProperties.FACING_HORIZONTAL);
	_facing = facing;

	_modelCore = modelCore;
	_modelLeft = modelLeft;
	_modelRight = modelRight;
}

@Override
public List<BakedQuad> getFaceQuads(final EnumFacing side) {

	final List<BakedQuad> allFaceQuads = new LinkedList<>();
	allFaceQuads.addAll(_modelCore.getFaceQuads(side));

	if (!_left) {
		allFaceQuads.addAll(_modelLeft.getFaceQuads(side));
	}

	if (!_right) {
		allFaceQuads.addAll(_modelRight.getFaceQuads(side));
	}

	final float rotationAngle;
	switch (_facing) {

		case DOWN:
		case UP:
		case NORTH:
			rotationAngle = 0;
			break;
		case EAST:
			rotationAngle = 90;
			break;
		case SOUTH:
			rotationAngle = 180;
			break;
		case WEST:
			rotationAngle = 270;
			break;
	}

	allFaceQuads.forEach(quad -> {
		final int[] vertices = quad.getVertexData();

	});

	return allFaceQuads;
}

@Override
public List<BakedQuad> getGeneralQuads() {

	final List<BakedQuad> allGeneralQuads = new LinkedList<>();
	allGeneralQuads.addAll(_modelCore.getGeneralQuads());

	if (!_left) {
		allGeneralQuads.addAll(_modelLeft.getGeneralQuads());
	}

	if (!_right) {
		allGeneralQuads.addAll(_modelRight.getGeneralQuads());
	}

	return allGeneralQuads;
}

@Override
public boolean isAmbientOcclusion() {

	return _modelCore.isAmbientOcclusion();
}

@Override
public boolean isGui3d() {

	return _modelCore.isGui3d();
}

@Override
public boolean isBuiltInRenderer() {

	return false;
}

@Override
public TextureAtlasSprite getTexture() {

	return _modelCore.getTexture();
}

@Override
public ItemCameraTransforms getItemCameraTransforms() {

	return _modelCore.getItemCameraTransforms();
}

@Override
public VertexFormat getFormat() {

	return Attributes.DEFAULT_BAKED_FORMAT;
}
}

 

 

Thanks for the help!

Link to comment
Share on other sites

Been away for a few days but I could still use help with this. Ideally, I like to avoid having to implement a custom blockstate file parser to pick those rotation values back up. Also, I looked at performing the rotation manually on the vertex data inside the AssembledBakedModel.getFaceQuads and AssembledBakedModel.getGeneralQuads methods, but I can't tell what format the vertex data is in. The values are huge float numbers, not the 0-1 or 0-16 I would expect.

Link to comment
Share on other sites

Hi

 

I think you're right about the reason why rotation isn't working.  That magic all happens in FaceBakery.makeBakedQuad(), from ModelBakery.bakeModel(), from IModel.bake()

Starts from ModelBlockDefinition.parseRotation() originally, goes through ModelBakery.bakeBlockModels(variant.getRotation()).  I imagine that the loader bypasses a critical step in that.

 

I think you might have success at this line

final IFlexibleBakedModel bakedModelCore = subComponent.bake(state, format, bakedTextureGetter);

but to be honest the whole model stuff makes my teeth ache and I'm not 100% sure how it works myself.  Apparently there is also a new, more flexible Forge model format, but I haven't worked with it. 

 

You can find a bit more information about the vertex model in here, if you're interested, but I think you can probably get it to work at a higher level than that.

https://github.com/TheGreyGhost/MinecraftByExample/blob/master/src/main/java/minecraftbyexample/mbe15_item_smartitemmodel/ChessboardSmartItemModel.java

(See vertexToInts() and createBakedQuadForFace())

 

If you figure it out, please let me know and I'll add it to MBE05....

 

-TGG

 

 

Link to comment
Share on other sites

I have been banging my head on model rotation for ISBMs the past couple and finally got it to work.

 

Spend some time studying the TRSRDeserializer class and how it sets up transformations for rotations that it parses from a JSON files.  Set up a JSON file with what you want and you can trace it through in the debugger if you like.

 

The Forge V1 blockstate docs will also be useful:

http://mcforge.readthedocs.org/en/latest/blockstates/forgeBlockstates/

https://gist.github.com/RainWarrior/0618131f51b8d37b80a6

 

Lastly (and this is the part it took me a while to track down) you'll probably need to call TRSRTransformation.blockCenterToCorner on your TRSRTransformation instance before you use it to bake your model. If you don't, you model will be rotated around the origin instead of the center of the block.  See the ForgeBlockStateV1 class for examples of usage.

Link to comment
Share on other sites

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



×
×
  • Create New...

Important Information

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