/* * Copyright 2015 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.core.world.generator.trees; import java.util.Map; import org.terasology.math.LSystemRule; import org.terasology.math.TeraMath; import org.terasology.math.geom.Matrix4f; import org.terasology.math.geom.Quat4f; import org.terasology.math.geom.Vector3f; import org.terasology.utilities.collection.CharSequenceIterator; import org.terasology.utilities.random.Random; import org.terasology.world.block.Block; import org.terasology.world.chunks.CoreChunk; /** * Encapsulates the recursive algorithm for the generation of trees */ public class RecursiveTreeGeneratorLSystem { private int maxDepth; private float angle; private Map<Character, LSystemRule> ruleSet; public RecursiveTreeGeneratorLSystem(int maxDepth, float angle, Map<Character, LSystemRule> ruleSet) { this.angle = angle; this.maxDepth = maxDepth; this.ruleSet = ruleSet; } public void recurse(CoreChunk view, Random rand, int posX, int posY, int posZ, float angleOffset, CharSequenceIterator axiomIterator, Vector3f position, Matrix4f rotation, Block bark, Block leaf, int depth, AbstractTreeGenerator treeGenerator) { Matrix4f tempRotation = new Matrix4f(); while (axiomIterator.hasNext()) { char c = axiomIterator.nextChar(); switch (c) { case 'G': case 'F': // Tree trunk treeGenerator.safelySetBlock(view, posX + (int) position.x + 1, posY + (int) position.y, posZ + (int) position.z, bark); treeGenerator.safelySetBlock(view, posX + (int) position.x - 1, posY + (int) position.y, posZ + (int) position.z, bark); treeGenerator.safelySetBlock(view, posX + (int) position.x, posY + (int) position.y, posZ + (int) position.z + 1, bark); treeGenerator.safelySetBlock(view, posX + (int) position.x, posY + (int) position.y, posZ + (int) position.z - 1, bark); // Generate leaves if (depth > 1) { int size = 1; for (int x = -size; x <= size; x++) { for (int y = -size; y <= size; y++) { for (int z = -size; z <= size; z++) { if (Math.abs(x) == size && Math.abs(y) == size && Math.abs(z) == size) { continue; } treeGenerator.safelySetBlock(view, posX + (int) position.x + x + 1, posY + (int) position.y + y, posZ + z + (int) position.z, leaf); treeGenerator.safelySetBlock(view, posX + (int) position.x + x - 1, posY + (int) position.y + y, posZ + z + (int) position.z, leaf); treeGenerator.safelySetBlock(view, posX + (int) position.x + x, posY + (int) position.y + y, posZ + z + (int) position.z + 1, leaf); treeGenerator.safelySetBlock(view, posX + (int) position.x + x, posY + (int) position.y + y, posZ + z + (int) position.z - 1, leaf); } } } } Vector3f dir = new Vector3f(1f, 0f, 0f); rotation.transformVector(dir); position.add(dir); break; case '[': recurse(view, rand, posX, posY, posZ, angleOffset, axiomIterator, new Vector3f(position), new Matrix4f(rotation), bark, leaf, depth, treeGenerator); break; case ']': return; case '+': tempRotation = new Matrix4f(new Quat4f(new Vector3f(0f, 0f, 1f), angle + angleOffset), Vector3f.ZERO, 1.0f); rotation.mul(tempRotation); break; case '-': tempRotation = new Matrix4f(new Quat4f(new Vector3f(0f, 0f, -1f), angle + angleOffset), Vector3f.ZERO, 1.0f); rotation.mul(tempRotation); break; case '&': tempRotation = new Matrix4f(new Quat4f(new Vector3f(0f, 1f, 0f), angle + angleOffset), Vector3f.ZERO, 1.0f); rotation.mul(tempRotation); break; case '^': tempRotation = new Matrix4f(new Quat4f(new Vector3f(0f, -1f, 0f), angle + angleOffset), Vector3f.ZERO, 1.0f); rotation.mul(tempRotation); break; case '*': tempRotation = new Matrix4f(new Quat4f(new Vector3f(1f, 0f, 0f), angle), Vector3f.ZERO, 1.0f); rotation.mul(tempRotation); break; case '/': tempRotation = new Matrix4f(new Quat4f(new Vector3f(-1f, 0f, 0f), angle), Vector3f.ZERO, 1.0f); rotation.mul(tempRotation); break; default: // If we have already reached the maximum depth, don't ever bother to lookup in the map if (depth == maxDepth - 1) { break; } LSystemRule rule = ruleSet.get(c); if (rule == null) { break; } float weightedFailureProbability = TeraMath.pow(1f - rule.getProbability(), maxDepth - depth); if (rand.nextFloat() < weightedFailureProbability) { break; } recurse(view, rand, posX, posY, posZ, angleOffset, new CharSequenceIterator(rule.getAxiom()), position, rotation, bark, leaf, depth + 1, treeGenerator); } } } }