Getting Started
Introduction
Kha is ultra-portable, high performance, open-source multimedia framework. It is low level SDK for building cross-platform games and media applications. With the help of Haxe Programming language and the Krafix shader-compiler it can cross-compile your code and optimize your assets. Kha gives you a common API to graphics, audio, input, and networking, for platforms such as Web, Mobile, Desktop, Consoles. And Graphics APIs such as Metal, Vulkan, DirectX, WebGL and OpenGL. So it is basically it is SDL but stronger.
Language
It is written in Haxe, a strictly-typed high-level open-source programming language with cross-compiler. It is possible to use C/C++ with Kinc, which is like kha but for C/C++ (Kha uses Kinc for C/C++ targets).
Targets
It support many different platforms with different graphics api:
- macOS (Metal or Open-GL).
- iOS (Metal or Open-GL).
- windows (D3D9/11/12, Vulkan or Open-GL).
- linux (Vulkan or Open-GL).
- HTML5 (WebGL1/2 and canvas).
- etc.
Complete structure of Kha look something like this:
Showcases
-
Softaware:
- Commercials:
- Non-Commercial:
- Paddy-Editor (Mine)
- Armory2D
-
Games:
- Commercials:
- Non-Commercials:
-
Libraries:
-
Engines:
Setup
Setting up kha is relatively easy. Kha comes with its own haxe version, so no need to worry about installing haxe. Kha have different workflow, i.e., Kode Studio, Git and Haxelib. You can find about them here. We are going to VSCode + Git workflow, that almost everyone uses.
- Download and install:
- Git clone kha recursively, wherever you want.
git clone --recursive https://github.com/Kode/Kha.git
- In
VSCode - extension
, install Kha extension pack and Haxe extension pack. - Go to setting, and navigate to
Extensions - Kha - Kha Path
and enter yourpath/to/Kha
that you just cloned. - Open an empty folder and press
F1
(fn
+F1
on macOS) and enterKha: Init Project
. New starter Kha project should be created. - Go to
Run
panel, selectKha: HTML5
from dropdown and press triangle next to it to start debugging.
You should get something like this:
Extra: Eliminating cache building issues
Kha cache code to improve build times. But sometime Kha doesn't detect changes made in code (this usually happen with small code changes such as typo fixes, variable changes, etc) and uses cached code and so the changes made doesn't appear. This can make programmer scratch their heads or even possible make them throw their laptop/pc out of window. You will have to delete build folder every time before running to avoid this issue. This can be solved with VSCode's tasks.
- Press
F1
(fn
+F1
on macOS) and enterTasks: Configure Task
and selectKha: Build for Debug HTML5
. - Paste:
{
"version": "2.0.0",
"tasks": [
{
"label": "Clean Build",
"type": "shell",
// "command": "rm -r \"${workspaceRoot}/build\""
// Uncomment above line if you are on unix (Linux/macOS)
// "command": "rmdir /Q /S \"${workspaceRoot}/build\""
// Uncomment above line if you are on windows
},
{
"type": "Kha",
"target": "Debug HTML5",
"problemMatcher": [
"$haxe-absolute",
"$haxe"
],
"dependsOn": [
"Clean Build"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
It just delete your build
folder before the build process start.
Extra: Removing that Allow incoming connection blah blah blah
on macOS, every time you use Krom.
To get around this annoying thing, VSCode's tasks comes to rescue once again.
- Open
task.json
- Add below object to
tasks
array:
{
"label": "Build Krom",
"type": "shell",
"command": "node path/to/Kha/make krom"
},
{
"label": "Krom",
"type": "shell",
"command": "path/to/Krom.app/Contents/MacOS/Krom ${workspaceRoot}/build/krom ${workspaceRoot}/build/krom-resources",
"dependsOn": [
"Clean Build",
"Build Krom"
],
"dependsOrder": "sequence",
"problemMatcher": []
}
Getting Started
Introduction
Let say you have uniform grid of cubes. 8 corners of each cubes has a value (density). Based upon this density, the corner is said to be inside/outside. Now, there are 2^8 = 256
possible amount of configuration of each of this cubes (only 14 of them are unique cases, rest of them are symmetries). And from this configuration, triangle(s) is made almost entirely from triangulation lookup tables, and placed relative to corresponding cube position.
Image from Sebastian Lague's Coding Adventure: Marching Cubes
This is performed on each of cubes while iterating (i.e., marching) over the grid, resulting in mesh made up of triangles. This algorithm is called Marching cubes
.
Example:
Basics perlin terrain generation#haxe #yeswekha pic.twitter.com/fO3JCygAxk
— Blackgoku36 (@UrjasviS) May 9, 2020
Perlin terrain in marching cubes
Lot of other iso-surface extraction method exists such as:
- Extended Marching Cubes
- Dual Marching Cubes
- Cubical Marching Squares
- Marching Tetrahedra
- Surface Nets
- Dual-Contouring
Different iso-surface extraction algorithm have different up and downs. Two big downs of marching cubes is that it doesn't support sharp features and doing LOD is quite difficult. But its big up is that it is easiest and simplest among other methods.
Marching cubes was originally made to visualize data from CT and MRI devices and are still used for it nowadays. Marching cubes are also used in games (such as Astroneer, 7 Days to Die) for procedural and editable terrains.
Resources:
- Polygonising a scalar field (paper)
- Wikipedia
- Nvidia's generating-complex-procedural-terrains-using-gpu
- Sebastian Lague's Coding Adventure: Marching Cubes
- b3agz's How to Make 7 Days to Die in Unity series
Chapter-1
In introduction, we got small overview of what marching cubes is, how it basically work, what are its ups/downs and what it is used for.
First we will implement MC on a single cube and demonstrate it with different configuration of cubes.
Git clone Kha 3D's starter. It include basics such as camera controller and mesh pipeline with basics shader to get us started without having to setup it up here.
Let get started! Create MCData.hx
in Source
folder, we will define all lookup tables here. There is nothing big to understand here right now.
MCData.hx
package;
import kha.math.FastVector3;
class MCData {
public static var cornerTable:Array<FastVector3> = [
new FastVector3(0,0,0),
new FastVector3(1,0,0),
new FastVector3(1,1,0),
new FastVector3(0,1,0),
new FastVector3(0,0,1),
new FastVector3(1,0,1),
new FastVector3(1,1,1),
new FastVector3(0,1,1)
];
public static var edgeTable = [
[new FastVector3(0.0, 0.0, 0.0), new FastVector3(1.0, 0.0, 0.0) ],
[new FastVector3(1.0, 0.0, 0.0), new FastVector3(1.0, 1.0, 0.0) ],
[new FastVector3(0.0, 1.0, 0.0), new FastVector3(1.0, 1.0, 0.0) ],
[new FastVector3(0.0, 0.0, 0.0), new FastVector3(0.0, 1.0, 0.0) ],
[new FastVector3(0.0, 0.0, 1.0), new FastVector3(1.0, 0.0, 1.0) ],
[new FastVector3(1.0, 0.0, 1.0), new FastVector3(1.0, 1.0, 1.0) ],
[new FastVector3(0.0, 1.0, 1.0), new FastVector3(1.0, 1.0, 1.0) ],
[new FastVector3(0.0, 0.0, 1.0), new FastVector3(0.0, 1.0, 1.0) ],
[new FastVector3(0.0, 0.0, 0.0), new FastVector3(0.0, 0.0, 1.0) ],
[new FastVector3(1.0, 0.0, 0.0), new FastVector3(1.0, 0.0, 1.0) ],
[new FastVector3(1.0, 1.0, 0.0), new FastVector3(1.0, 1.0, 1.0) ],
[new FastVector3(0.0, 1.0, 0.0), new FastVector3(0.0, 1.0, 1.0) ]
];
public static var edgeIndexes = [
[0, 1], [1, 2], [3, 2],
[0, 3], [4, 5], [5, 6],
[7, 6], [4, 7], [0, 4],
[1, 5], [2, 6], [3, 7]
];
public static var triangleTable:Array<Array<Int>> = [
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1],
[3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1],
[3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1],
[3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1],
[9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1],
[9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1],
[2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1],
[8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1],
[9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1],
[4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1],
[3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1],
[1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1],
[4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1],
[4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1],
[9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1],
[5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1],
[2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1],
[9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1],
[0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1],
[2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1],
[10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1],
[4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1],
[5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1],
[5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1],
[9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1],
[0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1],
[1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1],
[10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1],
[8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1],
[2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1],
[7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1],
[9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1],
[2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1],
[11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1],
[9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1],
[5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1],
[11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1],
[11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1],
[1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1],
[9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1],
[5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1],
[2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1],
[0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1],
[5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1],
[6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1],
[3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1],
[6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1],
[5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1],
[1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1],
[10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1],
[6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1],
[8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1],
[7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1],
[3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1],
[5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1],
[0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1],
[9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1],
[8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1],
[5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1],
[0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1],
[6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1],
[10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1],
[10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1],
[8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1],
[1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1],
[3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1],
[0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1],
[10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1],
[3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1],
[6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1],
[9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1],
[8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1],
[3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1],
[6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1],
[0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1],
[10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1],
[10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1],
[2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1],
[7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1],
[7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1],
[2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1],
[1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1],
[11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1],
[8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1],
[0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1],
[7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1],
[10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1],
[2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1],
[6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1],
[7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1],
[2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1],
[1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1],
[10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1],
[10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1],
[0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1],
[7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1],
[6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1],
[8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1],
[9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1],
[6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1],
[4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1],
[10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1],
[8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1],
[0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1],
[1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1],
[8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1],
[10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1],
[4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1],
[10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1],
[5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1],
[11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1],
[9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1],
[6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1],
[7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1],
[3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1],
[7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1],
[9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1],
[3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1],
[6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1],
[9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1],
[1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1],
[4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1],
[7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1],
[6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1],
[3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1],
[0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1],
[6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1],
[0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1],
[11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1],
[6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1],
[5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1],
[9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1],
[1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1],
[1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1],
[10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1],
[0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1],
[5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1],
[10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1],
[11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1],
[9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1],
[7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1],
[2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1],
[8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1],
[9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1],
[9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1],
[1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1],
[9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1],
[9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1],
[5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1],
[0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1],
[10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1],
[2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1],
[0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1],
[0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1],
[9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1],
[5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1],
[3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1],
[5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1],
[8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1],
[0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1],
[9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1],
[0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1],
[1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1],
[3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1],
[4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1],
[9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1],
[11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1],
[11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1],
[2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1],
[9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1],
[3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1],
[1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1],
[4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1],
[4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1],
[0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1],
[3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1],
[3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1],
[0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1],
[9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1],
[1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
];
}
Create MarchingCubes.hx
, we will put our main algorithm and other stuffs related to it here.
package;
import kha.math.FastVector3;
class MarchingCubes {
// Set configuration index of the cube
public var configIndex = 1;
// Index of a vertice
var idxV = 0;
// Index of an indice
var idxI = 0;
// Polygonize a cube at some position
function polygonize(position:FastVector3) {
// Since, cube doesn't produce any triangle at configuration index of 0 or 255, skip it.
if(configIndex == 0 || configIndex == 255) return;
var edgeIndex = 0;
// Total possible amount of triangles in a cubes x Amount of vertex per triangle = 5x3 = 15.
for (i in 0...15){
// Indice of triangle
var indice = MCData.triangleTable[configIndex][edgeIndex];
// Return if there are no more indice.
if(indice == -1) return;
// Get cube corner from a cube edge and add it with position.
var vert1 = position.add(MCData.cornerTable[MCData.edgeIndexes[indice][0]]);
var vert2 = position.add(MCData.cornerTable[MCData.edgeIndexes[indice][1]]);
// Get mid-point of two edge. (Will give blocky appearance, whe rendering whole volume).
var vertPosition = vert1.add(vert2).mult(0.5);
// Set mesh's vertice position to resulting position
App.mesh.vertices[idxV] = vertPosition.x;
App.mesh.vertices[idxV+1] = vertPosition.y;
App.mesh.vertices[idxV+2] = vertPosition.z;
idxV+=3;
// Set mesh's indice to generated triangle.
App.mesh.indices[idxI] = Std.int(App.mesh.vertices.length/3) - 1;
idxI+=1;
edgeIndex++;
}
}
}
Since triangles are procedurally generated we need to create normals for it.
package;
import kha.math.FastVector3;
class MarchingCubes {
public var configIndex = 1;
var idxV = 0;
var idxI = 0;
function polygonize(position:FastVector3) {...}
public static function calculateVertexNormal() {
var i = 0;
while (i < App.mesh.indices.length) {
// Get triangle's index
var indexA = App.mesh.indices[i];
var indexB = App.mesh.indices[i + 1];
var indexC = App.mesh.indices[i + 2];
var vertices = App.mesh.vertices;
// Get vertices position from indexes.
var verticeA = new FastVector3(vertices[indexA*3], vertices[indexA*3+1], vertices[indexA*3+2]);
var verticeB = new FastVector3(vertices[indexB*3], vertices[indexB*3+1], vertices[indexB*3+2]);
var verticeC = new FastVector3(vertices[indexC*3], vertices[indexC*3+1], vertices[indexC*3+2]);
// Get edge from 2 vertice
var edgeAB = verticeB.sub(verticeA);
var edgeAC = verticeC.sub(verticeA);
// Get normal from 2 edge
var areaWeightedNormal = edgeAB.cross(edgeAC);
// Set mesh's normals.
App.mesh.normals[indexA*3] = areaWeightedNormal.x;
App.mesh.normals[indexA*3+1] = areaWeightedNormal.y;
App.mesh.normals[indexA*3+2] = areaWeightedNormal.z;
App.mesh.normals[indexB*3] = areaWeightedNormal.x;
App.mesh.normals[indexB*3+1] = areaWeightedNormal.y;
App.mesh.normals[indexB*3+2] = areaWeightedNormal.z;
App.mesh.normals[indexC*3] = areaWeightedNormal.x;
App.mesh.normals[indexC*3+1] = areaWeightedNormal.y;
App.mesh.normals[indexC*3+2] = areaWeightedNormal.z;
i += 3;
}
}
}
When re-generating, we will clean up mesh.
package;
import kha.math.FastVector3;
class MarchingCubes {
public var configIndex = 1;
var idxV = 0;
var idxI = 0;
// Generate when instance is created
public function new() {
generate();
}
// Clean mesh, by resetting its properties.
public function clean() {
App.mesh.vertices.resize(0);
App.mesh.indices.resize(0);
idxI = 0;
idxV = 0;
}
// generate vertices and normals.
public function generate() {
// generate triangle in front of camera.
polygonize(new FastVector3(0, 0, 50));
calculateVertexNormal();
}
function polygonize(position:FastVector3) {...}
public static function calculateVertexNormal() {...}
}
We have marching cubes ready! Now we need to update Mesh pipeline in order to see result.
package;
import kha.graphics4.Graphics;
import kha.FastFloat;
import kha.Shaders;
import kha.graphics4.PipelineState;
import kha.graphics4.VertexStructure;
import kha.graphics4.VertexBuffer;
import kha.graphics4.IndexBuffer;
import kha.graphics4.VertexData;
import kha.graphics4.Usage;
import kha.graphics4.ConstantLocation;
import kha.graphics4.CompareMode;
import kha.graphics4.CullMode;
import kha.math.FastVector3;
class Mesh {
static var vertexBuffer:VertexBuffer;
static var indexBuffer:IndexBuffer;
static var structure: VertexStructure; // <-----
var structureLength = 6; // <-----
var pipeline:PipelineState;
~
var objectAmbientOcclusionC:ConstantLocation;
public var vertices:Array<Float> = []; // <-----
public var indices:Array<Int> = []; // <-----
public var normals:Array<Float> = []; // <-----
~
public function new() {...}
public function load() {
structure = new VertexStructure(); // <-----
structure.add("pos", VertexData.Float3);
~
// We hard code vertex buffer length to 15 (amount of total triangle possible)
vertexBuffer = new VertexBuffer(15, structure, Usage.StaticUsage);
~
}
public function remesh() {
// Re-set vertexBuffer
var vbData = vertexBuffer.lock();
for (i in 0...Std.int(vbData.length / structureLength)) {
vbData.set(i * structureLength, vertices[i * 3]);
vbData.set(i * structureLength + 1, vertices[i * 3 + 1]);
vbData.set(i * structureLength + 2, vertices[i * 3 + 2]);
vbData.set(i * structureLength + 3, normals[i * 3]);
vbData.set(i * structureLength + 4, normals[i * 3 + 1]);
vbData.set(i * structureLength + 5, normals[i * 3 + 2]);
}
vertexBuffer.unlock();
// Re-set indexBuffer
var iData = indexBuffer.lock();
for (i in 0...iData.length) {
iData[i] = indices[i];
}
indexBuffer.unlock();
}
public function update() {...}
public function render(g:Graphics) {...}
}
And now last small thing
package;
import kha.Scheduler;
import kha.Framebuffer;
class App {
public static var mesh:Mesh;
public static var mc:MarchingCubes; // <-----
~
static var onEndFrames: Array<Void->Void> = [];
var kb = Input.getKeyboard(); // <-----
public function new() {
mesh = new Mesh();
mc = new MarchingCubes(); // <-----
mesh.load();
}
public function update() {
mesh.update();
// When keyboard's Up arrow key has been pressed
if(kb.started(Up)){
// Increase configuration index
mc.configIndex +=1;
// Re-generate
mc.clean();
mc.generate();
App.mesh.remesh();
}
if (onEndFrames != null) for (endFrames in onEndFrames) endFrames();
}
public function render(g:Framebuffer) {...}
public static function notifyOnEndFrame(func:Void->Void) {...}
}
And we are done! If you play, you will see triangle generated. On pressing Up
button, triangle from different cube's configuration will be generated!
Source code for this chapter.
Chapter-2
In chapter-1 we got marching cubes working and we were able to check its different configurations.
We will create 32x32 volume (basically our terrain chunk), and apply marching cubes to it.
Create new Haxe script Volume.hx
package;
import kha.arrays.Float32Array;
class Volume {
// Width of volume
public static var width = 32;
// Height of volume
public static var height = 32;
// Offset of noise
public static var offset = 1.5;
public static function makeTerrainVolume(done:(Float32Array)->Void) {
// Float32Array of size of volume
var volume = new Float32Array(width * height * width);
// Init perlin noise
var perlin = new Perlin();
// Iterate through volume
for (x in 0...width){
for(z in 0...width){
for (y in 0...height){
// Create density from perlin noise, this is terribly basic terrain generation code.
// height of terrain * perlin noise that is useable.
var density = 10 * perlin.perlin(x/ 16 + offset, y/ 16 + offset, z/ 16 + offset);
// Location of cube
var l = (x) + (y * width) + (z * height * width);
// Set density in volume
volume[l] = y-density;
}
}
}
done(volume);
}
}
Perlin noise code is taken from here.
With that our volume generation is done. Now we will march over the volume and apply the algorithm to it.
package;
import kha.FastFloat;
import kha.arrays.Float32Array;
import kha.math.FastVector3;
class MarchingCubes {
var idxV = 0;
var idxI = 0;
// Surface density
var surface = 0.5;
// Density of cube's corner.
var cube:Array<FastFloat> = [0,0,0,0,0,0,0,0];
// Volume
public static var vol:Float32Array;
public function new() {
// Create terrain volume
Volume.makeTerrainVolume((volume)->{
vol = volume;
});
generate();
}
public function clean() {...}
public function generate() {
// Iterate over the volume
for(x in 0...Volume.width-1){
for(y in 0...Volume.height-1){
for (z in 0...Volume.width-1){
for (i in 0...8){
// Sample density for cube
cube[i] = getDensityFromVolume(
x + MCData.cornerTable[i].x,
y + MCData.cornerTable[i].y,
z + MCData.cornerTable[i].z
);
}
// Apply algorithm on cube
polygonize(new FastVector3(x,y,z), cube);
}
}
}
calculateVertexNormal();
}
function polygonize(position:FastVector3, cube:Array<FastFloat>) {
// Get configuration index based on cube's density
var configIndex = getConfigIndex(cube);
// Return if it is above or below the surface.
if(configIndex == 0 || configIndex == 255) return;
var edgeIndex = 0;
for (i in 0...15){...}
}
inline function getConfigIndex(cube:Array<FastFloat>) {
var configIndex = 0;
// If the density is above surface than do bitwise operation to determine the configuration index.
// If you were to try https://try.haxe.org/#7390D, you will see that it will give us all configuration index possible.
if(cube[0] > surface) configIndex |= 1;
if(cube[1] > surface) configIndex |= 2;
if(cube[2] > surface) configIndex |= 4;
if(cube[3] > surface) configIndex |= 8;
if(cube[4] > surface) configIndex |= 16;
if(cube[5] > surface) configIndex |= 32;
if(cube[6] > surface) configIndex |= 64;
if(cube[7] > surface) configIndex |= 128;
return configIndex;
}
inline public function getDensityFromVolume(x:Float, y:Float, z:Float) {
// Get density from volume at position
return vol[Std.int(x) + Std.int(y) * Volume.width + Std.int(z) * Volume.width * Volume.height];
}
public static function calculateVertexNormal() {...}
}
We will have to update our vertex buffer to hold more pipeline.
// Mesh.hx
// In load()
vertexBuffer = new VertexBuffer(Std.int(vertices.length / 3)*2, structure, Usage.StaticUsage);
To offset terrain on run.
// App.hx
// In update()
if(kb.started(Up)){
Volume.offset += 1;
Volume.makeTerrainVolume((vol)->{
MarchingCubes.vol = vol;
});
mc.clean();
mc.generate();
App.mesh.remesh();
}
And you should have blocky terrain which you can change with keyboard.
Unlike me, if you are not fan of blocky terrain, you can sample it vertices position and lerp it.
package;
import kha.FastFloat;
import kha.arrays.Float32Array;
import kha.math.FastVector3;
class MarchingCubes {
var idxV = 0;
~
public static var vol:Float32Array;
var smooth = true;
public function new() {...}
public function clean() {...}
public function generate() {...}
function polygonize(position:FastVector3, cube:Array<FastFloat>) {
~
for (i in 0...15){
var indice = MCData.triangleTable[configIndex][edgeIndex];
if(indice == -1) return;
var vert1 = position.add(MCData.cornerTable[MCData.edgeIndexes[indice][0]]);
var vert2 = position.add(MCData.cornerTable[MCData.edgeIndexes[indice][1]]);
var vertPosition = new FastVector3();
if(smooth){
// Get density of edge index.
var vert1Sample = cube[MCData.edgeIndexes[indice][0]];
var vert2Sample = cube[MCData.edgeIndexes[indice][1]];
// Get difference of it
var difference = vert2Sample - vert1Sample;
// If difference is zero, set surface as difference.
if (difference == 0) difference = surface;
else difference = (surface - vert1Sample) / difference;
// Else lerp it.
vertPosition = vert1.add(vert2.sub(vert1).mult(difference));
}else {
vertPosition = vert1.add(vert2).mult(0.5);
}
App.mesh.vertices[idxV] = vertPosition.x;
~
edgeIndex++;
}
}
inline function getConfigIndex(cube:Array<FastFloat>) {...}
inline public function getDensityFromVolume(x:Float, y:Float, z:Float) {...}
public static function calculateVertexNormal() {...}
}
You should finally get dunes that you probably wants.
Source code for this chapter.
How-to read
This is text that you have to read in order to understand what is going on.
Boring right? I know.
I am expandable
// This is code example. Click 2 paper icon to copy code, if you are pathetic human. ->
var you = "cute";
/* Playable rust code. Kinda cool right? Carry on, it doesn't support haxe*/ fn main(){ println!("Hey there!"); }
Some information blocks:
This is dangerous, stay away if you can.
This is warning, don't tell me that I didn't warn you.
Information that even illuminati doesn't have.
This is success, yay!
FAQ
I HAVE PROBLEM WITH THIS TUTORIAL, WHAT CAN I DO?
You can:
- Ask in #kha channel in Haxe discord server and mention
@BlackGoku36
- Use irc, connect to ktxsoftware.com and #kha channel.
- Create github issue here
Feel free to ask anything!
YOUR TUTORIALS ARE GOOD! MIND IF I TRANSLATE IT TO OTHER LANGUAGE?
Yes, you can translate it to whatever language you want, whether it is `Gujarati`, `Hindi`, `Japanese`, `Portuguese`, or even `MC enchanting table language`, feel free to do so. The only thing you gotta do is credit me and not claim yourself as original author, other than that, you are good.