This commit is contained in:
Zack Buhman 2026-03-05 21:53:02 -06:00
commit d8443b3c8c
26 changed files with 34254 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.~*
.\#*
\#*
*~
*.o
main
*.so
*.dylib

50
Makefile Normal file
View File

@ -0,0 +1,50 @@
#PREFIX = x86_64-w64-mingw32-
CC=$(PREFIX)gcc
CXX=$(PREFIX)g++
OPT = -Og -march=x86-64-v3
CSTD = -std=gnu23
CXXSTD = -std=gnu++23
CFLAGS += -g
CFLAGS += -fpic
CFLAGS += -I./include
CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -Wno-error=unused-but-set-variable
CFLAGS += -Wno-error=unknown-pragmas -Wno-unknown-pragmas
CFLAGS += $(shell pkg-config --cflags glfw3)
LDFLAGS += -lm
LDFLAGS += $(shell pkg-config --libs glfw3)
OBJS = \
src/gl.o \
src/opengl.o \
src/test.o
all: test.so
%.o: %.c
$(CC) $(ARCH) $(CSTD) $(CFLAGS) $(OPT) -c $< -o $@
%.o: %.cpp
$(CXX) $(ARCH) $(CXXSTD) $(CFLAGS) $(OPT) -c $< -o $@
test.so: $(OBJS)
$(CC) $(ARCH) $(OPT) -shared -g $^ -o $@ -lSDL3
main: $(OBJS) src/main.o
$(CC) $(ARCH) $(LDFLAGS) $(OPT) -g $^ -o $@
clean:
find . -type f ! -name "*.*" -delete
.SUFFIXES:
.INTERMEDIATE:
.SECONDARY:
.PHONY: all clean phony
%: RCS/%,v
%: RCS/%
%: %,v
%: s.%
%: SCCS/s.%

7
conf.lua Normal file
View File

@ -0,0 +1,7 @@
function love.conf(t)
t.window.width = 1024
t.window.height = 1024
t.window.depth = true
t.window.resizable = true
t.graphics.excluderenderers = {"vulkan", "metal"}
end

311
include/KHR/khrplatform.h Normal file
View File

@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3199
include/glad/gl.h Normal file

File diff suppressed because it is too large Load Diff

14
include/opengl.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void * read_file(const char * filename, int * out_size);
unsigned int compile_from_files(const char * vertex_path,
const char * fragment_path);
#ifdef __cplusplus
}
#endif

2941
include/sal.h Normal file

File diff suppressed because it is too large Load Diff

13
include/test.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void load();
void draw();
void update(float lx, float ly, float ry);
#ifdef __cplusplus
}
#endif

56
main.lua Normal file
View File

@ -0,0 +1,56 @@
local ffi = require 'ffi'
local joysticks
function init()
joysticks = love.joystick.getJoysticks()
ffi.cdef[[
void load();
void draw();
void update(float lx, float ly, float ry);
]]
test = ffi.load("./test.so")
test.load()
end
local update = function(dt)
for _, joystick in ipairs(joysticks) do
local lx = joystick:getGamepadAxis("leftx")
local ly = joystick:getGamepadAxis("lefty")
local ry = joystick:getGamepadAxis("righty")
test.update(lx, ly, ry)
end
end
local draw = function()
test.draw()
end
function love.run()
init()
love.timer.step()
return function()
love.event.pump()
for name, a,b,c,d,e,f,g,h in love.event.poll() do
if name == "quit" then
if c or not love.quit or not love.quit() then
return a or 0, b
end
end
if name == "keypressed" then
keypressed(a, b, c)
end
end
local dt = love.timer.step()
update(dt)
draw()
love.graphics.present()
love.timer.sleep(0.001)
end
end

Binary file not shown.

104
minecraft/gen/blocks.py Normal file
View File

