minecraft: separate mesh for tallgrass

This commit is contained in:
Zack Buhman 2026-03-07 17:39:20 -06:00
parent 9d97d6448e
commit 8ce7ca035a
21 changed files with 630 additions and 286 deletions

Binary file not shown.

View File

@ -1,98 +1,7 @@
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
}

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)

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

@ -0,0 +1,353 @@
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
INFO_UPDATEGAME1 = 248
INFO_UPDATEGAME2 = 249
LEAVES_CARRIED = 254
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
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
@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.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.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),
]
tiles_by_id = {
tile.block_id: tile for tile in tiles
}

View File

@ -7,6 +7,7 @@ from collections import defaultdict
import mcregion
import vec3
import vertex_buffer
import data
def wrap_n(nc, chunk_c):
if nc < 0:
@ -17,9 +18,15 @@ def wrap_n(nc, chunk_c):
chunk_c = chunk_c + 1
return nc, chunk_c
non_solid_blocks = {
data.BlockID.TALL_GRASS,
data.BlockID.MUSHROOM_1,
data.BlockID.MUSHROOM_2,
}
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:
if block_id == data.BlockID.AIR:
return
def neighbor_exists(nx, ny, nz):
@ -36,7 +43,9 @@ def block_neighbors(level_table, chunk_x, chunk_z, block_index):
if key not in level_table:
return True
n_block_id = level_table[key].blocks[n_block_index]
return n_block_id != 0
has_neighbor = (n_block_id != data.BlockID.AIR) and (n_block_id not in non_solid_blocks)
return has_neighbor
x, y, z = mcregion.xyz_from_block_index(block_index)
@ -49,7 +58,7 @@ def block_neighbors(level_table, chunk_x, chunk_z, block_index):
yield i
normal_indices = list(find_non_neighbors())
if normal_indices:
if block_id in non_solid_blocks or normal_indices:
yield center_position, block_id, normal_indices
def devoxelize_region(level_table):
@ -82,31 +91,61 @@ def build_block_configuration_table():
indices.extend(vertex_buffer.faces_by_normal[vertex_buffer.normals[j]])
yield indices
non_cube_blocks = {
data.BlockID.TALL_GRASS,
}
def pack_instance_data(position, block_id):
packed = struct.pack("<hhhBB",
position[0], position[1], position[2],
block_id,
0)
return packed
def build_block_instances(blocks):
by_configuration = defaultdict(list)
deferred_blocks = []
for position, block_id, normal_indices in blocks:
if block_id in non_cube_blocks:
deferred_blocks.append((position, block_id))
continue
configuration = normal_indices_as_block_configuration(normal_indices)
#print(position, block_id, block_configuration)
by_configuration[configuration].append((position, block_id))
offset = 0
configuration_instance_count_offset = []
with open(f"{data_path}.instance.vtx", "wb") as f:
######################################################################
# cubes
######################################################################
for configuration in range(64):
if configuration not in by_configuration:
configuration_instance_count_offset.append((0, 0))
continue
blocks = by_configuration[configuration]
configuration_instance_count_offset.append((len(blocks), offset))
for position, block_id in blocks:
packed = struct.pack("<hhhBB",
position[0], position[1], position[2],
block_id,
0)
_blocks = by_configuration[configuration]
configuration_instance_count_offset.append((len(_blocks), offset))
for position, block_id in _blocks:
assert block_id not in non_cube_blocks, block_id
packed = pack_instance_data(position, block_id)
f.write(packed)
offset += len(packed)
######################################################################
# non-cubes
######################################################################
nc_offset = offset
nc_instance_count = 0
for position, block_id in deferred_blocks:
if block_id not in non_cube_blocks:
continue
packed = pack_instance_data(position, block_id)
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:
for instance_count, offset in configuration_instance_count_offset:
print(instance_count, offset)

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.800011 0.600021 0.800011
v 0.800011 -1.000000 0.800011
v -0.800010 -1.000000 0.800011
v -0.800011 0.600021 0.800011
v -0.800011 0.600021 -0.800011
v -0.800011 -1.000000 -0.800011
v 0.800011 -1.000000 -0.800011
v 0.800010 0.600022 -0.800011
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

View File

