Compare commits

...

12 Commits

54 changed files with 2070 additions and 497 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ main
*.so *.so
*.dylib *.dylib
__pycache__ __pycache__
minecraft/region*.lights.vtx

View File

@ -19,7 +19,9 @@ LDFLAGS += $(shell pkg-config --libs glfw3)
OBJS = \ OBJS = \
src/gl.o \ src/gl.o \
src/opengl.o \ src/opengl.o \
src/test.o src/test.o \
src/font.o \
src/window.o
all: test.so all: test.so

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -8,3 +8,12 @@ short index_buffer_configuration_offsets[] = {
768, 780, 798, 816, 840, 858, 882, 906, 768, 780, 798, 816, 840, 858, 882, 906,
936, 954, 978, 1002, 1032, 1056, 1086, 1116, 936, 954, 978, 1002, 1032, 1056, 1086, 1116,
}; };
struct {
short offset;
short count;
} index_buffer_custom_offsets[] = {
{1152, 12}, // tallgrass.obj
{1164, 36}, // fence.obj
{1200, 36}, // torch.obj
{1236, 24}, // wheat.obj
};

68
include/font.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
namespace font {
struct font_desc {
char const * const path;
int const texture_width;
int const texture_height;
int const glyph_width;
int const glyph_height;
};
font_desc const terminus[] = {
{
.path = "font/terminus_128x64_6x12.data",
.texture_width = 128,
.texture_height = 64,
.glyph_width = 6,
.glyph_height = 12,
},
{
.path = "font/terminus_128x128_8x16.data",
.texture_width = 128,
.texture_height = 128,
.glyph_width = 8,
.glyph_height = 16,
},
{
.path = "font/terminus_256x128_10x18.data",
.texture_width = 256,
.texture_height = 128,
.glyph_width = 10,
.glyph_height = 18,
},
{
.path = "font/terminus_256x128_12x24.data",
.texture_width = 256,
.texture_height = 128,
.glyph_width = 12,
.glyph_height = 24,
},
{
.path = "font/terminus_256x256_16x32.data",
.texture_width = 256,
.texture_height = 256,
.glyph_width = 16,
.glyph_height = 32,
},
};
int const terminus_length = (sizeof (terminus)) / (sizeof (font_desc));
struct font {
font_desc const * desc;
unsigned int texture;
int stride;
struct {
float width;
float height;
} cell;
};
void load_shader();
font load_font(font_desc const& desc);
void load_fonts(font * const fonts, font_desc const * const descs, int length);
int best_font(font_desc const * const descs, int length);
void draw_start(font const& font, unsigned int vertex_array_object, unsigned int index_buffer);
void draw_string(font const& font, char const * const s, int x, int y);
}

View File

