From 2a0d4dd20bc9c445fcae18986f9e2c1e2a326479 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Sun, 25 Jan 2026 00:15:50 -0600 Subject: [PATCH] initial collada metadata data model --- .gitignore | 3 +- collada/{mesh.py => buffer.py} | 143 ++---- collada/generate.py | 42 ++ collada/header.py | 135 ++++++ collada/util.py | 13 + include/collada_types.hpp | 128 +++++ .../curve_interpolation.DAE | 448 ++++++++++++++++++ .../curve_interpolation.max | Bin 0 -> 204800 bytes 8 files changed, 817 insertions(+), 95 deletions(-) rename collada/{mesh.py => buffer.py} (54%) create mode 100644 collada/generate.py create mode 100644 collada/header.py create mode 100644 collada/util.py create mode 100644 include/collada_types.hpp create mode 100755 models/curve_interpolation/curve_interpolation.DAE create mode 100755 models/curve_interpolation/curve_interpolation.max diff --git a/.gitignore b/.gitignore index 83e9c3b..0531c73 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ __pycache__ *.pyc .\#* -\#* \ No newline at end of file +\#* +*.gch \ No newline at end of file diff --git a/collada/mesh.py b/collada/buffer.py similarity index 54% rename from collada/mesh.py rename to collada/buffer.py index 0c64029..0a54780 100644 --- a/collada/mesh.py +++ b/collada/buffer.py @@ -3,23 +3,16 @@ from itertools import chain, islice from collada import parse from collada import types +from collada.util import find_semantics -from prettyprinter import pprint, install_extras -install_extras(include=["dataclasses"]) +def linearize_offset_table(by_offset, p_stride): + for offset in range(p_stride): + for input, source in by_offset[offset]: + yield offset, input, source -def resolve_input_source(collada, source_uri): - return element - -def find_semantics(inputs, semantic): - return [i for i in inputs if i.semantic == semantic] - -def mesh_vertex_buffer(collada, mesh): - semantic_names = ["NORMAL", "TEXCOORD"] - - assert len(mesh.primitive_elements) == 1 - triangles, = mesh.primitive_elements - assert type(triangles) is types.Triangles +mesh_semantic_names = ["NORMAL", "TEXCOORD"] +def build_offset_table(collada, triangles, p_stride): vertex_input, = find_semantics(triangles.inputs, "VERTEX") vertices = collada.lookup(vertex_input.source, types.Vertices) position_input, = find_semantics(vertices.inputs, "POSITION") @@ -29,31 +22,41 @@ def mesh_vertex_buffer(collada, mesh): by_offset = defaultdict(list) by_offset[vertex_input.offset].append((vertex_input, position_source)) - for semantic_name in semantic_names: + for semantic_name in mesh_semantic_names: for input in find_semantics(triangles.inputs, semantic_name): source = collada.lookup(input.source, types.SourceCore) assert type(source.array_element) is types.FloatArray by_offset[input.offset].append((input, source)) - max_offset = max(i.offset for i in triangles.inputs) - p_stride = max_offset + 1 assert len(triangles.p) == triangles.count * 3 * p_stride + used_offsets = [i for i in range(p_stride) if i in by_offset] + linearized_table = linearize_offset_table(by_offset, p_stride) + return list(linearized_table), used_offsets + +def mesh_vertex_index_buffer(collada, mesh): + assert len(mesh.primitive_elements) == 1 + triangles, = mesh.primitive_elements + assert type(triangles) is types.Triangles + + max_offset = max(i.offset for i in triangles.inputs) + p_stride = max_offset + 1 + offset_table, used_offsets = build_offset_table(collada, triangles, p_stride) + ###################################################################### - # generate the index/vertex buffer + # generate the index and vertex buffers ###################################################################### + vertex_buffer_stride = sum( source.technique_common.accessor.stride - for input, source in chain.from_iterable(by_offset.values()) + for offset, input, source in offset_table ) - vertex_table = [] + vertex_index_table = [] index_table = {} next_output_index = 0 index_buffer = [] vertex_buffer = [] - used_offsets = [offset for offset in range(p_stride) if offset in by_offset] - for vertex_ix in range(triangles.count * 3): index_table_key = tuple(triangles.p[vertex_ix * p_stride + offset] for offset in used_offsets) if index_table_key in index_table: @@ -65,75 +68,26 @@ def mesh_vertex_buffer(collada, mesh): next_output_index += 1 # emit vertex attributes for new output index in vertex buffer - for offset in used_offsets: + for offset, input, source in offset_table: p_index = triangles.p[vertex_ix * p_stride + offset] - if offset == vertex_input.offset: - vertex_table.append(p_index) - for input, source in by_offset[offset]: - source_stride = source.technique_common.accessor.stride - source_index = p_index * source_stride - array_slice = source.array_element.floats[source_index:source_index+source_stride] - vertex_buffer.extend(array_slice) + if input.semantic == "VERTEX": + vertex_index_table.append(p_index) - """ - print("{") - for i in range(triangles.count): - print(", ".join(str(index_buffer[i * 3 + j]) for j in range(3)), end=",\n") - print("}") - print("{") - for i in range(len(index_table)): - print(", ".join(str(vertex_buffer[i * vertex_buffer_stride + j]) - for j in range(vertex_buffer_stride)), end=",\n") - print("}") - """ - # vertex table: - # list indices: (output/direct3d) vertex indices - # list values: (input/collada) vertex indices - return vertex_table + source_stride = source.technique_common.accessor.stride + source_index = p_index * source_stride + array_slice = source.array_element.floats[source_index:source_index+source_stride] + vertex_buffer.extend(array_slice) -def filter_tiny(fs, epsilon=0.00001): - return [f if abs(f) > epsilon else 0 for f in fs] + assert len(index_buffer) == triangles.count * 3 + assert len(vertex_buffer) == len(index_table) * vertex_buffer_stride -def matrix_transpose(fs): - return ( - fs[0], fs[4], fs[8], fs[12], - fs[1], fs[5], fs[9], fs[13], - fs[2], fs[6], fs[10], fs[14], - fs[3], fs[7], fs[11], fs[15], - ) + input_source_table = [(input, source) for offset, input, source in offset_table] -def matrix_print(fs): - for i, f in enumerate(fs): - print(f"{f:5.01f}f", end=", ") - if i % 4 == 3: - print() + # vertex_index_table: input/collada vertex indices in the order written to the index buffer + # input_source_table: (input, source) in the order written to the vertex buffer + return vertex_buffer, index_buffer, vertex_index_table, input_source_table -def skin_vertex_buffer(collada, skin, vertex_table): - inverse_bind_matrix_input, = find_semantics(skin.joints.inputs, "INV_BIND_MATRIX") - inverse_bind_matrix_source = collada.lookup(inverse_bind_matrix_input.source, types.SourceCore) - stride = inverse_bind_matrix_source.technique_common.accessor.stride - count = inverse_bind_matrix_source.technique_common.accessor.count - array = inverse_bind_matrix_source.array_element - assert type(inverse_bind_matrix_source.array_element) == types.FloatArray - assert stride == 16 - assert array.count == count * stride - - # enable to improve inverse bind matrix human-readability - #floats = filter_tiny(array.floats) - - inverse_bind_matrices = [] - print("static const float inverse_bind_matrices[] = {") - for i in range(count): - offset = stride * i - matrix = matrix_transpose(floats[offset:offset+stride]) - matrix_print(matrix) - if i + 1 < count: - print() - print("};") - - ###################################################################### - # vertex weights - ###################################################################### +def skin_vertex_buffer(collada, skin, vertex_index_table): max_offset = max(i.offset for i in skin.vertex_weights.inputs) weights_input, = find_semantics(skin.vertex_weights.inputs, "WEIGHT") weights_source = collada.lookup(weights_input.source, types.SourceCore) @@ -152,14 +106,19 @@ def skin_vertex_buffer(collada, skin, vertex_table): for vi in range(vcount): joint_index = skin.vertex_weights.v[v_offset + joints_input.offset] weight_index = skin.vertex_weights.v[v_offset + weights_input.offset] - pprint(weights_source) weight = weights_source.array_element.floats[weight_index] influences.append((joint_index, weight)) v_offset += v_stride + assert len(influences) <= 4, len(influences) vertex_influences.append(influences) + ###################################################################### + # generate the joint/weight buffer + ###################################################################### + vertex_buffer = [] - for vertex_index in vertex_table: + # vertex_index_table: input/collada vertex indices in the order written to the index buffer + for vertex_index in vertex_index_table: influences = vertex_influences[vertex_index] def emit(column): for i in range(4): @@ -169,11 +128,7 @@ def skin_vertex_buffer(collada, skin, vertex_table): vertex_buffer.append(influences[i][column]) emit(0) # emit joint int4 emit(1) # emit weight float4 - - for i, v in enumerate(vertex_buffer): - print(v, end=", ") - if i % 8 == 7: - print() + return vertex_buffer if __name__ == "__main__": import sys @@ -181,8 +136,8 @@ if __name__ == "__main__": mesh = collada.library_geometries[0].geometries[0].geometric_element assert type(mesh) is types.Mesh - vertex_table = mesh_vertex_buffer(collada, mesh) + vertex_buffer_pnt, index_buffer, vertex_index_table, input_source_table = mesh_vertex_index_buffer(collada, mesh) skin = collada.library_controllers[0].controllers[0].control_element assert type(skin) is types.Skin - skin_vertex_buffer(collada, skin, vertex_table) + vertex_buffer_jw = skin_vertex_buffer(collada, skin, vertex_index_table) diff --git a/collada/generate.py b/collada/generate.py new file mode 100644 index 0000000..9cb9f43 --- /dev/null +++ b/collada/generate.py @@ -0,0 +1,42 @@ +import io + +def should_autonewline(line): + return ( + "static_assert" not in line + and "extern" not in line + and (len(line.split()) < 2 or line.split()[1] != '=') # hacky; meh + ) + +def _render(out, lines): + indent = " " + level = 0 + namespace = 0 + for l in lines: + if l and (l[0] == "}" or l[0] == ")"): + level -= 2 + if level < 0: + assert namespace >= 0 + namespace -= 1 + level = 0 + + if len(l) == 0: + out.write("\n") + else: + out.write(indent * level + l + "\n") + + if l and (l[-1] == "{" or l[-1] == "("): + if l.startswith("namespace"): + namespace += 1 + else: + level += 2 + + if level == 0 and l and l[-1] == ";": + if should_autonewline(l): + out.write("\n") + return out + +def renderer(): + out = io.StringIO() + def render(lines): + return _render(out, lines) + return render, out diff --git a/collada/header.py b/collada/header.py new file mode 100644 index 0000000..b2b5a86 --- /dev/null +++ b/collada/header.py @@ -0,0 +1,135 @@ +from typing import Dict, List + +from dataclasses import dataclass +from itertools import islice +from io import BytesIO +import struct + +from collada.util import matrix_transpose, find_semantics +from collada import parse +from collada import types +from collada.generate import renderer +from collada import buffer + +from prettyprinter import pprint, install_extras +install_extras(include=["dataclasses"]) + +@dataclass +class State: + # arbitrary binary data, including vertex buffers and index + # buffers + buf: BytesIO + + # geometry__indices: + # keys: collada id + # values: the index the geometry was placed at in the C++ geometries[] array + geometry__indices: Dict[str, int] + + # geometry__vertex_index_tables: + # keys: collada id + # values: vertex_index_table (see buffer.py) + geometry__vertex_index_tables: Dict[str, List[int]] + + def __init__(self): + self.buf = BytesIO() + self.geometry__indices = {} + self.geometry__vertex_index_tables = {} + +def render_matrix(fs): + fsi = iter(fs) + for i in range(4): + s = ", ".join(f"{f}f" for f in islice(fsi, 4)) + yield f"{s}," + +def render_inverse_bind_matrix(collada, skin): + inverse_bind_matrix_input, = find_semantics(skin.joints.inputs, "INV_BIND_MATRIX") + inverse_bind_matrix_source = collada.lookup(inverse_bind_matrix_input.source, types.SourceCore) + stride = inverse_bind_matrix_source.technique_common.accessor.stride + count = inverse_bind_matrix_source.technique_common.accessor.count + array = inverse_bind_matrix_source.array_element + assert type(inverse_bind_matrix_source.array_element) == types.FloatArray + assert stride == 16 + assert array.count == count * stride + + inverse_bind_matrices = [] + yield "static const float inverse_bind_matrices[] = {" + for i in range(count): + offset = stride * i + matrix = matrix_transpose(array.floats[offset:offset+stride]) + yield from render_matrix(matrix) + yield "};" + +def renderbin(f, elems, t): + fmt = { + float: ' epsilon else 0 for f in fs] + +def matrix_transpose(fs): + return ( + fs[0], fs[4], fs[8], fs[12], + fs[1], fs[5], fs[9], fs[13], + fs[2], fs[6], fs[10], fs[14], + fs[3], fs[7], fs[11], fs[15], + ) diff --git a/include/collada_types.hpp b/include/collada_types.hpp new file mode 100644 index 0000000..1a9a55b --- /dev/null +++ b/include/collada_types.hpp @@ -0,0 +1,128 @@ +#pragma once + +namespace collada { + + struct float3 { + float const x; + float const y; + float const z; + }; + + struct float4 { + float const x; + float const y; + float const z; + float const w; + }; + + ////////////////////////////////////////////////////////////////////// + // animation + ////////////////////////////////////////////////////////////////////// + + enum class interpolation { + LINEAR, + BEZIER, + }; + + struct source { + union { + float const * const float_array; + enum interpolation const name_array; + }; + int const count; + int const stride; + }; + + struct sampler { + source const input; + source const output; + source const intangent; + source const outangent; + source const interpolation; + }; + + ////////////////////////////////////////////////////////////////////// + // geometry + ////////////////////////////////////////////////////////////////////// + + enum class input_format { + FLOAT3, + FLOAT4, + INT4, + }; + + struct input_element { + char const * const semantic; + int const semantic_index; + enum input_format const format; + }; + + struct mesh { + input_element const * const input_elements; + int const input_elements_count; + + int const vertex_buffer_offset; + int const vertex_buffer_size; + + int const index_buffer_offset; + int const index_buffer_size; + }; + + struct geometry { + mesh mesh; + }; + + ////////////////////////////////////////////////////////////////////// + // node + ////////////////////////////////////////////////////////////////////// + + struct lookat { + float3 const eye; + float3 const at; + float3 const up; + }; + + struct matrix { + float const _11, _12, _13, _14; + float const _21, _22, _23, _24; + float const _31, _32, _33, _34; + float const _41, _42, _43, _44; + }; + + enum class transform_type { + LOOKAT, + MATRIX, + ROTATE, + SCALE, + TRANSLATE, + }; + + struct transform { + transform_type const type; + union { + lookat const lookat; + matrix const matrix; + float4 const rotate; + float3 const scale; + float3 const translate; + }; + }; + + enum class node_type { + JOINT, + NODE, + }; + + struct node { + node_type const type; + + transform const * const transforms; + int const transforms_count; + + geometry const * const geometries; + int const geometries_count; + + node const * const nodes; + int const nodes_count; + }; +} diff --git a/models/curve_interpolation/curve_interpolation.DAE b/models/curve_interpolation/curve_interpolation.DAE new file mode 100755 index 0000000..c3b7b21 --- /dev/null +++ b/models/curve_interpolation/curve_interpolation.DAE @@ -0,0 +1,448 @@ + + + + + bilbo + OpenCOLLADA for 3ds Max; Version: 1.6; Revision: 68 + file:///C:/cygwin/home/bilbo/d3d10/models/curve_interpolation/curve_interpolation.max + + 2026-01-24T21:02:20 + 2026-01-24T21:02:20 + + Z_UP + + + + + + + + 0.1019608 0.6941176 0.1019608 1 + + + 0.1019608 0.6941176 0.1019608 1 + + + 1 1 1 1 + + + 10 + + + 0 0 0 1 + + + 1 1 1 1 + + + 1 + + + + + + + + + + + 0.8980392 0.6039216 0.8431373 1 + + + 0.8980392 0.6039216 0.8431373 1 + + + 1 1 1 1 + + + 10 + + + 0 0 0 1 + + + 1 1 1 1 + + + 1 + + + + + + + + + + + 0.1098039 0.5843137 0.6941176 1 + + + 0.1098039 0.5843137 0.6941176 1 + + + 1 1 1 1 + + + 10 + + + 0 0 0 1 + + + 1 1 1 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + -0.5 -0.5 0 0.5 -0.5 0 -0.5 0.5 0 0.5 0.5 0 -0.5 -0.5 1 0.5 -0.5 1 -0.5 0.5 1 0.5 0.5 1 + + + + + + + + + + 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 1 0 0 1 0 0 1 0 0 1 0 -1 0 0 -1 0 0 -1 0 0 -1 0 1 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 1 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 + + + + + + + + + + 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 1 0 1 1 0 + + + + + + + + + + + + + + + +