@ -1,41 +1,8 @@
import struct
import vec3
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]
}
import obj
import obj_state
import sys
normals = [
(-1.0, 0.0, 0.0),
@ -46,7 +13,9 @@ normals = [
(1.0, 0.0, 0.0),
]
def build_configuration_index_buffers(f):
def build_configuration_index_buffers(f, faces_by_normal):
assert(set(normals) == set(faces_by_normal.keys()))
offset = 0
configuration_offsets = []
for configuration in range(64):
@ -65,14 +34,37 @@ def build_configuration_index_buffers(f):
if i % 8 == 7:
print()
def build_vertex_buffer(f):
for position, normal, texture in vertex_table:
def build_vertex_buffer(f, vertex_buffer):
for position, normal, texture in vertex_buffer:
position = vec3.mul(position, 0.5)
f.write(struct.pack("<eeeeeeee", *position, *normal, *texture))
if __name__ == "__main__":
with open("configuration.idx", "wb") as f:
build_configuration_index_buffers(f)
def write_indices(f, index_buffer, start, count):
for i in range(count):
f.write(struct.pack("<B", index_buffer[start + i]))
with open("per_vertex.vtx", "wb") as f:
build_vertex_buffer(f)
def main():
vertex_buffer = []
index_buffer = []
index_lookup = {}
cube_state = obj.parse_obj_from_filename("cube.obj")
obj_state.append_triangles(cube_state, vertex_buffer, index_buffer, index_lookup)
cube_faces_by_normal = obj_state.build_faces_by_normal(vertex_buffer, index_buffer)
tallgrass_index_start = len(index_buffer)
tallgrass_state = obj.parse_obj_from_filename("tallgrass.obj")
obj_state.append_triangles(tallgrass_state, vertex_buffer, index_buffer, index_lookup)
tallgrass_index_count = len(index_buffer) - tallgrass_index_start
print(tallgrass_index_start, tallgrass_index_count)
with open("../configuration.idx", "wb") as f:
build_configuration_index_buffers(f, cube_faces_by_normal)
write_indices(f, index_buffer, tallgrass_index_start, tallgrass_index_count)
with open("../per_vertex.vtx", "wb") as f:
build_vertex_buffer(f, vertex_buffer)
if __name__ == "__main__":
main()

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.

View File

@ -12,7 +12,7 @@ uniform sampler2D TerrainSampler;
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,
34, 20, 52, 48, 49, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 39,
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,
@ -46,11 +46,13 @@ void main()
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);
if (diffuse_intensity < 0.1)
diffuse_intensity = 0.1;
if (fs_in.BlockID == 31) // tall_grass
diffuse_intensity = 1.0;
FragColor = vec4(texture_color.xyz * vec3(diffuse_intensity), 1.0);
}

View File

@ -34,7 +34,7 @@ struct char_tpl {
};
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.-1.0.instance.vtx", "minecraft/region.-1.0.instance.cfg" },
{ "minecraft/region.0.-1.instance.vtx", "minecraft/region.0.-1.instance.cfg" },
@ -49,11 +49,13 @@ static unsigned int per_vertex_buffer;
static const int vertex_size = 8;
static const int per_vertex_size = (3 + 3 + 2) * 2;
static const int instance_cfg_length = 64 + 1;
struct instance_cfg {
struct {
struct region_instance {
int instance_count;
int offset;
} cfg[64];
} cfg[instance_cfg_length];
};
static instance_cfg instance_cfg[region_count];
@ -160,7 +162,7 @@ void load_instance_cfg(int i)
{
int 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);
}
@ -308,6 +310,9 @@ void draw()
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++) {
int element_count = 6 * popcount(configuration);
const void * indices = (void *)((ptrdiff_t)index_buffer_configuration_offsets[configuration]); // index into configuration.idx
@ -320,5 +325,18 @@ void draw()
glDrawElementsInstancedBaseInstance(GL_TRIANGLES, element_count, GL_UNSIGNED_BYTE, indices, instance_count, base_instance);
}
//////////////////////////////////////////////////////////////////////
// non-cube blocks
//////////////////////////////////////////////////////////////////////
{
int element_count = 6 * 2;
const void * indices = (void *)((ptrdiff_t)1152);
int instance_count = instance_cfg[region_index].cfg[64].instance_count;
int base_instance = instance_cfg[region_index].cfg[64].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);
}
}
}