|
|
|
|
@@ -19,36 +19,11 @@ private:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The axisConversionArray, when given a primary index, allows easy
|
|
|
|
|
// access to the indices of the other two axies. Access the data at the
|
|
|
|
|
// primary index location to get the horizontal secondary axis.
|
|
|
|
|
// Access the data at the primary location plus three to get the
|
|
|
|
|
// remaining, tertiary, axis.
|
|
|
|
|
// All directions are specified by an index, 0, 1, or 2 which
|
|
|
|
|
// correspond to x, y, and z.
|
|
|
|
|
// The axisConversionArray is used in several places
|
|
|
|
|
// notably the crossection and taperedLimb methods.
|
|
|
|
|
// Example:
|
|
|
|
|
// If the primary axis is z, then the primary index is 2.
|
|
|
|
|
// The secondary index is axisConversionArray[2] which is 0,
|
|
|
|
|
// the index for the x axis.
|
|
|
|
|
// The remaining axis is axisConversionArray[2 + 3] which is 1,
|
|
|
|
|
// the index for the y axis.
|
|
|
|
|
// Using this method, the secondary axis will always be horizontal (x or z),
|
|
|
|
|
// and the tertiary always vertical (y), if possible.
|
|
|
|
|
unsigned char axisConversionArray[6];
|
|
|
|
|
|
|
|
|
|
// Set up the pseudorandom number generator
|
|
|
|
|
Random *rnd;
|
|
|
|
|
|
|
|
|
|
// Make fields to hold the level data and the random seed
|
|
|
|
|
Level *thisLevel;
|
|
|
|
|
|
|
|
|
|
// Field to hold the tree origin, x y and z.
|
|
|
|
|
int origin[3];
|
|
|
|
|
// Field to hold the tree height.
|
|
|
|
|
int height;
|
|
|
|
|
// Other important tree information.
|
|
|
|
|
int trunkHeight;
|
|
|
|
|
double trunkHeightScale;
|
|
|
|
|
double branchDensity;
|
|
|
|
|
@@ -58,19 +33,13 @@ private:
|
|
|
|
|
int trunkWidth;
|
|
|
|
|
int heightVariance;
|
|
|
|
|
int foliageHeight;
|
|
|
|
|
// The foliage coordinates are a list of [x,y,z,y of branch base] values for each cluster
|
|
|
|
|
int **foliageCoords;
|
|
|
|
|
int foliageCoordsLength;
|
|
|
|
|
void prepare(){
|
|
|
|
|
// Initialize the instance variables.
|
|
|
|
|
// Populate the list of foliage cluster locations.
|
|
|
|
|
// Designed to be overridden in child classes to change basic
|
|
|
|
|
// tree properties (trunk width, branch angle, foliage density, etc..).
|
|
|
|
|
trunkHeight = (int) (height * trunkHeightScale);
|
|
|
|
|
if (trunkHeight >= height) trunkHeight = height - 1;
|
|
|
|
|
int clustersPerY = (int) (1.382 + pow(foliageDensity * height / 13.0, 2));
|
|
|
|
|
if (clustersPerY < 1) clustersPerY = 1;
|
|
|
|
|
// The foliage coordinates are a list of [x,y,z,y of branch base] values for each cluster
|
|
|
|
|
int **tempFoliageCoords = new int *[clustersPerY * height];
|
|
|
|
|
for( int i = 0; i < clustersPerY * height; i++ )
|
|
|
|
|
{
|
|
|
|
|
@@ -99,7 +68,6 @@ private:
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The originOffset is to put the value in the middle of the block.
|
|
|
|
|
double originOffset = 0.5;
|
|
|
|
|
while (num < clustersPerY)
|
|
|
|
|
{
|
|
|
|
|
@@ -109,10 +77,7 @@ private:
|
|
|
|
|
int z = Mth::floor(radius * cos(angle) + origin[2] + originOffset);
|
|
|
|
|
int checkStart[] = { x, y, z };
|
|
|
|
|
int checkEnd[] = { x, y + foliageHeight, z };
|
|
|
|
|
// check the center column of the cluster for obstructions.
|
|
|
|
|
if (checkLine(checkStart, checkEnd) == -1) {
|
|
|
|
|
// If the cluster can be created, check the branch path
|
|
|
|
|
// for obstructions.
|
|
|
|
|
int checkBranchBase[] = { origin[0], origin[1], origin[2] };
|
|
|
|
|
double distance = sqrt(pow(abs(origin[0] - checkStart[0]), 2.0) + pow(abs(origin[2] - checkStart[2]), 2.0));
|
|
|
|
|
double branchHeight = distance * branchSlope;
|
|
|
|
|
@@ -124,11 +89,8 @@ private:
|
|
|
|
|
{
|
|
|
|
|
checkBranchBase[1] = (int) (checkStart[1] - branchHeight);
|
|
|
|
|
}
|
|
|
|
|
// Now check the branch path
|
|
|
|
|
if (checkLine(checkBranchBase, checkStart) == -1)
|
|
|
|
|
{
|
|
|
|
|
// If the branch path is clear, add the position to the list
|
|
|
|
|
// of foliage positions
|
|
|
|
|
tempFoliageCoords[clusterCount][0] = x;
|
|
|
|
|
tempFoliageCoords[clusterCount][1] = y;
|
|
|
|
|
tempFoliageCoords[clusterCount][2] = z;
|
|
|
|
|
@@ -141,34 +103,22 @@ private:
|
|
|
|
|
y--;
|
|
|
|
|
relativeY--;
|
|
|
|
|
}
|
|
|
|
|
// 4J Stu - Rather than copying the array, we are storing the number of valid elements in the array
|
|
|
|
|
|
|
|
|
|
foliageCoordsLength = clusterCount;
|
|
|
|
|
foliageCoords = tempFoliageCoords;
|
|
|
|
|
// Delete the rest of the array whilst we still know how big it was
|
|
|
|
|
|
|
|
|
|
for( int i = clusterCount; i < clustersPerY * height; i++ )
|
|
|
|
|
{
|
|
|
|
|
delete [] tempFoliageCoords[i];
|
|
|
|
|
tempFoliageCoords[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
// 4J - original code for above is the following, it isn't obvious to me why it is doing a copy of the array, so let's not for now
|
|
|
|
|
// foliageCoords = new int[clusterCount][4];
|
|
|
|
|
// System.arraycopy(tempFoliageCoords, 0, foliageCoords, 0, clusterCount);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void crossection(int x, int y, int z, float radius, byte direction, int material)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// Create a circular cross section.
|
|
|
|
|
//
|
|
|
|
|
// Used to nearly everything in the foliage, branches, and trunk.
|
|
|
|
|
// This is a good target for performance optimization.
|
|
|
|
|
|
|
|
|
|
// Passed values:
|
|
|
|
|
// x,y,z is the center location of the cross section
|
|
|
|
|
// radius is the radius of the section from the center
|
|
|
|
|
// direction is the direction the cross section is pointed, 0 for x, 1 for y, 2 for z
|
|
|
|
|
// material is the index number for the material to use
|
|
|
|
|
int rad = (int) (radius + 0.618);
|
|
|
|
|
byte secidx1 = axisConversionArray[direction];
|
|
|
|
|
byte secidx2 = axisConversionArray[direction + 3];
|
|
|
|
|
@@ -196,8 +146,6 @@ private:
|
|
|
|
|
|
|
|
|
|
if (!((thismat == 0) || (thismat == Tile::leaves->id)))
|
|
|
|
|
{
|
|
|
|
|
// If the material of the checked block is anything other than
|
|
|
|
|
// air or foliage, skip this tile.
|
|
|
|
|
offset2++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
@@ -211,12 +159,6 @@ private:
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
float treeShape(int y){
|
|
|
|
|
// Take the y position relative to the base of the tree.
|
|
|
|
|
// Return the distance the foliage should be from the trunk axis.
|
|
|
|
|
// Return a negative number if foliage should not be created at this height.
|
|
|
|
|
// This method is intended for overriding in child classes, allowing
|
|
|
|
|
// different shaped trees.
|
|
|
|
|
// This method should return a consistent value for each y (don't randomize).
|
|
|
|
|
if (y < (((float) height) * 0.3)) return (float) -1.618;
|
|
|
|
|
float radius = ((float) height) / ((float) 2.0);
|
|
|
|
|
float adjacent = (((float) height) / ((float) 2.0)) - y;
|
|
|
|
|
@@ -224,29 +166,19 @@ private:
|
|
|
|
|
if (adjacent == 0) distance = radius;
|
|
|
|
|
else if (abs(adjacent) >= radius) distance = (float) 0.0;
|
|
|
|
|
else distance = (float) sqrt(pow(abs(radius), 2) - pow(abs(adjacent), 2));
|
|
|
|
|
// Alter this factor to change the overall width of the tree.
|
|
|
|
|
distance *= (float) 0.5;
|
|
|
|
|
return distance;
|
|
|
|
|
}
|
|
|
|
|
float foliageShape(int y){
|
|
|
|
|
// Take the y position relative to the base of the foliage cluster.
|
|
|
|
|
// Return the radius of the cluster at this y
|
|
|
|
|
// Return a negative number if no foliage should be created at this level
|
|
|
|
|
// this method is intended for overriding in child classes, allowing
|
|
|
|
|
// foliage of different sizes and shapes.
|
|
|
|
|
if ((y < 0) || (y >= foliageHeight)) return (float) -1;
|
|
|
|
|
else if ((y == 0) || (y == (foliageHeight - 1))) return (float) 2;
|
|
|
|
|
else return (float) 3;
|
|
|
|
|
}
|
|
|
|
|
void foliageCluster(int x, int y, int z){
|
|
|
|
|
|
|
|
|
|
// Generate a cluster of foliage, with the base at x, y, z.
|
|
|
|
|
// The shape of the cluster is derived from foliageShape
|
|
|
|
|
// crossection is called to make each level.
|
|
|
|
|
int topy = y + foliageHeight;
|
|
|
|
|
int cury = topy - 1;
|
|
|
|
|
float radius;
|
|
|
|
|
// 4J Stu - Generate foliage from the top down so that we don't keep recalculating heightmaps
|
|
|
|
|
while (cury >= y)
|
|
|
|
|
{
|
|
|
|
|
radius = foliageShape(cury - y);
|
|
|
|
|
@@ -257,11 +189,6 @@ private:
|
|
|
|
|
}
|
|
|
|
|
void limb(int *start, int *end, int material)
|
|
|
|
|
{
|
|
|
|
|
// Create a limb from the start position to the end position.
|
|
|
|
|
// Used for creating the branches and trunk.
|
|
|
|
|
|
|
|
|
|
// Populate delta, the difference between start and end for all three axies.
|
|
|
|
|
// Set primidx to the index with the largest overall distance traveled.
|
|
|
|
|
int delta[] = { 0, 0, 0 };
|
|
|
|
|
byte idx = 0;
|
|
|
|
|
byte primidx = 0;
|
|
|
|
|
@@ -274,22 +201,15 @@ private:
|
|
|
|
|
}
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
|
|
|
|
// If the largest distance is zero, don't bother to do anything else.
|
|
|
|
|
if (delta[primidx] == 0) return;
|
|
|
|
|
// set up the other two axis indices.
|
|
|
|
|
byte secidx1 = axisConversionArray[primidx];
|
|
|
|
|
byte secidx2 = axisConversionArray[primidx + 3];
|
|
|
|
|
// primsign is digit 1 or -1 depending on whether the limb is headed
|
|
|
|
|
// along the positive or negative primidx axis.
|
|
|
|
|
char primsign;
|
|
|
|
|
if (delta[primidx] > 0) primsign = 1;
|
|
|
|
|
else primsign = -1;
|
|
|
|
|
// Initilize the per-step movement for the non-primary axies.
|
|
|
|
|
double secfac1 = ((double) delta[secidx1]) / ((double) delta[primidx]);
|
|
|
|
|
double secfac2 = ((double) delta[secidx2]) / ((double) delta[primidx]);
|
|
|
|
|
// Initialize the coordinates.
|
|
|
|
|
int coordinate[] = { 0, 0, 0 };
|
|
|
|
|
// Loop through each crossection along the primary axis, from start to end
|
|
|
|
|
int primoffset = 0;
|
|
|
|
|
int endoffset = delta[primidx] + primsign;
|
|
|
|
|
while (primoffset != endoffset)
|
|
|
|
|
@@ -319,8 +239,6 @@ private:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void makeFoliage(){
|
|
|
|
|
// Create the tree foliage.
|
|
|
|
|
// Call foliageCluster at the correct locations
|
|
|
|
|
int idx = 0;
|
|
|
|
|
int finish = foliageCoordsLength;
|
|
|
|
|
while (idx < finish)
|
|
|
|
|
@@ -333,18 +251,10 @@ private:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool trimBranches(int localY){
|
|
|
|
|
// For larger trees, randomly "prune" the branches so there
|
|
|
|
|
// aren't too many.
|
|
|
|
|
// Return true if the branch should be created.
|
|
|
|
|
// This method is intended for overriding in child classes, allowing
|
|
|
|
|
// decent amounts of branches on very large trees.
|
|
|
|
|
// Can also be used to disable branches on some tree types, or
|
|
|
|
|
// make branches more sparse.
|
|
|
|
|
if (localY < (height * 0.2)) return false;
|
|
|
|
|
else return true;
|
|
|
|
|
}
|
|
|
|
|
void makeTrunk(){
|
|
|
|
|
// Create the trunk of the tree.
|
|
|
|
|
int x = origin[0];
|
|
|
|
|
int startY = origin[1];
|
|
|
|
|
int topY = origin[1] + trunkHeight;
|
|
|
|
|
@@ -366,9 +276,6 @@ private:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void makeBranches(){
|
|
|
|
|
// Create the tree branches.
|
|
|
|
|
// Call trimBranches for each branch to see if you should create it.
|
|
|
|
|
// Call taperedLimb to the correct locations
|
|
|
|
|
int idx = 0;
|
|
|
|
|
int finish = foliageCoordsLength;
|
|
|
|
|
int baseCoord[] = { origin[0], origin[1], origin[2] };
|
|
|
|
|
@@ -386,12 +293,6 @@ private:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int checkLine(int *start, int *end){
|
|
|
|
|
// Check from coordinates start to end (both inclusive) for blocks other than air and foliage
|
|
|
|
|
// If a block other than air and foliage is found, return the number of steps taken.
|
|
|
|
|
// If no block other than air and foliage is found, return -1.
|
|
|
|
|
// Examples:
|
|
|
|
|
// If the third block searched is stone, return 2
|
|
|
|
|
// If the first block searched is lava, return 0
|
|
|
|
|
|
|
|
|
|
int delta[] = { 0, 0, 0 };
|
|
|
|
|
byte idx = 0;
|
|
|
|
|
@@ -405,22 +306,15 @@ private:
|
|
|
|
|
}
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
|
|
|
|
// If the largest distance is zero, don't bother to do anything else.
|
|
|
|
|
if (delta[primidx] == 0) return -1;
|
|
|
|
|
// set up the other two axis indices.
|
|
|
|
|
byte secidx1 = axisConversionArray[primidx];
|
|
|
|
|
byte secidx2 = axisConversionArray[primidx + 3];
|
|
|
|
|
// primsign is digit 1 or -1 depending on whether the limb is headed
|
|
|
|
|
// along the positive or negative primidx axis.
|
|
|
|
|
char primsign; // 4J Stu - Was byte, but we use in a sum below and byte=unsigned char so we were setting endoffset incorrectly
|
|
|
|
|
char primsign;
|
|
|
|
|
if (delta[primidx] > 0) primsign = 1;
|
|
|
|
|
else primsign = -1;
|
|
|
|
|
// Initilize the per-step movement for the non-primary axies.
|
|
|
|
|
double secfac1 = ((double) delta[secidx1]) / ((double) delta[primidx]);
|
|
|
|
|
double secfac2 = ((double) delta[secidx2]) / ((double) delta[primidx]);
|
|
|
|
|
// Initialize the coordinates.
|
|
|
|
|
int coordinate[] = { 0, 0, 0 };
|
|
|
|
|
// Loop through each crossection along the primary axis, from start to end
|
|
|
|
|
int primoffset = 0;
|
|
|
|
|
int endoffset = delta[primidx] + primsign;
|
|
|
|
|
int thismat;
|
|
|
|
|
@@ -432,60 +326,46 @@ private:
|
|
|
|
|
thismat = thisLevel->getTile(coordinate[0], coordinate[1], coordinate[2]);
|
|
|
|
|
if (!((thismat == 0) || (thismat == Tile::leaves->id)))
|
|
|
|
|
{
|
|
|
|
|
// If the material of the checked block is anything other than
|
|
|
|
|
// air or foliage, stop looking.
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
primoffset += primsign;
|
|
|
|
|
}
|
|
|
|
|
// If you reached the end without finding anything, return -1.
|
|
|
|
|
|
|
|
|
|
if (primoffset == endoffset)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
// Otherwise, return the number of steps you took.
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return abs(primoffset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool checkLocation(){
|
|
|
|
|
// Return true if the tree can be placed here.
|
|
|
|
|
// Return false if the tree can not be placed here.
|
|
|
|
|
|
|
|
|
|
// Examine the square under the trunk. Is it grass or dirt?
|
|
|
|
|
// If not, return false
|
|
|
|
|
// Examine center column for how tall the tree can be.
|
|
|
|
|
// If the checked height is shorter than height, but taller
|
|
|
|
|
// than 4, set the tree to the maximum height allowed.
|
|
|
|
|
// If the space is too short, return false.
|
|
|
|
|
int startPosition[] = { origin[0], origin[1], origin[2] };
|
|
|
|
|
int endPosition[] = { origin[0], origin[1] + height - 1, origin[2] };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check the location it is resting on
|
|
|
|
|
int baseMaterial = thisLevel->getTile(origin[0], origin[1] - 1, origin[2]);
|
|
|
|
|
if (!((baseMaterial == 2) || (baseMaterial == 3)))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int allowedHeight = checkLine(startPosition, endPosition);
|
|
|
|
|
// If the set height is good, go with that
|
|
|
|
|
if (allowedHeight == -1)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// If the space is too short, tell the build to abort
|
|
|
|
|
|
|
|
|
|
else if (allowedHeight < 6)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// If the space is shorter than the set height, but not too short
|
|
|
|
|
// shorten the height, and tell the build to continue
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
height = allowedHeight;
|
|
|
|
|
//System.out.println("Shortened the tree");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -502,9 +382,7 @@ public:
|
|
|
|
|
origin[0] = 0;
|
|
|
|
|
origin[1] = 0;
|
|
|
|
|
origin[2] = 0;
|
|
|
|
|
// Field to hold the tree height.
|
|
|
|
|
height = 0;
|
|
|
|
|
// Other important tree information.
|
|
|
|
|
trunkHeight = 0;
|
|
|
|
|
trunkHeightScale = 0.618;
|
|
|
|
|
branchDensity = 1.0;
|
|
|
|
|
@@ -528,65 +406,40 @@ public:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void init(double heightInit, double widthInit, double foliageDensityInit){
|
|
|
|
|
// all of the parameters should be from 0.0 to 1.0
|
|
|
|
|
// heightInit scales the maximum overall height of the tree (still randomizes height within the possible range)
|
|
|
|
|
// widthInit scales the maximum overall width of the tree (keep this above 0.3 or so)
|
|
|
|
|
// foliageDensityInit scales how many foliage clusters are created.
|
|
|
|
|
//
|
|
|
|
|
// Note, you can call "place" without calling "init".
|
|
|
|
|
// This is the same as calling init(1.0,1.0,1.0) and then calling place.
|
|
|
|
|
|
|
|
|
|
heightVariance = (int) (heightInit * 12);
|
|
|
|
|
if (heightInit > 0.5) foliageHeight = 5;
|
|
|
|
|
widthScale = widthInit;
|
|
|
|
|
foliageDensity = foliageDensityInit;
|
|
|
|
|
}
|
|
|
|
|
virtual bool place(Level *level, Random *random, int x, int y, int z){
|
|
|
|
|
// Note to Markus.
|
|
|
|
|
// currently the following fields are set randomly. If you like, make them
|
|
|
|
|
// parameters passed into "place".
|
|
|
|
|
//
|
|
|
|
|
// height: so the map generator can intelligently set the height of the tree,
|
|
|
|
|
// and make forests with large trees in the middle and smaller ones on the edges.
|
|
|
|
|
|
|
|
|
|
// Initialize the instance fields for the level and the seed.
|
|
|
|
|
thisLevel = level;
|
|
|
|
|
__int64 seed = random->nextLong();
|
|
|
|
|
rnd->setSeed(seed);
|
|
|
|
|
// Initialize the origin of the tree trunk
|
|
|
|
|
origin[0] = x;
|
|
|
|
|
origin[1] = y;
|
|
|
|
|
origin[2] = z;
|
|
|
|
|
// Sets the height. Take out this line if height is passed as a parameter
|
|
|
|
|
if (height == 0)
|
|
|
|
|
{
|
|
|
|
|
height = 5 + rnd->nextInt(heightVariance);
|
|
|
|
|
}
|
|
|
|
|
if (!(checkLocation()))
|
|
|
|
|
{
|
|
|
|
|
//System.out.println("Tree location failed");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//System.out.println("The height is");
|
|
|
|
|
//System.out.println(height);
|
|
|
|
|
//System.out.println("Trunk Height check done");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prepare();
|
|
|
|
|
|
|
|
|
|
//System.out.println("Prepare done");
|
|
|
|
|
|
|
|
|
|
makeFoliage();
|
|
|
|
|
|
|
|
|
|
//System.out.println("Foliage done");
|
|
|
|
|
|
|
|
|
|
makeTrunk();
|
|
|
|
|
|
|
|
|
|
//System.out.println("Trunk done");
|
|
|
|
|
|
|
|
|
|
makeBranches();
|
|
|
|
|
|
|
|
|
|
//System.out.println("Branches done");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|