Added grasses and flowers

- Added CROSimple to optimize simple non-Surface textures
- Added TileRenderCross
- Added Low, Medium and Tall grass
- Added Blue, Purple and White flat flowers
- Added Bushes
- Added tiny Dandelions and tiny Lavanders
- Improved grass and log textures
This commit is contained in:
OLEGSHA 2021-08-22 22:18:42 +03:00
parent 9fc1a21191
commit d01ef3654f
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
27 changed files with 649 additions and 53 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.cro;
import java.util.ArrayList;
import java.util.Collection;
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.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.block.BlockRender;
import ru.windcorp.progressia.client.world.tile.TileRender;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class ChunkRenderOptimizerSimple extends ChunkRenderOptimizer {
public interface BlockOptimizedSimple {
void getShapeParts(
DefaultChunkData chunk,
Vec3i relBlockInChunk,
Consumer<ShapePart> output
);
}
public interface TileOptimizedCustom {
void getShapeParts(
DefaultChunkData chunk,
Vec3i relBlockInChunk,
RelFace blockFace,
Consumer<ShapePart> output
);
}
private final Collection<ShapePart> parts = new ArrayList<>();
private final Consumer<ShapePart> partAdder = parts::add;
public ChunkRenderOptimizerSimple(String id) {
super(id);
}
@Override
public void startRender() {
parts.clear();
}
@Override
public void addBlock(BlockRender block, Vec3i relBlockInChunk) {
if (block instanceof BlockOptimizedSimple) {
((BlockOptimizedSimple) block).getShapeParts(chunk.getData(), relBlockInChunk, partAdder);
}
}
@Override
public void addTile(TileRender tile, Vec3i relBlockInChunk, RelFace blockFace) {
if (tile instanceof TileOptimizedCustom) {
((TileOptimizedCustom) tile).getShapeParts(chunk.getData(), relBlockInChunk, blockFace, partAdder);
}
}
@Override
public Renderable endRender() {
if (parts.isEmpty()) {
return null;
}
return new Shape(
Usage.STATIC,
WorldRenderProgram.getDefault(),
parts.toArray(new ShapePart[parts.size()])
);
}
}

View File

