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:

structure of kha

Showcases

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.

  1. Download and install:
  2. Git clone kha recursively, wherever you want.
git clone --recursive https://github.com/Kode/Kha.git
  1. In VSCode - extension, install Kha extension pack and Haxe extension pack.
  2. Go to setting, and navigate to Extensions - Kha - Kha Path and enter your path/to/Kha that you just cloned.
  3. Open an empty folder and press F1 (fn+F1 on macOS) and enter Kha: Init Project. New starter Kha project should be created.
  4. Go to Run panel, select Kha: HTML5 from dropdown and press triangle next to it to start debugging.

You should get something like this:

Some Image


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.

  1. Press F1 (fn + F1 on macOS) and enter Tasks: Configure Task and select Kha: Build for Debug HTML5.
  2. 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.

  1. Open task.json
  2. 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:

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:

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

Some expandables contents.

// 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.