private void growBranch(Random rand, int depth, BlockPos pos, float dx, float dy, float dz, float angleVerStart, float angleHorStart, float curWidth, float dieAt, float trunkWidthLoss, bool wideTrunk) { if (depth > 30) { Console.WriteLine("TreeGen.growBranch() aborted, too many branches!"); return; } TreeGenBranch branch = branchesByDepth[Math.Min(depth, branchesByDepth.Count - 1)]; short[] outline = forestFloor.GetOutline(); float widthloss = depth == 0 ? trunkWidthLoss : branch.WidthLoss(rand); float widthlossCurve = branch.widthlossCurve; float branchspacing = branch.branchSpacing.nextFloat(1, rand); float branchstart = branch.branchStart.nextFloat(1, rand); float branchQuantityStart = branch.branchQuantity.nextFloat(1, rand); float branchWidthMulitplierStart = branch.branchWidthMultiplier.nextFloat(1, rand); float reldistance, lastreldistance = 0; float totaldistance = curWidth / widthloss; int iteration = 0; float sequencesPerIteration = 1f / (curWidth / widthloss); float ddrag, angleVer, angleHor; // we want to place around the trunk/branch => offset the coordinates when growing stuff from the base float trunkOffsetX, trunkOffsetZ; BlockPos currentPos = new BlockPos(); float branchQuantity, branchWidth; float sinAngleVer, cosAnglerHor, sinAngleHor; float currentSequence; LCGRandom lcgrand = lcgrandTL.Value; while (curWidth > 0 && iteration++ < 5000) { curWidth -= widthloss; if (widthlossCurve + curWidth / 20 < 1f) { widthloss *= (widthlossCurve + curWidth / 20); } currentSequence = sequencesPerIteration * (iteration - 1); if (curWidth < dieAt) { break; } angleVer = branch.angleVertEvolve.nextFloat(angleVerStart, currentSequence); angleHor = branch.angleHoriEvolve.nextFloat(angleHorStart, currentSequence); sinAngleVer = GameMath.FastSin(angleVer); cosAnglerHor = GameMath.FastCos(angleHor); sinAngleHor = GameMath.FastSin(angleHor); trunkOffsetX = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * sinAngleVer * cosAnglerHor)); trunkOffsetZ = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * sinAngleVer * sinAngleHor)); ddrag = branch.gravityDrag * (float)Math.Sqrt(dx * dx + dz * dz); dx += sinAngleVer * cosAnglerHor / Math.Max(1, Math.Abs(ddrag)); dy += Math.Min(1, Math.Max(-1, GameMath.FastCos(angleVer) - ddrag)); dz += sinAngleVer * sinAngleHor / Math.Max(1, Math.Abs(ddrag)); int blockId = branch.getBlockId(curWidth, config.treeBlocks, this); if (blockId == 0) { return; } currentPos.Set(pos.X + dx, pos.Y + dy, pos.Z + dz); PlaceResumeState state = getPlaceResumeState(currentPos, blockId, wideTrunk); if (state == PlaceResumeState.CanPlace) { api.SetBlock(blockId, currentPos); // Update the canopy outline of the tree for this block position int idz = (int)(dz + 16); int idx = (int)(dx + 16); if (idz > 1 && idz < 31 && idx > 1 && idx < 31) { int canopyIndex = idz * 33 + idx; outline[canopyIndex - 68]++; outline[canopyIndex - 67]++; //bias canopy shading towards the North (- z direction) for sun effects outline[canopyIndex - 66]++; outline[canopyIndex - 65]++; outline[canopyIndex - 64]++; outline[canopyIndex - 35]++; outline[canopyIndex - 34] += 2; outline[canopyIndex - 33] += 2; outline[canopyIndex - 32] += 2; outline[canopyIndex - 31]++; outline[canopyIndex - 2]++; outline[canopyIndex - 1] += 2; outline[canopyIndex + 0] += 3; outline[canopyIndex + 1] += 2; outline[canopyIndex + 2]++; outline[canopyIndex + 33]++; } if (vineGrowthChance > 0 && rand.NextDouble() < vineGrowthChance && config.treeBlocks.vinesBlock != null) { BlockFacing facing = BlockFacing.HORIZONTALS[rand.Next(4)]; BlockPos vinePos = currentPos.AddCopy(facing); float cnt = 1 + rand.Next(11) * (vineGrowthChance + 0.2f); while (api.GetBlockId(vinePos) == 0 && cnt-- > 0) { Block block = config.treeBlocks.vinesBlock; if (cnt <= 0 && config.treeBlocks.vinesEndBlock != null) { block = config.treeBlocks.vinesEndBlock; } block.TryPlaceBlockForWorldGen(api, vinePos, facing, lcgrand); vinePos.Down(); } } } else { if (state == PlaceResumeState.Stop) { return; } } reldistance = (float)Math.Sqrt(dx * dx + dy * dy + dz * dz) / totaldistance; if (reldistance < branchstart) { continue; } if (reldistance > lastreldistance + branchspacing * (1f - reldistance)) { branchspacing = branch.branchSpacing.nextFloat(1, rand); lastreldistance = reldistance; if (branch.branchQuantityEvolve != null) { branchQuantity = branch.branchQuantityEvolve.nextFloat(branchQuantityStart, currentSequence); } else { branchQuantity = branch.branchQuantity.nextFloat(1, rand); } float prevHorAngle = 0f; float horAngle; float minHorangleDist = Math.Min(GameMath.PI / 5, branch.branchHorizontalAngle.var / 5); bool first = true; while (branchQuantity-- > 0) { if (branchQuantity < 1 && rand.NextDouble() < branchQuantity) { break; } curWidth *= branch.branchWidthLossMul; horAngle = angleHor + branch.branchHorizontalAngle.nextFloat(1, rand); int tries = 10; while (!first && Math.Abs(horAngle - prevHorAngle) < minHorangleDist && tries-- > 0) { float newAngle = angleHor + branch.branchHorizontalAngle.nextFloat(1, rand); if (Math.Abs(horAngle - prevHorAngle) < Math.Abs(newAngle - prevHorAngle)) { horAngle = newAngle; } } if (branch.branchWidthMultiplierEvolve != null) { branchWidth = curWidth * branch.branchWidthMultiplierEvolve.nextFloat(branchWidthMulitplierStart, currentSequence); } else { branchWidth = branch.branchWidthMultiplier.nextFloat(curWidth, rand); } growBranch( rand, depth + 1, pos, dx + trunkOffsetX, dy, dz + trunkOffsetZ, branch.branchVerticalAngle.nextFloat(1, rand), horAngle, branchWidth, Math.Max(0, branch.dieAt.nextFloat(1, rand)), trunkWidthLoss, false ); first = false; prevHorAngle = angleHor + horAngle; } } } }
private void growBranch(int depth, BlockPos pos, float dx, float dy, float dz, float angleVerStart, float angleHorStart, float curWidth, float dieAt) { if (depth > 30) { Console.WriteLine("TreeGen.growBranch() aborted, too many branches!"); return; } TreeGenBranch branch = branchesByDepth[Math.Min(depth, branchesByDepth.Count - 1)]; float branchspacing = branch.branchSpacing.nextFloat(); float branchstart = branch.branchStart.nextFloat(); float branchQuantityStart = branch.branchQuantity.nextFloat(); float branchWidthMulitplierStart = branch.branchWidthMultiplier.nextFloat(); float reldistance = 0, lastreldistance = 0; float totaldistance = curWidth / branch.widthloss; int iteration = 0; float sequencesPerIteration = 1f / (curWidth / branch.widthloss); float ddrag = 0, angleVer = 0, angleHor = 0; // we want to place around the trunk/branch => offset the coordinates when growing stuff from the base float trunkOffsetX = 0, trunkOffsetZ = 0, trunkOffsetY = 0; BlockPos currentPos; float branchQuantity, branchWidth; float sinAngleVer, cosAnglerHor, sinAngleHor; float currentSequence; while (curWidth > 0 && iteration++ < 5000) { curWidth -= branch.widthloss; currentSequence = sequencesPerIteration * (iteration - 1); if (curWidth < dieAt) { break; } angleVer = branch.angleVertEvolve.nextFloat(angleVerStart, currentSequence); angleHor = branch.angleHoriEvolve.nextFloat(angleHorStart, currentSequence); sinAngleVer = GameMath.FastSin(angleVer); cosAnglerHor = GameMath.FastCos(angleHor); sinAngleHor = GameMath.FastSin(angleHor); trunkOffsetX = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * sinAngleVer * cosAnglerHor)); trunkOffsetY = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * cosAnglerHor)) + 0.5f; trunkOffsetZ = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * sinAngleVer * sinAngleHor)); ddrag = branch.gravityDrag * GameMath.FastSqrt(dx * dx + dz * dz); dx += sinAngleVer * cosAnglerHor / Math.Max(1, Math.Abs(ddrag)); dy += Math.Min(1, Math.Max(-1, GameMath.FastCos(angleVer) - ddrag)); dz += sinAngleVer * sinAngleHor / Math.Max(1, Math.Abs(ddrag)); ushort blockId = getBlockId(curWidth); if (blockId == 0) { return; } currentPos = pos.AddCopy(dx, dy, dz); if (canPlace(currentPos, blockId)) { api.SetBlock(blockId, currentPos); if (vineGrowthChance > 0 && rand.NextDouble() < vineGrowthChance && config.treeBlocks.vinesBlock != null) { BlockFacing facing = BlockFacing.HORIZONTALS[rand.Next(4)]; BlockPos vinePos = currentPos.AddCopy(facing); float cnt = 1 + rand.Next(11) * (vineGrowthChance + 0.2f); while (api.GetBlockId(vinePos) == 0 && cnt-- > 0) { Block block = config.treeBlocks.vinesBlock; if (cnt <= 0 && config.treeBlocks.vinesEndBlock != null) { block = config.treeBlocks.vinesEndBlock; } block.TryPlaceBlockForWorldGen(api, vinePos, facing); vinePos.Down(); } } } reldistance = GameMath.FastSqrt(dx * dx + dy * dy + dz * dz) / totaldistance; if (reldistance < branchstart) { continue; } if (reldistance > lastreldistance + branchspacing * (1f - reldistance)) { branchspacing = branch.branchSpacing.nextFloat(); lastreldistance = reldistance; if (branch.branchQuantityEvolve != null) { branchQuantity = branch.branchQuantityEvolve.nextFloat(branchQuantityStart, currentSequence); } else { branchQuantity = branch.branchQuantity.nextFloat(); } float prevHorAngle = 0f; float horAngle; float minHorangleDist = Math.Min(GameMath.PI / 10, branch.branchHorizontalAngle.var / 5); bool first = true; while (branchQuantity-- > 0) { if (branchQuantity < 1 && rand.NextDouble() < branchQuantity) { break; } curWidth *= branch.branchWidthLossMul; horAngle = branch.branchHorizontalAngle.nextFloat(); int tries = 5; while (!first && Math.Abs(horAngle - prevHorAngle) < minHorangleDist && tries-- > 0) { horAngle = branch.branchHorizontalAngle.nextFloat(); } if (branch.branchWidthMultiplierEvolve != null) { branchWidth = curWidth * branch.branchWidthMultiplierEvolve.nextFloat(branchWidthMulitplierStart, currentSequence); } else { branchWidth = branch.branchWidthMultiplier.nextFloat(curWidth); } growBranch( depth + 1, pos, dx + trunkOffsetX, dy, dz + trunkOffsetZ, branch.branchVerticalAngle.nextFloat(), angleHor + branch.branchHorizontalAngle.nextFloat(), branchWidth, Math.Max(0, branch.dieAt.nextFloat()) ); first = false; prevHorAngle = horAngle; } } } }