@ -0,0 +1,104 @@
import struct
def id_to_px(i):
x = i % 16
y = i // 16
return x * 16, y * 16
def px_to_id(px, py):
x = px // 16
y = py // 16
i = y * 16 + x
return i
unk = 185
mapping = [
(1, 1, "stone"),
(2, 0, "grass"),
(31, 0, "grass"), # fixme actually tallgrass
(3, 2, "dirt"),
(4, 16, "stonebrick"),
(5, 4, "wood"),
(6, 15, "sapling"),
(7, 17, "bedrock"),
(8, 205, "water"), # flowing
(9, 205, "water"), # still
(10, 237, "lava"), # flowing
(11, 237, "lava"), # still
(12, 18, "sand"),
(13, 19, "gravel"),
(14, 32, "oreGold"),
(15, 33, "oreIron"),
(16, 34, "oreCoal"),
(17, 20, "log"),
(18, 52, "leaves"),
(19, 48, "sponge"),
(20, 49, "glass"),
(35, 64, "cloth"),
(37, 13, "flower"),
(38, 12, "rose"),
(39, 29, "mushroom"),
(40, 28, "mushroom"),
(41, 39, "blockGold"),
(42, 38, "blockIron"),
(43, 5, "stoneSlab"), # double
(44, 5, "stoneSlab"), # single
(45, 7, "brick"),
(46, 8, "tnt"),
(47, 35, "bookshelf"),
(48, 36, "stoneMoss"),
(49, 37, "obsidian"),
(50, 80, "torch"),
(51, 31, "fire"),
(52, 65, "mobSpawner"),
(53, 4, "stairsWood"),
(54, 27, "chest"),
(55, 84, "redstoneDust"),
(56, 50, "oreDiamond"),
(57, 40, "blockDiamond"),
(58, 43, "workbench"),
(59, 88, "crops"),
(60, 87, "farmland"),
(61, 44, "furnace"), # off
(62, 61, "furnace"), # burning
(63, unk, "sign"),
(64, 81, "doorWood"),
(65, 83, "ladder"),
(66, 128, "rail"),
(67, 16, "stairsStone"),
(68, unk, "sign"),
(69, 96, "lever"),
(70, 6, "pressurePlate"),
(71, 82, "doorIron"),
(72, 6, "pressurePlate"),
(73, 51, "oreRedstone"),
(74, 51, "oreRedstone"),
(75, 115, "notGate"),
(76, 99, "notGate"),
(77, unk, "button"),
(78, 66, "snow"),
(79, 67, "ice"),
(80, 66, "snow"),
(81, 70, "cactus"),
(82, 72, "clay"),
(83, 73, "reeds"),
(84, 74, "jukebox"),
(85, 4, "fence"),
(86, 102, "pumpkin"),
(87, 103, "hellrock"),
(88, 104, "hellsand"),
(89, 105, "lightgem"),
(90, 14, "portal"),
(91, 102, "pumpkin"),
]
lookup = {
k: v for k, v, _ in mapping
}
with open("block_id_to_texture_id.data", "wb") as f:
for i in range(256):
value = lookup.get(i, unk)
f.write(struct.pack("<i", value))
print(str(value)+',')

134
minecraft/gen/cube.py Normal file
View File

