Advertisement
Guest User

Untitled

a guest
Sep 13th, 2013
7,812
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 21.06 KB | None | 0 0
  1. package net.minecraft.world.level.levelgen.feature;
  2.  
  3. import java.util.Random;
  4.  
  5. import net.minecraft.util.Mth;
  6. import net.minecraft.world.level.Level;
  7. import net.minecraft.world.level.tile.LogTile;
  8. import net.minecraft.world.level.tile.Tile;
  9. import net.minecraft.world.level.tile.Tiles;
  10.  
  11. public class BasicTree extends AbstractTreeFeature {
  12.  
  13.     // The axisConversionArray, when given a primary index, allows easy
  14.     // access to the indices of the other two axies. Access the data at the
  15.     // primary index location to get the horizontal secondary axis.
  16.     // Access the data at the primary location plus three to get the
  17.     // remaining, tertiary, axis.
  18.     // All directions are specified by an index, 0, 1, or 2 which
  19.     // correspond to x, y, and z.
  20.     // The axisConversionArray is used in several places
  21.     // notably the crossection and taperedLimb methods.
  22.     // Example:
  23.     // If the primary axis is z, then the primary index is 2.
  24.     // The secondary index is axisConversionArray[2] which is 0,
  25.     // the index for the x axis.
  26.     // The remaining axis is axisConversionArray[2 + 3] which is 1,
  27.     // the index for the y axis.
  28.     // Using this method, the secondary axis will always be horizontal (x or z),
  29.     // and the tertiary always vertical (y), if possible.
  30.     static final byte[] axisConversionArray = {
  31.             2, 0, 0, 1, 2, 1
  32.     };
  33.  
  34.     // Set up the pseudorandom number generator
  35.     Random rnd = new Random();
  36.  
  37.     // Make fields to hold the level data and the random seed
  38.     Level thisLevel;
  39.  
  40.     // Field to hold the tree origin, x y and z.
  41.     int[] origin = {
  42.             0, 0, 0
  43.     };
  44.     // Field to hold the tree height.
  45.     int height;
  46.     // Other important tree information.
  47.     int trunkHeight;
  48.     double trunkHeightScale = 0.618;
  49.     double branchDensity = 1.0;
  50.     double branchSlope = 0.381;
  51.     double widthScale = 1.0;
  52.     double foliageDensity = 1.0;
  53.     int trunkWidth = 1;
  54.     int heightVariance = 12;
  55.     int foliageHeight = 4;
  56.     // The foliage coordinates are a list of [x,y,z,y of branch base] values for each cluster
  57.     int[][] foliageCoords;
  58.  
  59.     public BasicTree(boolean doUpdate) {
  60.         super(doUpdate);
  61.     }
  62.  
  63.     void prepare() {
  64.         // Initialize the instance variables.
  65.         // Populate the list of foliage cluster locations.
  66.         // Designed to be overridden in child classes to change basic
  67.         // tree properties (trunk width, branch angle, foliage density, etc..).
  68.         trunkHeight = (int) (height * trunkHeightScale);
  69.         if (trunkHeight >= height) trunkHeight = height - 1;
  70.         int clustersPerY = (int) (1.382 + Math.pow(foliageDensity * height / 13.0, 2));
  71.         if (clustersPerY < 1) clustersPerY = 1;
  72.         // The foliage coordinates are a list of [x,y,z,y of branch base]
  73.         // values for each cluster
  74.         int[][] tempFoliageCoords = new int[clustersPerY * height][4];
  75.         int y = origin[1] + height - foliageHeight;
  76.         int clusterCount = 1;
  77.         int trunkTop = origin[1] + trunkHeight;
  78.         int relativeY = y - origin[1];
  79.         tempFoliageCoords[0][0] = origin[0];
  80.         tempFoliageCoords[0][1] = y;
  81.         tempFoliageCoords[0][2] = origin[2];
  82.         tempFoliageCoords[0][3] = trunkTop;
  83.         y--;
  84.  
  85.         while (relativeY >= 0) {
  86.             int num = 0;
  87.  
  88.             float shapefac = treeShape(relativeY);
  89.             if (shapefac < 0) {
  90.                 y--;
  91.                 relativeY--;
  92.                 continue;
  93.             }
  94.  
  95.             // The originOffset is to put the value in the middle of the block.
  96.             double originOffset = 0.5;
  97.             while (num < clustersPerY) {
  98.                 double radius = widthScale * (shapefac * (rnd.nextFloat() + 0.328));
  99.                 double angle = rnd.nextFloat() * 2.0 * 3.14159;
  100.                 int x = Mth.floor(radius * Math.sin(angle) + origin[0] + originOffset);
  101.                 int z = Mth.floor(radius * Math.cos(angle) + origin[2] + originOffset);
  102.                 int[] checkStart = {
  103.                         x, y, z
  104.                 };
  105.                 int[] checkEnd = {
  106.                         x, y + foliageHeight, z
  107.                 };
  108.                 // check the center column of the cluster for obstructions.
  109.                 if (checkLine(checkStart, checkEnd) == -1) {
  110.                     // If the cluster can be created, check the branch path
  111.                     // for obstructions.
  112.                     int[] checkBranchBase = {
  113.                             origin[0], origin[1], origin[2]
  114.                     };
  115.                     double distance = Math.sqrt(Math.pow(Math.abs(origin[0] - checkStart[0]), 2) + Math.pow(Math.abs(origin[2] - checkStart[2]), 2));
  116.                     double branchHeight = distance * branchSlope;
  117.                     if ((checkStart[1] - branchHeight) > trunkTop) {
  118.                         checkBranchBase[1] = trunkTop;
  119.  
  120.                     } else {
  121.                         checkBranchBase[1] = (int) (checkStart[1] - branchHeight);
  122.                     }
  123.                     // Now check the branch path
  124.                     if (checkLine(checkBranchBase, checkStart) == -1) {
  125.                         // If the branch path is clear, add the position to the
  126.                         // list of foliage positions
  127.                         tempFoliageCoords[clusterCount][0] = x;
  128.                         tempFoliageCoords[clusterCount][1] = y;
  129.                         tempFoliageCoords[clusterCount][2] = z;
  130.                         tempFoliageCoords[clusterCount][3] = checkBranchBase[1];
  131.                         clusterCount++;
  132.                     }
  133.                 }
  134.                 num++;
  135.             }
  136.             y--;
  137.             relativeY--;
  138.         }
  139.         foliageCoords = new int[clusterCount][4];
  140.         System.arraycopy(tempFoliageCoords, 0, foliageCoords, 0, clusterCount);
  141.  
  142.     }
  143.  
  144.     void crossection(int x, int y, int z, float radius, byte direction, Tile material) {
  145.         // Create a circular cross section.
  146.         //
  147.         // Used to nearly everything in the foliage, branches, and trunk.
  148.         // This is a good target for performance optimization.
  149.  
  150.         // Passed values:
  151.         // x,y,z is the center location of the cross section
  152.         // radius is the radius of the section from the center
  153.         // direction is the direction the cross section is pointed, 0 for x, 1
  154.         // for y, 2 for z material is the index number for the material to use
  155.         int rad = (int) (radius + 0.618);
  156.         byte secidx1 = axisConversionArray[direction];
  157.         byte secidx2 = axisConversionArray[direction + 3];
  158.         int[] center = {
  159.                 x, y, z
  160.         };
  161.         int[] position = {
  162.                 0, 0, 0
  163.         };
  164.         int offset1 = -rad;
  165.         int offset2 = -rad;
  166.         Tile thisMat;
  167.         position[direction] = center[direction];
  168.         while (offset1 <= rad) {
  169.             position[secidx1] = center[secidx1] + offset1;
  170.             offset2 = -rad;
  171.             while (offset2 <= rad) {
  172.                 double thisdistance = Math.pow(Math.abs(offset1) + 0.5, 2) + Math.pow(Math.abs(offset2) + 0.5, 2);
  173.                 if (thisdistance > radius * radius) {
  174.                     offset2++;
  175.                     continue;
  176.                 }
  177.                 position[secidx2] = center[secidx2] + offset2;
  178.                 thisMat = thisLevel.getTile(position[0], position[1], position[2]);
  179.                 if (!(thisMat == null || thisMat == Tiles.LEAVES)) {
  180.                     // If the material of the checked block is anything other
  181.                     // than air or foliage, skip this tile.
  182.                     offset2++;
  183.                     continue;
  184.                 }
  185.                 placeBlock(thisLevel, position[0], position[1], position[2], material, 0);
  186.                 offset2++;
  187.             }
  188.             offset1++;
  189.         }
  190.  
  191.     }
  192.  
  193.     float treeShape(int y) {
  194.         // Take the y position relative to the base of the tree.
  195.         // Return the distance the foliage should be from the trunk axis.
  196.         // Return a negative number if foliage should not be created at this
  197.         // height.  This method is intended for overriding in child classes,
  198.         // allowing different shaped trees.  This method should return a
  199.         // consistent value for each y (don't randomize).
  200.         if (y < (((float) height) * 0.3)) return (float) -1.618;
  201.         float radius = ((float) height) / ((float) 2.0);
  202.         float adjacent = (((float) height) / ((float) 2.0)) - y;
  203.         float distance;
  204.         if (adjacent == 0) distance = radius;
  205.         else if (Math.abs(adjacent) >= radius) distance = (float) 0.0;
  206.         else distance = (float) Math.sqrt(Math.pow(Math.abs(radius), 2) - Math.pow(Math.abs(adjacent), 2));
  207.         // Alter this factor to change the overall width of the tree.
  208.         distance *= (float) 0.5;
  209.         return distance;
  210.     }
  211.  
  212.     float foliageShape(int y) {
  213.         // Take the y position relative to the base of the foliage cluster.
  214.         // Return the radius of the cluster at this y
  215.         // Return a negative number if no foliage should be created at this
  216.         // level this method is intended for overriding in child classes,
  217.         // allowing foliage of different sizes and shapes.
  218.         if ((y < 0) || (y >= foliageHeight)) return (float) -1;
  219.         else if ((y == 0) || (y == (foliageHeight - 1))) return (float) 2;
  220.         else return (float) 3;
  221.     }
  222.  
  223.     void foliageCluster(int x, int y, int z) {
  224.         // Generate a cluster of foliage, with the base at x, y, z.
  225.         // The shape of the cluster is derived from foliageShape
  226.         // crossection is called to make each level.
  227.         int cury = y;
  228.         int topy = y + foliageHeight;
  229.         float radius;
  230.         while (cury < topy) {
  231.             radius = foliageShape(cury - y);
  232.             crossection(x, cury, z, radius, (byte) 1, Tiles.LEAVES);
  233.             cury++;
  234.         }
  235.     }
  236.  
  237.     void limb(int[] start, int[] end, Tile material) {
  238.         // Create a limb from the start position to the end position.
  239.         // Used for creating the branches and trunk.
  240.  
  241.         // Populate delta, the difference between start and end for all three
  242.         // axies.  Set primidx to the index with the largest overall distance
  243.         // traveled.
  244.         int[] delta = {
  245.                 0, 0, 0
  246.         };
  247.         byte idx = 0;
  248.         byte primidx = 0;
  249.         while (idx < 3) {
  250.             delta[idx] = end[idx] - start[idx];
  251.             if (Math.abs(delta[idx]) > Math.abs(delta[primidx])) {
  252.                 primidx = idx;
  253.             }
  254.             idx++;
  255.         }
  256.         // If the largest distance is zero, don't bother to do anything else.
  257.         if (delta[primidx] == 0) return;
  258.         // set up the other two axis indices.
  259.         byte secidx1 = axisConversionArray[primidx];
  260.         byte secidx2 = axisConversionArray[primidx + 3];
  261.         // primsign is digit 1 or -1 depending on whether the limb is headed
  262.         // along the positive or negative primidx axis.
  263.         byte primsign;
  264.         if (delta[primidx] > 0) primsign = 1;
  265.         else primsign = -1;
  266.         // Initilize the per-step movement for the non-primary axies.
  267.         double secfac1 = ((double) delta[secidx1]) / ((double) delta[primidx]);
  268.         double secfac2 = ((double) delta[secidx2]) / ((double) delta[primidx]);
  269.         // Initialize the coordinates.
  270.         int[] coordinate = {
  271.                 0, 0, 0
  272.         };
  273.         // Loop through each crossection along the primary axis, from start to end
  274.         int primoffset = 0;
  275.         int endoffset = delta[primidx] + primsign;
  276.         while (primoffset != endoffset) {
  277.             coordinate[primidx] = Mth.floor(start[primidx] + primoffset + 0.5);
  278.             coordinate[secidx1] = Mth.floor(start[secidx1] + (primoffset * secfac1) + 0.5);
  279.             coordinate[secidx2] = Mth.floor(start[secidx2] + (primoffset * secfac2) + 0.5);
  280.  
  281.             int dir = LogTile.FACING_Y;
  282.             int xdiff = Math.abs(coordinate[0] - start[0]);
  283.             int zdiff = Math.abs(coordinate[2] - start[2]);
  284.             int maxdiff = Math.max(xdiff, zdiff);
  285.  
  286.             if (maxdiff > 0) {
  287.                 if (xdiff == maxdiff) {
  288.                     dir = LogTile.FACING_X;
  289.                 } else if (zdiff == maxdiff) {
  290.                     dir = LogTile.FACING_Z;
  291.                 }
  292.             }
  293.  
  294.             placeBlock(thisLevel, coordinate[0], coordinate[1], coordinate[2], material, dir);
  295.             primoffset += primsign;
  296.         }
  297.  
  298.     }
  299.  
  300.     void makeFoliage() {
  301.         // Create the tree foliage.
  302.         // Call foliageCluster at the correct locations
  303.         int idx = 0;
  304.         int finish = foliageCoords.length;
  305.         while (idx < finish) {
  306.             int x = foliageCoords[idx][0];
  307.             int y = foliageCoords[idx][1];
  308.             int z = foliageCoords[idx][2];
  309.             foliageCluster(x, y, z);
  310.             idx++;
  311.         }
  312.     }
  313.  
  314.     boolean trimBranches(int localY) {
  315.         // For larger trees, randomly "prune" the branches so there
  316.         // aren't too many.
  317.         // Return true if the branch should be created.
  318.         // This method is intended for overriding in child classes, allowing
  319.         // decent amounts of branches on very large trees.
  320.         // Can also be used to disable branches on some tree types, or
  321.         // make branches more sparse.
  322.         if (localY < (height * 0.2)) return false;
  323.         else return true;
  324.     }
  325.  
  326.     void makeTrunk() {
  327.         // Create the trunk of the tree.
  328.         int x = origin[0];
  329.         int startY = origin[1];
  330.         int topY = origin[1] + trunkHeight;
  331.         int z = origin[2];
  332.         int[] startCoord = {
  333.                 x, startY, z
  334.         };
  335.         int[] endCoord = {
  336.                 x, topY, z
  337.         };
  338.         limb(startCoord, endCoord, Tiles.LOG);
  339.         if (trunkWidth == 2) {
  340.             startCoord[0] += 1;
  341.             endCoord[0] += 1;
  342.             limb(startCoord, endCoord, Tiles.LOG);
  343.             startCoord[2] += 1;
  344.             endCoord[2] += 1;
  345.             limb(startCoord, endCoord, Tiles.LOG);
  346.             startCoord[0] += -1;
  347.             endCoord[0] += -1;
  348.             limb(startCoord, endCoord, Tiles.LOG);
  349.         }
  350.     }
  351.  
  352.     void makeBranches() {
  353.         // Create the tree branches.
  354.         // Call trimBranches for each branch to see if you should create it.
  355.         // Call taperedLimb to the correct locations
  356.         int idx = 0;
  357.         int finish = foliageCoords.length;
  358.         int[] baseCoord = {
  359.                 origin[0], origin[1], origin[2]
  360.         };
  361.         while (idx < finish) {
  362.             int[] coordValues = foliageCoords[idx];
  363.             int[] endCoord = {
  364.                     coordValues[0], coordValues[1], coordValues[2]
  365.             };
  366.             baseCoord[1] = coordValues[3];
  367.             int localY = baseCoord[1] - origin[1];
  368.             if (trimBranches(localY)) {
  369.                 limb(baseCoord, endCoord, Tiles.LOG);
  370.             }
  371.             idx++;
  372.         }
  373.     }
  374.  
  375.     int checkLine(int[] start, int[] end) {
  376.         // Check from coordinates start to end (both inclusive) for blocks
  377.         // other than air and foliage If a block other than air and foliage is
  378.         // found, return the number of steps taken.
  379.         // If no block other than air and foliage is found, return -1.
  380.         // Examples:
  381.         // If the third block searched is stone, return 2
  382.         // If the first block searched is lava, return 0
  383.  
  384.         int[] delta = {
  385.                 0, 0, 0
  386.         };
  387.         byte idx = 0;
  388.         byte primidx = 0;
  389.         while (idx < 3) {
  390.             delta[idx] = end[idx] - start[idx];
  391.             if (Math.abs(delta[idx]) > Math.abs(delta[primidx])) {
  392.                 primidx = idx;
  393.             }
  394.             idx++;
  395.         }
  396.         // If the largest distance is zero, don't bother to do anything else.
  397.         if (delta[primidx] == 0) return -1;
  398.         // set up the other two axis indices.
  399.         byte secidx1 = axisConversionArray[primidx];
  400.         byte secidx2 = axisConversionArray[primidx + 3];
  401.         // primsign is digit 1 or -1 depending on whether the limb is headed
  402.         // along the positive or negative primidx axis.
  403.         byte primsign;
  404.         if (delta[primidx] > 0) primsign = 1;
  405.         else primsign = -1;
  406.         // Initilize the per-step movement for the non-primary axies.
  407.         double secfac1 = ((double) delta[secidx1]) / ((double) delta[primidx]);
  408.         double secfac2 = ((double) delta[secidx2]) / ((double) delta[primidx]);
  409.         // Initialize the coordinates.
  410.         int[] coordinate = {
  411.                 0, 0, 0
  412.         };
  413.         // Loop through each crossection along the primary axis, from start to end
  414.         int primoffset = 0;
  415.         int endoffset = delta[primidx] + primsign;
  416.         Tile thisMat;
  417.         while (primoffset != endoffset) {
  418.             coordinate[primidx] = start[primidx] + primoffset;
  419.             coordinate[secidx1] = Mth.floor(start[secidx1] + (primoffset * secfac1));
  420.             coordinate[secidx2] = Mth.floor(start[secidx2] + (primoffset * secfac2));
  421.             thisMat = thisLevel.getTile(coordinate[0], coordinate[1], coordinate[2]);
  422.             if (!isFree(thisMat)) {
  423.                 // If the material of the checked block is anything other than
  424.                 // air or foliage, stop looking.
  425.                 break;
  426.             }
  427.             primoffset += primsign;
  428.         }
  429.         // If you reached the end without finding anything, return -1.
  430.         if (primoffset == endoffset) {
  431.             return -1;
  432.         }
  433.         // Otherwise, return the number of steps you took.
  434.         else {
  435.             return Math.abs(primoffset);
  436.         }
  437.     }
  438.  
  439.     boolean checkLocation() {
  440.         // Return true if the tree can be placed here.
  441.         // Return false if the tree can not be placed here.
  442.         // Examine the square under the trunk. Is it grass or dirt?
  443.         // If not, return false
  444.         // Examine center column for how tall the tree can be.
  445.         // If the checked height is shorter than height, but taller
  446.         // than 4, set the tree to the maximum height allowed.
  447.         // If the space is too short, return false.
  448.         int[] startPosition = {
  449.                 origin[0], origin[1], origin[2]
  450.         };
  451.         int[] endPosition = {
  452.                 origin[0], origin[1] + height - 1, origin[2]
  453.         };
  454.         // Check the location it is resting on
  455.         final Tile tile = thisLevel.getTile(origin[0], origin[1] - 1, origin[2]);
  456.         if (!(tile == Tiles.DIRT || tile == Tiles.GRASS || tile == Tiles.FARMLAND)) {
  457.             return false;
  458.         }
  459.         int allowedHeight = checkLine(startPosition, endPosition);
  460.         // If the set height is good, go with that
  461.         if (allowedHeight == -1) {
  462.             return true;
  463.         }
  464.         // If the space is too short, tell the build to abort
  465.         else if (allowedHeight < 6) {
  466.             return false;
  467.         }
  468.         // If the space is shorter than the set height, but not too short
  469.         // shorten the height, and tell the build to continue
  470.         else {
  471.             height = allowedHeight;
  472.             return true;
  473.         }
  474.     }
  475.  
  476.     @Override
  477.     public void init(double heightInit, double widthInit, double foliageDensityInit) {
  478.         // all of the parameters should be from 0.0 to 1.0
  479.         // heightInit scales the maximum overall height of the tree (still
  480.         // randomizes height within the possible range) widthInit scales the
  481.         // maximum overall width of the tree (keep this above 0.3 or so)
  482.         // foliageDensityInit scales how many foliage clusters are created.
  483.         //
  484.         // Note, you can call "place" without calling "init".
  485.         // This is the same as calling init(1.0,1.0,1.0) and then calling place.
  486.         heightVariance = (int) (heightInit * 12);
  487.         if (heightInit > 0.5) foliageHeight = 5;
  488.         widthScale = widthInit;
  489.         foliageDensity = foliageDensityInit;
  490.     }
  491.  
  492.     @Override
  493.     public boolean place(Level level, Random random, int x, int y, int z) {
  494.         // Note to Markus.
  495.         // currently the following fields are set randomly. If you like, make
  496.         // them parameters passed into "place".
  497.         //
  498.         // height: so the map generator can intelligently set the height of the
  499.         // tree, and make forests with large trees in the middle and smaller
  500.         // ones on the edges.
  501.  
  502.         // Initialize the instance fields for the level and the seed.
  503.         thisLevel = level;
  504.         long seed = random.nextLong();
  505.         rnd.setSeed(seed);
  506.         // Initialize the origin of the tree trunk
  507.         origin[0] = x;
  508.         origin[1] = y;
  509.         origin[2] = z;
  510.         // Sets the height. Take out this line if height is passed as a parameter
  511.         if (height == 0) {
  512.             height = 5 + rnd.nextInt(heightVariance);
  513.         }
  514.         if (!(checkLocation())) {
  515.             return false;
  516.         }
  517.         prepare();
  518.         makeFoliage();
  519.         makeTrunk();
  520.         makeBranches();
  521.         return true;
  522.     }
  523. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement