diff options
Diffstat (limited to 'editor/import')
31 files changed, 4878 insertions, 1769 deletions
diff --git a/editor/import/SCsub b/editor/import/SCsub index 2b1e889fb0..359d04e5df 100644 --- a/editor/import/SCsub +++ b/editor/import/SCsub @@ -1,5 +1,5 @@ #!/usr/bin/env python -Import('env') +Import("env") env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp new file mode 100644 index 0000000000..41e71248a9 --- /dev/null +++ b/editor/import/collada.cpp @@ -0,0 +1,2407 @@ +/*************************************************************************/ +/* collada.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "collada.h" + +#include <stdio.h> + +//#define DEBUG_DEFAULT_ANIMATION +//#define DEBUG_COLLADA +#ifdef DEBUG_COLLADA +#define COLLADA_PRINT(m_what) print_line(m_what) +#else +#define COLLADA_PRINT(m_what) +#endif + +#define COLLADA_IMPORT_SCALE_SCENE + +/* HELPERS */ + +String Collada::Effect::get_texture_path(const String &p_source, Collada &state) const { + const String &image = p_source; + ERR_FAIL_COND_V(!state.state.image_map.has(image), ""); + return state.state.image_map[image].path; +} + +Transform Collada::get_root_transform() const { + Transform unit_scale_transform; +#ifndef COLLADA_IMPORT_SCALE_SCENE + unit_scale_transform.scale(Vector3(state.unit_scale, state.unit_scale, state.unit_scale)); +#endif + return unit_scale_transform; +} + +void Collada::Vertex::fix_unit_scale(Collada &state) { +#ifdef COLLADA_IMPORT_SCALE_SCENE + vertex *= state.state.unit_scale; +#endif +} + +static String _uri_to_id(const String &p_uri) { + if (p_uri.begins_with("#")) { + return p_uri.substr(1, p_uri.size() - 1); + } else { + return p_uri; + } +} + +/** HELPER FUNCTIONS **/ + +Transform Collada::fix_transform(const Transform &p_transform) { + Transform tr = p_transform; + +#ifndef NO_UP_AXIS_SWAP + + if (state.up_axis != Vector3::AXIS_Y) { + for (int i = 0; i < 3; i++) { + SWAP(tr.basis[1][i], tr.basis[state.up_axis][i]); + } + for (int i = 0; i < 3; i++) { + SWAP(tr.basis[i][1], tr.basis[i][state.up_axis]); + } + + SWAP(tr.origin[1], tr.origin[state.up_axis]); + + tr.basis[state.up_axis][0] = -tr.basis[state.up_axis][0]; + tr.basis[state.up_axis][1] = -tr.basis[state.up_axis][1]; + tr.basis[0][state.up_axis] = -tr.basis[0][state.up_axis]; + tr.basis[1][state.up_axis] = -tr.basis[1][state.up_axis]; + tr.origin[state.up_axis] = -tr.origin[state.up_axis]; + } +#endif + + //tr.scale(Vector3(state.unit_scale.unit_scale.unit_scale)); + return tr; + //return state.matrix_fix * p_transform; +} + +static Transform _read_transform_from_array(const Vector<float> &array, int ofs = 0) { + Transform tr; + // i wonder why collada matrices are transposed, given that's opposed to opengl.. + tr.basis.elements[0][0] = array[0 + ofs]; + tr.basis.elements[0][1] = array[1 + ofs]; + tr.basis.elements[0][2] = array[2 + ofs]; + tr.basis.elements[1][0] = array[4 + ofs]; + tr.basis.elements[1][1] = array[5 + ofs]; + tr.basis.elements[1][2] = array[6 + ofs]; + tr.basis.elements[2][0] = array[8 + ofs]; + tr.basis.elements[2][1] = array[9 + ofs]; + tr.basis.elements[2][2] = array[10 + ofs]; + tr.origin.x = array[3 + ofs]; + tr.origin.y = array[7 + ofs]; + tr.origin.z = array[11 + ofs]; + return tr; +} + +/* STRUCTURES */ + +Transform Collada::Node::compute_transform(Collada &state) const { + Transform xform; + + for (int i = 0; i < xform_list.size(); i++) { + Transform xform_step; + const XForm &xf = xform_list[i]; + switch (xf.op) { + case XForm::OP_ROTATE: { + if (xf.data.size() >= 4) { + xform_step.rotate(Vector3(xf.data[0], xf.data[1], xf.data[2]), Math::deg2rad(xf.data[3])); + } + } break; + case XForm::OP_SCALE: { + if (xf.data.size() >= 3) { + xform_step.scale(Vector3(xf.data[0], xf.data[1], xf.data[2])); + } + + } break; + case XForm::OP_TRANSLATE: { + if (xf.data.size() >= 3) { + xform_step.origin = Vector3(xf.data[0], xf.data[1], xf.data[2]); + } + + } break; + case XForm::OP_MATRIX: { + if (xf.data.size() >= 16) { + xform_step = _read_transform_from_array(xf.data, 0); + } + + } break; + default: { + } + } + + xform = xform * xform_step; + } + +#ifdef COLLADA_IMPORT_SCALE_SCENE + xform.origin *= state.state.unit_scale; +#endif + return xform; +} + +Transform Collada::Node::get_transform() const { + return default_transform; +} + +Transform Collada::Node::get_global_transform() const { + if (parent) { + return parent->get_global_transform() * default_transform; + } else { + return default_transform; + } +} + +Vector<float> Collada::AnimationTrack::get_value_at_time(float p_time) const { + ERR_FAIL_COND_V(keys.size() == 0, Vector<float>()); + int i = 0; + + for (i = 0; i < keys.size(); i++) { + if (keys[i].time > p_time) { + break; + } + } + + if (i == 0) { + return keys[0].data; + } + if (i == keys.size()) { + return keys[keys.size() - 1].data; + } + + switch (keys[i].interp_type) { + case INTERP_BEZIER: //wait for bezier + case INTERP_LINEAR: { + float c = (p_time - keys[i - 1].time) / (keys[i].time - keys[i - 1].time); + + if (keys[i].data.size() == 16) { + //interpolate a matrix + Transform src = _read_transform_from_array(keys[i - 1].data); + Transform dst = _read_transform_from_array(keys[i].data); + + Transform interp = c < 0.001 ? src : src.interpolate_with(dst, c); + + Vector<float> ret; + ret.resize(16); + Transform tr; + // i wonder why collada matrices are transposed, given that's opposed to opengl.. + ret.write[0] = interp.basis.elements[0][0]; + ret.write[1] = interp.basis.elements[0][1]; + ret.write[2] = interp.basis.elements[0][2]; + ret.write[4] = interp.basis.elements[1][0]; + ret.write[5] = interp.basis.elements[1][1]; + ret.write[6] = interp.basis.elements[1][2]; + ret.write[8] = interp.basis.elements[2][0]; + ret.write[9] = interp.basis.elements[2][1]; + ret.write[10] = interp.basis.elements[2][2]; + ret.write[3] = interp.origin.x; + ret.write[7] = interp.origin.y; + ret.write[11] = interp.origin.z; + ret.write[12] = 0; + ret.write[13] = 0; + ret.write[14] = 0; + ret.write[15] = 1; + + return ret; + } else { + Vector<float> dest; + dest.resize(keys[i].data.size()); + for (int j = 0; j < dest.size(); j++) { + dest.write[j] = keys[i].data[j] * c + keys[i - 1].data[j] * (1.0 - c); + } + return dest; + //interpolate one by one + } + } break; + } + + ERR_FAIL_V(Vector<float>()); +} + +void Collada::_parse_asset(XMLParser &parser) { + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String name = parser.get_node_name(); + + if (name == "up_axis") { + parser.read(); + if (parser.get_node_data() == "X_UP") { + state.up_axis = Vector3::AXIS_X; + } + if (parser.get_node_data() == "Y_UP") { + state.up_axis = Vector3::AXIS_Y; + } + if (parser.get_node_data() == "Z_UP") { + state.up_axis = Vector3::AXIS_Z; + } + + COLLADA_PRINT("up axis: " + parser.get_node_data()); + } else if (name == "unit") { + state.unit_scale = parser.get_attribute_value("meter").to_double(); + COLLADA_PRINT("unit scale: " + rtos(state.unit_scale)); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "asset") { + break; //end of <asset> + } + } +} + +void Collada::_parse_image(XMLParser &parser) { + String id = parser.get_attribute_value("id"); + + if (!(state.import_flags & IMPORT_FLAG_SCENE)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + return; + } + + Image image; + + if (state.version < State::Version(1, 4, 0)) { + /* <1.4 */ + String path = parser.get_attribute_value("source").strip_edges(); + if (path.find("://") == -1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + image.path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().plus_file(path.percent_decode())); + } + } else { + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String name = parser.get_node_name(); + + if (name == "init_from") { + parser.read(); + String path = parser.get_node_data().strip_edges().percent_decode(); + + if (path.find("://") == -1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().plus_file(path)); + + } else if (path.find("file:///") == 0) { + path = path.replace_first("file:///", ""); + path = ProjectSettings::get_singleton()->localize_path(path); + } + + image.path = path; + + } else if (name == "data") { + ERR_PRINT("COLLADA Embedded image data not supported!"); + + } else if (name == "extra" && !parser.is_empty()) { + parser.skip_section(); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "image") { + break; //end of <asset> + } + } + } + + state.image_map[id] = image; +} + +void Collada::_parse_material(XMLParser &parser) { + if (!(state.import_flags & IMPORT_FLAG_SCENE)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + return; + } + + Material material; + + String id = parser.get_attribute_value("id"); + if (parser.has_attribute("name")) { + material.name = parser.get_attribute_value("name"); + } + + if (state.version < State::Version(1, 4, 0)) { + /* <1.4 */ + ERR_PRINT("Collada Materials < 1.4 are not supported (yet)"); + } else { + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT && parser.get_node_name() == "instance_effect") { + material.instance_effect = _uri_to_id(parser.get_attribute_value("url")); + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "material") { + break; //end of <asset> + } + } + } + + state.material_map[id] = material; +} + +//! reads floats from inside of xml element until end of xml element +Vector<float> Collada::_read_float_array(XMLParser &parser) { + if (parser.is_empty()) { + return Vector<float>(); + } + + Vector<String> splitters; + splitters.push_back(" "); + splitters.push_back("\n"); + splitters.push_back("\r"); + splitters.push_back("\t"); + + Vector<float> array; + while (parser.read() == OK) { + // TODO: check for comments inside the element + // and ignore them. + + if (parser.get_node_type() == XMLParser::NODE_TEXT) { + // parse float data + String str = parser.get_node_data(); + array = str.split_floats_mk(splitters, false); + //array=str.split_floats(" ",false); + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END) { + break; // end parsing text + } + } + + return array; +} + +Vector<String> Collada::_read_string_array(XMLParser &parser) { + if (parser.is_empty()) { + return Vector<String>(); + } + + Vector<String> array; + while (parser.read() == OK) { + // TODO: check for comments inside the element + // and ignore them. + + if (parser.get_node_type() == XMLParser::NODE_TEXT) { + // parse String data + String str = parser.get_node_data(); + array = str.split_spaces(); + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END) { + break; // end parsing text + } + } + + return array; +} + +Transform Collada::_read_transform(XMLParser &parser) { + if (parser.is_empty()) { + return Transform(); + } + + Vector<String> array; + while (parser.read() == OK) { + // TODO: check for comments inside the element + // and ignore them. + + if (parser.get_node_type() == XMLParser::NODE_TEXT) { + // parse float data + String str = parser.get_node_data(); + array = str.split_spaces(); + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END) { + break; // end parsing text + } + } + + ERR_FAIL_COND_V(array.size() != 16, Transform()); + Vector<float> farr; + farr.resize(16); + for (int i = 0; i < 16; i++) { + farr.write[i] = array[i].to_double(); + } + + return _read_transform_from_array(farr); +} + +String Collada::_read_empty_draw_type(XMLParser &parser) { + String empty_draw_type = ""; + + if (parser.is_empty()) { + return empty_draw_type; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_TEXT) { + empty_draw_type = parser.get_node_data(); + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END) { + break; // end parsing text + } + } + return empty_draw_type; +} + +Variant Collada::_parse_param(XMLParser &parser) { + if (parser.is_empty()) { + return Variant(); + } + + String from = parser.get_node_name(); + Variant data; + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "float") { + parser.read(); + if (parser.get_node_type() == XMLParser::NODE_TEXT) { + data = parser.get_node_data().to_double(); + } + } else if (parser.get_node_name() == "float2") { + Vector<float> v2 = _read_float_array(parser); + + if (v2.size() >= 2) { + data = Vector2(v2[0], v2[1]); + } + } else if (parser.get_node_name() == "float3") { + Vector<float> v3 = _read_float_array(parser); + + if (v3.size() >= 3) { + data = Vector3(v3[0], v3[1], v3[2]); + } + } else if (parser.get_node_name() == "float4") { + Vector<float> v4 = _read_float_array(parser); + + if (v4.size() >= 4) { + data = Color(v4[0], v4[1], v4[2], v4[3]); + } + } else if (parser.get_node_name() == "sampler2D") { + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "source") { + parser.read(); + + if (parser.get_node_type() == XMLParser::NODE_TEXT) { + data = parser.get_node_data(); + } + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "sampler2D") { + break; + } + } + } else if (parser.get_node_name() == "surface") { + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "init_from") { + parser.read(); + + if (parser.get_node_type() == XMLParser::NODE_TEXT) { + data = parser.get_node_data(); + } + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "surface") { + break; + } + } + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == from) { + break; + } + } + + COLLADA_PRINT("newparam ending " + parser.get_node_name()); + return data; +} + +void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &id) { + if (!(state.import_flags & IMPORT_FLAG_SCENE)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + return; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + // first come the tags we descend, but ignore the top-levels + + COLLADA_PRINT("node name: " + parser.get_node_name()); + + if (!parser.is_empty() && (parser.get_node_name() == "profile_COMMON" || parser.get_node_name() == "technique" || parser.get_node_name() == "extra")) { + _parse_effect_material(parser, effect, id); // try again + + } else if (parser.get_node_name() == "newparam") { + String name = parser.get_attribute_value("sid"); + Variant value = _parse_param(parser); + effect.params[name] = value; + COLLADA_PRINT("param: " + name + " value:" + String(value)); + + } else if (parser.get_node_name() == "constant" || + parser.get_node_name() == "lambert" || + parser.get_node_name() == "phong" || + parser.get_node_name() == "blinn") { + COLLADA_PRINT("shade model: " + parser.get_node_name()); + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String what = parser.get_node_name(); + + if (what == "emission" || + what == "diffuse" || + what == "specular" || + what == "reflective") { + // color or texture types + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "color") { + Vector<float> colorarr = _read_float_array(parser); + COLLADA_PRINT("colorarr size: " + rtos(colorarr.size())); + + if (colorarr.size() >= 3) { + // alpha strangely not alright? maybe it needs to be multiplied by value as a channel intensity + Color color(colorarr[0], colorarr[1], colorarr[2], 1.0); + if (what == "diffuse") { + effect.diffuse.color = color; + } + if (what == "specular") { + effect.specular.color = color; + } + if (what == "emission") { + effect.emission.color = color; + } + + COLLADA_PRINT(what + " color: " + color); + } + + } else if (parser.get_node_name() == "texture") { + String sampler = parser.get_attribute_value("texture"); + if (!effect.params.has(sampler)) { + ERR_PRINT(String("Couldn't find sampler: " + sampler + " in material:" + id).utf8().get_data()); + } else { + String surface = effect.params[sampler]; + + if (!effect.params.has(surface)) { + ERR_PRINT(String("Couldn't find surface: " + surface + " in material:" + id).utf8().get_data()); + } else { + String uri = effect.params[surface]; + + if (what == "diffuse") { + effect.diffuse.texture = uri; + } else if (what == "specular") { + effect.specular.texture = uri; + } else if (what == "emission") { + effect.emission.texture = uri; + } else if (what == "bump") { + if (parser.has_attribute("bumptype") && parser.get_attribute_value("bumptype") != "NORMALMAP") { + WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported."); + } + + effect.bump.texture = uri; + } + + COLLADA_PRINT(what + " texture: " + uri); + } + } + } else if (!parser.is_empty()) { + parser.skip_section(); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == what) { + break; + } + } + + } else if (what == "shininess") { + effect.shininess = _parse_param(parser); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && (parser.get_node_name() == "constant" || + parser.get_node_name() == "lambert" || + parser.get_node_name() == "phong" || + parser.get_node_name() == "blinn")) { + break; + } + } + } else if (parser.get_node_name() == "double_sided" || parser.get_node_name() == "show_double_sided") { // colladamax / google earth + + // 3DS Max / Google Earth double sided extension + parser.read(); + effect.found_double_sided = true; + effect.double_sided = parser.get_node_data().to_int(); + COLLADA_PRINT("double sided: " + itos(parser.get_node_data().to_int())); + } else if (parser.get_node_name() == "unshaded") { + parser.read(); + effect.unshaded = parser.get_node_data().to_int(); + } else if (parser.get_node_name() == "bump") { + // color or texture types + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "texture") { + String sampler = parser.get_attribute_value("texture"); + if (!effect.params.has(sampler)) { + ERR_PRINT(String("Couldn't find sampler: " + sampler + " in material:" + id).utf8().get_data()); + } else { + String surface = effect.params[sampler]; + + if (!effect.params.has(surface)) { + ERR_PRINT(String("Couldn't find surface: " + surface + " in material:" + id).utf8().get_data()); + } else { + String uri = effect.params[surface]; + + if (parser.has_attribute("bumptype") && parser.get_attribute_value("bumptype") != "NORMALMAP") { + WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported."); + } + + effect.bump.texture = uri; + COLLADA_PRINT(" bump: " + uri); + } + } + } else if (!parser.is_empty()) { + parser.skip_section(); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "bump") { + break; + } + } + + } else if (!parser.is_empty()) { + parser.skip_section(); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && + (parser.get_node_name() == "effect" || + parser.get_node_name() == "profile_COMMON" || + parser.get_node_name() == "technique" || + parser.get_node_name() == "extra")) { + break; + } + } +} + +void Collada::_parse_effect(XMLParser &parser) { + if (!(state.import_flags & IMPORT_FLAG_SCENE)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + return; + } + + String id = parser.get_attribute_value("id"); + + Effect effect; + if (parser.has_attribute("name")) { + effect.name = parser.get_attribute_value("name"); + } + _parse_effect_material(parser, effect, id); + + state.effect_map[id] = effect; + + COLLADA_PRINT("Effect ID:" + id); +} + +void Collada::_parse_camera(XMLParser &parser) { + if (!(state.import_flags & IMPORT_FLAG_SCENE)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + return; + } + + String id = parser.get_attribute_value("id"); + + state.camera_data_map[id] = CameraData(); + CameraData &camera = state.camera_data_map[id]; + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String name = parser.get_node_name(); + + if (name == "perspective") { + camera.mode = CameraData::MODE_PERSPECTIVE; + } else if (name == "orthographic") { + camera.mode = CameraData::MODE_ORTHOGONAL; + } else if (name == "xfov") { + parser.read(); + camera.perspective.x_fov = parser.get_node_data().to_double(); + + } else if (name == "yfov") { + parser.read(); + camera.perspective.y_fov = parser.get_node_data().to_double(); + } else if (name == "xmag") { + parser.read(); + camera.orthogonal.x_mag = parser.get_node_data().to_double(); + + } else if (name == "ymag") { + parser.read(); + camera.orthogonal.y_mag = parser.get_node_data().to_double(); + } else if (name == "aspect_ratio") { + parser.read(); + camera.aspect = parser.get_node_data().to_double(); + + } else if (name == "znear") { + parser.read(); + camera.z_near = parser.get_node_data().to_double(); + + } else if (name == "zfar") { + parser.read(); + camera.z_far = parser.get_node_data().to_double(); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "camera") { + break; //end of <asset> + } + } + + COLLADA_PRINT("Camera ID:" + id); +} + +void Collada::_parse_light(XMLParser &parser) { + if (!(state.import_flags & IMPORT_FLAG_SCENE)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + return; + } + + String id = parser.get_attribute_value("id"); + + state.light_data_map[id] = LightData(); + LightData &light = state.light_data_map[id]; + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String name = parser.get_node_name(); + + if (name == "ambient") { + light.mode = LightData::MODE_AMBIENT; + } else if (name == "directional") { + light.mode = LightData::MODE_DIRECTIONAL; + } else if (name == "point") { + light.mode = LightData::MODE_OMNI; + } else if (name == "spot") { + light.mode = LightData::MODE_SPOT; + } else if (name == "color") { + parser.read(); + Vector<float> colorarr = _read_float_array(parser); + COLLADA_PRINT("colorarr size: " + rtos(colorarr.size())); + + if (colorarr.size() >= 4) { + // alpha strangely not alright? maybe it needs to be multiplied by value as a channel intensity + Color color(colorarr[0], colorarr[1], colorarr[2], 1.0); + light.color = color; + } + + } else if (name == "constant_attenuation") { + parser.read(); + light.constant_att = parser.get_node_data().to_double(); + } else if (name == "linear_attenuation") { + parser.read(); + light.linear_att = parser.get_node_data().to_double(); + } else if (name == "quadratic_attenuation") { + parser.read(); + light.quad_att = parser.get_node_data().to_double(); + } else if (name == "falloff_angle") { + parser.read(); + light.spot_angle = parser.get_node_data().to_double(); + + } else if (name == "falloff_exponent") { + parser.read(); + light.spot_exp = parser.get_node_data().to_double(); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "light") { + break; //end of <asset> + } + } + + COLLADA_PRINT("Light ID:" + id); +} + +void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_name) { + if (!(state.import_flags & IMPORT_FLAG_SCENE)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + return; + } + + //load everything into a pre dictionary + + state.curve_data_map[p_id] = CurveData(); + + CurveData &curvedata = state.curve_data_map[p_id]; + curvedata.name = p_name; + + COLLADA_PRINT("curve name: " + p_name); + + String current_source; + // handles geometry node and the curve children in this loop + // read sources with arrays and accessor for each curve + if (parser.is_empty()) { + return; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String section = parser.get_node_name(); + + if (section == "source") { + String id = parser.get_attribute_value("id"); + curvedata.sources[id] = CurveData::Source(); + current_source = id; + COLLADA_PRINT("source data: " + id); + + } else if (section == "float_array" || section == "array") { + // create a new array and read it. + if (curvedata.sources.has(current_source)) { + curvedata.sources[current_source].array = _read_float_array(parser); + COLLADA_PRINT("section: " + current_source + " read " + itos(curvedata.sources[current_source].array.size()) + " values."); + } + } else if (section == "Name_array") { + // create a new array and read it. + if (curvedata.sources.has(current_source)) { + curvedata.sources[current_source].sarray = _read_string_array(parser); + COLLADA_PRINT("section: " + current_source + " read " + itos(curvedata.sources[current_source].array.size()) + " values."); + } + + } else if (section == "technique_common") { + //skip it + } else if (section == "accessor") { // child of source (below a technique tag) + + if (curvedata.sources.has(current_source)) { + curvedata.sources[current_source].stride = parser.get_attribute_value("stride").to_int(); + COLLADA_PRINT("section: " + current_source + " stride " + itos(curvedata.sources[current_source].stride)); + } + } else if (section == "control_vertices") { + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "input") { + String semantic = parser.get_attribute_value("semantic"); + String source = _uri_to_id(parser.get_attribute_value("source")); + + curvedata.control_vertices[semantic] = source; + + COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) { + break; + } + } + + } else if (!parser.is_empty()) { + parser.skip_section(); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "spline") { + break; + } + } +} + +void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name) { + if (!(state.import_flags & IMPORT_FLAG_SCENE)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + return; + } + + //load everything into a pre dictionary + + state.mesh_data_map[p_id] = MeshData(); + + MeshData &meshdata = state.mesh_data_map[p_id]; + meshdata.name = p_name; + + COLLADA_PRINT("mesh name: " + p_name); + + String current_source; + // handles geometry node and the mesh children in this loop + // read sources with arrays and accessor for each mesh + if (parser.is_empty()) { + return; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String section = parser.get_node_name(); + + if (section == "source") { + String id = parser.get_attribute_value("id"); + meshdata.sources[id] = MeshData::Source(); + current_source = id; + COLLADA_PRINT("source data: " + id); + + } else if (section == "float_array" || section == "array") { + // create a new array and read it. + if (meshdata.sources.has(current_source)) { + meshdata.sources[current_source].array = _read_float_array(parser); + COLLADA_PRINT("section: " + current_source + " read " + itos(meshdata.sources[current_source].array.size()) + " values."); + } + } else if (section == "technique_common") { + //skip it + } else if (section == "accessor") { // child of source (below a technique tag) + + if (meshdata.sources.has(current_source)) { + meshdata.sources[current_source].stride = parser.get_attribute_value("stride").to_int(); + COLLADA_PRINT("section: " + current_source + " stride " + itos(meshdata.sources[current_source].stride)); + } + } else if (section == "vertices") { + MeshData::Vertices vert; + String id = parser.get_attribute_value("id"); + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "input") { + String semantic = parser.get_attribute_value("semantic"); + String source = _uri_to_id(parser.get_attribute_value("source")); + + vert.sources[semantic] = source; + + COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) { + break; + } + } + + meshdata.vertices[id] = vert; + + } else if (section == "triangles" || section == "polylist" || section == "polygons") { + bool polygons = (section == "polygons"); + if (polygons) { + WARN_PRINT("Primitive type \"polygons\" is not well supported (concave shapes may fail). To ensure that the geometry is properly imported, please re-export using \"triangles\" or \"polylist\"."); + } + MeshData::Primitives prim; + + if (parser.has_attribute("material")) { + prim.material = parser.get_attribute_value("material"); + } + prim.count = parser.get_attribute_value("count").to_int(); + prim.vertex_size = 0; + int last_ref = 0; + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "input") { + String semantic = parser.get_attribute_value("semantic"); + String source = _uri_to_id(parser.get_attribute_value("source")); + + if (semantic == "TEXCOORD") { + /* + if (parser.has_attribute("set"))// a texcoord + semantic+=parser.get_attribute_value("set"); + else + semantic="TEXCOORD0";*/ + semantic = "TEXCOORD" + itos(last_ref++); + } + int offset = parser.get_attribute_value("offset").to_int(); + + MeshData::Primitives::SourceRef sref; + sref.source = source; + sref.offset = offset; + prim.sources[semantic] = sref; + prim.vertex_size = MAX(prim.vertex_size, offset + 1); + + COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source + " offset: " + itos(offset)); + + } else if (parser.get_node_name() == "p") { //indices + + Vector<float> values = _read_float_array(parser); + if (polygons) { + ERR_CONTINUE(prim.vertex_size == 0); + prim.polygons.push_back(values.size() / prim.vertex_size); + int from = prim.indices.size(); + prim.indices.resize(from + values.size()); + for (int i = 0; i < values.size(); i++) { + prim.indices.write[from + i] = values[i]; + } + + } else if (prim.vertex_size > 0) { + prim.indices = values; + } + + COLLADA_PRINT("read " + itos(values.size()) + " index values"); + + } else if (parser.get_node_name() == "vcount") { // primitive + + Vector<float> values = _read_float_array(parser); + prim.polygons = values; + COLLADA_PRINT("read " + itos(values.size()) + " polygon values"); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) { + break; + } + } + + meshdata.primitives.push_back(prim); + + } else if (parser.get_node_name() == "double_sided") { + parser.read(); + meshdata.found_double_sided = true; + meshdata.double_sided = parser.get_node_data().to_int(); + + } else if (parser.get_node_name() == "polygons") { + ERR_PRINT("Primitive type \"polygons\" not supported, re-export using \"polylist\" or \"triangles\"."); + } else if (!parser.is_empty()) { + parser.skip_section(); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "mesh") { + break; + } + } +} + +void Collada::_parse_skin_controller(XMLParser &parser, String p_id) { + state.skin_controller_data_map[p_id] = SkinControllerData(); + SkinControllerData &skindata = state.skin_controller_data_map[p_id]; + + skindata.base = _uri_to_id(parser.get_attribute_value("source")); + + String current_source; + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String section = parser.get_node_name(); + + if (section == "bind_shape_matrix") { + skindata.bind_shape = _read_transform(parser); +#ifdef COLLADA_IMPORT_SCALE_SCENE + skindata.bind_shape.origin *= state.unit_scale; + +#endif + COLLADA_PRINT("skeleton bind shape transform: " + skindata.bind_shape); + + } else if (section == "source") { + String id = parser.get_attribute_value("id"); + skindata.sources[id] = SkinControllerData::Source(); + current_source = id; + COLLADA_PRINT("source data: " + id); + + } else if (section == "float_array" || section == "array") { + // create a new array and read it. + if (skindata.sources.has(current_source)) { + skindata.sources[current_source].array = _read_float_array(parser); + COLLADA_PRINT("section: " + current_source + " read " + itos(skindata.sources[current_source].array.size()) + " values."); + } + } else if (section == "Name_array" || section == "IDREF_array") { + // create a new array and read it. + + if (section == "IDREF_array") { + skindata.use_idrefs = true; + } + if (skindata.sources.has(current_source)) { + skindata.sources[current_source].sarray = _read_string_array(parser); + if (section == "IDREF_array") { + Vector<String> sa = skindata.sources[current_source].sarray; + for (int i = 0; i < sa.size(); i++) { + state.idref_joints.insert(sa[i]); + } + } + COLLADA_PRINT("section: " + current_source + " read " + itos(skindata.sources[current_source].array.size()) + " values."); + } + } else if (section == "technique_common") { + //skip it + } else if (section == "accessor") { // child of source (below a technique tag) + + if (skindata.sources.has(current_source)) { + int stride = 1; + if (parser.has_attribute("stride")) { + stride = parser.get_attribute_value("stride").to_int(); + } + + skindata.sources[current_source].stride = stride; + COLLADA_PRINT("section: " + current_source + " stride " + itos(skindata.sources[current_source].stride)); + } + + } else if (section == "joints") { + SkinControllerData::Joints joint; + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "input") { + String semantic = parser.get_attribute_value("semantic"); + String source = _uri_to_id(parser.get_attribute_value("source")); + + joint.sources[semantic] = source; + + COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) { + break; + } + } + + skindata.joints = joint; + + } else if (section == "vertex_weights") { + SkinControllerData::Weights weights; + + weights.count = parser.get_attribute_value("count").to_int(); + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "input") { + String semantic = parser.get_attribute_value("semantic"); + String source = _uri_to_id(parser.get_attribute_value("source")); + + int offset = parser.get_attribute_value("offset").to_int(); + + SkinControllerData::Weights::SourceRef sref; + sref.source = source; + sref.offset = offset; + weights.sources[semantic] = sref; + + COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source + " offset: " + itos(offset)); + + } else if (parser.get_node_name() == "v") { //indices + + Vector<float> values = _read_float_array(parser); + weights.indices = values; + COLLADA_PRINT("read " + itos(values.size()) + " index values"); + + } else if (parser.get_node_name() == "vcount") { // weightsitive + + Vector<float> values = _read_float_array(parser); + weights.sets = values; + COLLADA_PRINT("read " + itos(values.size()) + " polygon values"); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) { + break; + } + } + + skindata.weights = weights; + } + /* + else if (!parser.is_empty()) + parser.skip_section(); + */ + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "skin") { + break; + } + } + + /* STORE REST MATRICES */ + + Vector<Transform> rests; + ERR_FAIL_COND(!skindata.joints.sources.has("JOINT")); + ERR_FAIL_COND(!skindata.joints.sources.has("INV_BIND_MATRIX")); + + String joint_arr = skindata.joints.sources["JOINT"]; + String ibm = skindata.joints.sources["INV_BIND_MATRIX"]; + + ERR_FAIL_COND(!skindata.sources.has(joint_arr)); + ERR_FAIL_COND(!skindata.sources.has(ibm)); + + SkinControllerData::Source &joint_source = skindata.sources[joint_arr]; + SkinControllerData::Source &ibm_source = skindata.sources[ibm]; + + ERR_FAIL_COND(joint_source.sarray.size() != ibm_source.array.size() / 16); + + for (int i = 0; i < joint_source.sarray.size(); i++) { + String name = joint_source.sarray[i]; + Transform xform = _read_transform_from_array(ibm_source.array, i * 16); //<- this is a mistake, it must be applied to vertices + xform.affine_invert(); // inverse for rest, because it's an inverse +#ifdef COLLADA_IMPORT_SCALE_SCENE + xform.origin *= state.unit_scale; +#endif + skindata.bone_rest_map[name] = xform; + } +} + +void Collada::_parse_morph_controller(XMLParser &parser, String p_id) { + state.morph_controller_data_map[p_id] = MorphControllerData(); + MorphControllerData &morphdata = state.morph_controller_data_map[p_id]; + + morphdata.mesh = _uri_to_id(parser.get_attribute_value("source")); + morphdata.mode = parser.get_attribute_value("method"); + String current_source; + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String section = parser.get_node_name(); + + if (section == "source") { + String id = parser.get_attribute_value("id"); + morphdata.sources[id] = MorphControllerData::Source(); + current_source = id; + COLLADA_PRINT("source data: " + id); + + } else if (section == "float_array" || section == "array") { + // create a new array and read it. + if (morphdata.sources.has(current_source)) { + morphdata.sources[current_source].array = _read_float_array(parser); + COLLADA_PRINT("section: " + current_source + " read " + itos(morphdata.sources[current_source].array.size()) + " values."); + } + } else if (section == "Name_array" || section == "IDREF_array") { + // create a new array and read it. + + /* + if (section=="IDREF_array") + morphdata.use_idrefs=true; + */ + if (morphdata.sources.has(current_source)) { + morphdata.sources[current_source].sarray = _read_string_array(parser); + /* + if (section=="IDREF_array") { + Vector<String> sa = morphdata.sources[current_source].sarray; + for(int i=0;i<sa.size();i++) + state.idref_joints.insert(sa[i]); + }*/ + COLLADA_PRINT("section: " + current_source + " read " + itos(morphdata.sources[current_source].array.size()) + " values."); + } + } else if (section == "technique_common") { + //skip it + } else if (section == "accessor") { // child of source (below a technique tag) + + if (morphdata.sources.has(current_source)) { + int stride = 1; + if (parser.has_attribute("stride")) { + stride = parser.get_attribute_value("stride").to_int(); + } + + morphdata.sources[current_source].stride = stride; + COLLADA_PRINT("section: " + current_source + " stride " + itos(morphdata.sources[current_source].stride)); + } + + } else if (section == "targets") { + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "input") { + String semantic = parser.get_attribute_value("semantic"); + String source = _uri_to_id(parser.get_attribute_value("source")); + + morphdata.targets[semantic] = source; + + COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) { + break; + } + } + } + /* + else if (!parser.is_empty()) + parser.skip_section(); + */ + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "morph") { + break; + } + } + + if (morphdata.targets.has("MORPH_WEIGHT")) { + state.morph_name_map[morphdata.targets["MORPH_WEIGHT"]] = p_id; + } +} + +void Collada::_parse_controller(XMLParser &parser) { + String id = parser.get_attribute_value("id"); + + if (parser.is_empty()) { + return; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String section = parser.get_node_name(); + + if (section == "skin") { + _parse_skin_controller(parser, id); + } else if (section == "morph") { + _parse_morph_controller(parser, id); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "controller") { + break; + } + } +} + +Collada::Node *Collada::_parse_visual_instance_geometry(XMLParser &parser) { + String type = parser.get_node_name(); + NodeGeometry *geom = memnew(NodeGeometry); + geom->controller = type == "instance_controller"; + geom->source = _uri_to_id(parser.get_attribute_value_safe("url")); + + if (parser.is_empty()) { //nothing else to parse... + return geom; + } + // try to find also many materials and skeletons! + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "instance_material") { + String symbol = parser.get_attribute_value("symbol"); + String target = _uri_to_id(parser.get_attribute_value("target")); + + NodeGeometry::Material mat; + mat.target = target; + geom->material_map[symbol] = mat; + COLLADA_PRINT("uses material: '" + target + "' on primitive'" + symbol + "'"); + } else if (parser.get_node_name() == "skeleton") { + parser.read(); + String uri = _uri_to_id(parser.get_node_data()); + if (uri != "") { + geom->skeletons.push_back(uri); + } + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == type) { + break; + } + } + + if (geom->controller) { + if (geom->skeletons.empty()) { + //XSI style + + if (state.skin_controller_data_map.has(geom->source)) { + SkinControllerData *skin = &state.skin_controller_data_map[geom->source]; + //case where skeletons reference bones with IDREF (XSI) + ERR_FAIL_COND_V(!skin->joints.sources.has("JOINT"), geom); + String joint_arr = skin->joints.sources["JOINT"]; + ERR_FAIL_COND_V(!skin->sources.has(joint_arr), geom); + Collada::SkinControllerData::Source &joint_source = skin->sources[joint_arr]; + geom->skeletons = joint_source.sarray; //quite crazy, but should work. + } + } + } + + return geom; +} + +Collada::Node *Collada::_parse_visual_instance_camera(XMLParser &parser) { + NodeCamera *cam = memnew(NodeCamera); + cam->camera = _uri_to_id(parser.get_attribute_value_safe("url")); + + if (state.up_axis == Vector3::AXIS_Z) { //collada weirdness + cam->post_transform.basis.rotate(Vector3(1, 0, 0), -Math_PI * 0.5); + } + + if (parser.is_empty()) { //nothing else to parse... + return cam; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "instance_camera") { + break; + } + } + + return cam; +} + +Collada::Node *Collada::_parse_visual_instance_light(XMLParser &parser) { + NodeLight *cam = memnew(NodeLight); + cam->light = _uri_to_id(parser.get_attribute_value_safe("url")); + + if (state.up_axis == Vector3::AXIS_Z) { //collada weirdness + cam->post_transform.basis.rotate(Vector3(1, 0, 0), -Math_PI * 0.5); + } + + if (parser.is_empty()) { //nothing else to parse... + return cam; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "instance_light") { + break; + } + } + + return cam; +} + +Collada::Node *Collada::_parse_visual_node_instance_data(XMLParser &parser) { + String instance_type = parser.get_node_name(); + + if (instance_type == "instance_geometry" || instance_type == "instance_controller") { + return _parse_visual_instance_geometry(parser); + } else if (instance_type == "instance_camera") { + return _parse_visual_instance_camera(parser); + } else if (instance_type == "instance_light") { + return _parse_visual_instance_light(parser); + } + + if (parser.is_empty()) { //nothing else to parse... + return nullptr; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == instance_type) { + break; + } + } + + return nullptr; +} + +Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) { + String name; + + String id = parser.get_attribute_value_safe("id"); + + bool found_name = false; + + if (id == "") { + id = "%NODEID%" + itos(Math::rand()); + + } else { + found_name = true; + } + + Vector<Node::XForm> xform_list; + Vector<Node *> children; + + String empty_draw_type = ""; + + Node *node = nullptr; + + name = parser.has_attribute("name") ? parser.get_attribute_value_safe("name") : parser.get_attribute_value_safe("id"); + if (name == "") { + name = id; + } else { + found_name = true; + } + + if ((parser.has_attribute("type") && parser.get_attribute_value("type") == "JOINT") || state.idref_joints.has(name)) { + // handle a bone + + NodeJoint *joint = memnew(NodeJoint); + + if (parser.has_attribute("sid")) { //bones may not have sid + joint->sid = parser.get_attribute_value("sid"); + //state.bone_map[joint->sid]=joint; + } else if (state.idref_joints.has(name)) { + joint->sid = name; //kind of a cheat but.. + } else if (parser.has_attribute("name")) { + joint->sid = parser.get_attribute_value_safe("name"); + } + + if (joint->sid != "") { + state.sid_to_node_map[joint->sid] = id; + } + + node = joint; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String section = parser.get_node_name(); + + if (section == "translate") { + Node::XForm xf; + if (parser.has_attribute("sid")) { + xf.id = parser.get_attribute_value("sid"); + } + xf.op = Node::XForm::OP_TRANSLATE; + + Vector<float> xlt = _read_float_array(parser); + xf.data = xlt; + xform_list.push_back(xf); + + } else if (section == "rotate") { + Node::XForm xf; + if (parser.has_attribute("sid")) { + xf.id = parser.get_attribute_value("sid"); + } + xf.op = Node::XForm::OP_ROTATE; + + Vector<float> rot = _read_float_array(parser); + xf.data = rot; + + xform_list.push_back(xf); + + } else if (section == "scale") { + Node::XForm xf; + if (parser.has_attribute("sid")) { + xf.id = parser.get_attribute_value("sid"); + } + + xf.op = Node::XForm::OP_SCALE; + + Vector<float> scale = _read_float_array(parser); + + xf.data = scale; + + xform_list.push_back(xf); + + } else if (section == "matrix") { + Node::XForm xf; + if (parser.has_attribute("sid")) { + xf.id = parser.get_attribute_value("sid"); + } + xf.op = Node::XForm::OP_MATRIX; + + Vector<float> matrix = _read_float_array(parser); + + xf.data = matrix; + String mtx; + for (int i = 0; i < matrix.size(); i++) { + mtx += " " + rtos(matrix[i]); + } + + xform_list.push_back(xf); + + } else if (section == "visibility") { + Node::XForm xf; + if (parser.has_attribute("sid")) { + xf.id = parser.get_attribute_value("sid"); + } + xf.op = Node::XForm::OP_VISIBILITY; + + Vector<float> visible = _read_float_array(parser); + + xf.data = visible; + + xform_list.push_back(xf); + + } else if (section == "empty_draw_type") { + empty_draw_type = _read_empty_draw_type(parser); + } else if (section == "technique" || section == "extra") { + } else if (section != "node") { + //usually what defines the type of node + if (section.begins_with("instance_")) { + if (!node) { + node = _parse_visual_node_instance_data(parser); + + } else { + ERR_PRINT("Multiple instance_* not supported."); + } + } + + } else { + /* Found a child node!! what to do..*/ + + Node *child = _parse_visual_scene_node(parser); + children.push_back(child); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "node") { + break; + } + } + + if (!node) { + node = memnew(Node); //generic node, nothing of relevance found + } + + node->noname = !found_name; + node->xform_list = xform_list; + node->children = children; + for (int i = 0; i < children.size(); i++) { + node->children[i]->parent = node; + } + + node->name = name; + node->id = id; + node->empty_draw_type = empty_draw_type; + + if (node->children.size() == 1) { + if (node->children[0]->noname && !node->noname) { + node->children[0]->name = node->name; + node->name = node->name + "-base"; + } + } + + node->default_transform = node->compute_transform(*this); + state.scene_map[id] = node; + + return node; +} + +void Collada::_parse_visual_scene(XMLParser &parser) { + String id = parser.get_attribute_value("id"); + + if (parser.is_empty()) { + return; + } + + state.visual_scene_map[id] = VisualScene(); + VisualScene &vscene = state.visual_scene_map[id]; + + if (parser.has_attribute("name")) { + vscene.name = parser.get_attribute_value("name"); + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String section = parser.get_node_name(); + + if (section == "node") { + vscene.root_nodes.push_back(_parse_visual_scene_node(parser)); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "visual_scene") { + break; + } + } + + COLLADA_PRINT("Scene ID:" + id); +} + +void Collada::_parse_animation(XMLParser &parser) { + if (!(state.import_flags & IMPORT_FLAG_ANIMATION)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + + return; + } + + Map<String, Vector<float>> float_sources; + Map<String, Vector<String>> string_sources; + Map<String, int> source_strides; + Map<String, Map<String, String>> samplers; + Map<String, Vector<String>> source_param_names; + Map<String, Vector<String>> source_param_types; + + String id = ""; + if (parser.has_attribute("id")) { + id = parser.get_attribute_value("id"); + } + + String current_source; + String current_sampler; + Vector<String> channel_sources; + Vector<String> channel_targets; + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String name = parser.get_node_name(); + if (name == "source") { + current_source = parser.get_attribute_value("id"); + source_param_names[current_source] = Vector<String>(); + source_param_types[current_source] = Vector<String>(); + + } else if (name == "float_array") { + if (current_source != "") { + float_sources[current_source] = _read_float_array(parser); + } + + } else if (name == "Name_array") { + if (current_source != "") { + string_sources[current_source] = _read_string_array(parser); + } + } else if (name == "accessor") { + if (current_source != "" && parser.has_attribute("stride")) { + source_strides[current_source] = parser.get_attribute_value("stride").to_int(); + } + } else if (name == "sampler") { + current_sampler = parser.get_attribute_value("id"); + samplers[current_sampler] = Map<String, String>(); + } else if (name == "param") { + if (parser.has_attribute("name")) { + source_param_names[current_source].push_back(parser.get_attribute_value("name")); + } else { + source_param_names[current_source].push_back(""); + } + + if (parser.has_attribute("type")) { + source_param_types[current_source].push_back(parser.get_attribute_value("type")); + } else { + source_param_types[current_source].push_back(""); + } + + } else if (name == "input") { + if (current_sampler != "") { + samplers[current_sampler][parser.get_attribute_value("semantic")] = parser.get_attribute_value("source"); + } + + } else if (name == "channel") { + channel_sources.push_back(parser.get_attribute_value("source")); + channel_targets.push_back(parser.get_attribute_value("target")); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "animation") { + break; //end of <asset> + } + } + + for (int i = 0; i < channel_sources.size(); i++) { + String source = _uri_to_id(channel_sources[i]); + String target = channel_targets[i]; + ERR_CONTINUE(!samplers.has(source)); + Map<String, String> &sampler = samplers[source]; + + ERR_CONTINUE(!sampler.has("INPUT")); //no input semantic? wtf? + String input_id = _uri_to_id(sampler["INPUT"]); + COLLADA_PRINT("input id is " + input_id); + ERR_CONTINUE(!float_sources.has(input_id)); + + ERR_CONTINUE(!sampler.has("OUTPUT")); + String output_id = _uri_to_id(sampler["OUTPUT"]); + ERR_CONTINUE(!float_sources.has(output_id)); + + ERR_CONTINUE(!source_param_names.has(output_id)); + + Vector<String> &names = source_param_names[output_id]; + + for (int l = 0; l < names.size(); l++) { + String name = names[l]; + + Vector<float> &time_keys = float_sources[input_id]; + int key_count = time_keys.size(); + + AnimationTrack track; //begin crating track + track.id = id; + + track.keys.resize(key_count); + + for (int j = 0; j < key_count; j++) { + track.keys.write[j].time = time_keys[j]; + state.animation_length = MAX(state.animation_length, time_keys[j]); + } + + //now read actual values + + int stride = 1; + + if (source_strides.has(output_id)) { + stride = source_strides[output_id]; + } + int output_len = stride / names.size(); + + ERR_CONTINUE(output_len == 0); + ERR_CONTINUE(!float_sources.has(output_id)); + + Vector<float> &output = float_sources[output_id]; + + ERR_CONTINUE_MSG((output.size() / stride) != key_count, "Wrong number of keys in output."); + + for (int j = 0; j < key_count; j++) { + track.keys.write[j].data.resize(output_len); + for (int k = 0; k < output_len; k++) { + track.keys.write[j].data.write[k] = output[l + j * stride + k]; //super weird but should work: + } + } + + if (sampler.has("INTERPOLATION")) { + String interp_id = _uri_to_id(sampler["INTERPOLATION"]); + ERR_CONTINUE(!string_sources.has(interp_id)); + Vector<String> &interps = string_sources[interp_id]; + ERR_CONTINUE(interps.size() != key_count); + + for (int j = 0; j < key_count; j++) { + if (interps[j] == "BEZIER") { + track.keys.write[j].interp_type = AnimationTrack::INTERP_BEZIER; + } else { + track.keys.write[j].interp_type = AnimationTrack::INTERP_LINEAR; + } + } + } + + if (sampler.has("IN_TANGENT") && sampler.has("OUT_TANGENT")) { + //bezier control points.. + String intangent_id = _uri_to_id(sampler["IN_TANGENT"]); + ERR_CONTINUE(!float_sources.has(intangent_id)); + Vector<float> &intangents = float_sources[intangent_id]; + + ERR_CONTINUE(intangents.size() != key_count * 2 * names.size()); + + String outangent_id = _uri_to_id(sampler["OUT_TANGENT"]); + ERR_CONTINUE(!float_sources.has(outangent_id)); + Vector<float> &outangents = float_sources[outangent_id]; + ERR_CONTINUE(outangents.size() != key_count * 2 * names.size()); + + for (int j = 0; j < key_count; j++) { + track.keys.write[j].in_tangent = Vector2(intangents[j * 2 * names.size() + 0 + l * 2], intangents[j * 2 * names.size() + 1 + l * 2]); + track.keys.write[j].out_tangent = Vector2(outangents[j * 2 * names.size() + 0 + l * 2], outangents[j * 2 * names.size() + 1 + l * 2]); + } + } + + if (target.find("/") != -1) { //transform component + track.target = target.get_slicec('/', 0); + track.param = target.get_slicec('/', 1); + if (track.param.find(".") != -1) { + track.component = track.param.get_slice(".", 1).to_upper(); + } + track.param = track.param.get_slice(".", 0); + if (names.size() > 1 && track.component == "") { + //this is a guess because the collada spec is ambiguous here... + //i suppose if you have many names (outputs) you can't use a component and i should abide to that. + track.component = name; + } + } else { + track.target = target; + } + + state.animation_tracks.push_back(track); + + if (!state.referenced_tracks.has(target)) { + state.referenced_tracks[target] = Vector<int>(); + } + + state.referenced_tracks[target].push_back(state.animation_tracks.size() - 1); + + if (id != "") { + if (!state.by_id_tracks.has(id)) { + state.by_id_tracks[id] = Vector<int>(); + } + + state.by_id_tracks[id].push_back(state.animation_tracks.size() - 1); + } + + COLLADA_PRINT("loaded animation with " + itos(key_count) + " keys"); + } + } +} + +void Collada::_parse_animation_clip(XMLParser &parser) { + if (!(state.import_flags & IMPORT_FLAG_ANIMATION)) { + if (!parser.is_empty()) { + parser.skip_section(); + } + + return; + } + + AnimationClip clip; + + if (parser.has_attribute("name")) { + clip.name = parser.get_attribute_value("name"); + } else if (parser.has_attribute("id")) { + clip.name = parser.get_attribute_value("id"); + } + if (parser.has_attribute("start")) { + clip.begin = parser.get_attribute_value("start").to_double(); + } + if (parser.has_attribute("end")) { + clip.end = parser.get_attribute_value("end").to_double(); + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String name = parser.get_node_name(); + if (name == "instance_animation") { + String url = _uri_to_id(parser.get_attribute_value("url")); + clip.tracks.push_back(url); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "animation_clip") { + break; //end of <asset> + } + } + + state.animation_clips.push_back(clip); +} + +void Collada::_parse_scene(XMLParser &parser) { + if (parser.is_empty()) { + return; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String name = parser.get_node_name(); + + if (name == "instance_visual_scene") { + state.root_visual_scene = _uri_to_id(parser.get_attribute_value("url")); + } else if (name == "instance_physics_scene") { + state.root_physics_scene = _uri_to_id(parser.get_attribute_value("url")); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "scene") { + break; //end of <asset> + } + } +} + +void Collada::_parse_library(XMLParser &parser) { + if (parser.is_empty()) { + return; + } + + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + String name = parser.get_node_name(); + COLLADA_PRINT("library name is: " + name); + if (name == "image") { + _parse_image(parser); + } else if (name == "material") { + _parse_material(parser); + } else if (name == "effect") { + _parse_effect(parser); + } else if (name == "camera") { + _parse_camera(parser); + } else if (name == "light") { + _parse_light(parser); + } else if (name == "geometry") { + String id = parser.get_attribute_value("id"); + String name2 = parser.get_attribute_value_safe("name"); + while (parser.read() == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "mesh") { + state.mesh_name_map[id] = (name2 != "") ? name2 : id; + _parse_mesh_geometry(parser, id, name2); + } else if (parser.get_node_name() == "spline") { + state.mesh_name_map[id] = (name2 != "") ? name2 : id; + _parse_curve_geometry(parser, id, name2); + } else if (!parser.is_empty()) { + parser.skip_section(); + } + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "geometry") { + break; + } + } + + } else if (name == "controller") { + _parse_controller(parser); + } else if (name == "animation") { + _parse_animation(parser); + } else if (name == "animation_clip") { + _parse_animation_clip(parser); + } else if (name == "visual_scene") { + COLLADA_PRINT("visual scene"); + _parse_visual_scene(parser); + } else if (!parser.is_empty()) { + parser.skip_section(); + } + + } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name().begins_with("library_")) { + break; //end of <asset> + } + } +} + +void Collada::_joint_set_owner(Collada::Node *p_node, NodeSkeleton *p_owner) { + if (p_node->type == Node::TYPE_JOINT) { + NodeJoint *nj = static_cast<NodeJoint *>(p_node); + nj->owner = p_owner; + + for (int i = 0; i < nj->children.size(); i++) { + _joint_set_owner(nj->children.write[i], p_owner); + } + } +} + +void Collada::_create_skeletons(Collada::Node **p_node, NodeSkeleton *p_skeleton) { + Node *node = *p_node; + + if (node->type == Node::TYPE_JOINT) { + if (!p_skeleton) { + // ohohohoohoo it's a joint node, time to work! + NodeSkeleton *sk = memnew(NodeSkeleton); + *p_node = sk; + sk->children.push_back(node); + sk->parent = node->parent; + node->parent = sk; + p_skeleton = sk; + } + + NodeJoint *nj = static_cast<NodeJoint *>(node); + nj->owner = p_skeleton; + } else { + p_skeleton = nullptr; + } + + for (int i = 0; i < node->children.size(); i++) { + _create_skeletons(&node->children.write[i], p_skeleton); + } +} + +bool Collada::_remove_node(Node *p_parent, Node *p_node) { + for (int i = 0; i < p_parent->children.size(); i++) { + if (p_parent->children[i] == p_node) { + p_parent->children.remove(i); + return true; + } + if (_remove_node(p_parent->children[i], p_node)) { + return true; + } + } + + return false; +} + +void Collada::_remove_node(VisualScene *p_vscene, Node *p_node) { + for (int i = 0; i < p_vscene->root_nodes.size(); i++) { + if (p_vscene->root_nodes[i] == p_node) { + p_vscene->root_nodes.remove(i); + return; + } + if (_remove_node(p_vscene->root_nodes[i], p_node)) { + return; + } + } + + ERR_PRINT("ERROR: Not found node to remove?"); +} + +void Collada::_merge_skeletons(VisualScene *p_vscene, Node *p_node) { + if (p_node->type == Node::TYPE_GEOMETRY) { + NodeGeometry *gnode = static_cast<NodeGeometry *>(p_node); + if (gnode->controller) { + // recount skeletons used + Set<NodeSkeleton *> skeletons; + + for (int i = 0; i < gnode->skeletons.size(); i++) { + String nodeid = gnode->skeletons[i]; + + ERR_CONTINUE(!state.scene_map.has(nodeid)); //weird, it should have it... + +#ifdef NO_SAFE_CAST + NodeJoint *nj = static_cast<NodeJoint *>(state.scene_map[nodeid]); +#else + NodeJoint *nj = dynamic_cast<NodeJoint *>(state.scene_map[nodeid]); +#endif + ERR_CONTINUE(!nj); //broken collada + ERR_CONTINUE(!nj->owner); //weird, node should have a skeleton owner + + skeletons.insert(nj->owner); + } + + if (skeletons.size() > 1) { + //do the merger!! + Set<NodeSkeleton *>::Element *E = skeletons.front(); + NodeSkeleton *base = E->get(); + + for (E = E->next(); E; E = E->next()) { + NodeSkeleton *merged = E->get(); + _remove_node(p_vscene, merged); + for (int i = 0; i < merged->children.size(); i++) { + _joint_set_owner(merged->children[i], base); + base->children.push_back(merged->children[i]); + merged->children[i]->parent = base; + } + + merged->children.clear(); //take children from it + memdelete(merged); + } + } + } + } + + for (int i = 0; i < p_node->children.size(); i++) { + _merge_skeletons(p_vscene, p_node->children[i]); + } +} + +void Collada::_merge_skeletons2(VisualScene *p_vscene) { + for (Map<String, SkinControllerData>::Element *E = state.skin_controller_data_map.front(); E; E = E->next()) { + SkinControllerData &cd = E->get(); + + NodeSkeleton *skeleton = nullptr; + + for (Map<String, Transform>::Element *F = cd.bone_rest_map.front(); F; F = F->next()) { + String name; + + if (!state.sid_to_node_map.has(F->key())) { + continue; + } + + name = state.sid_to_node_map[F->key()]; + + ERR_CONTINUE(!state.scene_map.has(name)); + + Node *node = state.scene_map[name]; + ERR_CONTINUE(node->type != Node::TYPE_JOINT); + + NodeSkeleton *sk = nullptr; + + while (node && !sk) { + if (node->type == Node::TYPE_SKELETON) { + sk = static_cast<NodeSkeleton *>(node); + } + node = node->parent; + } + + ERR_CONTINUE(!sk); + + if (!skeleton) { + skeleton = sk; + continue; + } + + if (skeleton != sk) { + //whoa.. wtf, merge. + _remove_node(p_vscene, sk); + for (int i = 0; i < sk->children.size(); i++) { + _joint_set_owner(sk->children[i], skeleton); + skeleton->children.push_back(sk->children[i]); + sk->children[i]->parent = skeleton; + } + + sk->children.clear(); //take children from it + memdelete(sk); + } + } + } +} + +bool Collada::_optimize_skeletons(VisualScene *p_vscene, Node *p_node) { + Node *node = p_node; + + if (node->type == Node::TYPE_SKELETON && node->parent && node->parent->type == Node::TYPE_NODE && node->parent->children.size() == 1) { + //replace parent by this... + Node *parent = node->parent; + + //i wonder if this is alright.. i think it is since created skeleton (first joint) is already animated by bone.. + node->id = parent->id; + node->name = parent->name; + node->xform_list = parent->xform_list; + node->default_transform = parent->default_transform; + + state.scene_map[node->id] = node; + node->parent = parent->parent; + + if (parent->parent) { + Node *gp = parent->parent; + bool found = false; + for (int i = 0; i < gp->children.size(); i++) { + if (gp->children[i] == parent) { + gp->children.write[i] = node; + found = true; + break; + } + } + if (!found) { + ERR_PRINT("BUG"); + } + } else { + bool found = false; + + for (int i = 0; i < p_vscene->root_nodes.size(); i++) { + if (p_vscene->root_nodes[i] == parent) { + p_vscene->root_nodes.write[i] = node; + found = true; + break; + } + } + if (!found) { + ERR_PRINT("BUG"); + } + } + + parent->children.clear(); + memdelete(parent); + return true; + } + + for (int i = 0; i < node->children.size(); i++) { + if (_optimize_skeletons(p_vscene, node->children[i])) { + return false; //stop processing, go up + } + } + + return false; +} + +bool Collada::_move_geometry_to_skeletons(VisualScene *p_vscene, Node *p_node, List<Node *> *p_mgeom) { + // Bind Shape Matrix scales the bones and makes them gigantic, so the matrix then shrinks the model? + // Solution: apply the Bind Shape Matrix to the VERTICES, and if the object comes scaled, it seems to be left alone! + + if (p_node->type == Node::TYPE_GEOMETRY) { + NodeGeometry *ng = static_cast<NodeGeometry *>(p_node); + if (ng->ignore_anim) { + return false; //already made child of skeleton and processeg + } + + if (ng->controller && ng->skeletons.size()) { + String nodeid = ng->skeletons[0]; + + ERR_FAIL_COND_V(!state.scene_map.has(nodeid), false); //weird, it should have it... +#ifdef NO_SAFE_CAST + NodeJoint *nj = static_cast<NodeJoint *>(state.scene_map[nodeid]); +#else + NodeJoint *nj = dynamic_cast<NodeJoint *>(state.scene_map[nodeid]); +#endif + ERR_FAIL_COND_V(!nj, false); + ERR_FAIL_COND_V(!nj->owner, false); //weird, node should have a skeleton owner + + NodeSkeleton *sk = nj->owner; + + Node *p = sk->parent; + bool node_is_parent_of_skeleton = false; + + while (p) { + if (p == p_node) { + node_is_parent_of_skeleton = true; + break; + } + p = p->parent; // try again + } + + ERR_FAIL_COND_V(node_is_parent_of_skeleton, false); + + //this should be correct + ERR_FAIL_COND_V(!state.skin_controller_data_map.has(ng->source), false); + SkinControllerData &skin = state.skin_controller_data_map[ng->source]; + Transform skel_inv = sk->get_global_transform().affine_inverse(); + p_node->default_transform = skel_inv * (skin.bind_shape /* p_node->get_global_transform()*/); // i honestly have no idea what to do with a previous model xform.. most exporters ignore it + + //make rests relative to the skeleton (they seem to be always relative to world) + for (Map<String, Transform>::Element *E = skin.bone_rest_map.front(); E; E = E->next()) { + E->get() = skel_inv * E->get(); //make the bone rest local to the skeleton + state.bone_rest_map[E->key()] = E->get(); // make it remember where the bone is globally, now that it's relative + } + + //but most exporters seem to work only if i do this.. + //p_node->default_transform = p_node->get_global_transform(); + + //p_node->default_transform=Transform(); //this seems to be correct, because bind shape makes the object local to the skeleton + p_node->ignore_anim = true; // collada may animate this later, if it does, then this is not supported (redo your original asset and don't animate the base mesh) + p_node->parent = sk; + //sk->children.push_back(0,p_node); //avoid INFINITE loop + p_mgeom->push_back(p_node); + return true; + } + } + + for (int i = 0; i < p_node->children.size(); i++) { + if (_move_geometry_to_skeletons(p_vscene, p_node->children[i], p_mgeom)) { + p_node->children.remove(i); + i--; + } + } + + return false; +} + +void Collada::_find_morph_nodes(VisualScene *p_vscene, Node *p_node) { + if (p_node->type == Node::TYPE_GEOMETRY) { + NodeGeometry *nj = static_cast<NodeGeometry *>(p_node); + + if (nj->controller) { + String base = nj->source; + + while (base != "" && !state.mesh_data_map.has(base)) { + if (state.skin_controller_data_map.has(base)) { + SkinControllerData &sk = state.skin_controller_data_map[base]; + base = sk.base; + } else if (state.morph_controller_data_map.has(base)) { + state.morph_ownership_map[base] = nj->id; + break; + } else { + ERR_FAIL_MSG("Invalid scene."); + } + } + } + } + + for (int i = 0; i < p_node->children.size(); i++) { + _find_morph_nodes(p_vscene, p_node->children[i]); + } +} + +void Collada::_optimize() { + for (Map<String, VisualScene>::Element *E = state.visual_scene_map.front(); E; E = E->next()) { + VisualScene &vs = E->get(); + for (int i = 0; i < vs.root_nodes.size(); i++) { + _create_skeletons(&vs.root_nodes.write[i]); + } + + for (int i = 0; i < vs.root_nodes.size(); i++) { + _merge_skeletons(&vs, vs.root_nodes[i]); + } + + _merge_skeletons2(&vs); + + for (int i = 0; i < vs.root_nodes.size(); i++) { + _optimize_skeletons(&vs, vs.root_nodes[i]); + } + + for (int i = 0; i < vs.root_nodes.size(); i++) { + List<Node *> mgeom; + if (_move_geometry_to_skeletons(&vs, vs.root_nodes[i], &mgeom)) { + vs.root_nodes.remove(i); + i--; + } + + while (!mgeom.empty()) { + Node *n = mgeom.front()->get(); + n->parent->children.push_back(n); + mgeom.pop_front(); + } + } + + for (int i = 0; i < vs.root_nodes.size(); i++) { + _find_morph_nodes(&vs, vs.root_nodes[i]); + } + } +} + +int Collada::get_uv_channel(String p_name) { + if (!channel_map.has(p_name)) { + ERR_FAIL_COND_V(channel_map.size() == 2, 0); + + channel_map[p_name] = channel_map.size(); + } + + return channel_map[p_name]; +} + +Error Collada::load(const String &p_path, int p_flags) { + Ref<XMLParser> parserr = memnew(XMLParser); + XMLParser &parser = *parserr.ptr(); + Error err = parser.open(p_path); + ERR_FAIL_COND_V_MSG(err, err, "Cannot open Collada file '" + p_path + "'."); + + state.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + state.import_flags = p_flags; + /* Skip headers */ + while ((err = parser.read()) == OK) { + if (parser.get_node_type() == XMLParser::NODE_ELEMENT) { + if (parser.get_node_name() == "COLLADA") { + break; + } else if (!parser.is_empty()) { + parser.skip_section(); // unknown section, likely headers + } + } + } + + ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CORRUPT, "Corrupted Collada file '" + p_path + "'."); + + /* Start loading Collada */ + + { + //version + String version = parser.get_attribute_value("version"); + state.version.major = version.get_slice(".", 0).to_int(); + state.version.minor = version.get_slice(".", 1).to_int(); + state.version.rev = version.get_slice(".", 2).to_int(); + COLLADA_PRINT("Collada VERSION: " + version); + } + + while ((err = parser.read()) == OK) { + /* Read all the main sections.. */ + + if (parser.get_node_type() != XMLParser::NODE_ELEMENT) { + continue; //no idea what this may be, but skipping anyway + } + + String section = parser.get_node_name(); + + COLLADA_PRINT("section: " + section); + + if (section == "asset") { + _parse_asset(parser); + + } else if (section.begins_with("library_")) { + _parse_library(parser); + } else if (section == "scene") { + _parse_scene(parser); + } else if (!parser.is_empty()) { + parser.skip_section(); // unknown section, likely headers + } + } + + _optimize(); + return OK; +} + +Collada::Collada() { +} diff --git a/editor/import/collada.h b/editor/import/collada.h new file mode 100644 index 0000000000..90c6c47e0b --- /dev/null +++ b/editor/import/collada.h @@ -0,0 +1,578 @@ +/*************************************************************************/ +/* collada.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef COLLADA_H +#define COLLADA_H + +#include "core/io/xml_parser.h" +#include "core/map.h" +#include "core/project_settings.h" +#include "scene/resources/material.h" + +class Collada { +public: + enum ImportFlags { + IMPORT_FLAG_SCENE = 1, + IMPORT_FLAG_ANIMATION = 2 + }; + + struct Image { + String path; + }; + + struct Material { + String name; + String instance_effect; + }; + + struct Effect { + String name; + Map<String, Variant> params; + + struct Channel { + int uv_idx = 0; + String texture; + Color color; + Channel() {} + }; + + Channel diffuse, specular, emission, bump; + float shininess = 40; + bool found_double_sided = false; + bool double_sided = true; + bool unshaded = false; + + String get_texture_path(const String &p_source, Collada &state) const; + + Effect() { + diffuse.color = Color(1, 1, 1, 1); + } + }; + + struct CameraData { + enum Mode { + MODE_PERSPECTIVE, + MODE_ORTHOGONAL + }; + + Mode mode = MODE_PERSPECTIVE; + + union { + struct { + float x_fov = 0; + float y_fov = 0; + } perspective; + struct { + float x_mag = 0; + float y_mag = 0; + } orthogonal; + }; + + float aspect = 1; + float z_near = 0.1; + float z_far = 100; + + CameraData() {} + }; + + struct LightData { + enum Mode { + MODE_AMBIENT, + MODE_DIRECTIONAL, + MODE_OMNI, + MODE_SPOT + }; + + Mode mode = MODE_AMBIENT; + + Color color = Color(1, 1, 1, 1); + + float constant_att = 0; + float linear_att = 0; + float quad_att = 0; + + float spot_angle = 45; + float spot_exp = 1; + + LightData() {} + }; + + struct MeshData { + String name; + struct Source { + Vector<float> array; + int stride; + }; + + Map<String, Source> sources; + + struct Vertices { + Map<String, String> sources; + }; + + Map<String, Vertices> vertices; + + struct Primitives { + struct SourceRef { + String source; + int offset; + }; + + String material; + Map<String, SourceRef> sources; + Vector<float> polygons; + Vector<float> indices; + int count; + int vertex_size; + }; + + Vector<Primitives> primitives; + + bool found_double_sided = false; + bool double_sided = true; + + MeshData() {} + }; + + struct CurveData { + String name; + bool closed = false; + + struct Source { + Vector<String> sarray; + Vector<float> array; + int stride; + }; + + Map<String, Source> sources; + + Map<String, String> control_vertices; + + CurveData() {} + }; + + struct SkinControllerData { + String base; + bool use_idrefs = false; + + Transform bind_shape; + + struct Source { + Vector<String> sarray; //maybe for names + Vector<float> array; + int stride = 1; + Source() {} + }; + + Map<String, Source> sources; + + struct Joints { + Map<String, String> sources; + } joints; + + struct Weights { + struct SourceRef { + String source; + int offset; + }; + + String material; + Map<String, SourceRef> sources; + Vector<float> sets; + Vector<float> indices; + int count; + } weights; + + Map<String, Transform> bone_rest_map; + + SkinControllerData() {} + }; + + struct MorphControllerData { + String mesh; + String mode; + + struct Source { + int stride = 1; + Vector<String> sarray; //maybe for names + Vector<float> array; + Source() {} + }; + + Map<String, Source> sources; + + Map<String, String> targets; + MorphControllerData() {} + }; + + struct Vertex { + int idx = 0; + Vector3 vertex; + Vector3 normal; + Vector3 uv; + Vector3 uv2; + Plane tangent; + Color color; + int uid = 0; + struct Weight { + int bone_idx; + float weight; + bool operator<(const Weight w) const { return weight > w.weight; } //heaviest first + }; + + Vector<Weight> weights; + + void fix_weights() { + weights.sort(); + if (weights.size() > 4) { + //cap to 4 and make weights add up 1 + weights.resize(4); + float total = 0; + for (int i = 0; i < 4; i++) { + total += weights[i].weight; + } + if (total) { + for (int i = 0; i < 4; i++) { + weights.write[i].weight /= total; + } + } + } + } + + void fix_unit_scale(Collada &state); + + bool operator<(const Vertex &p_vert) const { + if (uid == p_vert.uid) { + if (vertex == p_vert.vertex) { + if (normal == p_vert.normal) { + if (uv == p_vert.uv) { + if (uv2 == p_vert.uv2) { + if (!weights.empty() || !p_vert.weights.empty()) { + if (weights.size() == p_vert.weights.size()) { + for (int i = 0; i < weights.size(); i++) { + if (weights[i].bone_idx != p_vert.weights[i].bone_idx) { + return weights[i].bone_idx < p_vert.weights[i].bone_idx; + } + + if (weights[i].weight != p_vert.weights[i].weight) { + return weights[i].weight < p_vert.weights[i].weight; + } + } + } else { + return weights.size() < p_vert.weights.size(); + } + } + + return (color < p_vert.color); + } else { + return (uv2 < p_vert.uv2); + } + } else { + return (uv < p_vert.uv); + } + } else { + return (normal < p_vert.normal); + } + } else { + return vertex < p_vert.vertex; + } + } else { + return uid < p_vert.uid; + } + } + + Vertex() {} + }; + + struct Node { + enum Type { + + TYPE_NODE, + TYPE_JOINT, + TYPE_SKELETON, //this bone is not collada, it's added afterwards as optimization + TYPE_LIGHT, + TYPE_CAMERA, + TYPE_GEOMETRY + }; + + struct XForm { + enum Op { + OP_ROTATE, + OP_SCALE, + OP_TRANSLATE, + OP_MATRIX, + OP_VISIBILITY + }; + + String id; + Op op; + Vector<float> data; + }; + + Type type = TYPE_NODE; + + String name; + String id; + String empty_draw_type; + bool noname = false; + Vector<XForm> xform_list; + Transform default_transform; + Transform post_transform; + Vector<Node *> children; + + Node *parent = nullptr; + + Transform compute_transform(Collada &state) const; + Transform get_global_transform() const; + Transform get_transform() const; + + bool ignore_anim = false; + + Node() {} + virtual ~Node() { + for (int i = 0; i < children.size(); i++) { + memdelete(children[i]); + } + }; + }; + + struct NodeSkeleton : public Node { + NodeSkeleton() { type = TYPE_SKELETON; } + }; + + struct NodeJoint : public Node { + NodeSkeleton *owner = nullptr; + String sid; + NodeJoint() { + type = TYPE_JOINT; + } + }; + + struct NodeGeometry : public Node { + bool controller; + String source; + + struct Material { + String target; + }; + + Map<String, Material> material_map; + Vector<String> skeletons; + + NodeGeometry() { type = TYPE_GEOMETRY; } + }; + + struct NodeCamera : public Node { + String camera; + + NodeCamera() { type = TYPE_CAMERA; } + }; + + struct NodeLight : public Node { + String light; + + NodeLight() { type = TYPE_LIGHT; } + }; + + struct VisualScene { + String name; + Vector<Node *> root_nodes; + + ~VisualScene() { + for (int i = 0; i < root_nodes.size(); i++) { + memdelete(root_nodes[i]); + } + } + }; + + struct AnimationClip { + String name; + float begin = 0; + float end = 1; + Vector<String> tracks; + + AnimationClip() {} + }; + + struct AnimationTrack { + String id; + String target; + String param; + String component; + bool property = false; + + enum InterpolationType { + INTERP_LINEAR, + INTERP_BEZIER + }; + + struct Key { + enum Type { + TYPE_FLOAT, + TYPE_MATRIX + }; + + float time; + Vector<float> data; + Point2 in_tangent; + Point2 out_tangent; + InterpolationType interp_type = INTERP_LINEAR; + + Key() {} + }; + + Vector<float> get_value_at_time(float p_time) const; + + Vector<Key> keys; + + AnimationTrack() {} + }; + + /****************/ + /* IMPORT STATE */ + /****************/ + + struct State { + int import_flags = 0; + + float unit_scale = 1.0; + Vector3::Axis up_axis = Vector3::AXIS_Y; + bool z_up; + + struct Version { + int major, minor, rev; + + bool operator<(const Version &p_ver) const { return (major == p_ver.major) ? ((minor == p_ver.minor) ? (rev < p_ver.rev) : minor < p_ver.minor) : major < p_ver.major; } + Version(int p_major = 0, int p_minor = 0, int p_rev = 0) { + major = p_major; + minor = p_minor; + rev = p_rev; + } + } version; + + Map<String, CameraData> camera_data_map; + Map<String, MeshData> mesh_data_map; + Map<String, LightData> light_data_map; + Map<String, CurveData> curve_data_map; + + Map<String, String> mesh_name_map; + Map<String, String> morph_name_map; + Map<String, String> morph_ownership_map; + Map<String, SkinControllerData> skin_controller_data_map; + Map<String, MorphControllerData> morph_controller_data_map; + + Map<String, Image> image_map; + Map<String, Material> material_map; + Map<String, Effect> effect_map; + + Map<String, VisualScene> visual_scene_map; + Map<String, Node *> scene_map; + Set<String> idref_joints; + Map<String, String> sid_to_node_map; + //Map<String,NodeJoint*> bone_map; + + Map<String, Transform> bone_rest_map; + + String local_path; + String root_visual_scene; + String root_physics_scene; + + Vector<AnimationClip> animation_clips; + Vector<AnimationTrack> animation_tracks; + Map<String, Vector<int>> referenced_tracks; + Map<String, Vector<int>> by_id_tracks; + + float animation_length = 0; + + State() {} + } state; + + Error load(const String &p_path, int p_flags = 0); + + Collada(); + + Transform fix_transform(const Transform &p_transform); + + Transform get_root_transform() const; + + int get_uv_channel(String p_name); + +private: // private stuff + Map<String, int> channel_map; + + void _parse_asset(XMLParser &parser); + void _parse_image(XMLParser &parser); + void _parse_material(XMLParser &parser); + void _parse_effect_material(XMLParser &parser, Effect &effect, String &id); + void _parse_effect(XMLParser &parser); + void _parse_camera(XMLParser &parser); + void _parse_light(XMLParser &parser); + void _parse_animation_clip(XMLParser &parser); + + void _parse_mesh_geometry(XMLParser &parser, String p_id, String p_name); + void _parse_curve_geometry(XMLParser &parser, String p_id, String p_name); + + void _parse_skin_controller(XMLParser &parser, String p_id); + void _parse_morph_controller(XMLParser &parser, String p_id); + void _parse_controller(XMLParser &parser); + + Node *_parse_visual_instance_geometry(XMLParser &parser); + Node *_parse_visual_instance_camera(XMLParser &parser); + Node *_parse_visual_instance_light(XMLParser &parser); + + Node *_parse_visual_node_instance_data(XMLParser &parser); + Node *_parse_visual_scene_node(XMLParser &parser); + void _parse_visual_scene(XMLParser &parser); + + void _parse_animation(XMLParser &parser); + void _parse_scene(XMLParser &parser); + void _parse_library(XMLParser &parser); + + Variant _parse_param(XMLParser &parser); + Vector<float> _read_float_array(XMLParser &parser); + Vector<String> _read_string_array(XMLParser &parser); + Transform _read_transform(XMLParser &parser); + String _read_empty_draw_type(XMLParser &parser); + + void _joint_set_owner(Collada::Node *p_node, NodeSkeleton *p_owner); + void _create_skeletons(Collada::Node **p_node, NodeSkeleton *p_skeleton = nullptr); + void _find_morph_nodes(VisualScene *p_vscene, Node *p_node); + bool _remove_node(Node *p_parent, Node *p_node); + void _remove_node(VisualScene *p_vscene, Node *p_node); + void _merge_skeletons2(VisualScene *p_vscene); + void _merge_skeletons(VisualScene *p_vscene, Node *p_node); + bool _optimize_skeletons(VisualScene *p_vscene, Node *p_node); + + bool _move_geometry_to_skeletons(VisualScene *p_vscene, Node *p_node, List<Node *> *p_mgeom); + + void _optimize(); +}; + +#endif // COLLADA_H diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index d604a95320..12cbaaa885 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,34 +31,33 @@ #include "editor_import_collada.h" #include "core/os/os.h" -#include "editor/collada/collada.h" #include "editor/editor_node.h" -#include "scene/3d/camera.h" -#include "scene/3d/light.h" -#include "scene/3d/mesh_instance.h" -#include "scene/3d/path.h" -#include "scene/3d/skeleton.h" -#include "scene/3d/spatial.h" +#include "editor/import/collada.h" +#include "scene/3d/camera_3d.h" +#include "scene/3d/light_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/node_3d.h" +#include "scene/3d/path_3d.h" +#include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/animation.h" #include "scene/resources/packed_scene.h" #include "scene/resources/surface_tool.h" struct ColladaImport { - Collada collada; - Spatial *scene; + Node3D *scene; - Vector<Ref<Animation> > animations; + Vector<Ref<Animation>> animations; struct NodeMap { //String path; - Spatial *node; + Node3D *node; int bone; List<int> anim_tracks; NodeMap() { - node = NULL; + node = nullptr; bone = -1; } }; @@ -73,23 +72,23 @@ struct ColladaImport { Map<String, NodeMap> node_map; //map from collada node to engine node Map<String, String> node_name_map; //map from collada node to engine node - Map<String, Ref<ArrayMesh> > mesh_cache; - Map<String, Ref<Curve3D> > curve_cache; - Map<String, Ref<Material> > material_cache; - Map<Collada::Node *, Skeleton *> skeleton_map; + Map<String, Ref<ArrayMesh>> mesh_cache; + Map<String, Ref<Curve3D>> curve_cache; + Map<String, Ref<Material>> material_cache; + Map<Collada::Node *, Skeleton3D *> skeleton_map; - Map<Skeleton *, Map<String, int> > skeleton_bone_map; + Map<Skeleton3D *, Map<String, int>> skeleton_bone_map; Set<String> valid_animated_nodes; Vector<int> valid_animated_properties; Map<String, bool> bones_with_animation; - Error _populate_skeleton(Skeleton *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent); + Error _populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent); Error _create_scene_skeletons(Collada::Node *p_node); - Error _create_scene(Collada::Node *p_node, Spatial *p_parent); + Error _create_scene(Collada::Node *p_node, Node3D *p_parent); Error _create_resources(Collada::Node *p_node, bool p_use_compression); Error _create_material(const String &p_target); - Error _create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ArrayMesh> > p_morph_meshes = Vector<Ref<ArrayMesh> >(), bool p_use_compression = false, bool p_use_mesh_material = false); + Error _create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ArrayMesh>> p_morph_meshes = Vector<Ref<ArrayMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false); Error load(const String &p_path, int p_flags, bool p_force_make_tangents = false, bool p_use_compression = false); void _fix_param_animation_tracks(); void create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks); @@ -101,7 +100,6 @@ struct ColladaImport { void _pre_process_lights(Collada::Node *p_node); ColladaImport() { - found_ambient = false; found_directional = false; force_make_tangents = false; @@ -110,16 +108,17 @@ struct ColladaImport { } }; -Error ColladaImport::_populate_skeleton(Skeleton *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent) { - - if (p_node->type != Collada::Node::TYPE_JOINT) +Error ColladaImport::_populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent) { + if (p_node->type != Collada::Node::TYPE_JOINT) { return OK; + } Collada::NodeJoint *joint = static_cast<Collada::NodeJoint *>(p_node); p_skeleton->add_bone(p_node->name); - if (p_parent >= 0) + if (p_parent >= 0) { p_skeleton->set_bone_parent(r_bone, p_parent); + } NodeMap nm; nm.node = p_skeleton; @@ -130,7 +129,6 @@ Error ColladaImport::_populate_skeleton(Skeleton *p_skeleton, Collada::Node *p_n skeleton_bone_map[p_skeleton][joint->sid] = r_bone; if (collada.state.bone_rest_map.has(joint->sid)) { - p_skeleton->set_bone_rest(r_bone, collada.fix_transform(collada.state.bone_rest_map[joint->sid])); //should map this bone to something for animation? } else { @@ -139,22 +137,19 @@ Error ColladaImport::_populate_skeleton(Skeleton *p_skeleton, Collada::Node *p_n int id = r_bone++; for (int i = 0; i < p_node->children.size(); i++) { - Error err = _populate_skeleton(p_skeleton, p_node->children[i], r_bone, id); - if (err) + if (err) { return err; + } } return OK; } void ColladaImport::_pre_process_lights(Collada::Node *p_node) { - if (p_node->type == Collada::Node::TYPE_LIGHT) { - Collada::NodeLight *light = static_cast<Collada::NodeLight *>(p_node); if (collada.state.light_data_map.has(light->light)) { - Collada::LightData &ld = collada.state.light_data_map[light->light]; if (ld.mode == Collada::LightData::MODE_AMBIENT) { found_ambient = true; @@ -166,18 +161,16 @@ void ColladaImport::_pre_process_lights(Collada::Node *p_node) { } } - for (int i = 0; i < p_node->children.size(); i++) + for (int i = 0; i < p_node->children.size(); i++) { _pre_process_lights(p_node->children[i]); + } } Error ColladaImport::_create_scene_skeletons(Collada::Node *p_node) { - if (p_node->type == Collada::Node::TYPE_SKELETON) { - - Skeleton *sk = memnew(Skeleton); + Skeleton3D *sk = memnew(Skeleton3D); int bone = 0; for (int i = 0; i < p_node->children.size(); i++) { - _populate_skeleton(sk, p_node->children[i], bone, -1); } sk->localize_rests(); //after creating skeleton, rests must be localized...! @@ -185,54 +178,48 @@ Error ColladaImport::_create_scene_skeletons(Collada::Node *p_node) { } for (int i = 0; i < p_node->children.size(); i++) { - Error err = _create_scene_skeletons(p_node->children[i]); - if (err) + if (err) { return err; + } } return OK; } -Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { - - Spatial *node = NULL; +Error ColladaImport::_create_scene(Collada::Node *p_node, Node3D *p_parent) { + Node3D *node = nullptr; switch (p_node->type) { - case Collada::Node::TYPE_NODE: { - - node = memnew(Spatial); + node = memnew(Node3D); } break; case Collada::Node::TYPE_JOINT: { - return OK; // do nothing } break; case Collada::Node::TYPE_LIGHT: { - //node = memnew( Light) Collada::NodeLight *light = static_cast<Collada::NodeLight *>(p_node); if (collada.state.light_data_map.has(light->light)) { - Collada::LightData &ld = collada.state.light_data_map[light->light]; if (ld.mode == Collada::LightData::MODE_AMBIENT) { - - if (found_directional) + if (found_directional) { return OK; //do nothing not needed + } - if (!bool(GLOBAL_DEF("collada/use_ambient", false))) + if (!bool(GLOBAL_DEF("collada/use_ambient", false))) { return OK; + } //well, it's an ambient light.. - Light *l = memnew(DirectionalLight); + Light3D *l = memnew(DirectionalLight3D); //l->set_color(Light::COLOR_AMBIENT,ld.color); //l->set_color(Light::COLOR_DIFFUSE,Color(0,0,0)); //l->set_color(Light::COLOR_SPECULAR,Color(0,0,0)); node = l; } else if (ld.mode == Collada::LightData::MODE_DIRECTIONAL) { - //well, it's an ambient light.. - Light *l = memnew(DirectionalLight); + Light3D *l = memnew(DirectionalLight3D); /* if (found_ambient) //use it here l->set_color(Light::COLOR_AMBIENT,ambient); @@ -242,13 +229,12 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { */ node = l; } else { + Light3D *l; - Light *l; - - if (ld.mode == Collada::LightData::MODE_OMNI) - l = memnew(OmniLight); - else { - l = memnew(SpotLight); + if (ld.mode == Collada::LightData::MODE_OMNI) { + l = memnew(OmniLight3D); + } else { + l = memnew(SpotLight3D); //l->set_parameter(Light::PARAM_SPOT_ANGLE,ld.spot_angle); //l->set_parameter(Light::PARAM_SPOT_ATTENUATION,ld.spot_exp); } @@ -261,43 +247,33 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { } } else { - - node = memnew(Spatial); + node = memnew(Node3D); } } break; case Collada::Node::TYPE_CAMERA: { - Collada::NodeCamera *cam = static_cast<Collada::NodeCamera *>(p_node); - Camera *camera = memnew(Camera); + Camera3D *camera = memnew(Camera3D); if (collada.state.camera_data_map.has(cam->camera)) { - const Collada::CameraData &cd = collada.state.camera_data_map[cam->camera]; switch (cd.mode) { - case Collada::CameraData::MODE_ORTHOGONAL: { - if (cd.orthogonal.y_mag) { - - camera->set_keep_aspect_mode(Camera::KEEP_HEIGHT); + camera->set_keep_aspect_mode(Camera3D::KEEP_HEIGHT); camera->set_orthogonal(cd.orthogonal.y_mag * 2.0, cd.z_near, cd.z_far); } else if (!cd.orthogonal.y_mag && cd.orthogonal.x_mag) { - - camera->set_keep_aspect_mode(Camera::KEEP_WIDTH); + camera->set_keep_aspect_mode(Camera3D::KEEP_WIDTH); camera->set_orthogonal(cd.orthogonal.x_mag * 2.0, cd.z_near, cd.z_far); } } break; case Collada::CameraData::MODE_PERSPECTIVE: { - if (cd.perspective.y_fov) { - camera->set_perspective(cd.perspective.y_fov, cd.z_near, cd.z_far); } else if (!cd.perspective.y_fov && cd.perspective.x_fov) { - camera->set_perspective(cd.perspective.x_fov / cd.aspect, cd.z_near, cd.z_far); } @@ -309,28 +285,26 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { } break; case Collada::Node::TYPE_GEOMETRY: { - Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry *>(p_node); if (collada.state.curve_data_map.has(ng->source)) { - - node = memnew(Path); + node = memnew(Path3D); } else { //mesh since nothing else - node = memnew(MeshInstance); - //Object::cast_to<MeshInstance>(node)->set_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT, true); + node = memnew(MeshInstance3D); + //Object::cast_to<MeshInstance3D>(node)->set_flag(GeometryInstance3D::FLAG_USE_BAKED_LIGHT, true); } } break; case Collada::Node::TYPE_SKELETON: { - ERR_FAIL_COND_V(!skeleton_map.has(p_node), ERR_CANT_CREATE); - Skeleton *sk = skeleton_map[p_node]; + Skeleton3D *sk = skeleton_map[p_node]; node = sk; } break; } - if (p_node->name != "") + if (p_node->name != "") { node->set_name(p_node->name); + } NodeMap nm; nm.node = node; node_map[p_node->id] = nm; @@ -347,45 +321,42 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { } for (int i = 0; i < p_node->children.size(); i++) { - Error err = _create_scene(p_node->children[i], node); - if (err) + if (err) { return err; + } } return OK; } Error ColladaImport::_create_material(const String &p_target) { - ERR_FAIL_COND_V(material_cache.has(p_target), ERR_ALREADY_EXISTS); ERR_FAIL_COND_V(!collada.state.material_map.has(p_target), ERR_INVALID_PARAMETER); Collada::Material &src_mat = collada.state.material_map[p_target]; ERR_FAIL_COND_V(!collada.state.effect_map.has(src_mat.instance_effect), ERR_INVALID_PARAMETER); Collada::Effect &effect = collada.state.effect_map[src_mat.instance_effect]; - Ref<SpatialMaterial> material = memnew(SpatialMaterial); + Ref<StandardMaterial3D> material = memnew(StandardMaterial3D); - if (src_mat.name != "") + if (src_mat.name != "") { material->set_name(src_mat.name); - else if (effect.name != "") + } else if (effect.name != "") { material->set_name(effect.name); + } // DIFFUSE if (effect.diffuse.texture != "") { - String texfile = effect.get_texture_path(effect.diffuse.texture, collada); if (texfile != "") { - if (texfile.begins_with("/")) { texfile = texfile.replace_first("/", "res://"); } - Ref<Texture> texture = ResourceLoader::load(texfile, "Texture"); + Ref<Texture2D> texture = ResourceLoader::load(texfile, "Texture2D"); if (texture.is_valid()) { - - material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, texture); material->set_albedo(Color(1, 1, 1, 1)); - //material->set_parameter(SpatialMaterial::PARAM_DIFFUSE,Color(1,1,1,1)); + //material->set_parameter(StandardMaterial3D::PARAM_DIFFUSE,Color(1,1,1,1)); } else { missing_textures.push_back(texfile.get_file()); } @@ -397,21 +368,19 @@ Error ColladaImport::_create_material(const String &p_target) { // SPECULAR if (effect.specular.texture != "") { - String texfile = effect.get_texture_path(effect.specular.texture, collada); if (texfile != "") { - if (texfile.begins_with("/")) { texfile = texfile.replace_first("/", "res://"); } - Ref<Texture> texture = ResourceLoader::load(texfile, "Texture"); + Ref<Texture2D> texture = ResourceLoader::load(texfile, "Texture2D"); if (texture.is_valid()) { - material->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + material->set_texture(StandardMaterial3D::TEXTURE_METALLIC, texture); material->set_specular(1.0); - //material->set_texture(SpatialMaterial::PARAM_SPECULAR,texture); - //material->set_parameter(SpatialMaterial::PARAM_SPECULAR,Color(1,1,1,1)); + //material->set_texture(StandardMaterial3D::PARAM_SPECULAR,texture); + //material->set_parameter(StandardMaterial3D::PARAM_SPECULAR,Color(1,1,1,1)); } else { missing_textures.push_back(texfile.get_file()); } @@ -424,29 +393,26 @@ Error ColladaImport::_create_material(const String &p_target) { // EMISSION if (effect.emission.texture != "") { - String texfile = effect.get_texture_path(effect.emission.texture, collada); if (texfile != "") { - if (texfile.begins_with("/")) { texfile = texfile.replace_first("/", "res://"); } - Ref<Texture> texture = ResourceLoader::load(texfile, "Texture"); + Ref<Texture2D> texture = ResourceLoader::load(texfile, "Texture2D"); if (texture.is_valid()) { - - material->set_feature(SpatialMaterial::FEATURE_EMISSION, true); - material->set_texture(SpatialMaterial::TEXTURE_EMISSION, texture); + material->set_feature(StandardMaterial3D::FEATURE_EMISSION, true); + material->set_texture(StandardMaterial3D::TEXTURE_EMISSION, texture); material->set_emission(Color(1, 1, 1, 1)); - //material->set_parameter(SpatialMaterial::PARAM_EMISSION,Color(1,1,1,1)); + //material->set_parameter(StandardMaterial3D::PARAM_EMISSION,Color(1,1,1,1)); } else { missing_textures.push_back(texfile.get_file()); } } } else { if (effect.emission.color != Color()) { - material->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + material->set_feature(StandardMaterial3D::FEATURE_EMISSION, true); material->set_emission(effect.emission.color); } } @@ -454,21 +420,19 @@ Error ColladaImport::_create_material(const String &p_target) { // NORMAL if (effect.bump.texture != "") { - String texfile = effect.get_texture_path(effect.bump.texture, collada); if (texfile != "") { - if (texfile.begins_with("/")) { texfile = texfile.replace_first("/", "res://"); } - Ref<Texture> texture = ResourceLoader::load(texfile, "Texture"); + Ref<Texture2D> texture = ResourceLoader::load(texfile, "Texture2D"); if (texture.is_valid()) { - material->set_feature(SpatialMaterial::FEATURE_NORMAL_MAPPING, true); - material->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + material->set_feature(StandardMaterial3D::FEATURE_NORMAL_MAPPING, true); + material->set_texture(StandardMaterial3D::TEXTURE_NORMAL, texture); //material->set_emission(Color(1,1,1,1)); - //material->set_texture(SpatialMaterial::PARAM_NORMAL,texture); + //material->set_texture(StandardMaterial3D::PARAM_NORMAL,texture); } else { //missing_textures.push_back(texfile.get_file()); } @@ -479,42 +443,41 @@ Error ColladaImport::_create_material(const String &p_target) { material->set_roughness(roughness); if (effect.double_sided) { - material->set_cull_mode(SpatialMaterial::CULL_DISABLED); + material->set_cull_mode(StandardMaterial3D::CULL_DISABLED); + } + if (effect.unshaded) { + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); } - material->set_flag(SpatialMaterial::FLAG_UNSHADED, effect.unshaded); material_cache[p_target] = material; return OK; } -Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ArrayMesh> > p_morph_meshes, bool p_use_compression, bool p_use_mesh_material) { - +Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ArrayMesh>> p_morph_meshes, bool p_use_compression, bool p_use_mesh_material) { bool local_xform_mirror = p_local_xform.basis.determinant() < 0; if (p_morph_data) { - //add morphie target ERR_FAIL_COND_V(!p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA); String mt = p_morph_data->targets["MORPH_TARGET"]; ERR_FAIL_COND_V(!p_morph_data->sources.has(mt), ERR_INVALID_DATA); int morph_targets = p_morph_data->sources[mt].sarray.size(); for (int i = 0; i < morph_targets; i++) { - String target = p_morph_data->sources[mt].sarray[i]; ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(target), ERR_INVALID_DATA); String name = collada.state.mesh_data_map[target].name; p_mesh->add_blend_shape(name); } - if (p_morph_data->mode == "RELATIVE") + if (p_morph_data->mode == "RELATIVE") { p_mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_RELATIVE); - else if (p_morph_data->mode == "NORMALIZED") + } else if (p_morph_data->mode == "NORMALIZED") { p_mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); + } } int surface = 0; for (int p_i = 0; p_i < meshdata.primitives.size(); p_i++) { - const Collada::MeshData::Primitives &p = meshdata.primitives[p_i]; /* VERTEX SOURCE */ @@ -534,66 +497,60 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me /* NORMAL SOURCE */ - const Collada::MeshData::Source *normal_src = NULL; + const Collada::MeshData::Source *normal_src = nullptr; int normal_ofs = 0; if (p.sources.has("NORMAL")) { - String normal_source_id = p.sources["NORMAL"].source; normal_ofs = p.sources["NORMAL"].offset; ERR_FAIL_COND_V(!meshdata.sources.has(normal_source_id), ERR_INVALID_DATA); normal_src = &meshdata.sources[normal_source_id]; } - const Collada::MeshData::Source *binormal_src = NULL; + const Collada::MeshData::Source *binormal_src = nullptr; int binormal_ofs = 0; if (p.sources.has("TEXBINORMAL")) { - String binormal_source_id = p.sources["TEXBINORMAL"].source; binormal_ofs = p.sources["TEXBINORMAL"].offset; ERR_FAIL_COND_V(!meshdata.sources.has(binormal_source_id), ERR_INVALID_DATA); binormal_src = &meshdata.sources[binormal_source_id]; } - const Collada::MeshData::Source *tangent_src = NULL; + const Collada::MeshData::Source *tangent_src = nullptr; int tangent_ofs = 0; if (p.sources.has("TEXTANGENT")) { - String tangent_source_id = p.sources["TEXTANGENT"].source; tangent_ofs = p.sources["TEXTANGENT"].offset; ERR_FAIL_COND_V(!meshdata.sources.has(tangent_source_id), ERR_INVALID_DATA); tangent_src = &meshdata.sources[tangent_source_id]; } - const Collada::MeshData::Source *uv_src = NULL; + const Collada::MeshData::Source *uv_src = nullptr; int uv_ofs = 0; if (p.sources.has("TEXCOORD0")) { - String uv_source_id = p.sources["TEXCOORD0"].source; uv_ofs = p.sources["TEXCOORD0"].offset; ERR_FAIL_COND_V(!meshdata.sources.has(uv_source_id), ERR_INVALID_DATA); uv_src = &meshdata.sources[uv_source_id]; } - const Collada::MeshData::Source *uv2_src = NULL; + const Collada::MeshData::Source *uv2_src = nullptr; int uv2_ofs = 0; if (p.sources.has("TEXCOORD1")) { - String uv2_source_id = p.sources["TEXCOORD1"].source; uv2_ofs = p.sources["TEXCOORD1"].offset; ERR_FAIL_COND_V(!meshdata.sources.has(uv2_source_id), ERR_INVALID_DATA); uv2_src = &meshdata.sources[uv2_source_id]; } - const Collada::MeshData::Source *color_src = NULL; + const Collada::MeshData::Source *color_src = nullptr; int color_ofs = 0; if (p.sources.has("COLOR")) { - String color_source_id = p.sources["COLOR"].source; color_ofs = p.sources["COLOR"].offset; ERR_FAIL_COND_V(!meshdata.sources.has(color_source_id), ERR_INVALID_DATA); @@ -606,21 +563,18 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me /* ADD WEIGHTS IF EXIST */ /************************/ - Map<int, Vector<Collada::Vertex::Weight> > pre_weights; + Map<int, Vector<Collada::Vertex::Weight>> pre_weights; bool has_weights = false; if (p_skin_controller) { - - const Collada::SkinControllerData::Source *weight_src = NULL; + const Collada::SkinControllerData::Source *weight_src = nullptr; int weight_ofs = 0; if (p_skin_controller->weights.sources.has("WEIGHT")) { - String weight_id = p_skin_controller->weights.sources["WEIGHT"].source; weight_ofs = p_skin_controller->weights.sources["WEIGHT"].offset; if (p_skin_controller->sources.has(weight_id)) { - weight_src = &p_skin_controller->sources[weight_id]; } } @@ -628,7 +582,6 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me int joint_ofs = 0; if (p_skin_controller->weights.sources.has("JOINT")) { - joint_ofs = p_skin_controller->weights.sources["JOINT"].offset; } @@ -637,13 +590,11 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me int index_ofs = 0; int wstride = p_skin_controller->weights.sources.size(); for (int w_i = 0; w_i < p_skin_controller->weights.sets.size(); w_i++) { - int amount = p_skin_controller->weights.sets[w_i]; Vector<Collada::Vertex::Weight> weights; for (int a_i = 0; a_i < amount; a_i++) { - Collada::Vertex::Weight w; int read_from = index_ofs + a_i * wstride; @@ -654,8 +605,9 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me w.weight = weight_src->array[weight_index]; int bone_index = p_skin_controller->weights.indices[read_from + joint_ofs]; - if (bone_index == -1) + if (bone_index == -1) { continue; //ignore this weight (refers to bind shape) + } ERR_FAIL_INDEX_V(bone_index, bone_remap.size(), ERR_INVALID_DATA); w.bone_idx = bone_remap[bone_index]; @@ -674,11 +626,14 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me //make sure weights always add up to 1 float total = 0; - for (int i = 0; i < weights.size(); i++) + for (int i = 0; i < weights.size(); i++) { total += weights[i].weight; - if (total) - for (int i = 0; i < weights.size(); i++) + } + if (total) { + for (int i = 0; i < weights.size(); i++) { weights.write[i].weight /= total; + } + } if (weights.size() == 0 || total == 0) { //if nothing, add a weight to bone 0 //no weights assigned @@ -713,10 +668,8 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me int _prim_ofs = 0; int vertidx = 0; for (int p_j = 0; p_j < p.count; p_j++) { - int amount; if (p.polygons.size()) { - ERR_FAIL_INDEX_V(p_j, p.polygons.size(), ERR_INVALID_DATA); amount = p.polygons[p_j]; } else { @@ -728,15 +681,15 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me int prev2[2] = { 0, 0 }; for (int j = 0; j < amount; j++) { - int src = _prim_ofs; //_prim_ofs+=p.sources.size() ERR_FAIL_INDEX_V(src, p.indices.size(), ERR_INVALID_DATA); Collada::Vertex vertex; - if (!p_optimize) + if (!p_optimize) { vertex.uid = vertidx++; + } int vertex_index = p.indices[src + vertex_ofs]; //used for index field (later used by controllers) int vertex_pos = (vertex_src->stride ? vertex_src->stride : 3) * vertex_index; @@ -748,13 +701,11 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me } if (normal_src) { - int normal_pos = (normal_src->stride ? normal_src->stride : 3) * p.indices[src + normal_ofs]; ERR_FAIL_INDEX_V(normal_pos, normal_src->array.size(), ERR_INVALID_DATA); vertex.normal = Vector3(normal_src->array[normal_pos + 0], normal_src->array[normal_pos + 1], normal_src->array[normal_pos + 2]); if (tangent_src && binormal_src) { - int binormal_pos = (binormal_src->stride ? binormal_src->stride : 3) * p.indices[src + binormal_ofs]; ERR_FAIL_INDEX_V(binormal_pos, binormal_src->array.size(), ERR_INVALID_DATA); Vector3 binormal = Vector3(binormal_src->array[binormal_pos + 0], binormal_src->array[binormal_pos + 1], binormal_src->array[binormal_pos + 2]); @@ -769,21 +720,18 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me } if (uv_src) { - int uv_pos = (uv_src->stride ? uv_src->stride : 2) * p.indices[src + uv_ofs]; ERR_FAIL_INDEX_V(uv_pos, uv_src->array.size(), ERR_INVALID_DATA); vertex.uv = Vector3(uv_src->array[uv_pos + 0], 1.0 - uv_src->array[uv_pos + 1], 0); } if (uv2_src) { - int uv2_pos = (uv2_src->stride ? uv2_src->stride : 2) * p.indices[src + uv2_ofs]; ERR_FAIL_INDEX_V(uv2_pos, uv2_src->array.size(), ERR_INVALID_DATA); vertex.uv2 = Vector3(uv2_src->array[uv2_pos + 0], 1.0 - uv2_src->array[uv2_pos + 1], 0); } if (color_src) { - int color_pos = (color_src->stride ? color_src->stride : 3) * p.indices[src + color_ofs]; // colors are RGB in collada.. ERR_FAIL_INDEX_V(color_pos, color_src->array.size(), ERR_INVALID_DATA); vertex.color = Color(color_src->array[color_pos + 0], color_src->array[color_pos + 1], color_src->array[color_pos + 2], (color_src->stride > 3) ? color_src->array[color_pos + 3] : 1.0); @@ -791,7 +739,6 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me #ifndef NO_UP_AXIS_SWAP if (collada.state.up_axis == Vector3::AXIS_Z) { - Vector3 bn = vertex.normal.cross(vertex.tangent.normal) * vertex.tangent.d; SWAP(vertex.vertex.z, vertex.vertex.y); @@ -813,23 +760,21 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me //COLLADA_PRINT("vertex: "+vertex.vertex); if (vertex_set.has(vertex)) { - index = vertex_set.find(vertex)->get().idx; } else { - index = vertex_set.size(); vertex.idx = index; vertex_set.insert(vertex); } //build triangles if needed - if (j == 0) + if (j == 0) { prev2[0] = index; + } if (j >= 2) { //insert indices in reverse order (collada uses CCW as frontface) if (local_xform_mirror) { - indices_list.push_back(prev2[0]); indices_list.push_back(prev2[1]); indices_list.push_back(index); @@ -850,16 +795,13 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me vertex_array.resize(vertex_set.size()); for (Set<Collada::Vertex>::Element *F = vertex_set.front(); F; F = F->next()) { - vertex_array.write[F->get().idx] = F->get(); } if (has_weights) { - //if skeleton, localize Transform local_xform = p_local_xform; for (int i = 0; i < vertex_array.size(); i++) { - vertex_array.write[i].vertex = local_xform.xform(vertex_array[i].vertex); vertex_array.write[i].normal = local_xform.basis.xform(vertex_array[i].normal).normalized(); vertex_array.write[i].tangent.normal = local_xform.basis.xform(vertex_array[i].tangent.normal).normalized(); @@ -876,23 +818,23 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me /*****************/ { - - Ref<SpatialMaterial> material; + Ref<StandardMaterial3D> material; { - if (p_material_map.has(p.material)) { String target = p_material_map[p.material].target; if (!material_cache.has(target)) { Error err = _create_material(target); - if (!err) + if (!err) { material = material_cache[target]; - } else + } + } else { material = material_cache[target]; + } } else if (p.material != "") { - WARN_PRINTS("Collada: Unreferenced material in geometry instance: " + p.material); + WARN_PRINT("Collada: Unreferenced material in geometry instance: " + p.material); } } @@ -920,16 +862,15 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me if (has_weights) { Vector<float> weights; Vector<int> bones; - weights.resize(VS::ARRAY_WEIGHTS_SIZE); - bones.resize(VS::ARRAY_WEIGHTS_SIZE); + weights.resize(RS::ARRAY_WEIGHTS_SIZE); + bones.resize(RS::ARRAY_WEIGHTS_SIZE); //float sum=0.0; - for (int l = 0; l < VS::ARRAY_WEIGHTS_SIZE; l++) { + for (int l = 0; l < RS::ARRAY_WEIGHTS_SIZE; l++) { if (l < vertex_array[k].weights.size()) { weights.write[l] = vertex_array[k].weights[l].weight; bones.write[l] = vertex_array[k].weights[l].bone_idx; //sum += vertex_array[k].weights[l].weight; } else { - weights.write[l] = 0; bones.write[l] = 0; } @@ -952,7 +893,6 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me } if ((!binormal_src || !tangent_src) && normal_src && uv_src && force_make_tangents) { - surftool->generate_tangents(); } @@ -961,7 +901,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me //////////////////////////// Array d = surftool->commit_to_arrays(); - d.resize(VS::ARRAY_MAX); + d.resize(RS::ARRAY_MAX); Array mr; @@ -970,7 +910,6 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me //////////////////////////// for (int mi = 0; mi < p_morph_meshes.size(); mi++) { - Array a = p_morph_meshes[mi]->surface_get_arrays(surface); //add valid weight and bone arrays if they exist, TODO check if they are unique to shape (generally not) @@ -984,7 +923,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me mr.push_back(a); } - p_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, d, mr, p_use_compression ? Mesh::ARRAY_COMPRESS_DEFAULT : 0); + p_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, d, mr, Dictionary(), p_use_compression ? Mesh::ARRAY_COMPRESS_DEFAULT : 0); if (material.is_valid()) { if (p_use_mesh_material) { @@ -1005,23 +944,16 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me } Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compression) { - if (p_node->type == Collada::Node::TYPE_GEOMETRY && node_map.has(p_node->id)) { - - Spatial *node = node_map[p_node->id].node; + Node3D *node = node_map[p_node->id].node; Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry *>(p_node); - if (Object::cast_to<Path>(node)) { - - Path *path = Object::cast_to<Path>(node); - - String curve = ng->source; + if (Object::cast_to<Path3D>(node)) { + Path3D *path = Object::cast_to<Path3D>(node); if (curve_cache.has(ng->source)) { - path->set_curve(curve_cache[ng->source]); } else { - Ref<Curve3D> c = memnew(Curve3D); const Collada::CurveData &cd = collada.state.curve_data_map[ng->source]; @@ -1047,20 +979,19 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres const Collada::CurveData::Source &interps = cd.sources[cd.control_vertices["INTERPOLATION"]]; ERR_FAIL_COND_V(interps.stride != 1, ERR_INVALID_DATA); - const Collada::CurveData::Source *tilts = NULL; - if (cd.control_vertices.has("TILT") && cd.sources.has(cd.control_vertices["TILT"])) + const Collada::CurveData::Source *tilts = nullptr; + if (cd.control_vertices.has("TILT") && cd.sources.has(cd.control_vertices["TILT"])) { tilts = &cd.sources[cd.control_vertices["TILT"]]; + } int pc = vertices.array.size() / 3; for (int i = 0; i < pc; i++) { - Vector3 pos(vertices.array[i * 3 + 0], vertices.array[i * 3 + 1], vertices.array[i * 3 + 2]); Vector3 in(in_tangents.array[i * 3 + 0], in_tangents.array[i * 3 + 1], in_tangents.array[i * 3 + 2]); Vector3 out(out_tangents.array[i * 3 + 0], out_tangents.array[i * 3 + 1], out_tangents.array[i * 3 + 2]); #ifndef NO_UP_AXIS_SWAP if (collada.state.up_axis == Vector3::AXIS_Z) { - SWAP(pos.y, pos.z); pos.z = -pos.z; SWAP(in.y, in.z); @@ -1074,8 +1005,9 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres out *= collada.state.unit_scale; c->add_point(pos, in - pos, out - pos); - if (tilts) + if (tilts) { c->set_point_tilt(i, tilts->array[i]); + } } curve_cache[ng->source] = c; @@ -1083,27 +1015,24 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres } } - if (Object::cast_to<MeshInstance>(node)) { - + if (Object::cast_to<MeshInstance3D>(node)) { Collada::NodeGeometry *ng2 = static_cast<Collada::NodeGeometry *>(p_node); - MeshInstance *mi = Object::cast_to<MeshInstance>(node); + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(node); ERR_FAIL_COND_V(!mi, ERR_BUG); - Collada::SkinControllerData *skin = NULL; - Collada::MorphControllerData *morph = NULL; + Collada::SkinControllerData *skin = nullptr; + Collada::MorphControllerData *morph = nullptr; String meshid; Transform apply_xform; Vector<int> bone_remap; - Vector<Ref<ArrayMesh> > morphs; + Vector<Ref<ArrayMesh>> morphs; if (ng2->controller) { - String ngsource = ng2->source; if (collada.state.skin_controller_data_map.has(ngsource)) { - ERR_FAIL_COND_V(!collada.state.skin_controller_data_map.has(ngsource), ERR_INVALID_DATA); skin = &collada.state.skin_controller_data_map[ngsource]; @@ -1114,7 +1043,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres String skname = skeletons[0]; ERR_FAIL_COND_V(!node_map.has(skname), ERR_INVALID_DATA); NodeMap nmsk = node_map[skname]; - Skeleton *sk = Object::cast_to<Skeleton>(nmsk.node); + Skeleton3D *sk = Object::cast_to<Skeleton3D>(nmsk.node); ERR_FAIL_COND_V(!sk, ERR_INVALID_DATA); ERR_FAIL_COND_V(!skeleton_bone_map.has(sk), ERR_INVALID_DATA); Map<String, int> &bone_remap_map = skeleton_bone_map[sk]; @@ -1147,7 +1076,6 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres bone_remap.resize(joint_src->sarray.size()); for (int i = 0; i < bone_remap.size(); i++) { - String str = joint_src->sarray[i]; ERR_FAIL_COND_V(!bone_remap_map.has(str), ERR_INVALID_DATA); bone_remap.write[i] = bone_remap_map[str]; @@ -1155,7 +1083,6 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres } if (collada.state.morph_controller_data_map.has(ngsource)) { - //it's a morph!! morph = &collada.state.morph_controller_data_map[ngsource]; meshid = morph->mesh; @@ -1167,13 +1094,12 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres valid = true; Vector<String> names = morph->sources[target].sarray; for (int i = 0; i < names.size(); i++) { - String meshid2 = names[i]; if (collada.state.mesh_data_map.has(meshid2)) { Ref<ArrayMesh> mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid2]; mesh->set_name(meshdata.name); - Error err = _create_mesh_surfaces(false, mesh, ng2->material_map, meshdata, apply_xform, bone_remap, skin, NULL, Vector<Ref<ArrayMesh> >(), false); + Error err = _create_mesh_surfaces(false, mesh, ng2->material_map, meshdata, apply_xform, bone_remap, skin, nullptr, Vector<Ref<ArrayMesh>>(), false); ERR_FAIL_COND_V(err, err); morphs.push_back(mesh); @@ -1183,8 +1109,9 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres } } - if (!valid) + if (!valid) { morphs.clear(); + } ngsource = ""; } } @@ -1211,19 +1138,16 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres mesh_cache[meshid] = mesh; } else { - - WARN_PRINTS("Collada: Will not import geometry: " + meshid); + WARN_PRINT("Collada: Will not import geometry: " + meshid); } } if (!mesh.is_null()) { - mi->set_mesh(mesh); if (!use_mesh_builtin_materials) { const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid]; for (int i = 0; i < meshdata.primitives.size(); i++) { - String matname = meshdata.primitives[i].material; if (ng2->material_map.has(matname)) { @@ -1232,14 +1156,16 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres Ref<Material> material; if (!material_cache.has(target)) { Error err = _create_material(target); - if (!err) + if (!err) { material = material_cache[target]; - } else + } + } else { material = material_cache[target]; + } mi->set_surface_material(i, material); } else if (matname != "") { - WARN_PRINTS("Collada: Unreferenced material in geometry instance: " + matname); + WARN_PRINT("Collada: Unreferenced material in geometry instance: " + matname); } } } @@ -1248,16 +1174,15 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres } for (int i = 0; i < p_node->children.size(); i++) { - Error err = _create_resources(p_node->children[i], p_use_compression); - if (err) + if (err) { return err; + } } return OK; } Error ColladaImport::load(const String &p_path, int p_flags, bool p_force_make_tangents, bool p_use_compression) { - Error err = collada.load(p_path, p_flags); ERR_FAIL_COND_V_MSG(err, err, "Cannot load file '" + p_path + "'."); @@ -1265,17 +1190,15 @@ Error ColladaImport::load(const String &p_path, int p_flags, bool p_force_make_t ERR_FAIL_COND_V(!collada.state.visual_scene_map.has(collada.state.root_visual_scene), ERR_INVALID_DATA); Collada::VisualScene &vs = collada.state.visual_scene_map[collada.state.root_visual_scene]; - scene = memnew(Spatial); // root + scene = memnew(Node3D); // root //determine what's going on with the lights for (int i = 0; i < vs.root_nodes.size(); i++) { - _pre_process_lights(vs.root_nodes[i]); } //import scene for (int i = 0; i < vs.root_nodes.size(); i++) { - Error err2 = _create_scene_skeletons(vs.root_nodes[i]); if (err2 != OK) { memdelete(scene); @@ -1284,7 +1207,6 @@ Error ColladaImport::load(const String &p_path, int p_flags, bool p_force_make_t } for (int i = 0; i < vs.root_nodes.size(); i++) { - Error err2 = _create_scene(vs.root_nodes[i], scene); if (err2 != OK) { memdelete(scene); @@ -1305,48 +1227,36 @@ Error ColladaImport::load(const String &p_path, int p_flags, bool p_force_make_t } void ColladaImport::_fix_param_animation_tracks() { - for (Map<String, Collada::Node *>::Element *E = collada.state.scene_map.front(); E; E = E->next()) { - Collada::Node *n = E->get(); switch (n->type) { - case Collada::Node::TYPE_NODE: { // ? do nothing } break; case Collada::Node::TYPE_JOINT: { - } break; case Collada::Node::TYPE_SKELETON: { - } break; case Collada::Node::TYPE_LIGHT: { - } break; case Collada::Node::TYPE_CAMERA: { - } break; case Collada::Node::TYPE_GEOMETRY: { - Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry *>(n); // test source(s) String source = ng->source; while (source != "") { - if (collada.state.skin_controller_data_map.has(source)) { - const Collada::SkinControllerData &skin = collada.state.skin_controller_data_map[source]; //nothing to animate here i think source = skin.base; } else if (collada.state.morph_controller_data_map.has(source)) { - const Collada::MorphControllerData &morph = collada.state.morph_controller_data_map[source]; if (morph.targets.has("MORPH_WEIGHT") && morph.targets.has("MORPH_TARGET")) { - String weights = morph.targets["MORPH_WEIGHT"]; String targets = morph.targets["MORPH_TARGET"]; //fails here @@ -1358,11 +1268,9 @@ void ColladaImport::_fix_param_animation_tracks() { ERR_FAIL_COND(weight_src.array.size() != target_src.sarray.size()); for (int i = 0; i < weight_src.array.size(); i++) { - String track_name = weights + "(" + itos(i) + ")"; String mesh_name = target_src.sarray[i]; if (collada.state.mesh_name_map.has(mesh_name) && collada.state.referenced_tracks.has(track_name)) { - const Vector<int> &rt = collada.state.referenced_tracks[track_name]; for (int rti = 0; rti < rt.size(); rti++) { @@ -1379,7 +1287,6 @@ void ColladaImport::_fix_param_animation_tracks() { } source = morph.mesh; } else { - source = ""; // for now nothing else supported } } @@ -1390,27 +1297,23 @@ void ColladaImport::_fix_param_animation_tracks() { } void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks) { - _fix_param_animation_tracks(); for (int i = 0; i < collada.state.animation_clips.size(); i++) { - - for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) + for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) { tracks_in_clips.insert(collada.state.animation_clips[i].tracks[j]); + } } for (int i = 0; i < collada.state.animation_tracks.size(); i++) { - const Collada::AnimationTrack &at = collada.state.animation_tracks[i]; String node; if (!node_map.has(at.target)) { - if (node_name_map.has(at.target)) { - node = node_name_map[at.target]; } else { - WARN_PRINTS("Collada: Couldn't find node: " + at.target); + WARN_PRINT("Collada: Couldn't find node: " + at.target); continue; } } else { @@ -1418,23 +1321,21 @@ void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_im } if (at.property) { - valid_animated_properties.push_back(i); } else { - node_map[node].anim_tracks.push_back(i); valid_animated_nodes.insert(node); } } create_animation(-1, p_make_tracks_in_all_bones, p_import_value_tracks); - for (int i = 0; i < collada.state.animation_clips.size(); i++) + for (int i = 0; i < collada.state.animation_clips.size(); i++) { create_animation(i, p_make_tracks_in_all_bones, p_import_value_tracks); + } } void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks) { - Ref<Animation> animation = Ref<Animation>(memnew(Animation)); if (p_clip == -1) { @@ -1444,9 +1345,9 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones } for (Map<String, NodeMap>::Element *E = node_map.front(); E; E = E->next()) { - - if (E->get().bone < 0) + if (E->get().bone < 0) { continue; + } bones_with_animation[E->key()] = false; } //store and validate tracks @@ -1458,15 +1359,11 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones Set<int> track_filter; if (p_clip == -1) { - for (int i = 0; i < collada.state.animation_clips.size(); i++) { - int tc = collada.state.animation_clips[i].tracks.size(); for (int j = 0; j < tc; j++) { - String n = collada.state.animation_clips[i].tracks[j]; if (collada.state.by_id_tracks.has(n)) { - const Vector<int> &ti = collada.state.by_id_tracks[n]; for (int k = 0; k < ti.size(); k++) { track_filter.insert(ti[k]); @@ -1475,13 +1372,10 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones } } } else { - int tc = collada.state.animation_clips[p_clip].tracks.size(); for (int j = 0; j < tc; j++) { - String n = collada.state.animation_clips[p_clip].tracks[j]; if (collada.state.by_id_tracks.has(n)) { - const Vector<int> &ti = collada.state.by_id_tracks[n]; for (int k = 0; k < ti.size(); k++) { track_filter.insert(ti[k]); @@ -1499,11 +1393,11 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones float snapshot_interval = 1.0 / bake_fps; //should be customizable somewhere... float anim_length = collada.state.animation_length; - if (p_clip >= 0 && collada.state.animation_clips[p_clip].end) + if (p_clip >= 0 && collada.state.animation_clips[p_clip].end) { anim_length = collada.state.animation_clips[p_clip].end; + } while (f < anim_length) { - base_snapshots.push_back(f); f += snapshot_interval; @@ -1518,11 +1412,9 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones bool tracks_found = false; for (Set<String>::Element *E = valid_animated_nodes.front(); E; E = E->next()) { - // take snapshots if (!collada.state.scene_map.has(E->get())) { - continue; } @@ -1530,7 +1422,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones String path = scene->get_path_to(nm.node); if (nm.bone >= 0) { - Skeleton *sk = static_cast<Skeleton *>(nm.node); + Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node); String name = sk->get_bone_name(nm.bone); path = path + ":" + name; } @@ -1539,7 +1431,6 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones Collada::Node *cn = collada.state.scene_map[E->get()]; if (cn->ignore_anim) { - continue; } @@ -1554,25 +1445,23 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones //use snapshot keys from anim track instead, because this was most likely exported baked const Collada::AnimationTrack &at = collada.state.animation_tracks[nm.anim_tracks.front()->get()]; snapshots.clear(); - for (int i = 0; i < at.keys.size(); i++) + for (int i = 0; i < at.keys.size(); i++) { snapshots.push_back(at.keys[i].time); + } } for (int i = 0; i < snapshots.size(); i++) { - for (List<int>::Element *ET = nm.anim_tracks.front(); ET; ET = ET->next()) { //apply tracks if (p_clip == -1) { - if (track_filter.has(ET->get())) { - continue; } } else { - - if (!track_filter.has(ET->get())) + if (!track_filter.has(ET->get())) { continue; + } } found_anim = true; @@ -1581,16 +1470,14 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones int xform_idx = -1; for (int j = 0; j < cn->xform_list.size(); j++) { - if (cn->xform_list[j].id == at.param) { - xform_idx = j; break; } } if (xform_idx == -1) { - WARN_PRINTS("Collada: Couldn't find matching node " + at.target + " xform for track " + at.param + "."); + WARN_PRINT("Collada: Couldn't find matching node " + at.target + " xform for track " + at.param + "."); continue; } @@ -1621,12 +1508,10 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones if (nm.bone >= 0) { //make bone transform relative to rest (in case of skeleton) - Skeleton *sk = Object::cast_to<Skeleton>(nm.node); + Skeleton3D *sk = Object::cast_to<Skeleton3D>(nm.node); if (sk) { - xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform; } else { - ERR_PRINT("Collada: Invalid skeleton"); } } @@ -1640,35 +1525,35 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones } if (nm.bone >= 0) { - if (found_anim) + if (found_anim) { bones_with_animation[E->get()] = true; + } } - if (found_anim) + if (found_anim) { tracks_found = true; - else { + } else { animation->remove_track(track); } } if (p_make_tracks_in_all_bones) { - //some bones may lack animation, but since we don't store pose as a property, we must add keyframes! for (Map<String, bool>::Element *E = bones_with_animation.front(); E; E = E->next()) { - - if (E->get()) + if (E->get()) { continue; + } NodeMap &nm = node_map[E->key()]; String path = scene->get_path_to(nm.node); ERR_CONTINUE(nm.bone < 0); - Skeleton *sk = static_cast<Skeleton *>(nm.node); + Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node); String name = sk->get_bone_name(nm.bone); path = path + ":" + name; Collada::Node *cn = collada.state.scene_map[E->key()]; if (cn->ignore_anim) { - WARN_PRINTS("Collada: Ignoring animation on node: " + path); + WARN_PRINT("Collada: Ignoring animation on node: " + path); continue; } @@ -1695,24 +1580,24 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones if (p_import_value_tracks) { for (int i = 0; i < valid_animated_properties.size(); i++) { - int ti = valid_animated_properties[i]; if (p_clip == -1) { - - if (track_filter.has(ti)) + if (track_filter.has(ti)) { continue; + } } else { - - if (!track_filter.has(ti)) + if (!track_filter.has(ti)) { continue; + } } const Collada::AnimationTrack &at = collada.state.animation_tracks[ti]; // take snapshots - if (!collada.state.scene_map.has(at.target)) + if (!collada.state.scene_map.has(at.target)) { continue; + } NodeMap &nm = node_map[at.target]; String path = scene->get_path_to(nm.node); @@ -1725,7 +1610,6 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones animation->track_set_imported(track, true); //helps merging later for (int j = 0; j < at.keys.size(); j++) { - float time = at.keys[j].time; Variant value; Vector<float> data = at.keys[j].data; @@ -1737,7 +1621,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones //matrix WARN_PRINT("Collada: Value keys for matrices not supported."); } else { - WARN_PRINTS("Collada: Unexpected amount of value keys: " + itos(data.size())); + WARN_PRINT("Collada: Unexpected amount of value keys: " + itos(data.size())); } animation->track_insert_key(track, time, value); @@ -1748,7 +1632,6 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones } if (tracks_found) { - animations.push_back(animation); } } @@ -1758,37 +1641,35 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones /*********************************************************************************/ uint32_t EditorSceneImporterCollada::get_import_flags() const { - return IMPORT_SCENE | IMPORT_ANIMATION; } -void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) const { +void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) const { r_extensions->push_back("dae"); } -Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { +Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { ColladaImport state; uint32_t flags = Collada::IMPORT_FLAG_SCENE; - if (p_flags & IMPORT_ANIMATION) + if (p_flags & IMPORT_ANIMATION) { flags |= Collada::IMPORT_FLAG_ANIMATION; + } state.use_mesh_builtin_materials = !(p_flags & IMPORT_MATERIALS_IN_INSTANCES); state.bake_fps = p_bake_fps; Error err = state.load(p_path, flags, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & EditorSceneImporter::IMPORT_USE_COMPRESSION); - ERR_FAIL_COND_V_MSG(err != OK, NULL, "Cannot load scene from file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Cannot load scene from file '" + p_path + "'."); if (state.missing_textures.size()) { - /* - for(int i=0;i<state.missing_textures.size();i++) { - EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]); - } - */ + for(int i=0;i<state.missing_textures.size();i++) { + EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]); + } + */ if (r_missing_deps) { - for (int i = 0; i < state.missing_textures.size(); i++) { //EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]); r_missing_deps->push_back(state.missing_textures[i]); @@ -1797,18 +1678,17 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_ } if (p_flags & IMPORT_ANIMATION) { - state.create_animations(p_flags & IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS, p_flags & EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS); AnimationPlayer *ap = memnew(AnimationPlayer); for (int i = 0; i < state.animations.size(); i++) { String name; - if (state.animations[i]->get_name() == "") + if (state.animations[i]->get_name() == "") { name = "default"; - else + } else { name = state.animations[i]->get_name(); + } if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) { - if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) { state.animations.write[i]->set_loop(true); } @@ -1824,7 +1704,6 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_ } Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { - ColladaImport state; state.use_mesh_builtin_materials = false; @@ -1833,15 +1712,16 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'."); state.create_animations(p_flags & EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS, p_flags & EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS); - if (state.scene) + if (state.scene) { memdelete(state.scene); + } - if (state.animations.size() == 0) + if (state.animations.size() == 0) { return Ref<Animation>(); + } Ref<Animation> anim = state.animations[0]; String base = p_path.get_basename().to_lower(); if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) { - if (base.begins_with("loop") || base.ends_with("loop") || base.begins_with("cycle") || base.ends_with("cycle")) { anim->set_loop(true); } diff --git a/editor/import/editor_import_collada.h b/editor/import/editor_import_collada.h index 6497cdb110..57c694b698 100644 --- a/editor/import/editor_import_collada.h +++ b/editor/import/editor_import_collada.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,13 +34,12 @@ #include "editor/import/resource_importer_scene.h" class EditorSceneImporterCollada : public EditorSceneImporter { - GDCLASS(EditorSceneImporterCollada, EditorSceneImporter); public: virtual uint32_t get_import_flags() const; virtual void get_extensions(List<String> *r_extensions) const; - virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps = NULL, Error *r_err = NULL); + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr); virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); EditorSceneImporterCollada(); diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp index 98f4947c2d..6d46d4d2e9 100644 --- a/editor/import/editor_import_plugin.cpp +++ b/editor/import/editor_import_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -87,7 +87,6 @@ int EditorImportPlugin::get_import_order() const { } void EditorImportPlugin::get_import_options(List<ResourceImporter::ImportOption> *r_options, int p_preset) const { - ERR_FAIL_COND(!(get_script_instance() && get_script_instance()->has_method("get_import_options"))); Array needed; needed.push_back("name"); @@ -131,7 +130,6 @@ bool EditorImportPlugin::get_option_visibility(const String &p_option, const Map } Error EditorImportPlugin::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("import")), ERR_UNAVAILABLE); Dictionary options; Array platform_variants, gen_files; @@ -153,7 +151,6 @@ Error EditorImportPlugin::import(const String &p_source_file, const String &p_sa } void EditorImportPlugin::_bind_methods() { - ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_importer_name")); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_visible_name")); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "get_preset_count")); @@ -162,7 +159,7 @@ void EditorImportPlugin::_bind_methods() { ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::ARRAY, "get_import_options", PropertyInfo(Variant::INT, "preset"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_save_extension")); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_resource_type")); - ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::REAL, "get_priority")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::FLOAT, "get_priority")); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "get_import_order")); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "get_option_visibility", PropertyInfo(Variant::STRING, "option"), PropertyInfo(Variant::DICTIONARY, "options"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "import", PropertyInfo(Variant::STRING, "source_file"), PropertyInfo(Variant::STRING, "save_path"), PropertyInfo(Variant::DICTIONARY, "options"), PropertyInfo(Variant::ARRAY, "platform_variants"), PropertyInfo(Variant::ARRAY, "gen_files"))); diff --git a/editor/import/editor_import_plugin.h b/editor/import/editor_import_plugin.h index eb119b4ba3..be4679b6d3 100644 --- a/editor/import/editor_import_plugin.h +++ b/editor/import/editor_import_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -52,7 +52,7 @@ public: virtual int get_import_order() const; virtual void get_import_options(List<ImportOption> *r_options, int p_preset) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = nullptr); }; #endif //EDITOR_IMPORT_PLUGIN_H diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index fcf0e4af6f..6ffff09ce5 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,6 +29,7 @@ /*************************************************************************/ #include "editor_scene_importer_gltf.h" + #include "core/crypto/crypto_core.h" #include "core/io/json.h" #include "core/math/disjoint_set.h" @@ -36,24 +37,22 @@ #include "core/os/file_access.h" #include "core/os/os.h" #include "modules/regex/regex.h" -#include "scene/3d/bone_attachment.h" -#include "scene/3d/camera.h" -#include "scene/3d/mesh_instance.h" +#include "scene/3d/bone_attachment_3d.h" +#include "scene/3d/camera_3d.h" +#include "scene/3d/mesh_instance_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/surface_tool.h" uint32_t EditorSceneImporterGLTF::get_import_flags() const { - return IMPORT_SCENE | IMPORT_ANIMATION; } -void EditorSceneImporterGLTF::get_extensions(List<String> *r_extensions) const { +void EditorSceneImporterGLTF::get_extensions(List<String> *r_extensions) const { r_extensions->push_back("gltf"); r_extensions->push_back("glb"); } Error EditorSceneImporterGLTF::_parse_json(const String &p_path, GLTFState &state) { - Error err; FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); if (!f) { @@ -80,7 +79,6 @@ Error EditorSceneImporterGLTF::_parse_json(const String &p_path, GLTFState &stat } Error EditorSceneImporterGLTF::_parse_glb(const String &p_path, GLTFState &state) { - Error err; FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); if (!f) { @@ -162,7 +160,6 @@ String EditorSceneImporterGLTF::_sanitize_scene_name(const String &name) { } String EditorSceneImporterGLTF::_gen_unique_name(GLTFState &state, const String &p_name) { - const String s_name = _sanitize_scene_name(p_name); String name; @@ -203,7 +200,6 @@ String EditorSceneImporterGLTF::_sanitize_bone_name(const String &name) { } String EditorSceneImporterGLTF::_gen_unique_bone_name(GLTFState &state, const GLTFSkeletonIndex skel_i, const String &p_name) { - const String s_name = _sanitize_bone_name(p_name); String name; @@ -226,11 +222,18 @@ String EditorSceneImporterGLTF::_gen_unique_bone_name(GLTFState &state, const GL } Error EditorSceneImporterGLTF::_parse_scenes(GLTFState &state) { - ERR_FAIL_COND_V(!state.json.has("scenes"), ERR_FILE_CORRUPT); const Array &scenes = state.json["scenes"]; - for (int i = 0; i < 1; i++) { //only first scene is imported - const Dictionary &s = scenes[i]; + int loaded_scene = 0; + if (state.json.has("scene")) { + loaded_scene = state.json["scene"]; + } else { + WARN_PRINT("The load-time scene is not defined in the glTF2 file. Picking the first scene."); + } + + if (scenes.size()) { + ERR_FAIL_COND_V(loaded_scene >= scenes.size(), ERR_FILE_CORRUPT); + const Dictionary &s = scenes[loaded_scene]; ERR_FAIL_COND_V(!s.has("nodes"), ERR_UNAVAILABLE); const Array &nodes = s["nodes"]; for (int j = 0; j < nodes.size(); j++) { @@ -248,11 +251,9 @@ Error EditorSceneImporterGLTF::_parse_scenes(GLTFState &state) { } Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) { - ERR_FAIL_COND_V(!state.json.has("nodes"), ERR_FILE_CORRUPT); const Array &nodes = state.json["nodes"]; for (int i = 0; i < nodes.size(); i++) { - GLTFNode *node = memnew(GLTFNode); const Dictionary &n = nodes[i]; @@ -272,7 +273,6 @@ Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) { node->xform = _arr_to_xform(n["matrix"]); } else { - if (n.has("translation")) { node->translation = _arr_to_vec3(n["translation"]); } @@ -299,7 +299,6 @@ Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) { // build the hierarchy for (GLTFNodeIndex node_i = 0; node_i < state.nodes.size(); node_i++) { - for (int j = 0; j < state.nodes[node_i]->children.size(); j++) { GLTFNodeIndex child_i = state.nodes[node_i]->children[j]; @@ -316,7 +315,6 @@ Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) { } void EditorSceneImporterGLTF::_compute_node_heights(GLTFState &state) { - state.root_nodes.clear(); for (GLTFNodeIndex node_i = 0; node_i < state.nodes.size(); ++node_i) { GLTFNode *node = state.nodes[node_i]; @@ -338,7 +336,6 @@ void EditorSceneImporterGLTF::_compute_node_heights(GLTFState &state) { } static Vector<uint8_t> _parse_base64_uri(const String &uri) { - int start = uri.find(","); ERR_FAIL_COND_V(start == -1, Vector<uint8_t>()); @@ -358,20 +355,18 @@ static Vector<uint8_t> _parse_base64_uri(const String &uri) { } Error EditorSceneImporterGLTF::_parse_buffers(GLTFState &state, const String &p_base_path) { - - if (!state.json.has("buffers")) + if (!state.json.has("buffers")) { return OK; + } const Array &buffers = state.json["buffers"]; for (GLTFBufferIndex i = 0; i < buffers.size(); i++) { - if (i == 0 && state.glb_data.size()) { state.buffers.push_back(state.glb_data); } else { const Dictionary &buffer = buffers[i]; if (buffer.has("uri")) { - Vector<uint8_t> buffer_data; String uri = buffer["uri"]; @@ -379,7 +374,6 @@ Error EditorSceneImporterGLTF::_parse_buffers(GLTFState &state, const String &p_ //embedded data buffer_data = _parse_base64_uri(uri); } else { - uri = p_base_path.plus_file(uri).replace("\\", "/"); //fix for windows buffer_data = FileAccess::get_file_as_array(uri); ERR_FAIL_COND_V(buffer.size() == 0, ERR_PARSE_ERROR); @@ -399,11 +393,9 @@ Error EditorSceneImporterGLTF::_parse_buffers(GLTFState &state, const String &p_ } Error EditorSceneImporterGLTF::_parse_buffer_views(GLTFState &state) { - ERR_FAIL_COND_V(!state.json.has("bufferViews"), ERR_FILE_CORRUPT); const Array &buffers = state.json["bufferViews"]; for (GLTFBufferViewIndex i = 0; i < buffers.size(); i++) { - const Dictionary &d = buffers[i]; GLTFBufferView buffer_view; @@ -435,33 +427,37 @@ Error EditorSceneImporterGLTF::_parse_buffer_views(GLTFState &state) { } EditorSceneImporterGLTF::GLTFType EditorSceneImporterGLTF::_get_type_from_str(const String &p_string) { - - if (p_string == "SCALAR") + if (p_string == "SCALAR") { return TYPE_SCALAR; + } - if (p_string == "VEC2") + if (p_string == "VEC2") { return TYPE_VEC2; - if (p_string == "VEC3") + } + if (p_string == "VEC3") { return TYPE_VEC3; - if (p_string == "VEC4") + } + if (p_string == "VEC4") { return TYPE_VEC4; + } - if (p_string == "MAT2") + if (p_string == "MAT2") { return TYPE_MAT2; - if (p_string == "MAT3") + } + if (p_string == "MAT3") { return TYPE_MAT3; - if (p_string == "MAT4") + } + if (p_string == "MAT4") { return TYPE_MAT4; + } ERR_FAIL_V(TYPE_SCALAR); } Error EditorSceneImporterGLTF::_parse_accessors(GLTFState &state) { - ERR_FAIL_COND_V(!state.json.has("accessors"), ERR_FILE_CORRUPT); const Array &accessors = state.json["accessors"]; for (GLTFAccessorIndex i = 0; i < accessors.size(); i++) { - const Dictionary &d = accessors[i]; GLTFAccessor accessor; @@ -527,21 +523,25 @@ Error EditorSceneImporterGLTF::_parse_accessors(GLTFState &state) { } String EditorSceneImporterGLTF::_get_component_type_name(const uint32_t p_component) { - switch (p_component) { - case COMPONENT_TYPE_BYTE: return "Byte"; - case COMPONENT_TYPE_UNSIGNED_BYTE: return "UByte"; - case COMPONENT_TYPE_SHORT: return "Short"; - case COMPONENT_TYPE_UNSIGNED_SHORT: return "UShort"; - case COMPONENT_TYPE_INT: return "Int"; - case COMPONENT_TYPE_FLOAT: return "Float"; + case COMPONENT_TYPE_BYTE: + return "Byte"; + case COMPONENT_TYPE_UNSIGNED_BYTE: + return "UByte"; + case COMPONENT_TYPE_SHORT: + return "Short"; + case COMPONENT_TYPE_UNSIGNED_SHORT: + return "UShort"; + case COMPONENT_TYPE_INT: + return "Int"; + case COMPONENT_TYPE_FLOAT: + return "Float"; } return "<Error>"; } String EditorSceneImporterGLTF::_get_type_name(const GLTFType p_component) { - static const char *names[] = { "float", "vec2", @@ -556,7 +556,6 @@ String EditorSceneImporterGLTF::_get_type_name(const GLTFType p_component) { } Error EditorSceneImporterGLTF::_decode_buffer_view(GLTFState &state, double *dst, const GLTFBufferViewIndex p_buffer_view, const int skip_every, const int skip_bytes, const int element_size, const int count, const GLTFType type, const int component_count, const int component_type, const int component_size, const bool normalized, const int byte_offset, const bool for_vertex) { - const GLTFBufferView &bv = state.buffer_views[p_buffer_view]; int stride = bv.byte_stride ? bv.byte_stride : element_size; @@ -582,11 +581,9 @@ Error EditorSceneImporterGLTF::_decode_buffer_view(GLTFState &state, double *dst //fill everything as doubles for (int i = 0; i < count; i++) { - const uint8_t *src = &bufptr[offset + i * stride]; for (int j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { src += skip_bytes; } @@ -644,14 +641,25 @@ Error EditorSceneImporterGLTF::_decode_buffer_view(GLTFState &state, double *dst } int EditorSceneImporterGLTF::_get_component_type_size(const int component_type) { - switch (component_type) { - case COMPONENT_TYPE_BYTE: return 1; break; - case COMPONENT_TYPE_UNSIGNED_BYTE: return 1; break; - case COMPONENT_TYPE_SHORT: return 2; break; - case COMPONENT_TYPE_UNSIGNED_SHORT: return 2; break; - case COMPONENT_TYPE_INT: return 4; break; - case COMPONENT_TYPE_FLOAT: return 4; break; + case COMPONENT_TYPE_BYTE: + return 1; + break; + case COMPONENT_TYPE_UNSIGNED_BYTE: + return 1; + break; + case COMPONENT_TYPE_SHORT: + return 2; + break; + case COMPONENT_TYPE_UNSIGNED_SHORT: + return 2; + break; + case COMPONENT_TYPE_INT: + return 4; + break; + case COMPONENT_TYPE_FLOAT: + return 4; + break; default: { ERR_FAIL_V(0); } @@ -660,7 +668,6 @@ int EditorSceneImporterGLTF::_get_component_type_size(const int component_type) } Vector<double> EditorSceneImporterGLTF::_decode_accessor(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { - //spec, for reference: //https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment @@ -683,7 +690,6 @@ Vector<double> EditorSceneImporterGLTF::_decode_accessor(GLTFState &state, const switch (a.component_type) { case COMPONENT_TYPE_BYTE: case COMPONENT_TYPE_UNSIGNED_BYTE: { - if (a.type == TYPE_MAT2) { skip_every = 2; skip_bytes = 2; @@ -713,12 +719,12 @@ Vector<double> EditorSceneImporterGLTF::_decode_accessor(GLTFState &state, const double *dst = dst_buffer.ptrw(); if (a.buffer_view >= 0) { - ERR_FAIL_INDEX_V(a.buffer_view, state.buffer_views.size(), Vector<double>()); const Error err = _decode_buffer_view(state, dst, a.buffer_view, skip_every, skip_bytes, element_size, a.count, a.type, component_count, a.component_type, component_size, a.normalized, a.byte_offset, p_for_vertex); - if (err != OK) + if (err != OK) { return Vector<double>(); + } } else { //fill with zeros, as bufferview is not defined. @@ -734,14 +740,16 @@ Vector<double> EditorSceneImporterGLTF::_decode_accessor(GLTFState &state, const const int indices_component_size = _get_component_type_size(a.sparse_indices_component_type); Error err = _decode_buffer_view(state, indices.ptrw(), a.sparse_indices_buffer_view, 0, 0, indices_component_size, a.sparse_count, TYPE_SCALAR, 1, a.sparse_indices_component_type, indices_component_size, false, a.sparse_indices_byte_offset, false); - if (err != OK) + if (err != OK) { return Vector<double>(); + } Vector<double> data; data.resize(component_count * a.sparse_count); err = _decode_buffer_view(state, data.ptrw(), a.sparse_values_buffer_view, skip_every, skip_bytes, element_size, a.sparse_count, a.type, component_count, a.component_type, component_size, a.normalized, a.sparse_values_byte_offset, p_for_vertex); - if (err != OK) + if (err != OK) { return Vector<double>(); + } for (int i = 0; i < indices.size(); i++) { const int write_offset = int(indices[i]) * component_count; @@ -755,19 +763,19 @@ Vector<double> EditorSceneImporterGLTF::_decode_accessor(GLTFState &state, const return dst_buffer; } -PoolVector<int> EditorSceneImporterGLTF::_decode_accessor_as_ints(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { - +Vector<int> EditorSceneImporterGLTF::_decode_accessor_as_ints(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); - PoolVector<int> ret; + Vector<int> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } const double *attribs_ptr = attribs.ptr(); const int ret_size = attribs.size(); ret.resize(ret_size); { - PoolVector<int>::Write w = ret.write(); + int *w = ret.ptrw(); for (int i = 0; i < ret_size; i++) { w[i] = int(attribs_ptr[i]); } @@ -775,19 +783,19 @@ PoolVector<int> EditorSceneImporterGLTF::_decode_accessor_as_ints(GLTFState &sta return ret; } -PoolVector<float> EditorSceneImporterGLTF::_decode_accessor_as_floats(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { - +Vector<float> EditorSceneImporterGLTF::_decode_accessor_as_floats(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); - PoolVector<float> ret; + Vector<float> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } const double *attribs_ptr = attribs.ptr(); const int ret_size = attribs.size(); ret.resize(ret_size); { - PoolVector<float>::Write w = ret.write(); + float *w = ret.ptrw(); for (int i = 0; i < ret_size; i++) { w[i] = float(attribs_ptr[i]); } @@ -795,20 +803,20 @@ PoolVector<float> EditorSceneImporterGLTF::_decode_accessor_as_floats(GLTFState return ret; } -PoolVector<Vector2> EditorSceneImporterGLTF::_decode_accessor_as_vec2(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { - +Vector<Vector2> EditorSceneImporterGLTF::_decode_accessor_as_vec2(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); - PoolVector<Vector2> ret; + Vector<Vector2> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } ERR_FAIL_COND_V(attribs.size() % 2 != 0, ret); const double *attribs_ptr = attribs.ptr(); const int ret_size = attribs.size() / 2; ret.resize(ret_size); { - PoolVector<Vector2>::Write w = ret.write(); + Vector2 *w = ret.ptrw(); for (int i = 0; i < ret_size; i++) { w[i] = Vector2(attribs_ptr[i * 2 + 0], attribs_ptr[i * 2 + 1]); } @@ -816,20 +824,20 @@ PoolVector<Vector2> EditorSceneImporterGLTF::_decode_accessor_as_vec2(GLTFState return ret; } -PoolVector<Vector3> EditorSceneImporterGLTF::_decode_accessor_as_vec3(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { - +Vector<Vector3> EditorSceneImporterGLTF::_decode_accessor_as_vec3(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); - PoolVector<Vector3> ret; + Vector<Vector3> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } ERR_FAIL_COND_V(attribs.size() % 3 != 0, ret); const double *attribs_ptr = attribs.ptr(); const int ret_size = attribs.size() / 3; ret.resize(ret_size); { - PoolVector<Vector3>::Write w = ret.write(); + Vector3 *w = ret.ptrw(); for (int i = 0; i < ret_size; i++) { w[i] = Vector3(attribs_ptr[i * 3 + 0], attribs_ptr[i * 3 + 1], attribs_ptr[i * 3 + 2]); } @@ -837,42 +845,41 @@ PoolVector<Vector3> EditorSceneImporterGLTF::_decode_accessor_as_vec3(GLTFState return ret; } -PoolVector<Color> EditorSceneImporterGLTF::_decode_accessor_as_color(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { - +Vector<Color> EditorSceneImporterGLTF::_decode_accessor_as_color(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); - PoolVector<Color> ret; + Vector<Color> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } const int type = state.accessors[p_accessor].type; ERR_FAIL_COND_V(!(type == TYPE_VEC3 || type == TYPE_VEC4), ret); - int components; - if (type == TYPE_VEC3) { - components = 3; - } else { // TYPE_VEC4 - components = 4; + int vec_len = 3; + if (type == TYPE_VEC4) { + vec_len = 4; } - ERR_FAIL_COND_V(attribs.size() % components != 0, ret); + ERR_FAIL_COND_V(attribs.size() % vec_len != 0, ret); const double *attribs_ptr = attribs.ptr(); - const int ret_size = attribs.size() / components; + const int ret_size = attribs.size() / vec_len; ret.resize(ret_size); { - PoolVector<Color>::Write w = ret.write(); + Color *w = ret.ptrw(); for (int i = 0; i < ret_size; i++) { - w[i] = Color(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], components == 4 ? attribs_ptr[i * 4 + 3] : 1.0); + w[i] = Color(attribs_ptr[i * vec_len + 0], attribs_ptr[i * vec_len + 1], attribs_ptr[i * vec_len + 2], vec_len == 4 ? attribs_ptr[i * 4 + 3] : 1.0); } } return ret; } -Vector<Quat> EditorSceneImporterGLTF::_decode_accessor_as_quat(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { +Vector<Quat> EditorSceneImporterGLTF::_decode_accessor_as_quat(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); Vector<Quat> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret); const double *attribs_ptr = attribs.ptr(); @@ -885,13 +892,14 @@ Vector<Quat> EditorSceneImporterGLTF::_decode_accessor_as_quat(GLTFState &state, } return ret; } -Vector<Transform2D> EditorSceneImporterGLTF::_decode_accessor_as_xform2d(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { +Vector<Transform2D> EditorSceneImporterGLTF::_decode_accessor_as_xform2d(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); Vector<Transform2D> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret); ret.resize(attribs.size() / 4); @@ -903,12 +911,12 @@ Vector<Transform2D> EditorSceneImporterGLTF::_decode_accessor_as_xform2d(GLTFSta } Vector<Basis> EditorSceneImporterGLTF::_decode_accessor_as_basis(GLTFState &state, const GLTFAccessorIndex p_accessor, bool p_for_vertex) { - const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); Vector<Basis> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } ERR_FAIL_COND_V(attribs.size() % 9 != 0, ret); ret.resize(attribs.size() / 9); @@ -921,12 +929,12 @@ Vector<Basis> EditorSceneImporterGLTF::_decode_accessor_as_basis(GLTFState &stat } Vector<Transform> EditorSceneImporterGLTF::_decode_accessor_as_xform(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { - const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); Vector<Transform> ret; - if (attribs.size() == 0) + if (attribs.size() == 0) { return ret; + } ERR_FAIL_COND_V(attribs.size() % 16 != 0, ret); ret.resize(attribs.size() / 16); @@ -940,13 +948,12 @@ Vector<Transform> EditorSceneImporterGLTF::_decode_accessor_as_xform(GLTFState & } Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { - - if (!state.json.has("meshes")) + if (!state.json.has("meshes")) { return OK; + } Array meshes = state.json["meshes"]; for (GLTFMeshIndex i = 0; i < meshes.size(); i++) { - print_verbose("glTF: Parsing mesh: " + itos(i)); Dictionary d = meshes[i]; @@ -959,7 +966,6 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary(); for (int j = 0; j < primitives.size(); j++) { - Dictionary p = primitives[j]; Array array; @@ -976,11 +982,15 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { static const Mesh::PrimitiveType primitives2[7] = { Mesh::PRIMITIVE_POINTS, Mesh::PRIMITIVE_LINES, - Mesh::PRIMITIVE_LINE_LOOP, - Mesh::PRIMITIVE_LINE_STRIP, + Mesh::PRIMITIVE_LINES, //loop not supported, should ce converted + Mesh::PRIMITIVE_LINES, Mesh::PRIMITIVE_TRIANGLES, Mesh::PRIMITIVE_TRIANGLE_STRIP, - Mesh::PRIMITIVE_TRIANGLE_FAN, + Mesh::PRIMITIVE_TRIANGLES, //fan not supported, should be converted +#ifndef _MSC_VER +#warning line loop and triangle fan are not supported and need to be converted to lines and triangles +#endif + }; primitive = primitives2[mode]; @@ -1009,10 +1019,10 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { array[Mesh::ARRAY_BONES] = _decode_accessor_as_ints(state, a["JOINTS_0"], true); } if (a.has("WEIGHTS_0")) { - PoolVector<float> weights = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true); + Vector<float> weights = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true); { //gltf does not seem to normalize the weights for some reason.. int wc = weights.size(); - PoolVector<float>::Write w = weights.write(); + float *w = weights.ptrw(); for (int k = 0; k < wc; k += 4) { float total = 0.0; @@ -1032,13 +1042,13 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { } if (p.has("indices")) { - PoolVector<int> indices = _decode_accessor_as_ints(state, p["indices"], false); + Vector<int> indices = _decode_accessor_as_ints(state, p["indices"], false); if (primitive == Mesh::PRIMITIVE_TRIANGLES) { //swap around indices, convert ccw to cw for front face const int is = indices.size(); - const PoolVector<int>::Write w = indices.write(); + int *w = indices.ptrw(); for (int k = 0; k < is; k += 3) { SWAP(w[k + 1], w[k + 2]); } @@ -1047,13 +1057,13 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { } else if (primitive == Mesh::PRIMITIVE_TRIANGLES) { //generate indices because they need to be swapped for CW/CCW - const PoolVector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX]; + const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX]; ERR_FAIL_COND_V(vertices.size() == 0, ERR_PARSE_ERROR); - PoolVector<int> indices; + Vector<int> indices; const int vs = vertices.size(); indices.resize(vs); { - const PoolVector<int>::Write w = indices.write(); + int *w = indices.ptrw(); for (int k = 0; k < vs; k += 3) { w[k] = k; w[k + 1] = k + 2; @@ -1063,24 +1073,15 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { array[Mesh::ARRAY_INDEX] = indices; } - bool generated_tangents = false; - Variant erased_indices; + bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL")); - if (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL")) { + if (generate_tangents) { //must generate mikktspace tangents.. ergh.. Ref<SurfaceTool> st; st.instance(); st->create_from_triangle_arrays(array); - if (!p.has("targets")) { - //morph targets should not be reindexed, as array size might differ - //removing indices is the best bet here - st->deindex(); - erased_indices = a[Mesh::ARRAY_INDEX]; - a[Mesh::ARRAY_INDEX] = Variant(); - } st->generate_tangents(); array = st->commit_to_arrays(); - generated_tangents = true; } Array morphs; @@ -1102,7 +1103,6 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { } for (int k = 0; k < targets.size(); k++) { - const Dictionary &t = targets[k]; Array array_copy; @@ -1115,18 +1115,17 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { array_copy[Mesh::ARRAY_INDEX] = Variant(); if (t.has("POSITION")) { - PoolVector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true); - const PoolVector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX]; + Vector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true); + const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX]; const int size = src_varr.size(); ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR); { - const int max_idx = varr.size(); varr.resize(size); - const PoolVector<Vector3>::Write w_varr = varr.write(); - const PoolVector<Vector3>::Read r_varr = varr.read(); - const PoolVector<Vector3>::Read r_src_varr = src_varr.read(); + Vector3 *w_varr = varr.ptrw(); + const Vector3 *r_varr = varr.ptr(); + const Vector3 *r_src_varr = src_varr.ptr(); for (int l = 0; l < size; l++) { if (l < max_idx) { w_varr[l] = r_varr[l] + r_src_varr[l]; @@ -1138,17 +1137,17 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { array_copy[Mesh::ARRAY_VERTEX] = varr; } if (t.has("NORMAL")) { - PoolVector<Vector3> narr = _decode_accessor_as_vec3(state, t["NORMAL"], true); - const PoolVector<Vector3> src_narr = array[Mesh::ARRAY_NORMAL]; + Vector<Vector3> narr = _decode_accessor_as_vec3(state, t["NORMAL"], true); + const Vector<Vector3> src_narr = array[Mesh::ARRAY_NORMAL]; int size = src_narr.size(); ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR); { int max_idx = narr.size(); narr.resize(size); - const PoolVector<Vector3>::Write w_narr = narr.write(); - const PoolVector<Vector3>::Read r_narr = narr.read(); - const PoolVector<Vector3>::Read r_src_narr = src_narr.read(); + Vector3 *w_narr = narr.ptrw(); + const Vector3 *r_narr = narr.ptr(); + const Vector3 *r_src_narr = src_narr.ptr(); for (int l = 0; l < size; l++) { if (l < max_idx) { w_narr[l] = r_narr[l] + r_src_narr[l]; @@ -1160,25 +1159,23 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { array_copy[Mesh::ARRAY_NORMAL] = narr; } if (t.has("TANGENT")) { - const PoolVector<Vector3> tangents_v3 = _decode_accessor_as_vec3(state, t["TANGENT"], true); - const PoolVector<float> src_tangents = array[Mesh::ARRAY_TANGENT]; + const Vector<Vector3> tangents_v3 = _decode_accessor_as_vec3(state, t["TANGENT"], true); + const Vector<float> src_tangents = array[Mesh::ARRAY_TANGENT]; ERR_FAIL_COND_V(src_tangents.size() == 0, ERR_PARSE_ERROR); - PoolVector<float> tangents_v4; + Vector<float> tangents_v4; { - int max_idx = tangents_v3.size(); int size4 = src_tangents.size(); tangents_v4.resize(size4); - const PoolVector<float>::Write w4 = tangents_v4.write(); + float *w4 = tangents_v4.ptrw(); - const PoolVector<Vector3>::Read r3 = tangents_v3.read(); - const PoolVector<float>::Read r4 = src_tangents.read(); + const Vector3 *r3 = tangents_v3.ptr(); + const float *r4 = src_tangents.ptr(); for (int l = 0; l < size4 / 4; l++) { - if (l < max_idx) { w4[l * 4 + 0] = r3[l].x + r4[l * 4 + 0]; w4[l * 4 + 1] = r3[l].y + r4[l * 4 + 1]; @@ -1195,10 +1192,9 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { array_copy[Mesh::ARRAY_TANGENT] = tangents_v4; } - if (generated_tangents) { + if (generate_tangents) { Ref<SurfaceTool> st; st.instance(); - array_copy[Mesh::ARRAY_INDEX] = erased_indices; //needed for tangent generation, erased by deindex st->create_from_triangle_arrays(array_copy); st->deindex(); st->generate_tangents(); @@ -1239,13 +1235,12 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { } Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_base_path) { - - if (!state.json.has("images")) + if (!state.json.has("images")) { return OK; + } const Array &images = state.json["images"]; for (int i = 0; i < images.size(); i++) { - const Dictionary &d = images[i]; String mimetype; @@ -1254,7 +1249,7 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b } Vector<uint8_t> data; - const uint8_t *data_ptr = NULL; + const uint8_t *data_ptr = nullptr; int data_size = 0; if (d.has("uri")) { @@ -1267,9 +1262,8 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b data_ptr = data.ptr(); data_size = data.size(); } else { - uri = p_base_path.plus_file(uri).replace("\\", "/"); //fix for windows - Ref<Texture> texture = ResourceLoader::load(uri); + Ref<Texture2D> texture = ResourceLoader::load(uri); state.images.push_back(texture); continue; } @@ -1295,6 +1289,8 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b if (mimetype.findn("png") != -1) { //is a png + ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, ERR_UNAVAILABLE); + const Ref<Image> img = Image::_png_mem_loader_func(data_ptr, data_size); ERR_FAIL_COND_V(img.is_null(), ERR_FILE_CORRUPT); @@ -1309,6 +1305,8 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b if (mimetype.findn("jpeg") != -1) { //is a jpg + ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE); + const Ref<Image> img = Image::_jpg_mem_loader_func(data_ptr, data_size); ERR_FAIL_COND_V(img.is_null(), ERR_FILE_CORRUPT); @@ -1331,13 +1329,12 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b } Error EditorSceneImporterGLTF::_parse_textures(GLTFState &state) { - - if (!state.json.has("textures")) + if (!state.json.has("textures")) { return OK; + } const Array &textures = state.json["textures"]; for (GLTFTextureIndex i = 0; i < textures.size(); i++) { - const Dictionary &d = textures[i]; ERR_FAIL_COND_V(!d.has("source"), ERR_PARSE_ERROR); @@ -1350,33 +1347,31 @@ Error EditorSceneImporterGLTF::_parse_textures(GLTFState &state) { return OK; } -Ref<Texture> EditorSceneImporterGLTF::_get_texture(GLTFState &state, const GLTFTextureIndex p_texture) { - ERR_FAIL_INDEX_V(p_texture, state.textures.size(), Ref<Texture>()); +Ref<Texture2D> EditorSceneImporterGLTF::_get_texture(GLTFState &state, const GLTFTextureIndex p_texture) { + ERR_FAIL_INDEX_V(p_texture, state.textures.size(), Ref<Texture2D>()); const GLTFImageIndex image = state.textures[p_texture].src_image; - ERR_FAIL_INDEX_V(image, state.images.size(), Ref<Texture>()); + ERR_FAIL_INDEX_V(image, state.images.size(), Ref<Texture2D>()); return state.images[image]; } Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { - - if (!state.json.has("materials")) + if (!state.json.has("materials")) { return OK; + } const Array &materials = state.json["materials"]; for (GLTFMaterialIndex i = 0; i < materials.size(); i++) { - const Dictionary &d = materials[i]; - Ref<SpatialMaterial> material; + Ref<StandardMaterial3D> material; material.instance(); if (d.has("name")) { material->set_name(d["name"]); } if (d.has("pbrMetallicRoughness")) { - const Dictionary &mr = d["pbrMetallicRoughness"]; if (mr.has("baseColorFactor")) { const Array &arr = mr["baseColorFactor"]; @@ -1389,7 +1384,7 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { if (mr.has("baseColorTexture")) { const Dictionary &bct = mr["baseColorTexture"]; if (bct.has("index")) { - material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, _get_texture(state, bct["index"])); + material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, _get_texture(state, bct["index"])); } if (!mr.has("baseColorFactor")) { material->set_albedo(Color(1, 1, 1)); @@ -1411,11 +1406,11 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { if (mr.has("metallicRoughnessTexture")) { const Dictionary &bct = mr["metallicRoughnessTexture"]; if (bct.has("index")) { - const Ref<Texture> t = _get_texture(state, bct["index"]); - material->set_texture(SpatialMaterial::TEXTURE_METALLIC, t); - material->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE); - material->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, t); - material->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN); + const Ref<Texture2D> t = _get_texture(state, bct["index"]); + material->set_texture(StandardMaterial3D::TEXTURE_METALLIC, t); + material->set_metallic_texture_channel(StandardMaterial3D::TEXTURE_CHANNEL_BLUE); + material->set_texture(StandardMaterial3D::TEXTURE_ROUGHNESS, t); + material->set_roughness_texture_channel(StandardMaterial3D::TEXTURE_CHANNEL_GREEN); if (!mr.has("metallicFactor")) { material->set_metallic(1); } @@ -1429,8 +1424,8 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { if (d.has("normalTexture")) { const Dictionary &bct = d["normalTexture"]; if (bct.has("index")) { - material->set_texture(SpatialMaterial::TEXTURE_NORMAL, _get_texture(state, bct["index"])); - material->set_feature(SpatialMaterial::FEATURE_NORMAL_MAPPING, true); + material->set_texture(StandardMaterial3D::TEXTURE_NORMAL, _get_texture(state, bct["index"])); + material->set_feature(StandardMaterial3D::FEATURE_NORMAL_MAPPING, true); } if (bct.has("scale")) { material->set_normal_scale(bct["scale"]); @@ -1439,9 +1434,9 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { if (d.has("occlusionTexture")) { const Dictionary &bct = d["occlusionTexture"]; if (bct.has("index")) { - material->set_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION, _get_texture(state, bct["index"])); - material->set_ao_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_RED); - material->set_feature(SpatialMaterial::FEATURE_AMBIENT_OCCLUSION, true); + material->set_texture(StandardMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(state, bct["index"])); + material->set_ao_texture_channel(StandardMaterial3D::TEXTURE_CHANNEL_RED); + material->set_feature(StandardMaterial3D::FEATURE_AMBIENT_OCCLUSION, true); } } @@ -1449,7 +1444,7 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { const Array &arr = d["emissiveFactor"]; ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR); const Color c = Color(arr[0], arr[1], arr[2]).to_srgb(); - material->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + material->set_feature(StandardMaterial3D::FEATURE_EMISSION, true); material->set_emission(c); } @@ -1457,8 +1452,8 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { if (d.has("emissiveTexture")) { const Dictionary &bct = d["emissiveTexture"]; if (bct.has("index")) { - material->set_texture(SpatialMaterial::TEXTURE_EMISSION, _get_texture(state, bct["index"])); - material->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + material->set_texture(StandardMaterial3D::TEXTURE_EMISSION, _get_texture(state, bct["index"])); + material->set_feature(StandardMaterial3D::FEATURE_EMISSION, true); material->set_emission(Color(0, 0, 0)); } } @@ -1466,15 +1461,21 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { if (d.has("doubleSided")) { const bool ds = d["doubleSided"]; if (ds) { - material->set_cull_mode(SpatialMaterial::CULL_DISABLED); + material->set_cull_mode(StandardMaterial3D::CULL_DISABLED); } } if (d.has("alphaMode")) { const String &am = d["alphaMode"]; - if (am != "OPAQUE") { - material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - material->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + if (am == "BLEND") { + material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); + } else if (am == "MASK") { + material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_SCISSOR); + if (d.has("alphaCutoff")) { + material->set_alpha_scissor_threshold(d["alphaCutoff"]); + } else { + material->set_alpha_scissor_threshold(0.5f); + } } } @@ -1487,15 +1488,15 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { } EditorSceneImporterGLTF::GLTFNodeIndex EditorSceneImporterGLTF::_find_highest_node(GLTFState &state, const Vector<GLTFNodeIndex> &subset) { - int heighest = -1; + int highest = -1; GLTFNodeIndex best_node = -1; for (int i = 0; i < subset.size(); ++i) { const GLTFNodeIndex node_i = subset[i]; const GLTFNode *node = state.nodes[node_i]; - if (heighest == -1 || node->height < heighest) { - heighest = node->height; + if (highest == -1 || node->height < highest) { + highest = node->height; best_node = node_i; } } @@ -1504,7 +1505,6 @@ EditorSceneImporterGLTF::GLTFNodeIndex EditorSceneImporterGLTF::_find_highest_no } bool EditorSceneImporterGLTF::_capture_nodes_in_skin(GLTFState &state, GLTFSkin &skin, const GLTFNodeIndex node_index) { - bool found_joint = false; for (int i = 0; i < state.nodes[node_index]->children.size(); ++i) { @@ -1528,7 +1528,6 @@ bool EditorSceneImporterGLTF::_capture_nodes_in_skin(GLTFState &state, GLTFSkin } void EditorSceneImporterGLTF::_capture_nodes_for_multirooted_skin(GLTFState &state, GLTFSkin &skin) { - DisjointSet<GLTFNodeIndex> disjoint_set; for (int i = 0; i < skin.joints.size(); ++i) { @@ -1562,7 +1561,6 @@ void EditorSceneImporterGLTF::_capture_nodes_for_multirooted_skin(GLTFState &sta // Go up the tree till all of the multiple roots of the skin are at the same hierarchy level. // This sucks, but 99% of all game engines (not just Godot) would have this same issue. for (int i = 0; i < roots.size(); ++i) { - GLTFNodeIndex current_node = roots[i]; while (state.nodes[current_node]->height > maxHeight) { GLTFNodeIndex parent = state.nodes[current_node]->parent; @@ -1610,7 +1608,6 @@ void EditorSceneImporterGLTF::_capture_nodes_for_multirooted_skin(GLTFState &sta } Error EditorSceneImporterGLTF::_expand_skin(GLTFState &state, GLTFSkin &skin) { - _capture_nodes_for_multirooted_skin(state, skin); // Grab all nodes that lay in between skin joints/nodes @@ -1656,7 +1653,6 @@ Error EditorSceneImporterGLTF::_expand_skin(GLTFState &state, GLTFSkin &skin) { } Error EditorSceneImporterGLTF::_verify_skin(GLTFState &state, GLTFSkin &skin) { - // This may seem duplicated from expand_skins, but this is really a sanity check! (so it kinda is) // In case additional interpolating logic is added to the skins, this will help ensure that you // do not cause it to self implode into a fiery blaze @@ -1722,15 +1718,14 @@ Error EditorSceneImporterGLTF::_verify_skin(GLTFState &state, GLTFSkin &skin) { } Error EditorSceneImporterGLTF::_parse_skins(GLTFState &state) { - - if (!state.json.has("skins")) + if (!state.json.has("skins")) { return OK; + } const Array &skins = state.json["skins"]; // Create the base skins, and mark nodes that are joints for (int i = 0; i < skins.size(); i++) { - const Dictionary &d = skins[i]; GLTFSkin skin; @@ -1780,7 +1775,6 @@ Error EditorSceneImporterGLTF::_parse_skins(GLTFState &state) { } Error EditorSceneImporterGLTF::_determine_skeletons(GLTFState &state) { - // Using a disjoint set, we are going to potentially combine all skins that are actually branches // of a main skeleton, or treat skins defining the same set of nodes as ONE skeleton. // This is another unclear issue caused by the current glTF specification. @@ -1816,7 +1810,7 @@ Error EditorSceneImporterGLTF::_determine_skeletons(GLTFState &state) { skeleton_sets.get_representatives(groups_representatives); Vector<GLTFNodeIndex> highest_group_members; - Vector<Vector<GLTFNodeIndex> > groups; + Vector<Vector<GLTFNodeIndex>> groups; for (int i = 0; i < groups_representatives.size(); ++i) { Vector<GLTFNodeIndex> group; skeleton_sets.get_members(group, groups_representatives[i]); @@ -1858,7 +1852,6 @@ Error EditorSceneImporterGLTF::_determine_skeletons(GLTFState &state) { // Mark all the skins actual skeletons, after we have merged them for (GLTFSkeletonIndex skel_i = 0; skel_i < skeleton_owners.size(); ++skel_i) { - const GLTFNodeIndex skeleton_owner = skeleton_owners[skel_i]; GLTFSkeleton skeleton; @@ -1913,7 +1906,6 @@ Error EditorSceneImporterGLTF::_determine_skeletons(GLTFState &state) { } Error EditorSceneImporterGLTF::_reparent_non_joint_skeleton_subtrees(GLTFState &state, GLTFSkeleton &skeleton, const Vector<GLTFNodeIndex> &non_joints) { - DisjointSet<GLTFNodeIndex> subtree_set; // Populate the disjoint set with ONLY non joints that are in the skeleton hierarchy (non_joints vector) @@ -1973,8 +1965,9 @@ Error EditorSceneImporterGLTF::_reparent_to_fake_joint(GLTFState &state, GLTFSke state.nodes.push_back(fake_joint); // We better not be a joint, or we messed up in our logic - if (node->joint) + if (node->joint) { return FAILED; + } fake_joint->translation = node->translation; fake_joint->rotation = node->rotation; @@ -2028,7 +2021,6 @@ Error EditorSceneImporterGLTF::_reparent_to_fake_joint(GLTFState &state, GLTFSke } Error EditorSceneImporterGLTF::_determine_skeleton_roots(GLTFState &state, const GLTFSkeletonIndex skel_i) { - DisjointSet<GLTFNodeIndex> disjoint_set; for (GLTFNodeIndex i = 0; i < state.nodes.size(); ++i) { @@ -2083,10 +2075,9 @@ Error EditorSceneImporterGLTF::_determine_skeleton_roots(GLTFState &state, const Error EditorSceneImporterGLTF::_create_skeletons(GLTFState &state) { for (GLTFSkeletonIndex skel_i = 0; skel_i < state.skeletons.size(); ++skel_i) { - GLTFSkeleton &gltf_skeleton = state.skeletons.write[skel_i]; - Skeleton *skeleton = memnew(Skeleton); + Skeleton3D *skeleton = memnew(Skeleton3D); gltf_skeleton.godot_skeleton = skeleton; // Make a unique name, no gltf node represents this skeleton @@ -2135,7 +2126,6 @@ Error EditorSceneImporterGLTF::_create_skeletons(GLTFState &state) { skeleton->add_bone(node->name); skeleton->set_bone_rest(bone_index, node->xform); - skeleton->set_bone_pose(bone_index, node->xform); if (node->parent >= 0 && state.nodes[node->parent]->skeleton == skel_i) { const int bone_parent = skeleton->find_bone(state.nodes[node->parent]->name); @@ -2162,6 +2152,8 @@ Error EditorSceneImporterGLTF::_map_skin_joints_indices_to_skeleton_bone_indices const GLTFNodeIndex node_i = skin.joints_original[joint_index]; const GLTFNode *node = state.nodes[node_i]; + skin.joint_i_to_name.insert(joint_index, node->name); + const int bone_index = skeleton.godot_skeleton->find_bone(node->name); ERR_FAIL_COND_V(bone_index < 0, FAILED); @@ -2183,12 +2175,17 @@ Error EditorSceneImporterGLTF::_create_skins(GLTFState &state) { const bool has_ibms = !gltf_skin.inverse_binds.empty(); for (int joint_i = 0; joint_i < gltf_skin.joints_original.size(); ++joint_i) { - int bone_i = gltf_skin.joint_i_to_bone_i[joint_i]; - + Transform xform; if (has_ibms) { - skin->add_bind(bone_i, gltf_skin.inverse_binds[joint_i]); + xform = gltf_skin.inverse_binds[joint_i]; + } + + if (state.use_named_skin_binds) { + StringName name = gltf_skin.joint_i_to_name[joint_i]; + skin->add_named_bind(name, xform); } else { - skin->add_bind(bone_i, Transform()); + int bone_i = gltf_skin.joint_i_to_bone_i[joint_i]; + skin->add_bind(bone_i, xform); } } @@ -2216,7 +2213,6 @@ bool EditorSceneImporterGLTF::_skins_are_same(const Ref<Skin> &skin_a, const Ref } for (int i = 0; i < skin_a->get_bind_count(); ++i) { - if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) { return false; } @@ -2247,21 +2243,19 @@ void EditorSceneImporterGLTF::_remove_duplicate_skins(GLTFState &state) { } Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) { - - if (!state.json.has("cameras")) + if (!state.json.has("cameras")) { return OK; + } const Array &cameras = state.json["cameras"]; for (GLTFCameraIndex i = 0; i < cameras.size(); i++) { - const Dictionary &d = cameras[i]; GLTFCamera camera; ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); const String &type = d["type"]; if (type == "orthographic") { - camera.perspective = false; if (d.has("orthographic")) { const Dictionary &og = d["orthographic"]; @@ -2273,7 +2267,6 @@ Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) { } } else if (type == "perspective") { - camera.perspective = true; if (d.has("perspective")) { const Dictionary &ppt = d["perspective"]; @@ -2297,33 +2290,37 @@ Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) { } Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { - - if (!state.json.has("animations")) + if (!state.json.has("animations")) { return OK; + } const Array &animations = state.json["animations"]; for (GLTFAnimationIndex i = 0; i < animations.size(); i++) { - const Dictionary &d = animations[i]; GLTFAnimation animation; - if (!d.has("channels") || !d.has("samplers")) + if (!d.has("channels") || !d.has("samplers")) { continue; + } Array channels = d["channels"]; Array samplers = d["samplers"]; if (d.has("name")) { - animation.name = d["name"]; + String name = d["name"]; + if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) { + animation.loop = true; + } + animation.name = _sanitize_scene_name(name); } for (int j = 0; j < channels.size(); j++) { - const Dictionary &c = channels[j]; - if (!c.has("target")) + if (!c.has("target")) { continue; + } const Dictionary &t = c["target"]; if (!t.has("node") || !t.has("path")) { @@ -2356,6 +2353,7 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { const int output = s["output"]; GLTFAnimation::Interpolation interp = GLTFAnimation::INTERP_LINEAR; + int output_count = 1; if (s.has("interpolation")) { const String &in = s["interpolation"]; if (in == "STEP") { @@ -2364,14 +2362,16 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { interp = GLTFAnimation::INTERP_LINEAR; } else if (in == "CATMULLROMSPLINE") { interp = GLTFAnimation::INTERP_CATMULLROMSPLINE; + output_count = 3; } else if (in == "CUBICSPLINE") { interp = GLTFAnimation::INTERP_CUBIC_SPLINE; + output_count = 3; } } - const PoolVector<float> times = _decode_accessor_as_floats(state, input, false); + const Vector<float> times = _decode_accessor_as_floats(state, input, false); if (path == "translation") { - const PoolVector<Vector3> translations = _decode_accessor_as_vec3(state, output, false); + const Vector<Vector3> translations = _decode_accessor_as_vec3(state, output, false); track->translation_track.interpolation = interp; track->translation_track.times = Variant(times); //convert via variant track->translation_track.values = Variant(translations); //convert via variant @@ -2381,12 +2381,12 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { track->rotation_track.times = Variant(times); //convert via variant track->rotation_track.values = rotations; //convert via variant } else if (path == "scale") { - const PoolVector<Vector3> scales = _decode_accessor_as_vec3(state, output, false); + const Vector<Vector3> scales = _decode_accessor_as_vec3(state, output, false); track->scale_track.interpolation = interp; track->scale_track.times = Variant(times); //convert via variant track->scale_track.values = Variant(scales); //convert via variant } else if (path == "weights") { - const PoolVector<float> weights = _decode_accessor_as_floats(state, output, false); + const Vector<float> weights = _decode_accessor_as_floats(state, output, false); ERR_FAIL_INDEX_V(state.nodes[node]->mesh, state.meshes.size(), ERR_PARSE_ERROR); const GLTFMesh *mesh = &state.meshes[state.nodes[node]->mesh]; @@ -2395,8 +2395,11 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { track->weight_tracks.resize(wc); + const int expected_value_count = times.size() * output_count * wc; + ERR_FAIL_COND_V_MSG(weights.size() != expected_value_count, ERR_PARSE_ERROR, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead."); + const int wlen = weights.size() / wc; - PoolVector<float>::Read r = weights.read(); + const float *r = weights.ptr(); for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea GLTFAnimation::Channel<float> cf; cf.interpolation = interp; @@ -2411,7 +2414,7 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { track->weight_tracks.write[k] = cf; } } else { - WARN_PRINTS("Invalid path '" + path + "'."); + WARN_PRINT("Invalid path '" + path + "'."); } } @@ -2424,13 +2427,13 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { } void EditorSceneImporterGLTF::_assign_scene_names(GLTFState &state) { - for (int i = 0; i < state.nodes.size(); i++) { GLTFNode *n = state.nodes[i]; // Any joints get unique names generated when the skeleton is made, unique to the skeleton - if (n->skeleton >= 0) + if (n->skeleton >= 0) { continue; + } if (n->name.empty()) { if (n->mesh >= 0) { @@ -2446,12 +2449,11 @@ void EditorSceneImporterGLTF::_assign_scene_names(GLTFState &state) { } } -BoneAttachment *EditorSceneImporterGLTF::_generate_bone_attachment(GLTFState &state, Skeleton *skeleton, const GLTFNodeIndex node_index) { - +BoneAttachment3D *EditorSceneImporterGLTF::_generate_bone_attachment(GLTFState &state, Skeleton3D *skeleton, const GLTFNodeIndex node_index) { const GLTFNode *gltf_node = state.nodes[node_index]; const GLTFNode *bone_node = state.nodes[gltf_node->parent]; - BoneAttachment *bone_attachment = memnew(BoneAttachment); + BoneAttachment3D *bone_attachment = memnew(BoneAttachment3D); print_verbose("glTF: Creating bone attachment for: " + gltf_node->name); ERR_FAIL_COND_V(!bone_node->joint, nullptr); @@ -2461,12 +2463,12 @@ BoneAttachment *EditorSceneImporterGLTF::_generate_bone_attachment(GLTFState &st return bone_attachment; } -MeshInstance *EditorSceneImporterGLTF::_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) { +MeshInstance3D *EditorSceneImporterGLTF::_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) { const GLTFNode *gltf_node = state.nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->mesh, state.meshes.size(), nullptr); - MeshInstance *mi = memnew(MeshInstance); + MeshInstance3D *mi = memnew(MeshInstance3D); print_verbose("glTF: Creating mesh for: " + gltf_node->name); GLTFMesh &mesh = state.meshes.write[gltf_node->mesh]; @@ -2483,44 +2485,43 @@ MeshInstance *EditorSceneImporterGLTF::_generate_mesh_instance(GLTFState &state, return mi; } -Camera *EditorSceneImporterGLTF::_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) { +Camera3D *EditorSceneImporterGLTF::_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) { const GLTFNode *gltf_node = state.nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->camera, state.cameras.size(), nullptr); - Camera *camera = memnew(Camera); + Camera3D *camera = memnew(Camera3D); print_verbose("glTF: Creating camera for: " + gltf_node->name); const GLTFCamera &c = state.cameras[gltf_node->camera]; if (c.perspective) { - camera->set_perspective(c.fov_size, c.znear, c.znear); + camera->set_perspective(c.fov_size, c.znear, c.zfar); } else { - camera->set_orthogonal(c.fov_size, c.znear, c.znear); + camera->set_orthogonal(c.fov_size, c.znear, c.zfar); } return camera; } -Spatial *EditorSceneImporterGLTF::_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) { +Node3D *EditorSceneImporterGLTF::_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) { const GLTFNode *gltf_node = state.nodes[node_index]; - Spatial *spatial = memnew(Spatial); + Node3D *spatial = memnew(Node3D); print_verbose("glTF: Creating spatial for: " + gltf_node->name); return spatial; } -void EditorSceneImporterGLTF::_generate_scene_node(GLTFState &state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index) { - +void EditorSceneImporterGLTF::_generate_scene_node(GLTFState &state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) { const GLTFNode *gltf_node = state.nodes[node_index]; - Spatial *current_node = nullptr; + Node3D *current_node = nullptr; // Is our parent a skeleton - Skeleton *active_skeleton = Object::cast_to<Skeleton>(scene_parent); + Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(scene_parent); if (gltf_node->skeleton >= 0) { - Skeleton *skeleton = state.skeletons[gltf_node->skeleton].godot_skeleton; + Skeleton3D *skeleton = state.skeletons[gltf_node->skeleton].godot_skeleton; if (active_skeleton != skeleton) { ERR_FAIL_COND_MSG(active_skeleton != nullptr, "glTF: Generating scene detected direct parented Skeletons"); @@ -2538,7 +2539,7 @@ void EditorSceneImporterGLTF::_generate_scene_node(GLTFState &state, Node *scene // If we have an active skeleton, and the node is node skinned, we need to create a bone attachment if (current_node == nullptr && active_skeleton != nullptr && gltf_node->skin < 0) { - BoneAttachment *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index); + BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index); scene_parent->add_child(bone_attachment); bone_attachment->set_owner(scene_root); @@ -2576,18 +2577,15 @@ void EditorSceneImporterGLTF::_generate_scene_node(GLTFState &state, Node *scene template <class T> struct EditorSceneImporterGLTFInterpolate { - T lerp(const T &a, const T &b, float c) const { - return a + (b - a) * c; } T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) { - const float t2 = t * t; const float t3 = t2 * t; - return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } T bezier(T start, T control_1, T control_2, T end, float t) { @@ -2605,24 +2603,23 @@ struct EditorSceneImporterGLTFInterpolate { // thank you for existing, partial specialization template <> struct EditorSceneImporterGLTFInterpolate<Quat> { - Quat lerp(const Quat &a, const Quat &b, const float c) const { - ERR_FAIL_COND_V(!a.is_normalized(), Quat()); - ERR_FAIL_COND_V(!b.is_normalized(), Quat()); + ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quat(), "The quaternion \"a\" must be normalized."); + ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quat(), "The quaternion \"b\" must be normalized."); return a.slerp(b, c).normalized(); } Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, const float c) { - ERR_FAIL_COND_V(!p1.is_normalized(), Quat()); - ERR_FAIL_COND_V(!p2.is_normalized(), Quat()); + ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quat(), "The quaternion \"p1\" must be normalized."); + ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quat(), "The quaternion \"p2\" must be normalized."); return p1.slerp(p2, c).normalized(); } Quat bezier(const Quat start, const Quat control_1, const Quat control_2, const Quat end, const float t) { - ERR_FAIL_COND_V(!start.is_normalized(), Quat()); - ERR_FAIL_COND_V(!end.is_normalized(), Quat()); + ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quat(), "The start quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quat(), "The end quaternion must be normalized."); return start.slerp(end, t).normalized(); } @@ -2630,12 +2627,12 @@ struct EditorSceneImporterGLTFInterpolate<Quat> { template <class T> T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { - //could use binary search, worth it? int idx = -1; for (int i = 0; i < p_times.size(); i++) { - if (p_times[i] > p_time) + if (p_times[i] > p_time) { break; + } idx++; } @@ -2643,7 +2640,6 @@ T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, cons switch (p_interp) { case GLTFAnimation::INTERP_LINEAR: { - if (idx == -1) { return p_values[0]; } else if (idx >= p_times.size() - 1) { @@ -2656,7 +2652,6 @@ T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, cons } break; case GLTFAnimation::INTERP_STEP: { - if (idx == -1) { return p_values[0]; } else if (idx >= p_times.size() - 1) { @@ -2667,7 +2662,6 @@ T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, cons } break; case GLTFAnimation::INTERP_CATMULLROMSPLINE: { - if (idx == -1) { return p_values[1]; } else if (idx >= p_times.size() - 1) { @@ -2680,7 +2674,6 @@ T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, cons } break; case GLTFAnimation::INTERP_CUBIC_SPLINE: { - if (idx == -1) { return p_values[1]; } else if (idx >= p_times.size() - 1) { @@ -2703,7 +2696,6 @@ T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, cons } void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps) { - const GLTFAnimation &anim = state.animations[index]; String name = anim.name; @@ -2716,10 +2708,13 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye animation.instance(); animation->set_name(name); + if (anim.loop) { + animation->set_loop(true); + } + float length = 0; for (Map<int, GLTFAnimation::Track>::Element *E = anim.tracks.front(); E; E = E->next()) { - const GLTFAnimation::Track &track = E->get(); //need to find the path NodePath node_path; @@ -2733,7 +2728,7 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye const GLTFNode *node = state.nodes[E->key()]; if (node->skeleton >= 0) { - const Skeleton *sk = Object::cast_to<Skeleton>(state.scene_nodes.find(node_index)->get()); + const Skeleton3D *sk = Object::cast_to<Skeleton3D>(state.scene_nodes.find(node_index)->get()); ERR_FAIL_COND(sk == nullptr); const String path = ap->get_parent()->get_path_to(sk); @@ -2787,7 +2782,6 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye bool last = false; while (true) { - Vector3 pos = base_pos; Quat rot = base_rot; Vector3 scale = base_scale; @@ -2805,12 +2799,11 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye } if (node->skeleton >= 0) { - Transform xform; xform.basis.set_quat_scale(rot, scale); xform.origin = pos; - const Skeleton *skeleton = state.skeletons[node->skeleton].godot_skeleton; + const Skeleton3D *skeleton = state.skeletons[node->skeleton].godot_skeleton; const int bone_idx = skeleton->find_bone(node->name); xform = skeleton->get_bone_rest(bone_idx).affine_inverse() * xform; @@ -2879,7 +2872,7 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye ap->add_animation(name, animation); } -void EditorSceneImporterGLTF::_process_mesh_instances(GLTFState &state, Spatial *scene_root) { +void EditorSceneImporterGLTF::_process_mesh_instances(GLTFState &state, Node3D *scene_root) { for (GLTFNodeIndex node_i = 0; node_i < state.nodes.size(); ++node_i) { const GLTFNode *node = state.nodes[node_i]; @@ -2887,12 +2880,12 @@ void EditorSceneImporterGLTF::_process_mesh_instances(GLTFState &state, Spatial const GLTFSkinIndex skin_i = node->skin; Map<GLTFNodeIndex, Node *>::Element *mi_element = state.scene_nodes.find(node_i); - MeshInstance *mi = Object::cast_to<MeshInstance>(mi_element->get()); + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->get()); ERR_FAIL_COND(mi == nullptr); const GLTFSkeletonIndex skel_i = state.skins[node->skin].skeleton; const GLTFSkeleton &gltf_skeleton = state.skeletons[skel_i]; - Skeleton *skeleton = gltf_skeleton.godot_skeleton; + Skeleton3D *skeleton = gltf_skeleton.godot_skeleton; ERR_FAIL_COND(skeleton == nullptr); mi->get_parent()->remove_child(mi); @@ -2906,9 +2899,8 @@ void EditorSceneImporterGLTF::_process_mesh_instances(GLTFState &state, Spatial } } -Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, const int p_bake_fps) { - - Spatial *root = memnew(Spatial); +Node3D *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, const int p_bake_fps) { + Node3D *root = memnew(Node3D); // scene_name is already unique root->set_name(state.scene_name); @@ -2934,119 +2926,135 @@ Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, const int p_ } Node *EditorSceneImporterGLTF::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { - GLTFState state; if (p_path.to_lower().ends_with("glb")) { //binary file //text file Error err = _parse_glb(p_path, state); - if (err) - return NULL; + if (err) { + return nullptr; + } } else { //text file Error err = _parse_json(p_path, state); - if (err) - return NULL; + if (err) { + return nullptr; + } } - ERR_FAIL_COND_V(!state.json.has("asset"), NULL); + ERR_FAIL_COND_V(!state.json.has("asset"), nullptr); Dictionary asset = state.json["asset"]; - ERR_FAIL_COND_V(!asset.has("version"), NULL); + ERR_FAIL_COND_V(!asset.has("version"), nullptr); String version = asset["version"]; state.major_version = version.get_slice(".", 0).to_int(); state.minor_version = version.get_slice(".", 1).to_int(); + state.use_named_skin_binds = p_flags & IMPORT_USE_NAMED_SKIN_BINDS; /* STEP 0 PARSE SCENE */ Error err = _parse_scenes(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 1 PARSE NODES */ err = _parse_nodes(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 2 PARSE BUFFERS */ err = _parse_buffers(state, p_path.get_base_dir()); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 3 PARSE BUFFER VIEWS */ err = _parse_buffer_views(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 4 PARSE ACCESSORS */ err = _parse_accessors(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 5 PARSE IMAGES */ err = _parse_images(state, p_path.get_base_dir()); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 6 PARSE TEXTURES */ err = _parse_textures(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 7 PARSE TEXTURES */ err = _parse_materials(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 9 PARSE SKINS */ err = _parse_skins(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 10 DETERMINE SKELETONS */ err = _determine_skeletons(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 11 CREATE SKELETONS */ err = _create_skeletons(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 12 CREATE SKINS */ err = _create_skins(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 13 PARSE MESHES (we have enough info now) */ err = _parse_meshes(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 14 PARSE CAMERAS */ err = _parse_cameras(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 15 PARSE ANIMATIONS */ err = _parse_animations(state); - if (err != OK) - return NULL; + if (err != OK) { + return nullptr; + } /* STEP 16 ASSIGN SCENE NAMES */ _assign_scene_names(state); /* STEP 17 MAKE SCENE! */ - Spatial *scene = _generate_scene(state, p_bake_fps); + Node3D *scene = _generate_scene(state, p_bake_fps); return scene; } Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { - return Ref<Animation>(); } diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h index 6021bf10c8..eee978ce16 100644 --- a/editor/import/editor_scene_importer_gltf.h +++ b/editor/import/editor_scene_importer_gltf.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,15 +32,14 @@ #define EDITOR_SCENE_IMPORTER_GLTF_H #include "editor/import/resource_importer_scene.h" -#include "scene/3d/skeleton.h" -#include "scene/3d/spatial.h" +#include "scene/3d/node_3d.h" +#include "scene/3d/skeleton_3d.h" class AnimationPlayer; -class BoneAttachment; -class MeshInstance; +class BoneAttachment3D; +class MeshInstance3D; class EditorSceneImporterGLTF : public EditorSceneImporter { - GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter); typedef int GLTFAccessorIndex; @@ -92,89 +91,59 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { String _get_type_name(const GLTFType p_component); struct GLTFNode { - //matrices need to be transformed to this - GLTFNodeIndex parent; - int height; + GLTFNodeIndex parent = -1; + int height = -1; Transform xform; String name; - GLTFMeshIndex mesh; - GLTFCameraIndex camera; - GLTFSkinIndex skin; + GLTFMeshIndex mesh = -1; + GLTFCameraIndex camera = -1; + GLTFSkinIndex skin = -1; - GLTFSkeletonIndex skeleton; - bool joint; + GLTFSkeletonIndex skeleton = -1; + bool joint = false; Vector3 translation; Quat rotation; - Vector3 scale; + Vector3 scale = Vector3(1, 1, 1); Vector<int> children; - GLTFNodeIndex fake_joint_parent; - - GLTFNode() : - parent(-1), - height(-1), - mesh(-1), - camera(-1), - skin(-1), - skeleton(-1), - joint(false), - translation(0, 0, 0), - scale(Vector3(1, 1, 1)), - fake_joint_parent(-1) {} + GLTFNodeIndex fake_joint_parent = -1; + + GLTFNode() {} }; struct GLTFBufferView { - - GLTFBufferIndex buffer; - int byte_offset; - int byte_length; - int byte_stride; - bool indices; + GLTFBufferIndex buffer = -1; + int byte_offset = 0; + int byte_length = 0; + int byte_stride = 0; + bool indices = false; //matrices need to be transformed to this - GLTFBufferView() : - buffer(-1), - byte_offset(0), - byte_length(0), - byte_stride(0), - indices(false) { - } + GLTFBufferView() {} }; struct GLTFAccessor { - - GLTFBufferViewIndex buffer_view; - int byte_offset; - int component_type; - bool normalized; - int count; + GLTFBufferViewIndex buffer_view = 0; + int byte_offset = 0; + int component_type = 0; + bool normalized = false; + int count = 0; GLTFType type; - float min; - float max; - int sparse_count; - int sparse_indices_buffer_view; - int sparse_indices_byte_offset; - int sparse_indices_component_type; - int sparse_values_buffer_view; - int sparse_values_byte_offset; - - GLTFAccessor() { - buffer_view = 0; - byte_offset = 0; - component_type = 0; - normalized = false; - count = 0; - min = 0; - max = 0; - sparse_count = 0; - sparse_indices_byte_offset = 0; - sparse_values_byte_offset = 0; - } + float min = 0; + float max = 0; + int sparse_count = 0; + int sparse_indices_buffer_view = 0; + int sparse_indices_byte_offset = 0; + int sparse_indices_component_type = 0; + int sparse_values_buffer_view = 0; + int sparse_values_byte_offset = 0; + + GLTFAccessor() {} }; struct GLTFTexture { GLTFImageIndex src_image; @@ -189,21 +158,19 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector<GLTFNodeIndex> roots; // The created Skeleton for the scene - Skeleton *godot_skeleton; + Skeleton3D *godot_skeleton = nullptr; // Set of unique bone names for the skeleton Set<String> unique_names; - GLTFSkeleton() : - godot_skeleton(nullptr) { - } + GLTFSkeleton() {} }; struct GLTFSkin { String name; // The "skeleton" property defined in the gltf spec. -1 = Scene Root - GLTFNodeIndex skin_root; + GLTFNodeIndex skin_root = -1; Vector<GLTFNodeIndex> joints_original; Vector<Transform> inverse_binds; @@ -223,19 +190,18 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector<GLTFNodeIndex> roots; // The GLTF Skeleton this Skin points to (after we determine skeletons) - GLTFSkeletonIndex skeleton; + GLTFSkeletonIndex skeleton = -1; // A mapping from the joint indices (in the order of joints_original) to the // Godot Skeleton's bone_indices Map<int, int> joint_i_to_bone_i; + Map<int, StringName> joint_i_to_name; // The Actual Skin that will be created as a mapping between the IBM's of this skin // to the generated skeleton for the mesh instances. Ref<Skin> godot_skin; - GLTFSkin() : - skin_root(-1), - skeleton(-1) {} + GLTFSkin() {} }; struct GLTFMesh { @@ -244,21 +210,16 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { }; struct GLTFCamera { + bool perspective = true; + float fov_size = 64; + float zfar = 500; + float znear = 0.1; - bool perspective; - float fov_size; - float zfar; - float znear; - - GLTFCamera() { - perspective = true; - fov_size = 65; - zfar = 500; - znear = 0.1; - } + GLTFCamera() {} }; struct GLTFAnimation { + bool loop = false; enum Interpolation { INTERP_LINEAR, @@ -275,11 +236,10 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { }; struct Track { - Channel<Vector3> translation_track; Channel<Quat> rotation_track; Channel<Vector3> scale_track; - Vector<Channel<float> > weight_tracks; + Vector<Channel<float>> weight_tracks; }; String name; @@ -288,25 +248,26 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { }; struct GLTFState { - Dictionary json; int major_version; int minor_version; Vector<uint8_t> glb_data; + bool use_named_skin_binds; + Vector<GLTFNode *> nodes; - Vector<Vector<uint8_t> > buffers; + Vector<Vector<uint8_t>> buffers; Vector<GLTFBufferView> buffer_views; Vector<GLTFAccessor> accessors; Vector<GLTFMesh> meshes; //meshes are loaded directly, no reason not to. - Vector<Ref<Material> > materials; + Vector<Ref<Material>> materials; String scene_name; Vector<int> root_nodes; Vector<GLTFTexture> textures; - Vector<Ref<Texture> > images; + Vector<Ref<Texture2D>> images; Vector<GLTFSkin> skins; Vector<GLTFCamera> cameras; @@ -331,7 +292,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { String _sanitize_bone_name(const String &name); String _gen_unique_bone_name(GLTFState &state, const GLTFSkeletonIndex skel_i, const String &p_name); - Ref<Texture> _get_texture(GLTFState &state, const GLTFTextureIndex p_texture); + Ref<Texture2D> _get_texture(GLTFState &state, const GLTFTextureIndex p_texture); Error _parse_json(const String &p_path, GLTFState &state); Error _parse_glb(const String &p_path, GLTFState &state); @@ -348,11 +309,11 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Error _decode_buffer_view(GLTFState &state, double *dst, const GLTFBufferViewIndex p_buffer_view, const int skip_every, const int skip_bytes, const int element_size, const int count, const GLTFType type, const int component_count, const int component_type, const int component_size, const bool normalized, const int byte_offset, const bool for_vertex); Vector<double> _decode_accessor(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); - PoolVector<float> _decode_accessor_as_floats(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); - PoolVector<int> _decode_accessor_as_ints(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); - PoolVector<Vector2> _decode_accessor_as_vec2(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); - PoolVector<Vector3> _decode_accessor_as_vec3(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); - PoolVector<Color> _decode_accessor_as_color(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); + Vector<float> _decode_accessor_as_floats(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); + Vector<int> _decode_accessor_as_ints(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); + Vector<Vector2> _decode_accessor_as_vec2(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); + Vector<Vector3> _decode_accessor_as_vec3(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); + Vector<Color> _decode_accessor_as_color(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); Vector<Quat> _decode_accessor_as_quat(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); Vector<Transform2D> _decode_accessor_as_xform2d(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); Vector<Basis> _decode_accessor_as_basis(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); @@ -388,15 +349,15 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Error _parse_animations(GLTFState &state); - BoneAttachment *_generate_bone_attachment(GLTFState &state, Skeleton *skeleton, const GLTFNodeIndex node_index); - MeshInstance *_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); - Camera *_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); - Spatial *_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); + BoneAttachment3D *_generate_bone_attachment(GLTFState &state, Skeleton3D *skeleton, const GLTFNodeIndex node_index); + MeshInstance3D *_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); + Camera3D *_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); + Node3D *_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); - void _generate_scene_node(GLTFState &state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index); - Spatial *_generate_scene(GLTFState &state, const int p_bake_fps); + void _generate_scene_node(GLTFState &state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index); + Node3D *_generate_scene(GLTFState &state, const int p_bake_fps); - void _process_mesh_instances(GLTFState &state, Spatial *scene_root); + void _process_mesh_instances(GLTFState &state, Node3D *scene_root); void _assign_scene_names(GLTFState &state); @@ -408,7 +369,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { public: virtual uint32_t get_import_flags() const; virtual void get_extensions(List<String> *r_extensions) const; - virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps = NULL, Error *r_err = NULL); + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr); virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); EditorSceneImporterGLTF(); diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp index 18e53fc783..da2d1c9bdf 100644 --- a/editor/import/resource_importer_bitmask.cpp +++ b/editor/import/resource_importer_bitmask.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,55 +38,51 @@ #include "scene/resources/texture.h" String ResourceImporterBitMap::get_importer_name() const { - return "bitmap"; } String ResourceImporterBitMap::get_visible_name() const { - return "BitMap"; } -void ResourceImporterBitMap::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterBitMap::get_recognized_extensions(List<String> *p_extensions) const { ImageLoader::get_recognized_extensions(p_extensions); } + String ResourceImporterBitMap::get_save_extension() const { return "res"; } String ResourceImporterBitMap::get_resource_type() const { - return "BitMap"; } bool ResourceImporterBitMap::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - return true; } int ResourceImporterBitMap::get_preset_count() const { return 0; } -String ResourceImporterBitMap::get_preset_name(int p_idx) const { +String ResourceImporterBitMap::get_preset_name(int p_idx) const { return String(); } void ResourceImporterBitMap::get_import_options(List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "create_from", PROPERTY_HINT_ENUM, "Black & White,Alpha"), 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.5)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.5)); } Error ResourceImporterBitMap::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - int create_from = p_options["create_from"]; float threshold = p_options["threshold"]; Ref<Image> image; image.instance(); Error err = ImageLoader::load_image(p_source_file, image); - if (err != OK) + if (err != OK) { return err; + } int w = image->get_width(); int h = image->get_height(); @@ -94,11 +90,9 @@ Error ResourceImporterBitMap::import(const String &p_source_file, const String & Ref<BitMap> bitmap; bitmap.instance(); bitmap->create(Size2(w, h)); - image->lock(); for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { - bool bit; Color c = image->get_pixel(j, i); if (create_from == 0) { //b&W diff --git a/editor/import/resource_importer_bitmask.h b/editor/import/resource_importer_bitmask.h index 6ae7608ff2..927fac566e 100644 --- a/editor/import/resource_importer_bitmask.h +++ b/editor/import/resource_importer_bitmask.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,7 +51,7 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); ResourceImporterBitMap(); ~ResourceImporterBitMap(); diff --git a/editor/import/resource_importer_csv.cpp b/editor/import/resource_importer_csv.cpp index 64b5309cab..d29ba28a96 100644 --- a/editor/import/resource_importer_csv.cpp +++ b/editor/import/resource_importer_csv.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,16 +34,14 @@ #include "core/os/file_access.h" String ResourceImporterCSV::get_importer_name() const { - return "csv"; } String ResourceImporterCSV::get_visible_name() const { - return "CSV"; } -void ResourceImporterCSV::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterCSV::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("csv"); } @@ -52,20 +50,18 @@ String ResourceImporterCSV::get_save_extension() const { } String ResourceImporterCSV::get_resource_type() const { - return "TextFile"; } bool ResourceImporterCSV::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - return true; } int ResourceImporterCSV::get_preset_count() const { return 0; } -String ResourceImporterCSV::get_preset_name(int p_idx) const { +String ResourceImporterCSV::get_preset_name(int p_idx) const { return ""; } diff --git a/editor/import/resource_importer_csv.h b/editor/import/resource_importer_csv.h index 7d06bdb188..7aa48f68de 100644 --- a/editor/import/resource_importer_csv.h +++ b/editor/import/resource_importer_csv.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,7 +49,7 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); ResourceImporterCSV(); }; diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index d988e1dcc3..04e20dee86 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,16 +36,14 @@ #include "core/translation.h" String ResourceImporterCSVTranslation::get_importer_name() const { - return "csv_translation"; } String ResourceImporterCSVTranslation::get_visible_name() const { - return "CSV Translation"; } -void ResourceImporterCSVTranslation::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterCSVTranslation::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("csv"); } @@ -54,38 +52,40 @@ String ResourceImporterCSVTranslation::get_save_extension() const { } String ResourceImporterCSVTranslation::get_resource_type() const { - return "Translation"; } bool ResourceImporterCSVTranslation::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - return true; } int ResourceImporterCSVTranslation::get_preset_count() const { return 0; } -String ResourceImporterCSVTranslation::get_preset_name(int p_idx) const { +String ResourceImporterCSVTranslation::get_preset_name(int p_idx) const { return ""; } void ResourceImporterCSVTranslation::get_import_options(List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "delimiter", PROPERTY_HINT_ENUM, "Comma,Semicolon,Tab"), 0)); } Error ResourceImporterCSVTranslation::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - bool compress = p_options["compress"]; String delimiter; switch ((int)p_options["delimiter"]) { - case 0: delimiter = ","; break; - case 1: delimiter = ";"; break; - case 2: delimiter = "\t"; break; + case 0: + delimiter = ","; + break; + case 1: + delimiter = ";"; + break; + case 2: + delimiter = "\t"; + break; } FileAccessRef f = FileAccess::open(p_source_file, FileAccess::READ); @@ -96,10 +96,9 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const ERR_FAIL_COND_V(line.size() <= 1, ERR_PARSE_ERROR); Vector<String> locales; - Vector<Ref<Translation> > translations; + Vector<Ref<Translation>> translations; for (int i = 1; i < line.size(); i++) { - String locale = line[i]; ERR_FAIL_COND_V_MSG(!TranslationServer::is_locale_valid(locale), ERR_PARSE_ERROR, "Error importing CSV translation: '" + locale + "' is not a valid locale."); @@ -113,10 +112,8 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const line = f->get_csv_line(delimiter); while (line.size() == locales.size() + 1) { - String key = line[0]; if (key != "") { - for (int i = 1; i < line.size(); i++) { translations.write[i - 1]->add_message(key, line[i].c_unescape()); } diff --git a/editor/import/resource_importer_csv_translation.h b/editor/import/resource_importer_csv_translation.h index c2753b326f..742f6b8f60 100644 --- a/editor/import/resource_importer_csv_translation.h +++ b/editor/import/resource_importer_csv_translation.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,7 +49,7 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); ResourceImporterCSVTranslation(); }; diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp index 3d7663ba8f..885b00865b 100644 --- a/editor/import/resource_importer_image.cpp +++ b/editor/import/resource_importer_image.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,16 +36,14 @@ #include "scene/resources/texture.h" String ResourceImporterImage::get_importer_name() const { - return "image"; } String ResourceImporterImage::get_visible_name() const { - return "Image"; } -void ResourceImporterImage::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterImage::get_recognized_extensions(List<String> *p_extensions) const { ImageLoader::get_recognized_extensions(p_extensions); } @@ -54,20 +52,18 @@ String ResourceImporterImage::get_save_extension() const { } String ResourceImporterImage::get_resource_type() const { - return "Image"; } bool ResourceImporterImage::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - return true; } int ResourceImporterImage::get_preset_count() const { return 0; } -String ResourceImporterImage::get_preset_name(int p_idx) const { +String ResourceImporterImage::get_preset_name(int p_idx) const { return String(); } @@ -75,7 +71,6 @@ void ResourceImporterImage::get_import_options(List<ImportOption> *r_options, in } Error ResourceImporterImage::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'."); diff --git a/editor/import/resource_importer_image.h b/editor/import/resource_importer_image.h index beadf5a8ea..abb74d0665 100644 --- a/editor/import/resource_importer_image.h +++ b/editor/import/resource_importer_image.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -50,7 +50,7 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); ResourceImporterImage(); }; diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 7e3c4cecf4..1f39a12c25 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,208 +36,247 @@ #include "core/io/image_loader.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" +#include "resource_importer_texture.h" #include "scene/resources/texture.h" String ResourceImporterLayeredTexture::get_importer_name() const { + switch (mode) { + case MODE_CUBEMAP: { + return "cubemap_texture"; + } break; + case MODE_2D_ARRAY: { + return "2d_array_texture"; + } break; + case MODE_CUBEMAP_ARRAY: { + return "cubemap_array_texture"; + } break; + case MODE_3D: { + return "cubemap_3d_texture"; + } break; + } - return is_3d ? "texture_3d" : "texture_array"; + ERR_FAIL_V(""); } String ResourceImporterLayeredTexture::get_visible_name() const { + switch (mode) { + case MODE_CUBEMAP: { + return "Cubemap"; + } break; + case MODE_2D_ARRAY: { + return "Texture2DArray"; + } break; + case MODE_CUBEMAP_ARRAY: { + return "CubemapArray"; + } break; + case MODE_3D: { + return "3D"; + } break; + } - return is_3d ? "Texture3D" : "TextureArray"; + ERR_FAIL_V(""); } -void ResourceImporterLayeredTexture::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterLayeredTexture::get_recognized_extensions(List<String> *p_extensions) const { ImageLoader::get_recognized_extensions(p_extensions); } + String ResourceImporterLayeredTexture::get_save_extension() const { - return is_3d ? "tex3d" : "texarr"; + switch (mode) { + case MODE_CUBEMAP: { + return "scube"; + } break; + case MODE_2D_ARRAY: { + return "stexarray"; + } break; + case MODE_CUBEMAP_ARRAY: { + return "scubearray"; + } break; + case MODE_3D: { + return "stex3d"; + } break; + } + + ERR_FAIL_V(String()); } String ResourceImporterLayeredTexture::get_resource_type() const { - - return is_3d ? "Texture3D" : "TextureArray"; + switch (mode) { + case MODE_CUBEMAP: { + return "StreamCubemap"; + } break; + case MODE_2D_ARRAY: { + return "StreamTexture2DArray"; + } break; + case MODE_CUBEMAP_ARRAY: { + return "StreamCubemapArray"; + } break; + case MODE_3D: { + return "StreamTexture3D"; + } break; + } + ERR_FAIL_V(String()); } bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - + if (p_option == "compress/lossy_quality" && p_options.has("compress/mode")) { + return int(p_options["compress/mode"]) == COMPRESS_LOSSY; + } return true; } int ResourceImporterLayeredTexture::get_preset_count() const { - return 3; + return 0; } -String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { - static const char *preset_names[] = { - "3D", - "2D", - "ColorCorrect" - }; - - return preset_names[p_idx]; +String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { + return ""; } void ResourceImporterLayeredTexture::get_import_options(List<ImportOption> *r_options, int p_preset) const { - - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 1 : 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/no_bptc_if_rgb"), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), true)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/mipmaps"), p_preset == PRESET_COLOR_CORRECT ? 0 : 1)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/srgb", PROPERTY_HINT_ENUM, "Disable,Enable"), p_preset == PRESET_3D ? 1 : 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/horizontal", PROPERTY_HINT_RANGE, "1,256,1"), p_preset == PRESET_COLOR_CORRECT ? 16 : 8)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/vertical", PROPERTY_HINT_RANGE, "1,256,1"), p_preset == PRESET_COLOR_CORRECT ? 1 : 8)); -} - -void ResourceImporterLayeredTexture::_save_tex(const Vector<Ref<Image> > &p_images, const String &p_to_path, int p_compress_mode, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags) { - - FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE); - f->store_8('G'); - f->store_8('D'); - if (is_3d) { - f->store_8('3'); - } else { - f->store_8('A'); - } - f->store_8('T'); //godot streamable texture - - f->store_32(p_images[0]->get_width()); - f->store_32(p_images[0]->get_height()); - f->store_32(p_images.size()); //depth - f->store_32(p_texture_flags); - if (p_compress_mode != COMPRESS_VIDEO_RAM) { - //vram needs to do a first compression to tell what the format is, for the rest its ok - f->store_32(p_images[0]->get_format()); - f->store_32(p_compress_mode); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,Video RAM,Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1)); + + if (mode == MODE_2D_ARRAY || mode == MODE_3D) { + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/horizontal", PROPERTY_HINT_RANGE, "1,256,1"), 8)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/vertical", PROPERTY_HINT_RANGE, "1,256,1"), 8)); } - - if ((p_compress_mode == COMPRESS_LOSSLESS) && p_images[0]->get_format() > Image::FORMAT_RGBA8) { - p_compress_mode = COMPRESS_UNCOMPRESSED; //these can't go as lossy + if (mode == MODE_CUBEMAP || mode == MODE_CUBEMAP_ARRAY) { + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/arrangement", PROPERTY_HINT_ENUM, "1x6,2x3,3x2,6x1"), 1)); + if (mode == MODE_CUBEMAP_ARRAY) { + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/layout", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 1)); + } } +} +void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2) { for (int i = 0; i < p_images.size(); i++) { + if (p_force_po2) { + p_images.write[i]->resize_to_po2(); + } - switch (p_compress_mode) { - case COMPRESS_LOSSLESS: { - - Ref<Image> image = p_images[i]->duplicate(); - if (p_mipmaps) { - image->generate_mipmaps(); - } else { - image->clear_mipmaps(); - } + if (p_mipmaps) { + p_images.write[i]->generate_mipmaps(); + } else { + p_images.write[i]->clear_mipmaps(); + } + } - int mmc = image->get_mipmap_count() + 1; - f->store_32(mmc); + FileAccessRef f = FileAccess::open(p_to_path, FileAccess::WRITE); + f->store_8('G'); + f->store_8('S'); + f->store_8('T'); + f->store_8('L'); - for (int j = 0; j < mmc; j++) { + f->store_32(StreamTextureLayered::FORMAT_VERSION); + f->store_32(p_images.size()); + f->store_32(mode); + f->store_32(0); //dataformat + f->store_32(0); //mipmap limit - if (j > 0) { - image->shrink_x2(); - } + //reserverd + f->store_32(0); + f->store_32(0); + f->store_32(0); - PoolVector<uint8_t> data = Image::lossless_packer(image); - int data_len = data.size(); - f->store_32(data_len); + for (int i = 0; i < p_images.size(); i++) { + ResourceImporterTexture::save_to_stex_format(f, p_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy); + } - PoolVector<uint8_t>::Read r = data.read(); - f->store_buffer(r.ptr(), data_len); - } + f->close(); +} +Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + int compress_mode = p_options["compress/mode"]; + float lossy = p_options["compress/lossy_quality"]; + int hdr_compression = p_options["compress/hdr_compression"]; + int bptc_ldr = p_options["compress/bptc_ldr"]; + bool mipmaps = p_options["mipmaps/generate"]; + //bool mipmap_limit = p_options["mipmaps/limit"]; + + int channel_pack = p_options["compress/channel_pack"]; + int hslices = (p_options.has("slices/horizontal")) ? int(p_options["slices/horizontal"]) : 0; + int vslices = (p_options.has("slices/vertical")) ? int(p_options["slices/vertical"]) : 0; + int arrangement = (p_options.has("slices/arrangement")) ? int(p_options["slices/arrangement"]) : 0; + int layout = (p_options.has("slices/layout")) ? int(p_options["slices/layout"]) : 0; + int amount = (p_options.has("slices/amount")) ? int(p_options["slices/amount"]) : 0; + + if (mode == MODE_CUBEMAP || mode == MODE_CUBEMAP_ARRAY) { + switch (arrangement) { + case CUBEMAP_FORMAT_1X6: { + hslices = 1; + vslices = 6; } break; - case COMPRESS_VIDEO_RAM: { - - Ref<Image> image = p_images[i]->duplicate(); - image->generate_mipmaps(false); - - Image::CompressSource csource = Image::COMPRESS_SOURCE_LAYERED; - image->compress(p_vram_compression, csource, 0.7); - - if (i == 0) { - //hack so we can properly tell the format - f->store_32(image->get_format()); - f->store_32(p_compress_mode); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed - } - - PoolVector<uint8_t> data = image->get_data(); - int dl = data.size(); - - PoolVector<uint8_t>::Read r = data.read(); - f->store_buffer(r.ptr(), dl); + case CUBEMAP_FORMAT_2X3: { + hslices = 2; + vslices = 3; } break; - case COMPRESS_UNCOMPRESSED: { - - Ref<Image> image = p_images[i]->duplicate(); - - if (p_mipmaps) { - image->generate_mipmaps(); - } else { - image->clear_mipmaps(); - } - - PoolVector<uint8_t> data = image->get_data(); - int dl = data.size(); - - PoolVector<uint8_t>::Read r = data.read(); - - f->store_buffer(r.ptr(), dl); - + case CUBEMAP_FORMAT_3X2: { + hslices = 3; + vslices = 2; + } break; + case CUBEMAP_FORMAT_6X1: { + hslices = 6; + vslices = 1; } break; } - } - memdelete(f); -} - -Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - - int compress_mode = p_options["compress/mode"]; - int no_bptc_if_rgb = p_options["compress/no_bptc_if_rgb"]; - int repeat = p_options["flags/repeat"]; - bool filter = p_options["flags/filter"]; - bool mipmaps = p_options["flags/mipmaps"]; - int srgb = p_options["flags/srgb"]; - int hslices = p_options["slices/horizontal"]; - int vslices = p_options["slices/vertical"]; + if (mode == MODE_CUBEMAP_ARRAY) { + if (layout == 0) { + hslices *= amount; + } else { + vslices *= amount; + } + } + } Ref<Image> image; image.instance(); - Error err = ImageLoader::load_image(p_source_file, image, NULL, false, 1.0); - if (err != OK) + Error err = ImageLoader::load_image(p_source_file, image, nullptr, false, 1.0); + if (err != OK) { return err; + } - int tex_flags = 0; - if (repeat > 0) - tex_flags |= Texture::FLAG_REPEAT; - if (repeat == 2) - tex_flags |= Texture::FLAG_MIRRORED_REPEAT; - if (filter) - tex_flags |= Texture::FLAG_FILTER; - if (mipmaps || compress_mode == COMPRESS_VIDEO_RAM) - tex_flags |= Texture::FLAG_MIPMAPS; - if (srgb == 1) - tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR; - - Vector<Ref<Image> > slices; + if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) { + //basis universal does not support float formats, fall back + compress_mode = COMPRESS_VRAM_COMPRESSED; + } - int slice_w = image->get_width() / hslices; - int slice_h = image->get_height() / vslices; + if (compress_mode == COMPRESS_VRAM_COMPRESSED) { + mipmaps = true; + } //optimize - if (compress_mode == COMPRESS_VIDEO_RAM) { + if (compress_mode == COMPRESS_VRAM_COMPRESSED) { //if using video ram, optimize - if (srgb) { + if (channel_pack == 0) { //remove alpha if not needed, so compression is more efficient if (image->get_format() == Image::FORMAT_RGBA8 && !image->detect_alpha()) { image->convert(Image::FORMAT_RGB8); } - } else { + } else if (image->get_format() < Image::FORMAT_RGBA8) { image->optimize_channels(); } } + Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC; + if (channel_pack == 0) { + csource = Image::COMPRESS_SOURCE_SRGB; + } + + Image::UsedChannels used_channels = image->detect_used_channels(csource); + + Vector<Ref<Image>> slices; + + int slice_w = image->get_width() / hslices; + int slice_h = image->get_height() / vslices; + for (int i = 0; i < vslices; i++) { for (int j = 0; j < hslices; j++) { int x = slice_w * j; @@ -254,58 +293,77 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String extension = get_save_extension(); Array formats_imported; - if (compress_mode == COMPRESS_VIDEO_RAM) { + if (compress_mode == COMPRESS_VRAM_COMPRESSED) { //must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). //Android, GLES 2.x bool ok_on_pc = false; - bool encode_bptc = false; + bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); + bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565); + bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc"); + bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc"); - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) { - - encode_bptc = true; - - if (no_bptc_if_rgb) { - Image::DetectChannels channels = image->get_detected_channels(); - if (channels != Image::DETECTED_LA && channels != Image::DETECTED_RGBA) { - encode_bptc = false; + if (can_bptc) { + formats_imported.push_back("bptc"); //needs to be aded anyway + } + bool can_compress_hdr = hdr_compression > 0; + + if (is_hdr && can_compress_hdr) { + if (used_channels == Image::USED_CHANNELS_LA || used_channels == Image::USED_CHANNELS_RGBA) { + //can compress hdr, but hdr with alpha is not compressible + + if (hdr_compression == 2) { + //but user selected to compress hdr anyway, so force an alpha-less format. + if (image->get_format() == Image::FORMAT_RGBAF) { + for (int i = 0; i < slices.size(); i++) { + slices.write[i]->convert(Image::FORMAT_RGBF); + } + + } else if (image->get_format() == Image::FORMAT_RGBAH) { + for (int i = 0; i < slices.size(); i++) { + slices.write[i]->convert(Image::FORMAT_RGBH); + } + } + } else { + can_compress_hdr = false; } } - formats_imported.push_back("bptc"); + if (can_compress_hdr) { + if (!can_bptc) { + //default to rgbe + if (image->get_format() != Image::FORMAT_RGBE9995) { + for (int i = 0; i < slices.size(); i++) { + slices.write[i]->convert(Image::FORMAT_RGBE9995); + } + } + } + } else { + can_bptc = false; + } } - if (encode_bptc) { - - _save_tex(slices, p_save_path + ".bptc." + extension, compress_mode, Image::COMPRESS_BPTC, mipmaps, tex_flags); - r_platform_variants->push_back("bptc"); - ok_on_pc = true; + if (is_ldr && can_bptc) { + if (bptc_ldr == 0 || (bptc_ldr == 1 && !(used_channels == Image::USED_CHANNELS_LA || used_channels == Image::USED_CHANNELS_RGBA))) { + can_bptc = false; + } } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc")) { - - _save_tex(slices, p_save_path + ".s3tc." + extension, compress_mode, Image::COMPRESS_S3TC, mipmaps, tex_flags); + if (can_bptc || can_s3tc) { + _save_tex(slices, p_save_path + ".s3tc." + extension, compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, csource, used_channels, mipmaps, false); r_platform_variants->push_back("s3tc"); - ok_on_pc = true; formats_imported.push_back("s3tc"); + ok_on_pc = true; } if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { - - _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, Image::COMPRESS_ETC2, mipmaps, tex_flags); + _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true); r_platform_variants->push_back("etc2"); formats_imported.push_back("etc2"); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { - _save_tex(slices, p_save_path + ".etc." + extension, compress_mode, Image::COMPRESS_ETC, mipmaps, tex_flags); - r_platform_variants->push_back("etc"); - formats_imported.push_back("etc"); - } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { - - _save_tex(slices, p_save_path + ".pvrtc." + extension, compress_mode, Image::COMPRESS_PVRTC4, mipmaps, tex_flags); + _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true); r_platform_variants->push_back("pvrtc"); formats_imported.push_back("pvrtc"); } @@ -315,12 +373,12 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const } } else { //import normally - _save_tex(slices, p_save_path + "." + extension, compress_mode, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags); + _save_tex(slices, p_save_path + "." + extension, compress_mode, lossy, Image::COMPRESS_S3TC /* IGNORED */, csource, used_channels, mipmaps, false); } if (r_metadata) { Dictionary metadata; - metadata["vram_texture"] = compress_mode == COMPRESS_VIDEO_RAM; + metadata["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED; if (formats_imported.size()) { metadata["imported_formats"] = formats_imported; } @@ -336,10 +394,9 @@ const char *ResourceImporterLayeredTexture::compression_formats[] = { "etc", "etc2", "pvrtc", - NULL + nullptr }; String ResourceImporterLayeredTexture::get_import_settings_string() const { - String s; int index = 0; @@ -356,7 +413,6 @@ String ResourceImporterLayeredTexture::get_import_settings_string() const { } bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path) const { - //will become invalid if formats are missing to import Dictionary metadata = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path); @@ -391,12 +447,11 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p return valid; } -ResourceImporterLayeredTexture *ResourceImporterLayeredTexture::singleton = NULL; +ResourceImporterLayeredTexture *ResourceImporterLayeredTexture::singleton = nullptr; ResourceImporterLayeredTexture::ResourceImporterLayeredTexture() { - singleton = this; - is_3d = true; + mode = MODE_CUBEMAP; } ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() { diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h index d6acbbabca..18eaf31f6b 100644 --- a/editor/import/resource_importer_layered_texture.h +++ b/editor/import/resource_importer_layered_texture.h @@ -5,8 +5,38 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/*************************************************************************/ +/* resource_importer_layered_texture.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,18 +64,38 @@ #include "core/image.h" #include "core/io/resource_importer.h" -class StreamTexture; +class StreamTexture2D; class ResourceImporterLayeredTexture : public ResourceImporter { GDCLASS(ResourceImporterLayeredTexture, ResourceImporter); - bool is_3d; +public: + enum Mode { + MODE_2D_ARRAY, + MODE_CUBEMAP, + MODE_CUBEMAP_ARRAY, + MODE_3D, + }; + + enum CubemapFormat { + CUBEMAP_FORMAT_1X6, + CUBEMAP_FORMAT_2X3, + CUBEMAP_FORMAT_3X2, + CUBEMAP_FORMAT_6X1, + }; + + enum TextureFlags { + TEXTURE_FLAGS_MIPMAPS = 1 + }; + +private: + Mode mode; static const char *compression_formats[]; protected: - static void _texture_reimport_srgb(const Ref<StreamTexture> &p_tex); - static void _texture_reimport_3d(const Ref<StreamTexture> &p_tex); - static void _texture_reimport_normal(const Ref<StreamTexture> &p_tex); + static void _texture_reimport_srgb(const Ref<StreamTexture2D> &p_tex); + static void _texture_reimport_3d(const Ref<StreamTexture2D> &p_tex); + static void _texture_reimport_normal(const Ref<StreamTexture2D> &p_tex); static ResourceImporterLayeredTexture *singleton; @@ -57,16 +107,12 @@ public: virtual String get_save_extension() const; virtual String get_resource_type() const; - enum Preset { - PRESET_3D, - PRESET_2D, - PRESET_COLOR_CORRECT, - }; - enum CompressMode { COMPRESS_LOSSLESS, - COMPRESS_VIDEO_RAM, - COMPRESS_UNCOMPRESSED + COMPRESS_LOSSY, + COMPRESS_VRAM_COMPRESSED, + COMPRESS_VRAM_UNCOMPRESSED, + COMPRESS_BASIS_UNIVERSAL }; virtual int get_preset_count() const; @@ -75,17 +121,19 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - void _save_tex(const Vector<Ref<Image> > &p_images, const String &p_to_path, int p_compress_mode, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags); + void _save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2); - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); void update_imports(); virtual bool are_import_settings_valid(const String &p_path) const; virtual String get_import_settings_string() const; - void set_3d(bool p_3d) { is_3d = p_3d; } + void set_mode(Mode p_mode) { mode = p_mode; } + ResourceImporterLayeredTexture(); ~ResourceImporterLayeredTexture(); }; + #endif // RESOURCE_IMPORTER_LAYERED_TEXTURE_H diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index 31a099ef83..49b47bf4be 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,26 +32,23 @@ #include "core/io/resource_saver.h" #include "core/os/file_access.h" -#include "scene/3d/mesh_instance.h" -#include "scene/3d/spatial.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/node_3d.h" #include "scene/resources/mesh.h" #include "scene/resources/surface_tool.h" uint32_t EditorOBJImporter::get_import_flags() const { - return IMPORT_SCENE; } -static Error _parse_material_library(const String &p_path, Map<String, Ref<SpatialMaterial> > &material_map, List<String> *r_missing_deps) { - +static Error _parse_material_library(const String &p_path, Map<String, Ref<StandardMaterial3D>> &material_map, List<String> *r_missing_deps) { FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Couldn't open MTL file '%s', it may not exist or not be readable.", p_path)); - Ref<SpatialMaterial> current; + Ref<StandardMaterial3D> current; String current_name; String base_path = p_path.get_base_dir(); while (true) { - String l = f->get_line().strip_edges(); if (l.begins_with("newmtl ")) { @@ -63,7 +60,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati material_map[current_name] = current; } else if (l.begins_with("Ka ")) { //uv - WARN_PRINTS("OBJ: Ambient light for material '" + current_name + "' is ignored in PBR"); + WARN_PRINT("OBJ: Ambient light for material '" + current_name + "' is ignored in PBR"); } else if (l.begins_with("Kd ")) { //normal @@ -102,7 +99,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati c.a = d; current->set_albedo(c); if (c.a < 0.99) { - current->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + current->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); } } else if (l.begins_with("Tr ")) { //normal @@ -114,12 +111,12 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati c.a = 1.0 - d; current->set_albedo(c); if (c.a < 0.99) { - current->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + current->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); } } else if (l.begins_with("map_Ka ")) { //uv - WARN_PRINTS("OBJ: Ambient light texture for material '" + current_name + "' is ignored in PBR"); + WARN_PRINT("OBJ: Ambient light texture for material '" + current_name + "' is ignored in PBR"); } else if (l.begins_with("map_Kd ")) { //normal @@ -133,10 +130,10 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati path = base_path.plus_file(p); } - Ref<Texture> texture = ResourceLoader::load(path); + Ref<Texture2D> texture = ResourceLoader::load(path); if (texture.is_valid()) { - current->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + current->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, texture); } else if (r_missing_deps) { r_missing_deps->push_back(path); } @@ -153,10 +150,10 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati path = base_path.plus_file(p); } - Ref<Texture> texture = ResourceLoader::load(path); + Ref<Texture2D> texture = ResourceLoader::load(path); if (texture.is_valid()) { - current->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + current->set_texture(StandardMaterial3D::TEXTURE_METALLIC, texture); } else if (r_missing_deps) { r_missing_deps->push_back(path); } @@ -173,10 +170,10 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati path = base_path.plus_file(p); } - Ref<Texture> texture = ResourceLoader::load(path); + Ref<Texture2D> texture = ResourceLoader::load(path); if (texture.is_valid()) { - current->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + current->set_texture(StandardMaterial3D::TEXTURE_ROUGHNESS, texture); } else if (r_missing_deps) { r_missing_deps->push_back(path); } @@ -187,11 +184,11 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati String p = l.replace("map_bump", "").replace("\\", "/").strip_edges(); String path = base_path.plus_file(p); - Ref<Texture> texture = ResourceLoader::load(path); + Ref<Texture2D> texture = ResourceLoader::load(path); if (texture.is_valid()) { - current->set_feature(SpatialMaterial::FEATURE_NORMAL_MAPPING, true); - current->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + current->set_feature(StandardMaterial3D::FEATURE_NORMAL_MAPPING, true); + current->set_texture(StandardMaterial3D::TEXTURE_NORMAL, texture); } else if (r_missing_deps) { r_missing_deps->push_back(path); } @@ -203,8 +200,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati return OK; } -static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, List<String> *r_missing_deps) { - +static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, List<String> *r_missing_deps) { FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path)); @@ -213,6 +209,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p bool generate_tangents = p_generate_tangents; Vector3 scale_mesh = p_scale_mesh; + Vector3 offset_mesh = p_offset_mesh; int mesh_flags = p_optimize ? Mesh::ARRAY_COMPRESS_DEFAULT : 0; Vector<Vector3> vertices; @@ -220,7 +217,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p Vector<Vector2> uvs; String name; - Map<String, Map<String, Ref<SpatialMaterial> > > material_map; + Map<String, Map<String, Ref<StandardMaterial3D>>> material_map; Ref<SurfaceTool> surf_tool = memnew(SurfaceTool); surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES); @@ -230,7 +227,6 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p String current_group; while (true) { - String l = f->get_line().strip_edges(); while (l.length() && l[l.length() - 1] == '\\') { String add = f->get_line().strip_edges(); @@ -245,9 +241,9 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p Vector<String> v = l.split(" ", false); ERR_FAIL_COND_V(v.size() < 4, ERR_FILE_CORRUPT); Vector3 vtx; - vtx.x = v[1].to_float() * scale_mesh.x; - vtx.y = v[2].to_float() * scale_mesh.y; - vtx.z = v[3].to_float() * scale_mesh.z; + vtx.x = v[1].to_float() * scale_mesh.x + offset_mesh.x; + vtx.y = v[2].to_float() * scale_mesh.y + offset_mesh.y; + vtx.z = v[3].to_float() * scale_mesh.z + offset_mesh.z; vertices.push_back(vtx); } else if (l.begins_with("vt ")) { //uv @@ -282,12 +278,10 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p ERR_FAIL_COND_V(face[0].size() != face[1].size(), ERR_FILE_CORRUPT); for (int i = 2; i < v.size() - 1; i++) { - face[2] = v[i + 1].split("/"); ERR_FAIL_COND_V(face[0].size() != face[2].size(), ERR_FILE_CORRUPT); for (int j = 0; j < 3; j++) { - int idx = j; if (idx < 2) { @@ -296,23 +290,26 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p if (face[idx].size() == 3) { int norm = face[idx][2].to_int() - 1; - if (norm < 0) + if (norm < 0) { norm += normals.size() + 1; + } ERR_FAIL_INDEX_V(norm, normals.size(), ERR_FILE_CORRUPT); surf_tool->add_normal(normals[norm]); } if (face[idx].size() >= 2 && face[idx][1] != String()) { int uv = face[idx][1].to_int() - 1; - if (uv < 0) + if (uv < 0) { uv += uvs.size() + 1; + } ERR_FAIL_INDEX_V(uv, uvs.size(), ERR_FILE_CORRUPT); surf_tool->add_uv(uvs[uv]); } int vtx = face[idx][0].to_int() - 1; - if (vtx < 0) + if (vtx < 0) { vtx += vertices.size() + 1; + } ERR_FAIL_INDEX_V(vtx, vertices.size(), ERR_FILE_CORRUPT); Vector3 vertex = vertices[vtx]; @@ -325,10 +322,11 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p } } else if (l.begins_with("s ")) { //smoothing String what = l.substr(2, l.length()).strip_edges(); - if (what == "off") + if (what == "off") { surf_tool->add_smooth_group(false); - else + } else { surf_tool->add_smooth_group(true); + } } else if (/*l.begins_with("g ") ||*/ l.begins_with("usemtl ") || (l.begins_with("o ") || f->eof_reached())) { //commit group to mesh //groups are too annoying if (surf_tool->get_vertex_array().size()) { @@ -364,7 +362,6 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p } if (l.begins_with("o ") || f->eof_reached()) { - if (!p_single_mesh) { mesh->set_name(name); r_meshes.push_back(mesh); @@ -383,12 +380,10 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p } if (l.begins_with("usemtl ")) { - current_material = l.replace("usemtl", "").strip_edges(); } if (l.begins_with("g ")) { - current_group = l.substr(2, l.length()).strip_edges(); } @@ -396,7 +391,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p current_material_library = l.replace("mtllib", "").strip_edges(); if (!material_map.has(current_material_library)) { - Map<String, Ref<SpatialMaterial> > lib; + Map<String, Ref<StandardMaterial3D>> lib; Error err = _parse_material_library(current_material_library, lib, r_missing_deps); if (err == ERR_CANT_OPEN) { String dir = p_path.get_base_dir(); @@ -410,7 +405,6 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p } if (p_single_mesh) { - r_meshes.push_back(mesh); } @@ -418,23 +412,21 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p } Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { + List<Ref<Mesh>> meshes; - List<Ref<Mesh> > meshes; - - Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & IMPORT_USE_COMPRESSION, Vector3(1, 1, 1), r_missing_deps); + Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & IMPORT_USE_COMPRESSION, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps); if (err != OK) { if (r_err) { *r_err = err; } - return NULL; + return nullptr; } - Spatial *scene = memnew(Spatial); + Node3D *scene = memnew(Node3D); - for (List<Ref<Mesh> >::Element *E = meshes.front(); E; E = E->next()) { - - MeshInstance *mi = memnew(MeshInstance); + for (List<Ref<Mesh>>::Element *E = meshes.front(); E; E = E->next()) { + MeshInstance3D *mi = memnew(MeshInstance3D); mi->set_mesh(E->get()); mi->set_name(E->get()->get_name()); scene->add_child(mi); @@ -447,33 +439,36 @@ Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, in return scene; } -Ref<Animation> EditorOBJImporter::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { +Ref<Animation> EditorOBJImporter::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { return Ref<Animation>(); } void EditorOBJImporter::get_extensions(List<String> *r_extensions) const { - r_extensions->push_back("obj"); } EditorOBJImporter::EditorOBJImporter() { } + //////////////////////////////////////////////////// String ResourceImporterOBJ::get_importer_name() const { return "wavefront_obj"; } + String ResourceImporterOBJ::get_visible_name() const { return "OBJ As Mesh"; } -void ResourceImporterOBJ::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterOBJ::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("obj"); } + String ResourceImporterOBJ::get_save_extension() const { return "mesh"; } + String ResourceImporterOBJ::get_resource_type() const { return "Mesh"; } @@ -481,26 +476,26 @@ String ResourceImporterOBJ::get_resource_type() const { int ResourceImporterOBJ::get_preset_count() const { return 0; } + String ResourceImporterOBJ::get_preset_name(int p_idx) const { return ""; } void ResourceImporterOBJ::get_import_options(List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1))); + r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0))); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimize_mesh"), true)); } -bool ResourceImporterOBJ::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { +bool ResourceImporterOBJ::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { return true; } Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + List<Ref<Mesh>> meshes; - List<Ref<Mesh> > meshes; - - Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], NULL); + Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], p_options["offset_mesh"], nullptr); ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG); diff --git a/editor/import/resource_importer_obj.h b/editor/import/resource_importer_obj.h index adad21da61..aec5de3dcc 100644 --- a/editor/import/resource_importer_obj.h +++ b/editor/import/resource_importer_obj.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,13 +34,12 @@ #include "resource_importer_scene.h" class EditorOBJImporter : public EditorSceneImporter { - GDCLASS(EditorOBJImporter, EditorSceneImporter); public: virtual uint32_t get_import_flags() const; virtual void get_extensions(List<String> *r_extensions) const; - virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL); + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr); virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); EditorOBJImporter(); @@ -62,7 +61,7 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); ResourceImporterOBJ(); }; diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 3a99968192..ec82f78e75 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,33 +32,29 @@ #include "core/io/resource_saver.h" #include "editor/editor_node.h" -#include "scene/resources/packed_scene.h" - -#include "scene/3d/collision_shape.h" -#include "scene/3d/mesh_instance.h" -#include "scene/3d/navigation.h" -#include "scene/3d/physics_body.h" -#include "scene/3d/portal.h" -#include "scene/3d/room_instance.h" -#include "scene/3d/vehicle_body.h" +#include "scene/3d/collision_shape_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/navigation_3d.h" +#include "scene/3d/physics_body_3d.h" +#include "scene/3d/vehicle_body_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/animation.h" -#include "scene/resources/box_shape.h" -#include "scene/resources/plane_shape.h" -#include "scene/resources/ray_shape.h" +#include "scene/resources/box_shape_3d.h" +#include "scene/resources/packed_scene.h" +#include "scene/resources/ray_shape_3d.h" #include "scene/resources/resource_format_text.h" -#include "scene/resources/sphere_shape.h" +#include "scene/resources/sphere_shape_3d.h" +#include "scene/resources/world_margin_shape_3d.h" uint32_t EditorSceneImporter::get_import_flags() const { - if (get_script_instance()) { return get_script_instance()->call("_get_import_flags"); } ERR_FAIL_V(0); } -void EditorSceneImporter::get_extensions(List<String> *r_extensions) const { +void EditorSceneImporter::get_extensions(List<String> *r_extensions) const { if (get_script_instance()) { Array arr = get_script_instance()->call("_get_extensions"); for (int i = 0; i < arr.size(); i++) { @@ -69,39 +65,35 @@ void EditorSceneImporter::get_extensions(List<String> *r_extensions) const { ERR_FAIL(); } -Node *EditorSceneImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { +Node *EditorSceneImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { if (get_script_instance()) { return get_script_instance()->call("_import_scene", p_path, p_flags, p_bake_fps); } - ERR_FAIL_V(NULL); + ERR_FAIL_V(nullptr); } Ref<Animation> EditorSceneImporter::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { - if (get_script_instance()) { return get_script_instance()->call("_import_animation", p_path, p_flags); } - ERR_FAIL_V(NULL); + ERR_FAIL_V(nullptr); } //for documenters, these functions are useful when an importer calls an external conversion helper (like, fbx2gltf), //and you want to load the resulting file Node *EditorSceneImporter::import_scene_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) { - return ResourceImporterScene::get_singleton()->import_scene_from_other_importer(this, p_path, p_flags, p_bake_fps); } Ref<Animation> EditorSceneImporter::import_animation_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) { - return ResourceImporterScene::get_singleton()->import_animation_from_other_importer(this, p_path, p_flags, p_bake_fps); } void EditorSceneImporter::_bind_methods() { - ClassDB::bind_method(D_METHOD("import_scene_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneImporter::import_scene_from_other_importer); ClassDB::bind_method(D_METHOD("import_animation_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneImporter::import_animation_from_other_importer); @@ -129,27 +121,24 @@ void EditorSceneImporter::_bind_methods() { ///////////////////////////////// void EditorScenePostImport::_bind_methods() { - BIND_VMETHOD(MethodInfo(Variant::OBJECT, "post_import", PropertyInfo(Variant::OBJECT, "scene"))); ClassDB::bind_method(D_METHOD("get_source_folder"), &EditorScenePostImport::get_source_folder); ClassDB::bind_method(D_METHOD("get_source_file"), &EditorScenePostImport::get_source_file); } Node *EditorScenePostImport::post_import(Node *p_scene) { - - if (get_script_instance()) + if (get_script_instance()) { return get_script_instance()->call("post_import", p_scene); + } return p_scene; } String EditorScenePostImport::get_source_folder() const { - return source_folder; } String EditorScenePostImport::get_source_file() const { - return source_file; } @@ -162,18 +151,15 @@ EditorScenePostImport::EditorScenePostImport() { } String ResourceImporterScene::get_importer_name() const { - return "scene"; } String ResourceImporterScene::get_visible_name() const { - return "Scene"; } void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions) const { - - for (Set<Ref<EditorSceneImporter> >::Element *E = importers.front(); E; E = E->next()) { + for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) { E->get()->get_extensions(p_extensions); } } @@ -183,27 +169,29 @@ String ResourceImporterScene::get_save_extension() const { } String ResourceImporterScene::get_resource_type() const { - return "PackedScene"; } bool ResourceImporterScene::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - if (p_option.begins_with("animation/")) { - if (p_option != "animation/import" && !bool(p_options["animation/import"])) + if (p_option != "animation/import" && !bool(p_options["animation/import"])) { return false; + } - if (p_option == "animation/keep_custom_tracks" && int(p_options["animation/storage"]) == 0) + if (p_option == "animation/keep_custom_tracks" && int(p_options["animation/storage"]) == 0) { return false; + } - if (p_option.begins_with("animation/optimizer/") && p_option != "animation/optimizer/enabled" && !bool(p_options["animation/optimizer/enabled"])) + if (p_option.begins_with("animation/optimizer/") && p_option != "animation/optimizer/enabled" && !bool(p_options["animation/optimizer/enabled"])) { return false; + } if (p_option.begins_with("animation/clip_")) { int max_clip = p_options["animation/clips/amount"]; int clip = p_option.get_slice("/", 1).get_slice("_", 1).to_int() - 1; - if (clip >= max_clip) + if (clip >= max_clip) { return false; + } } } @@ -221,73 +209,82 @@ bool ResourceImporterScene::get_option_visibility(const String &p_option, const int ResourceImporterScene::get_preset_count() const { return PRESET_MAX; } -String ResourceImporterScene::get_preset_name(int p_idx) const { +String ResourceImporterScene::get_preset_name(int p_idx) const { switch (p_idx) { - case PRESET_SINGLE_SCENE: return TTR("Import as Single Scene"); - case PRESET_SEPARATE_ANIMATIONS: return TTR("Import with Separate Animations"); - case PRESET_SEPARATE_MATERIALS: return TTR("Import with Separate Materials"); - case PRESET_SEPARATE_MESHES: return TTR("Import with Separate Objects"); - case PRESET_SEPARATE_MESHES_AND_MATERIALS: return TTR("Import with Separate Objects+Materials"); - case PRESET_SEPARATE_MESHES_AND_ANIMATIONS: return TTR("Import with Separate Objects+Animations"); - case PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS: return TTR("Import with Separate Materials+Animations"); - case PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS: return TTR("Import with Separate Objects+Materials+Animations"); - case PRESET_MULTIPLE_SCENES: return TTR("Import as Multiple Scenes"); - case PRESET_MULTIPLE_SCENES_AND_MATERIALS: return TTR("Import as Multiple Scenes+Materials"); + case PRESET_SINGLE_SCENE: + return TTR("Import as Single Scene"); + case PRESET_SEPARATE_ANIMATIONS: + return TTR("Import with Separate Animations"); + case PRESET_SEPARATE_MATERIALS: + return TTR("Import with Separate Materials"); + case PRESET_SEPARATE_MESHES: + return TTR("Import with Separate Objects"); + case PRESET_SEPARATE_MESHES_AND_MATERIALS: + return TTR("Import with Separate Objects+Materials"); + case PRESET_SEPARATE_MESHES_AND_ANIMATIONS: + return TTR("Import with Separate Objects+Animations"); + case PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS: + return TTR("Import with Separate Materials+Animations"); + case PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS: + return TTR("Import with Separate Objects+Materials+Animations"); + case PRESET_MULTIPLE_SCENES: + return TTR("Import as Multiple Scenes"); + case PRESET_MULTIPLE_SCENES_AND_MATERIALS: + return TTR("Import as Multiple Scenes+Materials"); } return ""; } static bool _teststr(const String &p_what, const String &p_str) { - String what = p_what; //remove trailing spaces and numbers, some apps like blender add ".number" to duplicates so also compensate for this while (what.length() && ((what[what.length() - 1] >= '0' && what[what.length() - 1] <= '9') || what[what.length() - 1] <= 32 || what[what.length() - 1] == '.')) { - what = what.substr(0, what.length() - 1); } - if (what.findn("$" + p_str) != -1) //blender and other stuff + if (what.findn("$" + p_str) != -1) { //blender and other stuff return true; - if (what.to_lower().ends_with("-" + p_str)) //collada only supports "_" and "-" besides letters + } + if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters return true; - if (what.to_lower().ends_with("_" + p_str)) //collada only supports "_" and "-" besides letters + } + if (what.to_lower().ends_with("_" + p_str)) { //collada only supports "_" and "-" besides letters return true; + } return false; } static String _fixstr(const String &p_what, const String &p_str) { - String what = p_what; //remove trailing spaces and numbers, some apps like blender add ".number" to duplicates so also compensate for this while (what.length() && ((what[what.length() - 1] >= '0' && what[what.length() - 1] <= '9') || what[what.length() - 1] <= 32 || what[what.length() - 1] == '.')) { - what = what.substr(0, what.length() - 1); } String end = p_what.substr(what.length(), p_what.length() - what.length()); - if (what.findn("$" + p_str) != -1) //blender and other stuff + if (what.findn("$" + p_str) != -1) { //blender and other stuff return what.replace("$" + p_str, "") + end; - if (what.to_lower().ends_with("-" + p_str)) //collada only supports "_" and "-" besides letters + } + if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters return what.substr(0, what.length() - (p_str.length() + 1)) + end; - if (what.to_lower().ends_with("_" + p_str)) //collada only supports "_" and "-" besides letters + } + if (what.to_lower().ends_with("_" + p_str)) { //collada only supports "_" and "-" besides letters return what.substr(0, what.length() - (p_str.length() + 1)) + end; + } return what; } -static void _gen_shape_list(const Ref<Mesh> &mesh, List<Ref<Shape> > &r_shape_list, bool p_convex) { - +static void _gen_shape_list(const Ref<Mesh> &mesh, List<Ref<Shape3D>> &r_shape_list, bool p_convex) { if (!p_convex) { - - Ref<Shape> shape = mesh->create_trimesh_shape(); + Ref<Shape3D> shape = mesh->create_trimesh_shape(); r_shape_list.push_back(shape); } else { - - Vector<Ref<Shape> > cd = mesh->convex_decompose(); + Vector<Ref<Shape3D>> cd = mesh->convex_decompose(); if (cd.size()) { for (int i = 0; i < cd.size(); i++) { r_shape_list.push_back(cd[i]); @@ -296,11 +293,9 @@ static void _gen_shape_list(const Ref<Mesh> &mesh, List<Ref<Shape> > &r_shape_li } } -Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape> > > &collision_map, LightBakeMode p_light_bake_mode) { - +Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape3D>>> &collision_map, LightBakeMode p_light_bake_mode) { // children first for (int i = 0; i < p_node->get_child_count(); i++) { - Node *r = _fix_node(p_node->get_child(i), p_root, collision_map, p_light_bake_mode); if (!r) { i--; //was erased @@ -312,42 +307,36 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> bool isroot = p_node == p_root; if (!isroot && _teststr(name, "noimp")) { - memdelete(p_node); - return NULL; + return nullptr; } - if (Object::cast_to<MeshInstance>(p_node)) { - - MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); + if (Object::cast_to<MeshInstance3D>(p_node)) { + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); Ref<ArrayMesh> m = mi->get_mesh(); if (m.is_valid()) { - for (int i = 0; i < m->get_surface_count(); i++) { - - Ref<SpatialMaterial> mat = m->surface_get_material(i); - if (!mat.is_valid()) + Ref<StandardMaterial3D> mat = m->surface_get_material(i); + if (!mat.is_valid()) { continue; + } if (_teststr(mat->get_name(), "alpha")) { - - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); mat->set_name(_fixstr(mat->get_name(), "alpha")); } if (_teststr(mat->get_name(), "vcol")) { - - mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); + mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); mat->set_name(_fixstr(mat->get_name(), "vcol")); } } } if (p_light_bake_mode != LIGHT_BAKE_DISABLED) { - - mi->set_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT, true); + mi->set_gi_mode(GeometryInstance3D::GI_MODE_BAKED); } } @@ -358,7 +347,6 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> List<StringName> anims; ap->get_animation_list(&anims); for (List<StringName>::Element *E = anims.front(); E; E = E->next()) { - Ref<Animation> anim = ap->get_animation(E->get()); ERR_CONTINUE(anim.is_null()); for (int i = 0; i < anim->get_track_count(); i++) { @@ -377,15 +365,15 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> } if (_teststr(name, "colonly") || _teststr(name, "convcolonly")) { - - if (isroot) + if (isroot) { return p_node; - MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); + } + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); if (mi) { Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { - List<Ref<Shape> > shapes; + List<Ref<Shape3D>> shapes; String fixed_name; if (collision_map.has(mesh)) { shapes = collision_map[mesh]; @@ -403,11 +391,10 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> fixed_name = _fixstr(name, "convcolonly"); } - ERR_FAIL_COND_V(fixed_name == String(), NULL); + ERR_FAIL_COND_V(fixed_name == String(), nullptr); if (shapes.size()) { - - StaticBody *col = memnew(StaticBody); + StaticBody3D *col = memnew(StaticBody3D); col->set_transform(mi->get_transform()); col->set_name(fixed_name); p_node->replace_by(col); @@ -415,9 +402,8 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> p_node = col; int idx = 0; - for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { - - CollisionShape *cshape = memnew(CollisionShape); + for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) { + CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(E->get()); col->add_child(cshape); @@ -430,55 +416,55 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> } else if (p_node->has_meta("empty_draw_type")) { String empty_draw_type = String(p_node->get_meta("empty_draw_type")); - StaticBody *sb = memnew(StaticBody); + StaticBody3D *sb = memnew(StaticBody3D); sb->set_name(_fixstr(name, "colonly")); - Object::cast_to<Spatial>(sb)->set_transform(Object::cast_to<Spatial>(p_node)->get_transform()); + Object::cast_to<Node3D>(sb)->set_transform(Object::cast_to<Node3D>(p_node)->get_transform()); p_node->replace_by(sb); memdelete(p_node); - p_node = NULL; - CollisionShape *colshape = memnew(CollisionShape); + p_node = nullptr; + CollisionShape3D *colshape = memnew(CollisionShape3D); if (empty_draw_type == "CUBE") { - BoxShape *boxShape = memnew(BoxShape); + BoxShape3D *boxShape = memnew(BoxShape3D); boxShape->set_extents(Vector3(1, 1, 1)); colshape->set_shape(boxShape); - colshape->set_name("BoxShape"); + colshape->set_name("BoxShape3D"); } else if (empty_draw_type == "SINGLE_ARROW") { - RayShape *rayShape = memnew(RayShape); + RayShape3D *rayShape = memnew(RayShape3D); rayShape->set_length(1); colshape->set_shape(rayShape); - colshape->set_name("RayShape"); - Object::cast_to<Spatial>(sb)->rotate_x(Math_PI / 2); + colshape->set_name("RayShape3D"); + Object::cast_to<Node3D>(sb)->rotate_x(Math_PI / 2); } else if (empty_draw_type == "IMAGE") { - PlaneShape *planeShape = memnew(PlaneShape); - colshape->set_shape(planeShape); - colshape->set_name("PlaneShape"); + WorldMarginShape3D *world_margin_shape = memnew(WorldMarginShape3D); + colshape->set_shape(world_margin_shape); + colshape->set_name("WorldMarginShape3D"); } else { - SphereShape *sphereShape = memnew(SphereShape); + SphereShape3D *sphereShape = memnew(SphereShape3D); sphereShape->set_radius(1); colshape->set_shape(sphereShape); - colshape->set_name("SphereShape"); + colshape->set_name("SphereShape3D"); } sb->add_child(colshape); colshape->set_owner(sb->get_owner()); } - } else if (_teststr(name, "rigid") && Object::cast_to<MeshInstance>(p_node)) { - - if (isroot) + } else if (_teststr(name, "rigid") && Object::cast_to<MeshInstance3D>(p_node)) { + if (isroot) { return p_node; + } - MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { - List<Ref<Shape> > shapes; + List<Ref<Shape3D>> shapes; if (collision_map.has(mesh)) { shapes = collision_map[mesh]; } else { _gen_shape_list(mesh, shapes, true); } - RigidBody *rigid_body = memnew(RigidBody); + RigidBody3D *rigid_body = memnew(RigidBody3D); rigid_body->set_name(_fixstr(name, "rigid")); p_node->replace_by(rigid_body); rigid_body->set_transform(mi->get_transform()); @@ -489,9 +475,8 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> mi->set_owner(rigid_body->get_owner()); int idx = 0; - for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { - - CollisionShape *cshape = memnew(CollisionShape); + for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) { + CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(E->get()); rigid_body->add_child(cshape); @@ -501,14 +486,13 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> } } - } else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<MeshInstance>(p_node)) { - - MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); + } else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<MeshInstance3D>(p_node)) { + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { - List<Ref<Shape> > shapes; + List<Ref<Shape3D>> shapes; String fixed_name; if (collision_map.has(mesh)) { shapes = collision_map[mesh]; @@ -533,15 +517,14 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> } if (shapes.size()) { - StaticBody *col = memnew(StaticBody); + StaticBody3D *col = memnew(StaticBody3D); col->set_name("static_collision"); mi->add_child(col); col->set_owner(mi->get_owner()); int idx = 0; - for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { - - CollisionShape *cshape = memnew(CollisionShape); + for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) { + CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(E->get()); col->add_child(cshape); @@ -553,33 +536,33 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> } } - } else if (_teststr(name, "navmesh") && Object::cast_to<MeshInstance>(p_node)) { - - if (isroot) + } else if (_teststr(name, "navmesh") && Object::cast_to<MeshInstance3D>(p_node)) { + if (isroot) { return p_node; + } - MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); Ref<ArrayMesh> mesh = mi->get_mesh(); - ERR_FAIL_COND_V(mesh.is_null(), NULL); - NavigationMeshInstance *nmi = memnew(NavigationMeshInstance); + ERR_FAIL_COND_V(mesh.is_null(), nullptr); + NavigationRegion3D *nmi = memnew(NavigationRegion3D); nmi->set_name(_fixstr(name, "navmesh")); Ref<NavigationMesh> nmesh = memnew(NavigationMesh); nmesh->create_from_mesh(mesh); nmi->set_navigation_mesh(nmesh); - Object::cast_to<Spatial>(nmi)->set_transform(mi->get_transform()); + Object::cast_to<Node3D>(nmi)->set_transform(mi->get_transform()); p_node->replace_by(nmi); memdelete(p_node); p_node = nmi; } else if (_teststr(name, "vehicle")) { - - if (isroot) + if (isroot) { return p_node; + } Node *owner = p_node->get_owner(); - Spatial *s = Object::cast_to<Spatial>(p_node); - VehicleBody *bv = memnew(VehicleBody); + Node3D *s = Object::cast_to<Node3D>(p_node); + VehicleBody3D *bv = memnew(VehicleBody3D); String n = _fixstr(p_node->get_name(), "vehicle"); bv->set_name(n); p_node->replace_by(bv); @@ -593,13 +576,13 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> p_node = bv; } else if (_teststr(name, "wheel")) { - - if (isroot) + if (isroot) { return p_node; + } Node *owner = p_node->get_owner(); - Spatial *s = Object::cast_to<Spatial>(p_node); - VehicleWheel *bv = memnew(VehicleWheel); + Node3D *s = Object::cast_to<Node3D>(p_node); + VehicleWheel3D *bv = memnew(VehicleWheel3D); String n = _fixstr(p_node->get_name(), "wheel"); bv->set_name(n); p_node->replace_by(bv); @@ -612,16 +595,14 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> p_node = bv; - } else if (Object::cast_to<MeshInstance>(p_node)) { - + } else if (Object::cast_to<MeshInstance3D>(p_node)) { //last attempt, maybe collision inside the mesh data - MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); Ref<ArrayMesh> mesh = mi->get_mesh(); if (!mesh.is_null()) { - - List<Ref<Shape> > shapes; + List<Ref<Shape3D>> shapes; if (collision_map.has(mesh)) { shapes = collision_map[mesh]; } else if (_teststr(mesh->get_name(), "col")) { @@ -635,15 +616,14 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> } if (shapes.size()) { - StaticBody *col = memnew(StaticBody); + StaticBody3D *col = memnew(StaticBody3D); col->set_name("static_collision"); p_node->add_child(col); col->set_owner(p_node->get_owner()); int idx = 0; - for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { - - CollisionShape *cshape = memnew(CollisionShape); + for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) { + CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(E->get()); col->add_child(cshape); @@ -659,41 +639,39 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh> } void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, bool p_bake_all) { - - if (!scene->has_node(String("AnimationPlayer"))) + if (!scene->has_node(String("AnimationPlayer"))) { return; + } Node *n = scene->get_node(String("AnimationPlayer")); ERR_FAIL_COND(!n); AnimationPlayer *anim = Object::cast_to<AnimationPlayer>(n); ERR_FAIL_COND(!anim); - if (!anim->has_animation("default")) + if (!anim->has_animation("default")) { return; + } Ref<Animation> default_anim = anim->get_animation("default"); for (int i = 0; i < p_clips.size(); i += 4) { - String name = p_clips[i]; float from = p_clips[i + 1]; float to = p_clips[i + 2]; bool loop = p_clips[i + 3]; - if (from >= to) + if (from >= to) { continue; + } Ref<Animation> new_anim = memnew(Animation); for (int j = 0; j < default_anim->get_track_count(); j++) { - List<float> keys; int kc = default_anim->track_get_key_count(j); int dtrack = -1; for (int k = 0; k < kc; k++) { - float kt = default_anim->track_get_key_time(j, k); if (kt >= from && kt < to) { - //found a key within range, so create track if (dtrack == -1) { new_anim->add_track(default_anim->track_get_type(j)); @@ -701,7 +679,6 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo new_anim->track_set_path(dtrack, default_anim->track_get_path(j)); if (kt > (from + 0.01) && k > 0) { - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM) { Quat q; Vector3 p; @@ -730,7 +707,6 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo } if (dtrack != -1 && kt >= to) { - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM) { Quat q; Vector3 p; @@ -750,7 +726,6 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo dtrack = new_anim->get_track_count() - 1; new_anim->track_set_path(dtrack, default_anim->track_get_path(j)); if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM) { - Quat q; Vector3 p; Vector3 s; @@ -777,12 +752,10 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo } void ResourceImporterScene::_filter_anim_tracks(Ref<Animation> anim, Set<String> &keep) { - Ref<Animation> a = anim; ERR_FAIL_COND(!a.is_valid()); for (int j = 0; j < a->get_track_count(); j++) { - String path = a->track_get_path(j); if (!keep.has(path)) { @@ -793,9 +766,9 @@ void ResourceImporterScene::_filter_anim_tracks(Ref<Animation> anim, Set<String> } void ResourceImporterScene::_filter_tracks(Node *scene, const String &p_text) { - - if (!scene->has_node(String("AnimationPlayer"))) + if (!scene->has_node(String("AnimationPlayer"))) { return; + } Node *n = scene->get_node(String("AnimationPlayer")); ERR_FAIL_COND(!n); AnimationPlayer *anim = Object::cast_to<AnimationPlayer>(n); @@ -803,14 +776,12 @@ void ResourceImporterScene::_filter_tracks(Node *scene, const String &p_text) { Vector<String> strings = p_text.split("\n"); for (int i = 0; i < strings.size(); i++) { - strings.write[i] = strings[i].strip_edges(); } List<StringName> anim_names; anim->get_animation_list(&anim_names); for (List<StringName>::Element *E = anim_names.front(); E; E = E->next()) { - String name = E->get(); bool valid_for_this = false; bool valid = false; @@ -819,9 +790,7 @@ void ResourceImporterScene::_filter_tracks(Node *scene, const String &p_text) { Set<String> keep_local; for (int i = 0; i < strings.size(); i++) { - if (strings[i].begins_with("@")) { - valid_for_this = false; for (Set<String>::Element *F = keep_local.front(); F; F = F->next()) { keep.insert(F->get()); @@ -830,59 +799,64 @@ void ResourceImporterScene::_filter_tracks(Node *scene, const String &p_text) { Vector<String> filters = strings[i].substr(1, strings[i].length()).split(","); for (int j = 0; j < filters.size(); j++) { - String fname = filters[j].strip_edges(); - if (fname == "") + if (fname == "") { continue; + } int fc = fname[0]; bool plus; - if (fc == '+') + if (fc == '+') { plus = true; - else if (fc == '-') + } else if (fc == '-') { plus = false; - else + } else { continue; + } String filter = fname.substr(1, fname.length()).strip_edges(); - if (!name.matchn(filter)) + if (!name.matchn(filter)) { continue; + } valid_for_this = plus; } - if (valid_for_this) + if (valid_for_this) { valid = true; + } } else if (valid_for_this) { - Ref<Animation> a = anim->get_animation(name); - if (!a.is_valid()) + if (!a.is_valid()) { continue; + } for (int j = 0; j < a->get_track_count(); j++) { - String path = a->track_get_path(j); String tname = strings[i]; - if (tname == "") + if (tname == "") { continue; + } int fc = tname[0]; bool plus; - if (fc == '+') + if (fc == '+') { plus = true; - else if (fc == '-') + } else if (fc == '-') { plus = false; - else + } else { continue; + } String filter = tname.substr(1, tname.length()).strip_edges(); - if (!path.matchn(filter)) + if (!path.matchn(filter)) { continue; + } - if (plus) + if (plus) { keep_local.insert(path); - else if (!keep.has(path)) { + } else if (!keep.has(path)) { keep_local.erase(path); } } @@ -899,9 +873,9 @@ void ResourceImporterScene::_filter_tracks(Node *scene, const String &p_text) { } void ResourceImporterScene::_optimize_animations(Node *scene, float p_max_lin_error, float p_max_ang_error, float p_max_angle) { - - if (!scene->has_node(String("AnimationPlayer"))) + if (!scene->has_node(String("AnimationPlayer"))) { return; + } Node *n = scene->get_node(String("AnimationPlayer")); ERR_FAIL_COND(!n); AnimationPlayer *anim = Object::cast_to<AnimationPlayer>(n); @@ -910,14 +884,12 @@ void ResourceImporterScene::_optimize_animations(Node *scene, float p_max_lin_er List<StringName> anim_names; anim->get_animation_list(&anim_names); for (List<StringName>::Element *E = anim_names.front(); E; E = E->next()) { - Ref<Animation> a = anim->get_animation(E->get()); a->optimize(p_max_lin_error, p_max_ang_error, Math::deg2rad(p_max_angle)); } } static String _make_extname(const String &p_str) { - String ext_name = p_str.replace(".", "_"); ext_name = ext_name.replace(":", "_"); ext_name = ext_name.replace("\"", "_"); @@ -933,35 +905,31 @@ static String _make_extname(const String &p_str) { } void ResourceImporterScene::_find_meshes(Node *p_node, Map<Ref<ArrayMesh>, Transform> &meshes) { - List<PropertyInfo> pi; p_node->get_property_list(&pi); - MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); if (mi) { - Ref<ArrayMesh> mesh = mi->get_mesh(); if (mesh.is_valid() && !meshes.has(mesh)) { - Spatial *s = mi; + Node3D *s = mi; Transform transform; while (s) { transform = transform * s->get_transform(); - s = s->get_parent_spatial(); + s = Object::cast_to<Node3D>(s->get_parent()); } meshes[mesh] = transform; } } for (int i = 0; i < p_node->get_child_count(); i++) { - _find_meshes(p_node->get_child(i), meshes); } } -void ResourceImporterScene::_make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_animations_as_text, bool p_keep_animations, bool p_make_materials, bool p_materials_as_text, bool p_keep_materials, bool p_make_meshes, bool p_meshes_as_text, Map<Ref<Animation>, Ref<Animation> > &p_animations, Map<Ref<Material>, Ref<Material> > &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh> > &p_meshes) { - +void ResourceImporterScene::_make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_animations_as_text, bool p_keep_animations, bool p_make_materials, bool p_materials_as_text, bool p_keep_materials, bool p_make_meshes, bool p_meshes_as_text, Map<Ref<Animation>, Ref<Animation>> &p_animations, Map<Ref<Material>, Ref<Material>> &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh>> &p_meshes) { List<PropertyInfo> pi; if (p_make_animations) { @@ -971,12 +939,10 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String List<StringName> anims; ap->get_animation_list(&anims); for (List<StringName>::Element *E = anims.front(); E; E = E->next()) { - Ref<Animation> anim = ap->get_animation(E->get()); ERR_CONTINUE(anim.is_null()); if (!p_animations.has(anim)) { - //mark what comes from the file first, this helps eventually keep user data for (int i = 0; i < anim->get_track_count(); i++) { anim->track_set_imported(i, true); @@ -1015,15 +981,11 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String p_node->get_property_list(&pi); for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { - if (E->get().type == Variant::OBJECT) { - Ref<Material> mat = p_node->get(E->get().name); if (p_make_materials && mat.is_valid() && mat->get_name() != "") { - if (!p_materials.has(mat)) { - String ext_name; if (p_materials_as_text) { @@ -1036,28 +998,22 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String //if exists, use it p_materials[mat] = ResourceLoader::load(ext_name); } else { - ResourceSaver::save(ext_name, mat, ResourceSaver::FLAG_CHANGE_PATH); p_materials[mat] = ResourceLoader::load(ext_name, "", true); // disable loading from the cache. } } if (p_materials[mat] != mat) { - p_node->set(E->get().name, p_materials[mat]); } } else { - Ref<ArrayMesh> mesh = p_node->get(E->get().name); if (mesh.is_valid()) { - bool mesh_just_added = false; if (p_make_meshes) { - if (!p_meshes.has(mesh)) { - //meshes are always overwritten, keeping them is not practical String ext_name; @@ -1075,16 +1031,16 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String } if (p_make_materials) { - if (mesh_just_added || !p_meshes.has(mesh)) { - for (int i = 0; i < mesh->get_surface_count(); i++) { mat = mesh->surface_get_material(i); - if (!mat.is_valid()) + if (!mat.is_valid()) { continue; - if (mat->get_name() == "") + } + if (mat->get_name() == "") { continue; + } if (!p_materials.has(mat)) { String ext_name; @@ -1099,19 +1055,16 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String //if exists, use it p_materials[mat] = ResourceLoader::load(ext_name); } else { - ResourceSaver::save(ext_name, mat, ResourceSaver::FLAG_CHANGE_PATH); p_materials[mat] = ResourceLoader::load(ext_name, "", true); // disable loading from the cache. } } if (p_materials[mat] != mat) { - mesh->surface_set_material(i, p_materials[mat]); //re-save the mesh since a material is now assigned if (p_make_meshes) { - String ext_name; if (p_meshes_as_text) { @@ -1137,14 +1090,12 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String } for (int i = 0; i < p_node->get_child_count(); i++) { - _make_external_resources(p_node->get_child(i), p_base_path, p_make_animations, p_animations_as_text, p_keep_animations, p_make_materials, p_materials_as_text, p_keep_materials, p_make_meshes, p_meshes_as_text, p_animations, p_materials, p_meshes); } } void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, int p_preset) const { - - r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), "Spatial")); + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), "Node3D")); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), "Scene Root")); List<String> script_extentions; @@ -1153,8 +1104,9 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in String script_ext_hint; for (List<String>::Element *E = script_extentions.front(); E; E = E->next()) { - if (script_ext_hint != "") + if (script_ext_hint != "") { script_ext_hint += ","; + } script_ext_hint += "*." + E->get(); } @@ -1163,7 +1115,7 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in bool scenes_out = p_preset == PRESET_MULTIPLE_SCENES || p_preset == PRESET_MULTIPLE_SCENES_AND_MATERIALS; bool animations_out = p_preset == PRESET_SEPARATE_ANIMATIONS || p_preset == PRESET_SEPARATE_MESHES_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS; - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0)); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/custom_script", PROPERTY_HINT_FILE, script_ext_hint), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "nodes/storage", PROPERTY_HINT_ENUM, "Single Scene,Instanced Sub-Scenes"), scenes_out ? 1 : 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/location", PROPERTY_HINT_ENUM, "Node,Mesh"), (meshes_out || materials_out) ? 1 : 0)); @@ -1173,17 +1125,18 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.mesh),Files (.tres)"), meshes_out ? 1 : 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "external_files/store_in_subdir"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15)); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "animation/filter_script", PROPERTY_HINT_MULTILINE_TEXT), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "animation/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.anim),Files (.tres)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), animations_out)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/keep_custom_tracks"), animations_out)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/optimizer/max_linear_error"), 0.05)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/optimizer/max_angular_error"), 0.01)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/optimizer/max_angle"), 22)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/optimizer/max_linear_error"), 0.05)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/optimizer/max_angular_error"), 0.01)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/optimizer/max_angle"), 22)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/optimizer/remove_unused_tracks"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "animation/clips/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0)); for (int i = 0; i < 256; i++) { @@ -1195,7 +1148,6 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in } void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner) { - if (p_node != p_new_owner && p_node->get_owner() == p_scene) { p_node->set_owner(p_new_owner); } @@ -1207,31 +1159,29 @@ void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_ } Node *ResourceImporterScene::import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps) { - Ref<EditorSceneImporter> importer; String ext = p_path.get_extension().to_lower(); - for (Set<Ref<EditorSceneImporter> >::Element *E = importers.front(); E; E = E->next()) { - - if (E->get().ptr() == p_exception) + for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) { + if (E->get().ptr() == p_exception) { continue; + } List<String> extensions; E->get()->get_extensions(&extensions); for (List<String>::Element *F = extensions.front(); F; F = F->next()) { - if (F->get().to_lower() == ext) { - importer = E->get(); break; } } - if (importer.is_valid()) + if (importer.is_valid()) { break; + } } - ERR_FAIL_COND_V(!importer.is_valid(), NULL); + ERR_FAIL_COND_V(!importer.is_valid(), nullptr); List<String> missing; Error err; @@ -1239,37 +1189,34 @@ Node *ResourceImporterScene::import_scene_from_other_importer(EditorSceneImporte } Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps) { - Ref<EditorSceneImporter> importer; String ext = p_path.get_extension().to_lower(); - for (Set<Ref<EditorSceneImporter> >::Element *E = importers.front(); E; E = E->next()) { - - if (E->get().ptr() == p_exception) + for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) { + if (E->get().ptr() == p_exception) { continue; + } List<String> extensions; E->get()->get_extensions(&extensions); for (List<String>::Element *F = extensions.front(); F; F = F->next()) { - if (F->get().to_lower() == ext) { - importer = E->get(); break; } } - if (importer.is_valid()) + if (importer.is_valid()) { break; + } } - ERR_FAIL_COND_V(!importer.is_valid(), NULL); + ERR_FAIL_COND_V(!importer.is_valid(), nullptr); return importer->import_animation(p_path, p_flags, p_bake_fps); } Error ResourceImporterScene::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - const String &src_path = p_source_file; Ref<EditorSceneImporter> importer; @@ -1278,22 +1225,20 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p EditorProgress progress("import", TTR("Import Scene"), 104); progress.step(TTR("Importing Scene..."), 0); - for (Set<Ref<EditorSceneImporter> >::Element *E = importers.front(); E; E = E->next()) { - + for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) { List<String> extensions; E->get()->get_extensions(&extensions); for (List<String>::Element *F = extensions.front(); F; F = F->next()) { - if (F->get().to_lower() == ext) { - importer = E->get(); break; } } - if (importer.is_valid()) + if (importer.is_valid()) { break; + } } ERR_FAIL_COND_V(!importer.is_valid(), ERR_FILE_UNRECOGNIZED); @@ -1301,20 +1246,29 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p float fps = p_options["animation/fps"]; int import_flags = EditorSceneImporter::IMPORT_ANIMATION_DETECT_LOOP; - if (!bool(p_options["animation/optimizer/remove_unused_tracks"])) + if (!bool(p_options["animation/optimizer/remove_unused_tracks"])) { import_flags |= EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS; + } - if (bool(p_options["animation/import"])) + if (bool(p_options["animation/import"])) { import_flags |= EditorSceneImporter::IMPORT_ANIMATION; + } - if (int(p_options["meshes/compress"])) + if (int(p_options["meshes/compress"])) { import_flags |= EditorSceneImporter::IMPORT_USE_COMPRESSION; + } - if (bool(p_options["meshes/ensure_tangents"])) + if (bool(p_options["meshes/ensure_tangents"])) { import_flags |= EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS; + } - if (int(p_options["materials/location"]) == 0) + if (int(p_options["materials/location"]) == 0) { import_flags |= EditorSceneImporter::IMPORT_MATERIALS_IN_INSTANCES; + } + + if (bool(p_options["skins/use_named_skins"])) { + import_flags |= EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS; + } Error err = OK; List<String> missing_deps; // for now, not much will be done with this @@ -1326,17 +1280,16 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p String root_type = p_options["nodes/root_type"]; root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class. - Ref<Script> root_script = NULL; + Ref<Script> root_script = nullptr; if (ScriptServer::is_global_class(root_type)) { root_script = ResourceLoader::load(ScriptServer::get_global_class_path(root_type)); root_type = ScriptServer::get_global_class_base(root_type); } - if (root_type != "Spatial") { + if (root_type != "Node3D") { Node *base_node = Object::cast_to<Node>(ClassDB::instance(root_type)); if (base_node) { - scene->replace_by(base_node); memdelete(scene); scene = base_node; @@ -1347,15 +1300,17 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p scene->set_script(Variant(root_script)); } - if (Object::cast_to<Spatial>(scene)) { - float root_scale = p_options["nodes/root_scale"]; - Object::cast_to<Spatial>(scene)->scale(Vector3(root_scale, root_scale, root_scale)); + float root_scale = 1.0; + if (Object::cast_to<Node3D>(scene)) { + root_scale = p_options["nodes/root_scale"]; + Object::cast_to<Node3D>(scene)->scale(Vector3(root_scale, root_scale, root_scale)); } - if (p_options["nodes/root_name"] != "Scene Root") + if (p_options["nodes/root_name"] != "Scene Root") { scene->set_name(p_options["nodes/root_name"]); - else + } else { scene->set_name(p_save_path.get_file().get_basename()); + } err = OK; @@ -1367,7 +1322,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p float anim_optimizer_maxang = p_options["animation/optimizer/max_angle"]; int light_bake_mode = p_options["meshes/light_baking"]; - Map<Ref<Mesh>, List<Ref<Shape> > > collision_map; + Map<Ref<Mesh>, List<Ref<Shape3D>>> collision_map; scene = _fix_node(scene, scene, collision_map, LightBakeMode(light_bake_mode)); @@ -1377,7 +1332,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p Array animation_clips; { - int clip_count = p_options["animation/clips/amount"]; for (int i = 0; i < clip_count; i++) { @@ -1412,7 +1366,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p String base_path = p_source_file.get_base_dir(); if (external_animations || external_materials || external_meshes || external_scenes) { - if (bool(p_options["external_files/store_in_subdir"])) { String subdir_name = p_source_file.get_file().get_basename(); DirAccess *da = DirAccess::open(base_path); @@ -1424,40 +1377,119 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p } if (light_bake_mode == 2 /* || generate LOD */) { - Map<Ref<ArrayMesh>, Transform> meshes; _find_meshes(scene, meshes); - if (light_bake_mode == 2) { + String file_id = src_path.get_file(); + String cache_file_path = base_path.plus_file(file_id + ".unwrap_cache"); + + Vector<unsigned char> cache_data; + + if (FileAccess::exists(cache_file_path)) { + Error err2; + FileAccess *file = FileAccess::open(cache_file_path, FileAccess::READ, &err2); + + if (err2) { + if (file) { + memdelete(file); + } + } else { + int cache_size = file->get_len(); + cache_data.resize(cache_size); + file->get_buffer(cache_data.ptrw(), cache_size); + } + } + + float texel_size = p_options["meshes/lightmap_texel_size"]; + texel_size = MAX(0.001, texel_size); + + Map<String, unsigned int> used_unwraps; + + EditorProgress progress2("gen_lightmaps", TTR("Generating Lightmaps"), meshes.size()); + int step = 0; + for (Map<Ref<ArrayMesh>, Transform>::Element *E = meshes.front(); E; E = E->next()) { + Ref<ArrayMesh> mesh = E->key(); + String name = mesh->get_name(); + if (name == "") { //should not happen but.. + name = "Mesh " + itos(step); + } - float texel_size = p_options["meshes/lightmap_texel_size"]; - texel_size = MAX(0.001, texel_size); + progress2.step(TTR("Generating for Mesh: ") + name + " (" + itos(step) + "/" + itos(meshes.size()) + ")", step); - EditorProgress progress2("gen_lightmaps", TTR("Generating Lightmaps"), meshes.size()); - int step = 0; - for (Map<Ref<ArrayMesh>, Transform>::Element *E = meshes.front(); E; E = E->next()) { + int *ret_cache_data = (int *)cache_data.ptrw(); + unsigned int ret_cache_size = cache_data.size(); + bool ret_used_cache = true; // Tell the unwrapper to use the cache + Error err2 = mesh->lightmap_unwrap_cached(ret_cache_data, ret_cache_size, ret_used_cache, E->get(), texel_size); - Ref<ArrayMesh> mesh = E->key(); - String name = mesh->get_name(); - if (name == "") { //should not happen but.. - name = "Mesh " + itos(step); + if (err2 != OK) { + EditorNode::add_io_error("Mesh '" + name + "' failed lightmap generation. Please fix geometry."); + } else { + String hash = String::md5((unsigned char *)ret_cache_data); + used_unwraps.insert(hash, ret_cache_size); + + if (!ret_used_cache) { + // Cache was not used, add the generated entry to the current cache + if (cache_data.empty()) { + cache_data.resize(4 + ret_cache_size); + int *data = (int *)cache_data.ptrw(); + data[0] = 1; + memcpy(&data[1], ret_cache_data, ret_cache_size); + } else { + int current_size = cache_data.size(); + cache_data.resize(cache_data.size() + ret_cache_size); + unsigned char *ptrw = cache_data.ptrw(); + memcpy(&ptrw[current_size], ret_cache_data, ret_cache_size); + int *data = (int *)ptrw; + data[0] += 1; + } } + } + step++; + } - progress2.step(TTR("Generating for Mesh: ") + name + " (" + itos(step) + "/" + itos(meshes.size()) + ")", step); + Error err2; + FileAccess *file = FileAccess::open(cache_file_path, FileAccess::WRITE, &err2); - Error err2 = mesh->lightmap_unwrap(E->get(), texel_size); - if (err2 != OK) { - EditorNode::add_io_error("Mesh '" + name + "' failed lightmap generation. Please fix geometry."); + if (err2) { + if (file) { + memdelete(file); + } + } else { + // Store number of entries + file->store_32(used_unwraps.size()); + + // Store cache entries + const int *cache = (int *)cache_data.ptr(); + unsigned int r_idx = 1; + for (int i = 0; i < cache[0]; ++i) { + unsigned char *entry_start = (unsigned char *)&cache[r_idx]; + String entry_hash = String::md5(entry_start); + if (used_unwraps.has(entry_hash)) { + unsigned int entry_size = used_unwraps[entry_hash]; + file->store_buffer(entry_start, entry_size); } - step++; + + r_idx += 4; // hash + r_idx += 2; // size hint + + int vertex_count = cache[r_idx]; + r_idx += 1; // vertex count + r_idx += vertex_count; // vertex + r_idx += vertex_count * 2; // uvs + + int index_count = cache[r_idx]; + r_idx += 1; // index count + r_idx += index_count; // indices } + + file->close(); } } if (external_animations || external_materials || external_meshes) { - Map<Ref<Animation>, Ref<Animation> > anim_map; - Map<Ref<Material>, Ref<Material> > mat_map; - Map<Ref<ArrayMesh>, Ref<ArrayMesh> > mesh_map; + Map<Ref<Animation>, Ref<Animation>> anim_map; + Map<Ref<Material>, Ref<Material>> mat_map; + Map<Ref<ArrayMesh>, Ref<ArrayMesh>> mesh_map; bool keep_materials = bool(p_options["materials/keep_on_reimport"]); @@ -1474,9 +1506,8 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p if (!scr.is_valid()) { EditorNode::add_io_error(TTR("Couldn't load post-import script:") + " " + post_import_script_path); } else { - post_import_script = Ref<EditorScenePostImport>(memnew(EditorScenePostImport)); - post_import_script->set_script(scr.get_ref_ptr()); + post_import_script->set_script(scr); if (!post_import_script->get_script_instance()) { EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):") + " " + post_import_script_path); post_import_script.unref(); @@ -1489,7 +1520,9 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p post_import_script->init(base_path, p_source_file); scene = post_import_script->post_import(scene); if (!scene) { - EditorNode::add_io_error(TTR("Error running post-import script:") + " " + post_import_script_path); + EditorNode::add_io_error( + TTR("Error running post-import script:") + " " + post_import_script_path + "\n" + + TTR("Did you return a Node-derived object in the `post_import()` method?")); return err; } } @@ -1500,8 +1533,9 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p //save sub-scenes as instances! for (int i = 0; i < scene->get_child_count(); i++) { Node *child = scene->get_child(i); - if (child->get_owner() != scene) + if (child->get_owner() != scene) { continue; //not a real child probably created by scene type (ig, a scrollbar) + } _replace_owner(child, scene, child); String cn = String(child->get_name()).strip_edges().replace(".", "_").replace(":", "_"); @@ -1532,30 +1566,33 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p return OK; } -ResourceImporterScene *ResourceImporterScene::singleton = NULL; +ResourceImporterScene *ResourceImporterScene::singleton = nullptr; ResourceImporterScene::ResourceImporterScene() { singleton = this; } + /////////////////////////////////////// uint32_t EditorSceneImporterESCN::get_import_flags() const { return IMPORT_SCENE; } + void EditorSceneImporterESCN::get_extensions(List<String> *r_extensions) const { r_extensions->push_back("escn"); } -Node *EditorSceneImporterESCN::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { +Node *EditorSceneImporterESCN::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { Error error; Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error); - ERR_FAIL_COND_V_MSG(!ps.is_valid(), NULL, "Cannot load scene as text resource from path '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(!ps.is_valid(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'."); Node *scene = ps->instance(); - ERR_FAIL_COND_V(!scene, NULL); + ERR_FAIL_COND_V(!scene, nullptr); return scene; } + Ref<Animation> EditorSceneImporterESCN::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { ERR_FAIL_V(Ref<Animation>()); } diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index ef9a77917f..34d96bbc44 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,12 +34,11 @@ #include "core/io/resource_importer.h" #include "scene/resources/animation.h" #include "scene/resources/mesh.h" -#include "scene/resources/shape.h" +#include "scene/resources/shape_3d.h" class Material; class EditorSceneImporter : public Reference { - GDCLASS(EditorSceneImporter, Reference); protected: @@ -59,20 +58,20 @@ public: IMPORT_GENERATE_TANGENT_ARRAYS = 256, IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 512, IMPORT_MATERIALS_IN_INSTANCES = 1024, - IMPORT_USE_COMPRESSION = 2048 + IMPORT_USE_COMPRESSION = 2048, + IMPORT_USE_NAMED_SKIN_BINDS = 4096, }; virtual uint32_t get_import_flags() const; virtual void get_extensions(List<String> *r_extensions) const; - virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL); + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr); virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); EditorSceneImporter() {} }; class EditorScenePostImport : public Reference { - GDCLASS(EditorScenePostImport, Reference); String source_folder; @@ -92,7 +91,7 @@ public: class ResourceImporterScene : public ResourceImporter { GDCLASS(ResourceImporterScene, ResourceImporter); - Set<Ref<EditorSceneImporter> > importers; + Set<Ref<EditorSceneImporter>> importers; static ResourceImporterScene *singleton; @@ -124,7 +123,7 @@ class ResourceImporterScene : public ResourceImporter { public: static ResourceImporterScene *get_singleton() { return singleton; } - const Set<Ref<EditorSceneImporter> > &get_importers() const { return importers; } + const Set<Ref<EditorSceneImporter>> &get_importers() const { return importers; } void add_importer(Ref<EditorSceneImporter> p_importer) { importers.insert(p_importer); } void remove_importer(Ref<EditorSceneImporter> p_importer) { importers.erase(p_importer); } @@ -144,16 +143,16 @@ public: void _find_meshes(Node *p_node, Map<Ref<ArrayMesh>, Transform> &meshes); - void _make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_animations_as_text, bool p_keep_animations, bool p_make_materials, bool p_materials_as_text, bool p_keep_materials, bool p_make_meshes, bool p_meshes_as_text, Map<Ref<Animation>, Ref<Animation> > &p_animations, Map<Ref<Material>, Ref<Material> > &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh> > &p_meshes); + void _make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_animations_as_text, bool p_keep_animations, bool p_make_materials, bool p_materials_as_text, bool p_keep_materials, bool p_make_meshes, bool p_meshes_as_text, Map<Ref<Animation>, Ref<Animation>> &p_animations, Map<Ref<Material>, Ref<Material>> &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh>> &p_meshes); - Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape> > > &collision_map, LightBakeMode p_light_bake_mode); + Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape3D>>> &collision_map, LightBakeMode p_light_bake_mode); void _create_clips(Node *scene, const Array &p_clips, bool p_bake_all); void _filter_anim_tracks(Ref<Animation> anim, Set<String> &keep); void _filter_tracks(Node *scene, const String &p_text); void _optimize_animations(Node *scene, float p_max_lin_error, float p_max_ang_error, float p_max_angle); - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); Node *import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps); Ref<Animation> import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps); @@ -167,7 +166,7 @@ class EditorSceneImporterESCN : public EditorSceneImporter { public: virtual uint32_t get_import_flags() const; virtual void get_extensions(List<String> *r_extensions) const; - virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL); + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr); virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); }; diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp new file mode 100644 index 0000000000..a2e80dfa18 --- /dev/null +++ b/editor/import/resource_importer_shader_file.cpp @@ -0,0 +1,118 @@ +/*************************************************************************/ +/* resource_importer_shader_file.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "resource_importer_shader_file.h" + +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "editor/editor_node.h" +#include "editor/plugins/shader_file_editor_plugin.h" +#include "servers/rendering/rendering_device_binds.h" + +String ResourceImporterShaderFile::get_importer_name() const { + return "glsl"; +} + +String ResourceImporterShaderFile::get_visible_name() const { + return "GLSL Shader File"; +} + +void ResourceImporterShaderFile::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("glsl"); +} + +String ResourceImporterShaderFile::get_save_extension() const { + return "res"; +} + +String ResourceImporterShaderFile::get_resource_type() const { + return "RDShaderFile"; +} + +int ResourceImporterShaderFile::get_preset_count() const { + return 0; +} + +String ResourceImporterShaderFile::get_preset_name(int p_idx) const { + return String(); +} + +void ResourceImporterShaderFile::get_import_options(List<ImportOption> *r_options, int p_preset) const { +} + +bool ResourceImporterShaderFile::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + return true; +} + +static String _include_function(const String &p_path, void *userpointer) { + Error err; + + String *base_path = (String *)userpointer; + + String include = p_path; + if (include.is_rel_path()) { + include = base_path->plus_file(include); + } + + FileAccessRef file_inc = FileAccess::open(include, FileAccess::READ, &err); + if (err != OK) { + return String(); + } + return file_inc->get_as_utf8_string(); +} + +Error ResourceImporterShaderFile::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + /* STEP 1, Read shader code */ + + Error err; + FileAccessRef file = FileAccess::open(p_source_file, FileAccess::READ, &err); + ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN); + ERR_FAIL_COND_V(!file.operator->(), ERR_CANT_OPEN); + + String file_txt = file->get_as_utf8_string(); + Ref<RDShaderFile> shader_file; + shader_file.instance(); + String base_path = p_source_file.get_base_dir(); + err = shader_file->parse_versions_from_text(file_txt, "", _include_function, &base_path); + + if (err != OK) { + if (!ShaderFileEditor::singleton->is_visible_in_tree()) { + EditorNode::get_singleton()->add_io_error(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file)); + } + } + + ResourceSaver::save(p_save_path + ".res", shader_file); + + return OK; +} + +ResourceImporterShaderFile::ResourceImporterShaderFile() { +} diff --git a/editor/import/resource_importer_shader_file.h b/editor/import/resource_importer_shader_file.h new file mode 100644 index 0000000000..fa95ceecc1 --- /dev/null +++ b/editor/import/resource_importer_shader_file.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* resource_importer_shader_file.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RESOURCE_IMPORTER_SHADER_FILE_H +#define RESOURCE_IMPORTER_SHADER_FILE_H + +#include "core/io/resource_importer.h" + +class ResourceImporterShaderFile : public ResourceImporter { + GDCLASS(ResourceImporterShaderFile, ResourceImporter); + +public: + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); + + ResourceImporterShaderFile(); +}; + +#endif // RESOURCE_IMPORTER_SHADER_FILE_H diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 25431179b5..a13324f0fc 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,103 +32,101 @@ #include "core/io/config_file.h" #include "core/io/image_loader.h" +#include "core/version.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" -#include "scene/resources/texture.h" -void ResourceImporterTexture::_texture_reimport_srgb(const Ref<StreamTexture> &p_tex) { +void ResourceImporterTexture::_texture_reimport_roughness(const Ref<StreamTexture2D> &p_tex, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_channel) { + MutexLock lock(singleton->mutex); - singleton->mutex->lock(); StringName path = p_tex->get_path(); if (!singleton->make_flags.has(path)) { - singleton->make_flags[path] = 0; + singleton->make_flags[path] = MakeInfo(); } - singleton->make_flags[path] |= MAKE_SRGB_FLAG; - - singleton->mutex->unlock(); + singleton->make_flags[path].flags |= MAKE_ROUGHNESS_FLAG; + singleton->make_flags[path].channel_for_roughness = p_channel; + singleton->make_flags[path].normal_path_for_roughness = p_normal_path; } -void ResourceImporterTexture::_texture_reimport_3d(const Ref<StreamTexture> &p_tex) { +void ResourceImporterTexture::_texture_reimport_3d(const Ref<StreamTexture2D> &p_tex) { + MutexLock lock(singleton->mutex); - singleton->mutex->lock(); StringName path = p_tex->get_path(); if (!singleton->make_flags.has(path)) { - singleton->make_flags[path] = 0; + singleton->make_flags[path] = MakeInfo(); } - singleton->make_flags[path] |= MAKE_3D_FLAG; - - singleton->mutex->unlock(); + singleton->make_flags[path].flags |= MAKE_3D_FLAG; } -void ResourceImporterTexture::_texture_reimport_normal(const Ref<StreamTexture> &p_tex) { +void ResourceImporterTexture::_texture_reimport_normal(const Ref<StreamTexture2D> &p_tex) { + MutexLock lock(singleton->mutex); - singleton->mutex->lock(); StringName path = p_tex->get_path(); if (!singleton->make_flags.has(path)) { - singleton->make_flags[path] = 0; + singleton->make_flags[path] = MakeInfo(); } - singleton->make_flags[path] |= MAKE_NORMAL_FLAG; - - singleton->mutex->unlock(); + singleton->make_flags[path].flags |= MAKE_NORMAL_FLAG; } void ResourceImporterTexture::update_imports() { - if (EditorFileSystem::get_singleton()->is_scanning() || EditorFileSystem::get_singleton()->is_importing()) { return; // do nothing for now } - mutex->lock(); - - if (make_flags.empty()) { - mutex->unlock(); - return; - } + MutexLock lock(mutex); Vector<String> to_reimport; - for (Map<StringName, int>::Element *E = make_flags.front(); E; E = E->next()) { + { + if (make_flags.empty()) { + return; + } - Ref<ConfigFile> cf; - cf.instance(); - String src_path = String(E->key()) + ".import"; + for (Map<StringName, MakeInfo>::Element *E = make_flags.front(); E; E = E->next()) { + Ref<ConfigFile> cf; + cf.instance(); + String src_path = String(E->key()) + ".import"; - Error err = cf->load(src_path); - ERR_CONTINUE(err != OK); + Error err = cf->load(src_path); + ERR_CONTINUE(err != OK); - bool changed = false; - if (E->get() & MAKE_SRGB_FLAG && int(cf->get_value("params", "flags/srgb")) == 2) { - cf->set_value("params", "flags/srgb", 1); - changed = true; - } + bool changed = false; - if (E->get() & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) { - cf->set_value("params", "compress/normal_map", 1); - changed = true; - } + if (E->get().flags & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) { + cf->set_value("params", "compress/normal_map", 1); + changed = true; + } - if (E->get() & MAKE_3D_FLAG && bool(cf->get_value("params", "detect_3d"))) { - cf->set_value("params", "detect_3d", false); - cf->set_value("params", "compress/mode", 2); - cf->set_value("params", "flags/repeat", true); - cf->set_value("params", "flags/filter", true); - cf->set_value("params", "flags/mipmaps", true); - changed = true; - } + if (E->get().flags & MAKE_ROUGHNESS_FLAG && int(cf->get_value("params", "roughness/mode")) == 0) { + cf->set_value("params", "roughness/mode", E->get().channel_for_roughness + 2); + cf->set_value("params", "roughness/src_normal", E->get().normal_path_for_roughness); + changed = true; + } - if (changed) { - cf->save(src_path); - to_reimport.push_back(E->key()); - } - } + if (E->get().flags & MAKE_3D_FLAG && bool(cf->get_value("params", "detect_3d/compress_to"))) { + int compress_to = cf->get_value("params", "detect_3d/compress_to"); + cf->set_value("params", "detect_3d/compress_to", 0); + if (compress_to == 1) { + cf->set_value("params", "compress/mode", COMPRESS_VRAM_COMPRESSED); + } else if (compress_to == 2) { + cf->set_value("params", "compress/mode", COMPRESS_BASIS_UNIVERSAL); + } + cf->set_value("params", "mipmaps/generate", true); + changed = true; + } - make_flags.clear(); + if (changed) { + cf->save(src_path); + to_reimport.push_back(E->key()); + } + } - mutex->unlock(); + make_flags.clear(); + } if (to_reimport.size()) { EditorFileSystem::get_singleton()->reimport_files(to_reimport); @@ -136,42 +134,42 @@ void ResourceImporterTexture::update_imports() { } String ResourceImporterTexture::get_importer_name() const { - return "texture"; } String ResourceImporterTexture::get_visible_name() const { - - return "Texture"; + return "Texture2D"; } -void ResourceImporterTexture::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterTexture::get_recognized_extensions(List<String> *p_extensions) const { ImageLoader::get_recognized_extensions(p_extensions); } + String ResourceImporterTexture::get_save_extension() const { return "stex"; } String ResourceImporterTexture::get_resource_type() const { - - return "StreamTexture"; + return "StreamTexture2D"; } bool ResourceImporterTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - if (p_option == "compress/lossy_quality") { int compress_mode = int(p_options["compress/mode"]); - if (compress_mode != COMPRESS_LOSSY && compress_mode != COMPRESS_VIDEO_RAM) { + if (compress_mode != COMPRESS_LOSSY && compress_mode != COMPRESS_VRAM_COMPRESSED) { return false; } } else if (p_option == "compress/hdr_mode") { int compress_mode = int(p_options["compress/mode"]); - if (compress_mode != COMPRESS_VIDEO_RAM) { + if (compress_mode < COMPRESS_VRAM_COMPRESSED) { return false; } + } else if (p_option == "mipmaps/limit") { + return p_options["mipmaps/generate"]; + } else if (p_option == "compress/bptc_ldr") { int compress_mode = int(p_options["compress/mode"]); - if (compress_mode != COMPRESS_VIDEO_RAM) { + if (compress_mode < COMPRESS_VRAM_COMPRESSED) { return false; } if (!ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) { @@ -185,8 +183,8 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_option, cons int ResourceImporterTexture::get_preset_count() const { return 4; } -String ResourceImporterTexture::get_preset_name(int p_idx) const { +String ResourceImporterTexture::get_preset_name(int p_idx) const { static const char *preset_names[] = { "2D, Detect 3D", "2D", @@ -198,225 +196,228 @@ String ResourceImporterTexture::get_preset_name(int p_idx) const { } void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options, int p_preset) const { - - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_mode", PROPERTY_HINT_ENUM, "Enabled,Force RGBE"), 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Enabled,RGBA Only"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), p_preset == PRESET_3D ? 1 : 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), p_preset != PRESET_2D_PIXEL)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/mipmaps"), p_preset == PRESET_3D)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/anisotropic"), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/srgb", PROPERTY_HINT_ENUM, "Disable,Enable,Detect"), 2)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/streamed"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), (p_preset == PRESET_3D ? true : false))); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "roughness/mode", PROPERTY_HINT_ENUM, "Detect,Disabled,Red,Green,Blue,Alpha,Gray"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "roughness/src_normal", PROPERTY_HINT_FILE, "*.png,*.jpg"), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/HDR_as_SRGB"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/invert_color"), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "stream"), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "detect_3d"), p_preset == PRESET_DETECT)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/HDR_as_SRGB"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "detect_3d/compress_to", PROPERTY_HINT_ENUM, "Disabled,VRAM Compressed,Basis Universal"), (p_preset == PRESET_DETECT) ? 1 : 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0)); } -void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_force_po2_for_compressed) { - - FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE); - f->store_8('G'); - f->store_8('D'); - f->store_8('S'); - f->store_8('T'); //godot streamable texture - - bool resize_to_po2 = false; - - if (p_compress_mode == COMPRESS_VIDEO_RAM && p_force_po2_for_compressed && (p_mipmaps || p_texture_flags & Texture::FLAG_REPEAT)) { - resize_to_po2 = true; - f->store_16(next_power_of_2(p_image->get_width())); - f->store_16(p_image->get_width()); - f->store_16(next_power_of_2(p_image->get_height())); - f->store_16(p_image->get_height()); - } else { - f->store_16(p_image->get_width()); - f->store_16(0); - f->store_16(p_image->get_height()); - f->store_16(0); - } - f->store_32(p_texture_flags); - - uint32_t format = 0; - - if (p_streamable) - format |= StreamTexture::FORMAT_BIT_STREAM; - if (p_mipmaps) - format |= StreamTexture::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit - if (p_detect_3d) - format |= StreamTexture::FORMAT_BIT_DETECT_3D; - if (p_detect_srgb) - format |= StreamTexture::FORMAT_BIT_DETECT_SRGB; - if (p_detect_normal) - format |= StreamTexture::FORMAT_BIT_DETECT_NORMAL; - - if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() > Image::FORMAT_RGBA8) { - p_compress_mode = COMPRESS_UNCOMPRESSED; //these can't go as lossy - } - +void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality) { switch (p_compress_mode) { case COMPRESS_LOSSLESS: { + f->store_32(StreamTexture2D::DATA_FORMAT_LOSSLESS); + f->store_16(p_image->get_width()); + f->store_16(p_image->get_height()); + f->store_32(p_image->get_mipmap_count()); + f->store_32(p_image->get_format()); + + for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { + Vector<uint8_t> data = Image::lossless_packer(p_image->get_image_from_mipmap(i)); + int data_len = data.size(); + f->store_32(data_len); - Ref<Image> image = p_image->duplicate(); - if (p_mipmaps) { - image->generate_mipmaps(); - } else { - image->clear_mipmaps(); + const uint8_t *r = data.ptr(); + f->store_buffer(r, data_len); } - int mmc = image->get_mipmap_count() + 1; - - format |= StreamTexture::FORMAT_BIT_LOSSLESS; - f->store_32(format); - f->store_32(mmc); - - for (int i = 0; i < mmc; i++) { - - if (i > 0) { - image->shrink_x2(); - } - - PoolVector<uint8_t> data = Image::lossless_packer(image); + } break; + case COMPRESS_LOSSY: { + f->store_32(StreamTexture2D::DATA_FORMAT_LOSSY); + f->store_16(p_image->get_width()); + f->store_16(p_image->get_height()); + f->store_32(p_image->get_mipmap_count()); + f->store_32(p_image->get_format()); + + for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { + Vector<uint8_t> data = Image::lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); int data_len = data.size(); f->store_32(data_len); - PoolVector<uint8_t>::Read r = data.read(); - f->store_buffer(r.ptr(), data_len); + const uint8_t *r = data.ptr(); + f->store_buffer(r, data_len); } - } break; - case COMPRESS_LOSSY: { + case COMPRESS_VRAM_COMPRESSED: { Ref<Image> image = p_image->duplicate(); - if (p_mipmaps) { - image->generate_mipmaps(); - } else { - image->clear_mipmaps(); - } - int mmc = image->get_mipmap_count() + 1; + image->compress_from_channels(p_compress_format, p_channels, p_lossy_quality); - format |= StreamTexture::FORMAT_BIT_LOSSY; - f->store_32(format); - f->store_32(mmc); + f->store_32(StreamTexture2D::DATA_FORMAT_IMAGE); + f->store_16(image->get_width()); + f->store_16(image->get_height()); + f->store_32(image->get_mipmap_count()); + f->store_32(image->get_format()); - for (int i = 0; i < mmc; i++) { + Vector<uint8_t> data = image->get_data(); + int dl = data.size(); + const uint8_t *r = data.ptr(); + f->store_buffer(r, dl); + } break; + case COMPRESS_VRAM_UNCOMPRESSED: { + f->store_32(StreamTexture2D::DATA_FORMAT_IMAGE); + f->store_16(p_image->get_width()); + f->store_16(p_image->get_height()); + f->store_32(p_image->get_mipmap_count()); + f->store_32(p_image->get_format()); + + Vector<uint8_t> data = p_image->get_data(); + int dl = data.size(); + const uint8_t *r = data.ptr(); - if (i > 0) { - image->shrink_x2(); - } + f->store_buffer(r, dl); - PoolVector<uint8_t> data = Image::lossy_packer(image, p_lossy_quality); + } break; + case COMPRESS_BASIS_UNIVERSAL: { + f->store_32(StreamTexture2D::DATA_FORMAT_BASIS_UNIVERSAL); + f->store_16(p_image->get_width()); + f->store_16(p_image->get_height()); + f->store_32(p_image->get_mipmap_count()); + f->store_32(p_image->get_format()); + + for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { + Vector<uint8_t> data = Image::basis_universal_packer(p_image->get_image_from_mipmap(i), p_channels); int data_len = data.size(); f->store_32(data_len); - PoolVector<uint8_t>::Read r = data.read(); - f->store_buffer(r.ptr(), data_len); + const uint8_t *r = data.ptr(); + f->store_buffer(r, data_len); } } break; - case COMPRESS_VIDEO_RAM: { + } +} - Ref<Image> image = p_image->duplicate(); - if (resize_to_po2) { - image->resize_to_po2(); - } - if (p_mipmaps) { - image->generate_mipmaps(p_force_normal); - } +void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel) { + FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE); + f->store_8('G'); + f->store_8('S'); + f->store_8('T'); + f->store_8('2'); //godot streamable texture 2D + + //format version + f->store_32(StreamTexture2D::FORMAT_VERSION); + //texture may be resized later, so original size must be saved first + f->store_32(p_image->get_width()); + f->store_32(p_image->get_height()); + + uint32_t flags = 0; + if (p_streamable) { + flags |= StreamTexture2D::FORMAT_BIT_STREAM; + } + if (p_mipmaps) { + flags |= StreamTexture2D::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit + } + if (p_detect_3d) { + flags |= StreamTexture2D::FORMAT_BIT_DETECT_3D; + } + if (p_detect_roughness) { + flags |= StreamTexture2D::FORMAT_BIT_DETECT_ROUGNESS; + } + if (p_detect_normal) { + flags |= StreamTexture2D::FORMAT_BIT_DETECT_NORMAL; + } - if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) { - image->convert(Image::FORMAT_RGBE9995); - } else { - Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC; - if (p_force_normal) { - csource = Image::COMPRESS_SOURCE_NORMAL; - } else if (p_texture_flags & VS::TEXTURE_FLAG_CONVERT_TO_LINEAR) { - csource = Image::COMPRESS_SOURCE_SRGB; - } + f->store_32(flags); + f->store_32(p_limit_mipmap); + //reserved for future use + f->store_32(0); + f->store_32(0); + f->store_32(0); + + /* + print_line("streamable " + itos(p_streamable)); + print_line("mipmaps " + itos(p_mipmaps)); + print_line("detect_3d " + itos(p_detect_3d)); + print_line("roughness " + itos(p_detect_roughness)); + print_line("normal " + itos(p_detect_normal)); +*/ - image->compress(p_vram_compression, csource, p_lossy_quality); - } + if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() > Image::FORMAT_RGBA8) { + p_compress_mode = COMPRESS_VRAM_UNCOMPRESSED; //these can't go as lossy + } - format |= image->get_format(); + Ref<Image> image = p_image->duplicate(); - f->store_32(format); + if (((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED && p_force_po2_for_compressed)) && p_mipmaps) { + image->resize_to_po2(); + } - PoolVector<uint8_t> data = image->get_data(); - int dl = data.size(); - PoolVector<uint8_t>::Read r = data.read(); - f->store_buffer(r.ptr(), dl); - } break; - case COMPRESS_UNCOMPRESSED: { + if (p_mipmaps && (!image->has_mipmaps() || p_force_normal)) { + image->generate_mipmaps(p_force_normal); + } - Ref<Image> image = p_image->duplicate(); - if (p_mipmaps) { - image->generate_mipmaps(); - } else { - image->clear_mipmaps(); - } + if (!p_mipmaps) { + image->clear_mipmaps(); + } - format |= image->get_format(); - f->store_32(format); + if (image->has_mipmaps() && p_normal.is_valid()) { + image->generate_mipmap_roughness(p_roughness_channel, p_normal); + } - PoolVector<uint8_t> data = image->get_data(); - int dl = data.size(); - PoolVector<uint8_t>::Read r = data.read(); + Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC; + if (p_force_normal) { + csource = Image::COMPRESS_SOURCE_NORMAL; + } else if (p_srgb_friendly) { + csource = Image::COMPRESS_SOURCE_SRGB; + } - f->store_buffer(r.ptr(), dl); + Image::UsedChannels used_channels = image->detect_used_channels(csource); - } break; - } + save_to_stex_format(f, image, p_compress_mode, used_channels, p_vram_compression, p_lossy_quality); memdelete(f); } Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - - int compress_mode = p_options["compress/mode"]; + CompressMode compress_mode = CompressMode(int(p_options["compress/mode"])); float lossy = p_options["compress/lossy_quality"]; - int repeat = p_options["flags/repeat"]; - bool filter = p_options["flags/filter"]; - bool mipmaps = p_options["flags/mipmaps"]; - bool anisotropic = p_options["flags/anisotropic"]; - int srgb = p_options["flags/srgb"]; + int pack_channels = p_options["compress/channel_pack"]; + bool mipmaps = p_options["mipmaps/generate"]; + uint32_t mipmap_limit = int(mipmaps ? int(p_options["mipmaps/limit"]) : int(-1)); bool fix_alpha_border = p_options["process/fix_alpha_border"]; bool premult_alpha = p_options["process/premult_alpha"]; bool invert_color = p_options["process/invert_color"]; - bool stream = p_options["stream"]; - int size_limit = p_options["size_limit"]; + bool stream = p_options["compress/streamed"]; + int size_limit = p_options["process/size_limit"]; bool hdr_as_srgb = p_options["process/HDR_as_SRGB"]; int normal = p_options["compress/normal_map"]; float scale = p_options["svg/scale"]; - bool force_rgbe = p_options["compress/hdr_mode"]; + int hdr_compression = p_options["compress/hdr_compression"]; int bptc_ldr = p_options["compress/bptc_ldr"]; + int roughness = p_options["roughness/mode"]; + String normal_map = p_options["roughness/src_normal"]; + Ref<Image> normal_image; + Image::RoughnessChannel roughness_channel = Image::ROUGHNESS_CHANNEL_R; + + if (mipmaps && roughness > 1 && FileAccess::exists(normal_map)) { + normal_image.instance(); + if (ImageLoader::load_image(normal_map, normal_image) == OK) { + roughness_channel = Image::RoughnessChannel(roughness - 2); + } + } Ref<Image> image; image.instance(); - Error err = ImageLoader::load_image(p_source_file, image, NULL, hdr_as_srgb, scale); - if (err != OK) + Error err = ImageLoader::load_image(p_source_file, image, nullptr, hdr_as_srgb, scale); + if (err != OK) { return err; + } Array formats_imported; - int tex_flags = 0; - if (repeat > 0) - tex_flags |= Texture::FLAG_REPEAT; - if (repeat == 2) - tex_flags |= Texture::FLAG_MIRRORED_REPEAT; - if (filter) - tex_flags |= Texture::FLAG_FILTER; - if (mipmaps || compress_mode == COMPRESS_VIDEO_RAM) - tex_flags |= Texture::FLAG_MIPMAPS; - if (anisotropic) - tex_flags |= Texture::FLAG_ANISOTROPIC_FILTER; - if (srgb == 1) - tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR; - if (size_limit > 0 && (image->get_width() > size_limit || image->get_height() > size_limit)) { //limit size if (image->get_width() >= image->get_height()) { @@ -425,7 +426,6 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); } else { - int new_height = size_limit; int new_width = image->get_width() * new_height / image->get_height(); @@ -449,76 +449,96 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String int height = image->get_height(); int width = image->get_width(); - image->lock(); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { image->set_pixel(i, j, image->get_pixel(i, j).inverted()); } } - image->unlock(); } - bool detect_3d = p_options["detect_3d"]; - bool detect_srgb = srgb == 2; + if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) { + //basis universal does not support float formats, fall back + compress_mode = COMPRESS_VRAM_COMPRESSED; + } + + bool detect_3d = int(p_options["detect_3d/compress_to"]) > 0; + bool detect_roughness = roughness == 0; bool detect_normal = normal == 0; bool force_normal = normal == 1; + bool srgb_friendly_pack = pack_channels == 0; - if (compress_mode == COMPRESS_VIDEO_RAM) { + if (compress_mode == COMPRESS_VRAM_COMPRESSED) { //must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). //Android, GLES 2.x bool ok_on_pc = false; bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); - bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGBA5551); + bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565); bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc"); bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc"); if (can_bptc) { - Image::DetectChannels channels = image->get_detected_channels(); - if (is_hdr) { + //add to the list anyway + formats_imported.push_back("bptc"); + } - if (channels == Image::DETECTED_LA || channels == Image::DETECTED_RGBA) { - can_bptc = false; + bool can_compress_hdr = hdr_compression > 0; + bool has_alpha = image->detect_alpha() != Image::ALPHA_NONE; + + if (is_hdr && can_compress_hdr) { + if (has_alpha) { + //can compress hdr, but hdr with alpha is not compressible + if (hdr_compression == 2) { + //but user selected to compress hdr anyway, so force an alpha-less format. + if (image->get_format() == Image::FORMAT_RGBAF) { + image->convert(Image::FORMAT_RGBF); + } else if (image->get_format() == Image::FORMAT_RGBAH) { + image->convert(Image::FORMAT_RGBH); + } + } else { + can_compress_hdr = false; } - } else if (is_ldr) { + } - //handle "RGBA Only" setting - if (bptc_ldr == 1 && channels != Image::DETECTED_LA && channels != Image::DETECTED_RGBA) { - can_bptc = false; + if (can_compress_hdr) { + if (!can_bptc) { + //fallback to RGBE99995 + if (image->get_format() != Image::FORMAT_RGBE9995) { + image->convert(Image::FORMAT_RGBE9995); + } } + } else { + can_bptc = false; } - - formats_imported.push_back("bptc"); } - if (!can_bptc && is_hdr && !force_rgbe) { - //convert to ldr if this can't be stored hdr - image->convert(Image::FORMAT_RGBA8); + if (is_ldr && can_bptc) { + if (bptc_ldr == 0 || (bptc_ldr == 1 && !has_alpha)) { + can_bptc = false; + } } if (can_bptc || can_s3tc) { - _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, false); + _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("s3tc"); formats_imported.push_back("s3tc"); ok_on_pc = true; } if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { - - _save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, true); + _save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("etc2"); formats_imported.push_back("etc2"); } if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { - _save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, true); + _save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("etc"); formats_imported.push_back("etc"); } if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { - - _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, true); + _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("pvrtc"); formats_imported.push_back("pvrtc"); } @@ -528,12 +548,12 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String } } else { //import normally - _save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal, false); + _save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); } if (r_metadata) { Dictionary metadata; - metadata["vram_texture"] = compress_mode == COMPRESS_VIDEO_RAM; + metadata["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED; if (formats_imported.size()) { metadata["imported_formats"] = formats_imported; } @@ -548,10 +568,9 @@ const char *ResourceImporterTexture::compression_formats[] = { "etc", "etc2", "pvrtc", - NULL + nullptr }; String ResourceImporterTexture::get_import_settings_string() const { - String s; int index = 0; @@ -568,7 +587,6 @@ String ResourceImporterTexture::get_import_settings_string() const { } bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) const { - //will become invalid if formats are missing to import Dictionary metadata = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path); @@ -603,18 +621,14 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co return valid; } -ResourceImporterTexture *ResourceImporterTexture::singleton = NULL; +ResourceImporterTexture *ResourceImporterTexture::singleton = nullptr; ResourceImporterTexture::ResourceImporterTexture() { - singleton = this; - StreamTexture::request_3d_callback = _texture_reimport_3d; - StreamTexture::request_srgb_callback = _texture_reimport_srgb; - StreamTexture::request_normal_callback = _texture_reimport_normal; - mutex = Mutex::create(); + StreamTexture2D::request_3d_callback = _texture_reimport_3d; + StreamTexture2D::request_roughness_callback = _texture_reimport_roughness; + StreamTexture2D::request_normal_callback = _texture_reimport_normal; } ResourceImporterTexture::~ResourceImporterTexture() { - - memdelete(mutex); } diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index da712bf84d..b770d240eb 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,30 +33,56 @@ #include "core/image.h" #include "core/io/resource_importer.h" +#include "core/os/file_access.h" +#include "scene/resources/texture.h" +#include "servers/rendering_server.h" -class StreamTexture; +class StreamTexture2D; class ResourceImporterTexture : public ResourceImporter { GDCLASS(ResourceImporterTexture, ResourceImporter); +public: + enum CompressMode { + COMPRESS_LOSSLESS, + COMPRESS_LOSSY, + COMPRESS_VRAM_COMPRESSED, + COMPRESS_VRAM_UNCOMPRESSED, + COMPRESS_BASIS_UNIVERSAL + }; + protected: enum { MAKE_3D_FLAG = 1, - MAKE_SRGB_FLAG = 2, + MAKE_ROUGHNESS_FLAG = 2, MAKE_NORMAL_FLAG = 4 }; - Mutex *mutex; - Map<StringName, int> make_flags; + Mutex mutex; + struct MakeInfo { + int flags; + String normal_path_for_roughness; + RS::TextureDetectRoughnessChannel channel_for_roughness; + MakeInfo() { + flags = 0; + channel_for_roughness = RS::TEXTURE_DETECT_ROUGNHESS_R; + } + }; + + Map<StringName, MakeInfo> make_flags; - static void _texture_reimport_srgb(const Ref<StreamTexture> &p_tex); - static void _texture_reimport_3d(const Ref<StreamTexture> &p_tex); - static void _texture_reimport_normal(const Ref<StreamTexture> &p_tex); + static void _texture_reimport_roughness(const Ref<StreamTexture2D> &p_tex, const String &p_normal_path, RenderingServer::TextureDetectRoughnessChannel p_channel); + static void _texture_reimport_3d(const Ref<StreamTexture2D> &p_tex); + static void _texture_reimport_normal(const Ref<StreamTexture2D> &p_tex); static ResourceImporterTexture *singleton; static const char *compression_formats[]; + void _save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel); + public: + static void save_to_stex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality); + static ResourceImporterTexture *get_singleton() { return singleton; } virtual String get_importer_name() const; virtual String get_visible_name() const; @@ -71,22 +97,13 @@ public: PRESET_3D, }; - enum CompressMode { - COMPRESS_LOSSLESS, - COMPRESS_LOSSY, - COMPRESS_VIDEO_RAM, - COMPRESS_UNCOMPRESSED - }; - virtual int get_preset_count() const; virtual String get_preset_name(int p_idx) const; virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - void _save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_force_po2_for_compressed); - - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); void update_imports(); diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index 51a6cc6757..0818655c4c 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,16 +39,14 @@ #include "scene/resources/texture.h" String ResourceImporterTextureAtlas::get_importer_name() const { - return "texture_atlas"; } String ResourceImporterTextureAtlas::get_visible_name() const { - return "TextureAtlas"; } -void ResourceImporterTextureAtlas::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterTextureAtlas::get_recognized_extensions(List<String> *p_extensions) const { ImageLoader::get_recognized_extensions(p_extensions); } @@ -57,25 +55,22 @@ String ResourceImporterTextureAtlas::get_save_extension() const { } String ResourceImporterTextureAtlas::get_resource_type() const { - - return "Texture"; + return "Texture2D"; } bool ResourceImporterTextureAtlas::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - return true; } int ResourceImporterTextureAtlas::get_preset_count() const { return 0; } -String ResourceImporterTextureAtlas::get_preset_name(int p_idx) const { +String ResourceImporterTextureAtlas::get_preset_name(int p_idx) const { return String(); } void ResourceImporterTextureAtlas::get_import_options(List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "atlas_file", PROPERTY_HINT_SAVE_FILE, "*.png"), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_mode", PROPERTY_HINT_ENUM, "Region,Mesh2D"), 0)); } @@ -85,7 +80,6 @@ String ResourceImporterTextureAtlas::get_option_group_file() const { } Error ResourceImporterTextureAtlas::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - /* If this happens, it's because the atlas_file field was not filled, so just import a broken texture */ //use an xpm because it's size independent, the editor images are vector and size dependent @@ -103,7 +97,6 @@ Error ResourceImporterTextureAtlas::import(const String &p_source_file, const St } static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_transposed, Ref<Image> p_image, const Ref<Image> &p_src_image) { - int width = p_image->get_width(); int height = p_image->get_height(); int src_width = p_src_image->get_width(); @@ -113,7 +106,6 @@ static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_tr int y[3]; for (int j = 0; j < 3; j++) { - x[j] = vertices[j].x; y[j] = vertices[j].y; } @@ -140,11 +132,10 @@ static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_tr for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) { if (yi >= 0) { for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) { - int px = xi, py = yi; int sx = px, sy = py; - sx = CLAMP(sx, 0, src_width); - sy = CLAMP(sy, 0, src_height); + sx = CLAMP(sx, 0, src_width - 1); + sy = CLAMP(sy, 0, src_height - 1); Color color = p_src_image->get_pixel(sx, sy); if (p_transposed) { SWAP(px, py); @@ -165,8 +156,8 @@ static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_tr for (int xi = (xf < width ? int(xf) : width - 1); xi >= (xt > 0 ? xt : 0); xi--) { int px = xi, py = yi; int sx = px, sy = py; - sx = CLAMP(sx, 0, src_width); - sy = CLAMP(sy, 0, src_height); + sx = CLAMP(sx, 0, src_width - 1); + sy = CLAMP(sy, 0, src_height - 1); Color color = p_src_image->get_pixel(sx, sy); if (p_transposed) { SWAP(px, py); @@ -185,15 +176,15 @@ static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_tr } } xf += dx_far; - if (yi < y[1]) + if (yi < y[1]) { xt += dx_upper; - else + } else { xt += dx_low; + } } } -Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths) { - +Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant>> &p_source_file_options, const Map<String, String> &p_base_paths) { ERR_FAIL_COND_V(p_source_file_options.size() == 0, ERR_BUG); //should never happen Vector<EditorAtlasPacker::Chart> charts; @@ -202,8 +193,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file pack_data_files.resize(p_source_file_options.size()); int idx = 0; - for (const Map<String, Map<StringName, Variant> >::Element *E = p_source_file_options.front(); E; E = E->next(), idx++) { - + for (const Map<String, Map<StringName, Variant>>::Element *E = p_source_file_options.front(); E; E = E->next(), idx++) { PackData &pack_data = pack_data_files.write[idx]; const String &source = E->key(); const Map<StringName, Variant> &options = E->get(); @@ -218,7 +208,6 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file int mode = options["import_mode"]; if (mode == IMPORT_MODE_REGION) { - pack_data.is_mesh = false; EditorAtlasPacker::Chart chart; @@ -251,17 +240,15 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file Ref<BitMap> bit_map; bit_map.instance(); bit_map->create_from_image_alpha(image); - Vector<Vector<Vector2> > polygons = bit_map->clip_opaque_to_polygons(Rect2(0, 0, image->get_width(), image->get_height())); + Vector<Vector<Vector2>> polygons = bit_map->clip_opaque_to_polygons(Rect2(0, 0, image->get_width(), image->get_height())); for (int j = 0; j < polygons.size(); j++) { - EditorAtlasPacker::Chart chart; chart.vertices = polygons[j]; chart.can_transpose = true; Vector<int> poly = Geometry::triangulate_polygon(polygons[j]); for (int i = 0; i < poly.size(); i += 3) { - EditorAtlasPacker::Chart::Face f; f.vertex[0] = poly[i + 0]; f.vertex[1] = poly[i + 1]; @@ -286,12 +273,9 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file new_atlas.instance(); new_atlas->create(atlas_width, atlas_height, false, Image::FORMAT_RGBA8); - new_atlas->lock(); - for (int i = 0; i < pack_data_files.size(); i++) { - PackData &pack_data = pack_data_files.write[i]; - pack_data.image->lock(); + for (int j = 0; j < pack_data.chart_pieces.size(); j++) { const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[j]]; for (int k = 0; k < chart.faces.size(); k++) { @@ -304,16 +288,14 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file _plot_triangle(positions, chart.final_offset, chart.transposed, new_atlas, pack_data.image); } } - pack_data.image->unlock(); } - new_atlas->unlock(); //save the atlas new_atlas->save_png(p_group_file); //update cache if existing, else create - Ref<Texture> cache; + Ref<Texture2D> cache; if (ResourceCache::has(p_group_file)) { Resource *resptr = ResourceCache::get(p_group_file); cache.reference_ptr(resptr); @@ -327,11 +309,10 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file //save the images idx = 0; - for (const Map<String, Map<StringName, Variant> >::Element *E = p_source_file_options.front(); E; E = E->next(), idx++) { - + for (const Map<String, Map<StringName, Variant>>::Element *E = p_source_file_options.front(); E; E = E->next(), idx++) { PackData &pack_data = pack_data_files.write[idx]; - Ref<Texture> texture; + Ref<Texture2D> texture; if (!pack_data.is_mesh) { Vector2 offset = charts[pack_data.chart_pieces[0]].vertices[0] + charts[pack_data.chart_pieces[0]].final_offset; @@ -350,9 +331,9 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file for (int i = 0; i < pack_data.chart_pieces.size(); i++) { const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[i]]; - PoolVector<Vector2> vertices; - PoolVector<int> indices; - PoolVector<Vector2> uvs; + Vector<Vector2> vertices; + Vector<int> indices; + Vector<Vector2> uvs; int vc = chart.vertices.size(); int fc = chart.faces.size(); vertices.resize(vc); @@ -360,9 +341,9 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file indices.resize(fc * 3); { - PoolVector<Vector2>::Write vw = vertices.write(); - PoolVector<int>::Write iw = indices.write(); - PoolVector<Vector2>::Write uvw = uvs.write(); + Vector2 *vw = vertices.ptrw(); + int *iw = indices.ptrw(); + Vector2 *uvw = uvs.ptrw(); for (int j = 0; j < vc; j++) { vw[j] = chart.vertices[j]; diff --git a/editor/import/resource_importer_texture_atlas.h b/editor/import/resource_importer_texture_atlas.h index 3c6fc343c4..c61fa5c040 100644 --- a/editor/import/resource_importer_texture_atlas.h +++ b/editor/import/resource_importer_texture_atlas.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,7 @@ class ResourceImporterTextureAtlas : public ResourceImporter { Rect2 region; bool is_mesh; Vector<int> chart_pieces; //one for region, many for mesh - Vector<Vector<Vector2> > chart_vertices; //for mesh + Vector<Vector<Vector2>> chart_vertices; //for mesh Ref<Image> image; }; @@ -63,8 +63,8 @@ public: virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; virtual String get_option_group_file() const; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); - virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); + virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant>> &p_source_file_options, const Map<String, String> &p_base_paths); ResourceImporterTextureAtlas(); }; diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 74586a4100..cb669b4c89 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,29 +39,26 @@ const float TRIM_DB_LIMIT = -50; const int TRIM_FADE_OUT_FRAMES = 500; String ResourceImporterWAV::get_importer_name() const { - return "wav"; } String ResourceImporterWAV::get_visible_name() const { - return "Microsoft WAV"; } -void ResourceImporterWAV::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceImporterWAV::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("wav"); } + String ResourceImporterWAV::get_save_extension() const { return "sample"; } String ResourceImporterWAV::get_resource_type() const { - return "AudioStreamSample"; } bool ResourceImporterWAV::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - if (p_option == "force/max_rate_hz" && !bool(p_options["force/max_rate"])) { return false; } @@ -72,17 +69,16 @@ bool ResourceImporterWAV::get_option_visibility(const String &p_option, const Ma int ResourceImporterWAV::get_preset_count() const { return 0; } -String ResourceImporterWAV::get_preset_name(int p_idx) const { +String ResourceImporterWAV::get_preset_name(int p_idx) const { return String(); } void ResourceImporterWAV::get_import_options(List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/8_bit"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/mono"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/max_rate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "force/max_rate_hz", PROPERTY_HINT_EXP_RANGE, "11025,192000,1"), 44100)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "force/max_rate_hz", PROPERTY_HINT_EXP_RANGE, "11025,192000,1"), 44100)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "edit/trim"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "edit/normalize"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "edit/loop"), false)); @@ -90,7 +86,6 @@ void ResourceImporterWAV::get_import_options(List<ImportOption> *r_options, int } Error ResourceImporterWAV::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - /* STEP 1, READ WAVE FILE */ Error err; @@ -104,7 +99,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s file->get_buffer((uint8_t *)&riff, 4); //RIFF if (riff[0] != 'R' || riff[1] != 'I' || riff[2] != 'F' || riff[3] != 'F') { - file->close(); memdelete(file); ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); @@ -120,7 +114,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s file->get_buffer((uint8_t *)&wave, 4); //RIFF if (wave[0] != 'W' || wave[1] != 'A' || wave[2] != 'V' || wave[3] != 'E') { - file->close(); memdelete(file); ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Not a WAV file (no WAVE RIFF header)."); @@ -141,7 +134,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s Vector<float> data; while (!file->eof_reached()) { - /* chunk */ char chunkID[4]; file->get_buffer((uint8_t *)&chunkID, 4); //RIFF @@ -151,7 +143,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s uint32_t file_pos = file->get_position(); //save file pos, so we can skip to next chunk safely if (file->eof_reached()) { - //ERR_PRINT("EOF REACH"); break; } @@ -242,7 +233,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s uint32_t s = 0; for (int b = 0; b < (format_bits >> 3); b++) { - s |= ((uint32_t)file->get_8()) << (b * 8); } s <<= (32 - format_bits); @@ -270,8 +260,9 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s * 22:38 06.07.2017 GMT **/ - for (int i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) { file->get_32(); // i wish to know why should i do this... no doc! + } // only read 0x00 (loop forward), 0x01 (loop ping-pong) and 0x02 (loop backward) // Skip anything else because it's not supported, reserved for future uses or sampler specific @@ -322,12 +313,10 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s Vector<float> new_data; new_data.resize(new_data_frames * format_channels); for (int c = 0; c < format_channels; c++) { - float frac = .0f; int ipos = 0; for (int i = 0; i < new_data_frames; i++) { - //simple cubic interpolation should be enough. float mu = frac; @@ -370,20 +359,17 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s bool normalize = p_options["edit/normalize"]; if (normalize) { - float max = 0; for (int i = 0; i < data.size(); i++) { - float amp = Math::abs(data[i]); - if (amp > max) + if (amp > max) { max = amp; + } } if (max > 0) { - float mult = 1.0 / max; for (int i = 0; i < data.size(); i++) { - data.write[i] *= mult; } } @@ -392,7 +378,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s bool trim = p_options["edit/trim"]; if (trim && !loop && format_channels > 0) { - int first = 0; int last = (frames / format_channels) - 1; bool found = false; @@ -420,7 +405,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s Vector<float> new_data; new_data.resize((last - first) * format_channels); for (int i = first; i < last; i++) { - float fadeOutMult = 1; if (last - i < TRIM_FADE_OUT_FRAMES) { @@ -440,7 +424,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s bool make_loop = p_options["edit/loop"]; if (make_loop && !loop) { - loop = AudioStreamSample::LOOP_FORWARD; loop_begin = 0; loop_end = frames; @@ -450,7 +433,6 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s bool force_mono = p_options["force/mono"]; if (force_mono && format_channels == 2) { - Vector<float> new_data; new_data.resize(data.size() / 2); for (int i = 0; i < frames; i++) { @@ -463,20 +445,17 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s bool force_8_bit = p_options["force/8_bit"]; if (force_8_bit) { - is16 = false; } - PoolVector<uint8_t> dst_data; + Vector<uint8_t> dst_data; AudioStreamSample::Format dst_format; if (compression == 1) { - dst_format = AudioStreamSample::FORMAT_IMA_ADPCM; if (format_channels == 1) { _compress_ima_adpcm(data, dst_data); } else { - //byte interleave Vector<float> left; Vector<float> right; @@ -490,8 +469,8 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s right.write[i] = data[i * 2 + 1]; } - PoolVector<uint8_t> bleft; - PoolVector<uint8_t> bright; + Vector<uint8_t> bleft; + Vector<uint8_t> bright; _compress_ima_adpcm(left, bleft); _compress_ima_adpcm(right, bright); @@ -499,9 +478,9 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s int dl = bleft.size(); dst_data.resize(dl * 2); - PoolVector<uint8_t>::Write w = dst_data.write(); - PoolVector<uint8_t>::Read rl = bleft.read(); - PoolVector<uint8_t>::Read rr = bright.read(); + uint8_t *w = dst_data.ptrw(); + const uint8_t *rl = bleft.ptr(); + const uint8_t *rr = bright.ptr(); for (int i = 0; i < dl; i++) { w[i * 2 + 0] = rl[i]; @@ -510,15 +489,13 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s } } else { - dst_format = is16 ? AudioStreamSample::FORMAT_16_BITS : AudioStreamSample::FORMAT_8_BITS; dst_data.resize(data.size() * (is16 ? 2 : 1)); { - PoolVector<uint8_t>::Write w = dst_data.write(); + uint8_t *w = dst_data.ptrw(); int ds = data.size(); for (int i = 0; i < ds; i++) { - if (is16) { int16_t v = CLAMP(data[i] * 32768, -32768, 32767); encode_uint16(v, &w[i * 2]); diff --git a/editor/import/resource_importer_wav.h b/editor/import/resource_importer_wav.h index 24481ea46b..3ff3aea9f4 100644 --- a/editor/import/resource_importer_wav.h +++ b/editor/import/resource_importer_wav.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,7 +49,7 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - static void _compress_ima_adpcm(const Vector<float> &p_data, PoolVector<uint8_t> &dst_data) { + static void _compress_ima_adpcm(const Vector<float> &p_data, Vector<uint8_t> &dst_data) { /*p_sample_data->data = (void*)malloc(len); xm_s8 *dataptr=(xm_s8*)p_sample_data->data;*/ @@ -72,14 +72,15 @@ public: int datalen = p_data.size(); int datamax = datalen; - if (datalen & 1) + if (datalen & 1) { datalen++; + } dst_data.resize(datalen / 2 + 4); - PoolVector<uint8_t>::Write w = dst_data.write(); + uint8_t *w = dst_data.ptrw(); int i, step_idx = 0, prev = 0; - uint8_t *out = w.ptr(); + uint8_t *out = w; //int16_t xm_prev=0; const float *in = p_data.ptr(); @@ -96,10 +97,9 @@ public: uint8_t nibble; int16_t xm_sample; - if (i >= datamax) + if (i >= datamax) { xm_sample = 0; - else { - + } else { xm_sample = CLAMP(in[i] * 32767.0, -32768, 32767); /* if (xm_sample==32767 || xm_sample==-32768) @@ -121,9 +121,7 @@ public: } mask = 4; while (mask) { - if (diff >= step) { - nibble |= mask; diff -= step; vpdiff += step; @@ -133,10 +131,11 @@ public: mask >>= 1; }; - if (nibble & 8) + if (nibble & 8) { prev -= vpdiff; - else + } else { prev += vpdiff; + } if (prev > 32767) { //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev); @@ -147,10 +146,11 @@ public: } step_idx += _ima_adpcm_index_table[nibble]; - if (step_idx < 0) + if (step_idx < 0) { step_idx = 0; - else if (step_idx > 88) + } else if (step_idx > 88) { step_idx = 88; + } if (i & 1) { *out |= nibble << 4; @@ -162,7 +162,7 @@ public: } } - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr); ResourceImporterWAV(); }; |