0 0 9 2 1 11 3 2 10 3 2 10 1 3 8 0 0 9 4 4 8 5 5 9 7 6 11 7 6 11 6 7 10 4 4 8 0 8 4 1 9 5 5 10 7 5 10 7 4 11 6 0 8 4 1 12 0 3 13 1 7 14 3 7 14 3 5 15 2 1 12 0 3 16 4 2 17 5 6 18 7 6 18 7 7 19 6 3 16 4 2 20 0 0 21 1 4 22 3 4 22 3 6 23 2 2 20 0

+
+
+ + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + +
+ + + + 10 0 0 9.807853 1.950903 0 9.238795 3.826834 0 8.314696 5.555702 0 7.071068 7.071068 0 5.555702 8.314696 0 3.826834 9.238795 0 1.950904 9.807853 0 7.54979e-7 10 0 -1.950902 9.807853 0 -3.826833 9.238796 0 -5.555702 8.314696 0 -7.071068 7.071068 0 -8.314696 5.555702 0 -9.238796 3.826833 0 -9.807854 1.950901 0 -10 -3.25841e-6 0 -9.807852 -1.950907 0 -9.238793 -3.826839 0 -8.314693 -5.555707 0 -7.071063 -7.071073 0 -5.555696 -8.3147 0 -3.826827 -9.238798 0 -1.950894 -9.807855 0 9.65599e-6 -10 0 1.950913 -9.807851 0 3.826845 -9.238791 0 5.555712 -8.31469 0 7.071077 -7.071059 0 8.314704 -5.555691 0 9.238801 -3.826821 0 9.807856 -1.950888 0 + + + + + + + + + + 0 0 1 0 0 1 0 0 1 0 0 0.9999999 0 0 1 0 0 0.9999999 0 0 1 0 0 0.9999999 0 0 1 0 0 1 0 0 0.9999999 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0.9999999 0 0 1 0 0 0.9999999 0 0 1 0 0 0.9999999 0 0 0.9999999 0 0 0.9999999 0 0 1 0 0 0.9999999 0 0 1 0 0 1 0 0 0.9999999 0 0 1 0 0 1 0 0 0.9999999 0 0 0.9999999 0 0 1 + + + + + + + + + + 0.5975444 0.9903928 -0.25 0.691341 0.9619401 -0.25 0.7777845 0.9157352 -0.25 0.8535529 0.8535539 -0.25 0.9157345 0.7777857 -0.25 0.9619396 0.6913422 -0.25 0.9903926 0.5975457 -0.25 1 0.5000005 -0.25 0.9903927 0.4024553 -0.25 0.9619399 0.3086587 -0.25 0.915735 0.2222152 -0.25 0.8535537 0.1464469 -0.25 0.7777854 0.08426532 -0.25 0.6913419 0.03806034 -0.25 0.5975454 0.009607404 -0.25 0.5000002 0 -0.25 0.402455 0.009607315 -0.25 0.3086584 0.03806019 -0.25 0.2222149 0.08426517 -0.25 0.1464466 0.1464466 -0.25 0.08426517 0.2222149 -0.25 0.03806019 0.3086583 -0.25 0.009607345 0.4024549 -0.25 0 0.5 -0.25 0.009607345 0.5975451 -0.25 0.03806022 0.6913417 -0.25 0.08426517 0.7777851 -0.25 0.1464466 0.8535534 -0.25 0.2222148 0.9157348 -0.25 0.3086582 0.9619398 -0.25 0.4024548 0.9903927 -0.25 0.5 1 -0.25 + + + + + + + + + + + + + + + +

