grass height variance
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
d87c262955
commit
7df87e4510
@ -14,7 +14,9 @@
|
||||
"grassData": {
|
||||
"baseColor": {"x": 0.25, "y": 0.6, "z": 0.43},
|
||||
"tipColor": {"x": 0.17, "y": 0.71, "z": 0.12},
|
||||
"maxTipCurve" : 0.27
|
||||
"maxTipCurve" : 0.27,
|
||||
"minHeight" : 0.7,
|
||||
"maxHeight" : 5.0
|
||||
},
|
||||
"graphicsTemplate": {
|
||||
"model": {
|
||||
|
||||
@ -8,6 +8,11 @@
|
||||
|
||||
#define grassWidth 0.01 //TODO: convert to uniform
|
||||
|
||||
/**
|
||||
* Number of variables per instance
|
||||
*/
|
||||
#define NUM_PER_INSTANCE_VARS 6
|
||||
|
||||
|
||||
//input buffers
|
||||
layout (location = 0) in vec3 aPos;
|
||||
@ -57,6 +62,7 @@ mat4 rotation3dX(float angle);
|
||||
mat4 rotation3dY(float angle);
|
||||
mat4 rotation3dZ(float angle);
|
||||
vec3 rotateY(vec3 vector, float angle);
|
||||
mat4 scale3d(float x, float y, float z);
|
||||
float easeIn(float interpolator);
|
||||
float easeOut(float interpolator);
|
||||
float easeInOut(float interpolator);
|
||||
@ -75,15 +81,16 @@ void main() {
|
||||
|
||||
ivec2 texSize = textureSize(material.diffuse,0);
|
||||
|
||||
int sampleX = (gl_InstanceID % rowSize) * 5;
|
||||
int sampleX = (gl_InstanceID % rowSize) * NUM_PER_INSTANCE_VARS;
|
||||
int sampleY = (gl_InstanceID / rowSize);
|
||||
|
||||
//grab data out of texture
|
||||
float xOffset = texelFetch(material.diffuse,ivec2(0 + sampleX,sampleY),0).r;
|
||||
float yOffset = texelFetch(material.diffuse,ivec2(1 + sampleX,sampleY),0).r;
|
||||
float zOffset = texelFetch(material.diffuse,ivec2(2 + sampleX,sampleY),0).r;
|
||||
float rotVar = texelFetch(material.diffuse,ivec2(3 + sampleX,sampleY),0).r;
|
||||
float rotVar2 = texelFetch(material.diffuse,ivec2(4 + sampleX,sampleY),0).r;
|
||||
float xOffset = texelFetch(material.diffuse,ivec2(0 + sampleX,sampleY),0).r;
|
||||
float yOffset = texelFetch(material.diffuse,ivec2(1 + sampleX,sampleY),0).r;
|
||||
float zOffset = texelFetch(material.diffuse,ivec2(2 + sampleX,sampleY),0).r;
|
||||
float rotVar = texelFetch(material.diffuse,ivec2(3 + sampleX,sampleY),0).r;
|
||||
float rotVar2 = texelFetch(material.diffuse,ivec2(4 + sampleX,sampleY),0).r;
|
||||
float heightScale = texelFetch(material.diffuse,ivec2(5 + sampleX,sampleY),0).r;
|
||||
|
||||
//
|
||||
//curve float noise
|
||||
@ -130,6 +137,7 @@ void main() {
|
||||
|
||||
//
|
||||
//position transform
|
||||
//
|
||||
mat4 localTransform = mat4(
|
||||
1.0, 0.0, 0.0, 0.0, //column 1
|
||||
0.0, 1.0, 0.0, 0.0, //column 2
|
||||
@ -137,8 +145,13 @@ void main() {
|
||||
xOffset, yOffset, zOffset, 1.0 //column 4
|
||||
);
|
||||
|
||||
//
|
||||
// Scales the blade of grass vertically
|
||||
//
|
||||
mat4 localScale = scale3d(1.0,heightScale,1.0);
|
||||
|
||||
//normalize posiiton and normal
|
||||
vec4 FinalVertex = model * localTransform * windRot * localRot * localRot2 * vec4(aPos, 1.0);
|
||||
vec4 FinalVertex = model * localTransform * localScale * windRot * localRot * localRot2 * vec4(aPos, 1.0);
|
||||
vec4 FinalNormal = windRot * localRot * localRot2 * vec4(aNormal, 1.0);
|
||||
// vec4 FinalNormal = transpose(inverse(localRot2 * localRot * model * localTransform)) * vec4(aNormal, 1.0);
|
||||
|
||||
@ -225,6 +238,15 @@ mat4 rotation3dZ(float angle) {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
mat4 scale3d(float x, float y, float z){
|
||||
return mat4(
|
||||
x, 0.0, 0.0, 0.0,
|
||||
0.0, y, 0.0, 3.0,
|
||||
0.0, 0.0, z, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
float easeIn(float interpolator){
|
||||
return interpolator * interpolator;
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#maven.buildNumber.plugin properties file
|
||||
#Thu Mar 27 18:17:28 EDT 2025
|
||||
buildNumber=608
|
||||
#Fri Mar 28 09:59:20 EDT 2025
|
||||
buildNumber=609
|
||||
|
||||
@ -1351,6 +1351,9 @@ Code formatting
|
||||
File-controlled foliage coloration
|
||||
Fix TextureInstancedActor packing data texture incorrectly (column major instead of row major)
|
||||
|
||||
(03/28/2025)
|
||||
Grass height variance with control from ui + file
|
||||
|
||||
|
||||
# TODO
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package electrosphere.client.terrain.foliage;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
@ -68,22 +67,6 @@ public class FoliageCell {
|
||||
*/
|
||||
static final int AIR_VOXEL_ID = 0;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Size of a single item of foliage in the texture buffer
|
||||
* </p>
|
||||
* A lot of these are x 4 to account for size of float
|
||||
* 3 x 4 for position
|
||||
* 2 x 4 for euler rotation
|
||||
*
|
||||
*
|
||||
* eventually:
|
||||
* grass type
|
||||
* color
|
||||
* wind characteristics?
|
||||
*/
|
||||
static final int SINGLE_FOLIAGE_DATA_SIZE_BYTES = 3 * 4 + 2 * 4;
|
||||
|
||||
/**
|
||||
* The map of all attributes for instanced foliage
|
||||
*/
|
||||
@ -313,154 +296,6 @@ public class FoliageCell {
|
||||
this.hasGenerated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert blades of grass into the entity
|
||||
* @param vX the x offset of the voxel
|
||||
* @param vY the y offset of the voxel
|
||||
* @param vZ the z offset of the voxel
|
||||
* @param floatBufferView the gpu data buffer
|
||||
* @param chunkData the chunk data
|
||||
* @return the number of blades of grass added
|
||||
*/
|
||||
protected int insertBlades(int vX, int vY, int vZ, FloatBuffer floatBufferView, ChunkData chunkData){
|
||||
int rVal = 0;
|
||||
|
||||
//get positions offset
|
||||
Vector3d voxelRealPos = new Vector3d(this.getRealPos()).add(vX,vY,vZ);
|
||||
Vector3i currVoxelPos = new Vector3i(this.voxelPos).add(vX,vY,vZ);
|
||||
|
||||
int scale = (int)Math.pow(2,lod);
|
||||
|
||||
//check that the current voxel even supports foliage
|
||||
boolean shouldGenerate = false;
|
||||
List<String> foliageTypesSupported = null;
|
||||
if(chunkData != null && currVoxelPos.y + 1 < ServerTerrainChunk.CHUNK_DIMENSION){
|
||||
foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(chunkData.getType(currVoxelPos)).getAmbientFoliage();
|
||||
boolean airAbove = chunkData.getType(currVoxelPos.x,currVoxelPos.y+1,currVoxelPos.z) == AIR_VOXEL_ID;
|
||||
if(foliageTypesSupported != null && airAbove){
|
||||
shouldGenerate = true;
|
||||
}
|
||||
}
|
||||
if(shouldGenerate){
|
||||
//construct simple grid to place foliage on
|
||||
Vector3d sample_00 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add(-0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
Vector3d sample_01 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add(-0.5,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
Vector3d sample_02 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add(-0.5,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
Vector3d sample_10 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
Vector3d sample_11 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
Vector3d sample_12 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
Vector3d sample_20 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
Vector3d sample_21 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0.5,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
Vector3d sample_22 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0.5,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
//get the heights of each sample
|
||||
float height_11 = (float)(sample_11 != null ? sample_11.y : 0);
|
||||
float height_00 = (float)(sample_00 != null ? sample_00.y : height_11);
|
||||
float height_01 = (float)(sample_01 != null ? sample_01.y : height_11);
|
||||
float height_02 = (float)(sample_02 != null ? sample_02.y : height_11);
|
||||
float height_10 = (float)(sample_10 != null ? sample_10.y : height_11);
|
||||
float height_12 = (float)(sample_12 != null ? sample_12.y : height_11);
|
||||
float height_20 = (float)(sample_20 != null ? sample_20.y : height_11);
|
||||
float height_21 = (float)(sample_21 != null ? sample_21.y : height_11);
|
||||
float height_22 = (float)(sample_22 != null ? sample_22.y : height_11);
|
||||
//each height is in real world coordinates that are absolute
|
||||
//when rendering, there's already a y offset for the center of the field of grass (based on the model matrix)
|
||||
//so when offseting the position of the blade of grass RELATIVE to the overall instance being drawn, need to subtract the real world coordinates of the overall instance
|
||||
//in other words realPos SPECIFICALLY for the y dimension, for x and z you don't need to worry about it
|
||||
|
||||
//if we don't find data for the center sample, can't place grass so don't create entity
|
||||
if(sample_11 != null){
|
||||
//generate positions to place
|
||||
for(int x = 0; x < TARGET_FOLIAGE_SPACING; x=x+scale){
|
||||
for(int z = 0; z < TARGET_FOLIAGE_SPACING; z=z+scale){
|
||||
//get position to place
|
||||
double rand1 = placementRandomizer.nextDouble();
|
||||
double rand2 = placementRandomizer.nextDouble();
|
||||
double relativePositionOnGridX = x / (1.0 * TARGET_FOLIAGE_SPACING) + rand1 / TARGET_FOLIAGE_SPACING;
|
||||
double relativePositionOnGridZ = z / (1.0 * TARGET_FOLIAGE_SPACING) + rand2 / TARGET_FOLIAGE_SPACING;
|
||||
double offsetX = relativePositionOnGridX - 0.5;
|
||||
double offsetZ = relativePositionOnGridZ - 0.5;
|
||||
//determine quadrant we're placing in
|
||||
double offsetY = 0;
|
||||
boolean addBlade = false;
|
||||
if(relativePositionOnGridX >=0.5){
|
||||
if(relativePositionOnGridZ >= 0.5){
|
||||
relativePositionOnGridX = relativePositionOnGridX - 0.5;
|
||||
relativePositionOnGridZ = relativePositionOnGridZ - 0.5;
|
||||
relativePositionOnGridX /= 0.5;
|
||||
relativePositionOnGridZ /= 0.5;
|
||||
// System.out.println(relativePositionOnGridX + " " + relativePositionOnGridZ);
|
||||
//if we have heights for all four surrounding spots, interpolate for y value
|
||||
if(sample_11 != null && sample_12 != null && sample_21 != null && sample_22 != null){
|
||||
offsetY =
|
||||
height_11 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
|
||||
height_12 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
|
||||
height_21 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
|
||||
height_22 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
|
||||
addBlade = true;
|
||||
}
|
||||
} else {
|
||||
relativePositionOnGridX = relativePositionOnGridX - 0.5;
|
||||
relativePositionOnGridX /= 0.5;
|
||||
relativePositionOnGridZ /= 0.5;
|
||||
//if we have heights for all four surrounding spots, interpolate for y value
|
||||
if(sample_10 != null && sample_11 != null && sample_20 != null && sample_21 != null){
|
||||
offsetY =
|
||||
height_10 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
|
||||
height_11 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
|
||||
height_20 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
|
||||
height_21 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
|
||||
addBlade = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(relativePositionOnGridZ >= 0.5){
|
||||
relativePositionOnGridZ = relativePositionOnGridZ - 0.5;
|
||||
relativePositionOnGridX /= 0.5;
|
||||
relativePositionOnGridZ /= 0.5;
|
||||
//if we have heights for all four surrounding spots, interpolate for y value
|
||||
if(sample_01 != null && sample_02 != null && sample_11 != null && sample_12 != null){
|
||||
offsetY =
|
||||
height_01 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
|
||||
height_02 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
|
||||
height_11 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
|
||||
height_12 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
|
||||
addBlade = true;
|
||||
}
|
||||
} else {
|
||||
relativePositionOnGridX /= 0.5;
|
||||
relativePositionOnGridZ /= 0.5;
|
||||
//if we have heights for all four surrounding spots, interpolate for y value
|
||||
if(sample_00 != null && sample_01 != null && sample_10 != null && sample_11 != null){
|
||||
offsetY =
|
||||
height_00 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
|
||||
height_01 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
|
||||
height_10 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
|
||||
height_11 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
|
||||
addBlade = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(addBlade){
|
||||
//convert y to relative to chunk
|
||||
offsetY = offsetY - this.getRealPos().y;
|
||||
double rotVar = placementRandomizer.nextDouble() * Math.PI * 2;
|
||||
double rotVar2 = placementRandomizer.nextDouble();
|
||||
if(floatBufferView.limit() >= floatBufferView.position() + SINGLE_FOLIAGE_DATA_SIZE_BYTES / 4){
|
||||
floatBufferView.put((float)offsetX + vX);
|
||||
floatBufferView.put((float)offsetY + vY);
|
||||
floatBufferView.put((float)offsetZ + vZ);
|
||||
floatBufferView.put((float)rotVar);
|
||||
floatBufferView.put((float)rotVar2);
|
||||
rVal++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the real-space position of the foliage cell
|
||||
* @return the real-space position
|
||||
|
||||
@ -55,6 +55,14 @@ public class FoliageModel {
|
||||
*/
|
||||
static final int TARGET_FOLIAGE_SPACING = 200;
|
||||
|
||||
/**
|
||||
* The number of floats we're passing per blade of grass
|
||||
* 3 for position
|
||||
* 2 for rotation
|
||||
* 1 for vertical scale
|
||||
*/
|
||||
protected static final int NUM_PER_INSTANCE_VARS = 6;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Size of a single item of foliage in the texture buffer
|
||||
@ -62,6 +70,7 @@ public class FoliageModel {
|
||||
* A lot of these are x 4 to account for size of float
|
||||
* 3 x 4 for position
|
||||
* 2 x 4 for euler rotation
|
||||
* 1 x 4 for height scaling
|
||||
*
|
||||
*
|
||||
* eventually:
|
||||
@ -69,7 +78,7 @@ public class FoliageModel {
|
||||
* color
|
||||
* wind characteristics?
|
||||
*/
|
||||
static final int SINGLE_FOLIAGE_DATA_SIZE_BYTES = 3 * 4 + 2 * 4;
|
||||
protected static final int SINGLE_FOLIAGE_DATA_SIZE_BYTES = NUM_PER_INSTANCE_VARS * 4;
|
||||
|
||||
/**
|
||||
* Cutoff to place foliage at, weight-wise
|
||||
@ -242,7 +251,7 @@ public class FoliageModel {
|
||||
QueuedTexture queuedAsset = new QueuedTexture(QueuedTextureType.DATA_BUFF,buffer,textureWidth,textureHeight);
|
||||
Globals.assetManager.queuedAsset(queuedAsset);
|
||||
|
||||
TextureInstancedActor actor = TextureInstancedActor.attachTextureInstancedActor(rVal, foliageType.getGraphicsTemplate().getModel().getPath(), vertexPath, fragmentPath, queuedAsset, drawCount, textureWidth / 5);
|
||||
TextureInstancedActor actor = TextureInstancedActor.attachTextureInstancedActor(rVal, foliageType.getGraphicsTemplate().getModel().getPath(), vertexPath, fragmentPath, queuedAsset, drawCount, textureWidth / NUM_PER_INSTANCE_VARS);
|
||||
ClientEntityUtils.initiallyPositionEntity(rVal, realPos, new Quaterniond());
|
||||
EntityUtils.getScale(rVal).set(1,1,1);
|
||||
//add ambient foliage behavior tree
|
||||
@ -292,6 +301,8 @@ public class FoliageModel {
|
||||
int rVal = 0;
|
||||
|
||||
float maxTipCurve = foliageType.getGrassData().getMaxTipCurve();
|
||||
float minimumHeight = foliageType.getGrassData().getMinHeight();
|
||||
float heightMultiplier = foliageType.getGrassData().getMaxHeight() - foliageType.getGrassData().getMinHeight();
|
||||
|
||||
//construct simple grid to place foliage on
|
||||
// Vector3d sample_00 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add(-SAMPLE_OFFSET,SAMPLE_START_HEIGHT,-SAMPLE_OFFSET), new Vector3d(0,-1,0), RAY_LENGTH);
|
||||
@ -415,12 +426,14 @@ public class FoliageModel {
|
||||
offsetY = offsetY - realPos.y;
|
||||
double rotVar = placementRandomizer.nextDouble() * Math.PI * 2;
|
||||
double rotVar2 = placementRandomizer.nextDouble() * maxTipCurve;
|
||||
double heightScale = placementRandomizer.nextDouble();
|
||||
if(floatBufferView.limit() >= floatBufferView.position() + SINGLE_FOLIAGE_DATA_SIZE_BYTES / 4){
|
||||
floatBufferView.put((float)relativePositionOnGridX + vX);
|
||||
floatBufferView.put((float)offsetY + vY);
|
||||
floatBufferView.put((float)relativePositionOnGridZ + vZ);
|
||||
floatBufferView.put((float)rotVar);
|
||||
floatBufferView.put((float)rotVar2);
|
||||
floatBufferView.put((float)(heightScale * heightMultiplier + minimumHeight));
|
||||
rVal++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,16 @@ public class ImGuiEntityFoliageTab {
|
||||
*/
|
||||
static float[] grassMaxTipCurve = new float[1];
|
||||
|
||||
/**
|
||||
* Grass Min Height Scale
|
||||
*/
|
||||
static float[] grassMinHeightScale = new float[1];
|
||||
|
||||
/**
|
||||
* Grass Max Height Scale
|
||||
*/
|
||||
static float[] grassMaxHeightScale = new float[1];
|
||||
|
||||
/**
|
||||
* Client scene entity view
|
||||
*/
|
||||
@ -66,6 +76,14 @@ public class ImGuiEntityFoliageTab {
|
||||
grassData.setMaxTipCurve(grassMaxTipCurve[0]);
|
||||
}
|
||||
|
||||
if(ImGui.sliderFloat("Min Height Scale", grassMinHeightScale, 0.1f, 10)){
|
||||
grassData.setMinHeight(grassMinHeightScale[0]);
|
||||
}
|
||||
|
||||
if(ImGui.sliderFloat("Max Height Scale", grassMaxHeightScale, 0.1f, 10)){
|
||||
grassData.setMaxHeight(grassMaxHeightScale[0]);
|
||||
}
|
||||
|
||||
if(ImGui.button("Regenerate All Grass")){
|
||||
Globals.foliageCellManager.evictAll();
|
||||
}
|
||||
|
||||
@ -22,6 +22,16 @@ public class GrassData {
|
||||
*/
|
||||
float maxTipCurve;
|
||||
|
||||
/**
|
||||
* Minimum height of the grass
|
||||
*/
|
||||
float minHeight;
|
||||
|
||||
/**
|
||||
* Maximum height of the grass
|
||||
*/
|
||||
float maxHeight;
|
||||
|
||||
/**
|
||||
* Gets the base color of the grass
|
||||
* @return The base color
|
||||
@ -53,6 +63,40 @@ public class GrassData {
|
||||
public void setMaxTipCurve(float maxTipCurve) {
|
||||
this.maxTipCurve = maxTipCurve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum height of the grass
|
||||
* @return The minimum height of the grass
|
||||
*/
|
||||
public float getMinHeight() {
|
||||
return minHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum height of the grass
|
||||
* @param minHeight the minimum height of the grass
|
||||
*/
|
||||
public void setMinHeight(float minHeight) {
|
||||
this.minHeight = minHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum height of the grass
|
||||
* @return The maximum height of the grass
|
||||
*/
|
||||
public float getMaxHeight() {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum height of the grass
|
||||
* @param minHeight the maximum height of the grass
|
||||
*/
|
||||
public void setMaxHeight(float maxHeight) {
|
||||
this.maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user