@ -4,6 +4,9 @@
extern "C" { extern "C" {
#endif #endif
extern char const * g_source_path;
extern int g_source_path_length;
void * read_file(const char * filename, int * out_size); void * read_file(const char * filename, int * out_size);
unsigned int compile_from_files(const char * vertex_path, unsigned int compile_from_files(const char * vertex_path,

View File

@ -4,9 +4,11 @@
extern "C" { extern "C" {
#endif #endif
void load(); void load(const char * source_path);
void draw(); void draw();
void update(float lx, float ly, float ry); void update(float lx, float ly, float rx, float ry, float tl, float tr,
int up, int down, int left, int right,
int a, int b, int x, int y);
#ifdef __cplusplus #ifdef __cplusplus
} }

13
include/window.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern float g_window_width;
extern float g_window_height;
void update_window(int width, int height);
#ifdef __cplusplus
}
#endif

View File

@ -5,21 +5,36 @@ function init()
joysticks = love.joystick.getJoysticks() joysticks = love.joystick.getJoysticks()
ffi.cdef[[ ffi.cdef[[
void load(); void load(const char * source_path);
void update_window(int width, int height);
void draw(); void draw();
void update(float lx, float ly, float ry); void update(float lx, float ly, float rx, float ry, float tl, float tr,
int up, int down, int left, int right,
int a, int b, int x, int y);
]] ]]
test = ffi.load("./test.so") local source_path = love.filesystem.getSource()
test.load() test = ffi.load(source_path .. "/test.so")
test.load(source_path)
print(love.filesystem.getWorkingDirectory())
end end
local update = function(dt) local update = function(dt)
for _, joystick in ipairs(joysticks) do for _, joystick in ipairs(joysticks) do
local lx = joystick:getGamepadAxis("leftx") local lx = joystick:getGamepadAxis("leftx")
local ly = joystick:getGamepadAxis("lefty") local ly = joystick:getGamepadAxis("lefty")
local rx = joystick:getGamepadAxis("rightx")
local ry = joystick:getGamepadAxis("righty") local ry = joystick:getGamepadAxis("righty")
test.update(lx, ly, ry) local tl = joystick:getGamepadAxis("triggerleft")
local tr = joystick:getGamepadAxis("triggerright")
local up = joystick:isGamepadDown("dpup")
local down = joystick:isGamepadDown("dpdown")
local left = joystick:isGamepadDown("dpleft")
local right = joystick:isGamepadDown("dpright")
local a = joystick:isGamepadDown("a")
local b = joystick:isGamepadDown("b")
local x = joystick:isGamepadDown("x")
local y = joystick:isGamepadDown("y")
test.update(lx, ly, rx, ry, tl, tr, up, down, left, right, a, b, x, y)
end end
end end
@ -42,6 +57,12 @@ function love.run()
end end
end end
local width
local height
local flags
width, height, flags = love.window.getMode()
test.update_window(width, height)
local dt = love.timer.step() local dt = love.timer.step()
update(dt) update(dt)
@ -50,6 +71,6 @@ function love.run()
love.graphics.present() love.graphics.present()
love.timer.sleep(0.001) love.timer.sleep(0.001)
local fps = love.timer.getFPS( ) local fps = love.timer.getFPS( )
print(fps) --print(fps)
end end
end end

Binary file not shown.

Binary file not shown.

View File

@ -1,106 +1,13 @@
import struct import struct
import data
import sys
def id_to_px(i): unk = 253
x = i % 16
y = i // 16
return x * 16, y * 16
def px_to_id(px, py): with open(sys.argv[1], "wb") as f:
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): for i in range(256):
value = lookup.get(i, unk) if i in data.tiles_by_id:
value = data.tiles_by_id[i].texture
else:
value = unk
f.write(struct.pack("<i", value)) f.write(struct.pack("<i", value))
print(str(value).rjust(3), end=', ')
if i % 16 == 15:
print()

4
minecraft/gen/blocks.sh Normal file
View File

@ -0,0 +1,4 @@
set -eux
cd ./minecraft/gen
PYTHON=pypy3.11
$PYTHON blocks.py ../block_id_to_texture_id.data

36
minecraft/gen/cube.obj Normal file
View File

@ -0,0 +1,36 @@
# Blender 5.0.0
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 1.000000
vt -0.000000 0.000000
vt 1.000000 -0.000000
s 0
f 5/1/1 3/2/1 1/3/1
f 3/4/2 8/5/2 4/6/2
f 7/4/3 6/5/3 8/6/3
f 2/1/4 8/2/4 6/3/4
f 1/4/5 4/5/5 2/6/5
f 5/4/6 2/5/6 6/6/6
f 5/1/1 7/4/1 3/2/1
f 3/4/2 7/2/2 8/5/2
f 7/4/3 5/2/3 6/5/3
f 2/1/4 4/4/4 8/2/4
f 1/4/5 3/2/5 4/5/5
f 5/4/6 1/2/6 2/5/6

View File

@ -1,134 +0,0 @@
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)

364
minecraft/gen/data.py Normal file
View File

@ -0,0 +1,364 @@
from dataclasses import dataclass
class BlockID:
AIR = 0
STONE = 1
GRASS = 2
DIRT = 3
STONEBRICK = 4
WOOD = 5
SAPLING = 6
BEDROCK = 7
WATER = 8
WATER_CALM = 9
LAVA = 10
LAVA_CALM = 11
SAND = 12
GRAVEL = 13
ORE_GOLD = 14
ORE_IRON = 15
ORE_COAL = 16
TREE_TRUNK = 17
LEAVES = 18
SPONGE = 19
GLASS = 20
ORE_LAPIS = 21
BLOCK_LAPIS = 22
DISPENSER = 23
SANDSTONE = 24
NOTE_BLOCK = 25
BED = 26
RAIL_POWERED = 27
RAIL_ACTIVATOR = 28
PISTON_STICKY = 29
COBWEB = 30
TALL_GRASS = 31
DEAD_BUSH = 32
PISTON = 33
PISTON_HEAD = 34
CLOTH = 35
PISTON_MOVING = 36
FLOWER = 37
ROSE = 38
MUSHROOM_1 = 39
MUSHROOM_2 = 40
BLOCK_GOLD = 41
BLOCK_IRON = 42
STONESLAB_FULL = 43
STONESLAB_HALF = 44
BRICKS = 45
TNT = 46
BOOKSHELF = 47
MOSS_STONE = 48
OBSIDIAN = 49
TORCH = 50
FIRE = 51
SPAWNER = 52
STAIRS_WOOD = 53
CHEST = 54
WIRE = 55
ORE_EMERALD = 56
BLOCK_EMERALD = 57
WORKBENCH = 58
WHEAT = 59
FARMLAND = 60
FURNACE = 61
FURNACE_LIT = 62
SIGN = 63
DOOR_WOOD = 64
LADDER = 65
RAIL = 66
STAIRS_STONE = 67
SIGN_WALL = 68
LEVER = 69
PLATE_STONE = 70
DOOR_IRON = 71
PLATE_WOOD = 72
ORE_REDSTONE = 73
ORE_REDSTONE_LIT = 74
NOT_GATE_OFF = 75
NOT_GATE_ON = 76
BUTTON_STONE = 77
TOPSNOW = 78
ICE = 79
SNOW = 80
CACTUS = 81
CLAY = 82
REEDS = 83
JUKEBOX = 84
FENCE = 85
PUMPKIN = 86
NETHERRACK = 87
SOUL_SAND = 88
GLOWSTONE = 89
PORTAL = 90
PUMPKIN_LIT = 91
CAKE = 92
REPEATER_OFF = 93
REPEATER_ON = 94
INVISIBLE = 95
TRAPDOOR = 96
STONE_MONSTER_EGG = 97
STONE_BRICKS = 98
MUSHROOM1_BLOCK = 99
MUSHROOM2_BLOCK = 100
CLOTH_00 = 101
CLOTH_10 = 102
CLOTH_20 = 103
CLOTH_30 = 104
CLOTH_40 = 105
CLOTH_50 = 106
CLOTH_60 = 107
CLOTH_70 = 108
CLOTH_01 = 109
CLOTH_11 = 110
CLOTH_21 = 111
CLOTH_31 = 112
CLOTH_41 = 113
CLOTH_51 = 114
CLOTH_61 = 115
# modded
YELLOW_COBBLE = 128
MARBLE_YELLOW = 248
class Texture:
GRASS_TOP = 0
STONE = 1
DIRT = 2
GRASS_SIDE = 3
PLANKS = 4
STONE_SLAB_SIDE = 5
STONE_SLAB_TOP = 6
BRICKS = 7
TNT_SIDE = 8
TNT_TOP = 9
TNT_BOTTOM = 10
COBWEB = 11
ROSE = 12
FLOWER = 13
WATER_STATIC = 14
SAPLING = 15
STONEBRICK = 16
BEDROCK = 17
SAND = 18
GRAVEL = 19
LOG_SIDE = 20
LOG_TOP = 21
IRON = 22
GOLD = 23
EMERALD = 24
CHEST_ONE_TOP = 25
CHEST_ONE_SIDE = 26
CHEST_ONE_FRONT = 27
MUSHROOM_RED = 28
MUSHROOM_BROWN = 29
OBSIDIAN_CRYING = 30
FIRE1 = 31
ORE_GOLD = 32
ORE_IRON = 33
ORE_COAL = 34
BOOKSHELF = 35
MOSSY_STONE = 36
OBSIDIAN = 37
GRASS_SIDE_OVERLAY = 38
TALL_GRASS = 39
NONE40 = 40
CHEST_TWO_FRONT_LEFT = 41
CHEST_TWO_FRONT_RIGHT = 42
WORKBENCH_TOP = 43
FURNACE_FRONT = 44
FURNACE_SIDE = 45
DISPENSER_SIDE = 46
FIRE2 = 47
SPONGE = 48
GLASS = 49
ORE_EMERALD = 50
ORE_RED_STONE = 51
LEAVES_TRANSPARENT = 52
LEAVES_OPAQUE = 53
NONE54 = 54
DEAD_BUSH = 55
NONE56 = 56
CHEST_TWO_BACK_LEFT = 57
CHEST_TWO_BACK_RIGHT = 58
WORKBENCH_SIDE_1 = 59
WORKBENCH_SIDE_2 = 60
FURNACE_LIT = 61
FURNACE_TOP = 62
NONE63 = 63
CLOTH_64 = 64
SPAWNER = 65
SNOW = 66
ICE = 67
GRASS_SIDE_SNOW = 68
CACTUS_TOP = 69
CACTUS_SIDE = 70
CACTUS_BOTTOM = 71
CLAY = 72
REEDS = 73
JUKEBOX_SIDE = 74
JUKEBOX_TOP = 75
NONE76 = 76
NONE77 = 77
NONE78 = 78
NONE79 = 79
TORCH_LIT = 80
DOOR_TOP = 81
DOOR_IRON_TOP = 82
LADDER = 83
TRAPDOOR = 84
NONE85 = 85
FARMLAND = 86
FARMLAND_DRY = 87
WHEAT_0 = 88
WHEAT_1 = 89
WHEAT_2 = 90
WHEAT_3 = 91
WHEAT_4 = 92
WHEAT_5 = 93
WHEAT_6 = 94
WHEAT_7 = 95
LEVER = 96
DOOR_BOTTOM = 97
DOOR_IRON_BOTTOM = 98
TORCH_RED_STONE = 99
NONE100 = 100
NONE101 = 101
PUMPKIN_TOP = 102
BLOODSTONE = 103
SOULSAND = 104
GLOWSTONE = 105
STICKY_PISTON = 106
PISTON = 107
NONE108 = 108
NONE109 = 109
NONE110 = 110
NONE111 = 111
RAIL_CURVED = 112
CLOTH_112 = 113
CLOTH_113 = 114
TORCH_RED_STONE_OFF = 115
LOG_SPRUCE = 116
LOG_BIRCH = 117
PUMPKIN_SIDE = 118
PUMPKIN_FACE = 119
PUMPKIN_FACE_LIT = 120
CAKE_TOP = 121
CAKE_SIDE = 122
CAKE_SIDE_BIT = 123
CAKE_BOTTOM = 124
NONE125 = 125
NONE126 = 126
NONE127 = 127
RAIL = 128
LAPIS = 144
ORE_LAPIS = 160
POWERED_RAIL = 163
REDSTONE_DUST = 164
REDSTONE_DUST_LINE = 165
DETECTOR_RAIL = 195
SANDSTONE_TOP = 176
SANDSTONE_SIDE = 192
WATER = 205
SANDSTONE_BOTTOM = 208
LAVA = 237
INFO_UPDATEGAME1 = 252
INFO_UPDATEGAME2 = 253
LAVA_PLACEHOLDER = 255
# modded
YELLOW_COBBLE = 213
MARBLE_YELLOW = 214
@dataclass
class Tile:
block_id: BlockID
texture: Texture
tiles = [
Tile(BlockID.STONE, Texture.STONE),
Tile(BlockID.GRASS, Texture.GRASS_TOP), # fixme
Tile(BlockID.DIRT, Texture.DIRT),
Tile(BlockID.STONEBRICK, Texture.STONEBRICK),
Tile(BlockID.WOOD, Texture.PLANKS),
Tile(BlockID.BEDROCK, Texture.BEDROCK),
Tile(BlockID.WATER, Texture.WATER), # fixme
Tile(BlockID.WATER_CALM, Texture.WATER), # fixme
Tile(BlockID.LAVA, Texture.LAVA), # fixme
Tile(BlockID.LAVA_CALM, Texture.LAVA), # fixme
Tile(BlockID.SAND, Texture.SAND),
Tile(BlockID.GRAVEL, Texture.GRAVEL),
Tile(BlockID.ORE_GOLD, Texture.ORE_GOLD),
Tile(BlockID.ORE_IRON, Texture.ORE_IRON),
Tile(BlockID.ORE_COAL, Texture.ORE_COAL),
Tile(BlockID.TREE_TRUNK, Texture.LOG_SIDE), # fixme
Tile(BlockID.LEAVES, Texture.LEAVES_TRANSPARENT), # fixme
Tile(BlockID.GLASS, Texture.GLASS),
Tile(BlockID.ORE_LAPIS, Texture.ORE_LAPIS),
Tile(BlockID.BLOCK_LAPIS, Texture.LAPIS),
Tile(BlockID.SANDSTONE, Texture.SANDSTONE_SIDE),
Tile(BlockID.CLOTH, Texture.CLOTH_64), # wool, colored
Tile(BlockID.FLOWER, Texture.FLOWER),
Tile(BlockID.ROSE, Texture.ROSE),
Tile(BlockID.MUSHROOM_1, Texture.MUSHROOM_BROWN),
Tile(BlockID.MUSHROOM_2, Texture.MUSHROOM_RED),
Tile(BlockID.BLOCK_GOLD, Texture.GOLD),
Tile(BlockID.BLOCK_IRON, Texture.IRON),
Tile(BlockID.STONESLAB_FULL, Texture.STONE_SLAB_SIDE), # fixme
Tile(BlockID.STONESLAB_HALF, Texture.STONE_SLAB_SIDE), # fixme
Tile(BlockID.BRICKS, Texture.BRICKS),
Tile(BlockID.TNT, Texture.TNT_SIDE),
Tile(BlockID.BOOKSHELF, Texture.BOOKSHELF),
Tile(BlockID.MOSS_STONE, Texture.MOSSY_STONE),
Tile(BlockID.OBSIDIAN, Texture.OBSIDIAN),
Tile(BlockID.TORCH, Texture.TORCH_LIT),
Tile(BlockID.STAIRS_WOOD, Texture.PLANKS), # fixme
Tile(BlockID.WIRE, Texture.REDSTONE_DUST),
Tile(BlockID.ORE_EMERALD, Texture.ORE_EMERALD),
Tile(BlockID.BLOCK_EMERALD, Texture.EMERALD),
Tile(BlockID.FARMLAND, Texture.FARMLAND), # fixme
Tile(BlockID.DOOR_WOOD, Texture.DOOR_TOP), # fixme
Tile(BlockID.LADDER, Texture.LADDER),
Tile(BlockID.STAIRS_STONE, Texture.STONEBRICK),
Tile(BlockID.DOOR_IRON, Texture.DOOR_IRON_TOP), # fixme
Tile(BlockID.ORE_REDSTONE, Texture.ORE_RED_STONE), # fixme
Tile(BlockID.ORE_REDSTONE_LIT, Texture.ORE_RED_STONE), # fixme
Tile(BlockID.TOPSNOW, Texture.SNOW),
Tile(BlockID.ICE, Texture.ICE),
Tile(BlockID.SNOW, Texture.SNOW),
Tile(BlockID.CACTUS, Texture.CACTUS_SIDE),
Tile(BlockID.CLAY, Texture.CLAY),
Tile(BlockID.REEDS, Texture.REEDS),
Tile(BlockID.FENCE, Texture.PLANKS),
Tile(BlockID.INVISIBLE, Texture.STONE),
Tile(BlockID.WOOD, Texture.PLANKS),
#Tile(BlockID.LEAVES_CARRIED, Texture.LEAVES_TRANSPARENT), # fixme
Tile(BlockID.FIRE, Texture.FIRE1),
Tile(BlockID.SAPLING, Texture.SAPLING),
Tile(BlockID.SPONGE, Texture.SPONGE),
Tile(BlockID.TALL_GRASS, Texture.TALL_GRASS),
Tile(BlockID.DEAD_BUSH, Texture.DEAD_BUSH),
Tile(BlockID.PUMPKIN, Texture.PUMPKIN_FACE), # fixme
Tile(BlockID.PUMPKIN_LIT, Texture.PUMPKIN_FACE_LIT), # fixme
Tile(BlockID.NETHERRACK, Texture.BLOODSTONE),
Tile(BlockID.SOUL_SAND, Texture.SOULSAND),
Tile(BlockID.GLOWSTONE, Texture.GLOWSTONE),
Tile(BlockID.COBWEB, Texture.COBWEB),
Tile(BlockID.WORKBENCH, Texture.WORKBENCH_TOP), # fixme
Tile(BlockID.WHEAT, Texture.WHEAT_0),
# modded blocks
Tile(BlockID.YELLOW_COBBLE, Texture.YELLOW_COBBLE),
Tile(BlockID.MARBLE_YELLOW, Texture.MARBLE_YELLOW),
]
tiles_by_id = {
tile.block_id: tile for tile in tiles
}

48
minecraft/gen/fence.obj Normal file
View File

@ -0,0 +1,48 @@
# Blender 5.0.0
# www.blender.org
o Cube
v 0.250000 1.000000 -0.250000
v 0.250000 -1.000000 -0.250000
v 0.250000 1.000000 0.250000
v 0.250000 -1.000000 0.250000
v -0.250000 1.000000 -0.250000
v -0.250000 -1.000000 -0.250000
v -0.250000 1.000000 0.250000
v -0.250000 -1.000000 0.250000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 0.250000 1.250000
vt 0.500000 1.000000
vt 0.500000 1.250000
vt 0.250050 0.000100
vt 0.500000 0.999900
vt 0.250050 0.999900
vt 0.500000 0.000100
vt 0.749950 0.999900
vt 0.250000 0.000000
vt 0.500000 -0.250000
vt 0.500000 0.000000
vt 0.000100 0.000100
vt 0.000100 0.999900
vt 0.749950 0.000100
vt 0.999900 0.999900
vt 0.250000 1.000000
vt 0.250000 -0.250000
vt 0.999900 0.000100
s 0
f 5/1/1 3/2/1 1/3/1
f 3/4/2 8/5/2 4/6/2
f 7/7/3 6/8/3 8/5/3
f 2/9/4 8/10/4 6/11/4
f 1/12/5 4/6/5 2/13/5
f 5/14/6 2/15/6 6/8/6
f 5/1/1 7/16/1 3/2/1
f 3/4/2 7/7/2 8/5/2
f 7/7/3 5/14/3 6/8/3
f 2/9/4 4/17/4 8/10/4
f 1/12/5 3/4/5 4/6/5
f 5/14/6 1/18/6 2/15/6

View File

@ -3,10 +3,15 @@ import struct
from pprint import pprint from pprint import pprint
from itertools import chain from itertools import chain
from collections import defaultdict from collections import defaultdict
import functools
import os
import mcregion import mcregion
import vec3 import vec3
import vertex_buffer import vertex_buffer
import data
FAST = "FAST" in os.environ
def wrap_n(nc, chunk_c): def wrap_n(nc, chunk_c):
if nc < 0: if nc < 0:
@ -17,48 +22,79 @@ def wrap_n(nc, chunk_c):
chunk_c = chunk_c + 1 chunk_c = chunk_c + 1
return nc, chunk_c return nc, chunk_c
def block_neighbors(level_table, chunk_x, chunk_z, block_index): custom_blocks = [
block_id = level_table[(chunk_x, chunk_z)].blocks[block_index] { # "tallgrass" model
if block_id == 0: data.BlockID.TALL_GRASS,
return data.BlockID.MUSHROOM_1,
data.BlockID.MUSHROOM_2,
data.BlockID.FLOWER,
data.BlockID.ROSE,
data.BlockID.SAPLING,
},
{ # "fence" model
data.BlockID.FENCE,
},
{ # "torch" model
data.BlockID.TORCH,
},
{ # "wheat" model
data.BlockID.WHEAT,
},
]
def neighbor_exists(nx, ny, nz): non_solid_blocks = set(chain.from_iterable(custom_blocks))
hack_non_solid_blocks = set([
data.BlockID.LADDER,
data.BlockID.WIRE,
])
def neighbor_exists(level_table, chunk_x, chunk_z, nx, ny, nz):
if ny > 127 or ny < 0: if ny > 127 or ny < 0:
return False return False
nx, n_chunk_x = wrap_n(nx, chunk_x) nx, n_chunk_x = wrap_n(nx, chunk_x)
nz, n_chunk_z = wrap_n(nz, chunk_z) nz, n_chunk_z = wrap_n(nz, chunk_z)
if nx > 15 or nx < 0: assert nx <= 15 and nx >= 0
return True assert nz <= 15 and nz >= 0
if nz > 15 or nz < 0:
return True
n_block_index = mcregion.block_index_from_xyz(nx, ny, nz)
key = (n_chunk_x, n_chunk_z) key = (n_chunk_x, n_chunk_z)
if key not in level_table: if key not in level_table:
return True return True
n_block_index = mcregion.block_index_from_xyz(nx, ny, nz)
n_block_id = level_table[key].blocks[n_block_index] n_block_id = level_table[key].blocks[n_block_index]
return n_block_id != 0
x, y, z = mcregion.xyz_from_block_index(block_index) has_neighbor = (n_block_id != data.BlockID.AIR) and (n_block_id not in non_solid_blocks) and (n_block_id not in hack_non_solid_blocks)
return has_neighbor
center_position = vec3.add((x, y, z), (chunk_x * 16, 0, chunk_z * 16)) 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 == data.BlockID.AIR:
return
block_data = level_table[(chunk_x, chunk_z)].data[block_index // 2]
block_data = (block_data >> (1 - (block_index % 2)) * 4) & 0xf
xyz = mcregion.xyz_from_block_index(block_index)
center_position = vec3.add(xyz, (chunk_x * 16, 0, chunk_z * 16))
def find_non_neighbors(): def find_non_neighbors():
for i, normal in enumerate(vertex_buffer.normals): for i, normal in enumerate(vertex_buffer.normals):
neighbor = vec3.add(normal, (x, y, z)) neighbor = vec3.add(normal, xyz)
if not neighbor_exists(*neighbor): if not neighbor_exists(level_table, chunk_x, chunk_z, *neighbor):
yield i yield i
normal_indices = list(find_non_neighbors()) normal_indices = list(find_non_neighbors())
if normal_indices: if block_id in non_solid_blocks or block_id in hack_non_solid_blocks or normal_indices:
yield center_position, block_id, normal_indices yield center_position, block_id, block_data, normal_indices
def devoxelize_region(level_table): def devoxelize_region(level_table, level_table_keys):
for chunk_x, chunk_z in level_table.keys(): for chunk_x, chunk_z in level_table_keys:
for block_index in range(128 * 16 * 16): for block_index in range(128 * 16 * 16):
yield from block_neighbors(level_table, chunk_x, chunk_z, block_index) yield from block_neighbors(level_table, chunk_x, chunk_z, block_index)
if FAST:
return
def build_level_table(mem, locations): def build_level_table(level_table, mem, locations):
level_table = {}
for location in locations: for location in locations:
try: try:
level = mcregion.parse_location(mem, location) level = mcregion.parse_location(mem, location)
@ -82,38 +118,84 @@ def build_block_configuration_table():
indices.extend(vertex_buffer.faces_by_normal[vertex_buffer.normals[j]]) indices.extend(vertex_buffer.faces_by_normal[vertex_buffer.normals[j]])
yield indices yield indices
def pack_instance_data(position, block_id, block_data):
packed = struct.pack("<hhhBB",
position[0], position[1], position[2],
block_id,
block_data)
return packed
def pack_light_data(position, block_id):
packed = struct.pack("<ffff", position[0], position[1], position[2], block_id)
return packed
def build_block_instances(blocks): def build_block_instances(blocks):
by_configuration = defaultdict(list) by_configuration = defaultdict(list)
for position, block_id, normal_indices in blocks: deferred_blocks = defaultdict(list)
light_sources = []
def is_deferred_block(position, block_id, block_data):
for i, custom_block_types in enumerate(custom_blocks):
if block_id in custom_block_types:
deferred_blocks[i].append((position, block_id, block_data))
return True
return False
for position, block_id, block_data, normal_indices in blocks:
if block_id == data.BlockID.TORCH:
light_sources.append((position, block_id))
if is_deferred_block(position, block_id, block_data):
assert block_id in non_solid_blocks
continue
configuration = normal_indices_as_block_configuration(normal_indices) configuration = normal_indices_as_block_configuration(normal_indices)
#print(position, block_id, block_configuration) by_configuration[configuration].append((position, block_id, block_data))
by_configuration[configuration].append((position, block_id))
offset = 0 offset = 0
configuration_instance_count_offset = [] configuration_instance_count_offset = []
with open(f"{data_path}.instance.vtx", "wb") as f: with open(f"{data_path}.instance.vtx", "wb") as f:
######################################################################
# cubes
######################################################################
for configuration in range(64): for configuration in range(64):
if configuration not in by_configuration: if configuration not in by_configuration:
configuration_instance_count_offset.append((0, 0)) configuration_instance_count_offset.append((0, 0))
continue continue
blocks = by_configuration[configuration] _blocks = by_configuration[configuration]
configuration_instance_count_offset.append((len(blocks), offset)) configuration_instance_count_offset.append((len(_blocks), offset))
for position, block_id in blocks: for position, block_id, block_data in _blocks:
packed = struct.pack("<hhhBB", assert block_id not in non_solid_blocks, block_id
position[0], position[1], position[2], packed = pack_instance_data(position, block_id, block_data)
block_id,
0)
f.write(packed) f.write(packed)
offset += len(packed) offset += len(packed)
######################################################################
# non-cubes
######################################################################
for custom_block_ix in range(len(custom_blocks)):
nc_offset = offset
nc_instance_count = 0
for position, block_id, block_data in deferred_blocks[custom_block_ix]:
assert block_id in non_solid_blocks, block_id
packed = pack_instance_data(position, block_id, block_data)
f.write(packed)
offset += len(packed)
nc_instance_count += 1
configuration_instance_count_offset.append((nc_instance_count, nc_offset))
with open(f"{data_path}.instance.cfg", "wb") as f: with open(f"{data_path}.instance.cfg", "wb") as f:
for instance_count, offset in configuration_instance_count_offset: for instance_count, offset in configuration_instance_count_offset:
print(instance_count, offset) #print(instance_count, offset)
f.write(struct.pack("<ii", instance_count, offset)) f.write(struct.pack("<ii", instance_count, offset))
def main(mcr_path, data_path): with open(f"{data_path}.lights.vtx", "wb") as f:
with open(mcr_path, "rb") as f: for position, block_id in light_sources:
packed = pack_light_data(position, block_id)
f.write(packed)
def level_table_from_path(level_table, path):
with open(path, "rb") as f:
buf = f.read() buf = f.read()
mem = memoryview(buf) mem = memoryview(buf)
@ -122,13 +204,34 @@ def main(mcr_path, data_path):
offset, timestamps = mcregion.parse_timestamps(mem, offset) offset, timestamps = mcregion.parse_timestamps(mem, offset)
assert offset == 0x2000 assert offset == 0x2000
level_table = build_level_table(mem, locations) build_level_table(level_table, mem, locations)
blocks = devoxelize_region(level_table)
all_paths = [
"/home/bilbo/Love2DWorld/region/r.0.0.mcr",
"/home/bilbo/Love2DWorld/region/r.-1.-1.mcr",
"/home/bilbo/Love2DWorld/region/r.0.-1.mcr",
"/home/bilbo/Love2DWorld/region/r.-1.0.mcr",
]
def main2(level_table, level_table_keys):
blocks = devoxelize_region(level_table, level_table_keys)
build_block_instances(blocks) build_block_instances(blocks)
#pprint(list(build_block_configuration_table())) def main(mcr_path, data_path):
assert mcr_path in all_paths
level_table = {}
level_table_from_path(level_table, mcr_path)
level_table_keys = list(level_table.keys())
for path in all_paths:
if path == mcr_path:
continue
level_table_from_path(level_table, path)
main2(level_table, level_table_keys)
#import cProfile
#cProfile.runctx("main2(level_table, level_table_keys)", {},
# {"level_table_keys": level_table_keys, "level_table": level_table, "main2": main2})
mcr_path = sys.argv[1] mcr_path = sys.argv[1]
data_path = sys.argv[2] data_path = sys.argv[2]
main(mcr_path, data_path) main(mcr_path, data_path)

11
minecraft/gen/mc.sh Normal file
View File

@ -0,0 +1,11 @@
set -eux
cd ./minecraft/gen
PYTHON=pypy3.11
$PYTHON mc.py ~/Love2DWorld/region/r.0.0.mcr ../region.0.0 &
$PYTHON mc.py ~/Love2DWorld/region/r.-1.-1.mcr ../region.-1.-1 &
$PYTHON mc.py ~/Love2DWorld/region/r.0.-1.mcr ../region.0.-1 &
$PYTHON mc.py ~/Love2DWorld/region/r.-1.0.mcr ../region.-1.0 &
wait
cat ../region*.lights.vtx > ../global.lights.vtx

View File

@ -217,15 +217,30 @@ def parse_location(mem, location):
level = level_from_tag(tag) level = level_from_tag(tag)
return level return level
def xyz_from_block_index(block_index): def _xyz_from_block_index(block_index):
assert block_index >= 0 and block_index < (128 * 16 * 16) assert block_index >= 0 and block_index < (128 * 16 * 16)
x = int(block_index / (128 * 16)) x = block_index // (128 * 16)
y = int(block_index % 128) y = block_index % 128
z = int(int(block_index / 128) % 16) z = (block_index // 128) % 16
return x, y, z return x, y, z
def block_index_from_xyz(x, y, z): def _block_index_from_xyz(x, y, z):
assert x >= 0 and x < 16 assert x >= 0 and x < 16
assert y >= 0 and y < 128 assert y >= 0 and y < 128
assert z >= 0 and z < 16 assert z >= 0 and z < 16
return int(y + z * 128 + x * 128 * 16) return int(y + z * 128 + x * 128 * 16)
xyz_to_block_index = {}
block_index_to_xyz = {}
for i in range(128 * 16 * 16):
xyz = _xyz_from_block_index(i)
assert _block_index_from_xyz(*xyz) == i
xyz_to_block_index[xyz] = i
block_index_to_xyz[i] = xyz
def xyz_from_block_index(block_index):
return block_index_to_xyz[block_index]
def block_index_from_xyz(x, y, z):
return xyz_to_block_index[(x, y, z)]

View File

@ -0,0 +1,150 @@
import struct
from collections import defaultdict
import sys
import data
files = sys.argv[1:]
by_block_id = defaultdict(list)
no_tile = defaultdict(list)
block_ids = {
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115,
}
for filename in files:
with open(filename, 'rb') as f:
buf = f.read()
for i in range(len(buf) // 8):
x, y, z, block_id, block_data = struct.unpack("<hhhBB", buf[i*8:i*8+8])
if block_id == 35:
print(x, y, z, block_data)
if block_id not in block_ids and block_id not in data.tiles_by_id:
by_block_id[block_id].append((x, y, z, block_data))
elif block_id not in data.tiles_by_id:
no_tile[block_id].append((x, y, z, block_data))
print("no block id:")
for key in sorted(by_block_id.keys()):
print(" ", key, len(by_block_id[key]), by_block_id[key][0])
print("no tile:")
for key in sorted(no_tile.keys()):
print(" ", key, len(no_tile[key]), no_tile[key][0])

80
minecraft/gen/obj.py Normal file
View File

@ -0,0 +1,80 @@
from dataclasses import dataclass
def ignore(state, line):
pass
def normalize_float(s):
f = float(s)
if f == -0.0:
return 0.0
return f
def parse_float_components(s, length):
components = s.split()
assert len(components) == length
return tuple(map(normalize_float, components))
def parse_position(state, line):
position = parse_float_components(line, 3)
state.position.append(position)
def parse_normal(state, line):
normal = parse_float_components(line, 3)
state.normal.append(normal)
def parse_texture(state, line):
texture = parse_float_components(line, 2)
state.texture.append(texture)
def parse_ptn(s):
ptn = tuple(int(i) - 1 for i in s.split("/"))
assert all(c >= 0 for c in ptn), ptn
assert len(ptn) == 3, ptn
return ptn
def parse_triangle(state, line):
indices = line.split()
assert len(indices) == 3, line
triangle = tuple(map(parse_ptn, indices))
state.triangle.append(triangle)
prefixes = [
("#", ignore),
("o ", ignore),
("s ", ignore),
("v ", parse_position),
("vn ", parse_normal),
("vt ", parse_texture),
("f ", parse_triangle),
]
@dataclass
class ObjState:
position: list
normal: list
texture: list
triangle: list
def __init__(self):
self.position = []
self.normal = []
self.texture = []
self.triangle = []
def parse_obj(s):
state = ObjState()
lines = s.strip().split("\n")
for line in lines:
for prefix, func in prefixes:
if line.startswith(prefix):
line = line.removeprefix(prefix)
func(state, line)
break
else:
assert False, line
return state
def parse_obj_from_filename(filename):
with open(filename, "r") as f:
buf = f.read()
return parse_obj(buf)

View File

@ -0,0 +1,25 @@
from collections import defaultdict
import obj
def append_triangles(state, vertex_buffer, index_buffer, index_lookup):
for triangle in state.triangle:
for p_ix, t_ix, n_ix in triangle:
key = (p_ix, n_ix, t_ix)
if key not in index_lookup:
position = state.position[p_ix]
normal = state.normal[n_ix]
texture = state.texture[t_ix]
index = len(vertex_buffer)
index_lookup[key] = index
vertex_buffer.append((position, normal, texture))
index_buffer.append(index_lookup[key])
def build_faces_by_normal(vertex_buffer, index_buffer):
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, s
normal, = iter(s)
by_normal[normal].extend(tri)
return by_normal

View File

@ -0,0 +1,24 @@
# Blender 5.0.0
# www.blender.org
o TallGrass
v 0.800000 0.600000 0.800000
v 0.800000 -1.000000 0.800000
v -0.800000 -1.000000 0.800000
v -0.800000 0.600000 0.800000
v -0.800000 0.600000 -0.800000
v -0.800000 -1.000000 -0.800000
v 0.800000 -1.000000 -0.800000
v 0.800000 0.600000 -0.800000
vn 0.7071 -0.0000 0.7071
vn 0.7071 -0.0000 -0.7071
vt 1.000000 1.000000
vt 0.000000 -0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt -0.000000 1.000000
vt 1.000000 0.000000
s 0
f 3/1/1 8/2/1 4/3/1
f 5/2/2 2/4/2 6/5/2
f 3/1/1 7/5/1 8/2/1
f 5/2/2 1/6/2 2/4/2

40
minecraft/gen/torch.obj Normal file
View File

@ -0,0 +1,40 @@
# Blender 5.0.0
# www.blender.org
o Cube
v 0.125000 0.250000 -0.125000
v 0.125000 -1.000000 -0.125000
v 0.125000 0.250000 0.125000
v 0.125000 -1.000000 0.125000
v -0.125000 0.250000 -0.125000
v -0.125000 -1.000000 -0.125000
v -0.125000 0.250000 0.125000
v -0.125000 -1.000000 0.125000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 0.531250 0.406250
vt 0.468750 0.468750
vt 0.468750 0.406250
vt 0.437500 0.375000
vt 0.562500 1.000000
vt 0.437500 1.000000
vt 0.562500 0.875000
vt 0.437500 0.875000
vt 0.531250 0.468750
vt 0.562500 0.375000
s 0
f 5/1/1 3/2/1 1/3/1
f 3/4/2 8/5/2 4/6/2
f 7/4/3 6/5/3 8/6/3
f 2/7/4 8/6/4 6/8/4
f 1/4/5 4/5/5 2/6/5
f 5/4/6 2/5/6 6/6/6
f 5/1/1 7/9/1 3/2/1
f 3/4/2 7/10/2 8/5/2
f 7/4/3 5/10/3 6/5/3
f 2/7/4 4/5/4 8/6/4
f 1/4/5 3/10/5 4/5/5
f 5/4/6 1/10/6 2/5/6

View File

@ -1,41 +1,8 @@
import struct import struct
import vec3 import vec3
import obj
vertex_table = [ import obj_state
((-1.0, 1.0, -1.0), (0.0, 1.0, 0.0), (1.0, 0.0)), import sys
((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]
}
normals = [ normals = [
(-1.0, 0.0, 0.0), (-1.0, 0.0, 0.0),
@ -46,7 +13,9 @@ normals = [
(1.0, 0.0, 0.0), (1.0, 0.0, 0.0),
] ]
def build_configuration_index_buffers(f): def build_configuration_index_buffers(f, faces_by_normal, index_buffer):
assert(set(normals) == set(faces_by_normal.keys()))
offset = 0 offset = 0
configuration_offsets = [] configuration_offsets = []
for configuration in range(64): for configuration in range(64):
@ -58,6 +27,7 @@ def build_configuration_index_buffers(f):
indices = faces_by_normal[normal] indices = faces_by_normal[normal]
for index in indices: for index in indices:
f.write(struct.pack("<B", index)) f.write(struct.pack("<B", index))
index_buffer.append(index)
offset += 1 offset += 1
for i, offset in enumerate(configuration_offsets): for i, offset in enumerate(configuration_offsets):
@ -65,14 +35,49 @@ def build_configuration_index_buffers(f):
if i % 8 == 7: if i % 8 == 7:
print() print()
def build_vertex_buffer(f): def build_vertex_buffer(f, vertex_buffer):
for position, normal, texture in vertex_table: for position, normal, texture in vertex_buffer:
position = vec3.mul(position, 0.5) position = vec3.mul(position, 0.5)
f.write(struct.pack("<eeeeeeee", *position, *normal, *texture)) f.write(struct.pack("<eeeeeeee", *position, *normal, *texture))
if __name__ == "__main__": def write_indices(f, index_buffer, start, count):
with open("configuration.idx", "wb") as f: for i in range(count):
build_configuration_index_buffers(f) f.write(struct.pack("<B", index_buffer[start + i]))
with open("per_vertex.vtx", "wb") as f: def write_custom_obj(f, vertex_buffer, index_buffer, index_lookup, path):
build_vertex_buffer(f) index_start = len(index_buffer)
state = obj.parse_obj_from_filename(path)
obj_state.append_triangles(state, vertex_buffer, index_buffer, index_lookup)
index_count = len(index_buffer) - index_start
write_indices(f, index_buffer, index_start, index_count)
print(f"{index_start}, {index_count}, // {path}")
def main():
cube_index_buffer = []
cube_index_lookup = {}
vertex_buffer = []
index_buffer = []
index_lookup = {}
cube_state = obj.parse_obj_from_filename("cube.obj")
obj_state.append_triangles(cube_state, vertex_buffer, cube_index_buffer, cube_index_lookup)
cube_faces_by_normal = obj_state.build_faces_by_normal(vertex_buffer, cube_index_buffer)
with open("../configuration.idx", "wb") as f:
build_configuration_index_buffers(f, cube_faces_by_normal, index_buffer)
index_lookup = {}
write_custom_obj(f, vertex_buffer, index_buffer, index_lookup, "tallgrass.obj")
index_lookup = {}
write_custom_obj(f, vertex_buffer, index_buffer, index_lookup, "fence.obj")
index_lookup = {}
write_custom_obj(f, vertex_buffer, index_buffer, index_lookup, "torch.obj")
index_lookup = {}
write_custom_obj(f, vertex_buffer, index_buffer, index_lookup, "wheat.obj")
with open("../per_vertex.vtx", "wb") as f:
build_vertex_buffer(f, vertex_buffer)
if __name__ == "__main__":
main()

36
minecraft/gen/wheat.obj Normal file
View File

@ -0,0 +1,36 @@
# Blender 5.0.0
# www.blender.org
o Cube
v -0.500000 -1.000000 -1.000000
v -0.500000 1.000000 -1.000000
v 1.000000 -1.000000 0.500000
v 1.000000 1.000000 0.500000
v 0.500000 1.000000 -1.000000
v 0.500000 -1.000000 -1.000000
v 0.500000 1.000000 1.000000
v 0.500000 -1.000000 1.000000
v 1.000000 1.000000 -0.500000
v 1.000000 -1.000000 -0.500000
v -1.000000 1.000000 -0.500000
v -1.000000 -1.000000 -0.500000
v -0.500000 1.000000 1.000000
v -0.500000 -1.000000 1.000000
v -1.000000 1.000000 0.500000
v -1.000000 -1.000000 0.500000
vn -0.0000 -0.0000 1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn -1.0000 -0.0000 -0.0000
vt 0.000000 -0.000000
vt 1.000000 1.000000
vt -0.000000 1.000000
vt 1.000000 0.000000
s 0
f 4/1/1 16/2/1 3/3/1
f 5/1/2 8/2/2 6/3/2
f 11/1/3 10/2/3 12/3/3
f 13/1/4 1/2/4 14/3/4
f 4/1/1 15/4/1 16/2/1
f 5/1/2 7/4/2 8/2/2
f 11/1/3 9/4/3 10/2/3
f 13/1/4 2/4/4 1/2/4

BIN
minecraft/global.lights.vtx Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 65 KiB

18
shader/font.frag Normal file
View File

@ -0,0 +1,18 @@
#version 330 core
uniform sampler2D TextureSampler;
uniform vec2 Cell;
uniform vec2 Glyph;
out vec4 g_color;
in vec4 PixelTexture;
void main()
{
vec4 sample = texture(TextureSampler, PixelTexture.xy * Cell + Cell * Glyph);
float px = sample.x == 0.0 ? 0.0 : 1.0;
g_color = vec4(vec3(px), 1.0);
}

39
shader/font.vert Normal file
View File

@ -0,0 +1,39 @@
#version 330 core
const vec2 vtx[4] = vec2[](vec2(-1.0, 1.0), // tl
vec2( 1.0, 1.0), // tr
vec2( 1.0, -1.0), // br
vec2(-1.0, -1.0)); // bl
/*
tl tr
br
0 1 2
tr tl br
1 0 2
tl
bl br
2 1 3
br tl bl
2 0 3
1 0 2 3
*/
uniform mat4 Transform;
out vec4 PixelTexture;
void main()
{
vec2 vertex = vtx[gl_VertexID];
PixelTexture = vec4(vertex * vec2(0.5, -0.5) + 0.5, 0, 0);
vertex = vertex * vec2(0.5, 0.5) + vec2(0.5, -0.5);
gl_Position = Transform * vec4(vertex, 0.0, 1.0);
}

40
shader/lighting.frag Normal file
View File

@ -0,0 +1,40 @@
#version 330 core
uniform sampler2D PositionSampler;
uniform sampler2D NormalSampler;
uniform sampler2D ColorSampler;
uniform float Linear;
uniform float Quadratic;
uniform vec3 Eye;
layout (location = 0) out vec4 Color;
in vec4 PixelTexture;
layout (std140) uniform Lights
{
vec4 light[256];
};
void main()
{
vec4 position = texture(PositionSampler, PixelTexture.xy);
vec4 normal = texture(NormalSampler, PixelTexture.xy);
vec4 color = texture(ColorSampler, PixelTexture.xy);
vec3 out_color = color.xyz * 0.1;
for (int i = 0; i < 82; i++) {
vec3 light_position = light[i].xzy;
float light_distance = length(light_position - position.xyz);
vec3 light_direction = normalize(light_position - position.xyz);
float diffuse = max(dot(normal.xyz, light_direction), 0.0);
//float attenuation = 1.0 / (1.0 + Linear * light_distance + Quadratic * light_distance * light_distance);
float attenuation = 1.0 / (1.0 + Quadratic * light_distance * light_distance);
out_color += color.xyz * attenuation * diffuse;
//out_color = vec3(diffuse);
}
Color = vec4(out_color, 1.0);
}

13
shader/quad.frag Normal file
View File

@ -0,0 +1,13 @@
#version 330 core
uniform sampler2D TextureSampler;
layout (location = 0) out vec4 Color;
in vec4 PixelTexture;
void main()
{
vec4 sample = texture(TextureSampler, PixelTexture.xy);
Color = vec4(sample.xyz, 1.0);
}

17
shader/quad.vert Normal file
View File

@ -0,0 +1,17 @@
#version 330 core
const vec2 vtx[4] = vec2[](vec2(-1.0, 1.0), // tl
vec2( 1.0, 1.0), // tr
vec2( 1.0, -1.0), // br
vec2(-1.0, -1.0)); // bl
out vec4 PixelTexture;
void main()
{
vec2 vertex = vtx[gl_VertexID];
PixelTexture = vec4(vertex * 0.5 + 0.5, 0, 0);
gl_Position = vec4(vertex, 0.0, 1.0);
}

View File

@ -1,40 +1,50 @@
#version 330 core #version 330 core
in VS_OUT { in VS_OUT {
vec3 Position; // world coordinates
vec3 Normal; vec3 Normal;
vec2 Texture; vec2 Texture;
flat int BlockID; flat int BlockID;
flat int Data;
} fs_in; } fs_in;
out vec4 FragColor; layout (location = 0) out vec3 Position;
layout (location = 1) out vec3 Normal;
layout (location = 2) out vec3 Color;
uniform sampler2D TerrainSampler; uniform sampler2D TerrainSampler;
int Textures[256] = int[256]( layout (std140) uniform TextureID
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, ivec4 block_id_to_texture_id[256 / 4];
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, int wool[16] = int[16]( 64, // 0 64
66, 70, 72, 73, 74, 4, 102, 103, 104, 105, 14, 102, 185, 185, 185, 185, 210, // 32 208
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 194, // 32 192
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 178, // 32 176
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 162, // 32 160
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 146, // 32 144
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 130, // 32 128
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 114, // 32 112
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 225, // 16 224
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 209, // 16 208
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 193, // 16 192
185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185 177, // 16 176
); 161, // 16 160
145, // 16 144
129, // 16 128
113 // 16 112
);
void main() void main()
{ {
vec3 light_direction = normalize(vec3(-1, -0.5, 0.5)); int terrain_ix;
float diffuse_intensity = max(dot(normalize(fs_in.Normal), light_direction), 0.0); if (fs_in.BlockID == 35) // cloth
terrain_ix = wool[fs_in.Data];
else
terrain_ix = block_id_to_texture_id[fs_in.BlockID / 4][fs_in.BlockID % 4];
int terrain_ix = int(Textures[int(fs_in.BlockID)]);
int terrain_x = terrain_ix % 16; int terrain_x = terrain_ix % 16;
int terrain_y = terrain_ix / 16; int terrain_y = terrain_ix / 16;
ivec2 coord = ivec2(terrain_x, terrain_y) * 16; ivec2 coord = ivec2(terrain_x, terrain_y) * 16;
@ -46,11 +56,10 @@ void main()
return; return;
} }
if (int(fs_in.BlockID) == 18) // leaves if (fs_in.BlockID == 18 || fs_in.BlockID == 31) // leaves
texture_color.xyz *= vec3(0.125, 0.494, 0.027); texture_color.xyz *= vec3(0.125, 0.494, 0.027);
if (diffuse_intensity < 0.1) Position = fs_in.Position.xzy;
diffuse_intensity = 0.1; Normal = normalize(fs_in.Normal.xzy);
Color = texture_color.xyz;
FragColor = vec4(texture_color.xyz * vec3(diffuse_intensity), 1.0);
} }

View File

@ -7,20 +7,27 @@ in vec2 Texture;
// per-instance: // per-instance:
in vec3 BlockPosition; in vec3 BlockPosition;
in float BlockID; in float BlockID;
in float Data;
out VS_OUT { out VS_OUT {
vec3 Position;
vec3 Normal; vec3 Normal;
vec2 Texture; vec2 Texture;
flat int BlockID; flat int BlockID;
flat int Data;
} vs_out; } vs_out;
uniform mat4 Transform; uniform mat4 Transform;
void main() void main()
{ {
vs_out.Normal = Normal.xzy; vec3 position = Position + BlockPosition; // world coordinates
vs_out.Position = position;
vs_out.Normal = Normal;
vs_out.Texture = Texture; vs_out.Texture = Texture;
vs_out.BlockID = int(BlockID); vs_out.BlockID = int(BlockID);
vs_out.Data = int(Data);
gl_Position = Transform * vec4((Position + BlockPosition).xzy, 1.0); gl_Position = Transform * vec4(position.xzy, 1.0);
} }

167
src/font.cpp Normal file
View File

@ -0,0 +1,167 @@
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "directxmath/directxmath.h"
#include "glad/gl.h"
#include "opengl.h"
#include "font.h"
#include "window.h"
namespace font {
struct location {
struct {
unsigned int transform;
unsigned int texture_sampler;
unsigned int cell;
unsigned int glyph;
} uniform;
};
static location location;
static unsigned int font_program = -1;
void load_shader()
{
unsigned int program = compile_from_files("shader/font.vert",
NULL, // geom
"shader/font.frag");
location.uniform.transform = glGetUniformLocation(program, "Transform");
location.uniform.texture_sampler = glGetUniformLocation(program, "TextureSampler");
location.uniform.cell = glGetUniformLocation(program, "Cell");
location.uniform.glyph = glGetUniformLocation(program, "Glyph");
printf("font program:\n");
printf(" uniforms:\n transform %u\n texture_sampler %u\n cell %u\n glyph %u\n",
location.uniform.transform,
location.uniform.texture_sampler,
location.uniform.cell,
location.uniform.glyph
);
font_program = program;
}
font load_font(font_desc const& desc)
{
unsigned int texture;
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(desc.path, &texture_data_size);
assert(texture_data != nullptr);
int width = desc.texture_width;
int height = desc.texture_height;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, texture_data);
free(texture_data);
glBindTexture(GL_TEXTURE_2D, 0);
return (font){
.desc = &desc,
.texture = texture,
.stride = desc.texture_width / desc.glyph_width,
.cell = { (float)desc.glyph_width / (float)desc.texture_width,
(float)desc.glyph_height / (float)desc.texture_height },
};
}
static inline int min(int a, int b)
{
return a < b ? a : b;
}
int best_font(font_desc const * const descs, int length)
{
int dimension = min(g_window_width, g_window_height);
int ideal_height = (16 * dimension) / 1024;
//printf("ideal_height: %d\n", ideal_height);
int nearest = dimension;
int nearest_ix = -1;
for (int i = 0; i < length; i++) {
font_desc const& desc = descs[i];
int distance = abs(desc.glyph_height - ideal_height);
if (distance < nearest) {
nearest = distance;
nearest_ix = i;
}
}
assert(nearest_ix != -1);
//printf("selected %d\n", descs[nearest_ix].glyph_height);
return nearest_ix;
}
void load_fonts(font * const fonts, font_desc const * const descs, int length)
{
for (int i = 0; i < length; i++) {
fonts[i] = load_font(descs[i]);
}
}
inline static XMFLOAT2 glyph_coordinate(font const& font, int ord)
{
int c = ord - 32;
int x = c % font.stride;
int y = c / font.stride;
XMVECTOR coord = XMVectorSet(x, y, 0, 0);
XMFLOAT2 coordf;
XMStoreFloat2(&coordf, coord);
return coordf;
}
inline static XMFLOAT4X4 glyph_transform(font const& font, int x, int y)
{
XMMATRIX transform =
XMMatrixScaling(font.desc->glyph_width, font.desc->glyph_height, 0)
* XMMatrixTranslation(x, -y, 0)
* XMMatrixScaling(2.0f / g_window_width, 2.0f / g_window_height, 0)
* XMMatrixTranslation(-1, 1, 0);
XMFLOAT4X4 transformf;
XMStoreFloat4x4(&transformf, transform);
return transformf;
}
void draw_start(font const& font, unsigned int vertex_array_object, unsigned int index_buffer)
{
glUseProgram(font_program);
glDepthFunc(GL_ALWAYS);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font.texture);
glUniform1i(location.uniform.texture_sampler, 0);
glUniform2fv(location.uniform.cell, 1, (float *)&font.cell);
glBindVertexArray(vertex_array_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
}
void draw_string(font const& font, char const * const s, int x, int y)
{
int i = 0;
while (s[i] != 0) {
char c = s[i++];
if (!(c <= 0x20 || c > 0x7f)) {
XMFLOAT4X4 transform = glyph_transform(font, x, y);
glUniformMatrix4fv(location.uniform.transform, 1, GL_FALSE, (float *)&transform);
XMFLOAT2 glyph = glyph_coordinate(font, c);
glUniform2fv(location.uniform.glyph, 1, (float *)&glyph);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, (void *)0);
}
x += font.desc->glyph_width;
}
}
}

View File

@ -7,8 +7,30 @@
#include "glad/gl.h" #include "glad/gl.h"
#include "opengl.h" #include "opengl.h"
void * read_file(const char * filename, int * out_size) char const * g_source_path = NULL;
int g_source_path_length = 0;
char const * join_path(char * buf, const char * filename)
{ {
if (filename[0] == '/')
return filename;
int filename_length = strlen(filename);
assert(filename_length + g_source_path_length + 2 < 1024);
memcpy(buf, g_source_path, g_source_path_length);
buf[g_source_path_length] = '/';
memcpy(&buf[g_source_path_length + 1], filename, filename_length);
buf[g_source_path_length + 1 + filename_length] = 0;
return buf;
}
void * read_file(const char * r_filename, int * out_size)
{
char tmp[1024];
char const * filename = join_path(tmp, r_filename);
FILE * f = fopen(filename, "rb"); FILE * f = fopen(filename, "rb");
if (f == NULL) { if (f == NULL) {
fprintf(stderr, "fopen(%s): %s\n", filename, strerror(errno)); fprintf(stderr, "fopen(%s): %s\n", filename, strerror(errno));

View File

@ -6,27 +6,58 @@
#include "opengl.h" #include "opengl.h"
#include "directxmath/directxmath.h" #include "directxmath/directxmath.h"
#include "test.h" #include "test.h"
#include "font.h"
#include "window.h"
#include "data.inc" #include "data.inc"
struct location { struct test_location {
struct { struct {
unsigned int position; unsigned int position;
unsigned int normal; unsigned int normal;
unsigned int texture; unsigned int texture;
unsigned int block_position; unsigned int block_position;
unsigned int block_id; unsigned int block_id;
//unsigned int configuration; unsigned int data;
} attrib; } attrib;
struct { struct {
unsigned int transform; unsigned int transform;
unsigned int terrain_sampler; unsigned int terrain_sampler;
unsigned int texture_id;
} uniform;
struct {
unsigned int texture_id;
} binding;
};
static unsigned int test_program;
static test_location test_location;
struct quad_location {
struct {
unsigned int texture_sampler;
} uniform; } uniform;
}; };
static unsigned int quad_program;
static quad_location quad_location;
// state struct lighting_location {
static unsigned int test_program; struct {
static struct location location; unsigned int position_sampler;
unsigned int normal_sampler;
unsigned int color_sampler;
unsigned int quadratic;
unsigned int linear;
unsigned int eye;
unsigned int lights;
} uniform;
struct {
unsigned int lights;
} binding;
};
static unsigned int lighting_program;
static lighting_location lighting_location;
struct char_tpl { struct char_tpl {
const char * vtx; const char * vtx;
@ -34,73 +65,147 @@ struct char_tpl {
}; };
static const int region_count = 4; static const int region_count = 4;
static const char_tpl vertex_paths[region_count] = { static const char_tpl vertex_paths[] = {
{ "minecraft/region.0.0.instance.vtx", "minecraft/region.0.0.instance.cfg" }, { "minecraft/region.0.0.instance.vtx", "minecraft/region.0.0.instance.cfg" },
{ "minecraft/region.-1.0.instance.vtx", "minecraft/region.-1.0.instance.cfg" }, { "minecraft/region.-1.0.instance.vtx", "minecraft/region.-1.0.instance.cfg" },
{ "minecraft/region.0.-1.instance.vtx", "minecraft/region.0.-1.instance.cfg" }, { "minecraft/region.0.-1.instance.vtx", "minecraft/region.0.-1.instance.cfg" },
{ "minecraft/region.-1.-1.instance.vtx", "minecraft/region.-1.-1.instance.cfg" }, { "minecraft/region.-1.-1.instance.vtx", "minecraft/region.-1.-1.instance.cfg" },
}; };
static unsigned int vertex_array_objects[region_count]; static unsigned int vertex_array_object;
static unsigned int vertex_buffers[region_count]; static unsigned int per_instance_vertex_buffers[region_count];
static unsigned int vertex_count[region_count];
static unsigned int index_buffer; static unsigned int index_buffer;
static unsigned int per_vertex_buffer; static unsigned int per_vertex_buffer;
static const int vertex_size = 8; static const int vertex_size = 8;
static const int per_vertex_size = (3 + 3 + 2) * 2; static const int per_vertex_size = (3 + 3 + 2) * 2;
// also update index_buffer_custom_offsets in data.inc
static const int custom_block_types = 4;
static const int instance_cfg_length = 64 + custom_block_types;
struct instance_cfg { struct instance_cfg {
struct { struct region_instance {
int instance_count; int instance_count;
int offset; int offset;
} cfg[64]; } cfg[instance_cfg_length];
}; };
static instance_cfg instance_cfg[region_count]; static instance_cfg instance_cfg[region_count];
void load_program() static unsigned int empty_vertex_array_object = -1;
static unsigned int quad_index_buffer = -1;
void load_quad_index_buffer()
{
uint8_t const data[] = {
1, 0, 2, 3,
};
int const data_size = (sizeof (data));
glGenBuffers(1, &quad_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, data_size, data, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glGenVertexArrays(1, &empty_vertex_array_object);
}
void load_test_program()
{ {
unsigned int program = compile_from_files("shader/test.vert", unsigned int program = compile_from_files("shader/test.vert",
NULL, //"shader/test.geom", NULL,
"shader/test.frag"); "shader/test.frag");
location.attrib.position = glGetAttribLocation(program, "Position"); test_location.attrib.position = glGetAttribLocation(program, "Position");
location.attrib.normal = glGetAttribLocation(program, "Normal"); test_location.attrib.normal = glGetAttribLocation(program, "Normal");
location.attrib.texture = glGetAttribLocation(program, "Texture"); test_location.attrib.texture = glGetAttribLocation(program, "Texture");
location.attrib.block_position = glGetAttribLocation(program, "BlockPosition"); test_location.attrib.block_position = glGetAttribLocation(program, "BlockPosition");
location.attrib.block_id = glGetAttribLocation(program, "BlockID"); test_location.attrib.block_id = glGetAttribLocation(program, "BlockID");
printf("attributes:\n position %u\n normal %u\n texture %u\n block_position %u\n block_id %u\n\n", test_location.attrib.data = glGetAttribLocation(program, "Data");
location.attrib.position, printf("test program:\n");
location.attrib.normal, printf(" attributes:\n position %u\n normal %u\n texture %u\n block_position %u\n block_id %u\n block_id %u\n",
location.attrib.texture, test_location.attrib.position,
location.attrib.block_position, test_location.attrib.normal,
location.attrib.block_id); test_location.attrib.texture,
test_location.attrib.block_position,
test_location.attrib.block_id,
test_location.attrib.data);
location.uniform.transform = glGetUniformLocation(program, "Transform"); test_location.uniform.transform = glGetUniformLocation(program, "Transform");
location.uniform.terrain_sampler = glGetUniformLocation(program, "TerrainSampler"); test_location.uniform.terrain_sampler = glGetUniformLocation(program, "TerrainSampler");
printf("uniforms:\n transform %u\n terrain_sampler %u\n", test_location.uniform.texture_id = glGetUniformBlockIndex(program, "TextureID");
location.uniform.transform, printf(" uniforms:\n transform %u\n terrain_sampler %u\n texture_id %u\n",
location.uniform.terrain_sampler); test_location.uniform.transform,
test_location.uniform.terrain_sampler,
test_location.uniform.texture_id);
test_location.binding.texture_id = 0;
glUniformBlockBinding(program, test_location.uniform.texture_id, test_location.binding.texture_id);
test_program = program; test_program = program;
} }
void load_vertex_buffer(int i) void load_quad_program()
{
unsigned int program = compile_from_files("shader/quad.vert",
NULL,
"shader/quad.frag");
quad_location.uniform.texture_sampler = glGetUniformLocation(program, "TextureSampler");
printf("quad program:\n");
printf(" uniforms:\n texture_sampler %u\n",
quad_location.uniform.texture_sampler);
quad_program = program;
}
void load_lighting_program()
{
unsigned int program = compile_from_files("shader/quad.vert",
NULL,
"shader/lighting.frag");
lighting_location.uniform.position_sampler = glGetUniformLocation(program, "PositionSampler");
lighting_location.uniform.normal_sampler = glGetUniformLocation(program, "NormalSampler");
lighting_location.uniform.color_sampler = glGetUniformLocation(program, "ColorSampler");
lighting_location.uniform.quadratic = glGetUniformLocation(program, "Quadratic");
lighting_location.uniform.linear = glGetUniformLocation(program, "Linear");
lighting_location.uniform.eye = glGetUniformLocation(program, "Eye");
lighting_location.uniform.lights = glGetUniformBlockIndex(program, "Lights");
fprintf(stderr, "lighting program:\n");
fprintf(stderr, " uniforms:\n position_sampler %u normal_sampler %u color_sampler %u lights %u\n",
lighting_location.uniform.position_sampler,
lighting_location.uniform.normal_sampler,
lighting_location.uniform.color_sampler,
lighting_location.uniform.lights);
lighting_location.binding.lights = 0;
glUniformBlockBinding(program, lighting_location.uniform.lights, lighting_location.binding.lights);
lighting_program = program;
}
void load_per_instance_vertex_buffer(int i)
{ {
int vertex_buffer_data_size; int vertex_buffer_data_size;
void * vertex_buffer_data = read_file(vertex_paths[i].vtx, &vertex_buffer_data_size); void * vertex_buffer_data = read_file(vertex_paths[i].vtx, &vertex_buffer_data_size);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers[i]); glBindBuffer(GL_ARRAY_BUFFER, per_instance_vertex_buffers[i]);
glBufferData(GL_ARRAY_BUFFER, vertex_buffer_data_size, vertex_buffer_data, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, vertex_buffer_data_size, vertex_buffer_data, GL_STATIC_DRAW);
vertex_count[i] = vertex_buffer_data_size / vertex_size;
free(vertex_buffer_data); free(vertex_buffer_data);
glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
void load_per_vertex_buffer() void load_per_vertex_buffer()
{ {
glGenBuffers(1, &per_vertex_buffer);
int vertex_buffer_data_size; int vertex_buffer_data_size;
void * vertex_buffer_data = read_file("minecraft/per_vertex.vtx", &vertex_buffer_data_size); void * vertex_buffer_data = read_file("minecraft/per_vertex.vtx", &vertex_buffer_data_size);
@ -108,10 +213,14 @@ void load_per_vertex_buffer()
glBufferData(GL_ARRAY_BUFFER, vertex_buffer_data_size, vertex_buffer_data, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, vertex_buffer_data_size, vertex_buffer_data, GL_STATIC_DRAW);
free(vertex_buffer_data); free(vertex_buffer_data);
glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
void load_index_buffer() void load_index_buffer()
{ {
glGenBuffers(1, &index_buffer);
int index_buffer_data_size; int index_buffer_data_size;
void * index_buffer_data = read_file("minecraft/configuration.idx", &index_buffer_data_size); void * index_buffer_data = read_file("minecraft/configuration.idx", &index_buffer_data_size);
@ -119,94 +228,145 @@ void load_index_buffer()
glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_buffer_data_size, index_buffer_data, GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_buffer_data_size, index_buffer_data, GL_STATIC_DRAW);
free(index_buffer_data); free(index_buffer_data);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
} }
void load_vertex_attributes(int i) void load_test_vertex_attributes()
{ {
glBindBuffer(GL_ARRAY_BUFFER, per_vertex_buffer); glGenVertexArrays(1, &vertex_array_object);
glBindVertexArray(vertex_array_object);
glVertexAttribPointer(location.attrib.position, glVertexBindingDivisor(0, 0);
3, glVertexBindingDivisor(1, 1);
GL_HALF_FLOAT,
GL_FALSE,
per_vertex_size,
(void*)(0)
);
glVertexAttribPointer(location.attrib.normal, glEnableVertexAttribArray(test_location.attrib.position);
3, glVertexAttribFormat(test_location.attrib.position, 3, GL_HALF_FLOAT, GL_FALSE, 0);
GL_HALF_FLOAT, glVertexAttribBinding(test_location.attrib.position, 0);
GL_FALSE,
per_vertex_size,
(void*)(6)
);
glVertexAttribPointer(location.attrib.texture, glEnableVertexAttribArray(test_location.attrib.normal);
2, glVertexAttribFormat(test_location.attrib.normal, 3, GL_HALF_FLOAT, GL_FALSE, 6);
GL_HALF_FLOAT, glVertexAttribBinding(test_location.attrib.normal, 0);
GL_FALSE,
per_vertex_size,
(void*)(12)
);
glEnableVertexAttribArray(location.attrib.position);
glEnableVertexAttribArray(location.attrib.normal);
glEnableVertexAttribArray(location.attrib.texture);
glVertexAttribDivisor(location.attrib.position, 0);
glVertexAttribDivisor(location.attrib.normal, 0);
glVertexAttribDivisor(location.attrib.texture, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffers[i]); glEnableVertexAttribArray(test_location.attrib.texture);
glVertexAttribFormat(test_location.attrib.texture, 2, GL_HALF_FLOAT, GL_FALSE, 12);
glVertexAttribBinding(test_location.attrib.texture, 0);
glVertexAttribPointer(location.attrib.block_position, glEnableVertexAttribArray(test_location.attrib.block_position);
3, glVertexAttribFormat(test_location.attrib.block_position, 3, GL_SHORT, GL_FALSE, 0);
GL_SHORT, glVertexAttribBinding(test_location.attrib.block_position, 1);
GL_FALSE,
vertex_size, glEnableVertexAttribArray(test_location.attrib.block_id);
(void*)(0) glVertexAttribFormat(test_location.attrib.block_id, 1, GL_UNSIGNED_BYTE, GL_FALSE, 6);
); glVertexAttribBinding(test_location.attrib.block_id, 1);
glVertexAttribPointer(location.attrib.block_id,
1, glEnableVertexAttribArray(test_location.attrib.data);
GL_UNSIGNED_BYTE, glVertexAttribFormat(test_location.attrib.data, 1, GL_UNSIGNED_BYTE, GL_FALSE, 7);
GL_FALSE, glVertexAttribBinding(test_location.attrib.data, 1);
vertex_size,
(void*)(6) glBindVertexArray(0);
);
glEnableVertexAttribArray(location.attrib.block_position);
glEnableVertexAttribArray(location.attrib.block_id);
glVertexAttribDivisor(location.attrib.block_position, 1);
glVertexAttribDivisor(location.attrib.block_id, 1);
} }
void load_instance_cfg(int i) void load_instance_cfg(int i)
{ {
int data_size; int data_size;
void * data = read_file(vertex_paths[i].cfg, &data_size); void * data = read_file(vertex_paths[i].cfg, &data_size);
assert(data_size == 512); assert(data_size == (sizeof (struct instance_cfg)));
memcpy(&instance_cfg[i], data, data_size); memcpy(&instance_cfg[i], data, data_size);
} }
struct target_name {
enum {
POSITION = 0,
NORMAL = 1,
COLOR = 2,
};
};
template <int render_target_count>
struct geometry_buffer {
unsigned int framebuffer;
unsigned int target[render_target_count]; // textures
unsigned int renderbuffer;
int initialized;
int width;
int height;
};
struct target_type {
GLint internal_format;
GLenum attachment;
};
static geometry_buffer<3> geometry_buffer_pnc = {};
static target_type const geometry_buffer_pnc_types[3] = {
[target_name::POSITION] = { GL_RGBA16F, GL_COLOR_ATTACHMENT0 },
[target_name::NORMAL] = { GL_RGBA16F, GL_COLOR_ATTACHMENT1 },
[target_name::COLOR] = { GL_RGBA8, GL_COLOR_ATTACHMENT2 },
};
template <int render_target_count>
void init_geometry_buffer(geometry_buffer<render_target_count>& geometry_buffer, target_type const * const types)
{
int width = g_window_width;
int height = g_window_height;
if ((geometry_buffer.initialized == 1) && (width == geometry_buffer.width) && (height == geometry_buffer.height)) {
return;
}
if (geometry_buffer.initialized) {
glDeleteFramebuffers(1, &geometry_buffer.framebuffer);
glDeleteTextures(render_target_count, geometry_buffer.target);
glDeleteRenderbuffers(1, &geometry_buffer.renderbuffer);
}
glGenFramebuffers(1, &geometry_buffer.framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, geometry_buffer.framebuffer);
glGenTextures(render_target_count, geometry_buffer.target);
for (int i = 0; i < render_target_count; i++) {
glBindTexture(GL_TEXTURE_2D, geometry_buffer.target[i]);
glTexImage2D(GL_TEXTURE_2D, 0, types[i].internal_format, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
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);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, types[i].attachment, GL_TEXTURE_2D, geometry_buffer.target[i], 0);
}
static_assert(render_target_count == 3);
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, attachments); // bound to GL_DRAW_FRAMEBUFFER
glGenRenderbuffers(1, &geometry_buffer.renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, geometry_buffer.renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, geometry_buffer.renderbuffer);
assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
geometry_buffer.initialized = 1;
geometry_buffer.width = width;
geometry_buffer.height = height;
}
void load_buffers() void load_buffers()
{ {
load_test_vertex_attributes();
// per-vertex buffer // per-vertex buffer
glGenBuffers(1, &per_vertex_buffer);
load_per_vertex_buffer(); load_per_vertex_buffer();
// per-instance buffer // per-instance buffer
glGenVertexArrays(region_count, vertex_array_objects); glGenBuffers(region_count, per_instance_vertex_buffers);
glGenBuffers(region_count, vertex_buffers);
for (int i = 0; i < region_count; i++) { for (int i = 0; i < region_count; i++) {
glBindVertexArray(vertex_array_objects[i]); load_per_instance_vertex_buffer(i);
load_vertex_buffer(i);
load_vertex_attributes(i);
load_instance_cfg(i); load_instance_cfg(i);
} }
// index buffer // index buffer
glGenBuffers(1, &index_buffer);
load_index_buffer(); load_index_buffer();
} }
@ -231,54 +391,144 @@ void load_textures()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);
free(texture_data); free(texture_data);
glBindTexture(GL_TEXTURE_2D, 0);
} }
static unsigned int textures_ubo; static unsigned int light_uniform_buffer;
static unsigned int texture_id_uniform_buffer;
void load_texture_shader_storage() unsigned int load_uniform_buffer(char const * const path)
{ {
unsigned int buffer; unsigned int buffer;
glGenBuffers(1, &buffer); glGenBuffers(1, &buffer);
glBindBuffer(GL_UNIFORM_BUFFER, buffer); glBindBuffer(GL_UNIFORM_BUFFER, buffer);
int textures_data_size; int data_size;
void * textures_data = read_file("minecraft/block_id_to_texture_id.data", &textures_data_size); void * data = read_file(path, &data_size);
assert(textures_data != NULL); assert(data != NULL);
glBufferData(GL_UNIFORM_BUFFER, textures_data_size, textures_data, GL_STATIC_DRAW); glBufferData(GL_UNIFORM_BUFFER, data_size, data, GL_STATIC_DRAW);
free(textures_data); free(data);
textures_ubo = buffer; glBindBuffer(GL_UNIFORM_BUFFER, 0);
return buffer;
}
void load_light_uniform_buffer()
{
light_uniform_buffer = load_uniform_buffer("minecraft/global.lights.vtx");
}
void load_texture_id_uniform_buffer()
{
texture_id_uniform_buffer = load_uniform_buffer("minecraft/block_id_to_texture_id.data");
} }
extern "C" { extern "C" {
void * SDL_GL_GetProcAddress(const char *proc); void * SDL_GL_GetProcAddress(const char *proc);
} }
void load() struct view_state {
XMVECTOR up;
XMVECTOR eye;
XMVECTOR forward;
XMVECTOR direction;
float fov;
float pitch;
};
view_state view_state;
font::font * terminus_fonts;
void load(const char * source_path)
{ {
g_source_path_length = strlen(source_path);
assert(source_path[g_source_path_length - 1] != '/');
g_source_path = source_path;
fprintf(stderr, "getproc %p\n", SDL_GL_GetProcAddress); fprintf(stderr, "getproc %p\n", SDL_GL_GetProcAddress);
gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress); gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress);
load_program(); load_test_program();
load_buffers(); load_buffers();
load_textures(); load_textures();
//load_texture_shader_storage(); load_texture_id_uniform_buffer();
//unsigned int textures_layout = glGetUniformBlockIndex(test_program, "TexturesLayout"); view_state.up = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);
//glUniformBlockBinding(test_program, textures_layout, 0); view_state.eye = XMVectorSet(-55.5f, 48.25f, 50.0f, 1);
//printf("textures_layout %d\n", textures_layout); view_state.forward = XMVectorSet(-0.63, 0.78, 0, 0);
view_state.direction = view_state.forward;
view_state.pitch = -0.11;
view_state.fov = 1.5;
//////////////////////////////////////////////////////////////////////
// font
//////////////////////////////////////////////////////////////////////
font::load_shader();
terminus_fonts = (font::font *)malloc((sizeof (font::font)) * font::terminus_length);
font::load_fonts(terminus_fonts, font::terminus, font::terminus_length);
//////////////////////////////////////////////////////////////////////
// quad
//////////////////////////////////////////////////////////////////////
load_quad_program();
load_quad_index_buffer();
//////////////////////////////////////////////////////////////////////
// lighting
//////////////////////////////////////////////////////////////////////
load_lighting_program();
load_light_uniform_buffer();
} }
static float vx = 0.0; float _ry = 0.0;
static float vy = 0.0;
static float vz = 0.0;
void update(float lx, float ly, float ry) struct light_parameters {
float quadratic;
float linear;
};
light_parameters lighting = {
.quadratic = 1.0,
.linear = 1.0
};
void update(float lx, float ly, float rx, float ry, float tl, float tr,
int up, int down, int left, int right,
int a, int b, int x, int y)
{ {
vx += 2.5 * lx; //view_state.yaw += rx;
vy += -2.5 * ry; XMMATRIX mrz = XMMatrixRotationZ(rx * -0.035);
vz += -2.5 * ly;
view_state.forward = XMVector3Transform(XMVector3NormalizeEst(view_state.forward), mrz);
XMVECTOR normal = XMVector3NormalizeEst(XMVector3Cross(view_state.forward, view_state.up));
view_state.pitch += ry * -0.035;
if (view_state.pitch > 1.57f) view_state.pitch = 1.57f;
if (view_state.pitch < -1.57f) view_state.pitch = -1.57f;
XMMATRIX mrn = XMMatrixRotationAxis(normal, view_state.pitch);
view_state.direction = XMVector3Transform(view_state.forward, mrn);
view_state.eye += view_state.forward * -ly + normal * lx + view_state.up * (tl - tr);
float new_fov = view_state.fov + 0.01 * up + -0.01 * down;
if (new_fov > 0.00001f) {
view_state.fov = new_fov;
}
lighting.quadratic += 0.01 * a + -0.01 * b;
if (lighting.quadratic < 0.0f)
lighting.quadratic = 0.0f;
lighting.linear += 0.01 * x + -0.01 * y;
if (lighting.linear < 0.0f)
lighting.linear = 0.0f;
} }
static inline int popcount(int x) static inline int popcount(int x)
@ -286,45 +536,107 @@ static inline int popcount(int x)
return __builtin_popcount(x); return __builtin_popcount(x);
} }
void draw() template <typename T>
void labeled_value(char * const buf, char const * const label, char const * const format, T value)
{ {
XMVECTOR eye = XMVectorSet(vx + -50.0f, vz + -50.0f, vy + 150.0f, 0.0f); const int label_length = strlen(label);
XMVECTOR at = XMVectorSet(vx + 50.0f, vz + 50.0f, vy + 50.0f, 0.0f); memcpy(buf, label, label_length);
XMVECTOR up = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); int len = snprintf(&buf[label_length], 511 - label_length, format, value);
XMMATRIX view = XMMatrixLookAtRH(eye, at, up); buf[label_length + len] = 0;
}
float fov_angle_y = XMConvertToRadians(45 * 0.75); inline static float draw_vector(font::font const& ter_best, char * const buf, float y, char const * const label, XMVECTOR vec)
float aspect_ratio = 1.0; {
labeled_value<float>(buf, label, ".x: %.2f", XMVectorGetX(vec));
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
labeled_value<float>(buf, label, ".y: %.2f", XMVectorGetY(vec));
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
labeled_value<float>(buf, label, ".z: %.2f", XMVectorGetZ(vec));
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
return y;
}
void draw_hud()
{
char buf[512];
float y = 10.0f;
int font_ix = font::best_font(font::terminus, font::terminus_length);
font::font const& ter_best = terminus_fonts[font_ix];
font::draw_start(ter_best, empty_vertex_array_object, quad_index_buffer);
labeled_value<float>(buf, "fov: ", "%.3f", view_state.fov);
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
labeled_value<int>(buf, "font_height: ", "%d", ter_best.desc->glyph_height);
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
labeled_value<float>(buf, "lighting.quadratic: ", "%.2f", lighting.quadratic);
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
labeled_value<float>(buf, "lighting.linear: ", "%.2f", lighting.linear);
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
y = draw_vector(ter_best, buf, y, "eye", view_state.eye);
y = draw_vector(ter_best, buf, y, "forward", view_state.forward);
labeled_value<float>(buf, "pitch: ", "%.9f", view_state.pitch);
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
}
void draw_minecraft()
{
// possibly re-initialize geometry buffer if window width/height changes
init_geometry_buffer(geometry_buffer_pnc, geometry_buffer_pnc_types);
XMVECTOR at = XMVectorAdd(view_state.eye, view_state.direction);
XMMATRIX view = XMMatrixLookAtRH(view_state.eye, at, view_state.up);
float fov_angle_y = XMConvertToRadians(45 * view_state.fov);
float aspect_ratio = g_window_width / g_window_height;
float near_z = 1.0; float near_z = 1.0;
float far_z = 0.1; float far_z = 0.1;
XMMATRIX projection = XMMatrixPerspectiveFovRH(fov_angle_y, aspect_ratio, near_z, far_z); XMMATRIX projection = XMMatrixPerspectiveFovRH(fov_angle_y, aspect_ratio, near_z, far_z);
XMMATRIX transform = view * projection; 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); glUseProgram(test_program);
glBlendFunc(GL_ONE, GL_ZERO);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER); glDepthFunc(GL_GREATER);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glUniformMatrix4fv(location.uniform.transform, 1, false, (float *)&transform); glUniformMatrix4fv(test_location.uniform.transform, 1, false, (float *)&transform);
glUniform1i(location.uniform.terrain_sampler, 0); glUniform1i(test_location.uniform.terrain_sampler, 0);
//glBindBuffer(GL_UNIFORM_BUFFER, textures_ubo); glBindBufferBase(GL_UNIFORM_BUFFER, test_location.binding.texture_id, texture_id_uniform_buffer);
//glBindBufferBase(GL_UNIFORM_BUFFER, 0, textures_ubo);
//glEnable(GL_CULL_FACE); //glEnable(GL_CULL_FACE);
//glCullFace(GL_FRONT); //glCullFace(GL_FRONT);
//glFrontFace(GL_CCW); //glFrontFace(GL_CCW);
for (int region_index = 0; region_index < region_count; region_index++) { glBindVertexArray(vertex_array_object);
glBindVertexArray(vertex_array_objects[region_index]); glBindVertexBuffer(0, per_vertex_buffer, 0, per_vertex_size);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
for (int region_index = 0; region_index < region_count; region_index++) {
glBindVertexBuffer(1, per_instance_vertex_buffers[region_index], 0, vertex_size);
//////////////////////////////////////////////////////////////////////
// cube blocks
//////////////////////////////////////////////////////////////////////
for (int configuration = 1; configuration < 64; configuration++) { for (int configuration = 1; configuration < 64; configuration++) {
int element_count = 6 * popcount(configuration); int element_count = 6 * popcount(configuration);
const void * indices = (void *)((ptrdiff_t)index_buffer_configuration_offsets[configuration]); // index into configuration.idx const void * indices = (void *)((ptrdiff_t)index_buffer_configuration_offsets[configuration]); // index into configuration.idx
@ -337,5 +649,91 @@ void draw()
glDrawElementsInstancedBaseInstance(GL_TRIANGLES, element_count, GL_UNSIGNED_BYTE, indices, instance_count, base_instance); glDrawElementsInstancedBaseInstance(GL_TRIANGLES, element_count, GL_UNSIGNED_BYTE, indices, instance_count, base_instance);
} }
//////////////////////////////////////////////////////////////////////
// custom blocks
//////////////////////////////////////////////////////////////////////
for (int i = 0; i < custom_block_types; i++) {
int element_count = index_buffer_custom_offsets[i].count;
const void * indices = (void *)((ptrdiff_t)index_buffer_custom_offsets[i].offset);
int instance_count = instance_cfg[region_index].cfg[64 + i].instance_count;
int base_instance = instance_cfg[region_index].cfg[64 + i].offset / vertex_size; // index into region.0.0.instance.vtx
if (instance_count == 0)
continue;
glDrawElementsInstancedBaseInstance(GL_TRIANGLES, element_count, GL_UNSIGNED_BYTE, indices, instance_count, base_instance);
}
} }
} }
void draw_quad()
{
glUseProgram(quad_program);
glDepthFunc(GL_ALWAYS);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, geometry_buffer_pnc.target[1]);
glUniform1i(quad_location.uniform.texture_sampler, 0);
glBindVertexArray(empty_vertex_array_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_index_buffer);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, (void *)0);
}
static inline bool near_zero(float a)
{
return (fabsf(a) < 0.00001f);
}
void draw_lighting()
{
glUseProgram(lighting_program);
glDepthFunc(GL_ALWAYS);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, geometry_buffer_pnc.target[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, geometry_buffer_pnc.target[1]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, geometry_buffer_pnc.target[2]);
glUniform1i(lighting_location.uniform.position_sampler, 0);
glUniform1i(lighting_location.uniform.normal_sampler, 1);
glUniform1i(lighting_location.uniform.color_sampler, 2);
float quadratic = near_zero(lighting.quadratic) ? 0.0 : 1.0f / lighting.quadratic;
float linear = near_zero(lighting.linear) ? 0.0 : 1.0f / lighting.linear;
glUniform1f(lighting_location.uniform.quadratic, quadratic);
glUniform1f(lighting_location.uniform.linear, linear);
XMFLOAT3 eye;
XMStoreFloat3(&eye, view_state.eye);
glUniform3fv(lighting_location.uniform.eye, 1, (float*)&eye);
glBindBufferBase(GL_UNIFORM_BUFFER, lighting_location.binding.lights, light_uniform_buffer);
glBindVertexArray(empty_vertex_array_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_index_buffer);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, (void *)0);
}
void draw()
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(-1.0f);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, geometry_buffer_pnc.framebuffer);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw_minecraft();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw_lighting();
//draw_quad();
draw_hud();
}

10
src/window.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "window.h"
float g_window_width = 1;
float g_window_height = 1;
void update_window(int width, int height)
{
g_window_width = width;
g_window_height = height;
}