@ -0,0 +1,170 @@
/*
* 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.tile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
import glm.mat._3.Mat3;
import glm.mat._4.Mat4;
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.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapePart;
import ru.windcorp.progressia.client.graphics.model.ShapeParts;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSimple.TileOptimizedCustom;
import ru.windcorp.progressia.common.util.Matrices;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.rels.AxisRotations;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class TileRenderCross extends TileRender implements TileOptimizedCustom {
private static final float SQRT_2_OVER_2 = (float) Math.sqrt(2) / 2;
private static final float[] ONE_AND_NEGATIVE_ONE = new float[] { 1, -1 };
private final Texture texture;
private final float width;
public TileRenderCross(String id, Texture texture, boolean allowStretching) {
super(id);
this.texture = texture;
this.width = allowStretching ? 1 : SQRT_2_OVER_2;
}
public Texture getTexture(RelFace blockFace) {
return texture;
}
public Vec4 getColorMultiplier(RelFace blockFace) {
return Colors.WHITE;
}
@Override
public void getShapeParts(
DefaultChunkData chunk,
Vec3i bic,
RelFace blockFace,
Consumer<ShapePart> output
) {
Mat4 transform = Matrices.grab4();
Vec3 origin = Vectors.grab3();
Vec3 width = Vectors.grab3();
Vec3 height = Vectors.grab3();
Mat3 resolutionMatrix = AxisRotations.getResolutionMatrix3(blockFace.resolve(AbsFace.POS_Z));
Vec4 color = getColorMultiplier(blockFace);
Texture texture = getTexture(blockFace);
float originOffset = (1 - this.width) / 2;
WorldRenderProgram program = WorldRenderProgram.getDefault();
for (int i = 0; getTransform(chunk, bic, blockFace, i, transform); i++) {
for (float flip : ONE_AND_NEGATIVE_ONE) {
origin.set(flip * (originOffset - 0.5f), originOffset - 0.5f, 0);
width.set(flip * this.width, this.width, 0);
height.set(0, 0, 1);
VectorUtil.applyMat4(origin, transform);
VectorUtil.rotateOnly(width, transform);
VectorUtil.rotateOnly(height, transform);
origin.z += 1 - 0.5f;
if (blockFace != RelFace.UP) {
resolutionMatrix.mul(origin);
resolutionMatrix.mul(width);
resolutionMatrix.mul(height);
}
origin.add(bic.x, bic.y, bic.z);
output.accept(
ShapeParts.createRectangle(
program,
texture,
color,
origin,
width,
height,
false
)
);
output.accept(
ShapeParts.createRectangle(
program,
texture,
color,
origin,
width,
height,
true
)
);
}
}
Matrices.release(transform);
Vectors.release(origin);
Vectors.release(width);
Vectors.release(height);
}
protected boolean getTransform(
DefaultChunkData chunk,
Vec3i relBlockInChunk,
RelFace blockFace,
int count,
Mat4 output
) {
output.identity();
return count == 0;
}
@Override
public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk, RelFace blockFace) {
Collection<ShapePart> parts = new ArrayList<>(4);
getShapeParts(chunk, blockInChunk, blockFace, parts::add);
return new Shape(
Usage.STATIC,
WorldRenderProgram.getDefault(),
parts.toArray(new ShapePart[parts.size()])
);
}
@Override
public boolean needsOwnRenderable() {
return false;
}
}

View File

@ -180,6 +180,25 @@ public class VectorUtil {
return applyMat4(inOut, mat, inOut);
}
public static Vec3 rotateOnly(Vec3 in, Mat4 mat, Vec3 out) {
if (out == null) {
out = new Vec3();
}
Mat3 mat3 = Matrices.grab3();
mat3.set(mat);
mat3.mul(in, out);
Matrices.release(mat3);
return out;
}
public static Vec3 rotateOnly(Vec3 inOut, Mat4 mat) {
return rotateOnly(inOut, mat, inOut);
}
public static Vec3 rotate(Vec3 in, Vec3 axis, float angle, Vec3 out) {
if (out == null) {
out = new Vec3();

View File

@ -405,7 +405,7 @@ class DefaultServerContextImpl extends DefaultServerContext
@Override
public int getTileCount(Vec3i location, RelFace face) {
assert requireContextRole(Role.TILE_STACK);
assert requireContextRole(Role.WORLD);
TileDataStack stack = world.getTilesOrNull(location, face.resolve(AbsFace.POS_Z));
if (stack == null)
return 0;

View File

@ -20,7 +20,9 @@ package ru.windcorp.progressia.server.world.generation.surface.context;
import java.util.Random;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.generic.TileGenericStackRO;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.server.world.context.ServerTileContext;
import ru.windcorp.progressia.server.world.context.impl.RotatingServerContext;
import ru.windcorp.progressia.server.world.generation.surface.Surface;
@ -111,4 +113,11 @@ public class SurfaceContextImpl extends RotatingServerContext implements Surface
return this;
}
@Override
public void addTile(Vec3i userLocation, RelFace userFace, TileData tile) {
if (getTileCount(userLocation, userFace) < TileGenericStackRO.TILES_PER_FACE) {
super.addTile(userLocation, userFace, tile);
}
}
}

View File

@ -83,6 +83,10 @@ public class Rocks {
return blocks.get(variant);
}
public Collection<BlockData> getBlocks() {
return blocks.values();
}
private void register() {
for (RockVariant variant : RockVariant.values()) {

View File

@ -38,6 +38,7 @@ 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.ChunkRenderOptimizerSimple;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface;
import ru.windcorp.progressia.client.world.entity.*;
import ru.windcorp.progressia.client.world.tile.*;
@ -174,9 +175,20 @@ public class TestContent {
register(new TileRenderTransparentSurface("Test:Stones", getTileTexture("Stones")));
register(new HangingTileLogic("Test:Stones"));
register(new TileData("Test:YellowFlowers"));
register(new TileRenderTransparentSurface("Test:YellowFlowers", getTileTexture("YellowFlowers")));
register(new HangingTileLogic("Test:YellowFlowers"));
for (String color : new String[] {
"Yellow",
"White",
"Purple",
"Blue"
}) {
String fullName = color + "Flowers";
String id = "Test:" + fullName;
register(new TileData(id));
register(new TileRenderTransparentSurface(id, getTileTexture(fullName)));
register(new HangingTileLogic(id));
}
register(new TileData("Test:Sand"));
register(new TileRenderTransparentSurface("Test:Sand", getTileTexture("Sand")));
@ -242,6 +254,32 @@ public class TestContent {
register(new TileRenderOpaqueSurface("Test:TilesSmall", getTileTexture("TilesSmall")));
register(new HangingTileLogic("Test:TilesSmall"));
for (String variant : new String[] {
"Low", "Medium", "Tall"
}) {
String fullName = variant + "Grass";
String id = "Test:" + fullName;
register(new TileData(id));
register(new TileRenderHerb(id, getTileTexture(fullName), 6));
register(new HangingTileLogic(id));
}
for (String variant : new String[] {
"Dandelion", "Lavander"
}) {
String fullName = "Tiny" + variant + "Flowers";
String id = "Test:" + fullName;
register(new TileData(id));
register(new TileRenderTinyFlower(id, getTileTexture(fullName), 8, 0.5f));
register(new HangingTileLogic(id));
}
register(new TileData("Test:Bush"));
register(new TileRenderHerb("Test:Bush", getTileTexture("Bush"), 1));
register(new HangingTileLogic("Test:Bush"));
TileDataRegistry.getInstance().values().forEach(PLACEABLE_TILES::add);
PLACEABLE_TILES.removeIf(b -> placeableBlacklist.contains(b.getId()));
PLACEABLE_TILES.sort(Comparator.comparing(TileData::getId));
@ -285,6 +323,7 @@ public class TestContent {
i -> isAnythingSelected() && TestPlayerControls.getInstance().isBlockSelected()
)
);
logic.register(ControlLogic.of("Test:PlaceBlock", TestContent::onBlockPlaceReceived));
data.register("Test:PlaceTile", ControlPlaceTileData::new);
@ -442,7 +481,10 @@ public class TestContent {
private static void registerMisc() {
ChunkIO.registerCodec(new TestChunkCodec());
ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new);
ChunkRenderOptimizerRegistry.getInstance().register("Core:SimpleOptimizer", ChunkRenderOptimizerSimple::new);
GravityModelRegistry.getInstance().register("Test:TheGravityModel", TestGravityModel::new);
GravityModelRegistry.getInstance().register("Test:PlanetGravityModel", PlanetGravityModel::new);
}

View File

@ -0,0 +1,89 @@
/*
* 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.test;
import glm.mat._4.Mat4;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.world.tile.TileRenderCross;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class TileRenderHerb extends TileRenderCross {
/*
* Stolen from OpenJDK's Random implementation
* *evil cackling*
*/
private static final long MULTIPLIER = 0x5DEECE66DL;
private static final long ADDEND = 0xBL;
private static final long MASK = (1L << 48) - 1;
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
private static long permute(long seed) {
return (seed * MULTIPLIER + ADDEND) & MASK;
}
private static double getDouble(long seed) {
final int mask26bits = (1 << 26) - 1;
final int mask27bits = (1 << 27) - 1;
int randomBitsX26 = (int) (seed & 0xFFFFFFFF);
int randomBitsX27 = (int) ((seed >>> Integer.SIZE) & 0xFFFFFFFF);
randomBitsX26 = randomBitsX26 & mask26bits;
randomBitsX27 = randomBitsX27 & mask27bits;
return (((long) (randomBitsX26) << 27) + randomBitsX27) * DOUBLE_UNIT;
}
private final int maxCount;
public TileRenderHerb(String id, Texture texture, int maxCount) {
super(id, texture, true);
this.maxCount = maxCount;
}
@Override
protected boolean getTransform(
DefaultChunkData chunk,
Vec3i relBlockInChunk,
RelFace blockFace,
int count,
Mat4 output
) {
long seed = permute(count ^ getId().hashCode());
seed = permute(seed + relBlockInChunk.x);
seed = permute(seed + relBlockInChunk.y);
seed = permute(seed + relBlockInChunk.z);
seed = permute(seed + blockFace.getId());
float x = (float) getDouble(seed) * 0.8f - 0.4f;
seed = permute(seed);
float y = (float) getDouble(seed) * 0.8f - 0.4f;
seed = permute(seed);
float size = (float) getDouble(seed) * 0.5f + 0.5f;
seed = permute(seed);
double rotation = getDouble(seed) * Math.PI / 8;
output.identity().translate(x, y, 0).scale(size).rotateZ(rotation);
return (count == 0) || ((count < maxCount) && (seed % 3 != 0));
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.test;
import glm.mat._4.Mat4;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class TileRenderTinyFlower extends TileRenderHerb {
private final float size;
public TileRenderTinyFlower(String id, Texture texture, int maxCount, float size) {
super(id, texture, maxCount);
this.size = size;
}
@Override
protected boolean getTransform(
DefaultChunkData chunk,
Vec3i relBlockInChunk,
RelFace blockFace,
int count,
Mat4 output
) {
boolean result = super.getTransform(chunk, relBlockInChunk, blockFace, count, output);
output.scale(size);
return result;
}
}

View File

@ -215,20 +215,26 @@ public class Fields {
return tweak(f, 1, 1, bias);
}
public static Field anti(Field f) {
return tweak(f, 1, -1, 1);
}
public static Field octaves(Field f, double scaleFactor, double amplitudeFactor, int octaves) {
return (x, y) -> {
double result = 0;
double scale = 1;
double amplitude = 1;
double cumulativeAmplitude = 0;
for (int i = 0; i < octaves; ++i) {
result += f.compute(x * scale, y * scale) * amplitude;
cumulativeAmplitude += amplitude;
scale *= scaleFactor;
amplitude /= amplitudeFactor;
}
return result;
return result / cumulativeAmplitude;
};
}

View File

@ -0,0 +1,95 @@
/*
* 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.test.gen;
import java.util.Set;
import java.util.function.Function;
import com.google.common.collect.ImmutableSet;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceTopLayerFeature;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
import ru.windcorp.progressia.test.TestContent;
public class TestFlowerFeature extends SurfaceTopLayerFeature {
private static class FlowerGenerator {
private final TileData tile;
private final SurfaceFloatField floweriness;
public FlowerGenerator(TileData tile, Function<String, SurfaceFloatField> flowerinessGenerator) {
this.tile = tile;
this.floweriness = flowerinessGenerator.apply(tile.getName());
}
public void generate(SurfaceBlockContext context) {
if (context.getRandom().nextDouble() < floweriness.get(context)) {
context.addTile(RelFace.UP, tile);
}
}
}
private final Set<String> soilWhitelist;
{
ImmutableSet.Builder<String> b = ImmutableSet.builder();
b.add("Test:Dirt", "Test:Stone");
TestContent.ROCKS.getRocks().forEach(rock -> rock.getBlocks().forEach(block -> b.add(block.getId())));
soilWhitelist = b.build();
}
private final FlowerGenerator[] flowers;
public TestFlowerFeature(String id, Function<String, SurfaceFloatField> flowerinessGenerator) {
super(id);
this.flowers = TileDataRegistry.getInstance().values().stream()
.filter(tile -> tile.getName().endsWith("Flowers"))
.map(tile -> new FlowerGenerator(tile, flowerinessGenerator))
.toArray(FlowerGenerator[]::new);
}
@Override
protected void processTopBlock(SurfaceBlockContext context) {
if (context.getLocation().z < 0) {
return;
}
if (!soilWhitelist.contains(context.getBlock().getId())) {
return;
}
if (!context.pushRelative(RelFace.UP).logic().getBlock().isTransparent()) {
context.pop();
return;
}
context.pop();
for (FlowerGenerator flower : flowers) {
flower.generate(context);
}
}
@Override
protected boolean isSolid(SurfaceBlockContext context) {
return context.logic().getBlock().isSolid(RelFace.UP);
}
}

View File

@ -76,7 +76,7 @@ public class TestGenerationConfig {
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
SurfaceFloatField cliffs = FIELDS.get("Test:CliffSelector");
SurfaceFloatField cliffs = FIELDS.get("Test:Cliff");
WorleyProceduralNoise.Builder<TerrainLayer> builder = WorleyProceduralNoise.builder();
TestContent.ROCKS.getRocks().forEach(rock -> {
@ -108,21 +108,32 @@ public class TestGenerationConfig {
private static void registerFeatures(List<SurfaceFeature> features) {
SurfaceFloatField forestiness = FIELDS.register(
"Test:Forestiness",
"Test:Forest",
() -> squash(scale(FIELDS.primitive(), 200), 5)
);
SurfaceFloatField floweriness = FIELDS.register(
"Test:Floweriness",
SurfaceFloatField grassiness = FIELDS.register(
"Test:Grass",
f -> multiply(
scale(octaves(FIELDS.primitive(), 2, 2), 40),
tweak(FIELDS.get("Test:Forestiness", f), 1, -1, 1.1)
tweak(octaves(FIELDS.primitive(), 2, 2), 40, 0.5, 1.2),
squash(tweak(FIELDS.get("Test:Forest", f), 1, -0.7, 1), 10),
anti(squash(FIELDS.get("Test:Cliff", f), 10))
)
);
Function<String, SurfaceFloatField> floweriness = flowerName -> FIELDS.register(
"Test:Flower" + flowerName,
f -> multiply(
selectPositive(squash(scale(octaves(FIELDS.primitive(), 2, 3), 100), 2), 1, 0.5),
tweak(FIELDS.get("Test:Forest", f), 1, -1, 1.1),
anti(squash(FIELDS.get("Test:Cliff", f), 10))
)
);
features.add(new TestBushFeature("Test:BushFeature", forestiness));
features.add(new TestTreeFeature("Test:TreeFeature", forestiness));
features.add(new TestGrassFeature("Test:GrassFeature", FIELDS.get("Test:CliffSelector"), floweriness));
features.add(new TestGrassFeature("Test:GrassFeature", grassiness));
features.add(new TestFlowerFeature("Test:FlowerFeature", floweriness));
}
}

View File

@ -23,40 +23,47 @@ import java.util.Set;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import ru.windcorp.progressia.common.util.ArrayFloatRangeMap;
import ru.windcorp.progressia.common.util.FloatRangeMap;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
import ru.windcorp.progressia.server.world.generation.surface.SurfaceTopLayerFeature;
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
import ru.windcorp.progressia.test.TestContent;
public class TestGrassFeature extends SurfaceTopLayerFeature {
private static final Set<String> WHITELIST = ImmutableSet.of(
"Test:Dirt",
"Test:Stone",
"Test:GraniteMonolith",
"Test:GraniteCracked",
"Test:GraniteGravel"
);
private final Set<String> soilWhitelist;
{
ImmutableSet.Builder<String> b = ImmutableSet.builder();
b.add("Test:Dirt", "Test:Stone");
TestContent.ROCKS.getRocks().forEach(rock -> rock.getBlocks().forEach(block -> b.add(block.getId())));
soilWhitelist = b.build();
}
private final SurfaceFloatField grassiness;
private final SurfaceFloatField floweriness;
private final double scatterDensity = 1.0 / (3 * 3);
private final TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
private final List<TileData> flowers = ImmutableList.of(
TileDataRegistry.getInstance().get("Test:YellowFlowers")
);
private final FloatRangeMap<TileData> grasses = new ArrayFloatRangeMap<>();
{
grasses.put(0.6f, 1, TileDataRegistry.getInstance().get("Test:TallGrass"));
grasses.put(0.4f, 0.6f, TileDataRegistry.getInstance().get("Test:MediumGrass"));
grasses.put(0.1f, 0.4f, TileDataRegistry.getInstance().get("Test:LowGrass"));
}
private final List<TileData> scatter = ImmutableList.of(
TileDataRegistry.getInstance().get("Test:Stones"),
TileDataRegistry.getInstance().get("Test:Sand")
TileDataRegistry.getInstance().get("Test:Sand"),
TileDataRegistry.getInstance().get("Test:Bush")
);
public TestGrassFeature(String id, SurfaceFloatField grassiness, SurfaceFloatField floweriness) {
public TestGrassFeature(String id, SurfaceFloatField grassiness) {
super(id);
this.grassiness = grassiness;
this.floweriness = floweriness;
}
@Override
@ -64,7 +71,7 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
if (context.getLocation().z < 0) {
return;
}
if (!WHITELIST.contains(context.getBlock().getId())) {
if (!soilWhitelist.contains(context.getBlock().getId())) {
return;
}
@ -75,15 +82,11 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
context.pop();
double grassiness = this.grassiness.get(context);
if (grassiness < 0.2) {
growGrass(context);
if (grassiness > 0.1) {
growGrass(context, grassiness);
}
placeScatter(context);
if (grassiness < 0.2) {
growFlowers(context);
}
}
private void placeScatter(SurfaceBlockContext context) {
@ -93,9 +96,10 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
}
}
private void growGrass(SurfaceBlockContext context) {
private void growGrass(SurfaceBlockContext context, double grassiness) {
for (RelFace face : RelFace.getFaces()) {
if (face == RelFace.DOWN) continue;
if (face == RelFace.DOWN)
continue;
if (context.pushRelative(face).logic().getBlock().isTransparent()) {
context.pop();
@ -105,12 +109,12 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
}
}
}
private void growFlowers(SurfaceBlockContext context) {
if (context.getRandom().nextDouble() < floweriness.get(context)) {
TileData tile = pickRandom(context, flowers);
context.addTile(RelFace.UP, tile);
if (context.getRandom().nextDouble() < grassiness) {
TileData herbGrass = grasses.get((float) grassiness);
if (herbGrass != null) {
context.addTile(RelFace.UP, herbGrass);
}
}
}

View File

@ -54,9 +54,9 @@ public class TestHeightMap implements SurfaceFloatField {
tweak(octaves(fields.primitive(), 2, 3), 50, 0.2)
);
fields.register("Test:CliffSelector", face, multiply(
fields.register("Test:Cliff", face, multiply(
shoreCliffSelector,
bias(select(shoreCliffs, 0, 0.07), 0)
select(shoreCliffs, 0, 0.07)
));
fields.register("Test:Height", face, cutoff(add(

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B