diff --git a/.gitignore b/.gitignore index 9ae3a0d..2aad1b7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ *.o main *.so -*.dylib \ No newline at end of file +*.dylib +__pycache__ \ No newline at end of file diff --git a/include/opengl.h b/include/opengl.h index 1545e24..03483de 100644 --- a/include/opengl.h +++ b/include/opengl.h @@ -7,6 +7,7 @@ extern "C" { void * read_file(const char * filename, int * out_size); unsigned int compile_from_files(const char * vertex_path, + const char * geometry_path, const char * fragment_path); #ifdef __cplusplus diff --git a/main.lua b/main.lua index 0672ade..8161182 100644 --- a/main.lua +++ b/main.lua @@ -40,9 +40,6 @@ function love.run() return a or 0, b end end - if name == "keypressed" then - keypressed(a, b, c) - end end local dt = love.timer.step() @@ -52,5 +49,7 @@ function love.run() love.graphics.present() love.timer.sleep(0.001) + local fps = love.timer.getFPS( ) + print(fps) end end diff --git a/minecraft/block_id_to_texture_id.data b/minecraft/block_id_to_texture_id.data deleted file mode 100644 index 94099df..0000000 Binary files a/minecraft/block_id_to_texture_id.data and /dev/null differ diff --git a/minecraft/gen/blocks.py b/minecraft/gen/blocks.py index 6f3905b..e877391 100644 --- a/minecraft/gen/blocks.py +++ b/minecraft/gen/blocks.py @@ -101,4 +101,6 @@ with open("block_id_to_texture_id.data", "wb") as f: for i in range(256): value = lookup.get(i, unk) f.write(struct.pack("I", mem[ix:ix+4]) - chunk_offset = (chunk_location >> 8) & 0xffffff - chunk_sector_count = chunk_location & 0xff - yield chunk_offset, chunk_sector_count - -def parse_locations(mem, offset): - locations = list(_parse_locations(mem, offset)) - return offset + 1024 * 4, locations - -def _parse_timestamps(mem, offset): - for i in range(1024): - ix = offset + i * 4 - timestamp, = struct.unpack(">I", mem[ix:ix+4]) - yield timestamp - -def parse_timestamps(mem, offset): - timestamps = list(_parse_timestamps(mem, offset)) - return offset + 1024 * 4, timestamps - -def print_locations(locations): - for y in range(32): - for x in range(32): - offset, count = locations[y * 32 + x] - print(str(offset).rjust(4), end=' ') - print() - -class CountZeroException(Exception): - pass - -def parse_payload(mem, location): - offset, count = location - if count == 0: - raise CountZeroException() - ix = offset * 4096 - payload = mem[ix:ix + count * 4096] - length, = struct.unpack(">I", payload[0:4]) - assert length <= count * 4096, (length, count) - compression_type = payload[4] - data = payload[5:5 + (length - 1)] - assert compression_type == 2, compression_type - uncompressed = zlib.decompress(data) - return memoryview(uncompressed) - -class TAG: - Byte = 0x01 - Short = 0x02 - Int = 0x03 - Long = 0x04 - Float = 0x05 - Double = 0x06 - ByteArray = 0x07 - String = 0x08 - List = 0x09 - Compound = 0x0a - -@dataclass -class Byte: - name: str - value: int - -@dataclass -class Short: - name: str - value: int - -@dataclass -class Int: - name: str - value: int - -@dataclass -class Long: - name: str - value: int - -@dataclass -class Float: - name: str - value: float - -@dataclass -class Double: - name: str - value: float - -@dataclass -class ByteArray: - name: str - value: bytes - -@dataclass -class String: - name: str - value: str - -@dataclass -class List: - name: str - items: list - -@dataclass -class Compound: - name: str - tags: list - -def indent(level): - return " " * level - -def parse_tag_inner(mem, offset, level, tag_type, name): - payload = mem[offset:] - if tag_type == TAG.Byte: - value, = struct.unpack(">b", payload[0:1]) - return offset + 1, Byte(name, value) - if tag_type == TAG.Short: - value, = struct.unpack(">h", payload[0:2]) - return offset + 2, Short(name, value) - elif tag_type == TAG.Int: - value, = struct.unpack(">i", payload[0:4]) - return offset + 4, Int(name, value) - elif tag_type == TAG.Long: - value, = struct.unpack(">q", payload[0:8]) - return offset + 8, Long(name, value) - elif tag_type == TAG.Float: - value, = struct.unpack(">f", payload[0:4]) - return offset + 4, Float(name, value) - elif tag_type == TAG.Double: - value, = struct.unpack(">d", payload[0:8]) - return offset + 8, Double(name, value) - elif tag_type == TAG.ByteArray: - size, = struct.unpack(">i", payload[0:4]) - value = bytes(payload[4:4+size]) - return offset + 4 + size, ByteArray(name, value) - elif tag_type == TAG.String: - size, = struct.unpack(">H", payload[0:2]) - value = bytes(payload[2:2+size]).decode('utf-8') - return offset + 2 + size, String(name, value) - elif tag_type == TAG.List: - list_content_tag_id, size = struct.unpack(">BI", payload[0:5]) - items = [] - offset = offset + 5 - for i in range(size): - payload = mem[offset:] - offset, item = parse_tag_inner(mem, offset, level, list_content_tag_id, None) - items.append(item) - return offset, List(name, items) - elif tag_type == TAG.Compound: - tags = [] - while payload[0] != 0: - offset, tag = parse_tag(mem, offset, level+1) - payload = mem[offset:] - tags.append(tag) - return offset + 1, Compound(name, tags) - else: - assert False, tag_type - -def parse_tag(mem, offset, level): - data = mem[offset:] - tag_type = data[0] - name_length, = struct.unpack(">H", data[1:3]) - name = bytes(data[3:3+name_length]) - #print(indent(level), tag_type, name_length, name) - offset = offset + 3 + name_length - return parse_tag_inner(mem, offset, level, tag_type, name) - -@dataclass -class Level: - blocks: bytes - data: bytes - sky_light: bytes - block_light: bytes - height_map: bytes - x_pos: int - z_pos: int - -def level_from_tag(tag): - assert type(tag) == Compound - assert tag.name == b'' - assert len(tag.tags) == 1 - level, = tag.tags - assert type(level) == Compound - assert level.name == b'Level' - - name_mapping = { - b'Blocks': 'blocks', - b'Data': 'data', - b'SkyLight': 'sky_light', - b'BlockLight': 'block_light', - b'HeightMap': 'height_map', - b'xPos': 'x_pos', - b'zPos': 'z_pos', - } - - args = {} - - for tag in level.tags: - if tag.name in name_mapping: - arg_name = name_mapping[tag.name] - args[arg_name] = tag.value - - return Level(**args) - -def parse_location(mem, location): - uncompressed = parse_payload(mem, location) - offset, tag = parse_tag(uncompressed, 0, 0) - assert offset == len(uncompressed), (offset, len(uncompressed)) - level = level_from_tag(tag) - return level - -def xyz_from_block_index(block_index): - assert block_index >= 0 and block_index < (128 * 16 * 16) - x = int(block_index / (128 * 16)) - y = int(block_index % 128) - z = int(int(block_index / 128) % 16) - return x, y, z - -def block_index_from_xyz(x, y, z): - assert x >= 0 and x < 16 - assert y >= 0 and y < 128 - assert z >= 0 and z < 16 - return int(y + z * 128 + x * 128 * 16) +import mcregion +import vec3 +import vertex_buffer def wrap_n(nc, chunk_c): if nc < 0: @@ -243,77 +16,15 @@ def wrap_n(nc, chunk_c): chunk_c = chunk_c + 1 return nc, chunk_c -def vec3_add(v1, v2): - return ( - v1[0] + v2[0], - v1[1] + v2[1], - v1[2] + v2[2], - ) - -def vec3_mul(v, s): - return ( - v[0] * s, - v[1] * s, - v[2] * s, - ) - -vertex_table = [ - ((-1.0, 1.0, -1.0), (0.0, 1.0, 0.0), (1.0, 0.0)), - ((1.0, 1.0, 1.0), (0.0, 1.0, 0.0), (0.0, 1.0)), - ((1.0, 1.0, -1.0), (0.0, 1.0, 0.0), (0.0, 0.0)), - ((1.0, 1.0, 1.0), (0.0, 0.0, 1.0), (1.0, 1.0)), - - ((-1.0, -1.0, 1.0), (0.0, 0.0, 1.0), (0.0, 0.0)), - ((1.0, -1.0, 1.0), (0.0, 0.0, 1.0), (1.0, 0.0)), - ((-1.0, 1.0, 1.0), (-1.0, 0.0, 0.0), (1.0, 1.0)), - ((-1.0, -1.0, -1.0), (-1.0, 0.0, 0.0), (0.0, 0.0)), - - ((-1.0, -1.0, 1.0), (-1.0, 0.0, 0.0), (1.0, 0.0)), - ((1.0, -1.0, -1.0), (0.0, -1.0, 0.0), (1.0, 0.0)), - ((-1.0, -1.0, 1.0), (0.0, -1.0, 0.0), (0.0, 1.0)), - ((-1.0, -1.0, -1.0), (0.0, -1.0, 0.0), (0.0, 0.0)), - - ((1.0, 1.0, -1.0), (1.0, 0.0, 0.0), (1.0, 1.0)), - ((1.0, -1.0, 1.0), (1.0, 0.0, 0.0), (0.0, 0.0)), - ((1.0, -1.0, -1.0), (1.0, 0.0, 0.0), (1.0, 0.0)), - ((-1.0, 1.0, -1.0), (0.0, 0.0, -1.0), (1.0, 1.0)), - ((1.0, -1.0, -1.0), (0.0, 0.0, -1.0), (0.0, 0.0)), - ((-1.0, -1.0, -1.0), (0.0, 0.0, -1.0), (1.0, 0.0)), - ((-1.0, 1.0, 1.0), (0.0, 1.0, 0.0), (1.0, 1.0)), - ((-1.0, 1.0, 1.0), (0.0, 0.0, 1.0), (0.0, 1.0)), - ((-1.0, 1.0, -1.0), (-1.0, 0.0, 0.0), (0.0, 1.0)), - ((1.0, -1.0, 1.0), (0.0, -1.0, 0.0), (1.0, 1.0)), - ((1.0, 1.0, 1.0), (1.0, 0.0, 0.0), (0.0, 1.0)), - ((1.0, 1.0, -1.0), (0.0, 0.0, -1.0), (0.0, 1.0)) +normals = [ + (-1.0, 0.0, 0.0), + (0.0, -1.0, 0.0), + (0.0, 0.0, -1.0), + (0.0, 0.0, 1.0), + (0.0, 1.0, 0.0), + (1.0, 0.0, 0.0), ] -faces_by_normal = { - (-1.0, 0.0, 0.0): [6, 7, 8, 6, 20, 7], - (0.0, -1.0, 0.0): [9, 10, 11, 9, 21, 10], - (0.0, 0.0, -1.0): [15, 16, 17, 15, 23, 16], - (0.0, 0.0, 1.0): [3, 4, 5, 3, 19, 4], - (0.0, 1.0, 0.0): [0, 1, 2, 0, 18, 1], - (1.0, 0.0, 0.0): [12, 13, 14, 12, 22, 13] -} - -vertex_buffer = {} - -def add_vertex(vertex): - if vertex in vertex_buffer: - return vertex_buffer[vertex] - else: - index = len(vertex_buffer) - vertex_buffer[vertex] = index - return index - -def emit_face(center_position, block_id, triangles): - for index in triangles: - position, normal, texture = vertex_table[index] - position = vec3_add(vec3_mul(position, 0.5), center_position) - vertex = (position, normal, texture, block_id) - new_index = add_vertex(vertex) - yield new_index - def block_neighbors(level_table, chunk_x, chunk_z, block_index): block_id = level_table[(chunk_x, chunk_z)].blocks[block_index] if block_id == 0: @@ -328,36 +39,65 @@ def block_neighbors(level_table, chunk_x, chunk_z, block_index): return True if nz > 15 or nz < 0: return True - n_block_index = block_index_from_xyz(nx, ny, nz) + n_block_index = mcregion.block_index_from_xyz(nx, ny, nz) key = (n_chunk_x, n_chunk_z) if key not in level_table: return True n_block_id = level_table[key].blocks[n_block_index] return n_block_id != 0 - x, y, z = xyz_from_block_index(block_index) + x, y, z = mcregion.xyz_from_block_index(block_index) - center_position = vec3_add((x, y, z), (chunk_x * 16, 0, chunk_z * 16)) + center_position = vec3.add((x, y, z), (chunk_x * 16, 0, chunk_z * 16)) - for normal, triangles in faces_by_normal.items(): - neighbor = vec3_add(normal, (x, y, z)) - if not neighbor_exists(*neighbor): - yield from emit_face(center_position, block_id, triangles) + def find_non_neighbors(): + for i, normal in enumerate(normals): + neighbor = vec3.add(normal, (x, y, z)) + if not neighbor_exists(*neighbor): + yield i - #yield chunk_x, chunk_z, block_index, block_id - #break + normal_indices = list(find_non_neighbors()) + if normal_indices: + yield center_position, block_id, normal_indices def devoxelize_region(level_table): for chunk_x, chunk_z in level_table.keys(): for block_index in range(128 * 16 * 16): yield from block_neighbors(level_table, chunk_x, chunk_z, block_index) -from collections import defaultdict -counts = defaultdict(int) +def build_level_table(mem, locations): + level_table = {} + for location in locations: + try: + level = mcregion.parse_location(mem, location) + except CountZeroException: + continue + x, z = level.x_pos, level.z_pos + level_table[(x, z)] = level + return level_table -def linearized_vertex_buffer(): - for vertex, i in sorted(vertex_buffer.items(), key=lambda kv: kv[1]): - yield vertex +def normal_indices_as_block_configuration(normal_indices): + acc = 0 + for i in normal_indices: + acc |= (1 << i) + return acc + +def build_block_configuration_table(): + for i in range(64): + indices = [] + for j in range(6): + if ((i >> j) & 1) != 0: + indices.extend(vertex_buffer.faces_by_normal[normals[j]]) + yield indices + +def build_block_instances(f, blocks): + for position, block_id, normal_indices in blocks: + block_configuration = normal_indices_as_block_configuration(normal_indices) + #print(position, block_id, block_configuration) + f.write(struct.pack("I", mem[ix:ix+4]) + chunk_offset = (chunk_location >> 8) & 0xffffff + chunk_sector_count = chunk_location & 0xff + yield chunk_offset, chunk_sector_count + +def parse_locations(mem, offset): + locations = list(_parse_locations(mem, offset)) + return offset + 1024 * 4, locations + +def _parse_timestamps(mem, offset): + for i in range(1024): + ix = offset + i * 4 + timestamp, = struct.unpack(">I", mem[ix:ix+4]) + yield timestamp + +def parse_timestamps(mem, offset): + timestamps = list(_parse_timestamps(mem, offset)) + return offset + 1024 * 4, timestamps + +def print_locations(locations): + for y in range(32): + for x in range(32): + offset, count = locations[y * 32 + x] + print(str(offset).rjust(4), end=' ') + print() + +class CountZeroException(Exception): + pass + +def parse_payload(mem, location): + offset, count = location + if count == 0: + raise CountZeroException() + ix = offset * 4096 + payload = mem[ix:ix + count * 4096] + length, = struct.unpack(">I", payload[0:4]) + assert length <= count * 4096, (length, count) + compression_type = payload[4] + data = payload[5:5 + (length - 1)] + assert compression_type == 2, compression_type + uncompressed = zlib.decompress(data) + return memoryview(uncompressed) + +class TAG: + Byte = 0x01 + Short = 0x02 + Int = 0x03 + Long = 0x04 + Float = 0x05 + Double = 0x06 + ByteArray = 0x07 + String = 0x08 + List = 0x09 + Compound = 0x0a + +@dataclass +class Byte: + name: str + value: int + +@dataclass +class Short: + name: str + value: int + +@dataclass +class Int: + name: str + value: int + +@dataclass +class Long: + name: str + value: int + +@dataclass +class Float: + name: str + value: float + +@dataclass +class Double: + name: str + value: float + +@dataclass +class ByteArray: + name: str + value: bytes + +@dataclass +class String: + name: str + value: str + +@dataclass +class List: + name: str + items: list + +@dataclass +class Compound: + name: str + tags: list + +def indent(level): + return " " * level + +def parse_tag_inner(mem, offset, level, tag_type, name): + payload = mem[offset:] + if tag_type == TAG.Byte: + value, = struct.unpack(">b", payload[0:1]) + return offset + 1, Byte(name, value) + if tag_type == TAG.Short: + value, = struct.unpack(">h", payload[0:2]) + return offset + 2, Short(name, value) + elif tag_type == TAG.Int: + value, = struct.unpack(">i", payload[0:4]) + return offset + 4, Int(name, value) + elif tag_type == TAG.Long: + value, = struct.unpack(">q", payload[0:8]) + return offset + 8, Long(name, value) + elif tag_type == TAG.Float: + value, = struct.unpack(">f", payload[0:4]) + return offset + 4, Float(name, value) + elif tag_type == TAG.Double: + value, = struct.unpack(">d", payload[0:8]) + return offset + 8, Double(name, value) + elif tag_type == TAG.ByteArray: + size, = struct.unpack(">i", payload[0:4]) + value = bytes(payload[4:4+size]) + return offset + 4 + size, ByteArray(name, value) + elif tag_type == TAG.String: + size, = struct.unpack(">H", payload[0:2]) + value = bytes(payload[2:2+size]).decode('utf-8') + return offset + 2 + size, String(name, value) + elif tag_type == TAG.List: + list_content_tag_id, size = struct.unpack(">BI", payload[0:5]) + items = [] + offset = offset + 5 + for i in range(size): + payload = mem[offset:] + offset, item = parse_tag_inner(mem, offset, level, list_content_tag_id, None) + items.append(item) + return offset, List(name, items) + elif tag_type == TAG.Compound: + tags = [] + while payload[0] != 0: + offset, tag = parse_tag(mem, offset, level+1) + payload = mem[offset:] + tags.append(tag) + return offset + 1, Compound(name, tags) + else: + assert False, tag_type + +def parse_tag(mem, offset, level): + data = mem[offset:] + tag_type = data[0] + name_length, = struct.unpack(">H", data[1:3]) + name = bytes(data[3:3+name_length]) + #print(indent(level), tag_type, name_length, name) + offset = offset + 3 + name_length + return parse_tag_inner(mem, offset, level, tag_type, name) + +@dataclass +class Level: + blocks: bytes + data: bytes + sky_light: bytes + block_light: bytes + height_map: bytes + x_pos: int + z_pos: int + +def level_from_tag(tag): + assert type(tag) == Compound + assert tag.name == b'' + assert len(tag.tags) == 1 + level, = tag.tags + assert type(level) == Compound + assert level.name == b'Level' + + name_mapping = { + b'Blocks': 'blocks', + b'Data': 'data', + b'SkyLight': 'sky_light', + b'BlockLight': 'block_light', + b'HeightMap': 'height_map', + b'xPos': 'x_pos', + b'zPos': 'z_pos', + } + + args = {} + + for tag in level.tags: + if tag.name in name_mapping: + arg_name = name_mapping[tag.name] + args[arg_name] = tag.value + + return Level(**args) + +def parse_location(mem, location): + uncompressed = parse_payload(mem, location) + offset, tag = parse_tag(uncompressed, 0, 0) + assert offset == len(uncompressed), (offset, len(uncompressed)) + level = level_from_tag(tag) + return level + +def xyz_from_block_index(block_index): + assert block_index >= 0 and block_index < (128 * 16 * 16) + x = int(block_index / (128 * 16)) + y = int(block_index % 128) + z = int(int(block_index / 128) % 16) + return x, y, z + +def block_index_from_xyz(x, y, z): + assert x >= 0 and x < 16 + assert y >= 0 and y < 128 + assert z >= 0 and z < 16 + return int(y + z * 128 + x * 128 * 16) diff --git a/minecraft/gen/region.idx b/minecraft/gen/region.idx new file mode 100644 index 0000000..a51aa05 Binary files /dev/null and b/minecraft/gen/region.idx differ diff --git a/minecraft/gen/vec3.py b/minecraft/gen/vec3.py new file mode 100644 index 0000000..0377b3f --- /dev/null +++ b/minecraft/gen/vec3.py @@ -0,0 +1,13 @@ +def add(v1, v2): + return ( + v1[0] + v2[0], + v1[1] + v2[1], + v1[2] + v2[2], + ) + +def mul(v, s): + return ( + v[0] * s, + v[1] * s, + v[2] * s, + ) diff --git a/minecraft/gen/vertex_buffer.py b/minecraft/gen/vertex_buffer.py new file mode 100644 index 0000000..c256c55 --- /dev/null +++ b/minecraft/gen/vertex_buffer.py @@ -0,0 +1,64 @@ +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] +} + +vertex_buffer = {} + +def add_vertex(vertex): + if vertex in vertex_buffer: + return vertex_buffer[vertex] + else: + index = len(vertex_buffer) + vertex_buffer[vertex] = index + return index + +def emit_face(center_position, block_id, triangles): + for index in triangles: + position, normal, texture = vertex_table[index] + position = vec3.add(vec3.mul(position, 0.5), center_position) + vertex = (position, normal, texture, block_id) + new_index = add_vertex(vertex) + yield new_index + +def linearized_vertex_buffer(): + for vertex, i in sorted(vertex_buffer.items(), key=lambda kv: kv[1]): + yield vertex + +#with open(data_path + ".vtx", "wb") as f: +# for vertex in linearized_vertex_buffer(): +# vertex = [*vertex[0], *vertex[1], *vertex[2], vertex[3]]#, vertex[3]] +# f.write(struct.pack("