@ -0,0 +1,134 @@
positions = [
(1.0, 1.0, -1.0),
(1.0, -1.0, -1.0),
(1.0, 1.0, 1.0),
(1.0, -1.0, 1.0),
(-1.0, 1.0, -1.0),
(-1.0, -1.0, -1.0),
(-1.0, 1.0, 1.0),
(-1.0, -1.0, 1.0)
]
normals = [
(0.0, 1.0, 0.0),
(0.0, 0.0, 1.0),
(-1.0, 0.0, 0.0),
(0.0, -1.0, 0.0),
(1.0, 0.0, 0.0),
(0.0, 0.0, -1.0),
]
textures = [
(1.0, 0.0),
(0.0, 1.0),
(0.0, 0.0),
(1.0, 1.0),
(0.0, 0.0),
(1.0, 0.0),
]
indices = [
[
[4, 0, 0],
[2, 1, 0],
[0, 2, 0],
],
[
[2, 3, 1],
[7, 4, 1],
[3, 5, 1],
],
[
[6, 3, 2],
[5, 4, 2],
[7, 5, 2],
],
[
[1, 0, 3],
[7, 1, 3],
[5, 2, 3],
],
[
[0, 3, 4],
[3, 4, 4],
[1, 5, 4],
],
[
[4, 3, 5],
[1, 4, 5],
[5, 5, 5],
],
[
[4, 0, 0],
[6, 3, 0],
[2, 1, 0],
],
[
[2, 3, 1],
[6, 1, 1],
[7, 4, 1],
],
[
[6, 3, 2],
[4, 1, 2],
[5, 4, 2],
],
[
[1, 0, 3],
[3, 3, 3],
[7, 1, 3],
],
[
[0, 3, 4],
[2, 1, 4],
[3, 4, 4],
],
[
[4, 3, 5],
[0, 1, 5],
[1, 4, 5],
],
]
vertex_buffer = []
index_buffer = []
index_lookup = {}
for triangle in indices:
for p_ix, t_ix, n_ix in triangle:
key = (p_ix, n_ix, t_ix)
if key not in index_lookup:
position = positions[p_ix]
normal = normals[n_ix]
texture = textures[t_ix]
index = len(vertex_buffer)
index_lookup[key] = index
vertex_buffer.append((tuple(position),
tuple(normal),
tuple(texture)))
index_buffer.append(index_lookup[key])
def gen():
for position, normal, texture in vertex_buffer:
p = ", ".join(map(str, map(float, position)))
n = ", ".join(map(str, map(float, normal)))
t = ", ".join(map(str, map(float, texture)))
print(f"vertex_t(vec3({p}), vec3({n}), vec2({t})),")
for i in range(len(index_buffer) // 3):
tri = ", ".join(str(index_buffer[i * 3 + n]) for n in range(3))
print(f"{tri},")
from collections import defaultdict
by_normal = defaultdict(list)
for i in range(len(index_buffer) // 3):
tri = [index_buffer[i * 3 + n] for n in range(3)]
s = set(vertex_buffer[j][1] for j in tri)
assert len(s) == 1
normal, = iter(s)
by_normal[normal].append(tri)
from pprint import pprint
pprint(dict(by_normal))
pprint(vertex_buffer)

401
minecraft/gen/mc.py Normal file
View File

@ -0,0 +1,401 @@
# https://minecraft.fandom.com/wiki/Region_file_format
# https://minecraft.wiki/w/NBT_format#Binary_format
# https://minecraft.wiki/w/Chunk_format/McRegion
import sys
import struct
from pprint import pprint
import zlib
import enum
from dataclasses import dataclass
from itertools import chain
def _parse_locations(mem, offset):
for i in range(1024):
ix = offset + i * 4
chunk_location, = struct.unpack(">I", mem[ix:ix+4])
chunk_offset = (chunk_location >> 8) & 0xffffff
chunk_sector_count = chunk_location & 0xff
yield chunk_offset, chunk_sector_count
def parse_locations(mem, offset):
locations = list(_parse_locations(mem, offset))
return offset + 1024 * 4, locations
def _parse_timestamps(mem, offset):
for i in range(1024):
ix = offset + i * 4
timestamp, = struct.unpack(">I", mem[ix:ix+4])
yield timestamp
def parse_timestamps(mem, offset):
timestamps = list(_parse_timestamps(mem, offset))
return offset + 1024 * 4, timestamps
def print_locations(locations):
for y in range(32):
for x in range(32):
offset, count = locations[y * 32 + x]
print(str(offset).rjust(4), end=' ')
print()
class CountZeroException(Exception):
pass
def parse_payload(mem, location):
offset, count = location
if count == 0:
raise CountZeroException()
ix = offset * 4096
payload = mem[ix:ix + count * 4096]
length, = struct.unpack(">I", payload[0:4])
assert length <= count * 4096, (length, count)
compression_type = payload[4]
data = payload[5:5 + (length - 1)]
assert compression_type == 2, compression_type
uncompressed = zlib.decompress(data)
return memoryview(uncompressed)
class TAG:
Byte = 0x01
Short = 0x02
Int = 0x03
Long = 0x04
Float = 0x05
Double = 0x06
ByteArray = 0x07
String = 0x08
List = 0x09
Compound = 0x0a
@dataclass
class Byte:
name: str
value: int
@dataclass
class Short:
name: str
value: int
@dataclass
class Int:
name: str
value: int
@dataclass
class Long:
name: str
value: int
@dataclass
class Float:
name: str
value: float
@dataclass
class Double:
name: str
value: float
@dataclass
class ByteArray:
name: str
value: bytes
@dataclass
class String:
name: str
value: str
@dataclass
class List:
name: str
items: list
@dataclass
class Compound:
name: str
tags: list
def indent(level):
return " " * level
def parse_tag_inner(mem, offset, level, tag_type, name):
payload = mem[offset:]
if tag_type == TAG.Byte:
value, = struct.unpack(">b", payload[0:1])
return offset + 1, Byte(name, value)
if tag_type == TAG.Short:
value, = struct.unpack(">h", payload[0:2])
return offset + 2, Short(name, value)
elif tag_type == TAG.Int:
value, = struct.unpack(">i", payload[0:4])
return offset + 4, Int(name, value)
elif tag_type == TAG.Long:
value, = struct.unpack(">q", payload[0:8])
return offset + 8, Long(name, value)
elif tag_type == TAG.Float:
value, = struct.unpack(">f", payload[0:4])
return offset + 4, Float(name, value)
elif tag_type == TAG.Double:
value, = struct.unpack(">d", payload[0:8])
return offset + 8, Double(name, value)
elif tag_type == TAG.ByteArray:
size, = struct.unpack(">i", payload[0:4])
value = bytes(payload[4:4+size])
return offset + 4 + size, ByteArray(name, value)
elif tag_type == TAG.String:
size, = struct.unpack(">H", payload[0:2])
value = bytes(payload[2:2+size]).decode('utf-8')
return offset + 2 + size, String(name, value)
elif tag_type == TAG.List:
list_content_tag_id, size = struct.unpack(">BI", payload[0:5])
items = []
offset = offset + 5
for i in range(size):
payload = mem[offset:]
offset, item = parse_tag_inner(mem, offset, level, list_content_tag_id, None)
items.append(item)
return offset, List(name, items)
elif tag_type == TAG.Compound:
tags = []
while payload[0] != 0:
offset, tag = parse_tag(mem, offset, level+1)
payload = mem[offset:]
tags.append(tag)
return offset + 1, Compound(name, tags)
else:
assert False, tag_type
def parse_tag(mem, offset, level):
data = mem[offset:]
tag_type = data[0]
name_length, = struct.unpack(">H", data[1:3])
name = bytes(data[3:3+name_length])
#print(indent(level), tag_type, name_length, name)
offset = offset + 3 + name_length
return parse_tag_inner(mem, offset, level, tag_type, name)
@dataclass
class Level:
blocks: bytes
data: bytes
sky_light: bytes
block_light: bytes
height_map: bytes
x_pos: int
z_pos: int
def level_from_tag(tag):
assert type(tag) == Compound
assert tag.name == b''
assert len(tag.tags) == 1
level, = tag.tags
assert type(level) == Compound
assert level.name == b'Level'
name_mapping = {
b'Blocks': 'blocks',
b'Data': 'data',
b'SkyLight': 'sky_light',
b'BlockLight': 'block_light',
b'HeightMap': 'height_map',
b'xPos': 'x_pos',
b'zPos': 'z_pos',
}
args = {}
for tag in level.tags:
if tag.name in name_mapping:
arg_name = name_mapping[tag.name]
args[arg_name] = tag.value
return Level(**args)
def parse_location(mem, location):
uncompressed = parse_payload(mem, location)
offset, tag = parse_tag(uncompressed, 0, 0)
assert offset == len(uncompressed), (offset, len(uncompressed))
level = level_from_tag(tag)
return level
def xyz_from_block_index(block_index):
assert block_index >= 0 and block_index < (128 * 16 * 16)
x = int(block_index / (128 * 16))
y = int(block_index % 128)
z = int(int(block_index / 128) % 16)
return x, y, z
def block_index_from_xyz(x, y, z):
assert x >= 0 and x < 16
assert y >= 0 and y < 128
assert z >= 0 and z < 16
return int(y + z * 128 + x * 128 * 16)
def wrap_n(nc, chunk_c):
if nc < 0:
nc = 15
chunk_c = chunk_c - 1
if nc > 15:
nc = 0
chunk_c = chunk_c + 1
return nc, chunk_c
def vec3_add(v1, v2):
return (
v1[0] + v2[0],
v1[1] + v2[1],
v1[2] + v2[2],
)
def vec3_mul(v, s):
return (
v[0] * s,
v[1] * s,
v[2] * s,
)
vertex_table = [
((-1.0, 1.0, -1.0), (0.0, 1.0, 0.0), (1.0, 0.0)),
((1.0, 1.0, 1.0), (0.0, 1.0, 0.0), (0.0, 1.0)),
((1.0, 1.0, -1.0), (0.0, 1.0, 0.0), (0.0, 0.0)),
((1.0, 1.0, 1.0), (0.0, 0.0, 1.0), (1.0, 1.0)),
((-1.0, -1.0, 1.0), (0.0, 0.0, 1.0), (0.0, 0.0)),
((1.0, -1.0, 1.0), (0.0, 0.0, 1.0), (1.0, 0.0)),
((-1.0, 1.0, 1.0), (-1.0, 0.0, 0.0), (1.0, 1.0)),
((-1.0, -1.0, -1.0), (-1.0, 0.0, 0.0), (0.0, 0.0)),
((-1.0, -1.0, 1.0), (-1.0, 0.0, 0.0), (1.0, 0.0)),
((1.0, -1.0, -1.0), (0.0, -1.0, 0.0), (1.0, 0.0)),
((-1.0, -1.0, 1.0), (0.0, -1.0, 0.0), (0.0, 1.0)),
((-1.0, -1.0, -1.0), (0.0, -1.0, 0.0), (0.0, 0.0)),
((1.0, 1.0, -1.0), (1.0, 0.0, 0.0), (1.0, 1.0)),
((1.0, -1.0, 1.0), (1.0, 0.0, 0.0), (0.0, 0.0)),
((1.0, -1.0, -1.0), (1.0, 0.0, 0.0), (1.0, 0.0)),
((-1.0, 1.0, -1.0), (0.0, 0.0, -1.0), (1.0, 1.0)),
((1.0, -1.0, -1.0), (0.0, 0.0, -1.0), (0.0, 0.0)),
((-1.0, -1.0, -1.0), (0.0, 0.0, -1.0), (1.0, 0.0)),
((-1.0, 1.0, 1.0), (0.0, 1.0, 0.0), (1.0, 1.0)),
((-1.0, 1.0, 1.0), (0.0, 0.0, 1.0), (0.0, 1.0)),
((-1.0, 1.0, -1.0), (-1.0, 0.0, 0.0), (0.0, 1.0)),
((1.0, -1.0, 1.0), (0.0, -1.0, 0.0), (1.0, 1.0)),
((1.0, 1.0, 1.0), (1.0, 0.0, 0.0), (0.0, 1.0)),
((1.0, 1.0, -1.0), (0.0, 0.0, -1.0), (0.0, 1.0))
]
faces_by_normal = {
(-1.0, 0.0, 0.0): [6, 7, 8, 6, 20, 7],
(0.0, -1.0, 0.0): [9, 10, 11, 9, 21, 10],
(0.0, 0.0, -1.0): [15, 16, 17, 15, 23, 16],
(0.0, 0.0, 1.0): [3, 4, 5, 3, 19, 4],
(0.0, 1.0, 0.0): [0, 1, 2, 0, 18, 1],
(1.0, 0.0, 0.0): [12, 13, 14, 12, 22, 13]
}
vertex_buffer = {}
def add_vertex(vertex):
if vertex in vertex_buffer:
return vertex_buffer[vertex]
else:
index = len(vertex_buffer)
vertex_buffer[vertex] = index
return index
def emit_face(center_position, block_id, triangles):
for index in triangles:
position, normal, texture = vertex_table[index]
position = vec3_add(vec3_mul(position, 0.5), center_position)
vertex = (position, normal, texture, block_id)
new_index = add_vertex(vertex)
yield new_index
def block_neighbors(level_table, chunk_x, chunk_z, block_index):
block_id = level_table[(chunk_x, chunk_z)].blocks[block_index]
if block_id == 0:
return
def neighbor_exists(nx, ny, nz):
if ny > 127 or ny < 0:
return False
nx, n_chunk_x = wrap_n(nx, chunk_x)
nz, n_chunk_z = wrap_n(nz, chunk_z)
if nx > 15 or nx < 0:
return True
if nz > 15 or nz < 0:
return True
n_block_index = block_index_from_xyz(nx, ny, nz)
key = (n_chunk_x, n_chunk_z)
if key not in level_table:
return True
n_block_id = level_table[key].blocks[n_block_index]
return n_block_id != 0
x, y, z = xyz_from_block_index(block_index)
center_position = vec3_add((x, y, z), (chunk_x * 16, 0, chunk_z * 16))
for normal, triangles in faces_by_normal.items():
neighbor = vec3_add(normal, (x, y, z))
if not neighbor_exists(*neighbor):
yield from emit_face(center_position, block_id, triangles)
#yield chunk_x, chunk_z, block_index, block_id
#break
def devoxelize_region(level_table):
for chunk_x, chunk_z in level_table.keys():
for block_index in range(128 * 16 * 16):
yield from block_neighbors(level_table, chunk_x, chunk_z, block_index)
from collections import defaultdict
counts = defaultdict(int)
def linearized_vertex_buffer():
for vertex, i in sorted(vertex_buffer.items(), key=lambda kv: kv[1]):
yield vertex
def main(mcr_path, data_path):
with open(mcr_path, "rb") as f:
buf = f.read()
mem = memoryview(buf)
offset = 0
offset, locations = parse_locations(mem, offset)
offset, timestamps = parse_timestamps(mem, offset)
assert offset == 0x2000
level_table = {}
for location in locations:
try:
level = parse_location(mem, location)
except CountZeroException:
continue
x, z = level.x_pos, level.z_pos
level_table[(x, z)] = level
#with open(f"blocks__{x:02x}_{z:02x}.data", "wb") as f:
# f.write(level.blocks)
with open(data_path + ".idx", "wb") as f:
for index in devoxelize_region(level_table):
f.write(struct.pack("<I", index))
with open(data_path + ".vtx", "wb") as f:
for vertex in linearized_vertex_buffer():
vertex = [*vertex[0], *vertex[1], *vertex[2], vertex[3]]#, vertex[3]]
f.write(struct.pack("<fffffffff", *vertex))
#for chunk_x, chunk_z, block_index, block_id in devoxelize_region(level_table):
# #print(chunk_x, chunk_z, block_id)
# counts[block_id] += 1
# f.write(struct.pack("<bbBb", chunk_x, chunk_z, block_id, 0))
# f.write(struct.pack("<i", block_index))
mcr_path = sys.argv[1]
data_path = sys.argv[2]
main(mcr_path, data_path)

BIN
minecraft/terrain.data Normal file

Binary file not shown.

BIN
minecraft/terrain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

295
shader/test.frag Normal file
View File

@ -0,0 +1,295 @@
#version 330 core
in vec3 PixelNormal;
in vec2 PixelTexture;
in float PixelBlock;
out vec4 FragColor;
uniform sampler2D TerrainSampler;
/*layout (std140) uniform TexturesLayout
{
uint Textures[256];
};*/
int Textures[256] = int[256](
185,
1,
0,
2,
16,
4,
15,
17,
205,
205,
237,
237,
18,
19,
32,
33,
34,
20,
52,
48,
49,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
0,
185,
185,
185,
64,
185,
13,
12,
29,
28,
39,
38,
5,
5,
7,
8,
35,
36,
37,
80,
31,
65,
4,
27,
84,
50,
40,
43,
88,
87,
44,
61,
185,
81,
83,
128,
16,
185,
96,
6,
82,
6,
51,
51,
115,
99,
185,
66,
67,
66,
70,
72,
73,
74,
4,
102,
103,
104,
105,
14,
102,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185,
185
);
void main()
{
vec3 light_direction = normalize(vec3(-1, -0.5, 0.5));
float diffuse_intensity = max(dot(normalize(PixelNormal), light_direction), 0.0);
int terrain_ix = int(Textures[int(PixelBlock)]);
int terrain_x = terrain_ix % 16;
int terrain_y = terrain_ix / 16;
ivec2 coord = ivec2(terrain_x, terrain_y) * 16;
coord += ivec2(PixelTexture.xy * 16.0);
vec4 texture_color = texelFetch(TerrainSampler, coord, 0);
if (texture_color.w != 1.0) {
discard;
return;
}
if (int(PixelBlock) == 18) // leaves
texture_color.xyz *= vec3(0.125, 0.494, 0.027);
FragColor = vec4(texture_color.xyz * vec3(diffuse_intensity), 1.0);
}

23
shader/test.vert Normal file
View File

@ -0,0 +1,23 @@
#version 330 core
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
layout (location = 2) in vec3 Texture;
out vec3 PixelNormal;
out vec2 PixelTexture;
out float PixelBlock;
uniform mat4 Transform;
void main()
{
//gl_Position = vec4(position, 1.0);
//ourColor = color;
PixelNormal = Normal.xzy;
PixelTexture = Texture.xy;
PixelBlock = Texture.z;
gl_Position = Transform * vec4(Position.xzy, 1.0);
}

1356
src/gl.c Normal file

File diff suppressed because it is too large Load Diff

53
src/main.c Normal file
View File

@ -0,0 +1,53 @@
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
#include "glad/gl.h"
#include <GLFW/glfw3.h>
extern void load();
extern void draw();
int main()
{
glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
GLFWwindow* window = glfwCreateWindow(1024, 1024, "LearnOpenGL", NULL, NULL);
if (window == NULL) {
const char* description;
glfwGetError(&description);
printf("Failed to create GLFW window: %s\n", description);
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
gladLoadGL(glfwGetProcAddress);
glViewport(0, 0, 1024, 1024);
load();
while(!glfwWindowShouldClose(window)) {
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
draw();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}

112
src/opengl.c Normal file
View File

@ -0,0 +1,112 @@
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "glad/gl.h"
#include "opengl.h"
void * read_file(const char * filename, int * out_size)
{
FILE * f = fopen(filename, "rb");
if (f == NULL) {
fprintf(stderr, "fopen(%s): %s\n", filename, strerror(errno));
return NULL;
}
int fseek_end_ret = fseek(f, 0, SEEK_END);
if (fseek_end_ret < 0) {
fprintf(stderr, "fseek(%s, SEEK_END): %s\n", filename, strerror(errno));
return NULL;
}
long size = ftell(f);
if (size < 0) {
fprintf(stderr, "ftell(%s): %s\n", filename, strerror(errno));
return NULL;
}
int fseek_set_ret = fseek(f, 0, SEEK_SET);
if (fseek_set_ret < 0) {
fprintf(stderr, "lseek(%s, SEEK_SET): %s\n", filename, strerror(errno));
return NULL;
}
rewind(f);
void * buf = malloc(size);
size_t read_size = fread(buf, 1, size, f);
if (read_size != size) {
fprintf(stderr, "fread(%s): %s\n", filename, strerror(errno));
return NULL;
}
*out_size = size;
return buf;
}
unsigned int compile(const char * vertex_source,
int vertex_source_size,
const char * fragment_source,
int fragment_source_size)
{
int compile_status;
char info_log[512];
// vertex shader
unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_source, &vertex_source_size);
glCompileShader(vertex_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &compile_status);
if (!compile_status) {
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
fprintf(stderr, "vertex shader compile: %s\n", info_log);
}
// fragment shader
unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_source, &fragment_source_size);
glCompileShader(fragment_shader);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &compile_status);
if (!compile_status) {
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
fprintf(stderr, "fragment shader compile: %s\n", info_log);
}
// link shaders
unsigned int shader_program = glCreateProgram();
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);
glGetProgramiv(shader_program, GL_LINK_STATUS, &compile_status);
if (!compile_status) {
glGetProgramInfoLog(shader_program, 512, NULL, info_log);
fprintf(stderr, "shader link: %s\n", info_log);
}
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return shader_program;
}
unsigned int compile_from_files(const char * vertex_path,
const char * fragment_path)
{
int vertex_source_size;
char * vertex_source = read_file(vertex_path, &vertex_source_size);
assert(vertex_source != NULL);
int fragment_source_size;
char * fragment_source = read_file(fragment_path, &fragment_source_size);
assert(fragment_source != NULL);
unsigned int program = compile(vertex_source, vertex_source_size,
fragment_source, fragment_source_size);
free(vertex_source);
free(fragment_source);
return program;
}

