initial collada metadata data model

This commit is contained in:
Zack Buhman 2026-01-25 00:15:50 -06:00
parent 27824a0de1
commit 2a0d4dd20b
8 changed files with 817 additions and 95 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ __pycache__
*.pyc *.pyc
.\#* .\#*
\#* \#*
*.gch

View File

@ -3,23 +3,16 @@ from itertools import chain, islice
from collada import parse from collada import parse
from collada import types from collada import types
from collada.util import find_semantics
from prettyprinter import pprint, install_extras def linearize_offset_table(by_offset, p_stride):
install_extras(include=["dataclasses"]) for offset in range(p_stride):
for input, source in by_offset[offset]:
yield offset, input, source
def resolve_input_source(collada, source_uri): mesh_semantic_names = ["NORMAL", "TEXCOORD"]
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
def build_offset_table(collada, triangles, p_stride):
vertex_input, = find_semantics(triangles.inputs, "VERTEX") vertex_input, = find_semantics(triangles.inputs, "VERTEX")
vertices = collada.lookup(vertex_input.source, types.Vertices) vertices = collada.lookup(vertex_input.source, types.Vertices)
position_input, = find_semantics(vertices.inputs, "POSITION") position_input, = find_semantics(vertices.inputs, "POSITION")
@ -29,31 +22,41 @@ def mesh_vertex_buffer(collada, mesh):
by_offset = defaultdict(list) by_offset = defaultdict(list)
by_offset[vertex_input.offset].append((vertex_input, position_source)) 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): for input in find_semantics(triangles.inputs, semantic_name):
source = collada.lookup(input.source, types.SourceCore) source = collada.lookup(input.source, types.SourceCore)
assert type(source.array_element) is types.FloatArray assert type(source.array_element) is types.FloatArray
by_offset[input.offset].append((input, source)) 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 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( vertex_buffer_stride = sum(
source.technique_common.accessor.stride 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 = {} index_table = {}
next_output_index = 0 next_output_index = 0
index_buffer = [] index_buffer = []
vertex_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): for vertex_ix in range(triangles.count * 3):
index_table_key = tuple(triangles.p[vertex_ix * p_stride + offset] for offset in used_offsets) index_table_key = tuple(triangles.p[vertex_ix * p_stride + offset] for offset in used_offsets)
if index_table_key in index_table: if index_table_key in index_table:
@ -65,75 +68,26 @@ def mesh_vertex_buffer(collada, mesh):
next_output_index += 1 next_output_index += 1
# emit vertex attributes for new output index in vertex buffer # 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] p_index = triangles.p[vertex_ix * p_stride + offset]
if offset == vertex_input.offset: if input.semantic == "VERTEX":
vertex_table.append(p_index) vertex_index_table.append(p_index)
for input, source in by_offset[offset]:
source_stride = source.technique_common.accessor.stride source_stride = source.technique_common.accessor.stride
source_index = p_index * source_stride source_index = p_index * source_stride
array_slice = source.array_element.floats[source_index:source_index+source_stride] array_slice = source.array_element.floats[source_index:source_index+source_stride]
vertex_buffer.extend(array_slice) vertex_buffer.extend(array_slice)
""" assert len(index_buffer) == triangles.count * 3
print("{") assert len(vertex_buffer) == len(index_table) * vertex_buffer_stride
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
def filter_tiny(fs, epsilon=0.00001): input_source_table = [(input, source) for offset, input, source in offset_table]
return [f if abs(f) > epsilon else 0 for f in fs]
def matrix_transpose(fs): # vertex_index_table: input/collada vertex indices in the order written to the index buffer
return ( # input_source_table: (input, source) in the order written to the vertex buffer
fs[0], fs[4], fs[8], fs[12], return vertex_buffer, index_buffer, vertex_index_table, input_source_table
fs[1], fs[5], fs[9], fs[13],
fs[2], fs[6], fs[10], fs[14],
fs[3], fs[7], fs[11], fs[15],
)
def matrix_print(fs): def skin_vertex_buffer(collada, skin, vertex_index_table):
for i, f in enumerate(fs):
print(f"{f:5.01f}f", end=", ")
if i % 4 == 3:
print()
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
######################################################################
max_offset = max(i.offset for i in skin.vertex_weights.inputs) max_offset = max(i.offset for i in skin.vertex_weights.inputs)
weights_input, = find_semantics(skin.vertex_weights.inputs, "WEIGHT") weights_input, = find_semantics(skin.vertex_weights.inputs, "WEIGHT")
weights_source = collada.lookup(weights_input.source, types.SourceCore) 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): for vi in range(vcount):
joint_index = skin.vertex_weights.v[v_offset + joints_input.offset] joint_index = skin.vertex_weights.v[v_offset + joints_input.offset]
weight_index = skin.vertex_weights.v[v_offset + weights_input.offset] weight_index = skin.vertex_weights.v[v_offset + weights_input.offset]
pprint(weights_source)
weight = weights_source.array_element.floats[weight_index] weight = weights_source.array_element.floats[weight_index]
influences.append((joint_index, weight)) influences.append((joint_index, weight))
v_offset += v_stride v_offset += v_stride
assert len(influences) <= 4, len(influences)
vertex_influences.append(influences) vertex_influences.append(influences)
######################################################################
# generate the joint/weight buffer
######################################################################
vertex_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] influences = vertex_influences[vertex_index]
def emit(column): def emit(column):
for i in range(4): for i in range(4):
@ -169,11 +128,7 @@ def skin_vertex_buffer(collada, skin, vertex_table):
vertex_buffer.append(influences[i][column]) vertex_buffer.append(influences[i][column])
emit(0) # emit joint int4 emit(0) # emit joint int4
emit(1) # emit weight float4 emit(1) # emit weight float4
return vertex_buffer
for i, v in enumerate(vertex_buffer):
print(v, end=", ")
if i % 8 == 7:
print()
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
@ -181,8 +136,8 @@ if __name__ == "__main__":
mesh = collada.library_geometries[0].geometries[0].geometric_element mesh = collada.library_geometries[0].geometries[0].geometric_element
assert type(mesh) is types.Mesh 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 skin = collada.library_controllers[0].controllers[0].control_element
assert type(skin) is types.Skin assert type(skin) is types.Skin
skin_vertex_buffer(collada, skin, vertex_table) vertex_buffer_jw = skin_vertex_buffer(collada, skin, vertex_index_table)

42
collada/generate.py Normal file
View File

@ -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

135
collada/header.py Normal file
View File

@ -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 <geometry> 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 <geometry> 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: '<f',
int: '<i',
}[t]
for e in elems:
assert type(e) is t, (e, elems)
f.write(struct.pack(fmt, e))
def render_input_elements(input_source_table):
for input, source in input_source_table:
semantic = input.semantic if input.semantic != "VERTEX" else "POSITION"
semantic_index = 0 if input.set is None else input.set
assert all(param.type == 'float' for param in source.technique_common.accessor.params)
assert type(source.technique_common.accessor.stride) is int
stride = source.technique_common.accessor.stride
yield "{"
yield f'.semantic = "{semantic}",'
yield f".semantic_index = {semantic_index},"
yield f".format = input_format::FLOAT{stride},"
yield "},"
def render_geometry(state, collada, geometry):
mesh = geometry.geometric_element
assert type(mesh) is types.Mesh
vertex_buffer, index_buffer, vertex_index_table, input_source_table = buffer.mesh_vertex_index_buffer(collada, mesh)
vertex_buffer_offset = state.buf.tell()
renderbin(state.buf, vertex_buffer, float)
vertex_buffer_size = state.buf.tell() - vertex_buffer_offset
index_buffer_offset = state.buf.tell()
renderbin(state.buf, index_buffer, int)
index_buffer_size = state.buf.tell() - index_buffer_offset
state.geometry__vertex_index_tables[geometry.id] = vertex_index_table
yield "{"
yield ".mesh = {"
yield f".input_elements = {{"
yield from render_input_elements(input_source_table)
yield "},"
yield f".input_elements_count = {len(input_source_table)},"
yield ""
yield f".vertex_buffer_offset = {vertex_buffer_offset},"
yield f".vertex_buffer_size = {vertex_buffer_size},"
yield ""
yield f".index_buffer_offset = {index_buffer_offset},"
yield f".index_buffer_size = {index_buffer_size},"
yield "}"
yield "},"
def render_library_geometries(state, collada):
yield "geometry const geometries[] = {"
next_index = 0
for library_geometries in collada.library_geometries:
for geometry in library_geometries.geometries:
assert geometry.id is not None
state.geometry__indices[geometry.id] = next_index
next_index += 1
yield from render_geometry(state, collada, geometry)
yield "};"
if __name__ == "__main__":
import sys
collada = parse.parse_collada_file(sys.argv[1])
#skin = collada.library_controllers[0].controllers[0].control_element
#assert type(skin) is types.Skin
#foo = render_inverse_bind_matrix(collada, skin)
state = State()
render, out = renderer()
render(render_library_geometries(state, collada))
print(out.getvalue())

13
collada/util.py Normal file
View File

@ -0,0 +1,13 @@
def find_semantics(inputs, semantic):
return [i for i in inputs if i.semantic == semantic]
def filter_tiny(fs, epsilon=0.00001):
return [f if abs(f) > 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],
)

128
include/collada_types.hpp Normal file
View File

@ -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;
};
}

View File

@ -0,0 +1,448 @@
<?xml version="1.0" encoding="utf-8"?>
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<asset>
<contributor>
<author>bilbo</author>
<authoring_tool>OpenCOLLADA for 3ds Max; Version: 1.6; Revision: 68</authoring_tool>
<source_data>file:///C:/cygwin/home/bilbo/d3d10/models/curve_interpolation/curve_interpolation.max</source_data>
</contributor>
<created>2026-01-24T21:02:20</created>
<modified>2026-01-24T21:02:20</modified>
<unit name="inch" meter="0.0254"/>
<up_axis>Z_UP</up_axis>
</asset>
<library_effects>
<effect id="ColorEffectR26G177B26">
<profile_COMMON>
<technique sid="common">
<phong>
<ambient>
<color>0.1019608 0.6941176 0.1019608 1</color>
</ambient>
<diffuse>
<color>0.1019608 0.6941176 0.1019608 1</color>
</diffuse>
<specular>
<color>1 1 1 1</color>
</specular>
<shininess>
<float>10</float>
</shininess>
<reflective>
<color>0 0 0 1</color>
</reflective>
<transparent>
<color>1 1 1 1</color>
</transparent>
<transparency>
<float>1</float>
</transparency>
</phong>
</technique>
</profile_COMMON>
</effect>
<effect id="ColorEffectR229G154B215">
<profile_COMMON>
<technique sid="common">
<phong>
<ambient>
<color>0.8980392 0.6039216 0.8431373 1</color>
</ambient>
<diffuse>
<color>0.8980392 0.6039216 0.8431373 1</color>
</diffuse>
<specular>
<color>1 1 1 1</color>
</specular>
<shininess>
<float>10</float>
</shininess>
<reflective>
<color>0 0 0 1</color>
</reflective>
<transparent>
<color>1 1 1 1</color>
</transparent>
<transparency>
<float>1</float>
</transparency>
</phong>
</technique>
</profile_COMMON>
</effect>
<effect id="ColorEffectR28G149B177">
<profile_COMMON>
<technique sid="common">
<phong>
<ambient>
<color>0.1098039 0.5843137 0.6941176 1</color>
</ambient>
<diffuse>
<color>0.1098039 0.5843137 0.6941176 1</color>
</diffuse>
<specular>
<color>1 1 1 1</color>
</specular>
<shininess>
<float>10</float>
</shininess>
<reflective>
<color>0 0 0 1</color>
</reflective>
<transparent>
<color>1 1 1 1</color>
</transparent>
<transparency>
<float>1</float>
</transparency>
</phong>
</technique>
</profile_COMMON>
</effect>
</library_effects>
<library_materials>
<material id="ColorEffectR26G177B26-material" name="ColorEffectR26G177B26-material">
<instance_effect url="#ColorEffectR26G177B26"/>
</material>
<material id="ColorEffectR229G154B215-material" name="ColorEffectR229G154B215-material">
<instance_effect url="#ColorEffectR229G154B215"/>
</material>
<material id="ColorEffectR28G149B177-material" name="ColorEffectR28G149B177-material">
<instance_effect url="#ColorEffectR28G149B177"/>
</material>
</library_materials>
<library_geometries>
<geometry id="geom-Cube" name="Cube">
<mesh>
<source id="geom-Cube-positions">
<float_array id="geom-Cube-positions-array" count="24">-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</float_array>
<technique_common>
<accessor source="#geom-Cube-positions-array" count="8" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geom-Cube-normals">
<float_array id="geom-Cube-normals-array" count="72">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</float_array>
<technique_common>
<accessor source="#geom-Cube-normals-array" count="24" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geom-Cube-map1">
<float_array id="geom-Cube-map1-array" count="36">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</float_array>
<technique_common>
<accessor source="#geom-Cube-map1-array" count="12" stride="3">
<param name="S" type="float"/>
<param name="T" type="float"/>
<param name="P" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="geom-Cube-vertices">
<input semantic="POSITION" source="#geom-Cube-positions"/>
</vertices>
<triangles material="ColorMaterial" count="12">
<input semantic="VERTEX" source="#geom-Cube-vertices" offset="0"/>
<input semantic="NORMAL" source="#geom-Cube-normals" offset="1"/>
<input semantic="TEXCOORD" source="#geom-Cube-map1" offset="2" set="0"/>
<p>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</p>
</triangles>
</mesh>
<extra>
<technique profile="OpenCOLLADA3dsMax">
<max_box>
<length sid="length" type="float">1</length>
<width sid="width" type="float">1</width>
<height sid="height" type="float">1</height>
<widthsegments sid="widthsegments" type="int">1</widthsegments>
<lengthsegments sid="lengthsegments" type="int">1</lengthsegments>
<heightsegments sid="heightsegments" type="int">1</heightsegments>
<generateuvs sid="generateuvs" type="int">1</generateuvs>
</max_box>
</technique>
</extra>
</geometry>
<geometry id="geom-Cylinder001" name="Cylinder001">
<mesh>
<source id="geom-Cylinder001-positions">
<float_array id="geom-Cylinder001-positions-array" count="96">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</float_array>
<technique_common>
<accessor source="#geom-Cylinder001-positions-array" count="32" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geom-Cylinder001-normals">
<float_array id="geom-Cylinder001-normals-array" count="96">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</float_array>
<technique_common>
<accessor source="#geom-Cylinder001-normals-array" count="32" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geom-Cylinder001-map1">
<float_array id="geom-Cylinder001-map1-array" count="96">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</float_array>
<technique_common>
<accessor source="#geom-Cylinder001-map1-array" count="32" stride="3">
<param name="S" type="float"/>
<param name="T" type="float"/>
<param name="P" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="geom-Cylinder001-vertices">
<input semantic="POSITION" source="#geom-Cylinder001-positions"/>
</vertices>
<triangles material="ColorMaterial" count="30">
<input semantic="VERTEX" source="#geom-Cylinder001-vertices" offset="0"/>
<input semantic="NORMAL" source="#geom-Cylinder001-normals" offset="1"/>
<input semantic="TEXCOORD" source="#geom-Cylinder001-map1" offset="2" set="0"/>
<p>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</p>
</triangles>
</mesh>
</geometry>
<geometry id="geom-Plane001" name="Plane001">
<mesh>
<source id="geom-Plane001-positions">
<float_array id="geom-Plane001-positions-array" count="12">-7.071065 -7.071065 0 7.071065 -7.071065 0 -7.071065 7.071065 0 7.071065 7.071065 0</float_array>
<technique_common>
<accessor source="#geom-Plane001-positions-array" count="4" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geom-Plane001-normals">
<float_array id="geom-Plane001-normals-array" count="12">0 0 1 0 0 0.9999999 0 0 0.9999999 0 0 1</float_array>
<technique_common>
<accessor source="#geom-Plane001-normals-array" count="4" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geom-Plane001-map1">
<float_array id="geom-Plane001-map1-array" count="24">0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 0</float_array>
<technique_common>
<accessor source="#geom-Plane001-map1-array" count="8" stride="3">
<param name="S" type="float"/>
<param name="T" type="float"/>
<param name="P" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="geom-Plane001-vertices">
<input semantic="POSITION" source="#geom-Plane001-positions"/>
</vertices>
<triangles material="ColorMaterial" count="2">
<input semantic="VERTEX" source="#geom-Plane001-vertices" offset="0"/>
<input semantic="NORMAL" source="#geom-Plane001-normals" offset="1"/>
<input semantic="TEXCOORD" source="#geom-Plane001-map1" offset="2" set="0"/>
<p>2 0 6 0 1 4 3 2 7 1 3 5 3 2 7 0 1 4</p>
</triangles>
</mesh>
<extra>
<technique profile="OpenCOLLADA3dsMax">
<max_plane>
<lenght sid="lenght" type="float">14.14213</lenght>
<width sid="width" type="float">14.14213</width>
<widthsegments sid="widthsegments" type="int">1</widthsegments>
<lenghtsegments sid="lenghtsegments" type="int">1</lenghtsegments>
<density sid="density" type="float">1</density>
<scale sid="scale" type="float">1</scale>
<generateuvs sid="generateuvs" type="bool">1</generateuvs>
</max_plane>
</technique>
</extra>
</geometry>
</library_geometries>
<library_lights>
<light id="EnvironmentAmbientLight" name="EnvironmentAmbientLight">
<technique_common>
<ambient>
<color>0 0 0</color>
</ambient>
</technique_common>
</light>
</library_lights>
<library_visual_scenes>
<visual_scene id="MaxScene">
<node name="EnvironmentAmbientLight">
<instance_light url="#EnvironmentAmbientLight"/>
</node>
<node id="node-Cube" name="Cube">
<translate sid="translation">10 0 0</translate>
<instance_geometry url="#geom-Cube">
<bind_material>
<technique_common>
<instance_material symbol="ColorMaterial" target="#ColorEffectR26G177B26-material"/>
</technique_common>
</bind_material>
</instance_geometry>
<extra>
<technique profile="OpenCOLLADA">
<cast_shadows sid="cast_shadows" type="bool">1</cast_shadows>
<receive_shadows sid="receive_shadows" type="bool">1</receive_shadows>
<primary_visibility sid="primary_visibility" type="int">1</primary_visibility>
<secondary_visibility sid="secondary_visibility" type="int">1</secondary_visibility>
</technique>
</extra>
</node>
<node id="node-Cylinder001" name="Cylinder001">
<instance_geometry url="#geom-Cylinder001">
<bind_material>
<technique_common>
<instance_material symbol="ColorMaterial" target="#ColorEffectR229G154B215-material"/>
</technique_common>
</bind_material>
</instance_geometry>
<extra>
<technique profile="OpenCOLLADA">
<cast_shadows sid="cast_shadows" type="bool">1</cast_shadows>
<receive_shadows sid="receive_shadows" type="bool">1</receive_shadows>
<primary_visibility sid="primary_visibility" type="int">1</primary_visibility>
<secondary_visibility sid="secondary_visibility" type="int">1</secondary_visibility>
</technique>
</extra>
</node>
<node id="node-Plane001" name="Plane001">
<translate>0 0 0.01</translate>
<rotate>0 0 -1 -44.99999</rotate>
<instance_geometry url="#geom-Plane001">
<bind_material>
<technique_common>
<instance_material symbol="ColorMaterial" target="#ColorEffectR28G149B177-material"/>
</technique_common>
</bind_material>
</instance_geometry>
<extra>
<technique profile="OpenCOLLADA">
<cast_shadows sid="cast_shadows" type="bool">1</cast_shadows>
<receive_shadows sid="receive_shadows" type="bool">1</receive_shadows>
<primary_visibility sid="primary_visibility" type="int">1</primary_visibility>
<secondary_visibility sid="secondary_visibility" type="int">1</secondary_visibility>
</technique>
</extra>
</node>
</visual_scene>
</library_visual_scenes>
<library_animations>
<animation>
<source id="node-Cube_translation.X-input">
<float_array id="node-Cube_translation.X-input-array" count="4">0 1.666667 3.333333 5</float_array>
<technique_common>
<accessor source="#node-Cube_translation.X-input-array" count="4" stride="1">
<param name="TIME" type="float"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.X-output">
<float_array id="node-Cube_translation.X-output-array" count="4">10 -10 10 -10</float_array>
<technique_common>
<accessor source="#node-Cube_translation.X-output-array" count="4" stride="1">
<param name="X" type="float"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.X-intangent">
<float_array id="node-Cube_translation.X-intangent-array" count="8">-0.3332306 10 1.111167 -10 2.778333 10 4.4445 -9.219337</float_array>
<technique_common>
<accessor source="#node-Cube_translation.X-intangent-array" count="4" stride="2">
<param name="X" type="float"/>
<param name="Y" type="float"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.X-outtangent">
<float_array id="node-Cube_translation.X-outtangent-array" count="8">0.5555 10 2.222167 -10 3.888333 10 4.000208 -8.594958</float_array>
<technique_common>
<accessor source="#node-Cube_translation.X-outtangent-array" count="4" stride="2">
<param name="X" type="float"/>
<param name="Y" type="float"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.X-interpolation">
<Name_array id="node-Cube_translation.X-interpolation-array" count="4">BEZIER BEZIER BEZIER BEZIER</Name_array>
<technique_common>
<accessor source="#node-Cube_translation.X-interpolation-array" count="4" stride="1">
<param name="INTERPOLATION" type="name"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.Y-input">
<float_array id="node-Cube_translation.Y-input-array" count="4">-0.8333334 0.8333334 2.5 4.166667</float_array>
<technique_common>
<accessor source="#node-Cube_translation.Y-input-array" count="4" stride="1">
<param name="TIME" type="float"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.Y-output">
<float_array id="node-Cube_translation.Y-output-array" count="4">-10.05776 10.05852 -9.941484 10.05852</float_array>
<technique_common>
<accessor source="#node-Cube_translation.Y-output-array" count="4" stride="1">
<param name="Y" type="float"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.Y-intangent">
<float_array id="node-Cube_translation.Y-intangent-array" count="8">-1.166264 -10.05776 0.2778334 10.05852 1.9445 -9.941484 3.611667 10.05852</float_array>
<technique_common>
<accessor source="#node-Cube_translation.Y-intangent-array" count="4" stride="2">
<param name="X" type="float"/>
<param name="Y" type="float"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.Y-outtangent">
<float_array id="node-Cube_translation.Y-outtangent-array" count="8">-0.2783333 -10.05776 1.388833 10.05852 3.0555 -9.941484 4.499598 10.05852</float_array>
<technique_common>
<accessor source="#node-Cube_translation.Y-outtangent-array" count="4" stride="2">
<param name="X" type="float"/>
<param name="Y" type="float"/>
</accessor>
</technique_common>
</source>
<source id="node-Cube_translation.Y-interpolation">
<Name_array id="node-Cube_translation.Y-interpolation-array" count="4">BEZIER BEZIER BEZIER BEZIER</Name_array>
<technique_common>
<accessor source="#node-Cube_translation.Y-interpolation-array" count="4" stride="1">
<param name="INTERPOLATION" type="name"/>
</accessor>
</technique_common>
</source>
<sampler id="node-Cube_translation.X-sampler">
<input semantic="INPUT" source="#node-Cube_translation.X-input"/>
<input semantic="OUTPUT" source="#node-Cube_translation.X-output"/>
<input semantic="IN_TANGENT" source="#node-Cube_translation.X-intangent"/>
<input semantic="OUT_TANGENT" source="#node-Cube_translation.X-outtangent"/>
<input semantic="INTERPOLATION" source="#node-Cube_translation.X-interpolation"/>
</sampler>
<sampler id="node-Cube_translation.Y-sampler">
<input semantic="INPUT" source="#node-Cube_translation.Y-input"/>
<input semantic="OUTPUT" source="#node-Cube_translation.Y-output"/>
<input semantic="IN_TANGENT" source="#node-Cube_translation.Y-intangent"/>
<input semantic="OUT_TANGENT" source="#node-Cube_translation.Y-outtangent"/>
<input semantic="INTERPOLATION" source="#node-Cube_translation.Y-interpolation"/>
</sampler>
<channel source="#node-Cube_translation.X-sampler" target="node-Cube/translation.X"/>
<channel source="#node-Cube_translation.Y-sampler" target="node-Cube/translation.Y"/>
</animation>
</library_animations>
<scene>
<instance_visual_scene url="#MaxScene"/>
</scene>
</COLLADA>

Binary file not shown.