From 5f0f9332241500b4680449c68072b1c9b5d09fb0 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Thu, 29 Jan 2026 17:44:22 -0600 Subject: [PATCH] collada/header: render effect texture and image data structures --- collada/header.py | 216 ++++++-- include/collada_types.hpp | 34 +- .../curve_interpolation.DAE | 358 +++++++++++-- .../curve_interpolation.max | Bin 335872 -> 348160 bytes .../curve_interpolation.vtx | Bin 12492 -> 12492 bytes src/effect/collada_scene.fx | 13 + .../curve_interpolation.cpp | 475 +++++++++++++++++- 7 files changed, 987 insertions(+), 109 deletions(-) diff --git a/collada/header.py b/collada/header.py index 9ccb6e5..a118115 100644 --- a/collada/header.py +++ b/collada/header.py @@ -1,9 +1,12 @@ -from typing import Dict, List, Any +from typing import Dict, List, Any, Tuple +from operator import attrgetter from collections import defaultdict from dataclasses import dataclass from itertools import islice, chain +from urllib.parse import unquote from io import BytesIO +import os.path import struct from collada.util import matrix_transpose, find_semantics @@ -45,28 +48,58 @@ class State: linearized_nodes: List[types.Node] node_parents: Dict[int, int] + # image_paths: filename: path + image_paths: Dict[str, str] + + # image_indices: image_id: image_index + image_indices: Dict[str, str] + + # resource_names: resource compiler names already emitted + resource_names: Dict[str, str] + + # effect_textures_by_texcoord: (effect_id, channel): [sampler_sid] + effect_textures_by_texcoord: Dict[Tuple[str, str], list] + def __init__(self): self.vertex_buffer = BytesIO() self.index_buffer = BytesIO() self.geometry__indices = {} self.geometry__vertex_index_tables = {} - self.node_names = {} self.symbol_names = {} self.emitted_input_elements_arrays = {} self.node_animation_channels = defaultdict(set) self.linearized_nodes = [] self.node_parents = {} + self.image_paths = {} + self.image_indices = {} + self.resource_names = {} + self.effect_textures_by_texcoord = defaultdict(list) + +def _sanitize(name): + return name.replace(' ', '_').replace('-', '_').replace('.', '_').replace('/', '_') + +def _validate_name_value(name, value): + assert name is not None, name + assert value is not None, value + assert type(name) is str, name + assert '\\' not in name, name + +def sanitize_resource_name(state, name, value): + _validate_name_value(name, value) + assert '/' not in name, name + resource_name = _sanitize(name).upper() + assert resource_name not in state.resource_names or state.resource_names[resource_name] is value + state.resource_names[resource_name] = value + return resource_name def sanitize_name(state, name, value, *, allow_slash=False): - assert name is not None, value - assert type(name) is str + _validate_name_value(name, value) + assert ' ' not in name, name if not allow_slash: assert '/' not in name, name - c_id = name.lower().replace('-', '_').replace('.', '_').replace('/', '_') - - assert c_id not in state.node_names or state.node_names[c_id] is value + c_id = _sanitize(name).lower() + assert c_id not in state.symbol_names or state.symbol_names[c_id] is value state.symbol_names[c_id] = value - return c_id def render_matrix(fs): @@ -243,6 +276,23 @@ def find_material_symbol(geometry, material_symbol): return i # element index assert False, material_symbol +def shader_to_input_set(channel_to_input_set, shader, op): + if type(shader) is types.Constant: + return -1 + + shader_op = op(shader) + + if type(shader_op) is types.Color: + return -1 + if shader_op is None: + return -1 + + assert type(shader_op) is types.Texture + texture = shader_op + assert texture.texcoord in channel_to_input_set + + return channel_to_input_set[texture.texcoord] + def render_node_instance_geometry_instance_materials(state, collada, node_name, i, geometry, instance_materials): yield f"instance_material const instance_materials_{node_name}_{i}[] = {{" for instance_material in instance_materials: @@ -256,9 +306,32 @@ def render_node_instance_geometry_instance_materials(state, collada, node_name, material = collada.lookup(instance_material.target, types.Material) material_name = sanitize_name(state, material.id, material) + channel_to_input_set = {} + for bind_vertex_input in instance_material.bind_vertex_inputs: + assert bind_vertex_input.input_semantic == "TEXCOORD", bind_vertex_input + if bind_vertex_input.semantic in channel_to_input_set: + assert channel_to_input_set[bind_vertex_input.semantic] == bind_vertex_input.input_set + else: + channel_to_input_set[bind_vertex_input.semantic] = bind_vertex_input.input_set + + + effect = collada.lookup(material.instance_effect.url, types.Effect) + profile_common, = effect.profile_common + shader = profile_common.technique.shader + + emission_input_set = shader_to_input_set(channel_to_input_set, shader, attrgetter("emission")) + ambient_input_set = shader_to_input_set(channel_to_input_set, shader, attrgetter("ambient")) + diffuse_input_set = shader_to_input_set(channel_to_input_set, shader, attrgetter("diffuse")) + specular_input_set = shader_to_input_set(channel_to_input_set, shader, attrgetter("specular")) + yield "{" yield f".element_index = {element_index}, // an index into mesh.triangles" yield f".material = &material_{material_name}," + yield "" + yield f".emission = {{ .input_set = {emission_input_set} }}," + yield f".ambient = {{ .input_set = {ambient_input_set} }}," + yield f".diffuse = {{ .input_set = {diffuse_input_set} }}," + yield f".specular = {{ .input_set = {specular_input_set} }}," yield "}," yield "};" @@ -391,14 +464,39 @@ def render_header(namespace): yield 'using namespace collada;' def render_opt_color(field_name, color): - yield f".color = {render_float_tuple(color.value)}," + yield f".{field_name} = {render_float_tuple(color.value)}," -def render_opt_color_or_texture(field_name, color_or_texture): +def render_opt_texture(state, profile_common, field_name, texture): + sampler_sid = texture.texture + sampler = profile_common.sid_lookup[sampler_sid] + assert type(sampler) is types.Newparam, sampler + assert type(sampler.parameter_type) is types.Sampler2D, sampler + + surface_sid = sampler.parameter_type.source.sid + surface = profile_common.sid_lookup[surface_sid] + assert type(surface) is types.Newparam, surface + assert type(surface.parameter_type) is types.Surface, surface + assert surface.parameter_type.type is types.FxSurfaceType._2D + image_id = surface.parameter_type.init_from.uri + image_index = state.image_indices[image_id] + + yield f".{field_name} = {{ .image_index = {image_index} }}," + +def render_opt_color_or_texture(state, profile_common, field_name, color_or_texture): if color_or_texture is None: color_or_texture = types.Color(value=(0.0, 0.0, 0.0, 0.0)) - assert type(color_or_texture) is types.Color, color_or_texture + opt_type = { + types.Color: "COLOR", + types.Texture: "TEXTURE", + }[type(color_or_texture)] yield f".{field_name} = {{" - yield from render_opt_color("color", color_or_texture) + yield f".type = color_or_texture_type::{opt_type}," + if type(color_or_texture) is types.Color: + yield from render_opt_color("color", color_or_texture) + elif type(color_or_texture) is types.Texture: + yield from render_opt_texture(state, profile_common, "texture", color_or_texture) + else: + assert False, color_or_texture yield "}," def render_opt_float(field_name, f): @@ -408,7 +506,7 @@ def render_opt_float(field_name, f): def render_effect(state, collada, effect): profile_common, = effect.profile_common - assert profile_common.newparam == [] + shader = profile_common.technique.shader effect_name = sanitize_name(state, effect.id, effect) yield f"effect const effect_{effect_name} = {{" @@ -416,40 +514,40 @@ def render_effect(state, collada, effect): if type(shader) is types.Blinn: yield ".type = effect_type::BLINN," yield ".blinn = {" - yield from render_opt_color_or_texture("emission", shader.emission) - yield from render_opt_color_or_texture("ambient", shader.ambient) - yield from render_opt_color_or_texture("diffuse", shader.diffuse) - yield from render_opt_color_or_texture("specular", shader.specular) + yield from render_opt_color_or_texture(state, profile_common, "emission", shader.emission) + yield from render_opt_color_or_texture(state, profile_common, "ambient", shader.ambient) + yield from render_opt_color_or_texture(state, profile_common, "diffuse", shader.diffuse) + yield from render_opt_color_or_texture(state, profile_common, "specular", shader.specular) yield from render_opt_float("shininess", shader.shininess) - yield from render_opt_color_or_texture("reflective", shader.reflective) + yield from render_opt_color_or_texture(state, profile_common, "reflective", shader.reflective) yield from render_opt_float("reflectivity", shader.reflectivity) - yield from render_opt_color_or_texture("transparent", shader.transparent) + yield from render_opt_color_or_texture(state, profile_common, "transparent", shader.transparent) yield from render_opt_float("transparency", shader.transparency) yield from render_opt_float("index_of_refraction", shader.index_of_refraction) yield "}" elif type(shader) is types.Lambert: yield ".type = effect_type::LAMBERT," yield ".lambert = {" - yield from render_opt_color_or_texture("emission", shader.emission) - yield from render_opt_color_or_texture("ambient", shader.ambient) - yield from render_opt_color_or_texture("diffuse", shader.diffuse) - yield from render_opt_color_or_texture("reflective", shader.reflective) + yield from render_opt_color_or_texture(state, profile_common, "emission", shader.emission) + yield from render_opt_color_or_texture(state, profile_common, "ambient", shader.ambient) + yield from render_opt_color_or_texture(state, profile_common, "diffuse", shader.diffuse) + yield from render_opt_color_or_texture(state, profile_common, "reflective", shader.reflective) yield from render_opt_float("reflectivity", shader.reflectivity) - yield from render_opt_color_or_texture("transparent", shader.transparent) + yield from render_opt_color_or_texture(state, profile_common, "transparent", shader.transparent) yield from render_opt_float("transparency", shader.transparency) yield from render_opt_float("index_of_refraction", shader.index_of_refraction) yield "}" elif type(shader) is types.Phong: yield ".type = effect_type::PHONG," yield ".phong = {" - yield from render_opt_color_or_texture("emission", shader.emission) - yield from render_opt_color_or_texture("ambient", shader.ambient) - yield from render_opt_color_or_texture("diffuse", shader.diffuse) - yield from render_opt_color_or_texture("specular", shader.specular) + yield from render_opt_color_or_texture(state, profile_common, "emission", shader.emission) + yield from render_opt_color_or_texture(state, profile_common, "ambient", shader.ambient) + yield from render_opt_color_or_texture(state, profile_common, "diffuse", shader.diffuse) + yield from render_opt_color_or_texture(state, profile_common, "specular", shader.specular) yield from render_opt_float("shininess", shader.shininess) - yield from render_opt_color_or_texture("reflective", shader.reflective) + yield from render_opt_color_or_texture(state, profile_common, "reflective", shader.reflective) yield from render_opt_float("reflectivity", shader.reflectivity) - yield from render_opt_color_or_texture("transparent", shader.transparent) + yield from render_opt_color_or_texture(state, profile_common, "transparent", shader.transparent) yield from render_opt_float("transparency", shader.transparency) yield from render_opt_float("index_of_refraction", shader.index_of_refraction) yield "}" @@ -457,9 +555,9 @@ def render_effect(state, collada, effect): yield ".type = effect_type::CONSTANT," yield ".constant = {" yield from render_opt_color("color", shader.color) - yield from render_opt_color_or_texture("reflective", shader.reflective) + yield from render_opt_color_or_texture(state, profile_common, "reflective", shader.reflective) yield from render_opt_float("reflectivity", shader.reflectivity) - yield from render_opt_color_or_texture("transparent", shader.transparent) + yield from render_opt_color_or_texture(state, profile_common, "transparent", shader.transparent) yield from render_opt_float("transparency", shader.transparency) yield from render_opt_float("index_of_refraction", shader.index_of_refraction) yield "}" @@ -501,6 +599,9 @@ def render_descriptor(namespace): yield "" yield ".inputs_list = inputs_list," yield ".inputs_list_count = (sizeof (inputs_list)) / (sizeof (inputs_list[0]))," + yield "" + yield ".images = images," + yield ".images_count = (sizeof (images)) / (sizeof (images[0]))," yield "};" def render_end_of_namespace(): @@ -695,12 +796,63 @@ def render_library_lights(state, collada): for light in library_lights.lights: yield from render_light(state, collada, light) +def escape_space(s): + def _escape_space(s): + for c in s: + if c == ' ': + yield '\\' + yield c + return str(_escape_space(s)) + +def image_resource_name(state, uri): + uri = unquote(uri) + prefix = "file:///" + assert uri.startswith(prefix), uri + path = uri[len(prefix):] + assert os.path.exists(path), path + image_extensions = {".png", ".jpg", ".bmp", ".jpeg", ".tiff"} + assert os.path.splitext(path)[1].lower() in image_extensions, path + + filename = os.path.split(path)[1] + assert filename not in state.image_paths, filename + state.image_paths[filename] = path + return sanitize_resource_name(state, filename, path.lower()) + +def render_image(state, collada, image, image_index): + assert image.id is not None + assert image.id not in state.image_indices + state.image_indices[image.id] = image_index + + assert type(image.image_source) is types.InitFrom + resource_name = image_resource_name(state, image.image_source.uri) + image_name = sanitize_name(state, image.id, image) + + yield f"// image_index: {image_index}" + yield f"image const image_{image_name} = {{" + yield f'.resource_name = L"{resource_name}",' + yield "};" + +def render_library_images(state, collada): + image_index = 0 + for library_images in collada.library_images: + for image in library_images.images: + yield from render_image(state, collada, image, image_index) + image_index += 1 + + yield "image const * const images[] = {" + for library_images in collada.library_images: + for image in library_images.images: + image_name = sanitize_name(state, image.id, image) + yield f"&image_{image_name}," + yield "};" + def render_all(collada, namespace): state = State() render, out = renderer() render(render_header(namespace)) render(render_library_lights(state, collada)) render(render_library_animations(state, collada)) + render(render_library_images(state, collada)) render(render_library_effects(state, collada)) render(render_library_materials(state, collada)) render(render_library_geometries(state, collada)) diff --git a/include/collada_types.hpp b/include/collada_types.hpp index 775dda6..9f9623e 100644 --- a/include/collada_types.hpp +++ b/include/collada_types.hpp @@ -96,13 +96,32 @@ namespace collada { float3 color; }; + ////////////////////////////////////////////////////////////////////// + // image + ////////////////////////////////////////////////////////////////////// + + struct image { + const wchar_t * resource_name; + }; + ////////////////////////////////////////////////////////////////////// // effect ////////////////////////////////////////////////////////////////////// + enum color_or_texture_type { + COLOR, + TEXTURE, + }; + + struct texture { + int const image_index; // index in to images + }; + struct color_or_texture { + color_or_texture_type type; union { float4 color; + texture texture; }; }; @@ -216,9 +235,19 @@ namespace collada { effect const * const effect; }; + struct bind_vertex_input { + int input_set; // TEXCOORD semantic input slot + }; + struct instance_material { - int element_index; // an index into mesh.triangles + int const element_index; // an index into mesh.triangles material const * const material; + + // heavily simplified from collada data model + bind_vertex_input const emission; + bind_vertex_input const ambient; + bind_vertex_input const diffuse; + bind_vertex_input const specular; }; struct instance_geometry { @@ -328,6 +357,9 @@ namespace collada { inputs const * const inputs_list; int const inputs_list_count; + image const * const * const images; + int const images_count; + //animation const * const animations; //int const animations_count; }; diff --git a/scenes/curve_interpolation/curve_interpolation.DAE b/scenes/curve_interpolation/curve_interpolation.DAE index 99a9bce..4a51511 100755 --- a/scenes/curve_interpolation/curve_interpolation.DAE +++ b/scenes/curve_interpolation/curve_interpolation.DAE @@ -6,8 +6,8 @@ OpenCOLLADA for 3ds Max; Version: 1.6; Revision: 68 file:///C:/cygwin/home/bilbo/d3d10/scenes/curve_interpolation/curve_interpolation.max - 2026-01-28T15:18:44 - 2026-01-28T15:18:44 + 2026-01-29T17:33:59 + 2026-01-29T17:33:59 Z_UP @@ -377,18 +377,31 @@ - + + + + SiteWork_Planting_Grass_Bermuda1_jpg + + + + + SiteWork_Planting_Grass_Bermuda1_jpg-surface + + - + + + 0 0 0 1 + - 0.8980392 0.6039216 0.8431373 1 + 0.588 0.588 0.588 1 - 0.8980392 0.6039216 0.8431373 1 + - 1 1 1 1 + 0 0 0 1 10 @@ -396,28 +409,85 @@ 0 0 0 1 - + 1 1 1 1 1 - + + + + + 0 + 0 + 0 + 1.5 + 1 + 0 + 0 + 0 + 3 + + + 1 + 1 + 0 + 0 + 0 + 0 + 0.1 + + + - + + + + american_cherry_png + + + + + american_cherry_png-surface + + + + + Finishes_Flooring_Tile_Square_Medium_Blue_png + + + + + Finishes_Flooring_Tile_Square_Medium_Blue_png-surface + + + + + _02_png + + + + + _02_png-surface + + - + + + 0 0 0 1 + - 0.1098039 0.5843137 0.6941176 1 + 0.588 0.588 0.588 1 - 0.1098039 0.5843137 0.6941176 1 + - 1 1 1 1 + 10 @@ -425,15 +495,46 @@ 0 0 0 1 - + 1 1 1 1 1 - + + + + + + + + + + + + 0 + 0 + 0 + 1.5 + 1 + 0 + 0 + 0 + 3 + + + 1 + 1 + 0 + 0 + 0 + 0 + 0.1 + + + @@ -525,12 +626,6 @@ - - - - - - @@ -552,6 +647,12 @@ + + + + + + @@ -713,7 +814,7 @@ - 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 + 1.999001 0.9999998 0.5 1.979805 1.194895 0.5 1.922957 1.382301 0.5 1.830639 1.555015 0.5 1.706401 1.7064 0.5 1.555015 1.830639 0.5 1.382301 1.922956 0.5 1.194896 1.979805 0.5 1 1.999001 0.5 0.8051049 1.979805 0.5 0.6176993 1.922957 0.5 0.444985 1.830639 0.5 0.2935998 1.706401 0.5 0.1693612 1.555015 0.5 0.07704347 1.382301 0.5 0.02019453 1.194895 0.5 9.99033e-4 0.9999999 0.5 0.02019459 0.8051044 0.5 0.07704353 0.6176987 0.5 0.1693612 0.4449845 0.5 0.2936 0.2935994 0.5 0.4449852 0.1693608 0.5 0.6176994 0.07704329 0.5 0.8051052 0.02019441 0.5 1.000001 9.99033e-4 0.5 1.194896 0.02019471 0.5 1.382302 0.07704371 0.5 1.555016 0.1693616 0.5 1.706401 0.2936004 0.5 1.83064 0.4449857 0.5 1.922957 0.6177 0.5 1.979806 0.8051058 0.5 @@ -725,11 +826,11 @@ - + -

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