242
src/test.cpp Normal file
View File

@ -0,0 +1,242 @@
#include <stdio.h>
#include <stdlib.h>
#include "glad/gl.h"
#include "opengl.h"
#include "directxmath/directxmath.h"
#include "test.h"
struct location {
struct {
unsigned int position;
unsigned int normal;
unsigned int texture;
} attrib;
struct {
unsigned int transform;
unsigned int terrain_sampler;
} uniform;
};
// state
static unsigned int test_program;
static struct location location;
void load_program()
{
unsigned int program = compile_from_files("shader/test.vert",
"shader/test.frag");
location.attrib.position = glGetAttribLocation(program, "Position");
location.attrib.normal = glGetAttribLocation(program, "Normal");
location.attrib.texture = glGetAttribLocation(program, "Texture");
printf("attributes:\n position %u\n normal %u\n texture %u\n",
location.attrib.position,
location.attrib.normal,
location.attrib.texture);
location.uniform.transform = glGetUniformLocation(program, "Transform");
location.uniform.terrain_sampler = glGetUniformLocation(program, "TerrainSampler");
printf("uniforms:\n transform %u\n terrain_sampler %u\n",
location.uniform.transform,
location.uniform.terrain_sampler);
test_program = program;
}
static unsigned int vertex_array_objects[4];
static unsigned int vertex_buffers[4];
static unsigned int index_buffers[4];
static unsigned int index_count[4];
const char * vertex_paths[] = {
"minecraft/region.0.0.vtx",
"minecraft/region.0.-1.vtx",
"minecraft/region.-1.0.vtx",
"minecraft/region.-1.-1.vtx",
};
static const char * index_paths[] = {
"minecraft/region.0.0.idx",
"minecraft/region.0.-1.idx",
"minecraft/region.-1.0.idx",
"minecraft/region.-1.-1.idx",
};
void load_vertex_buffer(int i)
{
int vertex_buffer_data_size;
void * vertex_buffer_data = read_file(vertex_paths[i], &vertex_buffer_data_size);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers[i]);
glBufferData(GL_ARRAY_BUFFER, vertex_buffer_data_size, vertex_buffer_data, GL_STATIC_DRAW);
free(vertex_buffer_data);
}
void load_element_buffer(int i)
{
int index_buffer_data_size;
void * index_buffer_data = read_file(index_paths[i], &index_buffer_data_size);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffers[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_buffer_data_size, index_buffer_data, GL_STATIC_DRAW);
index_count[i] = index_buffer_data_size / 4;
free(index_buffer_data);
}
void load_vertex_attributes()
{
glVertexAttribPointer(location.attrib.position,
3,
GL_FLOAT,
GL_FALSE,
(sizeof (float)) * 9,
(void*)(0 * 4)
);
glVertexAttribPointer(location.attrib.normal,
3,
GL_FLOAT,
GL_FALSE,
(sizeof (float)) * 9,
(void*)(3 * 4)
);
glVertexAttribPointer(location.attrib.texture,
3,
GL_FLOAT,
GL_FALSE,
(sizeof (float)) * 9,
(void*)(6 * 4)
);
glEnableVertexAttribArray(location.attrib.position);
glEnableVertexAttribArray(location.attrib.normal);
glEnableVertexAttribArray(location.attrib.texture);
}
void load_buffers()
{
glGenVertexArrays(4, vertex_array_objects);
glGenBuffers(4, index_buffers);
glGenBuffers(4, vertex_buffers);
for (int i = 0; i < 4; i++) {
glBindVertexArray(vertex_array_objects[i]);
load_element_buffer(i);
load_vertex_buffer(i);
load_vertex_attributes();
}
}
static unsigned int texture;
void load_textures()
{
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
int texture_data_size;
void * texture_data = read_file("minecraft/terrain.data", &texture_data_size);
assert(texture_data != NULL);
int width = 256;
int height = 256;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);
free(texture_data);
}
static unsigned int textures_ubo;
void load_texture_shader_storage()
{
unsigned int buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
int textures_data_size;
void * textures_data = read_file("minecraft/block_id_to_texture_id.data", &textures_data_size);
assert(textures_data != NULL);
printf("%d\n", textures_data_size);
glBufferData(GL_UNIFORM_BUFFER, textures_data_size, textures_data, GL_STATIC_DRAW);
free(textures_data);
textures_ubo = buffer;
}
extern "C" {
void * SDL_GL_GetProcAddress(const char *proc);
}
void load()
{
fprintf(stderr, "getproc %p\n", SDL_GL_GetProcAddress);
gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress);
load_program();
load_buffers();
load_textures();
//load_texture_shader_storage();
//unsigned int textures_layout = glGetUniformBlockIndex(test_program, "TexturesLayout");
//glUniformBlockBinding(test_program, textures_layout, 0);
//printf("textures_layout %d\n", textures_layout);
}
static float vx = 0.0;
static float vy = 0.0;
static float vz = 0.0;
void update(float lx, float ly, float ry)
{
vx += 2.5 * lx;
vy += -2.5 * ry;
vz += -2.5 * ly;
}
void draw()
{
XMVECTOR eye = XMVectorSet(vx + -50.0f, vz + -50.0f, vy + 150.0f, 0.0f);
XMVECTOR at = XMVectorSet(vx + 50.0f, vz + 50.0f, vy + 50.0f, 0.0f);
XMVECTOR up = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);
XMMATRIX view = XMMatrixLookAtRH(eye, at, up);
float fov_angle_y = XMConvertToRadians(45 * 0.75);
float aspect_ratio = 1.0;
float near_z = 1.0;
float far_z = 0.1;
XMMATRIX projection = XMMatrixPerspectiveFovRH(fov_angle_y, aspect_ratio, near_z, far_z);
XMMATRIX transform = view * projection;
glClearColor(0.1f, 0.1f, 0.1f, 0.1f);
glClearDepth(-1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(test_program);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniformMatrix4fv(location.uniform.transform, 1, false, (float *)&transform);
glUniform1i(location.uniform.terrain_sampler, 0);
//glBindBuffer(GL_UNIFORM_BUFFER, textures_ubo);
//glBindBufferBase(GL_UNIFORM_BUFFER, 0, textures_ubo);
for (int i = 0; i < 4; i++) {
glBindVertexArray(vertex_array_objects[i]);
glDrawElements(GL_TRIANGLES, index_count[i], GL_UNSIGNED_INT, 0);
}
}