0 0 31 1 1 30 2 2 29 2 2 29 3 3 28 4 4 27 0 0 31 2 2 29 4 4 27 4 4 27 5 5 26 6 6 25 6 6 25 7 7 24 8 8 23 4 4 27 6 6 25 8 8 23 0 0 31 4 4 27 8 8 23 8 8 23 9 9 22 10 10 21 10 10 21 11 11 20 12 12 19 8 8 23 10 10 21 12 12 19 12 12 19 13 13 18 14 14 17 14 14 17 15 15 16 16 16 15 12 12 19 14 14 17 16 16 15 8 8 23 12 12 19 16 16 15 0 0 31 8 8 23 16 16 15 16 16 15 17 17 14 18 18 13 18 18 13 19 19 12 20 20 11 16 16 15 18 18 13 20 20 11 20 20 11 21 21 10 22 22 9 22 22 9 23 23 8 24 24 7 20 20 11 22 22 9 24 24 7 16 16 15 20 20 11 24 24 7 0 0 31 16 16 15 24 24 7 24 24 7 25 25 6 26 26 5 26 26 5 27 27 4 28 28 3 24 24 7 26 26 5 28 28 3 0 0 31 24 24 7 28 28 3 28 28 3 29 29 2 30 30 1 0 0 31 28 28 3 30 30 1 31 31 0 0 0 31 30 30 1

