diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml index b90abd105db3..49efaa1564fe 100644 --- a/modules/gltf/doc_classes/GLTFCamera.xml +++ b/modules/gltf/doc_classes/GLTFCamera.xml @@ -10,6 +10,34 @@ https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md + + + + + + Creates a new GLTFCamera instance by parsing the given [Dictionary]. + + + + + + + Create a new GLTFCamera instance from the given Godot [Camera3D] node. + + + + + + Serializes this GLTFCamera instance into a [Dictionary]. + + + + + + Converts this GLTFCamera instance into a Godot [Camera3D] node. + + + The distance to the far culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]zfar[/code] property. diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 4f9eaca2a929..1537ee614688 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -4556,27 +4556,7 @@ Error GLTFDocument::_serialize_cameras(Ref state) { Array cameras; cameras.resize(state->cameras.size()); for (GLTFCameraIndex i = 0; i < state->cameras.size(); i++) { - Dictionary d; - - Ref camera = state->cameras[i]; - - if (camera->get_perspective()) { - Dictionary persp; - persp["yfov"] = camera->get_fov(); - persp["zfar"] = camera->get_depth_far(); - persp["znear"] = camera->get_depth_near(); - d["perspective"] = persp; - d["type"] = "perspective"; - } else { - Dictionary ortho; - ortho["ymag"] = camera->get_size_mag(); - ortho["xmag"] = camera->get_size_mag(); - ortho["zfar"] = camera->get_depth_far(); - ortho["znear"] = camera->get_depth_near(); - d["orthographic"] = ortho; - d["type"] = "orthographic"; - } - cameras[i] = d; + cameras[i] = state->cameras[i]->to_dictionary(); } if (!state->cameras.size()) { @@ -4626,35 +4606,7 @@ Error GLTFDocument::_parse_cameras(Ref state) { const Array cameras = state->json["cameras"]; for (GLTFCameraIndex i = 0; i < cameras.size(); i++) { - const Dictionary &d = cameras[i]; - - Ref camera; - camera.instantiate(); - ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); - const String &type = d["type"]; - if (type == "perspective") { - camera->set_perspective(true); - if (d.has("perspective")) { - const Dictionary &persp = d["perspective"]; - camera->set_fov(persp["yfov"]); - if (persp.has("zfar")) { - camera->set_depth_far(persp["zfar"]); - } - camera->set_depth_near(persp["znear"]); - } - } else if (type == "orthographic") { - camera->set_perspective(false); - if (d.has("orthographic")) { - const Dictionary &ortho = d["orthographic"]; - camera->set_size_mag(ortho["ymag"]); - camera->set_depth_far(ortho["zfar"]); - camera->set_depth_near(ortho["znear"]); - } - } else { - ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Camera3D should be in 'orthographic' or 'perspective'"); - } - - state->cameras.push_back(camera); + state->cameras.push_back(GLTFCamera::from_dictionary(cameras[i])); } print_verbose("glTF: Total cameras: " + itos(state->cameras.size())); @@ -5110,32 +5062,16 @@ Camera3D *GLTFDocument::_generate_camera(Ref state, const GLTFNodeInd ERR_FAIL_INDEX_V(gltf_node->camera, state->cameras.size(), nullptr); - Camera3D *camera = memnew(Camera3D); print_verbose("glTF: Creating camera for: " + gltf_node->get_name()); Ref c = state->cameras[gltf_node->camera]; - camera->set_projection(c->get_perspective() ? Camera3D::PROJECTION_PERSPECTIVE : Camera3D::PROJECTION_ORTHOGONAL); - // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. - camera->set_fov(Math::rad_to_deg(c->get_fov())); - // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters. - camera->set_size(c->get_size_mag() * 2.0f); - camera->set_near(c->get_depth_near()); - camera->set_far(c->get_depth_far()); - return camera; + return c->to_node(); } GLTFCameraIndex GLTFDocument::_convert_camera(Ref state, Camera3D *p_camera) { print_verbose("glTF: Converting camera: " + p_camera->get_name()); - Ref c; - c.instantiate(); - c->set_perspective(p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); - // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. - c->set_fov(Math::deg_to_rad(p_camera->get_fov())); - // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters. - c->set_size_mag(p_camera->get_size() * 0.5f); - c->set_depth_far(p_camera->get_far()); - c->set_depth_near(p_camera->get_near()); + Ref c = GLTFCamera::from_node(p_camera); GLTFCameraIndex camera_index = state->cameras.size(); state->cameras.push_back(c); return camera_index; diff --git a/modules/gltf/structures/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp index c492913ea727..5069f39c4bfb 100644 --- a/modules/gltf/structures/gltf_camera.cpp +++ b/modules/gltf/structures/gltf_camera.cpp @@ -31,6 +31,12 @@ #include "gltf_camera.h" void GLTFCamera::_bind_methods() { + ClassDB::bind_static_method("GLTFCamera", D_METHOD("from_node", "camera_node"), &GLTFCamera::from_node); + ClassDB::bind_method(D_METHOD("to_node"), &GLTFCamera::to_node); + + ClassDB::bind_static_method("GLTFCamera", D_METHOD("from_dictionary", "dictionary"), &GLTFCamera::from_dictionary); + ClassDB::bind_method(D_METHOD("to_dictionary"), &GLTFCamera::to_dictionary); + ClassDB::bind_method(D_METHOD("get_perspective"), &GLTFCamera::get_perspective); ClassDB::bind_method(D_METHOD("set_perspective", "perspective"), &GLTFCamera::set_perspective); ClassDB::bind_method(D_METHOD("get_fov"), &GLTFCamera::get_fov); @@ -48,3 +54,78 @@ void GLTFCamera::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_far"), "set_depth_far", "get_depth_far"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_near"), "set_depth_near", "get_depth_near"); } + +Ref GLTFCamera::from_node(const Camera3D *p_camera) { + Ref c; + c.instantiate(); + c->set_perspective(p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); + // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. + c->set_fov(Math::deg_to_rad(p_camera->get_fov())); + // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters. + c->set_size_mag(p_camera->get_size() * 0.5f); + c->set_depth_far(p_camera->get_far()); + c->set_depth_near(p_camera->get_near()); + return c; +} + +Camera3D *GLTFCamera::to_node() const { + Camera3D *camera = memnew(Camera3D); + camera->set_projection(perspective ? Camera3D::PROJECTION_PERSPECTIVE : Camera3D::PROJECTION_ORTHOGONAL); + // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. + camera->set_fov(Math::rad_to_deg(fov)); + // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters. + camera->set_size(size_mag * 2.0f); + camera->set_near(depth_near); + camera->set_far(depth_far); + return camera; +} + +Ref GLTFCamera::from_dictionary(const Dictionary p_dictionary) { + ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref(), "Failed to parse GLTF camera, missing required field 'type'."); + Ref camera; + camera.instantiate(); + const String &type = p_dictionary["type"]; + if (type == "perspective") { + camera->set_perspective(true); + if (p_dictionary.has("perspective")) { + const Dictionary &persp = p_dictionary["perspective"]; + camera->set_fov(persp["yfov"]); + if (persp.has("zfar")) { + camera->set_depth_far(persp["zfar"]); + } + camera->set_depth_near(persp["znear"]); + } + } else if (type == "orthographic") { + camera->set_perspective(false); + if (p_dictionary.has("orthographic")) { + const Dictionary &ortho = p_dictionary["orthographic"]; + camera->set_size_mag(ortho["ymag"]); + camera->set_depth_far(ortho["zfar"]); + camera->set_depth_near(ortho["znear"]); + } + } else { + ERR_PRINT("Error parsing GLTF camera: Camera type '" + type + "' is unknown, should be perspective or orthographic."); + } + return camera; +} + +Dictionary GLTFCamera::to_dictionary() const { + Dictionary d; + if (perspective) { + Dictionary persp; + persp["yfov"] = fov; + persp["zfar"] = depth_far; + persp["znear"] = depth_near; + d["perspective"] = persp; + d["type"] = "perspective"; + } else { + Dictionary ortho; + ortho["ymag"] = size_mag; + ortho["xmag"] = size_mag; + ortho["zfar"] = depth_far; + ortho["znear"] = depth_near; + d["orthographic"] = ortho; + d["type"] = "orthographic"; + } + return d; +} diff --git a/modules/gltf/structures/gltf_camera.h b/modules/gltf/structures/gltf_camera.h index 8e528c063f82..50ae10e17a51 100644 --- a/modules/gltf/structures/gltf_camera.h +++ b/modules/gltf/structures/gltf_camera.h @@ -63,6 +63,12 @@ public: void set_depth_far(real_t p_val) { depth_far = p_val; } real_t get_depth_near() const { return depth_near; } void set_depth_near(real_t p_val) { depth_near = p_val; } + + static Ref from_node(const Camera3D *p_light); + Camera3D *to_node() const; + + static Ref from_dictionary(const Dictionary p_dictionary); + Dictionary to_dictionary() const; }; #endif // GLTF_CAMERA_H