/*
* Copyright (c) 2015 NOVA, All rights reserved.
* This library is free software, licensed under GNU Lesser General Public License version 3
*
* This file is part of NOVA.
*
* NOVA 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.
*
* NOVA 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 NOVA. If not, see <http://www.gnu.org/licenses/>.
*/
package nova.core.render.pipeline;
import nova.core.block.Block;
import nova.core.component.transform.BlockTransform;
import nova.core.render.model.Face;
import nova.core.render.model.MeshModel;
import nova.core.render.model.Model;
import nova.core.render.texture.Texture;
import nova.core.util.Direction;
import nova.core.util.math.RotationUtil;
import nova.core.util.shape.Cuboid;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
// TODO: create a CTM pipeline builder at some point, to automate construction of CTM pipelines
// (there are 47 different CTM textures for a block which connects omni-directionally, writing that manually would get annoying really fast)
public class ConnectedTextureRenderPipeline extends BlockRenderPipeline {
public final Block block;
public final Texture edgeTexture;
/**
* The mask the represents which sides the block should render its connected texture.
* E.g: 00000000 will render all directions
*/
public Supplier<Integer> connectMask;
/**
* A filter of which sides the connected texture renderer should render.
*/
public Predicate<Direction> faceFilter = dir -> true;
public ConnectedTextureRenderPipeline(Block block, Texture edgeTexture) {
super(block);
this.block = block;
this.edgeTexture = edgeTexture;
connectMask = () -> {
if (this.block.components.has(BlockTransform.class)) {
return Arrays.stream(Direction.VALID_DIRECTIONS)
.filter(d -> {
Optional<Block> b = this.block.world().getBlock(this.block.position().add(d.toVector()));
return b.isPresent() && b.get().sameType(this.block);
})
.map(d -> 1 << d.ordinal())
.reduce(0, (b, a) -> a | b);
}
return 0x0;
};
consumer = model -> {
//Render the block face
MeshModel vModel = new MeshModel();
draw(vModel);
model.addChild(vModel);
//Render the block edge
for (Direction dir : Direction.VALID_DIRECTIONS)
if (faceFilter.test(dir)) {
renderFace(dir, model);
}
};
}
public ConnectedTextureRenderPipeline withConnectMask(Supplier<Integer> connectMask) {
this.connectMask = connectMask;
return this;
}
/**
* Set the filter used to determine if this pipeline should handle these sides.
*
* @param faceFilter A filter of which sides the connected texture renderer should render.
* @return this
*/
public ConnectedTextureRenderPipeline withFaceFilter(Predicate<Direction> faceFilter) {
this.faceFilter = faceFilter;
return this;
}
/**
* Set the filter used to determine if this pipeline should handle these sides.
*
* @param faces An array of which sides the connected texture renderer should render.
* @return this
*/
public ConnectedTextureRenderPipeline withFaces(Direction... faces) {
this.faceFilter = dir -> Arrays.stream(faces).anyMatch(d -> d == dir);
return this;
}
/**
* Set the filter used to determine if this pipeline should handle these sides.
*
* @param faceMask A mask of which sides the connected texture renderer should render.
* Each bit corresponds to a direction.
* E.g: 000011 will render top and bottom
* @return this
*/
public ConnectedTextureRenderPipeline withFaceMask(int faceMask) {
this.faceFilter = dir -> (faceMask & (1 << dir.ordinal())) != 0;
return this;
}
/**
* Apply connected texture on top face.
*
* @param direction the direction.
* @param model the model.
*/
protected void renderFace(Direction direction, Model model) {
for (int r = 0; r < 4; r++) {
Cuboid bound = bounds.get()
.subtract(0.5) //Correct translation
.add(direction.toVector().scalarMultiply(r * 0.0001d)); //Lift up texture slightly, preventing z-fighting
Direction absDir = Direction.fromOrdinal(RotationUtil.rotateSide(direction.opposite().ordinal(), r));
int mask = connectMask.get();
if ((mask & (1 << absDir.ordinal())) == 0) {
MeshModel innerModel = new MeshModel();
innerModel.matrix.rotate(direction.toVector(), Math.PI / 2 * r);
Face face = drawDir(direction, innerModel, bound.min.getX(), bound.min.getY(), bound.min.getZ(), bound.max.getX(), bound.max.getY(), bound.max.getZ(), StaticCubeTextureCoordinates.instance);
face.texture = Optional.of(edgeTexture);
//TODO: Support colors
model.children.add(innerModel);
}
}
}
}