+
+
+
+ + + + -7.071065 -7.071065 0 7.071065 -7.071065 0 -7.071065 7.071065 0 7.071065 7.071065 0 + + + + + + + + + + 0 0 1 0 0 0.9999999 0 0 0.9999999 0 0 1 + + + + + + + + + + 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 0 + + + + + + + + + + + + + + + +

2 0 6 0 1 4 3 2 7 1 3 5 3 2 7 0 1 4

+
+
+ + + + 14.14213 + 14.14213 + 1 + 1 + 1 + 1 + 1 + + + +
+
+ + + + + 0 0 0 + + + + + + + + + + + 10 0 0 + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + 0 0 0.01 + 0 0 -1 -44.99999 + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + + 0 1.666667 3.333333 5 + + + + + + + + 10 -10 10 -10 + + + + + + + + -0.3332306 10 1.111167 -10 2.778333 10 4.4445 -9.219337 + + + + + + + + + 0.5555 10 2.222167 -10 3.888333 10 4.000208 -8.594958 + + + + + + + + + BEZIER BEZIER BEZIER BEZIER + + + + + + + + -0.8333334 0.8333334 2.5 4.166667 + + + + + + + + -10.05776 10.05852 -9.941484 10.05852 + + + + + + + + -1.166264 -10.05776 0.2778334 10.05852 1.9445 -9.941484 3.611667 10.05852 + + + + + + + + + -0.2783333 -10.05776 1.388833 10.05852 3.0555 -9.941484 4.499598 10.05852 + + + + + + + + + BEZIER BEZIER BEZIER BEZIER + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/models/curve_interpolation/curve_interpolation.max b/models/curve_interpolation/curve_interpolation.max new file mode 100755 index 0000000000000000000000000000000000000000..3e709d6a65ae0c418a852d0f69d8f96d3082de56 GIT binary patch literal 204800 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%0DfF&aDf+la?V3(>(?x*lCcGN6Rkn;@hf!2d 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 literal 0 HcmV?d00001