Refactored CROs, removed internal chunk borders. Documented CROs.
- Refactored CROs - Renamed CROOpaqueCube to CROSurface (including relevant interfaces) - Optimized CROS - CROS now takes neighbor chunks into account - Refactored default OptimizedSurface implementations - Documented some CRO code
This commit is contained in:
parent
85edc07c75
commit
260562310a
@ -47,7 +47,7 @@ repositories {
|
||||
* Currently used by:
|
||||
* - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1
|
||||
*/
|
||||
maven { url 'https://windcorp.ru/./maven' }
|
||||
maven { url 'http://192.168.0.71/./maven' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -38,10 +38,6 @@ public class StaticModel extends Model {
|
||||
this.transforms = transforms;
|
||||
}
|
||||
|
||||
public StaticModel(Builder builder) {
|
||||
this(builder.getParts(), builder.getTransforms());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mat4 getTransform(int partIndex) {
|
||||
return transforms[partIndex];
|
||||
@ -83,6 +79,10 @@ public class StaticModel extends Model {
|
||||
return transforms.toArray(new Mat4[transforms.size()]);
|
||||
}
|
||||
|
||||
public StaticModel build() {
|
||||
return new StaticModel(getParts(), getTransforms());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ public class LayerWorld extends Layer {
|
||||
);
|
||||
}
|
||||
|
||||
return new StaticModel(b);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
private static final float FRICTION_COEFF = Units.get("1e-5f kg/s");
|
||||
|
@ -18,35 +18,20 @@
|
||||
|
||||
package ru.windcorp.progressia.client.world;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.Model;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.client.graphics.model.StaticModel;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.model.StaticModel.Builder;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRenderNone;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRenderRegistry;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSupplier;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizers;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRenderRegistry;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRenderStack;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
||||
|
||||
public class ChunkRender
|
||||
@ -55,7 +40,7 @@ public class ChunkRender
|
||||
private final WorldRender world;
|
||||
private final ChunkData data;
|
||||
|
||||
private Model model = null;
|
||||
private final ChunkRenderModel model;
|
||||
|
||||
private final Map<TileDataStack, TileRenderStackImpl> tileRenderLists = Collections
|
||||
.synchronizedMap(new WeakHashMap<>());
|
||||
@ -63,6 +48,7 @@ public class ChunkRender
|
||||
public ChunkRender(WorldRender world, ChunkData data) {
|
||||
this.world = world;
|
||||
this.data = data;
|
||||
this.model = new ChunkRenderModel(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -107,165 +93,11 @@ public class ChunkRender
|
||||
}
|
||||
|
||||
public synchronized void render(ShapeRenderHelper renderer) {
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderer.pushTransform().translate(
|
||||
data.getX() * ChunkData.BLOCKS_PER_CHUNK,
|
||||
data.getY() * ChunkData.BLOCKS_PER_CHUNK,
|
||||
data.getZ() * ChunkData.BLOCKS_PER_CHUNK
|
||||
);
|
||||
|
||||
model.render(renderer);
|
||||
|
||||
renderer.popTransform();
|
||||
}
|
||||
|
||||
public synchronized void update() {
|
||||
Collection<ChunkRenderOptimizer> optimizers = ChunkRenderOptimizers.getAllSuppliers().stream()
|
||||
.map(ChunkRenderOptimizerSupplier::createOptimizer)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
optimizers.forEach(bro -> bro.startRender(this));
|
||||
|
||||
StaticModel.Builder builder = StaticModel.builder();
|
||||
|
||||
Vec3i cursor = new Vec3i();
|
||||
for (int x = 0; x < ChunkData.BLOCKS_PER_CHUNK; ++x) {
|
||||
for (int y = 0; y < ChunkData.BLOCKS_PER_CHUNK; ++y) {
|
||||
for (int z = 0; z < ChunkData.BLOCKS_PER_CHUNK; ++z) {
|
||||
cursor.set(x, y, z);
|
||||
|
||||
buildBlock(cursor, optimizers, builder);
|
||||
buildBlockTiles(cursor, optimizers, builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
optimizers.stream()
|
||||
.map(ChunkRenderOptimizer::endRender)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(builder::addPart);
|
||||
|
||||
model = new StaticModel(builder);
|
||||
}
|
||||
|
||||
private void buildBlock(
|
||||
Vec3i cursor,
|
||||
Collection<ChunkRenderOptimizer> optimizers,
|
||||
Builder builder
|
||||
) {
|
||||
BlockRender block = getBlock(cursor);
|
||||
|
||||
if (block instanceof BlockRenderNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
forwardBlockToOptimizers(block, cursor, optimizers);
|
||||
|
||||
if (!block.needsOwnRenderable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
addBlockRenderable(block, cursor, builder);
|
||||
}
|
||||
|
||||
private void forwardBlockToOptimizers(
|
||||
BlockRender block,
|
||||
Vec3i cursor,
|
||||
Collection<ChunkRenderOptimizer> optimizers
|
||||
) {
|
||||
optimizers.forEach(cro -> cro.processBlock(block, cursor));
|
||||
}
|
||||
|
||||
private void addBlockRenderable(
|
||||
BlockRender block,
|
||||
Vec3i cursor,
|
||||
Builder builder
|
||||
) {
|
||||
Renderable renderable = block.createRenderable();
|
||||
|
||||
if (renderable == null) {
|
||||
renderable = block::render;
|
||||
}
|
||||
|
||||
builder.addPart(
|
||||
renderable,
|
||||
new Mat4().identity().translate(cursor.x, cursor.y, cursor.z)
|
||||
);
|
||||
}
|
||||
|
||||
private void buildBlockTiles(
|
||||
Vec3i cursor,
|
||||
Collection<ChunkRenderOptimizer> optimizers,
|
||||
Builder builder
|
||||
) {
|
||||
for (BlockFace face : BlockFace.getFaces()) {
|
||||
buildFaceTiles(cursor, face, optimizers, builder);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildFaceTiles(
|
||||
Vec3i cursor,
|
||||
BlockFace face,
|
||||
Collection<ChunkRenderOptimizer> optimizers,
|
||||
Builder builder
|
||||
) {
|
||||
List<TileData> tiles = getData().getTilesOrNull(cursor, face);
|
||||
|
||||
if (tiles == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int layer = 0; layer < tiles.size(); ++layer) {
|
||||
|
||||
if (tiles.get(layer) == null) {
|
||||
System.out.println(tiles.get(layer).getId());
|
||||
}
|
||||
|
||||
buildTile(
|
||||
cursor,
|
||||
face,
|
||||
TileRenderRegistry.getInstance().get(
|
||||
tiles.get(layer).getId()
|
||||
),
|
||||
layer,
|
||||
optimizers,
|
||||
builder
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildTile(
|
||||
Vec3i cursor,
|
||||
BlockFace face,
|
||||
TileRender tile,
|
||||
int layer,
|
||||
Collection<ChunkRenderOptimizer> optimizers,
|
||||
Builder builder
|
||||
) {
|
||||
// TODO implement
|
||||
|
||||
Vec3 pos = new Vec3(cursor.x, cursor.y, cursor.z);
|
||||
|
||||
optimizers.forEach(cro -> cro.processTile(tile, cursor, face));
|
||||
|
||||
if (!tile.needsOwnRenderable())
|
||||
return;
|
||||
|
||||
Vec3 offset = new Vec3(
|
||||
face.getVector().x,
|
||||
face.getVector().y,
|
||||
face.getVector().z
|
||||
);
|
||||
|
||||
pos.add(offset.mul(1f / 64));
|
||||
|
||||
builder.addPart(
|
||||
tile.createRenderable(face),
|
||||
new Mat4().identity().translate(pos)
|
||||
);
|
||||
model.update();
|
||||
}
|
||||
|
||||
private class TileRenderStackImpl extends TileRenderStack {
|
||||
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.client.world;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.Model;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.client.graphics.model.StaticModel;
|
||||
import ru.windcorp.progressia.client.graphics.model.StaticModel.Builder;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRenderNone;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRenderNone;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRenderStack;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public class ChunkRenderModel implements Renderable {
|
||||
|
||||
private final ChunkRender chunk;
|
||||
|
||||
private final Collection<ChunkRenderOptimizer> optimizers = new ArrayList<>();
|
||||
private Model model = null;
|
||||
|
||||
public ChunkRenderModel(ChunkRender chunk) {
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ShapeRenderHelper renderer) {
|
||||
if (model == null) return;
|
||||
|
||||
renderer.pushTransform().translate(
|
||||
chunk.getX() * ChunkData.BLOCKS_PER_CHUNK,
|
||||
chunk.getY() * ChunkData.BLOCKS_PER_CHUNK,
|
||||
chunk.getZ() * ChunkData.BLOCKS_PER_CHUNK
|
||||
);
|
||||
|
||||
model.render(renderer);
|
||||
|
||||
renderer.popTransform();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
setupCROs();
|
||||
|
||||
StaticModel.Builder sink = StaticModel.builder();
|
||||
|
||||
optimizers.forEach(ChunkRenderOptimizer::startRender);
|
||||
|
||||
chunk.forEachBiC(blockInChunk -> {
|
||||
processBlockAndTiles(blockInChunk, sink);
|
||||
});
|
||||
|
||||
for (ChunkRenderOptimizer optimizer : optimizers) {
|
||||
Renderable renderable = optimizer.endRender();
|
||||
if (renderable != null) {
|
||||
sink.addPart(renderable);
|
||||
}
|
||||
}
|
||||
|
||||
this.model = sink.build();
|
||||
this.optimizers.clear();
|
||||
}
|
||||
|
||||
private void setupCROs() {
|
||||
Set<String> ids = ChunkRenderOptimizerRegistry.getInstance().keySet();
|
||||
|
||||
for (String id : ids) {
|
||||
ChunkRenderOptimizer optimizer = ChunkRenderOptimizerRegistry.getInstance().create(id);
|
||||
optimizer.setup(chunk);
|
||||
this.optimizers.add(optimizer);
|
||||
}
|
||||
}
|
||||
|
||||
private void processBlockAndTiles(Vec3i blockInChunk, Builder sink) {
|
||||
processBlock(blockInChunk, sink);
|
||||
|
||||
for (BlockFace face : BlockFace.getFaces()) {
|
||||
processTileStack(blockInChunk, face, sink);
|
||||
}
|
||||
}
|
||||
|
||||
private void processBlock(Vec3i blockInChunk, Builder sink) {
|
||||
BlockRender block = chunk.getBlock(blockInChunk);
|
||||
|
||||
if (block instanceof BlockRenderNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.needsOwnRenderable()) {
|
||||
sink.addPart(
|
||||
block.createRenderable(chunk.getData(), blockInChunk),
|
||||
new Mat4().identity().translate(blockInChunk.x, blockInChunk.y, blockInChunk.z)
|
||||
);
|
||||
}
|
||||
|
||||
processBlockWithCROs(block, blockInChunk);
|
||||
}
|
||||
|
||||
private void processBlockWithCROs(BlockRender block, Vec3i blockInChunk) {
|
||||
for (ChunkRenderOptimizer optimizer : optimizers) {
|
||||
optimizer.addBlock(block, blockInChunk);
|
||||
}
|
||||
}
|
||||
|
||||
private void processTileStack(Vec3i blockInChunk, BlockFace face, Builder sink) {
|
||||
TileRenderStack trs = chunk.getTilesOrNull(blockInChunk, face);
|
||||
|
||||
if (trs == null || trs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
trs.forEach(tile -> processTile(tile, blockInChunk, face, sink));
|
||||
}
|
||||
|
||||
private void processTile(TileRender tile, Vec3i blockInChunk, BlockFace face, Builder sink) {
|
||||
if (tile instanceof TileRenderNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tile.needsOwnRenderable()) {
|
||||
sink.addPart(
|
||||
tile.createRenderable(chunk.getData(), blockInChunk, face),
|
||||
new Mat4().identity().translate(blockInChunk.x, blockInChunk.y, blockInChunk.z)
|
||||
);
|
||||
}
|
||||
|
||||
processTileWithCROs(tile, blockInChunk, face);
|
||||
}
|
||||
|
||||
private void processTileWithCROs(TileRender tile, Vec3i blockInChunk, BlockFace face) {
|
||||
for (ChunkRenderOptimizer optimizer : optimizers) {
|
||||
optimizer.addTile(tile, blockInChunk, face);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,8 +18,12 @@
|
||||
|
||||
package ru.windcorp.progressia.client.world;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
|
||||
class ChunkUpdateListener implements ChunkDataListener {
|
||||
|
||||
@ -34,4 +38,52 @@ class ChunkUpdateListener implements ChunkDataListener {
|
||||
world.getChunk(chunk).markForUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkLoaded(ChunkData chunk) {
|
||||
Vec3i cursor = new Vec3i();
|
||||
for (BlockFace face : BlockFace.getFaces()) {
|
||||
cursor.set(chunk.getX(), chunk.getY(), chunk.getZ());
|
||||
cursor.add(face.getVector());
|
||||
world.markChunkForUpdate(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkBlockChanged(ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current) {
|
||||
onLocationChanged(chunk, blockInChunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkTilesChanged(
|
||||
ChunkData chunk,
|
||||
Vec3i blockInChunk,
|
||||
BlockFace face,
|
||||
TileData tile,
|
||||
boolean wasAdded
|
||||
) {
|
||||
onLocationChanged(chunk, blockInChunk);
|
||||
}
|
||||
|
||||
private void onLocationChanged(ChunkData chunk, Vec3i blockInChunk) {
|
||||
Vec3i chunkPos = new Vec3i(chunk.getPosition());
|
||||
|
||||
if (blockInChunk.x == 0) {
|
||||
chunkPos.x -= 1;
|
||||
} else if (blockInChunk.x == ChunkData.BLOCKS_PER_CHUNK - 1) {
|
||||
chunkPos.x += 1;
|
||||
} else if (blockInChunk.y == 0) {
|
||||
chunkPos.y -= 1;
|
||||
} else if (blockInChunk.y == ChunkData.BLOCKS_PER_CHUNK - 1) {
|
||||
chunkPos.y += 1;
|
||||
} else if (blockInChunk.z == 0) {
|
||||
chunkPos.z -= 1;
|
||||
} else if (blockInChunk.z == ChunkData.BLOCKS_PER_CHUNK - 1) {
|
||||
chunkPos.z += 1;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
world.markChunkForUpdate(chunkPos);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ package ru.windcorp.progressia.client.world.block;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.generic.GenericBlock;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
|
||||
public abstract class BlockRender extends Namespaced implements GenericBlock {
|
||||
@ -35,7 +37,7 @@ public abstract class BlockRender extends Namespaced implements GenericBlock {
|
||||
);
|
||||
}
|
||||
|
||||
public Renderable createRenderable() {
|
||||
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,10 @@
|
||||
|
||||
package ru.windcorp.progressia.client.world.block;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.EmptyModel;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
|
||||
public class BlockRenderNone extends BlockRender {
|
||||
|
||||
@ -28,7 +30,7 @@ public class BlockRenderNone extends BlockRender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable() {
|
||||
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
|
||||
return EmptyModel.getInstance();
|
||||
}
|
||||
|
||||
|
@ -65,9 +65,4 @@ public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsOwnRenderable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,17 +22,27 @@ import static ru.windcorp.progressia.common.world.block.BlockFace.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.model.Shapes;
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import glm.vec._4.Vec4;
|
||||
import ru.windcorp.progressia.client.graphics.Colors;
|
||||
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||
import ru.windcorp.progressia.client.graphics.model.Face;
|
||||
import ru.windcorp.progressia.client.graphics.model.Faces;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerCube.OpaqueCube;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface.BlockOptimizedSurface;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public abstract class BlockRenderTexturedCube
|
||||
extends BlockRender
|
||||
implements OpaqueCube {
|
||||
implements BlockOptimizedSurface {
|
||||
|
||||
private final Map<BlockFace, Texture> textures = new HashMap<>();
|
||||
|
||||
@ -55,22 +65,61 @@ public abstract class BlockRenderTexturedCube
|
||||
textures.put(WEST, westTexture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture(BlockFace face) {
|
||||
return textures.get(face);
|
||||
public Texture getTexture(BlockFace blockFace) {
|
||||
return textures.get(blockFace);
|
||||
}
|
||||
|
||||
public Vec4 getColorMultiplier(BlockFace blockFace) {
|
||||
return Colors.WHITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable() {
|
||||
return new Shapes.PppBuilder(
|
||||
public final void getFaces(
|
||||
ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
|
||||
boolean inner,
|
||||
Consumer<Face> output,
|
||||
Vec3 offset
|
||||
) {
|
||||
output.accept(createFace(chunk, blockInChunk, blockFace, inner, offset));
|
||||
}
|
||||
|
||||
private Face createFace(
|
||||
ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
|
||||
boolean inner,
|
||||
Vec3 offset
|
||||
) {
|
||||
return Faces.createBlockFace(
|
||||
WorldRenderProgram.getDefault(),
|
||||
getTexture(TOP),
|
||||
getTexture(BOTTOM),
|
||||
getTexture(NORTH),
|
||||
getTexture(SOUTH),
|
||||
getTexture(EAST),
|
||||
getTexture(WEST)
|
||||
).create();
|
||||
getTexture(blockFace),
|
||||
getColorMultiplier(blockFace),
|
||||
offset,
|
||||
blockFace,
|
||||
inner
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
|
||||
boolean opaque = isBlockOpaque();
|
||||
|
||||
Face[] faces = new Face[BLOCK_FACE_COUNT + (opaque ? BLOCK_FACE_COUNT : 0)];
|
||||
|
||||
for (int i = 0; i < BLOCK_FACE_COUNT; ++i) {
|
||||
faces[i] = createFace(chunk, blockInChunk, BlockFace.getFaces().get(i), false, Vectors.ZERO_3);
|
||||
}
|
||||
|
||||
if (!opaque) {
|
||||
for (int i = 0; i < BLOCK_FACE_COUNT; ++i) {
|
||||
faces[i + BLOCK_FACE_COUNT] = createFace(chunk, blockInChunk, BlockFace.getFaces().get(i), true, Vectors.ZERO_3);
|
||||
}
|
||||
}
|
||||
|
||||
return new Shape(Usage.STATIC, WorldRenderProgram.getDefault(), faces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsOwnRenderable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -65,9 +65,4 @@ public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsOwnRenderable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,27 +19,111 @@
|
||||
package ru.windcorp.progressia.client.world.cro;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public abstract class ChunkRenderOptimizer {
|
||||
/**
|
||||
* Chunk render optimizer (CRO) is an object that produces optimized models for
|
||||
* chunks. CROs are sequentially given information about the blocks and tiles of
|
||||
* a particular chunk, after which they are expected to produce a set of
|
||||
* {@link Renderable}s. As the name suggests, CROs are primarily expected to
|
||||
* output models that are optimized compared to models of individual blocks and
|
||||
* tiles. An example of a CRO is {@link ChunkRenderOptimizerSurface}: it removes
|
||||
* block surfaces and tiles that it knows cannot be seen, thus significantly
|
||||
* reducing total polygon count.
|
||||
* <h3>CRO lifecycle</h3>
|
||||
* A CRO instance is created by {@link ChunkRenderOptimizerRegistry}. It may
|
||||
* then be used to work on multiple chunks sequentially. Each chunk is processed
|
||||
* in the following way:
|
||||
* <ol>
|
||||
* <li>{@link #setup(ChunkRender)} is invoked to provide the {@link ChunkRender}
|
||||
* instance.</li>
|
||||
* <li>{@link #startRender()} is invoked. The CRO must reset its state.</li>
|
||||
* <li>{@link #addBlock(BlockRender, Vec3i)} and
|
||||
* {@link #addTile(TileRender, Vec3i, BlockFace)} are invoked for each block and
|
||||
* tile that this CRO should optimize. {@code addTile} specifies tiles in order
|
||||
* of ascension within a tile stack.</li>
|
||||
* <li>{@link #endRender()} is invoked. The CRO may perform any pending
|
||||
* calculations. The result of the optimization is returned.</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* Each CRO instance is accessed by a single thread.
|
||||
*/
|
||||
public abstract class ChunkRenderOptimizer extends Namespaced {
|
||||
|
||||
public abstract void startRender(ChunkRender chunk);
|
||||
/**
|
||||
* The chunk that this CRO is currently working on.
|
||||
*/
|
||||
protected ChunkRender chunk = null;
|
||||
|
||||
public abstract void processBlock(
|
||||
BlockRender block,
|
||||
Vec3i posInChunk
|
||||
);
|
||||
/**
|
||||
* Creates a new CRO instance with the specified ID.
|
||||
*
|
||||
* @param id the ID of this CRO
|
||||
*/
|
||||
public ChunkRenderOptimizer(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public abstract void processTile(
|
||||
TileRender tile,
|
||||
Vec3i posInChunk,
|
||||
BlockFace face
|
||||
);
|
||||
/**
|
||||
* This method is invoked before a new chunk processing cycle begins to
|
||||
* specify the chunk. When overriding, {@code super.setup(chunk)} must be
|
||||
* invoked.
|
||||
*
|
||||
* @param chunk the chunk that will be processed next
|
||||
*/
|
||||
public void setup(ChunkRender chunk) {
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
public abstract Shape endRender();
|
||||
/**
|
||||
* @return the chunk that this CRO is currently working on
|
||||
*/
|
||||
public ChunkRender getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this CRO to a state in which a new chunk may be processed.
|
||||
*/
|
||||
public abstract void startRender();
|
||||
|
||||
/**
|
||||
* Requests that this CRO processes the provided block. This method may only
|
||||
* be invoked between {@link #startRender()} and {@link #endRender()}. This
|
||||
* method is only invoked once per block. This method is not necessarily
|
||||
* invoked for each block.
|
||||
*
|
||||
* @param block a {@link BlockRender} instance describing the block.
|
||||
* It corresponds to
|
||||
* {@code getChunk().getBlock(blockInChunk)}.
|
||||
* @param blockInChunk the position of the block
|
||||
*/
|
||||
public abstract void addBlock(BlockRender block, Vec3i blockInChunk);
|
||||
|
||||
/**
|
||||
* Requests that this CRO processes the provided tile. This method may only
|
||||
* be invoked between {@link #startRender()} and {@link #endRender()}. This
|
||||
* method is only invoked once per tile. This method is not necessarily
|
||||
* invoked for each tile. When multiple tiles in a tile stack are requested,
|
||||
* this method is invoked for lower tiles first.
|
||||
*
|
||||
* @param tile a {@link BlockRender} instance describing the tile
|
||||
* @param blockInChunk the position of the block that the tile belongs to
|
||||
* @param blockFace the face that the tile belongs to
|
||||
*/
|
||||
public abstract void addTile(TileRender tile, Vec3i blockInChunk, BlockFace blockFace);
|
||||
|
||||
/**
|
||||
* Requests that the CRO assembles and outputs its model. This method may
|
||||
* only be invoked after {@link #startRender()}.
|
||||
*
|
||||
* @return the assembled {@link Renderable}.
|
||||
*/
|
||||
public abstract Renderable endRender();
|
||||
|
||||
}
|
||||
|
@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.windcorp.progressia.client.world.cro;
|
||||
|
||||
import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK;
|
||||
import static ru.windcorp.progressia.common.world.block.BlockFace.BLOCK_FACE_COUNT;
|
||||
import static ru.windcorp.progressia.common.world.generic.GenericTileStack.TILES_PER_FACE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.Colors;
|
||||
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||
import ru.windcorp.progressia.client.graphics.model.Face;
|
||||
import ru.windcorp.progressia.client.graphics.model.Faces;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public class ChunkRenderOptimizerCube extends ChunkRenderOptimizer {
|
||||
|
||||
public static interface OpaqueCube {
|
||||
public Texture getTexture(BlockFace face);
|
||||
|
||||
public boolean isOpaque(BlockFace face);
|
||||
|
||||
public boolean isBlockOpaque();
|
||||
}
|
||||
|
||||
public static interface OpaqueSurface {
|
||||
public Texture getTexture(BlockFace face);
|
||||
|
||||
public boolean isOpaque(BlockFace face);
|
||||
}
|
||||
|
||||
private static class BlockInfo {
|
||||
OpaqueCube block;
|
||||
final FaceInfo[] faces = new FaceInfo[BLOCK_FACE_COUNT];
|
||||
|
||||
{
|
||||
for (int i = 0; i < faces.length; ++i) {
|
||||
faces[i] = new FaceInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class FaceInfo {
|
||||
static final int NO_OPAQUE_TILES = -1;
|
||||
|
||||
int topOpaqueTile = NO_OPAQUE_TILES;
|
||||
final OpaqueSurface[] tiles = new OpaqueSurface[TILES_PER_FACE];
|
||||
int tileCount = 0;
|
||||
}
|
||||
|
||||
private final BlockInfo[][][] data = new BlockInfo[BLOCKS_PER_CHUNK][BLOCKS_PER_CHUNK][BLOCKS_PER_CHUNK];
|
||||
|
||||
{
|
||||
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
|
||||
for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) {
|
||||
for (int z = 0; z < BLOCKS_PER_CHUNK; ++z) {
|
||||
data[x][y][z] = new BlockInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startRender(ChunkRender chunk) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBlock(BlockRender block, Vec3i pos) {
|
||||
if (!(block instanceof OpaqueCube))
|
||||
return;
|
||||
OpaqueCube opaqueCube = (OpaqueCube) block;
|
||||
addBlock(pos, opaqueCube);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTile(TileRender tile, Vec3i pos, BlockFace face) {
|
||||
if (!(tile instanceof OpaqueSurface))
|
||||
return;
|
||||
OpaqueSurface opaqueTile = (OpaqueSurface) tile;
|
||||
addTile(pos, face, opaqueTile);
|
||||
}
|
||||
|
||||
protected void addBlock(Vec3i pos, OpaqueCube cube) {
|
||||
getBlock(pos).block = cube;
|
||||
}
|
||||
|
||||
private void addTile(Vec3i pos, BlockFace face, OpaqueSurface opaqueTile) {
|
||||
FaceInfo faceInfo = getFace(pos, face);
|
||||
|
||||
int index = faceInfo.tileCount;
|
||||
faceInfo.tileCount++;
|
||||
|
||||
faceInfo.tiles[index] = opaqueTile;
|
||||
|
||||
if (opaqueTile.isOpaque(face)) {
|
||||
faceInfo.topOpaqueTile = index;
|
||||
}
|
||||
}
|
||||
|
||||
protected BlockInfo getBlock(Vec3i cursor) {
|
||||
return data[cursor.x][cursor.y][cursor.z];
|
||||
}
|
||||
|
||||
protected FaceInfo getFace(Vec3i cursor, BlockFace face) {
|
||||
return getBlock(cursor).faces[face.getId()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape endRender() {
|
||||
|
||||
Collection<Face> shapeFaces = new ArrayList<>(
|
||||
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3
|
||||
);
|
||||
|
||||
Vec3i cursor = new Vec3i();
|
||||
|
||||
for (cursor.x = 0; cursor.x < BLOCKS_PER_CHUNK; ++cursor.x) {
|
||||
for (cursor.y = 0; cursor.y < BLOCKS_PER_CHUNK; ++cursor.y) {
|
||||
for (cursor.z = 0; cursor.z < BLOCKS_PER_CHUNK; ++cursor.z) {
|
||||
processInnerFaces(cursor, shapeFaces::add);
|
||||
processOuterFaces(cursor, shapeFaces::add);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Shape(
|
||||
Usage.STATIC,
|
||||
WorldRenderProgram.getDefault(),
|
||||
shapeFaces.toArray(new Face[shapeFaces.size()])
|
||||
);
|
||||
}
|
||||
|
||||
private void processOuterFaces(
|
||||
Vec3i cursor,
|
||||
Consumer<Face> output
|
||||
) {
|
||||
for (BlockFace face : BlockFace.getFaces()) {
|
||||
if (!shouldRenderOuterFace(cursor, face))
|
||||
continue;
|
||||
|
||||
Vec3 faceOrigin = new Vec3(cursor.x, cursor.y, cursor.z);
|
||||
Vec3 offset = new Vec3(face.getVector().x, face.getVector().y, face.getVector().z).mul(1f / 128);
|
||||
|
||||
FaceInfo info = getFace(cursor, face);
|
||||
|
||||
if (info.topOpaqueTile == FaceInfo.NO_OPAQUE_TILES) {
|
||||
OpaqueCube block = getBlock(cursor).block;
|
||||
|
||||
if (block != null) {
|
||||
addFace(
|
||||
faceOrigin,
|
||||
face,
|
||||
getBlock(cursor).block.getTexture(face),
|
||||
output
|
||||
);
|
||||
|
||||
faceOrigin.add(offset);
|
||||
}
|
||||
}
|
||||
|
||||
int startLayer = info.topOpaqueTile;
|
||||
if (startLayer == FaceInfo.NO_OPAQUE_TILES) {
|
||||
startLayer = 0;
|
||||
}
|
||||
|
||||
for (int layer = startLayer; layer < info.tileCount; ++layer) {
|
||||
addFace(
|
||||
faceOrigin,
|
||||
face,
|
||||
info.tiles[layer].getTexture(face),
|
||||
output
|
||||
);
|
||||
|
||||
faceOrigin.add(offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addFace(
|
||||
Vec3 cursor,
|
||||
BlockFace face,
|
||||
Texture texture,
|
||||
Consumer<Face> output
|
||||
) {
|
||||
if (texture == null)
|
||||
return;
|
||||
|
||||
output.accept(
|
||||
Faces.createBlockFace(
|
||||
WorldRenderProgram.getDefault(),
|
||||
texture,
|
||||
Colors.WHITE,
|
||||
new Vec3(cursor),
|
||||
face,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private boolean shouldRenderOuterFace(Vec3i cursor, BlockFace face) {
|
||||
cursor.add(face.getVector());
|
||||
try {
|
||||
|
||||
// TODO handle neighboring chunks properly
|
||||
if (!isInBounds(cursor))
|
||||
return true;
|
||||
|
||||
OpaqueCube adjacent = getBlock(cursor).block;
|
||||
|
||||
if (adjacent == null)
|
||||
return true;
|
||||
if (adjacent.isOpaque(face))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
} finally {
|
||||
cursor.sub(face.getVector());
|
||||
}
|
||||
}
|
||||
|
||||
private void processInnerFaces(
|
||||
Vec3i cursor,
|
||||
Consumer<Face> output
|
||||
) {
|
||||
// if (block.isBlockOpaque()) return;
|
||||
//
|
||||
// for (BlockFace face : BlockFace.getFaces()) {
|
||||
//
|
||||
// Texture texture = block.getTexture(face);
|
||||
// if (texture == null) continue;
|
||||
//
|
||||
// output.accept(Faces.createBlockFace(
|
||||
// WorldRenderProgram.getDefault(),
|
||||
// texture,
|
||||
// COLOR_MULTIPLIER,
|
||||
// new Vec3(cursor.x, cursor.y, cursor.z),
|
||||
// face,
|
||||
// true
|
||||
// ));
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
private boolean isInBounds(Vec3i cursor) {
|
||||
return isInBounds(cursor.x) &&
|
||||
isInBounds(cursor.y) &&
|
||||
isInBounds(cursor.z);
|
||||
}
|
||||
|
||||
private boolean isInBounds(int c) {
|
||||
return c >= 0 && c < BLOCKS_PER_CHUNK;
|
||||
}
|
||||
|
||||
}
|
@ -18,28 +18,17 @@
|
||||
|
||||
package ru.windcorp.progressia.client.world.cro;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import ru.windcorp.progressia.common.util.namespaces.NamespacedFactoryRegistry;
|
||||
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
public class ChunkRenderOptimizerRegistry extends NamespacedFactoryRegistry<ChunkRenderOptimizer> {
|
||||
|
||||
public abstract class ChunkRenderOptimizerSupplier extends Namespaced {
|
||||
private static final ChunkRenderOptimizerRegistry INSTANCE = new ChunkRenderOptimizerRegistry();
|
||||
|
||||
public ChunkRenderOptimizerSupplier(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public abstract ChunkRenderOptimizer createOptimizer();
|
||||
|
||||
public static ChunkRenderOptimizerSupplier of(
|
||||
String id,
|
||||
Supplier<ChunkRenderOptimizer> supplier
|
||||
) {
|
||||
return new ChunkRenderOptimizerSupplier(id) {
|
||||
@Override
|
||||
public ChunkRenderOptimizer createOptimizer() {
|
||||
return supplier.get();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @return the instance
|
||||
*/
|
||||
public static ChunkRenderOptimizerRegistry getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.windcorp.progressia.client.world.cro;
|
||||
|
||||
import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK;
|
||||
import static ru.windcorp.progressia.common.world.block.BlockFace.BLOCK_FACE_COUNT;
|
||||
import static ru.windcorp.progressia.common.world.generic.GenericTileStack.TILES_PER_FACE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||
import ru.windcorp.progressia.client.graphics.model.Face;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
||||
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
|
||||
|
||||
private static final float OVERLAY_OFFSET = 1 / 128f;
|
||||
|
||||
/**
|
||||
* A common interface to objects that can provide optimizeable surfaces.
|
||||
* This is an internal interface; use {@link BlockOptimizedSurface} or
|
||||
* {@link TileOptimizedSurface} instead.
|
||||
*/
|
||||
private static interface OptimizedSurface {
|
||||
|
||||
/**
|
||||
* Creates and outputs a set of faces that correspond to this surface.
|
||||
* The coordinates of the face vertices must be in chunk coordinate
|
||||
* system.
|
||||
*
|
||||
* @param chunk the chunk that contains the requested face
|
||||
* @param blockInChunk the block in chunk
|
||||
* @param blockFace the requested face
|
||||
* @param inner whether this face should be visible from inside
|
||||
* ({@code true}) or outside ({@code false})
|
||||
* @param output a consumer that the created faces must be given
|
||||
* to
|
||||
* @param offset an additional offset that must be applied to all
|
||||
* vertices
|
||||
*/
|
||||
void getFaces(
|
||||
ChunkData chunk,
|
||||
Vec3i blockInChunk,
|
||||
BlockFace blockFace,
|
||||
boolean inner,
|
||||
Consumer<Face> output,
|
||||
Vec3 offset /* kostyl 156% */
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the opacity of the surface identified by the provided
|
||||
* {@link BlockFace}.
|
||||
* Opaque surfaces prevent surfaces behind them from being included in
|
||||
* chunk models.
|
||||
*
|
||||
* @param blockFace the face to query
|
||||
* @return {@code true} iff the surface is opaque.
|
||||
*/
|
||||
boolean isOpaque(BlockFace blockFace);
|
||||
}
|
||||
|
||||
/**
|
||||
* A block that can be optimized by {@link ChunkRenderOptimizerSurface}.
|
||||
*/
|
||||
public static interface BlockOptimizedSurface extends OptimizedSurface {
|
||||
|
||||
/**
|
||||
* Returns the opacity of the block. Opaque blocks do not expect that
|
||||
* the camera can be inside them. Opaque blocks prevent surfaces that
|
||||
* face them
|
||||
* from being included in chunk models.
|
||||
*
|
||||
* @return {@code true} iff the block is opaque.
|
||||
*/
|
||||
boolean isBlockOpaque();
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile that can be optimized by {@link ChunkRenderOptimizerSurface}.
|
||||
*/
|
||||
public static interface TileOptimizedSurface extends OptimizedSurface {
|
||||
// Empty for now
|
||||
}
|
||||
|
||||
private static class BlockInfo {
|
||||
BlockOptimizedSurface block;
|
||||
final FaceInfo[] faces = new FaceInfo[BLOCK_FACE_COUNT];
|
||||
|
||||
{
|
||||
for (int i = 0; i < faces.length; ++i) {
|
||||
faces[i] = new FaceInfo(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class FaceInfo {
|
||||
static final int BLOCK_LAYER = -1;
|
||||
|
||||
final BlockInfo block;
|
||||
|
||||
int topOpaqueSurface = BLOCK_LAYER;
|
||||
int bottomOpaqueSurface = Integer.MAX_VALUE;
|
||||
|
||||
final TileOptimizedSurface[] tiles = new TileOptimizedSurface[TILES_PER_FACE];
|
||||
int tileCount = 0;
|
||||
|
||||
FaceInfo(BlockInfo block) {
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
OptimizedSurface getSurface(int layer) {
|
||||
return layer == BLOCK_LAYER ? block.block : tiles[layer];
|
||||
}
|
||||
}
|
||||
|
||||
private final BlockInfo[][][] data = new BlockInfo[BLOCKS_PER_CHUNK][BLOCKS_PER_CHUNK][BLOCKS_PER_CHUNK];
|
||||
|
||||
public ChunkRenderOptimizerSurface(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startRender() {
|
||||
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
|
||||
for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) {
|
||||
for (int z = 0; z < BLOCKS_PER_CHUNK; ++z) {
|
||||
data[x][y][z] = new BlockInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBlock(BlockRender block, Vec3i pos) {
|
||||
if (!(block instanceof BlockOptimizedSurface))
|
||||
return;
|
||||
|
||||
BlockOptimizedSurface bos = (BlockOptimizedSurface) block;
|
||||
addBlock(pos, bos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTile(TileRender tile, Vec3i pos, BlockFace face) {
|
||||
if (!(tile instanceof TileOptimizedSurface))
|
||||
return;
|
||||
|
||||
TileOptimizedSurface tos = (TileOptimizedSurface) tile;
|
||||
addTile(pos, face, tos);
|
||||
}
|
||||
|
||||
protected void addBlock(Vec3i pos, BlockOptimizedSurface block) {
|
||||
getBlock(pos).block = block;
|
||||
}
|
||||
|
||||
private void addTile(Vec3i pos, BlockFace face, TileOptimizedSurface tile) {
|
||||
FaceInfo faceInfo = getFace(pos, face);
|
||||
|
||||
int index = faceInfo.tileCount;
|
||||
faceInfo.tileCount++;
|
||||
|
||||
faceInfo.tiles[index] = tile;
|
||||
|
||||
if (tile.isOpaque(face)) {
|
||||
faceInfo.topOpaqueSurface = index;
|
||||
|
||||
if (faceInfo.bottomOpaqueSurface == FaceInfo.BLOCK_LAYER) {
|
||||
faceInfo.bottomOpaqueSurface = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected BlockInfo getBlock(Vec3i cursor) {
|
||||
return data[cursor.x][cursor.y][cursor.z];
|
||||
}
|
||||
|
||||
protected FaceInfo getFace(Vec3i cursor, BlockFace face) {
|
||||
return getBlock(cursor).faces[face.getId()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable endRender() {
|
||||
Collection<Face> shapeFaces = new ArrayList<>(
|
||||
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3
|
||||
);
|
||||
|
||||
Vec3i cursor = new Vec3i();
|
||||
Consumer<Face> consumer = shapeFaces::add;
|
||||
|
||||
for (cursor.x = 0; cursor.x < BLOCKS_PER_CHUNK; ++cursor.x) {
|
||||
for (cursor.y = 0; cursor.y < BLOCKS_PER_CHUNK; ++cursor.y) {
|
||||
for (cursor.z = 0; cursor.z < BLOCKS_PER_CHUNK; ++cursor.z) {
|
||||
processInnerFaces(cursor, consumer);
|
||||
processOuterFaces(cursor, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shapeFaces.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Shape(
|
||||
Usage.STATIC,
|
||||
WorldRenderProgram.getDefault(),
|
||||
shapeFaces.toArray(new Face[shapeFaces.size()])
|
||||
);
|
||||
}
|
||||
|
||||
private void processOuterFaces(
|
||||
Vec3i blockInChunk,
|
||||
Consumer<Face> output
|
||||
) {
|
||||
for (BlockFace blockFace : BlockFace.getFaces()) {
|
||||
processOuterFace(blockInChunk, blockFace, output);
|
||||
}
|
||||
}
|
||||
|
||||
private void processOuterFace(Vec3i blockInChunk, BlockFace blockFace, Consumer<Face> output) {
|
||||
if (!shouldRenderOuterFace(blockInChunk, blockFace))
|
||||
return;
|
||||
|
||||
FaceInfo info = getFace(blockInChunk, blockFace);
|
||||
|
||||
if (info.tileCount == 0 && info.block.block == null)
|
||||
return;
|
||||
|
||||
Vec3 faceOrigin = new Vec3(blockInChunk.x, blockInChunk.y, blockInChunk.z);
|
||||
Vec3 offset = new Vec3(blockFace.getFloatVector()).mul(OVERLAY_OFFSET);
|
||||
|
||||
for (
|
||||
int layer = info.topOpaqueSurface;
|
||||
layer < info.tileCount;
|
||||
++layer
|
||||
) {
|
||||
OptimizedSurface surface = info.getSurface(layer);
|
||||
if (surface == null)
|
||||
continue; // layer may be BLOCK_LAYER, then block may be null
|
||||
|
||||
surface.getFaces(chunk.getData(), blockInChunk, blockFace, false, output, faceOrigin);
|
||||
|
||||
faceOrigin.add(offset);
|
||||
}
|
||||
}
|
||||
|
||||
private void processInnerFaces(
|
||||
Vec3i blockInChunk,
|
||||
Consumer<Face> output
|
||||
) {
|
||||
for (BlockFace blockFace : BlockFace.getFaces()) {
|
||||
processInnerFace(blockInChunk, blockFace, output);
|
||||
}
|
||||
}
|
||||
|
||||
private void processInnerFace(Vec3i blockInChunk, BlockFace blockFace, Consumer<Face> output) {
|
||||
if (!shouldRenderInnerFace(blockInChunk, blockFace))
|
||||
return;
|
||||
|
||||
FaceInfo info = getFace(blockInChunk, blockFace);
|
||||
|
||||
if (info.tileCount == 0 && info.block.block == null)
|
||||
return;
|
||||
|
||||
Vec3 faceOrigin = new Vec3(blockInChunk.x, blockInChunk.y, blockInChunk.z);
|
||||
Vec3 offset = new Vec3(blockFace.getFloatVector()).mul(OVERLAY_OFFSET);
|
||||
|
||||
for (
|
||||
int layer = FaceInfo.BLOCK_LAYER;
|
||||
layer <= info.bottomOpaqueSurface && layer < info.tileCount;
|
||||
++layer
|
||||
) {
|
||||
OptimizedSurface surface = info.getSurface(layer);
|
||||
if (surface == null)
|
||||
continue; // layer may be BLOCK_LAYER, then block may be null
|
||||
|
||||
surface.getFaces(chunk.getData(), blockInChunk, blockFace, true, output, faceOrigin);
|
||||
|
||||
faceOrigin.add(offset);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldRenderOuterFace(Vec3i blockInChunk, BlockFace face) {
|
||||
blockInChunk.add(face.getVector());
|
||||
try {
|
||||
return shouldRenderWhenFacing(blockInChunk, face);
|
||||
} finally {
|
||||
blockInChunk.sub(face.getVector());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldRenderInnerFace(Vec3i blockInChunk, BlockFace face) {
|
||||
return shouldRenderWhenFacing(blockInChunk, face);
|
||||
}
|
||||
|
||||
private boolean shouldRenderWhenFacing(Vec3i blockInChunk, BlockFace face) {
|
||||
if (chunk.containsBiC(blockInChunk)) {
|
||||
return shouldRenderWhenFacingLocal(blockInChunk, face);
|
||||
} else {
|
||||
return shouldRenderWhenFacingNeighbor(blockInChunk, face);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldRenderWhenFacingLocal(Vec3i blockInChunk, BlockFace face) {
|
||||
BlockOptimizedSurface block = getBlock(blockInChunk).block;
|
||||
|
||||
if (block == null) {
|
||||
return true;
|
||||
}
|
||||
if (block.isOpaque(face)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean shouldRenderWhenFacingNeighbor(Vec3i blockInLocalChunk, BlockFace face) {
|
||||
Vec3i blockInChunk = Vectors.grab3i().set(blockInLocalChunk.x, blockInLocalChunk.y, blockInLocalChunk.z);
|
||||
Vec3i chunkPos = Vectors.grab3i().set(chunk.getX(), chunk.getY(), chunk.getZ());
|
||||
|
||||
try {
|
||||
// Determine blockInChunk and chunkPos
|
||||
if (blockInLocalChunk.x == -1) {
|
||||
blockInChunk.x = BLOCKS_PER_CHUNK - 1;
|
||||
chunkPos.x -= 1;
|
||||
} else if (blockInLocalChunk.x == BLOCKS_PER_CHUNK) {
|
||||
blockInChunk.x = 0;
|
||||
chunkPos.x += 1;
|
||||
} else if (blockInLocalChunk.y == -1) {
|
||||
blockInChunk.y = BLOCKS_PER_CHUNK - 1;
|
||||
chunkPos.y -= 1;
|
||||
} else if (blockInLocalChunk.y == BLOCKS_PER_CHUNK) {
|
||||
blockInChunk.y = 0;
|
||||
chunkPos.y += 1;
|
||||
} else if (blockInLocalChunk.z == -1) {
|
||||
blockInChunk.z = BLOCKS_PER_CHUNK - 1;
|
||||
chunkPos.z -= 1;
|
||||
} else if (blockInLocalChunk.z == BLOCKS_PER_CHUNK) {
|
||||
blockInChunk.z = 0;
|
||||
chunkPos.z += 1;
|
||||
} else {
|
||||
throw new AssertionError(
|
||||
"Requested incorrent neighbor ("
|
||||
+ blockInLocalChunk.x + "; "
|
||||
+ blockInLocalChunk.y + "; "
|
||||
+ blockInLocalChunk.z + ")"
|
||||
);
|
||||
}
|
||||
|
||||
ChunkRender chunk = this.chunk.getWorld().getChunk(chunkPos);
|
||||
if (chunk == null)
|
||||
return false;
|
||||
|
||||
BlockRender block = chunk.getBlock(blockInChunk);
|
||||
if (!(block instanceof BlockOptimizedSurface))
|
||||
return true;
|
||||
|
||||
BlockOptimizedSurface bos = (BlockOptimizedSurface) block;
|
||||
if (!bos.isOpaque(face))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
} finally {
|
||||
Vectors.release(blockInChunk);
|
||||
Vectors.release(chunkPos);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -19,9 +19,11 @@
|
||||
package ru.windcorp.progressia.client.world.tile;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.generic.GenericTile;
|
||||
|
||||
@ -37,7 +39,7 @@ public class TileRender extends Namespaced implements GenericTile {
|
||||
);
|
||||
}
|
||||
|
||||
public Renderable createRenderable(BlockFace face) {
|
||||
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace face) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -18,19 +18,10 @@
|
||||
|
||||
package ru.windcorp.progressia.client.world.tile;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.client.graphics.Colors;
|
||||
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||
import ru.windcorp.progressia.client.graphics.model.Faces;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerCube.OpaqueSurface;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public class TileRenderGrass extends TileRender implements OpaqueSurface {
|
||||
public class TileRenderGrass extends TileRenderSurface {
|
||||
|
||||
private final Texture topTexture;
|
||||
private final Texture sideTexture;
|
||||
@ -55,27 +46,4 @@ public class TileRenderGrass extends TileRender implements OpaqueSurface {
|
||||
return face == BlockFace.TOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable(BlockFace face) {
|
||||
ShapeRenderProgram program = WorldRenderProgram.getDefault();
|
||||
|
||||
return new Shape(
|
||||
Usage.STATIC,
|
||||
WorldRenderProgram.getDefault(),
|
||||
Faces.createBlockFace(
|
||||
program,
|
||||
getTexture(face),
|
||||
Colors.WHITE,
|
||||
new Vec3(0, 0, 0),
|
||||
face,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsOwnRenderable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,39 +15,28 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.client.world.tile;
|
||||
|
||||
package ru.windcorp.progressia.client.world.cro;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.EmptyModel;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
public class TileRenderNone extends TileRender {
|
||||
|
||||
public class ChunkRenderOptimizers {
|
||||
|
||||
private ChunkRenderOptimizers() {
|
||||
public TileRenderNone(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
private static final Map<String, ChunkRenderOptimizerSupplier> SUPPLIERS = new HashMap<>();
|
||||
|
||||
static {
|
||||
register(
|
||||
ChunkRenderOptimizerSupplier.of(
|
||||
"Default:OpaqueCube",
|
||||
ChunkRenderOptimizerCube::new
|
||||
)
|
||||
);
|
||||
@Override
|
||||
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace face) {
|
||||
return EmptyModel.getInstance();
|
||||
}
|
||||
|
||||
public static ChunkRenderOptimizerSupplier getSupplier(String id) {
|
||||
return SUPPLIERS.get(id);
|
||||
}
|
||||
|
||||
public static void register(ChunkRenderOptimizerSupplier supplier) {
|
||||
SUPPLIERS.put(supplier.getId(), supplier);
|
||||
}
|
||||
|
||||
public static Collection<ChunkRenderOptimizerSupplier> getAllSuppliers() {
|
||||
return SUPPLIERS.values();
|
||||
@Override
|
||||
public boolean needsOwnRenderable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -18,19 +18,25 @@
|
||||
|
||||
package ru.windcorp.progressia.client.world.tile;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import glm.vec._4.Vec4;
|
||||
import ru.windcorp.progressia.client.graphics.Colors;
|
||||
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||
import ru.windcorp.progressia.client.graphics.model.Face;
|
||||
import ru.windcorp.progressia.client.graphics.model.Faces;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerCube.OpaqueSurface;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface.TileOptimizedSurface;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public abstract class TileRenderSurface extends TileRender implements OpaqueSurface {
|
||||
public abstract class TileRenderSurface extends TileRender implements TileOptimizedSurface {
|
||||
|
||||
private final Texture texture;
|
||||
|
||||
@ -39,26 +45,51 @@ public abstract class TileRenderSurface extends TileRender implements OpaqueSurf
|
||||
this.texture = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture(BlockFace face) {
|
||||
public TileRenderSurface(String id) {
|
||||
this(id, null);
|
||||
}
|
||||
|
||||
public Texture getTexture(BlockFace blockFace) {
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable(BlockFace face) {
|
||||
ShapeRenderProgram program = WorldRenderProgram.getDefault();
|
||||
public Vec4 getColorMultiplier(BlockFace blockFace) {
|
||||
return Colors.WHITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void getFaces(
|
||||
ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
|
||||
boolean inner,
|
||||
Consumer<Face> output,
|
||||
Vec3 offset
|
||||
) {
|
||||
output.accept(createFace(chunk, blockInChunk, blockFace, inner, offset));
|
||||
}
|
||||
|
||||
private Face createFace(
|
||||
ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
|
||||
boolean inner,
|
||||
Vec3 offset
|
||||
) {
|
||||
return Faces.createBlockFace(
|
||||
WorldRenderProgram.getDefault(),
|
||||
getTexture(blockFace),
|
||||
getColorMultiplier(blockFace),
|
||||
offset,
|
||||
blockFace,
|
||||
inner
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace) {
|
||||
return new Shape(
|
||||
Usage.STATIC,
|
||||
WorldRenderProgram.getDefault(),
|
||||
Faces.createBlockFace(
|
||||
program,
|
||||
getTexture(face),
|
||||
Colors.WHITE,
|
||||
new Vec3(0, 0, 0),
|
||||
face,
|
||||
false
|
||||
)
|
||||
|
||||
createFace(chunk, blockInChunk, blockFace, false, Vectors.ZERO_3),
|
||||
createFace(chunk, blockInChunk, blockFace, true, Vectors.ZERO_3)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -159,4 +159,8 @@ public class Coordinates {
|
||||
return output;
|
||||
}
|
||||
|
||||
public static boolean isOnChunkBorder(int blockInChunk) {
|
||||
return blockInChunk == 0 || blockInChunk == BLOCKS_PER_CHUNK - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,10 +27,12 @@ import glm.vec._3.i.Vec3i;
|
||||
public class BlockRelation {
|
||||
|
||||
private final Vec3i vector = new Vec3i();
|
||||
private final Vec3 floatVector = new Vec3();
|
||||
private final Vec3 normalized = new Vec3();
|
||||
|
||||
public BlockRelation(int x, int y, int z) {
|
||||
vector.set(x, y, z);
|
||||
floatVector.set(x, y, z);
|
||||
normalized.set(x, y, z).normalize();
|
||||
}
|
||||
|
||||
@ -42,6 +44,10 @@ public class BlockRelation {
|
||||
return vector;
|
||||
}
|
||||
|
||||
public Vec3 getFloatVector() {
|
||||
return floatVector;
|
||||
}
|
||||
|
||||
public Vec3 getNormalized() {
|
||||
return normalized;
|
||||
}
|
||||
|
@ -92,6 +92,72 @@ public interface GenericChunk<Self extends GenericChunk<Self, B, T, TS>, B exten
|
||||
return result;
|
||||
}
|
||||
|
||||
default boolean isSurfaceBiC(Vec3i blockInChunk) {
|
||||
int hits = 0;
|
||||
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.x)) hits++;
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.y)) hits++;
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.z)) hits++;
|
||||
|
||||
return hits >= 1;
|
||||
}
|
||||
|
||||
default boolean isSurfaceBiW(Vec3i blockInWorld) {
|
||||
Vec3i v = Vectors.grab3i();
|
||||
|
||||
v = Coordinates.getInWorld(getPosition(), Vectors.ZERO_3i, v);
|
||||
v = blockInWorld.sub(v, v);
|
||||
|
||||
boolean result = isSurfaceBiC(v);
|
||||
|
||||
Vectors.release(v);
|
||||
return result;
|
||||
}
|
||||
|
||||
default boolean isEdgeBiC(Vec3i blockInChunk) {
|
||||
int hits = 0;
|
||||
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.x)) hits++;
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.y)) hits++;
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.z)) hits++;
|
||||
|
||||
return hits >= 2;
|
||||
}
|
||||
|
||||
default boolean isEdgeBiW(Vec3i blockInWorld) {
|
||||
Vec3i v = Vectors.grab3i();
|
||||
|
||||
v = Coordinates.getInWorld(getPosition(), Vectors.ZERO_3i, v);
|
||||
v = blockInWorld.sub(v, v);
|
||||
|
||||
boolean result = isEdgeBiC(v);
|
||||
|
||||
Vectors.release(v);
|
||||
return result;
|
||||
}
|
||||
|
||||
default boolean isVertexBiC(Vec3i blockInChunk) {
|
||||
int hits = 0;
|
||||
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.x)) hits++;
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.y)) hits++;
|
||||
if (Coordinates.isOnChunkBorder(blockInChunk.z)) hits++;
|
||||
|
||||
return hits == 3;
|
||||
}
|
||||
|
||||
default boolean isVertexBiW(Vec3i blockInWorld) {
|
||||
Vec3i v = Vectors.grab3i();
|
||||
|
||||
v = Coordinates.getInWorld(getPosition(), Vectors.ZERO_3i, v);
|
||||
v = blockInWorld.sub(v, v);
|
||||
|
||||
boolean result = isVertexBiC(v);
|
||||
|
||||
Vectors.release(v);
|
||||
return result;
|
||||
}
|
||||
|
||||
default void forEachBiC(Consumer<? super Vec3i> action) {
|
||||
VectorUtil.iterateCuboid(
|
||||
0,
|
||||
|
@ -39,6 +39,8 @@ import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||
import ru.windcorp.progressia.client.graphics.world.Selection;
|
||||
import ru.windcorp.progressia.client.world.block.*;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface;
|
||||
import ru.windcorp.progressia.client.world.entity.*;
|
||||
import ru.windcorp.progressia.client.world.tile.*;
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
@ -420,6 +422,7 @@ public class TestContent {
|
||||
|
||||
private static void registerMisc() {
|
||||
ChunkIO.registerCodec(new TestChunkCodec());
|
||||
ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ public class TestEntityRenderHuman extends EntityRender {
|
||||
).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create()
|
||||
);
|
||||
|
||||
return new StaticModel(b);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -386,7 +386,7 @@ public class TestEntityRenderJavapony extends EntityRender {
|
||||
).setOrigin(16, -4, 8).setSize(4, 8, 4).create()
|
||||
);
|
||||
|
||||
return new StaticModel(b);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
private static Renderable createLeg(
|
||||
@ -427,7 +427,7 @@ public class TestEntityRenderJavapony extends EntityRender {
|
||||
).setOrigin(-8, -8, -32).setSize(16, 16, 32).create()
|
||||
);
|
||||
|
||||
return new StaticModel(b);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Reference in New Issue
Block a user