public static void AddSunlightRemovals(ref NativeArray3x3 <Light> lights, NativeList <TorchLightOp> lightOps, NativeQueue <LightRemovalNode> lrbfs) { for (int loi = 0; loi < lightOps.Length; ++loi) { TorchLightOp op = lightOps[loi]; int opx = op.index % S; int opy = op.index / (S * S); int opz = (op.index % (S * S)) / S; int startIndex = (opx + S) + (opz + S) * W + (opy + S) * W * W; Light curLight = lights.Get(opx, opy, opz); lrbfs.Enqueue(new LightRemovalNode { index = startIndex, light = curLight.sun }); curLight.sun = 0; lights.Set(opx, opy, opz, curLight); } }
static void TrySpawnGemstone(ref NativeArray3x3 <Block> blocks, Random urand, int x, int y, int z) { //int dir = urand.NextInt(0, 6); int3 pos = new int3(x, y, z); int3 dir; bool stalag = urand.NextFloat() < 0.5f; // ones on ground pointing up if (stalag) // changed to just be up and down for now see how that looks { dir = new int3(0, 1, 0); } else { dir = new int3(0, -1, 0); } if (blocks.Get(x - dir.x, y - dir.y, z - dir.z) == Blocks.AIR) { return; } int len = urand.NextInt(2, 8 + (int)(math.abs(y) * 0.01f)); len /= stalag ? 1 : 2; // stalags should be twice as tall as stalacts (ceiling ones) len = math.clamp(len, 2, 20); // make sure dont get too crazy long int sides, xdir, zdir; if (len > 2) // if taller than this have some secondary columns with random lengths { sides = urand.NextInt(0, 4); xdir = urand.NextInt(2, len - 1); // size of secondary columns zdir = urand.NextInt(2, len - 1); } else { xdir = zdir = sides = 0; } int gemType = urand.NextInt(0, 3); Block gemBlock; if (gemType == 0) { gemBlock = Blocks.RUBY; } else if (gemType == 1) { gemBlock = Blocks.EMERALD; } else { gemBlock = Blocks.SAPPHIRE; } for (int i = 0; i < len; ++i) { int3 p = pos + dir * i; if (blocks.Get(p.x, p.y, p.z) != Blocks.AIR) { return; } blocks.Set(p.x, p.y, p.z, gemBlock); if (xdir-- > 0) { if (sides / 2 == 0) // west { if (blocks.Get(p.x - 1, p.y, p.z) == Blocks.AIR) { blocks.Set(p.x - 1, p.y, p.z, gemBlock); } } else // east { if (blocks.Get(p.x + 1, p.y, p.z) == Blocks.AIR) { blocks.Set(p.x + 1, p.y, p.z, gemBlock); } } } if (zdir-- > 0) { if (sides % 2 == 0) // south { if (blocks.Get(p.x, p.y, p.z - 1) == Blocks.AIR) { blocks.Set(p.x, p.y, p.z - 1, gemBlock); } } else // north { if (blocks.Get(p.x, p.y, p.z + 1) == Blocks.AIR) { blocks.Set(p.x, p.y, p.z + 1, gemBlock); } } } } }
static void TrySpawnTree(ref NativeArray3x3 <Block> blocks, Random urand, int x, int y, int z) { int width = urand.NextInt(1, 4); int height = 0; if (width == 1) { height = urand.NextInt(3, 10); for (int i = 0; i <= height; ++i) { Block b = blocks.Get(x, y + i, z); if (i == 0) { if (b != Blocks.GRASS) { return; } } else { if (b != Blocks.AIR) { return; } } } } else if (width == 2) { height = urand.NextInt(8, 20); for (int i = 0; i <= height; ++i) { for (int u = 0; u <= 1; ++u) { for (int v = 0; v <= 1; ++v) { Block b = blocks.Get(x + u, y + i, z + v); if (i == 0) { if (b != Blocks.GRASS) { return; } } else { if (b != Blocks.AIR) { return; } } } } } } else if (width == 3) { height = urand.NextInt(16, 30); for (int i = 0; i <= height; ++i) { for (int u = -1; u <= 1; ++u) { for (int v = -1; v <= 1; ++v) { Block b = blocks.Get(x + u, y + i, z + v); if (i == 0) { if (b != Blocks.GRASS) { return; } } else { if (b != Blocks.AIR) { return; } } } } } } for (int i = 1; i <= height + 1; ++i) { float f = (height - i); float hf = (float)i / height; hf = 1.0f - hf; hf *= hf; int s = (int)(f * hf); if (s <= 0) { s = 1; } s += urand.NextInt(-1, 2); int us = -s; int vs = -s; if (width == 3) { us -= 1; vs -= 1; } for (int u = us; u <= s + width - 1; ++u) { for (int v = vs; v <= s + width - 1; ++v) { // spawn trunk if near center if (width == 1 && u == 0 && v == 0 || width == 2 && (u >= 0 && u <= 1) && (v >= 0 && v <= 1) || width == 3 && (u >= -1 && u <= 1) && (v >= -1 && v <= 1)) { blocks.Set(x + u, y + i, z + v, Blocks.PINE); } else if (i >= width * 2 && i % 2 == 0 || i > height - 1) // otherwise leaves { if (blocks.Get(x + u, y + i, z + v) == Blocks.AIR) { blocks.Set(x + u, y + i, z + v, Blocks.PINELEAF); } } } } } }
// this is a pine tree routine static void TrySpawnGoodTree(ref NativeArray3x3 <Block> blocks, Random urand, int x, int y, int z) { float width = urand.NextFloat(0.5f, 2f); int height = 0; height = (int)(28 * width / 2.0f) + urand.NextInt(-3, 3); math.clamp(height, 5, 29); // make sure space is kinda clear for tree height at least int wi = (int)width + 1; for (int u = -wi; u <= wi; ++u) { for (int v = -wi; v <= wi; ++v) { if (math.lengthsq(new float2(u, v)) < wi * wi) { if (blocks.Get(x + u, y, z + v) != Blocks.GRASS) { return; } } } } for (int i = 1; i <= height; ++i) { if (blocks.Get(x, y + i, z) != Blocks.AIR) { return; } } int nextLeafLevel = urand.NextInt(3, 5); for (int i = 1; i <= height; ++i) { float hp = (float)i / height; float curWidth = math.lerp(width, width / 2.0f, hp); float leafEdge = 0.0f; //bool leafLayer = (i >= 3 && i % 2 == 0) || i >= height; bool leafLayer = i == nextLeafLevel || i == height; if (leafLayer) { leafEdge = math.lerp(width * 4.0f, 1.5f, Mth.QuadraticEaseOut(hp)); float r = urand.NextFloat(); if (r < 0.1f) { nextLeafLevel += 1; } else if (r < 0.75f || height < 20) { nextLeafLevel += 2; } else // taller trees have small chance of large gaps in leaves { nextLeafLevel += 3; } if (i == height) { leafEdge = 1.0f; } else if (urand.NextFloat() < .2f) { leafEdge++; } } int cwi = (int)(curWidth + leafEdge) + 1; for (int u = -cwi; u <= cwi; ++u) { for (int v = -cwi; v <= cwi; ++v) { float len = math.lengthsq(new float2(u, v)); if (len < curWidth * curWidth) { blocks.Set(x + u, y + i, z + v, Blocks.PINE); } else if (leafLayer && len < (curWidth + leafEdge) * (curWidth + leafEdge)) { if (blocks.Get(x + u, y + i, z + v) == Blocks.AIR) { blocks.Set(x + u, y + i, z + v, Blocks.PINELEAF); } } } } } // add leaves to the top int topper = height > 20 ? 2 : 1; for (int i = height + 1; i < height + 1 + topper; ++i) { if (blocks.Get(x, y + i, z) == Blocks.AIR) { blocks.Set(x, y + i, z, Blocks.PINELEAF); } } }
public static void ProcessSunlight(ref NativeArray3x3 <Light> lights, ref NativeArray3x3 <Block> blocks, NativeArray <BlockData> blockData, NativeQueue <int> lbfs, NativeQueue <int> lbfs_U, NativeQueue <LightRemovalNode> lrbfs, NativeQueue <LightRemovalNode> lrbfs_U) { // not sure if going to have sunlightops... the ordering might get boned up on quick place delete operations, but way more efficient this way //for (int oi = 0; oi < ops.Length;) { // // check to see what type of operation first op is // bool isProp = ops[oi].val != 0; // // queue up each matching operation type until you hit end or mismatch // while (oi < ops.Length) { // SunLightOp op = ops[oi]; // if ((op.val != 0) != isProp) { // break; // this ops type is different from first in run // } // int opx = op.index % S; // int opy = op.index / (S * S); // int opz = (op.index % (S * S)) / S; // int startIndex = (opx + S) + (opz + S) * W + (opy + S) * W * W; // Light curLight = lights.Get(opx, opy, opz); // if (isProp) { // // set the new light value here // Light newLight = curLight; // newLight.sun = op.val; // lights.Set(opx, opy, opz, newLight); // // if the new sun value is same or less than current dont need to progagate // // this might not happen with sunlight but left check anyways // if (op.val <= curLight.sun) { // continue; // } // } else { // lrbfs.Enqueue(new LightRemovalNode { index = startIndex, light = curLight.sun }); // curLight.sun = 0; // lights.Set(opx, opy, opz, curLight); // } // oi++; // } //if (!isProp) { while (lrbfs.Count > 0) { LightRemovalNode node = lrbfs.Dequeue(); // extract coords from index int x = node.index % W - S; int y = node.index / (W * W) - S; int z = (node.index % (W * W)) / W - S; byte oneLess = (byte)(node.light - 1); // each time reduce light by one Light light = lights.Get(x - 1, y, z); if (light.sun != 0) // WEST { int index = x - 1 + S + (z + S) * W + (y + S) * W * W; if (light.sun < node.light) { light.sun = 0; lights.Set(x - 1, y, z, light); lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } light = lights.Get(x, y - 1, z); if (light.sun != 0) // DOWN { int index = x + S + (z + S) * W + (y - 1 + S) * W * W; if (light.sun < node.light || node.light == MAX_LIGHT) { light.sun = 0; lights.Set(x, y - 1, z, light); byte lv = node.light == MAX_LIGHT ? MAX_LIGHT : oneLess; lrbfs.Enqueue(new LightRemovalNode { index = index, light = lv }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } light = lights.Get(x, y, z - 1); if (light.sun != 0) // SOUTH { int index = x + S + (z - 1 + S) * W + (y + S) * W * W; if (light.sun < node.light) { light.sun = 0; lights.Set(x, y, z - 1, light); lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } light = lights.Get(x + 1, y, z); if (light.sun != 0) // EAST { int index = x + 1 + S + (z + S) * W + (y + S) * W * W; if (light.sun < node.light) { light.sun = 0; lights.Set(x + 1, y, z, light); lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } light = lights.Get(x, y + 1, z); if (light.sun != 0) // UP { int index = x + S + (z + S) * W + (y + 1 + S) * W * W; if (light.sun < node.light) { light.sun = 0; lights.Set(x, y + 1, z, light); lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } light = lights.Get(x, y, z + 1); if (light.sun != 0) // NORTH { int index = x + S + (z + 1 + S) * W + (y + S) * W * W; if (light.sun < node.light) { light.sun = 0; lights.Set(x, y, z + 1, light); lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } } // propagate (either way) while (lbfs.Count > 0) { int index = lbfs.Dequeue(); // extract coords from index int x = index % W - S; int y = index / (W * W) - S; int z = (index % (W * W)) / W - S; // get light level at this node int sunLight = lights.Get(x, y, z).sun; // check each neighbor blocks light reduction value // if neighbor light level is 2 or more levels less than this node, set them to this light-1 and add to queue // also add additional light reduction value byte LR = blockData[blocks.Get(x - 1, y, z).type].lightReduction; if (LR < sunLight) // WEST { Light light = lights.Get(x - 1, y, z); if (light.sun + 2 + LR <= sunLight) { light.sun = (byte)(sunLight - 1 - LR); lights.Set(x - 1, y, z, light); lbfs.Enqueue(x - 1 + S + (z + S) * W + (y + S) * W * W); } } LR = blockData[blocks.Get(x, y - 1, z).type].lightReduction; if (LR < sunLight) // DOWN { Light light = lights.Get(x, y - 1, z); if (light.sun + 2 + LR <= sunLight) { if (sunLight == MAX_LIGHT) // if at maxlight dont reduce by 1 each time { light.sun = (byte)(sunLight - LR); } else { light.sun = (byte)(sunLight - 1 - LR); } lights.Set(x, y - 1, z, light); if (y <= -31) { lbfs_U.Enqueue(x + S + (z + S) * W + (32 + S) * W * W); // add to unfinished queue and shift index to be proper for downdown chunk } else { lbfs.Enqueue(x + S + (z + S) * W + (y - 1 + S) * W * W); } } } LR = blockData[blocks.Get(x, y, z - 1).type].lightReduction; if (LR < sunLight) // SOUTH { Light light = lights.Get(x, y, z - 1); if (light.sun + 2 + LR <= sunLight) { light.sun = (byte)(sunLight - 1 - LR); lights.Set(x, y, z - 1, light); lbfs.Enqueue(x + S + (z - 1 + S) * W + (y + S) * W * W); } } LR = blockData[blocks.Get(x + 1, y, z).type].lightReduction; if (LR < sunLight) // EAST { Light light = lights.Get(x + 1, y, z); if (light.sun + 2 + LR <= sunLight) { light.sun = (byte)(sunLight - 1 - LR); lights.Set(x + 1, y, z, light); lbfs.Enqueue(x + 1 + S + (z + S) * W + (y + S) * W * W); } } LR = blockData[blocks.Get(x, y + 1, z).type].lightReduction; if (LR < sunLight) // UP { Light light = lights.Get(x, y + 1, z); if (light.sun + 2 + LR <= sunLight) { light.sun = (byte)(sunLight - 1 - LR); lights.Set(x, y + 1, z, light); lbfs.Enqueue(x + S + (z + S) * W + (y + 1 + S) * W * W); } } LR = blockData[blocks.Get(x, y, z + 1).type].lightReduction; if (LR < sunLight) // NORTH { Light light = lights.Get(x, y, z + 1); if (light.sun + 2 + LR <= sunLight) { light.sun = (byte)(sunLight - 1 - LR); lights.Set(x, y, z + 1, light); lbfs.Enqueue(x + S + (z + 1 + S) * W + (y + S) * W * W); } } } }
public static void ProcessTorchLightOpsOptimal(ref NativeArray3x3 <Light> lights, ref NativeArray3x3 <Block> blocks, NativeArray <BlockData> blockData, NativeList <TorchLightOp> ops, NativeQueue <int> lbfs, NativeQueue <LightRemovalNode> lrbfs) { lights.flags = 0; // set blocks at torchlightops to have correct is light flag set for (int opIndex = 0; opIndex < ops.Length; ++opIndex) { TorchLightOp op = ops[opIndex]; int opx = op.index % S; int opy = op.index / (S * S); int opz = (op.index % (S * S)) / S; Light opLight = lights.Get(opx, opy, opz); opLight.torch = SetIsLight(opLight.torch, op.val > 0); lights.Set(opx, opy, opz, opLight); } // loop over each color channel of torch light for (int cIndex = 0; cIndex < 3; cIndex++) { for (int oi = 0; oi < ops.Length;) { // check to see what type of operation first op is bool isProp = GetChannel(ops[oi].val, cIndex) != 0; // queue up each matching operation type until you hit end or mismatch while (oi < ops.Length) { TorchLightOp op = ops[oi]; int opChannel = GetChannel(op.val, cIndex); if ((opChannel != 0) != isProp) { break; // this ops type is different from first in run } int opx = op.index % S; int opy = op.index / (S * S); int opz = (op.index % (S * S)) / S; int startIndex = (opx + S) + (opz + S) * W + (opy + S) * W * W; Light curLight = lights.Get(opx, opy, opz); int curChannel = GetChannel(curLight.torch, cIndex); if (isProp) { // set the new light value here Light newLight = curLight; newLight.torch = SetChannel(newLight.torch, cIndex, opChannel); lights.Set(opx, opy, opz, newLight); // if the new ops channel value is same as current light channel then add to propagate if (opChannel > curChannel) { lbfs.Enqueue(startIndex); } } else { lrbfs.Enqueue(new LightRemovalNode { index = startIndex, light = (byte)curChannel }); curLight.torch = SetChannel(curLight.torch, cIndex, 0); lights.Set(opx, opy, opz, curLight); } oi++; } if (!isProp) { while (lrbfs.Count > 0) { LightRemovalNode node = lrbfs.Dequeue(); // extract coords from index int x = node.index % W - S; int y = node.index / (W * W) - S; int z = (node.index % (W * W)) / W - S; byte oneLess = (byte)(node.light - 1); // each time reduce light by one //ushort westLight = light.Get(x - 1, y, z).torch; Light westLight = lights.Get(x - 1, y, z); byte westChannel = (byte)GetChannel(westLight.torch, cIndex); if (westChannel != 0) { int index = x - 1 + S + (z + S) * W + (y + S) * W * W; if (westChannel < node.light) { if (!GetIsLight(westLight.torch)) { westLight.torch = SetChannel(westLight.torch, cIndex, 0); lights.Set(x - 1, y, z, westLight); } else // if this node is a light, dont override value, but still add a removal node as if you did, then add to repropagate to fill it back in { lbfs.Enqueue(index); } lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } Light downLight = lights.Get(x, y - 1, z); byte downChannel = (byte)GetChannel(downLight.torch, cIndex); if (downChannel != 0) { int index = x + S + (z + S) * W + (y - 1 + S) * W * W; if (downChannel < node.light) { if (!GetIsLight(downLight.torch)) { downLight.torch = SetChannel(downLight.torch, cIndex, 0); lights.Set(x, y - 1, z, downLight); } else { lbfs.Enqueue(index); } lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } Light southLight = lights.Get(x, y, z - 1); byte southChannel = (byte)GetChannel(southLight.torch, cIndex); if (southChannel != 0) { int index = x + S + (z - 1 + S) * W + (y + S) * W * W; if (southChannel < node.light) { if (!GetIsLight(southLight.torch)) { southLight.torch = SetChannel(southLight.torch, cIndex, 0); lights.Set(x, y, z - 1, southLight); } else { lbfs.Enqueue(index); } lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } Light eastLight = lights.Get(x + 1, y, z); byte eastChannel = (byte)GetChannel(eastLight.torch, cIndex); if (eastChannel != 0) { int index = x + 1 + S + (z + S) * W + (y + S) * W * W; if (eastChannel < node.light) { if (!GetIsLight(eastLight.torch)) { eastLight.torch = SetChannel(eastLight.torch, cIndex, 0); lights.Set(x + 1, y, z, eastLight); } else { lbfs.Enqueue(index); } lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } Light upLight = lights.Get(x, y + 1, z); byte upChannel = (byte)GetChannel(upLight.torch, cIndex); if (upChannel != 0) { int index = x + S + (z + S) * W + (y + 1 + S) * W * W; if (upChannel < node.light) { if (!GetIsLight(upLight.torch)) { upLight.torch = SetChannel(upLight.torch, cIndex, 0); lights.Set(x, y + 1, z, upLight); } else { lbfs.Enqueue(index); } lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } Light northLight = lights.Get(x, y, z + 1); byte northChannel = (byte)GetChannel(northLight.torch, cIndex); if (northChannel != 0) { int index = x + S + (z + 1 + S) * W + (y + S) * W * W; if (northChannel < node.light) { if (!GetIsLight(northLight.torch)) { northLight.torch = SetChannel(northLight.torch, cIndex, 0); lights.Set(x, y, z + 1, northLight); } else { lbfs.Enqueue(index); } lrbfs.Enqueue(new LightRemovalNode { index = index, light = oneLess }); } else // add to propagate queue so can fill gaps left behind by removal { lbfs.Enqueue(index); } } } } // propagate either way while (lbfs.Count > 0) { int index = lbfs.Dequeue(); // extract coords from index int x = index % W - S; int y = index / (W * W) - S; int z = (index % (W * W)) / W - S; // get light level at this node int mChan = GetChannel(lights.Get(x, y, z).torch, cIndex); //if(mChan == 0) { // can happen frequently when batching together light removals // continue; //} // check each neighbor blocks light reduction value // if neighbor light level is 2 or more levels less than this node, set them to this light-1 and add to queue // also add additional light reduction value byte LR = blockData[blocks.Get(x - 1, y, z).type].lightReduction; if (LR < mChan) // WEST { Light light = lights.Get(x - 1, y, z); if (GetChannel(light.torch, cIndex) + 2 + LR <= mChan) { light.torch = SetChannel(light.torch, cIndex, mChan - 1 - LR); lights.Set(x - 1, y, z, light); lbfs.Enqueue(x - 1 + S + (z + S) * W + (y + S) * W * W); } } LR = blockData[blocks.Get(x, y - 1, z).type].lightReduction; if (LR < mChan) // DOWN { Light light = lights.Get(x, y - 1, z); if (GetChannel(light.torch, cIndex) + 2 + LR <= mChan) { light.torch = SetChannel(light.torch, cIndex, mChan - 1 - LR); lights.Set(x, y - 1, z, light); lbfs.Enqueue(x + S + (z + S) * W + (y - 1 + S) * W * W); } } LR = blockData[blocks.Get(x, y, z - 1).type].lightReduction; if (LR < mChan) // SOUTH { Light light = lights.Get(x, y, z - 1); if (GetChannel(light.torch, cIndex) + 2 + LR <= mChan) { light.torch = SetChannel(light.torch, cIndex, mChan - 1 - LR); lights.Set(x, y, z - 1, light); lbfs.Enqueue(x + S + (z - 1 + S) * W + (y + S) * W * W); } } LR = blockData[blocks.Get(x + 1, y, z).type].lightReduction; if (LR < mChan) // EAST { Light light = lights.Get(x + 1, y, z); if (GetChannel(light.torch, cIndex) + 2 + LR <= mChan) { light.torch = SetChannel(light.torch, cIndex, mChan - 1 - LR); lights.Set(x + 1, y, z, light); lbfs.Enqueue(x + 1 + S + (z + S) * W + (y + S) * W * W); } } LR = blockData[blocks.Get(x, y + 1, z).type].lightReduction; if (LR < mChan) // UP { Light light = lights.Get(x, y + 1, z); if (GetChannel(light.torch, cIndex) + 2 + LR <= mChan) { light.torch = SetChannel(light.torch, cIndex, mChan - 1 - LR); lights.Set(x, y + 1, z, light); lbfs.Enqueue(x + S + (z + S) * W + (y + 1 + S) * W * W); } } LR = blockData[blocks.Get(x, y, z + 1).type].lightReduction; if (LR < mChan) // NORTH { Light light = lights.Get(x, y, z + 1); if (GetChannel(light.torch, cIndex) + 2 + LR <= mChan) { light.torch = SetChannel(light.torch, cIndex, mChan - 1 - LR); lights.Set(x, y, z + 1, light); lbfs.Enqueue(x + S + (z + 1 + S) * W + (y + S) * W * W); } } } } } }