├── Game.rbxl ├── src └── StarterPlayer │ └── StarterPlayerScripts │ ├── Marching │ ├── CellStyles │ │ ├── rock.lua │ │ ├── lightRock.lua │ │ ├── worldBorder.lua │ │ ├── init.lua │ │ └── diamond.lua │ ├── GenerationConfig.lua │ ├── TriStorage.lua │ ├── createTriangle.lua │ ├── init.lua │ ├── GridCell.lua │ └── MarchTables.lua │ ├── VolumeGrid.lua │ └── Interaction.client.lua ├── default.project.json ├── README.md └── LICENSE /Game.rbxl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imaginaerume/MarchingCubeCaves/HEAD/Game.rbxl -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/CellStyles/rock.lua: -------------------------------------------------------------------------------- 1 | return { 2 | Color = Color3.fromRGB(200, 200, 200), 3 | Material = "SmoothPlastic", 4 | } -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/CellStyles/lightRock.lua: -------------------------------------------------------------------------------- 1 | return { 2 | Color = Color3.fromRGB(220,220,220), 3 | Material = "SmoothPlastic", 4 | } -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/CellStyles/worldBorder.lua: -------------------------------------------------------------------------------- 1 | return { 2 | Color = Color3.fromRGB(60, 60, 60), 3 | Material = "SmoothPlastic", 4 | } -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/CellStyles/init.lua: -------------------------------------------------------------------------------- 1 | local CellStyles = {} 2 | 3 | for _, style in pairs(script:GetChildren()) do 4 | CellStyles[style.Name] = require(style) 5 | end 6 | 7 | return CellStyles -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/CellStyles/diamond.lua: -------------------------------------------------------------------------------- 1 | return { 2 | Color = Color3.fromRGB(63, 126, 165), 3 | Material = "Neon", 4 | MaterialStrength = 0.7, 5 | Children = { 6 | { 7 | class = "PointLight", 8 | props = { 9 | Color = Color3.fromRGB(86, 120, 255), 10 | Brightness = 0.225, 11 | Range = 4, 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project", 3 | "tree": { 4 | "$className": "DataModel", 5 | "StarterPlayer": { 6 | "$className": "StarterPlayer", 7 | "StarterPlayerScripts": { 8 | "$className": "StarterPlayerScripts", 9 | "$ignoreUnknownInstances": true, 10 | "$path": "src/StarterPlayer/StarterPlayerScripts" 11 | }, 12 | "$ignoreUnknownInstances": true 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/GenerationConfig.lua: -------------------------------------------------------------------------------- 1 | return { 2 | --enables the ore counting Gui 3 | oreCounterEnabled = true, 4 | 5 | --cell scale 6 | worldScale = 6, 7 | 8 | --size of world grid 9 | cellsPerAxis = 24, 10 | 11 | --cutoff value for marching cubes generation 12 | isoLevel = 0.5, 13 | 14 | --perlin noise values 15 | terrainAmplitude = 500, 16 | terrainFrequency = 60, 17 | 18 | oreAmplitude = 300, 19 | oreFrequency = 200, 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Marching cube caves 2 | A Roblox Lua implementation of the Marching Cubes algorithm, in this case used to generate cool triangular caves and ore veins with Perlin Noise. 3 | 4 | ## Screenshots 5 | ![A cave](https://i.imgur.com/z7ItkEt.jpg) 6 | 7 | ## Controls 8 | E: Throw light 9 | 10 | Q: Throw sticky light 11 | 12 | G: Connect grapple to point 13 | 14 | Hold right click: reel in grapple 15 | 16 | Spacebar: Stop grappling 17 | 18 | ## Learning sources 19 | Sebastian Lague 20 | * https://github.com/SebLague/Marching-Cubes 21 | * "Coding Adventure: Marching Cubes": https://www.youtube.com/watch?v=M3iI2l0ltbE 22 | 23 | Paul Bourke 24 | * "Polygonising a scalar field": http://paulbourke.net/geometry/polygonise/ 25 | -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/VolumeGrid.lua: -------------------------------------------------------------------------------- 1 | --Generates a table type that can be indexed in 3 dimensions by creating a new table if one doesn't exist for the x and y dimensions 2 | local VolumeGrid = {} 3 | 4 | function VolumeGrid.new() 5 | return setmetatable({}, { 6 | __index = function(grid, x) 7 | --return the grid class method if it exists 8 | if VolumeGrid[x] then 9 | return VolumeGrid[x] 10 | end 11 | 12 | local row = {} 13 | grid[x] = row 14 | 15 | setmetatable(row, { 16 | __index = function(_, y) 17 | local column = {} 18 | row[y] = column 19 | 20 | return column 21 | end 22 | }) 23 | 24 | return row 25 | end 26 | }) 27 | end 28 | 29 | function VolumeGrid:addCell(x,y,z, cell) 30 | cell.grid = self 31 | self[x][y][z] = cell 32 | end 33 | 34 | return VolumeGrid -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Imaginaerume 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/TriStorage.lua: -------------------------------------------------------------------------------- 1 | --Preemptively adds triangles to the world and reuses them to reduce load 2 | local buffer = 5000 3 | local TriStorage = {} 4 | local tris = {} 5 | 6 | local container = Instance.new("Model", workspace) 7 | container.Name = "triBinContainer" 8 | 9 | function TriStorage.add(tri) 10 | if not tri then 11 | tri = Instance.new("WedgePart") 12 | end 13 | 14 | tri.Anchored = true 15 | tri.CanCollide = true 16 | tri.TopSurface = 0 17 | tri.BottomSurface = 0 18 | tri.Size = Vector3.new(5, 5, 5) 19 | tri.CFrame = CFrame.new(0, -500, 0) 20 | tri.Parent = container 21 | 22 | tris[#tris+1] = tri 23 | end 24 | 25 | function TriStorage.get() 26 | local tri = tris[1] 27 | table.remove(tris, 1) 28 | 29 | if #tris == 0 then 30 | fillBuffer() 31 | end 32 | 33 | return tri 34 | end 35 | 36 | function TriStorage.recycle(tri) 37 | tri.Transparency = 0 38 | tri.Material = "SmoothPlastic" 39 | 40 | TriStorage.add(tri) 41 | end 42 | 43 | function fillBuffer() 44 | for i = #tris, buffer do 45 | TriStorage.add() 46 | end 47 | end 48 | 49 | fillBuffer() 50 | 51 | return TriStorage -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/createTriangle.lua: -------------------------------------------------------------------------------- 1 | local TriStorage = require(script.Parent.TriStorage) 2 | local getTri = TriStorage.get 3 | local thickness = 0 4 | 5 | local function cfFromAxes(p, x, y, z) 6 | return CFrame.new( 7 | p.x, p.y, p.z, 8 | x.x, y.x, z.x, 9 | x.y, y.y, z.y, 10 | x.z, y.z, z.z 11 | ) 12 | end 13 | 14 | return function(a, b, c) 15 | local ab, ac, bc = b - a, c - a, c - b 16 | local abl, acl, bcl = ab.magnitude, ac.magnitude, bc.magnitude 17 | 18 | if abl > bcl and abl > acl then 19 | c, a = a, c 20 | elseif acl > bcl and acl > abl then 21 | a, b = b, a 22 | end 23 | 24 | ab, ac, bc = b - a, c - a, c - b 25 | 26 | local t1 = getTri() 27 | local t2 = getTri() 28 | 29 | local out = ac:Cross(ab).unit 30 | local biDir = bc:Cross(out).unit 31 | local biLength = math.abs(ab:Dot(biDir)) 32 | local bcLength = bc.magnitude 33 | 34 | t1.Size = Vector3.new(thickness, math.abs(ab:Dot(bc))/bcLength, biLength) 35 | t2.Size = Vector3.new(thickness, biLength, math.abs(ac:Dot(bc))/bcLength) 36 | 37 | bc = -bc.unit 38 | t1.CFrame = cfFromAxes((a+b)/2, -out, bc, -biDir) 39 | t2.CFrame = cfFromAxes((a+c)/2, -out, biDir, bc) 40 | 41 | return t1, t2 42 | end -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Interaction.client.lua: -------------------------------------------------------------------------------- 1 | --horrible demo code for throwing lights, digging, and grappling 2 | local UIS = game:GetService("UserInputService") 3 | UIS.MouseIconEnabled = false 4 | 5 | repeat wait() until game.Players.LocalPlayer.Character 6 | 7 | local Mouse = game.Players.LocalPlayer:GetMouse() 8 | local Character = game.Players.LocalPlayer.Character 9 | 10 | local explode = require(script.Parent.Marching) 11 | 12 | local light = Instance.new("PointLight", Character.HumanoidRootPart) 13 | light.Color = Color3.new(0.5, 0.5, 0.9) 14 | light.Range = 15 15 | light.Brightness = 0.35 16 | 17 | local function fireProjectile(start, velocity, range, stopOnTouch) 18 | local projectile = Instance.new("Part", workspace) 19 | projectile.Shape = "Ball" 20 | projectile.Size = Vector3.new(1.5, 1.5, 1.5) 21 | projectile.TopSurface = 0 22 | projectile.BottomSurface = 0 23 | projectile.Color = Color3.fromHSV(Random.new():NextNumber(0,1), 0.9, 0.9) 24 | projectile.Material = "Neon" 25 | projectile.CFrame = CFrame.new(start) 26 | projectile.CanCollide = false 27 | projectile.Velocity = (Mouse.Hit.p-start).unit*velocity 28 | 29 | local light = Instance.new("PointLight", projectile) 30 | light.Color = projectile.Color 31 | light.Range = range 32 | 33 | wait() 34 | 35 | projectile.CanCollide = true 36 | 37 | if stopOnTouch then 38 | projectile.Touched:connect(function() 39 | projectile.Anchored = true 40 | end) 41 | end 42 | end 43 | 44 | local rope 45 | local shouldReel = false 46 | 47 | UIS.InputBegan:connect(function(input) 48 | local character = game.Players.LocalPlayer.Character 49 | local start = (character.HumanoidRootPart.CFrame * CFrame.new(0, 3, 0)).p 50 | 51 | if input.KeyCode == Enum.KeyCode.E then 52 | fireProjectile(start, 180, 30, false) 53 | elseif input.KeyCode == Enum.KeyCode.Q then 54 | fireProjectile(start, 360, 60, true) 55 | elseif input.KeyCode == Enum.KeyCode.Space and rope then 56 | rope:Destroy() 57 | rope = nil 58 | shouldReel = false 59 | elseif input.KeyCode == Enum.KeyCode.G and not rope then 60 | local a1 = Instance.new("Attachment", Character.HumanoidRootPart) 61 | local a2 = Instance.new("Attachment", workspace.Terrain) 62 | a2.Position = Mouse.Hit.p 63 | 64 | rope = Instance.new("RopeConstraint", Character) 65 | rope.Length = (a1.WorldPosition-a2.WorldPosition).magnitude+1 66 | rope.Attachment0 = a2 67 | rope.Attachment1 = a1 68 | rope.Visible = true 69 | 70 | elseif input.UserInputType == Enum.UserInputType.MouseButton2 then 71 | shouldReel = true 72 | elseif input.UserInputType == Enum.UserInputType.MouseButton1 then 73 | explode(Mouse.Hit.p, 1) 74 | end 75 | end) 76 | 77 | UIS.InputEnded:connect(function(input) 78 | if input.UserInputType == Enum.UserInputType.MouseButton2 then 79 | shouldReel = false 80 | end 81 | end) 82 | 83 | game:GetService("RunService").Heartbeat:connect(function() 84 | if rope and rope.Length > 5 and shouldReel then 85 | rope.Length = rope.Length - 0.15 86 | end 87 | end) 88 | -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/init.lua: -------------------------------------------------------------------------------- 1 | --Dependencies 2 | local GenerationConfig = require(script.GenerationConfig) 3 | local VolumeGrid = require(script.Parent:WaitForChild("VolumeGrid")) 4 | local GridCell = require(script.GridCell) 5 | 6 | --Config values 7 | local WORLD_SCALE = GenerationConfig.worldScale 8 | local CELLS_PER_AXIS = GenerationConfig.cellsPerAxis 9 | local ISO_LEVEL = GenerationConfig.isoLevel 10 | 11 | local TERRAIN_AMPLITUDE = GenerationConfig.terrainAmplitude 12 | local TERRAIN_FREQUENCY = GenerationConfig.terrainFrequency 13 | local ORE_AMPLITUDE = GenerationConfig.oreAmplitude 14 | local ORE_FREQUENCY = GenerationConfig.oreFrequency 15 | 16 | --Randomizes the perlin noise grid 17 | local NOISE_OFFSET = Random.new():NextNumber(-(10^8), 10^8) 18 | local grid = VolumeGrid.new() 19 | 20 | --Generate cell grid 21 | for x = 1, CELLS_PER_AXIS do 22 | for y = 1, CELLS_PER_AXIS do 23 | for z = 1, CELLS_PER_AXIS do 24 | local noise = math.clamp(math.noise( 25 | (NOISE_OFFSET+x*TERRAIN_FREQUENCY)/TERRAIN_AMPLITUDE, 26 | (NOISE_OFFSET+y*TERRAIN_FREQUENCY)/TERRAIN_AMPLITUDE, 27 | (NOISE_OFFSET+z*TERRAIN_FREQUENCY)/TERRAIN_AMPLITUDE 28 | ), -0.5, 0.5) 29 | 30 | local oreNoise = math.clamp(math.noise( 31 | (NOISE_OFFSET+y*ORE_FREQUENCY)/ORE_AMPLITUDE, 32 | (NOISE_OFFSET+x*ORE_FREQUENCY)/ORE_AMPLITUDE, 33 | (NOISE_OFFSET+z*ORE_FREQUENCY)/ORE_AMPLITUDE 34 | ), -0.5, 0.5) 35 | 36 | local cell = GridCell.new(x,y,z, noise + 0.5) 37 | 38 | if oreNoise < 0.4 then 39 | cell.cellType = "rock" 40 | elseif oreNoise < 0.4999 then 41 | cell.cellType = "lightRock" 42 | elseif oreNoise >= 0.4999 then 43 | cell.cellType = "diamond" 44 | end 45 | 46 | if x == 1 or y == 1 or z == 1 or x == CELLS_PER_AXIS or y == CELLS_PER_AXIS or z == CELLS_PER_AXIS then 47 | cell.cellType = "worldBorder" 48 | end 49 | 50 | grid:addCell(x,y,z, cell) 51 | end 52 | end 53 | end 54 | 55 | --seal the edges 56 | for x = 1, CELLS_PER_AXIS do 57 | for y = 1, CELLS_PER_AXIS do 58 | grid[x][y][1].w = 1 59 | grid[x][y][CELLS_PER_AXIS].w = 1 60 | end 61 | end 62 | 63 | for z = 1, CELLS_PER_AXIS do 64 | for y = 1, CELLS_PER_AXIS do 65 | grid[1][y][z].w = 1 66 | grid[CELLS_PER_AXIS][y][z].w = 1 67 | end 68 | end 69 | 70 | for x = 1, CELLS_PER_AXIS do 71 | for z = 1, CELLS_PER_AXIS do 72 | grid[x][1][z].w = 1 73 | grid[x][CELLS_PER_AXIS][z].w = 1 74 | end 75 | end 76 | 77 | --render the grid 78 | for x, row in pairs(grid) do 79 | for y, column in pairs(row) do 80 | for z, cell in pairs(column) do 81 | cell:update() 82 | end 83 | end 84 | end 85 | 86 | --Digging, explosions 87 | function explosion(position, radius) 88 | --format position 89 | local position = position / WORLD_SCALE 90 | local roundedPos = Vector3.new(math.floor(position.x+0.5), math.floor(position.y+0.5), math.floor(position.z+0.5)) 91 | 92 | local origin = grid[roundedPos.x][roundedPos.y][roundedPos.z] 93 | 94 | if origin then 95 | local inRadius = origin:getCellsInRadius(radius) 96 | 97 | --Set the cell to non-existent, but don't update until all cells have been destroyed to reduce duplicate checks 98 | for _, cell in pairs(inRadius) do 99 | cell:destroy(true) 100 | end 101 | 102 | --Check all cells surrounding the explosion once 103 | local alreadyUpdated = {} 104 | 105 | for _, adjacent in pairs(origin:getCellsInRadius(radius+2)) do 106 | if not alreadyUpdated[adjacent] then 107 | adjacent:update() 108 | alreadyUpdated[adjacent] = true 109 | end 110 | end 111 | end 112 | end 113 | 114 | --Place the player in an open area 115 | local player = game.Players.LocalPlayer 116 | local character = player.Character or (player.CharacterAdded:wait() and player.Character) 117 | local emptyAbove = 8 118 | 119 | for x, row in pairs(grid) do 120 | for y, column in pairs(row) do 121 | for z, cell in pairs(column) do 122 | local isEmptyAbove = true 123 | 124 | for y2 = 1, emptyAbove do 125 | if not grid[x][y+y2][z] or grid[x][y+y2][z].w >= ISO_LEVEL then 126 | isEmptyAbove = false 127 | end 128 | end 129 | 130 | if cell.w > ISO_LEVEL and isEmptyAbove then 131 | character.HumanoidRootPart.CFrame = CFrame.new(Vector3.new(cell.x, cell.y + 4, cell.z)*WORLD_SCALE) 132 | break 133 | end 134 | end 135 | end 136 | end 137 | 138 | return explosion 139 | -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/GridCell.lua: -------------------------------------------------------------------------------- 1 | local GenerationConfig = require(script.Parent.GenerationConfig) 2 | local MarchTables = require(script.Parent.MarchTables) 3 | local createTriangle = require(script.Parent.createTriangle) 4 | local TriStorage = require(script.Parent.TriStorage) 5 | local CellStyles = require(script.Parent.CellStyles) 6 | 7 | local OresCounter 8 | if GenerationConfig.oreCounterEnabled then 9 | local PlayerGui = game.Players.LocalPlayer:WaitForChild("PlayerGui") 10 | OresCounter = PlayerGui.ScreenGui.Ores 11 | end 12 | 13 | --Marching cubes tables 14 | local TRIANGULATION = MarchTables.triangulation 15 | local CORNER_INDEX_A_FROM_EDGE = MarchTables.cornerIndexAFromEdge 16 | local CORNER_INDEX_B_FROM_EDGE = MarchTables.cornerIndexBFromEdge 17 | 18 | local WORLD_SCALE = GenerationConfig.worldScale 19 | local CELLS_PER_AXIS = GenerationConfig.cellsPerAxis 20 | local ISO_LEVEL = GenerationConfig.isoLevel 21 | 22 | --Helper functions 23 | local function renderTriangle(posA, posB, posC) 24 | return createTriangle(posA*WORLD_SCALE, posB*WORLD_SCALE, posC*WORLD_SCALE) 25 | end 26 | 27 | local function interpolateVerts(v1, v2) 28 | return Vector3.new(v1.x, v1.y, v1.z):lerp(Vector3.new(v2.x, v2.y, v2.z), 0.5) 29 | end 30 | 31 | local worldModel = Instance.new("Model", workspace) 32 | worldModel.Name = "MarchingCubesWorld" 33 | 34 | --Grid cell class 35 | local GridCell = {} 36 | 37 | function GridCell.new(x,y,z, w) 38 | local cell = { 39 | x = x, 40 | y = y, 41 | z = z, 42 | w = w, 43 | tris = {}, 44 | } 45 | 46 | setmetatable(cell, {__index = GridCell}) 47 | 48 | return cell 49 | end 50 | 51 | function GridCell:destroy(dontUpdate) 52 | if self.w ~= 0 and self.cellType ~= "worldBorder" then 53 | self.w = 0 54 | 55 | if GenerationConfig.oreCounterEnabled and OresCounter:FindFirstChild(self.cellType) then 56 | OresCounter[self.cellType].Quantity.Text = tonumber(OresCounter[self.cellType].Quantity.Text) + 1 57 | end 58 | 59 | if not dontUpdate then 60 | self:updateAdjacent() 61 | end 62 | end 63 | end 64 | 65 | function GridCell:updateAdjacent() 66 | for _, cell in pairs(self:getAdjacent()) do 67 | cell:update() 68 | end 69 | end 70 | 71 | function GridCell:getAdjacent(facesOnly) 72 | local grid = self.grid 73 | local adjacent = {} 74 | 75 | if not facesOnly then 76 | for x = self.x-1, self.x+1 do 77 | for y = self.y-1, self.y+1 do 78 | for z = self.z-1, self.y+1 do 79 | table.insert(adjacent, grid[x][y][z]) 80 | end 81 | end 82 | end 83 | else 84 | local x,y,z = self.x, self.y, self.z 85 | 86 | table.insert(adjacent, grid[x][y][z]) 87 | table.insert(adjacent, grid[x+1][y][z]) 88 | table.insert(adjacent, grid[x][y][z+1]) 89 | table.insert(adjacent, grid[x][y+1][z]) 90 | table.insert(adjacent, grid[x-1][y][z]) 91 | table.insert(adjacent, grid[x][y][z-1]) 92 | table.insert(adjacent, grid[x][y-1][z]) 93 | end 94 | 95 | return adjacent 96 | end 97 | 98 | function GridCell:getCellsInRadius(radius) 99 | local grid = self.grid 100 | local cells = {} 101 | 102 | for x = self.x - radius, self.x + radius do 103 | for y = self.y - radius, self.y + radius do 104 | for z = self.z - radius, self.z + radius do 105 | if grid[x][y][z] and (Vector3.new(self.x, self.y, self.z) - Vector3.new(x, y, z)).magnitude <= radius then 106 | table.insert(cells, grid[x][y][z]) 107 | end 108 | end 109 | end 110 | end 111 | 112 | return cells 113 | end 114 | 115 | function GridCell:getCellsInRange(range) 116 | local grid = self.grid 117 | local cells = {} 118 | 119 | for x = self.x - range, self.x + range do 120 | for y = self.y - range, self.y + range do 121 | for z = self.z - range, self.z + range do 122 | table.insert(cells, grid[x][y][z]) 123 | end 124 | end 125 | end 126 | 127 | return cells 128 | end 129 | 130 | function GridCell:update() 131 | local grid = self.grid 132 | local cell = self 133 | local w = cell.w 134 | 135 | if cell.x >= CELLS_PER_AXIS or cell.y >= CELLS_PER_AXIS or cell.z >= CELLS_PER_AXIS then 136 | return 137 | end 138 | 139 | local cubeCorners = { 140 | grid[cell.x][cell.y][cell.z], 141 | grid[cell.x+1][cell.y][cell.z], 142 | grid[cell.x+1][cell.y][cell.z+1], 143 | grid[cell.x][cell.y][cell.z+1], 144 | grid[cell.x][cell.y+1][cell.z], 145 | grid[cell.x+1][cell.y+1][cell.z], 146 | grid[cell.x+1][cell.y+1][cell.z+1], 147 | grid[cell.x][cell.y+1][cell.z+1] 148 | } 149 | 150 | local cubeIndex = 0; 151 | for i = 0, 7 do 152 | if cubeCorners[i+1] and cubeCorners[i+1].w < ISO_LEVEL then 153 | cubeIndex = bit32.bor(cubeIndex, bit32.lshift(1, i)) 154 | end 155 | end 156 | 157 | cubeIndex = cubeIndex + 1 158 | 159 | if cell.lastCubeIndex and cell.lastCubeIndex == cubeIndex then 160 | return 161 | end 162 | 163 | cell.lastCubeIndex = cubeIndex 164 | 165 | local newTris = {} 166 | 167 | local i = 1 168 | while TRIANGULATION[cubeIndex][i] ~= -1 do 169 | local a0 = CORNER_INDEX_A_FROM_EDGE[TRIANGULATION[cubeIndex][i]+1] 170 | local b0 = CORNER_INDEX_B_FROM_EDGE[TRIANGULATION[cubeIndex][i]+1] 171 | 172 | local a1 = CORNER_INDEX_A_FROM_EDGE[TRIANGULATION[cubeIndex][i+1]+1] 173 | local b1 = CORNER_INDEX_B_FROM_EDGE[TRIANGULATION[cubeIndex][i+1]+1] 174 | 175 | local a2 = CORNER_INDEX_A_FROM_EDGE[TRIANGULATION[cubeIndex][i+2]+1] 176 | local b2 = CORNER_INDEX_B_FROM_EDGE[TRIANGULATION[cubeIndex][i+2]+1] 177 | 178 | local t1, t2 = renderTriangle( 179 | interpolateVerts(cubeCorners[a0+1], cubeCorners[b0+1]), 180 | interpolateVerts(cubeCorners[a1+1], cubeCorners[b1+1]), 181 | interpolateVerts(cubeCorners[a2+1], cubeCorners[b2+1]) 182 | ) 183 | 184 | local style = CellStyles[cell.cellType] 185 | for prop, val in pairs(style) do 186 | if prop ~= "Children" and prop ~= "MaterialStrength" then 187 | if not style.MaterialStrength or (prop ~= "Material") then 188 | t1[prop] = val 189 | t2[prop] = val 190 | end 191 | end 192 | end 193 | 194 | --Material strength affects how visible the material is by making a translucent copy that layers on top of a smooth, opaque part 195 | if style.MaterialStrength then 196 | local base1 = t1:Clone() 197 | base1.Transparency = style.MaterialStrength 198 | base1.Material = style.Material 199 | table.insert(newTris, base1) 200 | 201 | local base2 = t2:Clone() 202 | base2.Transparency = style.MaterialStrength 203 | base2.Material = style.Material 204 | table.insert(newTris, base2) 205 | 206 | t1.Transparency = 0 207 | t2.Transparency = 0 208 | end 209 | 210 | if style.Children then 211 | for _, childData in pairs(style.Children) do 212 | local child = Instance.new(childData.class) 213 | for prop, value in pairs(childData.props) do 214 | child[prop] = value 215 | end 216 | child.Parent = t1 217 | child:Clone().Parent = t2 218 | end 219 | end 220 | 221 | i = i + 3 222 | 223 | table.insert(newTris, t1) 224 | table.insert(newTris, t2) 225 | end 226 | 227 | --clear old tris 228 | for _, tri in pairs(self.tris) do 229 | TriStorage.recycle(tri) 230 | end 231 | 232 | --render new tris 233 | for _, tri in pairs(newTris) do 234 | tri.Parent = worldModel 235 | end 236 | 237 | self.tris = newTris 238 | end 239 | 240 | return GridCell -------------------------------------------------------------------------------- /src/StarterPlayer/StarterPlayerScripts/Marching/MarchTables.lua: -------------------------------------------------------------------------------- 1 | --Contains the marching cubes lookup table to determine what triangles belong to a certain cell types 2 | local triangulation = { 3 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 4 | { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 5 | { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 6 | { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 7 | { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 8 | { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 9 | { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 10 | { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, 11 | { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 12 | { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 13 | { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 14 | { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, 15 | { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 16 | { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, 17 | { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, 18 | { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 19 | { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 20 | { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 21 | { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 22 | { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, 23 | { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 24 | { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, 25 | { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, 26 | { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, 27 | { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 28 | { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, 29 | { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, 30 | { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, 31 | { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, 32 | { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, 33 | { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, 34 | { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, 35 | { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 36 | { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 37 | { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 38 | { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, 39 | { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 40 | { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, 41 | { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, 42 | { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, 43 | { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 44 | { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, 45 | { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, 46 | { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, 47 | { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, 48 | { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, 49 | { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, 50 | { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, 51 | { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 52 | { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, 53 | { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, 54 | { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 55 | { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, 56 | { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, 57 | { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, 58 | { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, 59 | { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, 60 | { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, 61 | { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, 62 | { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, 63 | { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, 64 | { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, 65 | { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, 66 | { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 67 | { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 68 | { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 69 | { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 70 | { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, 71 | { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 72 | { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, 73 | { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, 74 | { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, 75 | { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 76 | { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, 77 | { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, 78 | { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, 79 | { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, 80 | { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, 81 | { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, 82 | { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, 83 | { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 84 | { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, 85 | { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, 86 | { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, 87 | { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, 88 | { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, 89 | { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, 90 | { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, 91 | { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, 92 | { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, 93 | { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, 94 | { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, 95 | { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, 96 | { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, 97 | { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, 98 | { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, 99 | { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 100 | { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, 101 | { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, 102 | { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, 103 | { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, 104 | { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, 105 | { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 106 | { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, 107 | { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, 108 | { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, 109 | { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, 110 | { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, 111 | { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, 112 | { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, 113 | { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, 114 | { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 115 | { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, 116 | { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, 117 | { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, 118 | { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, 119 | { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, 120 | { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, 121 | { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, 122 | { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 123 | { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, 124 | { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, 125 | { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, 126 | { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, 127 | { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, 128 | { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 129 | { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, 130 | { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 131 | { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 132 | { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 133 | { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 134 | { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, 135 | { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 136 | { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, 137 | { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, 138 | { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, 139 | { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 140 | { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, 141 | { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, 142 | { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, 143 | { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, 144 | { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, 145 | { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, 146 | { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, 147 | { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 148 | { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, 149 | { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, 150 | { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, 151 | { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, 152 | { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, 153 | { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, 154 | { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, 155 | { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, 156 | { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 157 | { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, 158 | { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, 159 | { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, 160 | { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, 161 | { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, 162 | { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 163 | { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 164 | { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, 165 | { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, 166 | { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, 167 | { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, 168 | { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, 169 | { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, 170 | { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, 171 | { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, 172 | { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, 173 | { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, 174 | { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, 175 | { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, 176 | { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, 177 | { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, 178 | { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, 179 | { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, 180 | { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, 181 | { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, 182 | { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, 183 | { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, 184 | { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, 185 | { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, 186 | { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, 187 | { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, 188 | { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, 189 | { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, 190 | { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 191 | { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, 192 | { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, 193 | { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 194 | { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 195 | { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 196 | { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, 197 | { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, 198 | { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, 199 | { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, 200 | { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, 201 | { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, 202 | { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, 203 | { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, 204 | { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, 205 | { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, 206 | { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, 207 | { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 208 | { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, 209 | { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, 210 | { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 211 | { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, 212 | { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, 213 | { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, 214 | { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, 215 | { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, 216 | { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, 217 | { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, 218 | { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 219 | { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, 220 | { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, 221 | { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, 222 | { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, 223 | { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, 224 | { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 225 | { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, 226 | { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 227 | { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, 228 | { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, 229 | { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, 230 | { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, 231 | { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, 232 | { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, 233 | { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, 234 | { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, 235 | { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, 236 | { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, 237 | { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, 238 | { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 239 | { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, 240 | { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, 241 | { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 242 | { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 243 | { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 244 | { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, 245 | { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, 246 | { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 247 | { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, 248 | { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, 249 | { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 250 | { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 251 | { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, 252 | { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 253 | { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, 254 | { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 255 | { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 256 | { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 257 | { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 258 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } 259 | } 260 | 261 | local cornerIndexAFromEdge = { 262 | 0, 263 | 1, 264 | 2, 265 | 3, 266 | 4, 267 | 5, 268 | 6, 269 | 7, 270 | 0, 271 | 1, 272 | 2, 273 | 3 274 | } 275 | 276 | local cornerIndexBFromEdge = { 277 | 1, 278 | 2, 279 | 3, 280 | 0, 281 | 5, 282 | 6, 283 | 7, 284 | 4, 285 | 4, 286 | 5, 287 | 6, 288 | 7 289 | } 290 | 291 | return { 292 | triangulation = triangulation, 293 | cornerIndexAFromEdge = cornerIndexAFromEdge, 294 | cornerIndexBFromEdge = cornerIndexBFromEdge, 295 | } --------------------------------------------------------------------------------