public static void LightUpdate(ref NativeArray3x3 <Light> lights, NativeList <Face> faces, NativeList <Color32> colors) { for (int i = 0; i < faces.Length; ++i) { int pos = faces[i].pos; int x = pos % Chunk.SIZE; int y = pos / (Chunk.SIZE * Chunk.SIZE); int z = (pos % (Chunk.SIZE * Chunk.SIZE)) / Chunk.SIZE; //Light curLight = lights.Get(x, y, z); //if (GetIsLight(curLight.torch)) { // colors.Add(GetColorFromLight(curLight)); //} else { switch (faces[i].dir) { case Dir.west: colors.Add(GetColorFromLight(lights.Get(x - 1, y, z))); break; case Dir.down: colors.Add(GetColorFromLight(lights.Get(x, y - 1, z))); break; case Dir.south: colors.Add(GetColorFromLight(lights.Get(x, y, z - 1))); break; case Dir.east: colors.Add(GetColorFromLight(lights.Get(x + 1, y, z))); break; case Dir.up: colors.Add(GetColorFromLight(lights.Get(x, y + 1, z))); break; case Dir.north: colors.Add(GetColorFromLight(lights.Get(x, y, z + 1))); break; default: colors.Add(GetColorFromLight(lights.Get(x, y, z))); break; } //} // add the last color 3 more times (4 colors per face) Color32 lastColor = colors[colors.Length - 1]; colors.Add(lastColor); colors.Add(lastColor); colors.Add(lastColor); } }
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); } }
// called only once all neighbors are generated public static void BuildStructures(Vector3i chunkBlockPos, int seed, ref NativeArray3x3 <Block> blocks) { // not sure here lol Random urand = new Random((uint)(chunkBlockPos.GetHashCode() + seed)); for (int y = 0; y < Chunk.SIZE; ++y) { for (int z = 0; z < Chunk.SIZE; ++z) { for (int x = 0; x < Chunk.SIZE; ++x) { //if (blocks.Get(x, y, z) == Blocks.AIR && blocks.Get(x, y - 1, z) == Blocks.STONE) { // blocks.Set(x, y, z, Blocks.GRASS); // blocks.Set(x, y - 1, z, Blocks.GRASS); //} // random chance to try to spawn tree if (urand.NextFloat() < 0.01f) { TrySpawnGoodTree(ref blocks, urand, x, y, z); } // try to spawn gems if (blocks.Get(x, y, z) == Blocks.AIR) { float gemChance = Mth.Blend(0.02f, 0.0f, y + chunkBlockPos.y, -400, -100); if (urand.NextFloat() < gemChance) { TrySpawnGemstone(ref blocks, urand, x, y, z); } } } } } }
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); } } } } } }
//public const int VOXEL_SIZE = 1; //https://github.com/roboleary/GreedyMesh/blob/master/src/mygame/Main.java //https://github.com/darkedge/starlight/blob/master/starlight/starlight_game.cpp public static void BuildGreedyCollider(ref NativeArray3x3 <Block> blocks, NativeList <Vector3> vertices, NativeList <int> triangles, NativeArray <BlockData> blockData) { void AddQuadTrianglesGreedy(bool clockwise) { if (!clockwise) { triangles.Add(vertices.Length - 2); // 2 triangles.Add(vertices.Length - 4); // 0 triangles.Add(vertices.Length - 3); // 1 triangles.Add(vertices.Length - 3); // 1 triangles.Add(vertices.Length - 1); // 3 triangles.Add(vertices.Length - 2); // 2 } else { triangles.Add(vertices.Length - 2); // 2 triangles.Add(vertices.Length - 1); // 3 triangles.Add(vertices.Length - 3); // 1 triangles.Add(vertices.Length - 3); // 1 triangles.Add(vertices.Length - 4); // 0 triangles.Add(vertices.Length - 2); // 2 } } // setup variables for algo int i, j, k, l, w, h, d1, d2, n = 0; Dir side = Dir.south; int[] x = new int[] { 0, 0, 0 }; int[] q = new int[] { 0, 0, 0 }; int[] du = new int[] { 0, 0, 0 }; int[] dv = new int[] { 0, 0, 0 }; // slice will contain groups of matching blocks as we proceed through chunk in 6 directions, onces for each face Block[] slice = new Block[Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT]; int[] maxDim = new int[] { Chunk.CHUNK_WIDTH, Chunk.CHUNK_HEIGHT, Chunk.CHUNK_WIDTH }; // sweep over six dimensions for (int dim = 0; dim < 6; ++dim) { int d0 = dim % 3; d1 = (dim + 1) % 3; // u d2 = (dim + 2) % 3; // v // when going thru z dimension, make x d1 and y d2 so makes more sense for uvs if (d0 == 2) { d1 = 1; d2 = 0; } int bf = dim / 3 * 2 - 1; // -1 -1 -1 +1 +1 +1 bool backFace = bf < 0; x[0] = 0; x[1] = 0; x[2] = 0; // set the direction vector from dimension q[0] = 0; q[1] = 0; q[2] = 0; q[d0] = 1; side = (Dir)dim; // move through dimension from front to back for (x[d0] = -1; x[d0] < maxDim[d0];) { // compute mask (which is a slice) n = 0; for (x[d2] = 0; x[d2] < maxDim[d2]; x[d2]++) { for (x[d1] = 0; x[d1] < maxDim[d1]; x[d1]++) { // the second part of the ors are to make sure you dont add collision data for other chunk block faces on your borders Block block1 = (backFace || x[d0] >= 0) ? blocks.Get(x[0], x[1], x[2]) : Blocks.AIR; // block were at Block block2 = (!backFace || x[d0] < S - 1) ? blocks.Get(x[0] + q[0], x[1] + q[1], x[2] + q[2]) : Blocks.AIR; slice[n++] = BlockData.ColliderSolid(blockData, block1) && BlockData.ColliderSolid(blockData, block2) ? Blocks.AIR : backFace ? block2 : block1; // saving this for when porting back to meshing //slice[n++] = block1.IsSolid(side) && block2.IsSolid(Dirs.Opp(side)) ? // Blocks.AIR : backFace ? block2 : block1; } } // i think the current dimension we are slicing thru is incremented here so the blocks // will have the correct placement coordinate x[d0]++; // generate mesh for the mask n = 0; for (j = 0; j < maxDim[d2]; ++j) { for (i = 0; i < maxDim[d1];) { if (!BlockData.ColliderSolid(blockData, slice[n])) { ++i; ++n; continue; } // normal equality check can split on type and more like AO and stuff later if want to change this back // just need to change this below line and the line like 8 lines down // compute width for (w = 1; i + w < maxDim[d1] && BlockData.ColliderSolid(blockData, slice[n + w]) == BlockData.ColliderSolid(blockData, slice[n]); ++w) { } // compute height bool done = false; for (h = 1; j + h < maxDim[d2]; ++h) { for (k = 0; k < w; ++k) { if (BlockData.ColliderSolid(blockData, slice[n + k + h * maxDim[d1]]) != BlockData.ColliderSolid(blockData, slice[n])) { done = true; break; } } if (done) { break; } } x[d1] = i; x[d2] = j; du[0] = 0; du[1] = 0; du[2] = 0; du[d1] = w; dv[0] = 0; dv[1] = 0; dv[2] = 0; dv[d2] = h; int s = (int)side; Vector3 botLeft = new Vector3(x[0], x[1], x[2]) + MeshUtils.padOffset[s][0]; Vector3 botRight = new Vector3(x[0] + dv[0], x[1] + dv[1], x[2] + dv[2]) + MeshUtils.padOffset[s][1]; Vector3 topLeft = new Vector3(x[0] + du[0], x[1] + du[1], x[2] + du[2]) + MeshUtils.padOffset[s][2]; Vector3 topRight = new Vector3(x[0] + du[0] + dv[0], x[1] + du[1] + dv[1], x[2] + du[2] + dv[2]) + MeshUtils.padOffset[s][3]; botLeft /= Chunk.BPU; topLeft /= Chunk.BPU; topRight /= Chunk.BPU; botRight /= Chunk.BPU; vertices.Add(botLeft); vertices.Add(botRight); vertices.Add(topLeft); vertices.Add(topRight); AddQuadTrianglesGreedy(d0 == 2 ? backFace : !backFace); // zero out the quad in the mask for (l = 0; l < h; ++l) { for (k = 0; k < w; ++k) { slice[n + k + l * maxDim[d1]] = Blocks.AIR; } } // increment counters and continue i += w; n += w; } } } } }
static int GetOpacity(ref NativeArray3x3 <Block> blocks, NativeArray <BlockData> blockData, int x, int y, int z) { return(BlockData.RenderSolid(blockData, blocks.Get(x, y, z), Dir.none) ? 1 : 0); // dir.none for now since all blocks are either transparent or not }
static void AddUVs(int x, int y, int z, ref NativeMeshData data, ref NativeArray3x3 <Block> blocks, NativeArray <BlockData> blockData, Dir dir, bool isLight) { // calculate uv1s, xy is uv coordinates, z is texture type BlockData bd = blockData[blocks.Get(x, y, z).type]; if (bd.renderType == 1) // normal texture { if (bd.texture < 0) // dynamic, depends on nearby blocks { data.AddFaceUVs(GetTextureIndex(dir, x, y, z, ref blocks)); } else { data.AddFaceUVs(bd.texture); } } else if (bd.renderType == 2) // using tiling textures { if (bd.texture < 0) // dynamic, depends on nearby blocks { int texture = GetTileTextureIndex(dir, x, y, z, ref blocks); data.AddTileUvs(texture, dir, x, y, z, ref blocks, blockData); } else { data.AddTileUvs(bd.texture, dir, x, y, z, ref blocks, blockData); } } // now add uv2s, x is tiletype and y is ambient occlusion (z is unused for now) Vector3 uv2_0, uv2_1, uv2_2, uv2_3; uv2_0 = uv2_1 = uv2_2 = uv2_3 = default; uv2_0.x = uv2_1.x = uv2_2.x = uv2_3.x = bd.renderType == 1 ? 0.5f : 1.5f; if (isLight) { uv2_0.y = uv2_1.y = uv2_2.y = uv2_3.y = 1.0f; // dont add ao on the faces of lights, looks weird } else { switch (dir) { case Dir.west: { int up = GetOpacity(ref blocks, blockData, x - 1, y + 1, z); int down = GetOpacity(ref blocks, blockData, x - 1, y - 1, z); int north = GetOpacity(ref blocks, blockData, x - 1, y, z + 1); int south = GetOpacity(ref blocks, blockData, x - 1, y, z - 1); uv2_0.y = CalcAO(down, north, ref blocks, blockData, x - 1, y - 1, z + 1); uv2_1.y = CalcAO(up, north, ref blocks, blockData, x - 1, y + 1, z + 1); uv2_2.y = CalcAO(up, south, ref blocks, blockData, x - 1, y + 1, z - 1); uv2_3.y = CalcAO(down, south, ref blocks, blockData, x - 1, y - 1, z - 1); } break; case Dir.down: { int north = GetOpacity(ref blocks, blockData, x, y - 1, z + 1); int south = GetOpacity(ref blocks, blockData, x, y - 1, z - 1); int east = GetOpacity(ref blocks, blockData, x + 1, y - 1, z); int west = GetOpacity(ref blocks, blockData, x - 1, y - 1, z); uv2_0.y = CalcAO(south, west, ref blocks, blockData, x - 1, y - 1, z - 1); uv2_1.y = CalcAO(south, east, ref blocks, blockData, x + 1, y - 1, z - 1); uv2_2.y = CalcAO(north, east, ref blocks, blockData, x + 1, y - 1, z + 1); uv2_3.y = CalcAO(north, west, ref blocks, blockData, x - 1, y - 1, z + 1); } break; case Dir.south: { int up = GetOpacity(ref blocks, blockData, x, y + 1, z - 1); int down = GetOpacity(ref blocks, blockData, x, y - 1, z - 1); int east = GetOpacity(ref blocks, blockData, x + 1, y, z - 1); int west = GetOpacity(ref blocks, blockData, x - 1, y, z - 1); uv2_0.y = CalcAO(down, west, ref blocks, blockData, x - 1, y - 1, z - 1); uv2_1.y = CalcAO(up, west, ref blocks, blockData, x - 1, y + 1, z - 1); uv2_2.y = CalcAO(up, east, ref blocks, blockData, x + 1, y + 1, z - 1); uv2_3.y = CalcAO(down, east, ref blocks, blockData, x + 1, y - 1, z - 1); } break; case Dir.east: { int up = GetOpacity(ref blocks, blockData, x + 1, y + 1, z); int down = GetOpacity(ref blocks, blockData, x + 1, y - 1, z); int north = GetOpacity(ref blocks, blockData, x + 1, y, z + 1); int south = GetOpacity(ref blocks, blockData, x + 1, y, z - 1); uv2_0.y = CalcAO(down, south, ref blocks, blockData, x + 1, y - 1, z - 1); uv2_1.y = CalcAO(up, south, ref blocks, blockData, x + 1, y + 1, z - 1); uv2_2.y = CalcAO(up, north, ref blocks, blockData, x + 1, y + 1, z + 1); uv2_3.y = CalcAO(down, north, ref blocks, blockData, x + 1, y - 1, z + 1); } break; case Dir.up: { int north = GetOpacity(ref blocks, blockData, x, y + 1, z + 1); int south = GetOpacity(ref blocks, blockData, x, y + 1, z - 1); int east = GetOpacity(ref blocks, blockData, x + 1, y + 1, z); int west = GetOpacity(ref blocks, blockData, x - 1, y + 1, z); uv2_0.y = CalcAO(north, west, ref blocks, blockData, x - 1, y + 1, z + 1); uv2_1.y = CalcAO(north, east, ref blocks, blockData, x + 1, y + 1, z + 1); uv2_2.y = CalcAO(south, east, ref blocks, blockData, x + 1, y + 1, z - 1); uv2_3.y = CalcAO(south, west, ref blocks, blockData, x - 1, y + 1, z - 1); } break; case Dir.north: { int up = GetOpacity(ref blocks, blockData, x, y + 1, z + 1); int down = GetOpacity(ref blocks, blockData, x, y - 1, z + 1); int east = GetOpacity(ref blocks, blockData, x + 1, y, z + 1); int west = GetOpacity(ref blocks, blockData, x - 1, y, z + 1); uv2_0.y = CalcAO(down, east, ref blocks, blockData, x + 1, y - 1, z + 1); uv2_1.y = CalcAO(up, east, ref blocks, blockData, x + 1, y + 1, z + 1); uv2_2.y = CalcAO(up, west, ref blocks, blockData, x - 1, y + 1, z + 1); uv2_3.y = CalcAO(down, west, ref blocks, blockData, x - 1, y - 1, z + 1); } break; default: break; } } data.uv2s.Add(uv2_0); data.uv2s.Add(uv2_1); data.uv2s.Add(uv2_2); data.uv2s.Add(uv2_3); // do anisotropy flip if (uv2_0.y + uv2_2.y > uv2_1.y + uv2_3.y) { data.AddQuadTriangles(); } else { data.AddFlippedQuadTriangles(); } }
static void AddBlockFaces(int x, int y, int z, ref NativeMeshData data, ref NativeArray3x3 <Block> blocks, ref NativeArray3x3 <Light> lights, NativeArray <BlockData> blockData) { bool isLight = LightCalculator.GetIsLight(lights.Get(x, y, z).torch); if (!BlockData.RenderSolid(blockData, blocks.Get(x - 1, y, z), Dir.east)) { AddWestFace(x, y, z, ref data); AddUVs(x, y, z, ref data, ref blocks, blockData, Dir.west, isLight); data.AddFaceColor(LightCalculator.GetColorFromLight(lights.Get(x - 1, y, z))); data.AddFaceNormal(Dirs.norm3f[Dirs.WEST]); data.faces.Add(new Face { pos = (ushort)(x + z * Chunk.SIZE + y * Chunk.SIZE * Chunk.SIZE), dir = Dir.west }); } if (!BlockData.RenderSolid(blockData, blocks.Get(x, y - 1, z), Dir.up)) { AddDownFace(x, y, z, ref data); AddUVs(x, y, z, ref data, ref blocks, blockData, Dir.down, isLight); data.AddFaceColor(LightCalculator.GetColorFromLight(lights.Get(x, y - 1, z))); data.AddFaceNormal(Dirs.norm3f[Dirs.DOWN]); data.faces.Add(new Face { pos = (ushort)(x + z * Chunk.SIZE + y * Chunk.SIZE * Chunk.SIZE), dir = Dir.down }); } if (!BlockData.RenderSolid(blockData, blocks.Get(x, y, z - 1), Dir.north)) { AddSouthFace(x, y, z, ref data); AddUVs(x, y, z, ref data, ref blocks, blockData, Dir.south, isLight); data.AddFaceColor(LightCalculator.GetColorFromLight(lights.Get(x, y, z - 1))); data.AddFaceNormal(Dirs.norm3f[Dirs.SOUTH]); data.faces.Add(new Face { pos = (ushort)(x + z * Chunk.SIZE + y * Chunk.SIZE * Chunk.SIZE), dir = Dir.south }); } if (!BlockData.RenderSolid(blockData, blocks.Get(x + 1, y, z), Dir.west)) { AddEastFace(x, y, z, ref data); AddUVs(x, y, z, ref data, ref blocks, blockData, Dir.east, isLight); data.AddFaceColor(LightCalculator.GetColorFromLight(lights.Get(x + 1, y, z))); data.AddFaceNormal(Dirs.norm3f[Dirs.EAST]); data.faces.Add(new Face { pos = (ushort)(x + z * Chunk.SIZE + y * Chunk.SIZE * Chunk.SIZE), dir = Dir.east }); } if (!BlockData.RenderSolid(blockData, blocks.Get(x, y + 1, z), Dir.down)) { AddUpFace(x, y, z, ref data); AddUVs(x, y, z, ref data, ref blocks, blockData, Dir.up, isLight); data.AddFaceColor(LightCalculator.GetColorFromLight(lights.Get(x, y + 1, z))); data.AddFaceNormal(Dirs.norm3f[Dirs.UP]); data.faces.Add(new Face { pos = (ushort)(x + z * Chunk.SIZE + y * Chunk.SIZE * Chunk.SIZE), dir = Dir.up }); } if (!BlockData.RenderSolid(blockData, blocks.Get(x, y, z + 1), Dir.south)) { AddNorthFace(x, y, z, ref data); AddUVs(x, y, z, ref data, ref blocks, blockData, Dir.north, isLight); data.AddFaceColor(LightCalculator.GetColorFromLight(lights.Get(x, y, z + 1))); data.AddFaceNormal(Dirs.norm3f[Dirs.NORTH]); data.faces.Add(new Face { pos = (ushort)(x + z * Chunk.SIZE + y * Chunk.SIZE * Chunk.SIZE), dir = Dir.north }); } }
static int GetTextureIndex(Dir dir, int x, int y, int z, ref NativeArray3x3 <Block> blocks) { Block b = blocks.Get(x, y, z); if (b == Blocks.GRASS) { switch (dir) { case Dir.up: return(2); case Dir.down: return(1); } if (blocks.Get(x, y + 1, z) != Blocks.AIR) { return(1); } switch (dir) { case Dir.west: if (blocks.Get(x - 1, y - 1, z) == Blocks.GRASS) { return(2); } break; case Dir.east: if (blocks.Get(x + 1, y - 1, z) == Blocks.GRASS) { return(2); } break; case Dir.south: if (blocks.Get(x, y - 1, z - 1) == Blocks.GRASS) { return(2); } break; case Dir.north: if (blocks.Get(x, y - 1, z + 1) == Blocks.GRASS) { return(2); } break; } return(3); } else if (b == Blocks.PINE) { switch (dir) { case Dir.up: case Dir.down: return(5); default: return(4); } } return(-1); // shouldnt ever reach this point }