Skip to content

Commit

Permalink
Add first iteration of custom block-renderers (#561)
Browse files Browse the repository at this point in the history
  • Loading branch information
TBlueF committed Nov 1, 2024
1 parent b26b908 commit c2fa00d
Show file tree
Hide file tree
Showing 15 changed files with 239 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.map.TextureGallery;
import de.bluecolored.bluemap.core.map.TileMetaConsumer;
import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModelFactory;
import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModelRenderer;
import de.bluecolored.bluemap.core.resources.pack.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.world.Chunk;
Expand All @@ -39,31 +39,31 @@ public class HiresModelRenderer {
private final ResourcePack resourcePack;
private final RenderSettings renderSettings;

private final ThreadLocal<BlockStateModelFactory> threadLocalModelFactory;
private final ThreadLocal<BlockStateModelRenderer> threadLocalBlockRenderer;

public HiresModelRenderer(ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings) {
this.resourcePack = resourcePack;
this.renderSettings = renderSettings;

this.threadLocalModelFactory = ThreadLocal.withInitial(() -> new BlockStateModelFactory(resourcePack, textureGallery, renderSettings));
this.threadLocalBlockRenderer = ThreadLocal.withInitial(() -> new BlockStateModelRenderer(resourcePack, textureGallery, renderSettings));
}

public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel model) {
render(world, modelMin, modelMax, model, (x, z, c, h, l) -> {});
}

public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel model, TileMetaConsumer tileMetaConsumer) {
public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel tileModel, TileMetaConsumer tileMetaConsumer) {
Vector3i min = modelMin.max(renderSettings.getMinPos());
Vector3i max = modelMax.min(renderSettings.getMaxPos());
Vector3i modelAnchor = new Vector3i(modelMin.getX(), 0, modelMin.getZ());

BlockStateModelFactory modelFactory = threadLocalModelFactory.get();
BlockStateModelRenderer blockRenderer = threadLocalBlockRenderer.get();

int maxHeight, minY, maxY;
double topBlockLight;
Color columnColor = new Color(), blockColor = new Color();
BlockNeighborhood<?> block = new BlockNeighborhood<>(resourcePack, renderSettings, world, 0, 0, 0);
BlockModelView blockModel = new BlockModelView(model);
TileModelView blockModel = new TileModelView(tileModel);

int x, y, z;
for (x = modelMin.getX(); x <= modelMax.getX(); x++){
Expand All @@ -85,7 +85,7 @@ public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel

blockModel.initialize();

modelFactory.render(block, blockModel, blockColor);
blockRenderer.render(block, blockModel, blockColor);

//update topBlockLight
topBlockLight = Math.max(topBlockLight, block.getBlockLightLevel() * (1 - columnColor.a));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,109 +27,109 @@
import de.bluecolored.bluemap.core.util.math.MatrixM3f;
import de.bluecolored.bluemap.core.util.math.MatrixM4f;

public class BlockModelView {
public class TileModelView {

private TileModel hiresTile;
private TileModel tileModel;
private int start, size;

public BlockModelView(TileModel hiresTile) {
initialize(hiresTile);
public TileModelView(TileModel tileModel) {
initialize(tileModel);
}

public BlockModelView initialize(TileModel hiresTile, int start) {
this.hiresTile = hiresTile;
public TileModelView initialize(TileModel hiresTile, int start) {
this.tileModel = hiresTile;
this.start = start;
this.size = hiresTile.size() - start;

return this;
}

public BlockModelView initialize(TileModel hiresTile) {
this.hiresTile = hiresTile;
public TileModelView initialize(TileModel hiresTile) {
this.tileModel = hiresTile;
this.start = hiresTile.size();
this.size = 0;

return this;
}

public BlockModelView initialize(int start) {
public TileModelView initialize(int start) {
this.start = start;
this.size = hiresTile.size() - start;
this.size = tileModel.size() - start;

return this;
}

public BlockModelView initialize() {
this.start = hiresTile.size();
public TileModelView initialize() {
this.start = tileModel.size();
this.size = 0;

return this;
}

public BlockModelView reset() {
hiresTile.reset(this.start);
public TileModelView reset() {
tileModel.reset(this.start);
this.size = 0;

return this;
}

public int add(int count) {
int s = hiresTile.add(count);
int s = tileModel.add(count);
if (s != start + size) throw new IllegalStateException("Size of HiresTileModel had external changes since view-initialisation!");
this.size += count;
return s;
}

public BlockModelView rotate(float angle, float axisX, float axisY, float axisZ) {
hiresTile.rotate(start, size, angle, axisX, axisY, axisZ);
public TileModelView rotate(float angle, float axisX, float axisY, float axisZ) {
tileModel.rotate(start, size, angle, axisX, axisY, axisZ);
return this;
}

public BlockModelView rotate(float pitch, float yaw, float roll) {
hiresTile.rotate(start, size, pitch, yaw, roll);
public TileModelView rotate(float pitch, float yaw, float roll) {
tileModel.rotate(start, size, pitch, yaw, roll);
return this;
}

public BlockModelView scale(float sx, float sy, float sz) {
hiresTile.scale(start, size, sx, sy, sz);
public TileModelView scale(float sx, float sy, float sz) {
tileModel.scale(start, size, sx, sy, sz);
return this;
}

public BlockModelView translate(float dx, float dy, float dz) {
hiresTile.translate(start, size, dx, dy, dz);
public TileModelView translate(float dx, float dy, float dz) {
tileModel.translate(start, size, dx, dy, dz);
return this;
}

public BlockModelView transform(MatrixM3f t) {
hiresTile.transform(start, size, t);
public TileModelView transform(MatrixM3f t) {
tileModel.transform(start, size, t);
return this;
}

public BlockModelView transform(
public TileModelView transform(
float m00, float m01, float m02,
float m10, float m11, float m12,
float m20, float m21, float m22
) {
hiresTile.transform(start, size,
tileModel.transform(start, size,
m00, m01, m02,
m10, m11, m12,
m20, m21, m22
);
return this;
}

public BlockModelView transform(MatrixM4f t) {
hiresTile.transform(start, size, t);
public TileModelView transform(MatrixM4f t) {
tileModel.transform(start, size, t);
return this;
}

public BlockModelView transform(
public TileModelView transform(
float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33
) {
hiresTile.transform(start, size,
tileModel.transform(start, size,
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
Expand All @@ -138,8 +138,8 @@ public BlockModelView transform(
return this;
}

public TileModel getHiresTile() {
return hiresTile;
public TileModel getTileModel() {
return tileModel;
}

public int getStart() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package de.bluecolored.bluemap.core.map.hires.blockmodel;

import de.bluecolored.bluemap.core.map.hires.TileModelView;
import de.bluecolored.bluemap.core.resources.pack.resourcepack.blockstate.Variant;
import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.world.block.BlockNeighborhood;

public interface BlockRenderer {

/**
* Renders the given blocks (block-state-)variant into the given blockModel, and sets the given blockColor to the
* color that represents the rendered block.
* <p>
* <b>Implementation Note:</b><br>
* This method is guaranteed to be called only on <b>one thread per BlockRenderer instance</b>, so you can use this
* for optimizations.<br>
* Keep in mind this method will be called once for every block that is being rendered, so be very careful
* about performance and instance-creations.
* </p>
* @param block The block information that should be rendered.
* @param variant The block-state variant that should be rendered.
* @param blockModel The model(-view) where the block should be rendered to.
* @param blockColor The color that should be set to the color that represents the rendered block.
*/
void render(BlockNeighborhood<?> block, Variant variant, TileModelView blockModel, Color blockColor);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.bluecolored.bluemap.core.map.hires.blockmodel;

import de.bluecolored.bluemap.core.map.TextureGallery;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resources.pack.resourcepack.ResourcePack;

public interface BlockRendererFactory {

BlockRenderer create(ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package de.bluecolored.bluemap.core.map.hires.blockmodel;

import de.bluecolored.bluemap.core.map.TextureGallery;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resources.pack.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.util.Keyed;
import de.bluecolored.bluemap.core.util.Registry;
import de.bluecolored.bluemap.core.world.BlockState;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

public interface BlockRendererType extends Keyed, BlockRendererFactory {

BlockRendererType DEFAULT = new Impl(Key.bluemap("default"), ResourceModelRenderer::new);
BlockRendererType LIQUID = new Impl(Key.bluemap("liquid"), LiquidModelRenderer::new);
BlockRendererType MISSING = new Impl(Key.bluemap("missing"), MissingModelRenderer::new);

Registry<BlockRendererType> REGISTRY = new Registry<>(
DEFAULT,
LIQUID,
MISSING
);

/**
* If the loaded resourcepack does not have any resources for this blockState, this method will be called.
* If this method returns true, this renderer will be used to render the block instead of rendering the default
* black-purple "missing block" model.
* When rendering, the provided "variant" will always be bluemaps default "missing-block" resource.
*
* <p>
* This can (and should only then) be used to provide a way of rendering blocks that are completely dynamically
* created by a mod, and there is no way to provide static block-state resources that point at the correct renderer.
* </p>
*
* @param blockState The {@link BlockState} that was not found in the loaded resources.
* @return true if this renderer-type can render the provided {@link BlockState} despite missing resources.
*/
default boolean isFallbackFor(BlockState blockState) {
return false;
}

@RequiredArgsConstructor
class Impl implements BlockRendererType {

@Getter private final Key key;
private final BlockRendererFactory rendererFactory;

@Override
public BlockRenderer create(ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings) {
return rendererFactory.create(resourcePack, textureGallery, renderSettings);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
*/
package de.bluecolored.bluemap.core.map.hires.blockmodel;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.core.map.TextureGallery;
import de.bluecolored.bluemap.core.map.hires.BlockModelView;
import de.bluecolored.bluemap.core.map.hires.TileModelView;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resources.pack.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resources.pack.resourcepack.blockmodel.BlockModel;
Expand All @@ -37,27 +39,25 @@
import java.util.ArrayList;
import java.util.List;

public class BlockStateModelFactory {
public class BlockStateModelRenderer {

private final ResourcePack resourcePack;
private final ResourceModelBuilder resourceModelBuilder;
private final LiquidModelBuilder liquidModelBuilder;
private final LoadingCache<BlockRendererType, BlockRenderer> blockRenderers;

private final List<Variant> variants = new ArrayList<>();

public BlockStateModelFactory(ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings) {
public BlockStateModelRenderer(ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings) {
this.resourcePack = resourcePack;

this.resourceModelBuilder = new ResourceModelBuilder(resourcePack, textureGallery, renderSettings);
this.liquidModelBuilder = new LiquidModelBuilder(resourcePack, textureGallery, renderSettings);
this.blockRenderers = Caffeine.newBuilder()
.build(type -> type.create(resourcePack, textureGallery, renderSettings));
}

public void render(BlockNeighborhood<?> block, BlockModelView blockModel, Color blockColor) {
public void render(BlockNeighborhood<?> block, TileModelView blockModel, Color blockColor) {
render(block, block.getBlockState(), blockModel, blockColor);
}

private final Color waterloggedColor = new Color();
public void render(BlockNeighborhood<?> block, BlockState blockState, BlockModelView blockModel, Color blockColor) {
public void render(BlockNeighborhood<?> block, BlockState blockState, TileModelView blockModel, Color blockColor) {
blockColor.set(0, 0, 0, 0, true);

//shortcut for air
Expand All @@ -79,7 +79,7 @@ public void render(BlockNeighborhood<?> block, BlockState blockState, BlockModel
}

private final Color variantColor = new Color();
private void renderModel(BlockNeighborhood<?> block, BlockState blockState, BlockModelView blockModel, Color blockColor) {
private void renderModel(BlockNeighborhood<?> block, BlockState blockState, TileModelView blockModel, Color blockColor) {
int modelStart = blockModel.getStart();

var stateResource = resourcePack.getBlockState(blockState);
Expand All @@ -98,11 +98,8 @@ private void renderModel(BlockNeighborhood<?> block, BlockState blockState, Bloc

variantColor.set(0f, 0f, 0f, 0f, true);

if (modelResource.isLiquid()) {
liquidModelBuilder.build(block, blockState, variant, blockModel.initialize(), variantColor);
} else {
resourceModelBuilder.build(block, variant, blockModel.initialize(), variantColor);
}
blockRenderers.get(modelResource.getRenderer())
.render(block, variant, blockModel.initialize(), blockColor);

if (variantColor.a > blockColorOpacity)
blockColorOpacity = variantColor.a;
Expand Down
Loading

0 comments on commit c2fa00d

Please sign in to comment.