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 0000000..3e709d6 Binary files /dev/null and b/models/curve_interpolation/curve_interpolation.max differ