collada: implement animated rotate and scale
This also improves the correctness of channel source interpretation.
This commit is contained in:
parent
6a2a74a0f6
commit
683115e0c1
9
Makefile
9
Makefile
@ -50,7 +50,11 @@ SHADERS = \
|
||||
$(BUILD_TYPE)/effect/collada.fxo \
|
||||
$(BUILD_TYPE)/effect/collada_scene.fxo
|
||||
|
||||
$(BUILD_TYPE)/%.res: %.rc $(SHADERS)
|
||||
BUFFERS = \
|
||||
models/curve_interpolation/curve_interpolation.vtx \
|
||||
models/curve_interpolation/curve_interpolation.idx
|
||||
|
||||
$(BUILD_TYPE)/%.res: %.rc $(SHADERS) $(BUFFERS)
|
||||
@mkdir -p $(@D)
|
||||
$(WINDRES) -O coff -I$(BUILD_TYPE)/effect -o $@ $<
|
||||
|
||||
@ -67,7 +71,8 @@ OBJS = \
|
||||
$(BUILD_TYPE)/render_state.obj \
|
||||
$(BUILD_TYPE)/input.obj \
|
||||
$(BUILD_TYPE)/collada.obj \
|
||||
$(BUILD_TYPE)/collada_scene.obj
|
||||
$(BUILD_TYPE)/collada_scene.obj \
|
||||
$(BUILD_TYPE)/scenes/curve_interpolation.obj
|
||||
|
||||
$(BUILD_TYPE)/d3d10.exe: $(OBJS)
|
||||
@mkdir -p $(@D)
|
||||
|
||||
@ -475,8 +475,10 @@ def render_input_elements_list(state):
|
||||
yield "},"
|
||||
yield "};"
|
||||
|
||||
def render_descriptor():
|
||||
yield "descriptor const descriptor = {"
|
||||
def render_descriptor(namespace):
|
||||
yield f"extern collada::descriptor const descriptor;"
|
||||
yield ""
|
||||
yield "collada::descriptor const descriptor = {"
|
||||
yield ".nodes = nodes,"
|
||||
yield ".nodes_count = (sizeof (nodes)) / (sizeof (nodes[0])),"
|
||||
yield ""
|
||||
@ -494,18 +496,6 @@ def render_animation_children(state, collada, animation_name, animations):
|
||||
yield "&animation_{animation_name},"
|
||||
yield "};"
|
||||
|
||||
def array_c_type(accessor):
|
||||
if accessor.params[0].name == "INTERPOLATION":
|
||||
return "interpolation"
|
||||
assert all(param.type == "float" for param in accessor.params)
|
||||
if accessor.stride == 1:
|
||||
return "float"
|
||||
elif accessor.stride in {2, 3, 4}:
|
||||
assert list(param.name for param in accessor.params) == ["X", "Y", "Z", "W"][:accessor.stride], accessor.params
|
||||
return f"float{accessor.stride}"
|
||||
else:
|
||||
assert False, accessor.stride
|
||||
|
||||
def render_array(state, collada, accessor, array):
|
||||
array_name = sanitize_name(state, array.id, array)
|
||||
# render the array
|
||||
@ -518,30 +508,26 @@ def render_array(state, collada, accessor, array):
|
||||
assert name in {"BEZIER", "LINEAR"}, name
|
||||
yield f"interpolation::{name},"
|
||||
yield "};"
|
||||
return "interpolation"
|
||||
elif type(array) is types.FloatArray:
|
||||
c_type = array_c_type(accessor)
|
||||
yield f"{c_type} const array_{array_name}[] = {{"
|
||||
yield f"float const array_{array_name}[] = {{"
|
||||
it = iter(array.floats)
|
||||
for i in range(accessor.count):
|
||||
vector = ", ".join(f"{float(f)}f" for f in islice(it, accessor.stride))
|
||||
if accessor.stride == 1:
|
||||
yield f"{vector},"
|
||||
else:
|
||||
yield f"{{ {vector} }},"
|
||||
yield f"{vector},"
|
||||
yield "};"
|
||||
else:
|
||||
assert False, type(array)
|
||||
|
||||
def render_source(state, collada, field_name, source):
|
||||
array_name = sanitize_name(state, source.array_element.id, source.array_element)
|
||||
c_type = array_c_type(source.technique_common.accessor)
|
||||
c_type = "interpolation" if type(source.array_element) is types.NameArray else "float"
|
||||
source_name = sanitize_name(state, source.id, source)
|
||||
#yield f"source const source_{source_name} = {{"
|
||||
yield f"// {source_name}"
|
||||
yield f".{field_name} = {{"
|
||||
yield f".{c_type}_array = array_{array_name},"
|
||||
yield f".count = {source.technique_common.accessor.count},"
|
||||
yield f".stride = {source.technique_common.accessor.stride},"
|
||||
yield "},"
|
||||
|
||||
def render_sampler(state, collada, sampler):
|
||||
@ -549,13 +535,32 @@ def render_sampler(state, collada, sampler):
|
||||
enumerate(["INPUT", "OUTPUT", "IN_TANGENT", "OUT_TANGENT", "INTERPOLATION"]))
|
||||
inputs = sorted((input for input in sampler.inputs if input.semantic in order),
|
||||
key=lambda input: order[input.semantic])
|
||||
inputs_semantics = [input.semantic for input in inputs]
|
||||
assert len(inputs_semantics) == len(set(inputs_semantics))
|
||||
assert "INPUT" in inputs_semantics
|
||||
assert "OUTPUT" in inputs_semantics
|
||||
assert "INTERPOLATION" in inputs_semantics
|
||||
|
||||
# sampler validation
|
||||
counts = set()
|
||||
for input in inputs:
|
||||
source = collada.lookup(input.source, types.SourceCore)
|
||||
if input.semantic == "INTERPOLATION":
|
||||
nonlinear_interpolation_params = [e for e in source.array_element.names if e != "LINEAR"]
|
||||
if nonlinear_interpolation_params:
|
||||
assert "IN_TANGENT" in inputs_semantics
|
||||
assert "OUT_TANGENT" in inputs_semantics
|
||||
counts.add(source.technique_common.accessor.count)
|
||||
assert len(counts) == 1
|
||||
|
||||
# render the source arrays first
|
||||
for input in inputs:
|
||||
assert type(input) is types.InputUnshared
|
||||
source = collada.lookup(input.source, types.SourceCore)
|
||||
|
||||
yield from render_array(state, collada, source.technique_common.accessor, source.array_element)
|
||||
|
||||
# render the sampler
|
||||
sampler_name = sanitize_name(state, sampler.id, sampler)
|
||||
yield f"sampler const sampler_{sampler_name} = {{"
|
||||
for input in inputs:
|
||||
@ -565,7 +570,7 @@ def render_sampler(state, collada, sampler):
|
||||
yield "};"
|
||||
|
||||
target_attributes = {
|
||||
"A", "ANGLE", "B", "G", "P", "Q", "R", "S", "T", "TIME", "U", "V", "W", "X", "Y", "Z"
|
||||
"A", "ANGLE", "B", "G", "P", "Q", "R", "S", "T", "TIME", "U", "V", "W", "X", "Y", "Z", "ALL"
|
||||
}
|
||||
|
||||
def find_transform_index_in_node(node, transform):
|
||||
@ -588,11 +593,14 @@ def render_channel(state, collada, channel):
|
||||
sampler_name = sanitize_name(state, sampler.id, sampler)
|
||||
|
||||
assert '/' in channel.target, channel.target
|
||||
assert '.' in channel.target, channel.target
|
||||
assert "(" not in channel.target, channel.target
|
||||
|
||||
node_id, rest = channel.target.split("/")
|
||||
node_transform_sid, target_attribute = rest.split(".")
|
||||
if '.' in rest:
|
||||
node_transform_sid, target_attribute = rest.split(".")
|
||||
else:
|
||||
node_transform_sid = rest
|
||||
target_attribute = 'ALL'
|
||||
assert target_attribute in target_attributes
|
||||
|
||||
node = collada.lookup(f"#{node_id}", types.Node)
|
||||
@ -659,10 +667,20 @@ def render_all(collada, namespace):
|
||||
# root elements
|
||||
render(render_library_visual_scenes(state, collada))
|
||||
render(render_input_elements_list(state))
|
||||
render(render_descriptor())
|
||||
render(render_descriptor(namespace))
|
||||
render(render_end_of_namespace())
|
||||
return state, out
|
||||
|
||||
def render_hpp(namespace):
|
||||
yield f"namespace {namespace} {{"
|
||||
yield "extern collada::descriptor const descriptor;"
|
||||
yield "}"
|
||||
|
||||
def render_all_hpp(namespace):
|
||||
render, out = renderer()
|
||||
render(render_hpp(namespace))
|
||||
return out
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
collada = parse.parse_collada_file(sys.argv[1])
|
||||
|
||||
@ -7,15 +7,16 @@ from collada import header
|
||||
def usage():
|
||||
name = sys.argv[0]
|
||||
print("usage:")
|
||||
print(f" {name} [input_collada.dae] [output_header.hpp] [output_vertex.vtx] [output_vertex.idx]")
|
||||
print(f" {name} [input_collada.dae] [output_source.cpp] [output_header.hpp] [output_vertex.vtx] [output_vertex.idx]")
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
try:
|
||||
input_collada = sys.argv[1]
|
||||
output_header = sys.argv[2]
|
||||
output_vertex = sys.argv[3]
|
||||
output_index = sys.argv[4]
|
||||
output_source = sys.argv[2]
|
||||
output_header = sys.argv[3]
|
||||
output_vertex = sys.argv[4]
|
||||
output_index = sys.argv[5]
|
||||
assert input_collada.lower().endswith(".dae")
|
||||
assert output_header.lower().endswith(".hpp")
|
||||
assert output_vertex.lower().endswith(".vtx")
|
||||
@ -25,10 +26,16 @@ def main():
|
||||
|
||||
collada = parse.parse_collada_file(input_collada)
|
||||
namespace = path.splitext(path.split(input_collada)[1])[0]
|
||||
state, out = header.render_all(collada, namespace)
|
||||
state, out_source = header.render_all(collada, namespace)
|
||||
out_header = header.render_all_hpp(namespace)
|
||||
|
||||
with open(output_source, 'wb') as f:
|
||||
source_buf = out_source.getvalue()
|
||||
assert "\r\n" in source_buf
|
||||
f.write(source_buf.encode('utf-8'))
|
||||
|
||||
with open(output_header, 'wb') as f:
|
||||
header_buf = out.getvalue()
|
||||
header_buf = out_header.getvalue()
|
||||
assert "\r\n" in header_buf
|
||||
f.write(header_buf.encode('utf-8'))
|
||||
|
||||
|
||||
@ -15,9 +15,7 @@ namespace collada_scene {
|
||||
union {
|
||||
lookat lookat;
|
||||
XMMATRIX matrix;
|
||||
XMVECTOR rotate;
|
||||
XMVECTOR scale;
|
||||
XMVECTOR translate;
|
||||
XMVECTOR vector;
|
||||
};
|
||||
collada::transform_type type;
|
||||
};
|
||||
|
||||
@ -220,12 +220,10 @@ namespace collada {
|
||||
struct source {
|
||||
union {
|
||||
float const * const float_array;
|
||||
float2 const * const float2_array;
|
||||
float3 const * const float3_array;
|
||||
float4 const * const float4_array;
|
||||
enum interpolation const * const interpolation_array;
|
||||
};
|
||||
int const count;
|
||||
int const stride;
|
||||
};
|
||||
|
||||
struct sampler {
|
||||
@ -253,6 +251,7 @@ namespace collada {
|
||||
X, // first cartesian coordinate
|
||||
Y, // second cartesian coordinate
|
||||
Z, // third cartesian coordinate
|
||||
ALL,
|
||||
};
|
||||
|
||||
struct channel {
|
||||
|
||||
@ -1,700 +1,3 @@
|
||||
#include "collada_types.hpp"
|
||||
|
||||
namespace curve_interpolation {
|
||||
|
||||
using namespace collada;
|
||||
|
||||
float const array_node_cube_translation_x_input_array[] = {
|
||||
0.0f,
|
||||
1.666667f,
|
||||
3.333333f,
|
||||
5.0f,
|
||||
};
|
||||
|
||||
float const array_node_cube_translation_x_output_array[] = {
|
||||
10.0f,
|
||||
-10.0f,
|
||||
10.0f,
|
||||
-10.0f,
|
||||
};
|
||||
|
||||
float2 const array_node_cube_translation_x_intangent_array[] = {
|
||||
{ -0.3332306f, 10.0f },
|
||||
{ 1.111167f, -10.0f },
|
||||
{ 2.778333f, 10.0f },
|
||||
{ 4.4445f, -9.219337f },
|
||||
};
|
||||
|
||||
float2 const array_node_cube_translation_x_outtangent_array[] = {
|
||||
{ 0.5555f, 10.0f },
|
||||
{ 2.222167f, -10.0f },
|
||||
{ 3.888333f, 10.0f },
|
||||
{ 4.000208f, -8.594958f },
|
||||
};
|
||||
|
||||
enum interpolation const array_node_cube_translation_x_interpolation_array[] = {
|
||||
interpolation::BEZIER,
|
||||
interpolation::BEZIER,
|
||||
interpolation::BEZIER,
|
||||
interpolation::BEZIER,
|
||||
};
|
||||
|
||||
sampler const sampler_node_cube_translation_x_sampler = {
|
||||
// node_cube_translation_x_input
|
||||
.input = {
|
||||
.float_array = array_node_cube_translation_x_input_array,
|
||||
.count = 4,
|
||||
},
|
||||
// node_cube_translation_x_output
|
||||
.output = {
|
||||
.float_array = array_node_cube_translation_x_output_array,
|
||||
.count = 4,
|
||||
},
|
||||
// node_cube_translation_x_intangent
|
||||
.in_tangent = {
|
||||
.float2_array = array_node_cube_translation_x_intangent_array,
|
||||
.count = 4,
|
||||
},
|
||||
// node_cube_translation_x_outtangent
|
||||
.out_tangent = {
|
||||
.float2_array = array_node_cube_translation_x_outtangent_array,
|
||||
.count = 4,
|
||||
},
|
||||
// node_cube_translation_x_interpolation
|
||||
.interpolation = {
|
||||
.interpolation_array = array_node_cube_translation_x_interpolation_array,
|
||||
.count = 4,
|
||||
},
|
||||
};
|
||||
|
||||
float const array_node_cube_translation_y_input_array[] = {
|
||||
-0.8333334f,
|
||||
0.8333334f,
|
||||
2.5f,
|
||||
4.166667f,
|
||||
};
|
||||
|
||||
float const array_node_cube_translation_y_output_array[] = {
|
||||
-10.05776f,
|
||||
10.05852f,
|
||||
-9.941484f,
|
||||
10.05852f,
|
||||
};
|
||||
|
||||
float2 const array_node_cube_translation_y_intangent_array[] = {
|
||||
{ -1.166264f, -10.05776f },
|
||||
{ 0.2778334f, 10.05852f },
|
||||
{ 1.9445f, -9.941484f },
|
||||
{ 3.611667f, 10.05852f },
|
||||
};
|
||||
|
||||
float2 const array_node_cube_translation_y_outtangent_array[] = {
|
||||
{ -0.2783333f, -10.05776f },
|
||||
{ 1.388833f, 10.05852f },
|
||||
{ 3.0555f, -9.941484f },
|
||||
{ 4.499598f, 10.05852f },
|
||||
};
|
||||
|
||||
enum interpolation const array_node_cube_translation_y_interpolation_array[] = {
|
||||
interpolation::BEZIER,
|
||||
interpolation::BEZIER,
|
||||
interpolation::BEZIER,
|
||||
interpolation::BEZIER,
|
||||
};
|
||||
|
||||
sampler const sampler_node_cube_translation_y_sampler = {
|
||||
// node_cube_translation_y_input
|
||||
.input = {
|
||||
.float_array = array_node_cube_translation_y_input_array,
|
||||
.count = 4,
|
||||
},
|
||||
// node_cube_translation_y_output
|
||||
.output = {
|
||||
.float_array = array_node_cube_translation_y_output_array,
|
||||
.count = 4,
|
||||
},
|
||||
// node_cube_translation_y_intangent
|
||||
.in_tangent = {
|
||||
.float2_array = array_node_cube_translation_y_intangent_array,
|
||||
.count = 4,
|
||||
},
|
||||
// node_cube_translation_y_outtangent
|
||||
.out_tangent = {
|
||||
.float2_array = array_node_cube_translation_y_outtangent_array,
|
||||
.count = 4,
|
||||
},
|
||||
// node_cube_translation_y_interpolation
|
||||
.interpolation = {
|
||||
.interpolation_array = array_node_cube_translation_y_interpolation_array,
|
||||
.count = 4,
|
||||
},
|
||||
};
|
||||
|
||||
channel const node_channel_node_cube_translation_x = {
|
||||
.source_sampler = &sampler_node_cube_translation_x_sampler,
|
||||
.target_transform_index = 0,
|
||||
.target_attribute = target_attribute::X,
|
||||
};
|
||||
|
||||
channel const node_channel_node_cube_translation_y = {
|
||||
.source_sampler = &sampler_node_cube_translation_y_sampler,
|
||||
.target_transform_index = 0,
|
||||
.target_attribute = target_attribute::Y,
|
||||
};
|
||||
|
||||
effect const effect_material__15 = {
|
||||
.type = effect_type::BLINN,
|
||||
.blinn = {
|
||||
.emission = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.ambient = {
|
||||
.color = {0.6705883f, 0.5843138f, 1.0f, 1.0f},
|
||||
},
|
||||
.diffuse = {
|
||||
.color = {0.6705883f, 0.5843138f, 1.0f, 1.0f},
|
||||
},
|
||||
.specular = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.shininess = 10.0f,
|
||||
.reflective = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.reflectivity = 0.0f,
|
||||
.transparent = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.transparency = 1.0f,
|
||||
.index_of_refraction = 0.0f,
|
||||
}
|
||||
};
|
||||
|
||||
effect const effect_material__16 = {
|
||||
.type = effect_type::BLINN,
|
||||
.blinn = {
|
||||
.emission = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.ambient = {
|
||||
.color = {0.5803922f, 1.0f, 0.9647059f, 1.0f},
|
||||
},
|
||||
.diffuse = {
|
||||
.color = {0.5803922f, 1.0f, 0.9647059f, 1.0f},
|
||||
},
|
||||
.specular = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.shininess = 10.0f,
|
||||
.reflective = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.reflectivity = 0.0f,
|
||||
.transparent = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.transparency = 1.0f,
|
||||
.index_of_refraction = 0.0f,
|
||||
}
|
||||
};
|
||||
|
||||
effect const effect_material__17 = {
|
||||
.type = effect_type::BLINN,
|
||||
.blinn = {
|
||||
.emission = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.ambient = {
|
||||
.color = {0.6509804f, 1.0f, 0.5803922f, 1.0f},
|
||||
},
|
||||
.diffuse = {
|
||||
.color = {0.6509804f, 1.0f, 0.5803922f, 1.0f},
|
||||
},
|
||||
.specular = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.shininess = 10.0f,
|
||||
.reflective = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.reflectivity = 0.0f,
|
||||
.transparent = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.transparency = 1.0f,
|
||||
.index_of_refraction = 0.0f,
|
||||
}
|
||||
};
|
||||
|
||||
effect const effect_material__18 = {
|
||||
.type = effect_type::BLINN,
|
||||
.blinn = {
|
||||
.emission = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.ambient = {
|
||||
.color = {0.9333334f, 1.0f, 0.5647059f, 1.0f},
|
||||
},
|
||||
.diffuse = {
|
||||
.color = {0.9333334f, 1.0f, 0.5647059f, 1.0f},
|
||||
},
|
||||
.specular = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.shininess = 10.0f,
|
||||
.reflective = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.reflectivity = 0.0f,
|
||||
.transparent = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.transparency = 1.0f,
|
||||
.index_of_refraction = 0.0f,
|
||||
}
|
||||
};
|
||||
|
||||
effect const effect_material__19 = {
|
||||
.type = effect_type::BLINN,
|
||||
.blinn = {
|
||||
.emission = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.ambient = {
|
||||
.color = {1.0f, 0.7686275f, 0.5803922f, 1.0f},
|
||||
},
|
||||
.diffuse = {
|
||||
.color = {1.0f, 0.7686275f, 0.5803922f, 1.0f},
|
||||
},
|
||||
.specular = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.shininess = 10.0f,
|
||||
.reflective = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.reflectivity = 0.0f,
|
||||
.transparent = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.transparency = 1.0f,
|
||||
.index_of_refraction = 0.0f,
|
||||
}
|
||||
};
|
||||
|
||||
effect const effect_material__20 = {
|
||||
.type = effect_type::BLINN,
|
||||
.blinn = {
|
||||
.emission = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.ambient = {
|
||||
.color = {1.0f, 0.5803922f, 0.5803922f, 1.0f},
|
||||
},
|
||||
.diffuse = {
|
||||
.color = {1.0f, 0.5803922f, 0.5803922f, 1.0f},
|
||||
},
|
||||
.specular = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.shininess = 10.0f,
|
||||
.reflective = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.reflectivity = 0.0f,
|
||||
.transparent = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.transparency = 1.0f,
|
||||
.index_of_refraction = 0.0f,
|
||||
}
|
||||
};
|
||||
|
||||
effect const effect_coloreffectr229g154b215 = {
|
||||
.type = effect_type::PHONG,
|
||||
.phong = {
|
||||
.emission = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||
},
|
||||
.ambient = {
|
||||
.color = {0.8980392f, 0.6039216f, 0.8431373f, 1.0f},
|
||||
},
|
||||
.diffuse = {
|
||||
.color = {0.8980392f, 0.6039216f, 0.8431373f, 1.0f},
|
||||
},
|
||||
.specular = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.shininess = 10.0f,
|
||||
.reflective = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.reflectivity = 0.0f,
|
||||
.transparent = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.transparency = 1.0f,
|
||||
.index_of_refraction = 0.0f,
|
||||
}
|
||||
};
|
||||
|
||||
effect const effect_coloreffectr28g149b177 = {
|
||||
.type = effect_type::PHONG,
|
||||
.phong = {
|
||||
.emission = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||
},
|
||||
.ambient = {
|
||||
.color = {0.1098039f, 0.5843137f, 0.6941176f, 1.0f},
|
||||
},
|
||||
.diffuse = {
|
||||
.color = {0.1098039f, 0.5843137f, 0.6941176f, 1.0f},
|
||||
},
|
||||
.specular = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.shininess = 10.0f,
|
||||
.reflective = {
|
||||
.color = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||
},
|
||||
.reflectivity = 0.0f,
|
||||
.transparent = {
|
||||
.color = {1.0f, 1.0f, 1.0f, 1.0f},
|
||||
},
|
||||
.transparency = 1.0f,
|
||||
.index_of_refraction = 0.0f,
|
||||
}
|
||||
};
|
||||
|
||||
material const material_coloreffectr229g154b215_material = {
|
||||
.effect = &effect_coloreffectr229g154b215,
|
||||
};
|
||||
|
||||
material const material_coloreffectr28g149b177_material = {
|
||||
.effect = &effect_coloreffectr28g149b177,
|
||||
};
|
||||
|
||||
material const material_material__15_material = {
|
||||
.effect = &effect_material__15,
|
||||
};
|
||||
|
||||
material const material_material__16_material = {
|
||||
.effect = &effect_material__16,
|
||||
};
|
||||
|
||||
material const material_material__17_material = {
|
||||
.effect = &effect_material__17,
|
||||
};
|
||||
|
||||
material const material_material__18_material = {
|
||||
.effect = &effect_material__18,
|
||||
};
|
||||
|
||||
material const material_material__19_material = {
|
||||
.effect = &effect_material__19,
|
||||
};
|
||||
|
||||
material const material_material__20_material = {
|
||||
.effect = &effect_material__20,
|
||||
};
|
||||
|
||||
input_element const input_elements_position_0_3_normal_0_3_texcoord_0_3[] = {
|
||||
{
|
||||
.semantic = "POSITION",
|
||||
.semantic_index = 0,
|
||||
.format = input_format::FLOAT3,
|
||||
},
|
||||
{
|
||||
.semantic = "NORMAL",
|
||||
.semantic_index = 0,
|
||||
.format = input_format::FLOAT3,
|
||||
},
|
||||
{
|
||||
.semantic = "TEXCOORD",
|
||||
.semantic_index = 0,
|
||||
.format = input_format::FLOAT3,
|
||||
},
|
||||
};
|
||||
|
||||
triangles const triangles_geom_cube[] = {
|
||||
{
|
||||
.count = 2, // triangles
|
||||
.index_offset = 0, // indices
|
||||
.inputs_index = 0, // index into inputs_list
|
||||
},
|
||||
{
|
||||
.count = 2, // triangles
|
||||
.index_offset = 6, // indices
|
||||
.inputs_index = 0, // index into inputs_list
|
||||
},
|
||||
{
|
||||
.count = 2, // triangles
|
||||
.index_offset = 12, // indices
|
||||
.inputs_index = 0, // index into inputs_list
|
||||
},
|
||||
{
|
||||
.count = 2, // triangles
|
||||
.index_offset = 18, // indices
|
||||
.inputs_index = 0, // index into inputs_list
|
||||
},
|
||||
{
|
||||
.count = 2, // triangles
|
||||
.index_offset = 24, // indices
|
||||
.inputs_index = 0, // index into inputs_list
|
||||
},
|
||||
{
|
||||
.count = 2, // triangles
|
||||
.index_offset = 30, // indices
|
||||
.inputs_index = 0, // index into inputs_list
|
||||
},
|
||||
};
|
||||
|
||||
geometry const geometry_geom_cube = {
|
||||
.mesh = {
|
||||
.triangles = triangles_geom_cube,
|
||||
.triangles_count = 6,
|
||||
|
||||
.vertex_buffer_offset = 0,
|
||||
.vertex_buffer_size = 864,
|
||||
|
||||
.index_buffer_offset = 0,
|
||||
.index_buffer_size = 144,
|
||||
}
|
||||
};
|
||||
|
||||
triangles const triangles_geom_cylinder001[] = {
|
||||
{
|
||||
.count = 30, // triangles
|
||||
.index_offset = 0, // indices
|
||||
.inputs_index = 0, // index into inputs_list
|
||||
},
|
||||
};
|
||||
|
||||
geometry const geometry_geom_cylinder001 = {
|
||||
.mesh = {
|
||||
.triangles = triangles_geom_cylinder001,
|
||||
.triangles_count = 1,
|
||||
|
||||
.vertex_buffer_offset = 864,
|
||||
.vertex_buffer_size = 1152,
|
||||
|
||||
.index_buffer_offset = 144,
|
||||
.index_buffer_size = 360,
|
||||
}
|
||||
};
|
||||
|
||||
triangles const triangles_geom_plane001[] = {
|
||||
{
|
||||
.count = 2, // triangles
|
||||
.index_offset = 0, // indices
|
||||
.inputs_index = 0, // index into inputs_list
|
||||
},
|
||||
};
|
||||
|
||||
geometry const geometry_geom_plane001 = {
|
||||
.mesh = {
|
||||
.triangles = triangles_geom_plane001,
|
||||
.triangles_count = 1,
|
||||
|
||||
.vertex_buffer_offset = 2016,
|
||||
.vertex_buffer_size = 144,
|
||||
|
||||
.index_buffer_offset = 504,
|
||||
.index_buffer_size = 24,
|
||||
}
|
||||
};
|
||||
|
||||
geometry const * const geometries[] = {
|
||||
&geometry_geom_cube,
|
||||
&geometry_geom_cylinder001,
|
||||
&geometry_geom_plane001,
|
||||
};
|
||||
|
||||
transform const transforms_node_environmentambientlight[] = {
|
||||
};
|
||||
|
||||
instance_geometry const instance_geometries_node_environmentambientlight[] = {
|
||||
};
|
||||
|
||||
channel const * const node_channels_node_environmentambientlight[] = {};
|
||||
|
||||
node const node_node_environmentambientlight = {
|
||||
.parent_index = -1,
|
||||
|
||||
.type = node_type::NODE,
|
||||
|
||||
.transforms = transforms_node_environmentambientlight,
|
||||
.transforms_count = 0,
|
||||
|
||||
.instance_geometries = instance_geometries_node_environmentambientlight,
|
||||
.instance_geometries_count = 0,
|
||||
|
||||
.channels = node_channels_node_environmentambientlight,
|
||||
.channels_count = 0,
|
||||
};
|
||||
|
||||
transform const transforms_node_cube[] = {
|
||||
{
|
||||
.type = transform_type::TRANSLATE,
|
||||
.translate = {10.0f, -1.14258e-07f, 0.0f},
|
||||
},
|
||||
};
|
||||
|
||||
instance_material const instance_materials_node_cube_0[] = {
|
||||
{
|
||||
.element_index = 1, // an index into mesh.triangles
|
||||
.material = &material_material__15_material,
|
||||
},
|
||||
{
|
||||
.element_index = 0, // an index into mesh.triangles
|
||||
.material = &material_material__16_material,
|
||||
},
|
||||
{
|
||||
.element_index = 5, // an index into mesh.triangles
|
||||
.material = &material_material__17_material,
|
||||
},
|
||||
{
|
||||
.element_index = 3, // an index into mesh.triangles
|
||||
.material = &material_material__18_material,
|
||||
},
|
||||
{
|
||||
.element_index = 2, // an index into mesh.triangles
|
||||
.material = &material_material__19_material,
|
||||
},
|
||||
{
|
||||
.element_index = 4, // an index into mesh.triangles
|
||||
.material = &material_material__20_material,
|
||||
},
|
||||
};
|
||||
|
||||
instance_geometry const instance_geometries_node_cube[] = {
|
||||
{
|
||||
.geometry = &geometry_geom_cube,
|
||||
.instance_materials = instance_materials_node_cube_0,
|
||||
.instance_materials_count = 6,
|
||||
},
|
||||
};
|
||||
|
||||
channel const * const node_channels_node_cube[] = {
|
||||
&node_channel_node_cube_translation_y,
|
||||
&node_channel_node_cube_translation_x,
|
||||
};
|
||||
|
||||
node const node_node_cube = {
|
||||
.parent_index = -1,
|
||||
|
||||
.type = node_type::NODE,
|
||||
|
||||
.transforms = transforms_node_cube,
|
||||
.transforms_count = 1,
|
||||
|
||||
.instance_geometries = instance_geometries_node_cube,
|
||||
.instance_geometries_count = 1,
|
||||
|
||||
.channels = node_channels_node_cube,
|
||||
.channels_count = 2,
|
||||
};
|
||||
|
||||
transform const transforms_node_cylinder001[] = {
|
||||
};
|
||||
|
||||
instance_material const instance_materials_node_cylinder001_0[] = {
|
||||
{
|
||||
.element_index = 0, // an index into mesh.triangles
|
||||
.material = &material_coloreffectr229g154b215_material,
|
||||
},
|
||||
};
|
||||
|
||||
instance_geometry const instance_geometries_node_cylinder001[] = {
|
||||
{
|
||||
.geometry = &geometry_geom_cylinder001,
|
||||
.instance_materials = instance_materials_node_cylinder001_0,
|
||||
.instance_materials_count = 1,
|
||||
},
|
||||
};
|
||||
|
||||
channel const * const node_channels_node_cylinder001[] = {
|
||||
};
|
||||
|
||||
node const node_node_cylinder001 = {
|
||||
.parent_index = -1,
|
||||
|
||||
.type = node_type::NODE,
|
||||
|
||||
.transforms = transforms_node_cylinder001,
|
||||
.transforms_count = 0,
|
||||
|
||||
.instance_geometries = instance_geometries_node_cylinder001,
|
||||
.instance_geometries_count = 1,
|
||||
|
||||
.channels = node_channels_node_cylinder001,
|
||||
.channels_count = 0,
|
||||
};
|
||||
|
||||
transform const transforms_node_plane001[] = {
|
||||
{
|
||||
.type = transform_type::TRANSLATE,
|
||||
.translate = {0.0f, 0.0f, 0.01f},
|
||||
},
|
||||
{
|
||||
.type = transform_type::ROTATE,
|
||||
.rotate = {0.0f, 0.0f, -1.0f, -44.99999f},
|
||||
},
|
||||
};
|
||||
|
||||
instance_material const instance_materials_node_plane001_0[] = {
|
||||
{
|
||||
.element_index = 0, // an index into mesh.triangles
|
||||
.material = &material_coloreffectr28g149b177_material,
|
||||
},
|
||||
};
|
||||
|
||||
instance_geometry const instance_geometries_node_plane001[] = {
|
||||
{
|
||||
.geometry = &geometry_geom_plane001,
|
||||
.instance_materials = instance_materials_node_plane001_0,
|
||||
.instance_materials_count = 1,
|
||||
},
|
||||
};
|
||||
|
||||
channel const * const node_channels_node_plane001[] = {
|
||||
};
|
||||
|
||||
node const node_node_plane001 = {
|
||||
.parent_index = -1,
|
||||
|
||||
.type = node_type::NODE,
|
||||
|
||||
.transforms = transforms_node_plane001,
|
||||
.transforms_count = 2,
|
||||
|
||||
.instance_geometries = instance_geometries_node_plane001,
|
||||
.instance_geometries_count = 1,
|
||||
|
||||
.channels = node_channels_node_plane001,
|
||||
.channels_count = 0,
|
||||
};
|
||||
|
||||
node const * const nodes[] = {
|
||||
&node_node_environmentambientlight,
|
||||
&node_node_cube,
|
||||
&node_node_cylinder001,
|
||||
&node_node_plane001,
|
||||
};
|
||||
|
||||
inputs const inputs_list[] = {
|
||||
{
|
||||
.elements = input_elements_position_0_3_normal_0_3_texcoord_0_3,
|
||||
.elements_count = 3,
|
||||
},
|
||||
};
|
||||
|
||||
descriptor const descriptor = {
|
||||
.nodes = nodes,
|
||||
.nodes_count = (sizeof (nodes)) / (sizeof (nodes[0])),
|
||||
|
||||
.inputs_list = inputs_list,
|
||||
.inputs_list_count = (sizeof (inputs_list)) / (sizeof (inputs_list[0])),
|
||||
};
|
||||
|
||||
extern collada::descriptor const descriptor;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -83,13 +83,13 @@ namespace collada_scene {
|
||||
transform.matrix = XMLoadFloat4x4((XMFLOAT4X4 *)&node->transforms[i].matrix);
|
||||
break;
|
||||
case transform_type::ROTATE:
|
||||
transform.rotate = XMLoadFloat4((XMFLOAT4 *)&node->transforms[i].rotate);
|
||||
transform.vector = XMLoadFloat4((XMFLOAT4 *)&node->transforms[i].rotate);
|
||||
break;
|
||||
case transform_type::SCALE:
|
||||
transform.scale = XMLoadFloat3((XMFLOAT3 *)&node->transforms[i].scale);
|
||||
transform.vector = XMLoadFloat3((XMFLOAT3*)&node->transforms[i].scale);
|
||||
break;
|
||||
case transform_type::TRANSLATE:
|
||||
transform.translate = XMLoadFloat3((XMFLOAT3*)&node->transforms[i].translate);
|
||||
transform.vector = XMLoadFloat3((XMFLOAT3*)&node->transforms[i].translate);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@ -271,12 +271,12 @@ namespace collada_scene {
|
||||
{
|
||||
switch (transform.type) {
|
||||
case transform_type::TRANSLATE:
|
||||
return XMMatrixTranslationFromVector(transform.translate);
|
||||
return XMMatrixTranslationFromVector(transform.vector);
|
||||
case transform_type::ROTATE:
|
||||
return XMMatrixRotationNormal(transform.rotate,
|
||||
XMConvertToRadians(XMVectorGetW(transform.rotate)));
|
||||
return XMMatrixRotationNormal(transform.vector,
|
||||
XMConvertToRadians(XMVectorGetW(transform.vector)));
|
||||
case transform_type::SCALE:
|
||||
return XMMatrixScalingFromVector(transform.scale);
|
||||
return XMMatrixScalingFromVector(transform.vector);
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
@ -360,17 +360,17 @@ namespace collada_scene {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline float interpolation_value(source const& source, int ix, float t)
|
||||
static inline float linear_interpolate_iv(source const& source, int frame_ix, float t)
|
||||
{
|
||||
float prev = source.float_array[ix];
|
||||
float next = source.float_array[ix+1];
|
||||
float prev = source.float_array[(frame_ix+0) * source.stride];
|
||||
float next = source.float_array[(frame_ix+1) * source.stride];
|
||||
return (t - prev) / (next - prev);
|
||||
}
|
||||
|
||||
static inline float linear_interpolate(source const& source, int ix, float iv)
|
||||
static inline float linear_interpolate_value(source const& source, int frame_ix, int parameter_ix, float iv)
|
||||
{
|
||||
float prev = source.float_array[ix];
|
||||
float next = source.float_array[ix+1];
|
||||
float prev = source.float_array[(frame_ix+0) * source.stride + parameter_ix];
|
||||
float next = source.float_array[(frame_ix+1) * source.stride + parameter_ix];
|
||||
return prev + iv * (next - prev);
|
||||
}
|
||||
|
||||
@ -425,7 +425,13 @@ namespace collada_scene {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static float bezier_sampler(sampler const * const sampler, int ix, float t)
|
||||
static inline XMFLOAT2 const * tangent_index(source const& source, int frame_ix, int parameter_ix)
|
||||
{
|
||||
int ix = frame_ix * source.stride + parameter_ix * 2;
|
||||
return (XMFLOAT2 const *)&source.float_array[ix];
|
||||
}
|
||||
|
||||
static float bezier_sampler(sampler const * const sampler, int frame_ix, int parameter_ix, float t)
|
||||
{
|
||||
/*
|
||||
P0 is (INPUT[i] , OUTPUT[i])
|
||||
@ -433,14 +439,100 @@ namespace collada_scene {
|
||||
C1 (or T1) is (IN_TANGENT[i+1][0], IN_TANGENT[i+1][1])
|
||||
P1 is (INPUT[i+1], OUTPUT[i+1])
|
||||
*/
|
||||
XMVECTOR p0 = XMVectorSet(sampler->input.float_array[ix], sampler->output.float_array[ix], 0, 0);
|
||||
XMVECTOR c0 = XMLoadFloat2((XMFLOAT2 *)&sampler->out_tangent.float2_array[ix]);
|
||||
XMVECTOR c1 = XMLoadFloat2((XMFLOAT2 *)&sampler->in_tangent.float2_array[ix+1]);
|
||||
XMVECTOR p1 = XMVectorSet(sampler->input.float_array[ix+1], sampler->output.float_array[ix+1], 0, 0);
|
||||
|
||||
float frame0_input = sampler->input.float_array[frame_ix+0];
|
||||
float frame1_input = sampler->input.float_array[frame_ix+1];
|
||||
|
||||
float frame0_output = sampler->output.float_array[(frame_ix+0) * sampler->output.stride + parameter_ix];
|
||||
float frame1_output = sampler->output.float_array[(frame_ix+1) * sampler->output.stride + parameter_ix];
|
||||
|
||||
XMVECTOR p0 = XMVectorSet(frame0_input, frame0_output, 0, 0);
|
||||
XMVECTOR c0 = XMLoadFloat2(tangent_index(sampler->out_tangent, frame_ix + 0, parameter_ix));
|
||||
XMVECTOR c1 = XMLoadFloat2(tangent_index(sampler->in_tangent, frame_ix + 1, parameter_ix));
|
||||
XMVECTOR p1 = XMVectorSet(frame1_input, frame1_output, 0, 0);
|
||||
|
||||
return bezier_binary_search(p0, c0, c1, p1, t);
|
||||
}
|
||||
|
||||
static void apply_transform_target(transform& transform,
|
||||
enum target_attribute channel_target_attribute,
|
||||
float value)
|
||||
{
|
||||
switch (transform.type) {
|
||||
case transform_type::TRANSLATE: __attribute__((fallthrough));
|
||||
case transform_type::SCALE:
|
||||
switch (channel_target_attribute) {
|
||||
case target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return;
|
||||
case target_attribute::Y: transform.vector = XMVectorSetY(transform.vector, value); return;
|
||||
case target_attribute::Z: transform.vector = XMVectorSetZ(transform.vector, value); return;
|
||||
default: assert(false);
|
||||
}
|
||||
case transform_type::ROTATE:
|
||||
switch (channel_target_attribute) {
|
||||
case target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return;
|
||||
case target_attribute::Y: transform.vector = XMVectorSetY(transform.vector, value); return;
|
||||
case target_attribute::Z: transform.vector = XMVectorSetZ(transform.vector, value); return;
|
||||
case target_attribute::ANGLE: transform.vector = XMVectorSetW(transform.vector, value); return;
|
||||
default: assert(false);
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static enum target_attribute const rotate_target_attributes[] = {
|
||||
target_attribute::X,
|
||||
target_attribute::Y,
|
||||
target_attribute::Z,
|
||||
target_attribute::ANGLE,
|
||||
};
|
||||
|
||||
static enum target_attribute const translate_scale_target_attributes[] = {
|
||||
target_attribute::X,
|
||||
target_attribute::Y,
|
||||
target_attribute::Z,
|
||||
};
|
||||
|
||||
static void animate_channel_segment(channel const& channel,
|
||||
transform& transform,
|
||||
int frame_ix, float t)
|
||||
{
|
||||
enum target_attribute const * target_attributes = &channel.target_attribute;
|
||||
int target_attributes_count = 1;
|
||||
if (channel.target_attribute == target_attribute::ALL) {
|
||||
switch (transform.type) {
|
||||
case transform_type::TRANSLATE: __attribute__((fallthrough));
|
||||
case transform_type::SCALE:
|
||||
target_attributes = translate_scale_target_attributes;
|
||||
target_attributes_count = 3;
|
||||
break;
|
||||
case transform_type::ROTATE:
|
||||
target_attributes = rotate_target_attributes;
|
||||
target_attributes_count = 4;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int parameter_ix = 0; parameter_ix < target_attributes_count; parameter_ix++) {
|
||||
|
||||
enum collada::interpolation interpolation = channel.source_sampler->interpolation.interpolation_array[frame_ix];
|
||||
|
||||
float value;
|
||||
if (interpolation == interpolation::BEZIER) {
|
||||
value = bezier_sampler(channel.source_sampler, frame_ix, parameter_ix, t);
|
||||
} else {
|
||||
float iv = linear_interpolate_iv(channel.source_sampler->input, frame_ix, t);
|
||||
value = linear_interpolate_value(channel.source_sampler->output, frame_ix, parameter_ix, iv);
|
||||
}
|
||||
|
||||
apply_transform_target(transform, target_attributes[parameter_ix], value);
|
||||
}
|
||||
}
|
||||
|
||||
static void animate_node(node const& node, node_instance& node_instance, float t)
|
||||
{
|
||||
for (int i = 0; i < node.channels_count; i++) {
|
||||
@ -448,32 +540,9 @@ namespace collada_scene {
|
||||
transform& transform = node_instance.transforms[channel.target_transform_index];
|
||||
|
||||
int frame_ix = find_frame_ix(channel.source_sampler->input, t);
|
||||
if (frame_ix < 0)
|
||||
continue; // animation is missing a key frame
|
||||
assert(frame_ix >= 0); // animation is missing a key frame
|
||||
|
||||
enum collada::interpolation const * const interpolation =
|
||||
channel.source_sampler->interpolation.interpolation_array;
|
||||
|
||||
float value;
|
||||
if (interpolation[frame_ix] == interpolation::BEZIER) {
|
||||
value = bezier_sampler(channel.source_sampler, frame_ix, t);
|
||||
} else {
|
||||
float iv = interpolation_value(channel.source_sampler->input, frame_ix, t);
|
||||
value = linear_interpolate(channel.source_sampler->output, frame_ix, iv);
|
||||
}
|
||||
|
||||
switch (transform.type) {
|
||||
case transform_type::TRANSLATE:
|
||||
switch (channel.target_attribute) {
|
||||
case target_attribute::X: transform.translate = XMVectorSetX(transform.translate, value); break;
|
||||
case target_attribute::Y: transform.translate = XMVectorSetY(transform.translate, value); break;
|
||||
case target_attribute::Z: transform.translate = XMVectorSetZ(transform.translate, value); break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
animate_channel_segment(channel, transform, frame_ix, t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -129,7 +129,7 @@ XMFLOAT4 g_vLightColors[2] = {
|
||||
|
||||
//
|
||||
|
||||
XMVECTOR g_Eye = XMVectorSet(0.0f, -3.0f, 13.0f, 1.0f);
|
||||
XMVECTOR g_Eye = XMVectorSet(0.0f, -10.0f, 13.0f, 1.0f);
|
||||
XMVECTOR g_At = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
// collada scene state
|
||||
|
||||
1172
src/scenes/curve_interpolation.cpp
Normal file
1172
src/scenes/curve_interpolation.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user