/* * 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.component.ComponentMap; import nova.core.component.ComponentProvider; import nova.core.component.misc.Collider; import nova.core.render.Color; import nova.core.render.RenderException; import nova.core.render.model.Face; import nova.core.render.model.MeshModel; import nova.core.render.model.Vertex; import nova.core.render.texture.Texture; import nova.core.util.Direction; import nova.core.util.shape.Cuboid; import java.util.Objects; import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; /** * A block rendering builder that generates a function that renders a block model. * * @author Calclavia */ public class BlockRenderPipeline extends RenderPipeline { @SuppressWarnings("rawtypes") public final ComponentProvider<? extends ComponentMap> componentProvider; /** * Called to get the texture of this block for a certain side. * side - The side of the block that the texture is for. * Returns - An optional of the texture. */ public Function<Direction, Optional<Texture>> texture = (dir) -> Optional.empty(); /** * Called to get a shape of this block to be rendered. * Defaults to collision box or to Cuboid.ONE, if there * is no Collider supplied. */ public Supplier<Cuboid> bounds; /** * Called when this block is to be rendered. * Direction - The direction to render * Returns - true if the side should render */ public Predicate<Direction> renderSide = (dir) -> true; /** * Gets the color of a specific face. This is called by the default block * renderer. * direction - The side of the block. * Returns the color */ public Function<Direction, Color> colorMultiplier = (dir) -> Color.white; @SuppressWarnings({"rawtypes", "unchecked"}) public BlockRenderPipeline(ComponentProvider componentProvider) { this.componentProvider = componentProvider; bounds = () -> this.componentProvider.components.getOp(Collider.class).map(c -> c.boundingBox.get()).orElse(Cuboid.ONE); consumer = model -> model.addChild(draw(new MeshModel())); } /** * This method is called to specify a texture to use for the block. * * @param texture A function that takes a {@link nova.core.util.Direction} * and returns the {@link nova.core.render.texture.Texture} for that side. * @return this */ public BlockRenderPipeline withTexture(Function<Direction, Optional<Texture>> texture) { this.texture = texture; return this; } /** * This method is called to specify a texture to use for the block. * * @param texture The {@link nova.core.render.texture.Texture} for all sides. * @return this */ public BlockRenderPipeline withTexture(Texture texture) { Objects.requireNonNull(texture, "Texture is null, please initiate the texture before the block"); this.texture = (dir) -> Optional.of(texture); return this; } /** * This method is called to specify the size of the block, defaults to the size * of the block's {@link nova.core.util.shape.Cuboid} or a full 1×1×1 cube. * * @param bounds A supplier that returns * the {@link nova.core.util.shape.Cuboid} which specifies the block size. * @return this */ public BlockRenderPipeline withBounds(Supplier<Cuboid> bounds) { this.bounds = bounds; return this; } /** * This method is called to specify the size of the block, defaults to the size * of the block's {@link nova.core.util.shape.Cuboid} or a full 1×1×1 cube. * * @param bounds The {@link nova.core.util.shape.Cuboid} which specifies the block size. * @return this */ public BlockRenderPipeline withBounds(Cuboid bounds) { this.bounds = () -> bounds; return this; } /** * This method is called to specify which sides should and shouldn't render. * * @param renderSide A predicate that takes a {@link nova.core.util.Direction} * and returns a boolean specifying whether or not the side should render. * @return */ public BlockRenderPipeline filter(Predicate<Direction> renderSide) { this.renderSide = renderSide; return this; } /** * This method is called to specify a color multiplier to use for the block. * * @param colorMultiplier A function that takes a {@link nova.core.util.Direction} * and returns the {@link nova.core.render.Color} multiplier for that side. * @return this */ public BlockRenderPipeline withColor(Color colorMultiplier) { this.colorMultiplier = dir -> colorMultiplier; return this; } /** * This method is called to specify a color multiplier to use for the block. * * @param colorMultiplier The {@link nova.core.render.Color} multiplier for all sides. * @return this */ public BlockRenderPipeline withColor(Function<Direction, Color> colorMultiplier) { this.colorMultiplier = colorMultiplier; return this; } /** * Draws a standard block. * * @param model VertexModel to use * @return This VertexModel */ public MeshModel draw(MeshModel model) { Cuboid boundingBox = bounds.get(); double minX = boundingBox.min.getX() - 0.5; double minY = boundingBox.min.getY() - 0.5; double minZ = boundingBox.min.getZ() - 0.5; double maxX = boundingBox.max.getX() - 0.5; double maxY = boundingBox.max.getY() - 0.5; double maxZ = boundingBox.max.getZ() - 0.5; if (renderSide.test(Direction.DOWN)) { Color downColor = colorMultiplier.apply(Direction.DOWN); Face face = drawDown(model, minX, minY, minZ, maxX, maxY, maxZ, StaticCubeTextureCoordinates.instance); face.texture = texture.apply(Direction.DOWN); face.vertices.forEach(v -> v.color = downColor); } if (renderSide.test(Direction.UP)) { Color upColor = colorMultiplier.apply(Direction.UP); Face face = drawUp(model, minX, minY, minZ, maxX, maxY, maxZ, StaticCubeTextureCoordinates.instance); face.texture = texture.apply(Direction.UP); face.vertices.forEach(v -> v.color = upColor); } if (renderSide.test(Direction.NORTH)) { Color northColor = colorMultiplier.apply(Direction.NORTH); Face face = drawNorth(model, minX, minY, minZ, maxX, maxY, maxZ, StaticCubeTextureCoordinates.instance); face.texture = texture.apply(Direction.NORTH); face.vertices.forEach(v -> v.color = northColor); } if (renderSide.test(Direction.SOUTH)) { Color southColor = colorMultiplier.apply(Direction.SOUTH); Face face = drawSouth(model, minX, minY, minZ, maxX, maxY, maxZ, StaticCubeTextureCoordinates.instance); face.texture = texture.apply(Direction.SOUTH); face.vertices.forEach(v -> v.color = southColor); } if (renderSide.test(Direction.WEST)) { Color westColor = colorMultiplier.apply(Direction.WEST); Face face = drawWest(model, minX, minY, minZ, maxX, maxY, maxZ, StaticCubeTextureCoordinates.instance); face.texture = texture.apply(Direction.WEST); face.vertices.forEach(v -> v.color = westColor); } if (renderSide.test(Direction.EAST)) { Color eastColor = colorMultiplier.apply(Direction.EAST); Face face = drawEast(model, minX, minY, minZ, maxX, maxY, maxZ, StaticCubeTextureCoordinates.instance); face.texture = texture.apply(Direction.EAST); face.vertices.forEach(v -> v.color = eastColor); } return model; } /** * Creates the botom face of the model * * @param model The model to render * @param minX Min X coord * @param minY Min Y coord * @param minZ Min Z coord * @param maxX Max X coord * @param maxY Max Y coord * @param maxZ Max Z coord * @param textureCoordinates Texture coordinates to render * @return The bottom face of the model */ public static Face drawDown( MeshModel model, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CubeTextureCoordinates textureCoordinates) { Face down = new Face(); down.normal = Direction.DOWN.toVector(); //Top-left corner down.drawVertex(new Vertex(maxX, minY, maxZ, textureCoordinates.getBottomMinU(), textureCoordinates.getBottomMinV())); //Top-right corner down.drawVertex(new Vertex(minX, minY, maxZ, textureCoordinates.getBottomMaxU(), textureCoordinates.getBottomMinV())); //Bottom-right corner down.drawVertex(new Vertex(minX, minY, minZ, textureCoordinates.getBottomMaxU(), textureCoordinates.getBottomMaxV())); //Bottom-left corner down.drawVertex(new Vertex(maxX, minY, minZ, textureCoordinates.getBottomMinU(), textureCoordinates.getBottomMaxV())); model.drawFace(down); return down; } /** * Creates the top face of the model * * @param model The model to render * @param minX Min X coord * @param minY Min Y coord * @param minZ Min Z coord * @param maxX Max X coord * @param maxY Max Y coord * @param maxZ Max Z coord * @param textureCoordinates Texture coordinates to render * @return The top face of the model */ public static Face drawUp( MeshModel model, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CubeTextureCoordinates textureCoordinates) { Face up = new Face(); up.normal = Direction.UP.toVector(); //Bottom-left corner up.drawVertex(new Vertex(maxX, maxY, minZ, textureCoordinates.getTopMinU(), textureCoordinates.getTopMaxV())); //Bottom-right corner up.drawVertex(new Vertex(minX, maxY, minZ, textureCoordinates.getTopMaxU(), textureCoordinates.getTopMaxV())); //Top-right corner up.drawVertex(new Vertex(minX, maxY, maxZ, textureCoordinates.getTopMaxU(), textureCoordinates.getTopMinV())); //Top-left corner up.drawVertex(new Vertex(maxX, maxY, maxZ, textureCoordinates.getTopMinU(), textureCoordinates.getTopMinV())); model.drawFace(up); return up; } /** * Creates the north face of the model * * @param model The model to render * @param minX Min X coord * @param minY Min Y coord * @param minZ Min Z coord * @param maxX Max X coord * @param maxY Max Y coord * @param maxZ Max Z coord * @param textureCoordinates Texture coordinates to render * @return The north face of the model */ public static Face drawNorth( MeshModel model, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CubeTextureCoordinates textureCoordinates) { Face north = new Face(); north.normal = Direction.NORTH.toVector(); //Top-left corner north.drawVertex(new Vertex(minX, maxY, minZ, textureCoordinates.getNorthMinU(), textureCoordinates.getNorthMinV())); //Top-right corner north.drawVertex(new Vertex(maxX, maxY, minZ, textureCoordinates.getNorthMaxU(), textureCoordinates.getNorthMinV())); //Bottom-right corner north.drawVertex(new Vertex(maxX, minY, minZ, textureCoordinates.getNorthMaxU(), textureCoordinates.getNorthMaxV())); //Bottom-left corner north.drawVertex(new Vertex(minX, minY, minZ, textureCoordinates.getNorthMinU(), textureCoordinates.getNorthMaxV())); model.drawFace(north); return north; } /** * Creates the south face of the model * * @param model The model to render * @param minX Min X coord * @param minY Min Y coord * @param minZ Min Z coord * @param maxX Max X coord * @param maxY Max Y coord * @param maxZ Max Z coord * @param textureCoordinates Texture coordinates to render * @return The south face of the model */ public static Face drawSouth( MeshModel model, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CubeTextureCoordinates textureCoordinates) { Face south = new Face(); south.normal = Direction.SOUTH.toVector(); //Bottom-left corner south.drawVertex(new Vertex(minX, minY, maxZ, textureCoordinates.getSouthMinU(), textureCoordinates.getSouthMaxV())); //Bottom-right corner south.drawVertex(new Vertex(maxX, minY, maxZ, textureCoordinates.getSouthMaxU(), textureCoordinates.getSouthMaxV())); //Top-right corner south.drawVertex(new Vertex(maxX, maxY, maxZ, textureCoordinates.getSouthMaxU(), textureCoordinates.getSouthMinV())); //Top-left corner south.drawVertex(new Vertex(minX, maxY, maxZ, textureCoordinates.getSouthMinU(), textureCoordinates.getSouthMinV())); model.drawFace(south); return south; } /** * Creates the west face of the model * * @param model The model to render * @param minX Min X coord * @param minY Min Y coord * @param minZ Min Z coord * @param maxX Max X coord * @param maxY Max Y coord * @param maxZ Max Z coord * @param textureCoordinates Texture coordinates to render * @return The west face of the model */ public static Face drawWest( MeshModel model, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CubeTextureCoordinates textureCoordinates) { Face west = new Face(); west.normal = Direction.WEST.toVector(); //Bottom-left corner west.drawVertex(new Vertex(minX, minY, minZ, textureCoordinates.getWestMinU(), textureCoordinates.getWestMaxV())); //Bottom-right corner west.drawVertex(new Vertex(minX, minY, maxZ, textureCoordinates.getWestMaxU(), textureCoordinates.getWestMaxV())); //Top-right corner west.drawVertex(new Vertex(minX, maxY, maxZ, textureCoordinates.getWestMaxU(), textureCoordinates.getWestMinV())); //Top-left corner west.drawVertex(new Vertex(minX, maxY, minZ, textureCoordinates.getWestMinU(), textureCoordinates.getWestMinV())); model.drawFace(west); return west; } /** * Creates the east face of the model * * @param model The model to render * @param minX Min X coord * @param minY Min Y coord * @param minZ Min Z coord * @param maxX Max X coord * @param maxY Max Y coord * @param maxZ Max Z coord * @param textureCoordinates Texture coordinates to render * @return The east face of the model */ public static Face drawEast( MeshModel model, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CubeTextureCoordinates textureCoordinates) { Face east = new Face(); east.normal = Direction.EAST.toVector(); //Top-left corner east.drawVertex(new Vertex(maxX, maxY, minZ, textureCoordinates.getEastMinU(), textureCoordinates.getEastMinV())); //Top-right corner east.drawVertex(new Vertex(maxX, maxY, maxZ, textureCoordinates.getEastMaxU(), textureCoordinates.getEastMinV())); //Bottom-right corner east.drawVertex(new Vertex(maxX, minY, maxZ, textureCoordinates.getEastMaxU(), textureCoordinates.getEastMaxV())); //Bottom-left corner east.drawVertex(new Vertex(maxX, minY, minZ, textureCoordinates.getEastMinU(), textureCoordinates.getEastMaxV())); model.drawFace(east); return east; } /** * Creates a face of the model in a specified direction * * @param dir The direction of the face to make * @param model The model to use * @param minX Min X coord * @param minY Min Y coord * @param minZ Min Z coord * @param maxX Max X coord * @param maxY Max Y coord * @param maxZ Max Z coord * @param textureCoordinates Texture coordinates to render * @return The face of the model in that dirction */ public static Face drawDir(Direction dir, MeshModel model, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CubeTextureCoordinates textureCoordinates) { switch (dir) { case DOWN: return drawDown(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); case UP: return drawUp(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); case NORTH: return drawNorth(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); case SOUTH: return drawSouth(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); case EAST: return drawEast(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); case WEST: return drawWest(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); } throw new RenderException("Invalid draw direction!"); } /** * Applies the textures to the model * * @param model The model to use * @param minX Min X coord * @param minY Min Y coord * @param minZ Min Z coord * @param maxX Max X coord * @param maxY Max Y coord * @param maxZ Max Z coord * @param textureCoordinates Texture coordinates to render * @return The cube model with textures */ public static MeshModel drawCube( MeshModel model, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CubeTextureCoordinates textureCoordinates) { drawDown(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); drawUp(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); drawNorth(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); drawSouth(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); drawWest(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); drawEast(model, minX, minY, minZ, maxX, maxY, maxZ, textureCoordinates); return model; } /** * Binds the specified texture coordinates to the model for the specified cuboid for rendering * * @param model The model to apply the textures to * @param cuboid The cuboid where the model applies to * @param textureCoordinates The texture coordinates to use * @return The model with the textures applied */ public static MeshModel drawCube(MeshModel model, Cuboid cuboid, CubeTextureCoordinates textureCoordinates) { return drawCube(model, cuboid.min.getX(), cuboid.min.getY(), cuboid.min.getZ(), cuboid.max.getX(), cuboid.max.getY(), cuboid.max.getZ(), textureCoordinates); } public static MeshModel drawCube(MeshModel model) { return drawCube(model, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, StaticCubeTextureCoordinates.instance); } }