diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeParts.java b/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeParts.java index 4030249..807fcda 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeParts.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeParts.java @@ -153,6 +153,6 @@ public class ShapeParts { height, inner ); - } + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapePrototype.java b/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapePrototype.java new file mode 100644 index 0000000..f2cc631 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapePrototype.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +package ru.windcorp.progressia.client.graphics.model; + +import java.nio.ShortBuffer; +import java.util.Objects; + +import glm.mat._3.Mat3; +import glm.mat._4.Mat4; +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; +import glm.vec._4.Vec4; +import ru.windcorp.progressia.client.graphics.Colors; +import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram.VertexBuilder; +import ru.windcorp.progressia.client.graphics.texture.Texture; +import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; +import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram.WRPVertexBuilder; +import ru.windcorp.progressia.common.util.StashingStack; +import ru.windcorp.progressia.common.util.VectorUtil; + +public class ShapePrototype { + + private final ShapeRenderProgram program; + + private Texture texture; + + private final Vec3[] positions; + private final Vec4[] colorMultipliers; + private final Vec2[] textureCoords; + private final Vec3[] forcedNormals; + + private ShortBuffer indices; + private ShortBuffer flippedIndices = null; + + protected static final int TRANSFORM_STACK_SIZE = 64; + private final StashingStack transformStack = new StashingStack<>( + TRANSFORM_STACK_SIZE, + Mat4::new + ); + + public ShapePrototype( + ShapeRenderProgram program, + Texture texture, + Vec3[] positions, + Vec4[] colorMultipliers, + Vec2[] textureCoords, + Vec3[] forcedNormals, + ShortBuffer indices + ) { + this.program = Objects.requireNonNull(program, "program"); + this.texture = texture; + this.indices = Objects.requireNonNull(indices, "indices"); + + Objects.requireNonNull(positions, "positions"); + Objects.requireNonNull(colorMultipliers, "colorMultipliers"); + Objects.requireNonNull(textureCoords, "textureCoords"); + + if (forcedNormals != null && !(program instanceof WorldRenderProgram)) { + throw new IllegalArgumentException("Cannot force normals on non-WorldRenderPrograms cuz javahorse stupiddd"); + } + + if (forcedNormals == null) { + forcedNormals = new Vec3[positions.length]; + } + + this.positions = positions; + this.colorMultipliers = colorMultipliers; + this.textureCoords = textureCoords; + this.forcedNormals = forcedNormals; + + if (positions.length != colorMultipliers.length) { + throw new IllegalArgumentException("positions.length (" + positions.length + ") != colorMultipliers.length (" + colorMultipliers + ")"); + } + + if (positions.length != textureCoords.length) { + throw new IllegalArgumentException("positions.length (" + positions.length + ") != textureCoords.length (" + textureCoords + ")"); + } + + if (positions.length != forcedNormals.length) { + throw new IllegalArgumentException("positions.length (" + positions.length + ") != forcedNormals.length (" + forcedNormals + ")"); + } + + transformStack.push().identity(); + } + + public ShapePart build() { + VertexBuilder builder = program.getVertexBuilder(); + + Vec3 transformedPosition = new Vec3(); + + for (int i = 0; i < positions.length; ++i) { + + transformedPosition.set(positions[i]); + if (transformStack.getSize() > 1) { + VectorUtil.applyMat4(transformedPosition, transformStack.getHead()); + } + + if (forcedNormals[i] != null && builder instanceof WRPVertexBuilder) { + ((WRPVertexBuilder) builder).addVertex( + transformedPosition, colorMultipliers[i], textureCoords[i], forcedNormals[i] + ); + } else { + builder.addVertex(transformedPosition, colorMultipliers[i], textureCoords[i]); + } + } + + return new ShapePart(texture, builder.assemble(), indices); + } + + public ShapePrototype apply(Mat4 transform) { + for (Vec3 vector : positions) { + VectorUtil.applyMat4(vector, transform); + } + return this; + } + + public ShapePrototype apply(Mat3 transform) { + for (Vec3 vector : positions) { + transform.mul(vector); + } + return this; + } + + public Mat4 push() { + Mat4 previous = transformStack.getHead(); + return transformStack.push().set(previous); + } + + public ShapePrototype pop() { + transformStack.pop(); + return this; + } + + public ShapePrototype setTexture(Texture texture) { + this.texture = texture; + return this; + } + + public ShapePrototype deleteTexture() { + this.texture = null; + return this; + } + + public ShapePrototype resetColorMultiplier() { + for (Vec4 color : colorMultipliers) { + color.set(Colors.WHITE); + } + return this; + } + + public ShapePrototype addColorMultiplier(Vec4 color) { + for (Vec4 c : colorMultipliers) { + c.mul(color); + } + return this; + } + + public ShapePrototype flip() { + ShortBuffer tmp = indices; + indices = flippedIndices; + flippedIndices = tmp; + + if (indices == null) { + int length = flippedIndices.limit(); + indices = ShortBuffer.allocate(length); + for (int i = 0; i < length; ++i) { + indices.put(i, flippedIndices.get(length - i - 1)); + } + } + + return this; + } + + public ShapePrototype makeDoubleSided() { + int length = indices.limit(); + ShortBuffer newIndices = ShortBuffer.allocate(length * 2); + + for (int i = 0; i < length; ++i) { + newIndices.put(i, indices.get(i)); + } + for (int i = 0; i < length; ++i) { + newIndices.put(i + length, indices.get(length - i - 1)); + } + + indices = flippedIndices = newIndices; + + return this; + } + + public static ShapePrototype unitSquare(Texture texture, Vec4 color, ShapeRenderProgram program) { + return new ShapePrototype( + program, + texture, + new Vec3[] { + new Vec3(0, 0, 0), + new Vec3(0, 1, 0), + new Vec3(1, 0, 0), + new Vec3(1, 1, 0) + }, + new Vec4[] { + new Vec4(color), + new Vec4(color), + new Vec4(color), + new Vec4(color) + }, + new Vec2[] { + new Vec2(0, 0), + new Vec2(0, 1), + new Vec2(1, 0), + new Vec2(1, 1) + }, + null, + ShortBuffer.wrap(new short[] {3, 1, 0, 2, 3, 0}) + ); + } + + public static ShapePrototype unitSquare(Texture texture, Vec4 color) { + return unitSquare(texture, color, WorldRenderProgram.getDefault()); + } + + public static ShapePrototype unitSquare(Texture texture) { + return unitSquare(texture, Colors.WHITE, WorldRenderProgram.getDefault()); + } + + public static ShapePrototype unitSquare(Vec4 color) { + return unitSquare(null, color, WorldRenderProgram.getDefault()); + } + + public static ShapePrototype unitSquare() { + return unitSquare(null, Colors.WHITE, WorldRenderProgram.getDefault()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index e027cef..c2444d2 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -65,6 +65,8 @@ import ru.windcorp.progressia.server.world.tile.*; import ru.windcorp.progressia.test.Flowers.FlowerVariant; import ru.windcorp.progressia.test.Rocks.RockType; import ru.windcorp.progressia.test.gen.TestGravityModel; +import ru.windcorp.progressia.test.trees.BlockRenderLeavesHazel; +import ru.windcorp.progressia.test.trees.BlockRenderLeavesPine; public class TestContent { @@ -131,7 +133,7 @@ public class TestContent { register(new BlockLogic("Test:Log")); register(new BlockData("Test:TemporaryLeaves")); - register(new BlockRenderTransparentCube("Test:TemporaryLeaves", getBlockTexture("TemporaryLeaves"))); + register(new BlockRenderLeavesHazel("Test:TemporaryLeaves", getBlockTexture("LeavesHazel"))); // Sic, using Glass logic for leaves because Test register(new TestBlockLogicGlass("Test:TemporaryLeaves")); @@ -142,6 +144,11 @@ public class TestContent { register(new BlockData("Test:Tux")); register(new TestBlockRenderTux("Test:Tux")); register(new BlockLogic("Test:Tux")); + + register(new BlockData("Test:LeavesPine")); + register(new BlockRenderLeavesPine("Test:LeavesPine", getBlockTexture("LeavesPine"))); + // Sic, using Glass logic for leaves because Test + register(new TestBlockLogicGlass("Test:LeavesPine")); BlockDataRegistry.getInstance().values().forEach(PLACEABLE_BLOCKS::add); PLACEABLE_BLOCKS.removeIf(b -> placeableBlacklist.contains(b.getId())); diff --git a/src/main/java/ru/windcorp/progressia/test/gen/feature/TestBushFeature.java b/src/main/java/ru/windcorp/progressia/test/gen/feature/TestBushFeature.java index b559582..84328bd 100644 --- a/src/main/java/ru/windcorp/progressia/test/gen/feature/TestBushFeature.java +++ b/src/main/java/ru/windcorp/progressia/test/gen/feature/TestBushFeature.java @@ -38,10 +38,12 @@ public class TestBushFeature extends MultiblockVegetationFeature { Vec3i center = context.getLocation().add_(0, 0, 1); - context.setBlock(center, trunk); - context.setBlock(center.add_(0, 0, 1), leaves); + if (size > 0.8) { + context.setBlock(center, trunk); + context.setBlock(center.add_(0, 0, 1), leaves); + } - iterateBlob(center, stretch(size, 1.3, 2.5), stretch(size, 0.6, 1.5), 0.7, 2, p -> { + iterateBlob(center, stretch(size, 0.5, 2.5), stretch(size, 0.6, 1.5), 0.7, 2, p -> { setLeaves(context, p, leaves); }); } diff --git a/src/main/java/ru/windcorp/progressia/test/gen/feature/TestTreeFeature.java b/src/main/java/ru/windcorp/progressia/test/gen/feature/TestTreeFeature.java index 611ec07..72becc1 100644 --- a/src/main/java/ru/windcorp/progressia/test/gen/feature/TestTreeFeature.java +++ b/src/main/java/ru/windcorp/progressia/test/gen/feature/TestTreeFeature.java @@ -24,33 +24,44 @@ import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField; import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext; public class TestTreeFeature extends MultiblockVegetationFeature { - + private final BlockData trunk = BlockDataRegistry.getInstance().get("Test:Log"); - private final BlockData leaves = BlockDataRegistry.getInstance().get("Test:TemporaryLeaves"); + private final BlockData leaves = BlockDataRegistry.getInstance().get("Test:LeavesPine"); public TestTreeFeature(String id, SurfaceFloatField selector) { - super(id, selector, 10 * 10); + super(id, selector, 7 * 7); } - + @Override protected void grow(SurfaceBlockContext context, double selectorValue) { - + Vec3i start = context.getLocation().add_(0, 0, 1); Vec3i center = start.add_(0); double size = selectorValue * randomDouble(context, 0.8, 1.2); - - int height = (int) stretch(size, 3, 7); + double volume = stretch(size, 2.5, 1); + + int height = (int) stretch(size, 8, 12); for (; center.z < start.z + height; ++center.z) { context.setBlock(center, trunk); } - - double branchHorDistance = 0; + --center.z; + + iterateBlob(center, 1.5 * volume, 1.75, 0.5, 3, p -> { + setLeaves(context, p, leaves); + }); + + int branchCount = 1 + context.getRandom().nextInt(2) + (int) (stretch(size, 0, 4)); + + for (int i = 0; i < branchCount; ++i) { - do { - double branchSize = 0.5 + randomDouble(context, 1, 2) * size; double branchHorAngle = randomDouble(context, 0, 2 * Math.PI); - int branchVertOffset = (int) randomDouble(context, -2, 0); + double branchHorDistance = randomDouble(context, 0.7, 1.5) * volume; + + int branchVertOffset = (int) randomDouble(context, -height / 3.0, -height / 1.5); + + double branchSize = randomDouble(context, 1, 2) * volume; + double branchHeight = 1.5; Vec3i branchCenter = center.add_( (int) (Math.sin(branchHorAngle) * branchHorDistance), @@ -58,12 +69,12 @@ public class TestTreeFeature extends MultiblockVegetationFeature { branchVertOffset ); - iterateBlob(branchCenter, 1 * branchSize, 2.3 * branchSize, 0.5, 3, p -> { + iterateBlob(branchCenter, branchSize, branchHeight, 0.5, 3, p -> { setLeaves(context, p, leaves); }); - branchHorDistance = randomDouble(context, 0.7, 1.5); - } while (context.getRandom().nextInt(8) > 1); + } + } } diff --git a/src/main/java/ru/windcorp/progressia/test/trees/BlockRenderLeavesHazel.java b/src/main/java/ru/windcorp/progressia/test/trees/BlockRenderLeavesHazel.java new file mode 100644 index 0000000..9626b16 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/trees/BlockRenderLeavesHazel.java @@ -0,0 +1,99 @@ +/* + * 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 . + */ +package ru.windcorp.progressia.test.trees; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Random; +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.graphics.backend.Usage; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.Shape; +import ru.windcorp.progressia.client.graphics.model.ShapePart; +import ru.windcorp.progressia.client.graphics.model.ShapePrototype; +import ru.windcorp.progressia.client.graphics.texture.Texture; +import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; +import ru.windcorp.progressia.client.world.block.BlockRender; +import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSimple.BlockOptimizedSimple; +import ru.windcorp.progressia.common.world.DefaultChunkData; + +public class BlockRenderLeavesHazel extends BlockRender implements BlockOptimizedSimple { + + private final Texture texture; + + public BlockRenderLeavesHazel(String id, Texture texture) { + super(id); + this.texture = texture; + } + + @Override + public void getShapeParts(DefaultChunkData chunk, Vec3i bic, Consumer output) { + ShapePrototype sp = ShapePrototype.unitSquare(texture).makeDoubleSided(); + sp.push().translate(bic.x, bic.y, bic.z); + + Random random = new Random(chunk.getX() ^ bic.x); + random.setSeed(random.nextLong() ^ chunk.getY() ^ bic.y); + random.setSeed(random.nextLong() ^ chunk.getZ() ^ bic.z); + + final float angle = (float) (Math.PI / 4); + + sp.push().translate(0, 0, random.nextFloat() * 0.5f) + .rotateZ(random.nextFloat() * Math.PI) + .rotateX(-0.3f + random.nextFloat() * 0.6) + .scale(1.3f); + + sp.push().translate(0, 0, -0.5f).rotateX(angle).translate(-0.5f, -0.5f, 0); + output.accept(sp.build()); + sp.pop(); + + sp.push().translate(0, 0, -0.5f).rotateX(-angle).translate(-0.5f, -0.5f, 0); + output.accept(sp.build()); + sp.pop(); + + sp.push().translate(0, 0, -0.5f).rotateY(angle).translate(-0.5f, -0.5f, 0); + output.accept(sp.build()); + sp.pop(); + + sp.push().translate(0, 0, -0.5f).rotateY(-angle).translate(-0.5f, -0.5f, 0); + output.accept(sp.build()); + sp.pop(); + + sp.pop(); + } + + @Override + public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk) { + Collection parts = new ArrayList<>(6); + + getShapeParts(chunk, blockInChunk, parts::add); + + return new Shape( + Usage.STATIC, + WorldRenderProgram.getDefault(), + parts.toArray(new ShapePart[parts.size()]) + ); + } + + @Override + public boolean needsOwnRenderable() { + return false; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/trees/BlockRenderLeavesPine.java b/src/main/java/ru/windcorp/progressia/test/trees/BlockRenderLeavesPine.java new file mode 100644 index 0000000..32f98b5 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/trees/BlockRenderLeavesPine.java @@ -0,0 +1,106 @@ +/* + * 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 . + */ +package ru.windcorp.progressia.test.trees; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Random; +import java.util.function.Consumer; + +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.Renderable; +import ru.windcorp.progressia.client.graphics.model.Shape; +import ru.windcorp.progressia.client.graphics.model.ShapePart; +import ru.windcorp.progressia.client.graphics.model.ShapePrototype; +import ru.windcorp.progressia.client.graphics.texture.Texture; +import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; +import ru.windcorp.progressia.client.world.block.BlockRender; +import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSimple.BlockOptimizedSimple; +import ru.windcorp.progressia.common.world.DefaultChunkData; + +public class BlockRenderLeavesPine extends BlockRender implements BlockOptimizedSimple { + + private final Texture texture; + + public BlockRenderLeavesPine(String id, Texture texture) { + super(id); + this.texture = texture; + } + + @Override + public void getShapeParts(DefaultChunkData chunk, Vec3i bic, Consumer output) { + Random random = new Random(chunk.getX() ^ bic.x); + random.setSeed(random.nextLong() ^ chunk.getY() ^ bic.y); + random.setSeed(random.nextLong() ^ chunk.getZ() ^ bic.z); + + ShapePrototype sp = ShapePrototype.unitSquare(texture).makeDoubleSided(); + sp.push().translate( + bic.x + random.nextFloat() * 0.3f - 0.15f, + bic.y + random.nextFloat() * 0.3f - 0.15f, + bic.z + ); + + final float angle = 0.5f; + + for (float offset : new float[] { -0.25f, -0.75f }) { + sp.push().translate(0, 0, offset + random.nextFloat() * 0.5f) + .rotateZ(-0.3f + random.nextFloat() * 0.6) + .rotateX(-0.3f + random.nextFloat() * 0.6); + + sp.push().translate(0, 0, -0.3f).rotateX(angle).translate(-0.5f, -0.5f, 0.5f); + output.accept(sp.build()); + sp.pop(); + + sp.push().translate(0, 0, -0.3f).rotateX(-angle).translate(-0.5f, -0.5f, 0.5f); + output.accept(sp.build()); + sp.pop(); + + sp.push().translate(0, 0, -0.3f).rotateY(angle).translate(-0.5f, -0.5f, 0.5f); + output.accept(sp.build()); + sp.pop(); + + sp.push().translate(0, 0, -0.3f).rotateY(-angle).translate(-0.5f, -0.5f, 0.5f); + output.accept(sp.build()); + sp.pop(); + + sp.pop(); + sp.addColorMultiplier(Colors.GRAY_A); + } + } + + @Override + public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk) { + Collection parts = new ArrayList<>(6); + + getShapeParts(chunk, blockInChunk, parts::add); + + return new Shape( + Usage.STATIC, + WorldRenderProgram.getDefault(), + parts.toArray(new ShapePart[parts.size()]) + ); + } + + @Override + public boolean needsOwnRenderable() { + return false; + } + +} diff --git a/src/main/resources/assets/textures/blocks/LeavesHazel.png b/src/main/resources/assets/textures/blocks/LeavesHazel.png new file mode 100644 index 0000000..ec12d2d Binary files /dev/null and b/src/main/resources/assets/textures/blocks/LeavesHazel.png differ diff --git a/src/main/resources/assets/textures/blocks/LeavesPine.png b/src/main/resources/assets/textures/blocks/LeavesPine.png new file mode 100644 index 0000000..f01e127 Binary files /dev/null and b/src/main/resources/assets/textures/blocks/LeavesPine.png differ diff --git a/src/main/resources/assets/textures/blocks/LogSide.png b/src/main/resources/assets/textures/blocks/LogSide.png index c4d7ca1..0f9506f 100644 Binary files a/src/main/resources/assets/textures/blocks/LogSide.png and b/src/main/resources/assets/textures/blocks/LogSide.png differ diff --git a/src/main/resources/assets/textures/blocks/LogTop.png b/src/main/resources/assets/textures/blocks/LogTop.png index fb61de5..99adc89 100644 Binary files a/src/main/resources/assets/textures/blocks/LogTop.png and b/src/main/resources/assets/textures/blocks/LogTop.png differ