• API
• FAQ
• Tools
• Trends
• Archive
SHARE
TWEET

# Untitled

a guest Sep 13th, 2013 6,259 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
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;
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. }
RAW Paste Data
Top