+

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

@@ -756,9 +857,9 @@
- 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 0 + 9.99093e-4 9.9957e-4 1 1.999 9.99093e-4 1 9.9957e-4 1.999001 1 1.999001 1.999 1 - + @@ -768,26 +869,13 @@ - + -

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

+

2 0 2 0 1 0 3 2 3 1 3 1 3 2 3 0 1 0

- - - - 14.14213 - 14.14213 - 1 - 1 - 1 - 1 - 1 - - - @@ -902,19 +990,21 @@ - + 1 1 1 - + 0 + 0 + 1 + - 0 1 1 2 1 - 10 + 20 0 0 7 @@ -930,6 +1020,20 @@ + + + file:///C:/Program%20Files/Common%20Files/Autodesk%20Shared/Materials/Textures/1/Mats/american_cherry.png + + + file:///C:/Program%20Files/Common%20Files/Autodesk%20Shared/Materials/Textures/3/Mats/102.png + + + file:///C:/Program%20Files/Common%20Files/Autodesk%20Shared/Materials/Textures/3/Mats/Finishes.Flooring.Tile.Square.Medium%20Blue.png + + + file:///C:/Program%20Files/Common%20Files/Autodesk%20Shared/Materials/Textures/3/Mats/SiteWork.Planting.Grass.Bermuda1.jpg + + @@ -984,7 +1088,9 @@ - + + + @@ -1003,7 +1109,11 @@ - + + + + + @@ -1037,7 +1147,7 @@ - 2.124535 8.291501 6.185831 + 2.124535 8.291501 6.185831 0.5773504 -0.5773501 -0.5773504 -120 @@ -1302,6 +1412,132 @@
+ + 0 0.5 1 1.5 2 2.833333 3.333333 + + + + + + + + 2.124535 -5.611371 -5.611371 0.04967833 0.04967833 2.124535 2.124535 + + + + + + + + 0.9997917 2.124535 0.33335 -5.611371 0.83335 -5.611371 1.33335 0.04967833 1.83335 0.04967833 2.555583 2.124535 3.166683 2.124535 + + + + + + + + + 0.16665 2.124535 0.66665 -5.611371 1.16665 -5.611371 1.66665 0.04967833 2.27775 0.04967833 2.999983 2.124535 3.666564 2.124535 + + + + + + + + + BEZIER BEZIER BEZIER BEZIER BEZIER BEZIER BEZIER + + + + + + + + 0 0.5 1 1.5 2 2.833333 3.333333 + + + + + + + + 8.291501 0.2940993 0.2940993 -3.820096 -3.820096 8.291501 8.291501 + + + + + + + + 0.9997917 8.291501 0.33335 0.2940993 0.83335 0.2940993 1.33335 -3.820096 1.83335 -3.820096 2.555583 8.291501 3.166683 8.291501 + + + + + + + + + 0.16665 8.291501 0.66665 0.2940993 1.16665 0.2940993 1.66665 -3.820096 2.27775 -3.820096 2.999983 8.291501 3.666564 8.291501 + + + + + + + + + BEZIER BEZIER BEZIER BEZIER BEZIER BEZIER BEZIER + + + + + + + + 0 0.5 1 1.5 2 2.833333 3.333333 + + + + + + + + 6.185831 6.185831 6.185831 6.185831 6.185831 6.185831 6.185831 + + + + + + + + 0.9997917 6.185831 0.33335 6.185831 0.83335 6.185831 1.33335 6.185831 1.83335 6.185831 2.555583 6.185831 3.166683 6.185831 + + + + + + + + + 0.16665 6.185831 0.66665 6.185831 1.16665 6.185831 1.66665 6.185831 2.27775 6.185831 2.999983 6.185831 3.666564 6.185831 + + + + + + + + + BEZIER BEZIER BEZIER BEZIER BEZIER BEZIER BEZIER + + + + + + @@ -1340,12 +1576,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scenes/curve_interpolation/curve_interpolation.max b/scenes/curve_interpolation/curve_interpolation.max index 1a8671c52e59d1751a2f30515b7f6830e2c49cb6..2c3710a29cb3dd8d961c472aa7e91c1da1de7386 100755 GIT binary patch delta 39512 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¬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 delta 934 zcmZ9LK}Zx)7{^~LOCtHmB4gy#&r%lH28`+fiS{!<&NjjWfF zwh+P>VwTsAe4eD>Ny~@q&G~6na6cyUpZAM7h>UO6e`r0+uII;<=Gcno2X;4dhCuv3 zp7DDh-^xC{e>ZGw6WPx1w(Qj0a9GF?1zmEj=cm`J3QS>|*@eey-gA52{mm#%_jbcS zI`GbDn3TTLeto-6VdE7_$yevB7~E8d26=A@j@aI({bI$7F!;OxV;qiH11A-B6rF=- z?S~`QWNv(`$cxmf9I-1d$^nN0R