├── CSharpCode ├── DualContouring │ ├── Givens.cs │ ├── Mat3.cs │ ├── MatUtils.cs │ ├── Octree.cs │ ├── OctreeDrawInfo.cs │ ├── OctreeNode.cs │ ├── OctreeNodeType.cs │ ├── QefData.cs │ ├── QefSolver.cs │ ├── SMat3.cs │ ├── SVD.cs │ ├── Schur2.cs │ ├── density.cs │ └── mesh.cs ├── GameManager.cs └── Utils │ ├── Config.cs │ ├── DrawCubeInLines.cs │ ├── Noise │ └── PerlinNoise.cs │ └── PlayerInput.cs ├── DC_UnityPackage.zip └── README.md /CSharpCode/DualContouring/Givens.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This is free and unencumbered software released into the public domain. 3 | * 4 | * Anyone is free to copy, modify, publish, use, compile, sell, or 5 | * distribute this software, either in source code form or as a compiled 6 | * binary, for any purpose, commercial or non-commercial, and by any 7 | * means. 8 | * 9 | * In jurisdictions that recognize copyright laws, the author or authors 10 | * of this software dedicate any and all copyright interest in the 11 | * software to the public domain. We make this dedication for the benefit 12 | * of the public at large and to the detriment of our heirs and 13 | * successors. We intend this dedication to be an overt act of 14 | * relinquishment in perpetuity of all present and future rights to this 15 | * software under copyright law. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * For more information, please refer to 26 | */ 27 | 28 | public static class Givens 29 | { 30 | public static void rot01_post(Mat3 m, float c, float s) 31 | { 32 | float m00 = m.m00, m01 = m.m01, m10 = m.m10, m11 = m.m11, m20 = m.m20, m21 = m.m21; 33 | m.set(c * m00 - s * m01, s * m00 + c * m01, m.m02, c * m10 - s * m11, 34 | s * m10 + c * m11, m.m12, c * m20 - s * m21, s * m20 + c * m21, m.m22); 35 | } 36 | 37 | public static void rot02_post(Mat3 m, float c, float s) 38 | { 39 | float m00 = m.m00, m02 = m.m02, m10 = m.m10, m12 = m.m12, m20 = m.m20, m22 = m.m22; 40 | m.set(c * m00 - s * m02, m.m01, s * m00 + c * m02, c * m10 - s * m12, m.m11, 41 | s * m10 + c * m12, c * m20 - s * m22, m.m21, s * m20 + c * m22); 42 | } 43 | 44 | public static void rot12_post(Mat3 m, float c, float s) 45 | { 46 | float m01 = m.m01, m02 = m.m02, m11 = m.m11, m12 = m.m12, m21 = m.m21, m22 = m.m22; 47 | m.set(m.m00, c * m01 - s * m02, s * m01 + c * m02, m.m10, c * m11 - s * m12, 48 | s * m11 + c * m12, m.m20, c * m21 - s * m22, s * m21 + c * m22); 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /CSharpCode/DualContouring/Mat3.cs: -------------------------------------------------------------------------------- 1 | public class Mat3 2 | { 3 | public float m00, m01, m02, m10, m11, m12, m20, m21, m22; 4 | public Mat3() 5 | { } 6 | public Mat3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) { } 7 | public void clear() { } 8 | public void set(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) { } 9 | public void set(Mat3 rhs) { } 10 | public void setSymmetric(float a00, float a01, float a02, float a11, float a12, float a22) { } 11 | public void setSymmetric(SMat3 rhs) { } 12 | 13 | private Mat3(Mat3 rhs) { } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /CSharpCode/DualContouring/MatUtils.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public static class MatUtils 4 | { 5 | public static float fnorm(Mat3 a) 6 | { 7 | return Mathf.Sqrt((a.m00 * a.m00) + (a.m01 * a.m01) + (a.m02 * a.m02) 8 | + (a.m10 * a.m10) + (a.m11 * a.m11) + (a.m12 * a.m12) 9 | + (a.m20 * a.m20) + (a.m21 * a.m21) + (a.m22 * a.m22)); 10 | } 11 | 12 | public static float fnorm(SMat3 a) 13 | { 14 | return Mathf.Sqrt((a.m00 * a.m00) + (a.m01 * a.m01) + (a.m02 * a.m02) 15 | + (a.m01 * a.m01) + (a.m11 * a.m11) + (a.m12 * a.m12) 16 | + (a.m02 * a.m02) + (a.m12 * a.m12) + (a.m22 * a.m22)); 17 | } 18 | 19 | public static float off(Mat3 a) 20 | { 21 | return Mathf.Sqrt((a.m01 * a.m01) + (a.m02 * a.m02) + (a.m10 * a.m10) + (a.m12 * a.m12) + (a.m20 * a.m20) + (a.m21 * a.m21)); 22 | } 23 | 24 | public static float off(SMat3 a) 25 | { 26 | return Mathf.Sqrt(2 * ((a.m01 * a.m01) + (a.m02 * a.m02) + (a.m12 * a.m12))); 27 | } 28 | 29 | public static void mmul(out Mat3 Out, Mat3 a, Mat3 b) 30 | { 31 | Out = new Mat3(); 32 | Out.set(a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20, 33 | a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21, 34 | a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22, 35 | a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20, 36 | a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21, 37 | a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22, 38 | a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20, 39 | a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21, 40 | a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22); 41 | } 42 | 43 | public static void mmul_ata(out SMat3 Out, Mat3 a) 44 | { 45 | Out = new SMat3(); 46 | 47 | Out.setSymmetric(a.m00 * a.m00 + a.m10 * a.m10 + a.m20 * a.m20, 48 | a.m00 * a.m01 + a.m10 * a.m11 + a.m20 * a.m21, 49 | a.m00 * a.m02 + a.m10 * a.m12 + a.m20 * a.m22, 50 | a.m01 * a.m01 + a.m11 * a.m11 + a.m21 * a.m21, 51 | a.m01 * a.m02 + a.m11 * a.m12 + a.m21 * a.m22, 52 | a.m02 * a.m02 + a.m12 * a.m12 + a.m22 * a.m22); 53 | } 54 | 55 | public static void transpose(out Mat3 Out, Mat3 a) 56 | { 57 | Out = new Mat3(); 58 | 59 | Out.set(a.m00, a.m10, a.m20, a.m01, a.m11, a.m21, a.m02, a.m12, a.m22); 60 | } 61 | 62 | public static void vmul(out Vector3 Out, Mat3 a, Vector3 v) 63 | { 64 | Out = new Vector3( 65 | (a.m00 * v.x) + (a.m01 * v.y) + (a.m02 * v.z), 66 | (a.m10 * v.x) + (a.m11 * v.y) + (a.m12 * v.z), 67 | (a.m20 * v.x) + (a.m21 * v.y) + (a.m22 * v.z)); 68 | } 69 | 70 | public static void vmul_symmetric(out Vector3 Out, SMat3 a, Vector3 v) 71 | { 72 | Out = new Vector3( 73 | (a.m00 * v.x) + (a.m01 * v.y) + (a.m02 * v.z), 74 | (a.m01 * v.x) + (a.m11 * v.y) + (a.m12 * v.z), 75 | (a.m02 * v.x) + (a.m12 * v.y) + (a.m22 * v.z)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /CSharpCode/DualContouring/Octree.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Implementations of Octree member functions. 4 | 5 | Copyright (C) 2011 Tao Ju 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public License 9 | (LGPL) as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | */ 21 | 22 | using Code.Utils; 23 | using System.Collections.Generic; 24 | using UnityEngine; 25 | 26 | public class Octree 27 | { 28 | private static Color[] drawColors = new Color[] 29 | { 30 | Color.white, 31 | Color.yellow, 32 | Color.grey, 33 | Color.green, 34 | Color.blue, 35 | Color.black, 36 | Color.red, 37 | Color.cyan, 38 | }; 39 | 40 | public static int MATERIAL_AIR = 0; 41 | public static int MATERIAL_SOLID = 1; 42 | 43 | public static float QEF_ERROR = 1e-6f; 44 | public static int QEF_SWEEPS = 4; 45 | 46 | #region Readonly Variables 47 | public static readonly Vector3[] CHILD_MIN_OFFSETS = 48 | { 49 | // needs to match the vertMap from Dual Contouring impl 50 | new Vector3( 0, 0, 0 ), 51 | new Vector3( 0, 0, 1 ), 52 | new Vector3( 0, 1, 0 ), 53 | new Vector3( 0, 1, 1 ), 54 | new Vector3( 1, 0, 0 ), 55 | new Vector3( 1, 0, 1 ), 56 | new Vector3( 1, 1, 0 ), 57 | new Vector3( 1, 1, 1 ), 58 | }; 59 | 60 | // data from the original DC impl, drives the contouring process 61 | 62 | public static readonly int[][] edgevmap = new int[12][] 63 | { 64 | new int[2]{2,4},new int[2]{1,5},new int[2]{2,6},new int[2]{3,7}, // x-axis 65 | new int[2]{0,2},new int[2]{1,3},new int[2]{4,6},new int[2]{5,7}, // y-axis 66 | new int[2]{0,1},new int[2]{2,3},new int[2]{4,5},new int[2]{6,7} // z-axis 67 | }; 68 | 69 | public static readonly int[] edgemask = { 5, 3, 6 }; 70 | 71 | public static readonly int[][] vertMap = new int[8][] 72 | { 73 | new int[3]{0,0,0}, 74 | new int[3]{0,0,1}, 75 | new int[3]{0,1,0}, 76 | new int[3]{0,1,1}, 77 | new int[3]{1,0,0}, 78 | new int[3]{1,0,1}, 79 | new int[3]{1,1,0}, 80 | new int[3]{1,1,1} 81 | }; 82 | 83 | public static readonly int[][] faceMap = new int[6][] 84 | { 85 | new int[4]{4, 8, 5, 9}, 86 | new int[4]{6, 10, 7, 11}, 87 | new int[4]{0, 8, 1, 10}, 88 | new int[4]{2, 9, 3, 11}, 89 | new int[4]{0, 4, 2, 6}, 90 | new int[4]{1, 5, 3, 7} 91 | }; 92 | 93 | public static readonly int[][] cellProcFaceMask = new int[12][] 94 | { 95 | new int[3]{0,4,0}, 96 | new int[3]{1,5,0}, 97 | new int[3]{2,6,0}, 98 | new int[3]{3,7,0}, 99 | new int[3]{0,2,1}, 100 | new int[3]{4,6,1}, 101 | new int[3]{1,3,1}, 102 | new int[3]{5,7,1}, 103 | new int[3]{0,1,2}, 104 | new int[3]{2,3,2}, 105 | new int[3]{4,5,2}, 106 | new int[3]{6,7,2} 107 | }; 108 | 109 | public static readonly int[][] cellProcEdgeMask = new int[6][] 110 | { 111 | new int[5]{0,1,2,3,0}, 112 | new int[5]{4,5,6,7,0}, 113 | new int[5]{0,4,1,5,1}, 114 | new int[5]{2,6,3,7,1}, 115 | new int[5]{0,2,4,6,2}, 116 | new int[5]{1,3,5,7,2} 117 | }; 118 | 119 | public static readonly int[][][] faceProcFaceMask = new int[3][][] 120 | { 121 | new int[4][]{ new int[3]{4,0,0}, new int[3]{5,1,0}, new int[3]{6,2,0}, new int[3]{7,3,0} }, 122 | new int[4][]{ new int[3]{2,0,1}, new int[3]{6,4,1}, new int[3]{3,1,1}, new int[3]{7,5,1} }, 123 | new int[4][]{ new int[3]{1,0,2}, new int[3]{3,2,2}, new int[3]{5,4,2}, new int[3]{7,6,2} } 124 | }; 125 | 126 | public static readonly int[][][] faceProcEdgeMask = new int[3][][] 127 | { 128 | new int[4][]{new int[6]{1,4,0,5,1,1},new int[6]{1,6,2,7,3,1},new int[6]{0,4,6,0,2,2},new int[6]{0,5,7,1,3,2}}, 129 | new int[4][]{new int[6]{0,2,3,0,1,0},new int[6]{0,6,7,4,5,0},new int[6]{1,2,0,6,4,2},new int[6]{1,3,1,7,5,2}}, 130 | new int[4][]{new int[6]{1,1,0,3,2,0},new int[6]{1,5,4,7,6,0},new int[6]{0,1,5,0,4,1},new int[6]{0,3,7,2,6,1}} 131 | }; 132 | 133 | public static readonly int[][][] edgeProcEdgeMask = new int[3][][] 134 | { 135 | new int[2][]{new int[5]{3,2,1,0,0},new int[5]{7,6,5,4,0}}, 136 | new int[2][]{new int[5]{5,1,4,0,1},new int[5]{7,3,6,2,1}}, 137 | new int[2][]{new int[5]{6,4,2,0,2},new int[5]{7,5,3,1,2}}, 138 | }; 139 | 140 | public static readonly int[][] processEdgeMask = new int[3][] 141 | { 142 | new int[4]{3,2,1,0},new int[4]{7,5,6,4},new int[4]{11,10,9,8} 143 | }; 144 | #endregion 145 | 146 | 147 | public static OctreeNode SimplifyOctree(OctreeNode node, float threshold) 148 | { 149 | if (node == null) 150 | { 151 | return null; 152 | } 153 | 154 | if (node.Type != OctreeNodeType.Node_Internal) 155 | { 156 | // can't simplify! 157 | return node; 158 | } 159 | 160 | QefSolver qef = new QefSolver(); 161 | int[] signs = new int[8] { -1, -1, -1, -1, -1, -1, -1, -1 }; 162 | int midsign = -1; 163 | int edgeCount = 0; 164 | bool isCollapsible = true; 165 | 166 | for (int i = 0; i < 8; i++) 167 | { 168 | node.children[i] = SimplifyOctree(node.children[i], threshold); 169 | 170 | if (node.children[i] != null) 171 | { 172 | OctreeNode child = node.children[i]; 173 | 174 | if (child.Type == OctreeNodeType.Node_Internal) 175 | { 176 | isCollapsible = false; 177 | } 178 | else 179 | { 180 | qef.add(child.drawInfo.qef); 181 | 182 | midsign = (child.drawInfo.corners >> (7 - i)) & 1; 183 | signs[i] = (child.drawInfo.corners >> i) & 1; 184 | 185 | edgeCount++; 186 | } 187 | } 188 | } 189 | 190 | if (!isCollapsible) 191 | { 192 | // at least one child is an internal node, can't collapse 193 | return node; 194 | } 195 | 196 | Vector3 qefPosition = Vector3.zero; 197 | qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); 198 | float error = qef.getError(); 199 | 200 | // convert to glm vec3 for ease of use 201 | Vector3 position = new Vector3(qefPosition.x, qefPosition.y, qefPosition.z); 202 | 203 | // at this point the masspoint will actually be a sum, so divide to make it the average 204 | if (error > threshold) 205 | { 206 | // this collapse breaches the threshold 207 | return node; 208 | } 209 | 210 | if (position.x < node.min.x || position.x > (node.min.x + node.size) || 211 | position.y < node.min.y || position.y > (node.min.y + node.size) || 212 | position.z < node.min.z || position.z > (node.min.z + node.size)) 213 | { 214 | position = qef.getMassPoint(); 215 | } 216 | 217 | // change the node from an internal node to a 'psuedo leaf' node 218 | OctreeDrawInfo drawInfo = new OctreeDrawInfo(); 219 | drawInfo.corners = 0; 220 | drawInfo.index = -1; 221 | 222 | for (int i = 0; i < 8; i++) 223 | { 224 | if (signs[i] == -1) 225 | { 226 | // Undetermined, use centre sign instead 227 | drawInfo.corners |= (midsign << i); 228 | } 229 | else 230 | { 231 | drawInfo.corners |= (signs[i] << i); 232 | } 233 | } 234 | 235 | drawInfo.averageNormal = Vector3.zero; 236 | for (int i = 0; i < 8; i++) 237 | { 238 | if (node.children[i] != null) 239 | { 240 | OctreeNode child = node.children[i]; 241 | if (child.Type == OctreeNodeType.Node_Psuedo || 242 | child.Type == OctreeNodeType.Node_Leaf) 243 | { 244 | drawInfo.averageNormal += child.drawInfo.averageNormal; 245 | } 246 | } 247 | } 248 | 249 | drawInfo.averageNormal = drawInfo.averageNormal.normalized; 250 | drawInfo.position = position; 251 | drawInfo.qef = qef.getData(); 252 | 253 | for (int i = 0; i < 8; i++) 254 | { 255 | DestroyOctree(node.children[i]); 256 | node.children[i] = null; 257 | } 258 | 259 | node.Type = OctreeNodeType.Node_Psuedo; 260 | node.drawInfo = drawInfo; 261 | 262 | return node; 263 | } 264 | 265 | public static void GenerateVertexIndices(OctreeNode node, List vertexBuffer) 266 | { 267 | if (node == null) 268 | { 269 | return; 270 | } 271 | 272 | if (node.Type != OctreeNodeType.Node_Leaf) 273 | { 274 | for (int i = 0; i < 8; i++) 275 | { 276 | GenerateVertexIndices(node.children[i], vertexBuffer); 277 | } 278 | } 279 | 280 | if (node.Type != OctreeNodeType.Node_Internal) 281 | { 282 | node.drawInfo.index = vertexBuffer.Count; 283 | 284 | vertexBuffer.Add(new MeshVertex(node.drawInfo.position, node.drawInfo.averageNormal)); 285 | } 286 | } 287 | 288 | public static void ContourProcessEdge(OctreeNode[] node, int dir, List indexBuffer) 289 | { 290 | int minSize = 1000000; // arbitrary big number 291 | int minIndex = 0; 292 | int[] indices = new int[4] { -1, -1, -1, -1 }; 293 | bool flip = false; 294 | bool[] signChange = new bool[4] { false, false, false, false }; 295 | 296 | for (int i = 0; i < 4; i++) 297 | { 298 | int edge = processEdgeMask[dir][i]; 299 | int c1 = edgevmap[edge][0]; 300 | int c2 = edgevmap[edge][1]; 301 | 302 | int m1 = (node[i].drawInfo.corners >> c1) & 1; 303 | int m2 = (node[i].drawInfo.corners >> c2) & 1; 304 | 305 | if (node[i].size < minSize) 306 | { 307 | minSize = node[i].size; 308 | minIndex = i; 309 | flip = m1 != MATERIAL_AIR; 310 | } 311 | 312 | indices[i] = node[i].drawInfo.index; 313 | 314 | signChange[i] = 315 | (m1 == MATERIAL_AIR && m2 != MATERIAL_AIR) || 316 | (m1 != MATERIAL_AIR && m2 == MATERIAL_AIR); 317 | } 318 | 319 | if (signChange[minIndex]) 320 | { 321 | if (!flip) 322 | { 323 | indexBuffer.Add(indices[0]); 324 | indexBuffer.Add(indices[1]); 325 | indexBuffer.Add(indices[3]); 326 | 327 | indexBuffer.Add(indices[0]); 328 | indexBuffer.Add(indices[3]); 329 | indexBuffer.Add(indices[2]); 330 | } 331 | else 332 | { 333 | indexBuffer.Add(indices[0]); 334 | indexBuffer.Add(indices[3]); 335 | indexBuffer.Add(indices[1]); 336 | 337 | indexBuffer.Add(indices[0]); 338 | indexBuffer.Add(indices[2]); 339 | indexBuffer.Add(indices[3]); 340 | } 341 | 342 | } 343 | } 344 | 345 | public static void ContourEdgeProc(OctreeNode[] node, int dir, List indexBuffer) 346 | { 347 | if (node[0] == null || node[1] == null || node[2] == null || node[3] == null) 348 | { 349 | return; 350 | } 351 | 352 | if (node[0].Type != OctreeNodeType.Node_Internal && 353 | node[1].Type != OctreeNodeType.Node_Internal && 354 | node[2].Type != OctreeNodeType.Node_Internal && 355 | node[3].Type != OctreeNodeType.Node_Internal) 356 | { 357 | ContourProcessEdge(node, dir, indexBuffer); 358 | } 359 | else 360 | { 361 | for (int i = 0; i < 2; i++) 362 | { 363 | OctreeNode[] edgeNodes = new OctreeNode[4]; 364 | int[] c = new int[4] 365 | { 366 | edgeProcEdgeMask[dir][i][0], 367 | edgeProcEdgeMask[dir][i][1], 368 | edgeProcEdgeMask[dir][i][2], 369 | edgeProcEdgeMask[dir][i][3], 370 | }; 371 | 372 | for (int j = 0; j < 4; j++) 373 | { 374 | if (node[j].Type == OctreeNodeType.Node_Leaf || node[j].Type == OctreeNodeType.Node_Psuedo) 375 | { 376 | edgeNodes[j] = node[j]; 377 | } 378 | else 379 | { 380 | edgeNodes[j] = node[j].children[c[j]]; 381 | } 382 | } 383 | 384 | ContourEdgeProc(edgeNodes, edgeProcEdgeMask[dir][i][4], indexBuffer); 385 | } 386 | } 387 | } 388 | 389 | public static void ContourFaceProc(OctreeNode[] node, int dir, List indexBuffer) 390 | { 391 | if (node[0] == null || node[1] == null) 392 | { 393 | return; 394 | } 395 | 396 | if (node[0].Type == OctreeNodeType.Node_Internal || 397 | node[1].Type == OctreeNodeType.Node_Internal) 398 | { 399 | for (int i = 0; i < 4; i++) 400 | { 401 | OctreeNode[] faceNodes = new OctreeNode[2]; 402 | int[] c = new int[2] 403 | { 404 | faceProcFaceMask[dir][i][0], 405 | faceProcFaceMask[dir][i][1], 406 | }; 407 | 408 | for (int j = 0; j < 2; j++) 409 | { 410 | if (node[j].Type != OctreeNodeType.Node_Internal) 411 | { 412 | faceNodes[j] = node[j]; 413 | } 414 | else 415 | { 416 | faceNodes[j] = node[j].children[c[j]]; 417 | } 418 | } 419 | 420 | ContourFaceProc(faceNodes, faceProcFaceMask[dir][i][2], indexBuffer); 421 | } 422 | 423 | int[][] orders = new int[2][] 424 | { 425 | new int[4]{ 0, 0, 1, 1 }, 426 | new int[4]{ 0, 1, 0, 1 }, 427 | }; 428 | 429 | for (int i = 0; i < 4; i++) 430 | { 431 | OctreeNode[] edgeNodes = new OctreeNode[4]; 432 | int[] c = new int[4] 433 | { 434 | faceProcEdgeMask[dir][i][1], 435 | faceProcEdgeMask[dir][i][2], 436 | faceProcEdgeMask[dir][i][3], 437 | faceProcEdgeMask[dir][i][4], 438 | }; 439 | 440 | int[] order = orders[faceProcEdgeMask[dir][i][0]]; 441 | for (int j = 0; j < 4; j++) 442 | { 443 | if (node[order[j]].Type == OctreeNodeType.Node_Leaf || 444 | node[order[j]].Type == OctreeNodeType.Node_Psuedo) 445 | { 446 | edgeNodes[j] = node[order[j]]; 447 | } 448 | else 449 | { 450 | edgeNodes[j] = node[order[j]].children[c[j]]; 451 | } 452 | } 453 | 454 | ContourEdgeProc(edgeNodes, faceProcEdgeMask[dir][i][5], indexBuffer); 455 | } 456 | } 457 | } 458 | 459 | public static void ContourCellProc(OctreeNode node, List indexBuffer) 460 | { 461 | if (node == null) 462 | { 463 | return; 464 | } 465 | 466 | if (node.Type == OctreeNodeType.Node_Internal) 467 | { 468 | for (int i = 0; i < 8; i++) 469 | { 470 | ContourCellProc(node.children[i], indexBuffer); 471 | } 472 | 473 | for (int i = 0; i < 12; i++) 474 | { 475 | OctreeNode[] faceNodes = new OctreeNode[2]; 476 | int[] c = { cellProcFaceMask[i][0], cellProcFaceMask[i][1] }; 477 | 478 | faceNodes[0] = node.children[c[0]]; 479 | faceNodes[1] = node.children[c[1]]; 480 | 481 | ContourFaceProc(faceNodes, cellProcFaceMask[i][2], indexBuffer); 482 | } 483 | 484 | for (int i = 0; i < 6; i++) 485 | { 486 | OctreeNode[] edgeNodes = new OctreeNode[4]; 487 | int[] c = new int[4] 488 | { 489 | cellProcEdgeMask[i][0], 490 | cellProcEdgeMask[i][1], 491 | cellProcEdgeMask[i][2], 492 | cellProcEdgeMask[i][3], 493 | }; 494 | 495 | for (int j = 0; j < 4; j++) 496 | { 497 | edgeNodes[j] = node.children[c[j]]; 498 | } 499 | 500 | ContourEdgeProc(edgeNodes, cellProcEdgeMask[i][4], indexBuffer); 501 | } 502 | } 503 | } 504 | 505 | public static Vector3 ApproximateZeroCrossingPosition(Vector3 p0, Vector3 p1) 506 | { 507 | // approximate the zero crossing by finding the min value along the edge 508 | float minValue = 100000f; 509 | float t = 0f; 510 | float currentT = 0f; 511 | const int steps = 8; 512 | const float increment = 1f / (float)steps; 513 | while (currentT <= 1.0f) 514 | { 515 | Vector3 p = p0 + ((p1 - p0) * currentT); 516 | float density = Mathf.Abs(glm.Density_Func(p)); 517 | if (density < minValue) 518 | { 519 | minValue = density; 520 | t = currentT; 521 | } 522 | 523 | currentT += increment; 524 | } 525 | 526 | return p0 + ((p1 - p0) * t); 527 | } 528 | 529 | public static Vector3 CalculateSurfaceNormal(Vector3 p) 530 | { 531 | float H = 0.001f; 532 | float dx = glm.Density_Func(p + new Vector3(H, 0.0f, 0.0f)) - glm.Density_Func(p - new Vector3(H, 0.0f, 0.0f)); 533 | float dy = glm.Density_Func(p + new Vector3(0.0f, H, 0.0f)) - glm.Density_Func(p - new Vector3(0.0f, H, 0.0f)); 534 | float dz = glm.Density_Func(p + new Vector3(0.0f, 0.0f, H)) - glm.Density_Func(p - new Vector3(0.0f, 0.0f, H)); 535 | 536 | return new Vector3(dx, dy, dz).normalized; 537 | } 538 | 539 | public static OctreeNode ConstructLeaf(OctreeNode leaf) 540 | { 541 | if (leaf == null || leaf.size != 1) 542 | { 543 | return null; 544 | } 545 | 546 | int corners = 0; 547 | for (int i = 0; i < 8; i++) 548 | { 549 | Vector3 cornerPos = leaf.min + CHILD_MIN_OFFSETS[i]; 550 | float density = glm.Density_Func(cornerPos); 551 | int material = density < 0.0f ? MATERIAL_SOLID : MATERIAL_AIR; 552 | corners |= (material << i); 553 | } 554 | 555 | if (corners == 0 || corners == 255) 556 | { 557 | // voxel is full inside or outside the volume 558 | //delete leaf 559 | //setting as null isn't required by the GC in C#... but its in the original, so why not! 560 | leaf = null; 561 | return null; 562 | } 563 | 564 | // otherwise the voxel contains the surface, so find the edge intersections 565 | const int MAX_CROSSINGS = 6; 566 | int edgeCount = 0; 567 | Vector3 averageNormal = Vector3.zero; 568 | QefSolver qef = new QefSolver(); 569 | 570 | for (int i = 0; i < 12 && edgeCount < MAX_CROSSINGS; i++) 571 | { 572 | int c1 = edgevmap[i][0]; 573 | int c2 = edgevmap[i][1]; 574 | 575 | int m1 = (corners >> c1) & 1; 576 | int m2 = (corners >> c2) & 1; 577 | 578 | if ((m1 == MATERIAL_AIR && m2 == MATERIAL_AIR) || (m1 == MATERIAL_SOLID && m2 == MATERIAL_SOLID)) 579 | { 580 | // no zero crossing on this edge 581 | continue; 582 | } 583 | 584 | Vector3 p1 = leaf.min + CHILD_MIN_OFFSETS[c1]; 585 | Vector3 p2 = leaf.min + CHILD_MIN_OFFSETS[c2]; 586 | Vector3 p = ApproximateZeroCrossingPosition(p1, p2); 587 | Vector3 n = CalculateSurfaceNormal(p); 588 | qef.add(p.x, p.y, p.z, n.x, n.y, n.z); 589 | 590 | averageNormal += n; 591 | 592 | edgeCount++; 593 | } 594 | 595 | Vector3 qefPosition = Vector3.zero; 596 | qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); 597 | 598 | OctreeDrawInfo drawInfo = new OctreeDrawInfo(); 599 | drawInfo.corners = 0; 600 | drawInfo.index = -1; 601 | drawInfo.position = new Vector3(qefPosition.x, qefPosition.y, qefPosition.z); 602 | drawInfo.qef = qef.getData(); 603 | 604 | Vector3 min = leaf.min; 605 | Vector3 max = new Vector3(leaf.min.x + leaf.size, leaf.min.y + leaf.size, leaf.min.z + leaf.size); 606 | if (drawInfo.position.x < min.x || drawInfo.position.x > max.x || 607 | drawInfo.position.y < min.y || drawInfo.position.y > max.y || 608 | drawInfo.position.z < min.z || drawInfo.position.z > max.z) 609 | { 610 | drawInfo.position = qef.getMassPoint(); 611 | } 612 | 613 | drawInfo.averageNormal = Vector3.Normalize(averageNormal / (float)edgeCount); 614 | drawInfo.corners = corners; 615 | 616 | leaf.Type = OctreeNodeType.Node_Leaf; 617 | leaf.drawInfo = drawInfo; 618 | 619 | return leaf; 620 | } 621 | 622 | public static OctreeNode ConstructOctreeNodes(OctreeNode node) 623 | { 624 | if (node == null) 625 | { 626 | return null; 627 | } 628 | 629 | if (node.size == 1) 630 | { 631 | return ConstructLeaf(node); 632 | } 633 | 634 | int childSize = node.size / 2; 635 | bool hasChildren = false; 636 | 637 | for (int i = 0; i < 8; i++) 638 | { 639 | OctreeNode child = new OctreeNode(); 640 | child.size = childSize; 641 | child.min = node.min + (CHILD_MIN_OFFSETS[i] * childSize); 642 | child.Type = OctreeNodeType.Node_Internal; 643 | 644 | node.children[i] = ConstructOctreeNodes(child); 645 | hasChildren |= (node.children[i] != null); 646 | } 647 | 648 | if (!hasChildren) 649 | { 650 | //delete leaf 651 | //setting as null isn't required by the GC in C#... but its in the original, so why not! 652 | node = null; 653 | return null; 654 | } 655 | 656 | return node; 657 | } 658 | 659 | public static OctreeNode BuildOctree(Vector3 min, int size, float threshold) 660 | { 661 | Debug.Log(string.Format("Building Octree at {0}, with size of {1} and threshold of {2}", min, size, threshold)); 662 | 663 | OctreeNode root = new OctreeNode(); 664 | root.min = min; 665 | root.size = size; 666 | root.Type = OctreeNodeType.Node_Internal; 667 | 668 | root = ConstructOctreeNodes(root); 669 | root = SimplifyOctree(root, threshold); 670 | 671 | return root; 672 | } 673 | 674 | public static void GenerateMeshFromOctree(OctreeNode node, List vertexBuffer, List indexBuffer) 675 | { 676 | if (node == null) 677 | { 678 | return; 679 | } 680 | 681 | vertexBuffer = new List(); 682 | indexBuffer = new List(); 683 | 684 | GenerateVertexIndices(node, vertexBuffer); 685 | ContourCellProc(node, indexBuffer); 686 | 687 | GameObject go = new GameObject("Mesh"); 688 | Mesh mesh; 689 | MeshFilter filter; 690 | MeshRenderer meshRenderer; 691 | 692 | meshRenderer = go.AddComponent(); 693 | filter = go.AddComponent(); 694 | 695 | meshRenderer.sharedMaterial = Resources.Load("Default") as Material; 696 | 697 | Vector3[] vertArray = new Vector3[vertexBuffer.Count]; 698 | Vector2[] uvs = new Vector2[vertexBuffer.Count]; 699 | for (int i = 0; i < vertexBuffer.Count; i++) 700 | { 701 | vertArray[i] = vertexBuffer[i].xyz; 702 | uvs[i] = new Vector2(vertexBuffer[i].xyz.x, vertexBuffer[i].xyz.z); 703 | } 704 | 705 | Vector3[] normsArray = new Vector3[vertexBuffer.Count]; 706 | for (int i = 0; i < vertexBuffer.Count; i++) 707 | { 708 | normsArray[i] = vertexBuffer[i].normal; 709 | } 710 | 711 | mesh = filter.mesh; 712 | mesh.vertices = vertArray; 713 | mesh.uv = uvs; 714 | mesh.triangles = indexBuffer.ToArray(); 715 | mesh.normals = normsArray; 716 | mesh.RecalculateBounds(); 717 | 718 | for (int i = 0; i < 8; i++) 719 | { 720 | Debug.Log("vert: " + vertArray[i]); 721 | } 722 | 723 | for (int i = 0; i < 8; i++) 724 | { 725 | Debug.Log("index: " + indexBuffer[i]); 726 | } 727 | 728 | } 729 | 730 | public static void DrawOctree(OctreeNode rootNode, int colorIndex) 731 | { 732 | if (rootNode != null && rootNode.children.Length > 0) 733 | { 734 | for (int i = 0; i < rootNode.children.Length; i++) 735 | { 736 | DrawOctree(rootNode.children[i], colorIndex + 1); 737 | } 738 | 739 | DrawOctreeNode(rootNode, drawColors[colorIndex]); 740 | } 741 | } 742 | 743 | public static void DrawOctreeNode(OctreeNode node, Color color) 744 | { 745 | DrawCubeInLines.DrawCube(node.min, node.size, color); 746 | } 747 | 748 | public static void DestroyOctree(OctreeNode node) 749 | { 750 | if (node == null) 751 | { 752 | return; 753 | } 754 | 755 | for (int i = 0; i < 8; i++) 756 | { 757 | DestroyOctree(node.children[i]); 758 | } 759 | 760 | node = null; 761 | } 762 | 763 | } -------------------------------------------------------------------------------- /CSharpCode/DualContouring/OctreeDrawInfo.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | public struct OctreeDrawInfo 3 | { 4 | public int index; 5 | public int corners; 6 | public Vector3 position; 7 | public Vector3 averageNormal; 8 | public QefData qef; 9 | } 10 | -------------------------------------------------------------------------------- /CSharpCode/DualContouring/OctreeNode.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | 4 | public class OctreeNode 5 | { 6 | public OctreeNodeType Type; 7 | public Vector3 min; 8 | public int size; 9 | public OctreeNode[] children; 10 | public OctreeDrawInfo drawInfo; 11 | 12 | public OctreeNode() 13 | { 14 | Type = OctreeNodeType.Node_None; 15 | min = Vector3.zero; 16 | size = 0; 17 | drawInfo = new OctreeDrawInfo(); 18 | 19 | children = new OctreeNode[8]; 20 | for (int i = 0; i < 8; i++) 21 | { 22 | children[i] = null; 23 | } 24 | } 25 | 26 | public OctreeNode(OctreeNodeType _type) 27 | { 28 | Type = _type; 29 | min = Vector3.zero; 30 | size = 0; 31 | drawInfo = new OctreeDrawInfo(); 32 | 33 | children = new OctreeNode[8]; 34 | for (int i = 0; i < 8; i++) 35 | { 36 | children[i] = null; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /CSharpCode/DualContouring/OctreeNodeType.cs: -------------------------------------------------------------------------------- 1 | public enum OctreeNodeType 2 | { 3 | Node_None, 4 | Node_Internal, 5 | Node_Psuedo, 6 | Node_Leaf 7 | } -------------------------------------------------------------------------------- /CSharpCode/DualContouring/QefData.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This is free and unencumbered software released into the public domain. 3 | * 4 | * Anyone is free to copy, modify, publish, use, compile, sell, or 5 | * distribute this software, either in source code form or as a compiled 6 | * binary, for any purpose, commercial or non-commercial, and by any 7 | * means. 8 | * 9 | * In jurisdictions that recognize copyright laws, the author or authors 10 | * of this software dedicate any and all copyright interest in the 11 | * software to the public domain. We make this dedication for the benefit 12 | * of the public at large and to the detriment of our heirs and 13 | * successors. We intend this dedication to be an overt act of 14 | * relinquishment in perpetuity of all present and future rights to this 15 | * software under copyright law. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * For more information, please refer to 26 | */ 27 | 28 | public class QefData 29 | { 30 | public float ata_00, ata_01, ata_02, ata_11, ata_12, ata_22; 31 | public float atb_x, atb_y, atb_z; 32 | public float btb; 33 | public float massPoint_x, massPoint_y, massPoint_z; 34 | public int numPoints; 35 | 36 | public QefData() 37 | { 38 | clear(); 39 | } 40 | 41 | public QefData(QefData rhs) 42 | { 43 | set(rhs); 44 | } 45 | 46 | public QefData(float ata_00, float ata_01,float ata_02, float ata_11, float ata_12,float ata_22, float atb_x, float atb_y, 47 | float atb_z, float btb, float massPoint_x, float massPoint_y, float massPoint_z, int numPoints) 48 | { 49 | 50 | set(ata_00, ata_01, ata_02, ata_11, ata_12, ata_22, atb_x, atb_y, atb_z, btb, massPoint_x, massPoint_y, massPoint_z, numPoints); 51 | } 52 | 53 | public void add(QefData rhs) 54 | { 55 | ata_00 += rhs.ata_00; 56 | ata_01 += rhs.ata_01; 57 | ata_02 += rhs.ata_02; 58 | ata_11 += rhs.ata_11; 59 | ata_12 += rhs.ata_12; 60 | ata_22 += rhs.ata_22; 61 | atb_x += rhs.atb_x; 62 | atb_y += rhs.atb_y; 63 | atb_z += rhs.atb_z; 64 | btb += rhs.btb; 65 | massPoint_x += rhs.massPoint_x; 66 | massPoint_y += rhs.massPoint_y; 67 | massPoint_z += rhs.massPoint_z; 68 | numPoints += rhs.numPoints; 69 | } 70 | 71 | public void clear() 72 | { 73 | set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 74 | } 75 | 76 | public void set(float ata_00, float ata_01, float ata_02, float ata_11, float ata_12, float ata_22, float atb_x, float atb_y, 77 | float atb_z, float btb, float massPoint_x, float massPoint_y, float massPoint_z, int numPoints) 78 | { 79 | this.ata_00 = ata_00; 80 | this.ata_01 = ata_01; 81 | this.ata_02 = ata_02; 82 | this.ata_11 = ata_11; 83 | this.ata_12 = ata_12; 84 | this.ata_22 = ata_22; 85 | this.atb_x = atb_x; 86 | this.atb_y = atb_y; 87 | this.atb_z = atb_z; 88 | this.btb = btb; 89 | this.massPoint_x = massPoint_x; 90 | this.massPoint_y = massPoint_y; 91 | this.massPoint_z = massPoint_z; 92 | this.numPoints = numPoints; 93 | } 94 | 95 | public void set(QefData rhs) 96 | { 97 | set(rhs.ata_00, rhs.ata_01, rhs.ata_02, rhs.ata_11, rhs.ata_12, 98 | rhs.ata_22, rhs.atb_x, rhs.atb_y, rhs.atb_z, rhs.btb, 99 | rhs.massPoint_x, rhs.massPoint_y, rhs.massPoint_z, 100 | rhs.numPoints); 101 | } 102 | } -------------------------------------------------------------------------------- /CSharpCode/DualContouring/QefSolver.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This is free and unencumbered software released into the public domain. 3 | * 4 | * Anyone is free to copy, modify, publish, use, compile, sell, or 5 | * distribute this software, either in source code form or as a compiled 6 | * binary, for any purpose, commercial or non-commercial, and by any 7 | * means. 8 | * 9 | * In jurisdictions that recognize copyright laws, the author or authors 10 | * of this software dedicate any and all copyright interest in the 11 | * software to the public domain. We make this dedication for the benefit 12 | * of the public at large and to the detriment of our heirs and 13 | * successors. We intend this dedication to be an overt act of 14 | * relinquishment in perpetuity of all present and future rights to this 15 | * software under copyright law. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * For more information, please refer to 26 | */ 27 | 28 | using System; 29 | using UnityEngine; 30 | 31 | public class QefSolver 32 | { 33 | private QefData data; 34 | private SMat3 ata; 35 | private Vector3 atb, massPoint, x; 36 | private bool hasSolution; 37 | 38 | public QefSolver() 39 | { 40 | data = new QefData(); 41 | ata = new SMat3(); 42 | atb = Vector3.zero; 43 | massPoint = Vector3.zero; 44 | x = Vector3.zero; 45 | hasSolution = false; 46 | } 47 | 48 | private QefSolver(QefSolver rhs) 49 | { } 50 | 51 | public Vector3 getMassPoint() 52 | { 53 | return massPoint; 54 | } 55 | 56 | public void add(float px, float py, float pz, float nx, float ny, float nz) 57 | { 58 | hasSolution = false; 59 | 60 | Vector3 tmpv = new Vector3(nx, ny, nz).normalized; 61 | nx = tmpv.x; 62 | ny = tmpv.y; 63 | nz = tmpv.z; 64 | 65 | data.ata_00 += nx * nx; 66 | data.ata_01 += nx * ny; 67 | data.ata_02 += nx * nz; 68 | data.ata_11 += ny * ny; 69 | data.ata_12 += ny * nz; 70 | data.ata_22 += nz * nz; 71 | float dot = nx * px + ny * py + nz * pz; 72 | data.atb_x += dot * nx; 73 | data.atb_y += dot * ny; 74 | data.atb_z += dot * nz; 75 | data.btb += dot * dot; 76 | data.massPoint_x += px; 77 | data.massPoint_y += py; 78 | data.massPoint_z += pz; 79 | ++data.numPoints; 80 | } 81 | 82 | public void add(Vector3 p, Vector3 n) 83 | { 84 | add(p.x, p.y, p.z, n.x, n.y, n.z); 85 | } 86 | 87 | public void add(QefData rhs) 88 | { 89 | hasSolution = false; 90 | data.add(rhs); 91 | } 92 | 93 | public QefData getData() 94 | { 95 | return data; 96 | } 97 | 98 | public float getError() 99 | { 100 | if (!hasSolution) 101 | { 102 | throw new ArgumentException("Qef Solver does not have a solution!"); 103 | } 104 | 105 | return getError(x); 106 | } 107 | 108 | public float getError(Vector3 pos) 109 | { 110 | if (!hasSolution) 111 | { 112 | setAta(); 113 | setAtb(); 114 | } 115 | 116 | Vector3 atax; 117 | MatUtils.vmul_symmetric(out atax, ata, pos); 118 | return Vector3.Dot(pos, atax) - 2 * Vector3.Dot(pos, atb) + data.btb; 119 | } 120 | 121 | public void reset() 122 | { 123 | hasSolution = false; 124 | data.clear(); 125 | } 126 | 127 | public float solve(Vector3 outx, float svd_tol, int svd_sweeps, float pinv_tol) 128 | { 129 | if (data.numPoints == 0) 130 | { 131 | throw new ArgumentException("..."); 132 | } 133 | 134 | massPoint.Set(data.massPoint_x, data.massPoint_y, data.massPoint_z); 135 | massPoint *= (1.0f / data.numPoints); 136 | setAta(); 137 | setAtb(); 138 | Vector3 tmpv; 139 | MatUtils.vmul_symmetric(out tmpv, ata, massPoint); 140 | atb = atb - tmpv; 141 | x = Vector3.zero; 142 | float result = SVD.solveSymmetric(ata, atb, x, svd_tol, svd_sweeps, pinv_tol); 143 | x += massPoint * 1; 144 | setAtb(); 145 | outx = x; 146 | hasSolution = true; 147 | return result; 148 | } 149 | 150 | private void setAta() 151 | { 152 | ata.setSymmetric(data.ata_00, data.ata_01, data.ata_02, data.ata_11, data.ata_12, data.ata_22); 153 | } 154 | 155 | private void setAtb() 156 | { 157 | atb.Set(data.atb_x, data.atb_y, data.atb_z); 158 | } 159 | 160 | 161 | 162 | } 163 | 164 | -------------------------------------------------------------------------------- /CSharpCode/DualContouring/SMat3.cs: -------------------------------------------------------------------------------- 1 | public class SMat3 2 | { 3 | public float m00, m01, m02, m11, m12, m22; 4 | 5 | public SMat3() 6 | { 7 | clear(); 8 | } 9 | 10 | public SMat3(float m00, float m01, float m02, float m11, float m12, float m22) 11 | { 12 | this.setSymmetric(m00, m01, m02, m11, m12, m22); 13 | } 14 | 15 | public void clear() 16 | { 17 | this.setSymmetric(0, 0, 0, 0, 0, 0); 18 | } 19 | 20 | public void setSymmetric(float a00, float a01, float a02, float a11, float a12, float a22) 21 | { 22 | this.m00 = a00; 23 | this.m01 = a01; 24 | this.m02 = a02; 25 | this.m11 = a11; 26 | this.m12 = a12; 27 | this.m22 = a22; 28 | } 29 | 30 | public void setSymmetric(SMat3 rhs) 31 | { 32 | this.setSymmetric(rhs.m00, rhs.m01, rhs.m02, rhs.m11, rhs.m12, rhs.m22); 33 | } 34 | 35 | private SMat3(SMat3 rhs) 36 | { 37 | this.setSymmetric(rhs); 38 | } 39 | } -------------------------------------------------------------------------------- /CSharpCode/DualContouring/SVD.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This is free and unencumbered software released into the public domain. 3 | * 4 | * Anyone is free to copy, modify, publish, use, compile, sell, or 5 | * distribute this software, either in source code form or as a compiled 6 | * binary, for any purpose, commercial or non-commercial, and by any 7 | * means. 8 | * 9 | * In jurisdictions that recognize copyright laws, the author or authors 10 | * of this software dedicate any and all copyright interest in the 11 | * software to the public domain. We make this dedication for the benefit 12 | * of the public at large and to the detriment of our heirs and 13 | * successors. We intend this dedication to be an overt act of 14 | * relinquishment in perpetuity of all present and future rights to this 15 | * software under copyright law. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * For more information, please refer to 26 | */ 27 | 28 | using UnityEngine; 29 | 30 | public static class SVD 31 | { 32 | public static void rotate01(SMat3 vtav, Mat3 v) 33 | { 34 | if (vtav.m01 == 0) 35 | { 36 | return; 37 | } 38 | 39 | float c = 0, s = 0; 40 | Schur2.rot01(vtav, c, s); 41 | Givens.rot01_post(v, c, s); 42 | } 43 | 44 | public static void rotate02(SMat3 vtav, Mat3 v) 45 | { 46 | if (vtav.m02 == 0) 47 | { 48 | return; 49 | } 50 | 51 | float c = 0, s = 0; 52 | Schur2.rot02(vtav, c, s); 53 | Givens.rot02_post(v, c, s); 54 | } 55 | 56 | public static void rotate12(SMat3 vtav, Mat3 v) 57 | { 58 | if (vtav.m12 == 0) 59 | { 60 | return; 61 | } 62 | 63 | float c = 0, s = 0; 64 | Schur2.rot12(vtav, c, s); 65 | Givens.rot12_post(v, c, s); 66 | } 67 | 68 | public static void getSymmetricSvd(SMat3 a, SMat3 vtav, Mat3 v, float tol, int max_sweeps) 69 | { 70 | vtav.setSymmetric(a); 71 | v.set(1, 0, 0, 0, 1, 0, 0, 0, 1); 72 | float delta = tol * MatUtils.fnorm(vtav); 73 | 74 | for (int i = 0; i < max_sweeps && MatUtils.off(vtav) > delta; ++i) 75 | { 76 | rotate01(vtav, v); 77 | rotate02(vtav, v); 78 | rotate12(vtav, v); 79 | } 80 | } 81 | 82 | public static float calcError(Mat3 A, Vector3 x, Vector3 b) 83 | { 84 | Vector3 vtmp; 85 | MatUtils.vmul(out vtmp, A, x); 86 | vtmp = b - vtmp; 87 | return Vector3.Dot(vtmp, vtmp); 88 | } 89 | 90 | public static float calcError(SMat3 origA, Vector3 x, Vector3 b) 91 | { 92 | Mat3 A = new Mat3(); 93 | Vector3 vtmp; 94 | A.setSymmetric(origA); 95 | MatUtils.vmul(out vtmp, A, x); 96 | vtmp = b - vtmp; 97 | return Vector3.Dot(vtmp, vtmp); 98 | } 99 | 100 | public static float pinv(float x, float tol) 101 | { 102 | return (Mathf.Abs(x) < tol || Mathf.Abs(1 / x) < tol) ? 0 : (1 / x); 103 | } 104 | 105 | public static void pseudoinverse(out Mat3 Out, SMat3 d, Mat3 v, float tol) 106 | { 107 | float d0 = pinv(d.m00, tol), d1 = pinv(d.m11, tol), d2 = pinv(d.m22, tol); 108 | 109 | Out = new Mat3(); 110 | Out.set(v.m00 * d0 * v.m00 + v.m01 * d1 * v.m01 + v.m02 * d2 * v.m02, 111 | v.m00 * d0 * v.m10 + v.m01 * d1 * v.m11 + v.m02 * d2 * v.m12, 112 | v.m00 * d0 * v.m20 + v.m01 * d1 * v.m21 + v.m02 * d2 * v.m22, 113 | v.m10 * d0 * v.m00 + v.m11 * d1 * v.m01 + v.m12 * d2 * v.m02, 114 | v.m10 * d0 * v.m10 + v.m11 * d1 * v.m11 + v.m12 * d2 * v.m12, 115 | v.m10 * d0 * v.m20 + v.m11 * d1 * v.m21 + v.m12 * d2 * v.m22, 116 | v.m20 * d0 * v.m00 + v.m21 * d1 * v.m01 + v.m22 * d2 * v.m02, 117 | v.m20 * d0 * v.m10 + v.m21 * d1 * v.m11 + v.m22 * d2 * v.m12, 118 | v.m20 * d0 * v.m20 + v.m21 * d1 * v.m21 + v.m22 * d2 * v.m22); 119 | } 120 | 121 | public static float solveSymmetric(SMat3 A, Vector3 b, Vector3 x, float svd_tol, int svd_sweeps, float pinv_tol) 122 | { 123 | Mat3 pinv; 124 | Mat3 V = new Mat3(); 125 | SMat3 VTAV = new SMat3(); 126 | getSymmetricSvd(A, VTAV, V, svd_tol, svd_sweeps); 127 | pseudoinverse(out pinv, VTAV, V, pinv_tol); 128 | MatUtils.vmul(out x, pinv, b); 129 | return calcError(A, x, b); 130 | } 131 | 132 | public static void calcSymmetricGivensCoefficients(float a_pp, float a_pq, float a_qq, float c, float s) 133 | { 134 | if (a_pq == 0) 135 | { 136 | c = 1; 137 | s = 0; 138 | return; 139 | } 140 | 141 | float tau = (a_qq - a_pp) / (2 * a_pq); 142 | float stt = Mathf.Sqrt(1.0f + tau * tau); 143 | float tan = 1.0f / ((tau >= 0) ? (tau + stt) : (tau - stt)); 144 | c = 1.0f / Mathf.Sqrt(1.0f + tan * tan); 145 | s = tan * c; 146 | } 147 | 148 | public static float solveLeastSquares(Mat3 a, Vector3 b, Vector3 x, float svd_tol, int svd_sweeps, float pinv_tol) 149 | { 150 | Mat3 at; 151 | SMat3 ata; 152 | Vector3 atb; 153 | MatUtils.transpose(out at, a); 154 | MatUtils.mmul_ata(out ata, a); 155 | MatUtils.vmul(out atb, at, b); 156 | return solveSymmetric(ata, atb, x, svd_tol, svd_sweeps, pinv_tol); 157 | } 158 | 159 | } -------------------------------------------------------------------------------- /CSharpCode/DualContouring/Schur2.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This is free and unencumbered software released into the public domain. 3 | * 4 | * Anyone is free to copy, modify, publish, use, compile, sell, or 5 | * distribute this software, either in source code form or as a compiled 6 | * binary, for any purpose, commercial or non-commercial, and by any 7 | * means. 8 | * 9 | * In jurisdictions that recognize copyright laws, the author or authors 10 | * of this software dedicate any and all copyright interest in the 11 | * software to the public domain. We make this dedication for the benefit 12 | * of the public at large and to the detriment of our heirs and 13 | * successors. We intend this dedication to be an overt act of 14 | * relinquishment in perpetuity of all present and future rights to this 15 | * software under copyright law. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * For more information, please refer to 26 | */ 27 | 28 | public static class Schur2 29 | { 30 | public static void rot01(SMat3 m, float c, float s) 31 | { 32 | SVD.calcSymmetricGivensCoefficients(m.m00, m.m01, m.m11, c, s); 33 | float cc = c * c; 34 | float ss = s * s; 35 | float mix = 2 * c * s * m.m01; 36 | m.setSymmetric(cc * m.m00 - mix + ss * m.m11, 0, c * m.m02 - s * m.m12, 37 | ss * m.m00 + mix + cc * m.m11, s * m.m02 + c * m.m12, m.m22); 38 | } 39 | 40 | public static void rot02(SMat3 m, float c, float s) 41 | { 42 | SVD.calcSymmetricGivensCoefficients(m.m00, m.m02, m.m22, c, s); 43 | float cc = c * c; 44 | float ss = s * s; 45 | float mix = 2 * c * s * m.m02; 46 | m.setSymmetric(cc * m.m00 - mix + ss * m.m22, c * m.m01 - s * m.m12, 0, 47 | m.m11, s * m.m01 + c * m.m12, ss * m.m00 + mix + cc * m.m22); 48 | } 49 | 50 | public static void rot12(SMat3 m, float c, float s) 51 | { 52 | SVD.calcSymmetricGivensCoefficients(m.m11, m.m12, m.m22, c, s); 53 | float cc = c * c; 54 | float ss = s * s; 55 | float mix = 2 * c * s * m.m12; 56 | m.setSymmetric(m.m00, c * m.m01 - s * m.m02, s * m.m01 + c * m.m02, 57 | cc * m.m11 - mix + ss * m.m22, 0, ss * m.m11 + mix + cc * m.m22); 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /CSharpCode/DualContouring/density.cs: -------------------------------------------------------------------------------- 1 | using Code.Noise; 2 | using UnityEngine; 3 | 4 | public static class glm 5 | { 6 | public static float Sphere(Vector3 worldPosition, Vector3 origin, float radius) 7 | { 8 | return Vector3.Magnitude(worldPosition - origin) - radius; 9 | } 10 | 11 | public static float Cuboid(Vector3 worldPosition, Vector3 origin, Vector3 halfDimensions) 12 | { 13 | Vector3 local_pos = worldPosition - origin; 14 | Vector3 pos = local_pos; 15 | 16 | Vector3 d = new Vector3(Mathf.Abs(pos.x), Mathf.Abs(pos.y), Mathf.Abs(pos.z)) - halfDimensions; 17 | float m = Mathf.Max(d.x, Mathf.Max(d.y, d.z)); 18 | return Mathf.Min(m, Vector3.Magnitude(d.magnitude > 0 ? d : Vector3.zero)); 19 | } 20 | 21 | public static float FractalNoise(int octaves, float frequency, float lacunarity, float persistence, Vector2 position) 22 | { 23 | float SCALE = 1.0f / 128.0f; 24 | Vector2 p = position * SCALE; 25 | float noise = 0.0f; 26 | 27 | float amplitude = 1.0f; 28 | p *= frequency; 29 | 30 | for (int i = 0; i < octaves; i++) 31 | { 32 | noise += Noise.Perlin(p.x, p.y) * amplitude; 33 | p *= lacunarity; 34 | amplitude *= persistence; 35 | } 36 | 37 | // move into [0, 1] range 38 | return 0.5f + (0.5f * noise); 39 | } 40 | 41 | 42 | public static float Density_Func(Vector3 worldPosition) 43 | { 44 | float MAX_HEIGHT = 20.0f; 45 | float noise = FractalNoise(4, 0.5343f, 2.2324f, 0.68324f, new Vector2(worldPosition.x, worldPosition.z)); 46 | float terrain = worldPosition.y - (MAX_HEIGHT * noise); 47 | 48 | float cube = Cuboid(worldPosition, new Vector3(-4.0f, 10.0f, -4.0f), new Vector3(12.0f, 12.0f, 12.0f)); 49 | float sphere = Sphere(worldPosition, new Vector3(15.0f, 2.5f, 1.0f), 16.0f); 50 | 51 | return Mathf.Max(-cube, Mathf.Min(sphere, terrain)); 52 | } 53 | } -------------------------------------------------------------------------------- /CSharpCode/DualContouring/mesh.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | public struct MeshVertex 5 | { 6 | public Vector3 xyz, normal; 7 | 8 | public MeshVertex(Vector3 _xyz, Vector3 _normal) 9 | { 10 | xyz = _xyz; 11 | normal = _normal; 12 | } 13 | } 14 | 15 | public class DCMesh 16 | { 17 | public List vertexArrayObj_, vertexBuffer_, indexBuffer_; 18 | public int numIndices_; 19 | 20 | public DCMesh() 21 | { 22 | vertexArrayObj_ = new List(); 23 | vertexBuffer_ = new List(); 24 | indexBuffer_ = new List(); 25 | numIndices_ = 0; 26 | 27 | } 28 | 29 | public void initialise() 30 | { 31 | 32 | } 33 | public void uploadData(List vertices, List indices) 34 | { 35 | 36 | } 37 | 38 | 39 | public void destroy() 40 | { 41 | 42 | } 43 | 44 | } 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /CSharpCode/GameManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using Code.Utils; 4 | using Code; 5 | using System.Collections.Generic; 6 | 7 | public class GameManager : MonoBehaviour 8 | { 9 | const int MAX_THRESHOLDS = 5; 10 | static float[] THRESHOLDS = new float[MAX_THRESHOLDS] { -1.0f, 0.1f, 1.0f, 10.0f, 50.0f }; 11 | static int thresholdIndex = -1; 12 | 13 | OctreeNode root = null; 14 | 15 | // octreeSize must be a power of two! 16 | const int octreeSize = 64; 17 | 18 | void Awake() 19 | { 20 | } 21 | 22 | void Start () 23 | { 24 | PlayerInput.Start(); 25 | 26 | thresholdIndex = (thresholdIndex + 1) % MAX_THRESHOLDS; 27 | List vertices = new List(); 28 | List indices = new List(); 29 | 30 | 31 | root = Octree.BuildOctree(new Vector3(-octreeSize / 2, -octreeSize / 2, -octreeSize / 2), octreeSize, THRESHOLDS[thresholdIndex]); 32 | 33 | if (root == null) 34 | Debug.Log("root is null"); 35 | 36 | Octree.GenerateMeshFromOctree(root, vertices, indices); 37 | } 38 | 39 | // Update is called once per frame 40 | void Update () 41 | { 42 | PlayerInput.Update(); 43 | } 44 | 45 | void OnApplicationQuit() 46 | { 47 | Octree.DestroyOctree(root); 48 | } 49 | 50 | void OnGUI () 51 | { 52 | //You need a killer computer to be able to use this!! (or drop the octree size some.. ) 53 | //Octree.DrawOctree(root, 0); 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /CSharpCode/Utils/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Code.Utils 7 | { 8 | public static class Config 9 | { 10 | public static int ChunkSize = 8; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CSharpCode/Utils/DrawCubeInLines.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Code.Utils 4 | { 5 | public static class DrawCubeInLines 6 | { 7 | public static void DrawCube(Vector3 pos, int size, Color color) 8 | { 9 | DrawGLLine(new Vector3(pos.x, pos.y, pos.z), new Vector3(pos.x + size, pos.y, pos.z), color); 10 | DrawGLLine(new Vector3(pos.x + size, pos.y, pos.z), new Vector3(pos.x + size, pos.y + size, pos.z), color); 11 | DrawGLLine(new Vector3(pos.x + size, pos.y + size, pos.z), new Vector3(pos.x, pos.y + size, pos.z), color); 12 | DrawGLLine(new Vector3(pos.x, pos.y + size, pos.z), new Vector3(pos.x, pos.y, pos.z), color); 13 | 14 | DrawGLLine(new Vector3(pos.x, pos.y, pos.z + size), new Vector3(pos.x + size, pos.y, pos.z + size), color); 15 | DrawGLLine(new Vector3(pos.x + size, pos.y, pos.z + size), new Vector3(pos.x + size, pos.y + size, pos.z + size), color); 16 | DrawGLLine(new Vector3(pos.x + size, pos.y + size, pos.z + size), new Vector3(pos.x, pos.y + size, pos.z + size), color); 17 | DrawGLLine(new Vector3(pos.x, pos.y + size, pos.z + size), new Vector3(pos.x, pos.y, pos.z + size), color); 18 | 19 | DrawGLLine(new Vector3(pos.x, pos.y, pos.z), new Vector3(pos.x, pos.y, pos.z + size), color); 20 | DrawGLLine(new Vector3(pos.x + size, pos.y, pos.z), new Vector3(pos.x + size, pos.y, pos.z + size), color); 21 | DrawGLLine(new Vector3(pos.x + size, pos.y + size, pos.z), new Vector3(pos.x + size, pos.y + size, pos.z + size), color); 22 | DrawGLLine(new Vector3(pos.x, pos.y + size, pos.z), new Vector3(pos.x, pos.y + size, pos.z + size), color); 23 | } 24 | 25 | public static void DrawTri(Vector3 pos1, Vector3 pos2, Vector3 pos3, Color color) 26 | { 27 | DrawGLLine(pos1, pos2, color); 28 | DrawGLLine(pos2, pos3, color); 29 | DrawGLLine(pos3, pos1, color); 30 | } 31 | 32 | public static void DrawGLLine(Vector3 P1, Vector3 P2, Color lineColor) 33 | { 34 | GL.PushMatrix(); 35 | GL.Begin(GL.LINES); 36 | GL.Color(lineColor); 37 | GL.Vertex(P1); 38 | GL.Vertex(P2); 39 | GL.End(); 40 | GL.PopMatrix(); 41 | } 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /CSharpCode/Utils/Noise/PerlinNoise.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Code.Noise 4 | { 5 | public partial class Noise 6 | { 7 | #region static readonlys 8 | private static readonly Vector3[] Grad3 = { 9 | new Vector3(1,1,0), new Vector3(-1,1,0), new Vector3(1,-1,0), new Vector3(-1,-1,0), 10 | new Vector3(1,0,1), new Vector3(-1,0,1), new Vector3(1,0,-1), new Vector3(-1,0,-1), 11 | new Vector3(0,1,1), new Vector3(0,-1,1), new Vector3(0,1,-1), new Vector3(0,-1,-1) 12 | }; 13 | 14 | private static readonly int[] P = {151,160,137,91,90,15, 15 | 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 16 | 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 17 | 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 18 | 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 19 | 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 20 | 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 21 | 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 22 | 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 23 | 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 24 | 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 25 | 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 26 | 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180}; 27 | 28 | 29 | private static readonly int[] Perm = new int[512]; 30 | #endregion 31 | 32 | public static float Perlin(float x, float y) 33 | { 34 | var i = x > 0 ? (int)x : (int)x - 1; 35 | var j = y > 0 ? (int)y : (int)y - 1; 36 | 37 | x = x - i; 38 | y = y - j; 39 | 40 | i = i & 255; 41 | j = j & 255; 42 | 43 | var gll = Perm[i + Perm[j]] % 12; 44 | var glh = Perm[i + Perm[j + 1]] % 12; 45 | var ghl = Perm[i + 1 + Perm[j]] % 12; 46 | var ghh = Perm[i + 1 + Perm[j + 1]] % 12; 47 | 48 | var nll = Vector2.Dot(Grad3[gll], new Vector2(x, y)); 49 | var nlh = Vector2.Dot(Grad3[glh], new Vector2(x, y - 1)); 50 | var nhl = Vector2.Dot(Grad3[ghl], new Vector2(x - 1, y)); 51 | var nhh = Vector2.Dot(Grad3[ghh], new Vector2(x - 1, y - 1)); 52 | 53 | var u = (float)(x * x * x * (x * (x * 6 - 15) + 10)); 54 | var v = (float)(y * y * y * (y * (y * 6 - 15) + 10)); 55 | 56 | //var nyl = Mathf.Lerp(nll, nhl, u); 57 | var nyl = (1-u)*nll + u*nhl; 58 | //var nyh = Mathf.Lerp(nlh, nhh, u); 59 | var nyh = (1-u)*nlh + u*nhh; 60 | 61 | //var nxy = Mathf.Lerp(nyl, nyh, v); 62 | var nxy = (1-v)*nyl + v*nyh; 63 | 64 | return nxy; 65 | 66 | 67 | } 68 | 69 | public static float Perlin(float x, float y, float z) 70 | { 71 | var X = x > 0 ? (int)x : (int)x - 1; 72 | var Y = y > 0 ? (int)y : (int)y - 1; 73 | var Z = z > 0 ? (int)z : (int)z - 1; 74 | 75 | x = x - X; 76 | y = y - Y; 77 | z = z - Z; 78 | 79 | X = X & 255; 80 | Y = Y & 255; 81 | Z = Z & 255; 82 | 83 | var gi000 = Perm[X + Perm[Y + Perm[Z]]] % 12; 84 | var gi001 = Perm[X + Perm[Y + Perm[Z + 1]]] % 12; 85 | var gi010 = Perm[X + Perm[Y + 1 + Perm[Z]]] % 12; 86 | var gi011 = Perm[X + Perm[Y + 1 + Perm[Z + 1]]] % 12; 87 | var gi100 = Perm[X + 1 + Perm[Y + Perm[Z]]] % 12; 88 | var gi101 = Perm[X + 1 + Perm[Y + Perm[Z + 1]]] % 12; 89 | var gi110 = Perm[X + 1 + Perm[Y + 1 + Perm[Z]]] % 12; 90 | var gi111 = Perm[X + 1 + Perm[Y + 1 + Perm[Z + 1]]] % 12; 91 | 92 | 93 | //TODO: inline the dot products to speed up perlin noise 94 | var n000 = Vector2.Dot(Grad3[gi000], new Vector3( x, y, z)); 95 | var n100 = Vector2.Dot(Grad3[gi100], new Vector3(x - 1, y, z)); 96 | var n010 = Vector2.Dot(Grad3[gi010], new Vector3(x, y - 1, z)); 97 | var n110 = Vector2.Dot(Grad3[gi110], new Vector3(x - 1, y - 1, z)); 98 | var n001 = Vector2.Dot(Grad3[gi001], new Vector3( x, y, z - 1)); 99 | var n101 = Vector2.Dot(Grad3[gi101], new Vector3(x - 1, y, z - 1)); 100 | var n011 = Vector2.Dot(Grad3[gi011], new Vector3(x, y - 1, z - 1)); 101 | var n111 = Vector2.Dot(Grad3[gi111], new Vector3(x - 1, y - 1, z - 1)); 102 | 103 | var u = (float)(x * x * x * (x * (x * 6 - 15) + 10)); 104 | var v = (float)(y * y * y * (y * (y * 6 - 15) + 10)); 105 | var w = (float)(z * z * z * (z * (z * 6 - 15) + 10)); 106 | 107 | //TODO: inline lerps to speed up perlin noise 108 | var nx00 = Mathf.Lerp(n000, n100, u); 109 | var nx01 = Mathf.Lerp(n001, n101, u); 110 | var nx10 = Mathf.Lerp(n010, n110, u); 111 | var nx11 = Mathf.Lerp(n011, n111, u); 112 | 113 | var nxy0 = Mathf.Lerp(nx00, nx10, v); 114 | var nxy1 = Mathf.Lerp(nx01, nx11, v); 115 | 116 | 117 | var nxyz = Mathf.Lerp(nxy0, nxy1, w); 118 | 119 | return nxyz; 120 | 121 | 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /CSharpCode/Utils/PlayerInput.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Code.Utils 4 | { 5 | public static class PlayerInput 6 | { 7 | static Vector3 movementVector; 8 | 9 | enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 } 10 | static RotationAxes axes = RotationAxes.MouseXAndY; 11 | static float sensitivityX = 5F; 12 | static float sensitivityY = 5F; 13 | 14 | static float minimumY = -60F; 15 | static float maximumY = 60F; 16 | 17 | static float rotationY = 0F; 18 | 19 | static Transform transform; 20 | 21 | public static void Start() 22 | { 23 | transform = Camera.main.transform; 24 | } 25 | 26 | public static void Update() 27 | { 28 | movementVector = Vector3.zero; 29 | 30 | if (Input.GetKey(KeyCode.W)) 31 | movementVector += Camera.main.transform.forward * 7; 32 | 33 | if (Input.GetKey(KeyCode.S)) 34 | movementVector -= Camera.main.transform.forward * 7; 35 | 36 | if (Input.GetKey(KeyCode.A)) 37 | movementVector -= Camera.main.transform.right * 7; 38 | 39 | if (Input.GetKey(KeyCode.D)) 40 | movementVector += Camera.main.transform.right * 7; 41 | 42 | if (Input.GetKey(KeyCode.Space)) 43 | movementVector += Camera.main.transform.up * 7; 44 | 45 | if (Input.GetKey(KeyCode.LeftShift)) 46 | movementVector -= Camera.main.transform.up * 7; 47 | 48 | Camera.main.transform.position = Camera.main.transform.position + movementVector * Time.deltaTime; 49 | 50 | CheckMouseInput(); 51 | } 52 | 53 | private static void CheckMouseInput() 54 | { 55 | if (axes == RotationAxes.MouseXAndY) 56 | { 57 | float rotationX = transform.localEulerAngles.y + Input.GetAxis("Mouse X") * sensitivityX; 58 | 59 | rotationY += Input.GetAxis("Mouse Y") * sensitivityY; 60 | rotationY = Mathf.Clamp(rotationY, minimumY, maximumY); 61 | 62 | transform.localEulerAngles = new Vector3(-rotationY, rotationX, 0); 63 | } 64 | else if (axes == RotationAxes.MouseX) 65 | { 66 | transform.Rotate(0, Input.GetAxis("Mouse X") * sensitivityX, 0); 67 | } 68 | else 69 | { 70 | rotationY += Input.GetAxis("Mouse Y") * sensitivityY; 71 | rotationY = Mathf.Clamp(rotationY, minimumY, maximumY); 72 | 73 | transform.localEulerAngles = new Vector3(-rotationY, transform.localEulerAngles.y, 0); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /DC_UnityPackage.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuckbone/DualContouringCSharp/1307f3b8cd795a6d91c3acfe6a478306e1b180d0/DC_UnityPackage.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DualContouringCSharp 2 | A Dual Contouring Example using C# and Unity3D 3 | 4 | The unity package can be imported into unity and run. The code portion of this has everything set up, but you need to manually add the game manager script into the scene you're using. 5 | 6 | Also, please do not use this code as the "Ideal way to code in C#"!!! It is by no means optimized for managed code. This is nothing but a direct port of NGildea's Dual Contouring Impl from his blog. 7 | --------------------------------------------------------------------------------