浏览代码

GLTF: Fix orthographic cameras, internally store data in GLTF's format

Aaron Franke 3 年之前
父节点
当前提交
be81b33e2b

+ 12 - 1
modules/gltf/doc_classes/GLTFCamera.xml

@@ -1,19 +1,30 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <class name="GLTFCamera" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
 	<brief_description>
+		Represents a GLTF camera.
 	</brief_description>
 	<description>
+		Represents a camera as defined by the base GLTF spec.
 	</description>
 	<tutorials>
+		<link title="GLTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link>
+		<link title="GLTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link>
 	</tutorials>
 	<members>
 		<member name="depth_far" type="float" setter="set_depth_far" getter="get_depth_far" default="4000.0">
+			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.
 		</member>
 		<member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05">
+			The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]znear[/code] property.
 		</member>
-		<member name="fov_size" type="float" setter="set_fov_size" getter="get_fov_size" default="75.0">
+		<member name="fov" type="float" setter="set_fov" getter="get_fov" default="1.309">
+			The FOV of the camera. This class and GLTF define the camera FOV in radians, while Godot uses degrees. This maps to GLTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true.
 		</member>
 		<member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
+			Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to GLTF's camera [code]type[/code] property. See [member Camera3D.projection] and the GLTF spec for more information.
+		</member>
+		<member name="size_mag" type="float" setter="set_size_mag" getter="get_size_mag" default="0.5">
+			The size of the camera. This class and GLTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to GLTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false.
 		</member>
 	</members>
 </class>

+ 41 - 46
modules/gltf/gltf_document.cpp

@@ -50,7 +50,6 @@
 #include "core/version.h"
 #include "drivers/png/png_driver_common.h"
 #include "scene/2d/node_2d.h"
-#include "scene/3d/camera_3d.h"
 #include "scene/3d/mesh_instance_3d.h"
 #include "scene/3d/multimesh_instance_3d.h"
 #include "scene/3d/node_3d.h"
@@ -4582,22 +4581,21 @@ Error GLTFDocument::_serialize_cameras(Ref<GLTFState> state) {
 
 		Ref<GLTFCamera> camera = state->cameras[i];
 
-		if (camera->get_perspective() == false) {
-			Dictionary og;
-			og["ymag"] = Math::deg2rad(camera->get_fov_size());
-			og["xmag"] = Math::deg2rad(camera->get_fov_size());
-			og["zfar"] = camera->get_depth_far();
-			og["znear"] = camera->get_depth_near();
-			d["orthographic"] = og;
-			d["type"] = "orthographic";
-		} else if (camera->get_perspective()) {
-			Dictionary ppt;
-			// GLTF spec is in radians, Godot's camera is in degrees.
-			ppt["yfov"] = Math::deg2rad(camera->get_fov_size());
-			ppt["zfar"] = camera->get_depth_far();
-			ppt["znear"] = camera->get_depth_near();
-			d["perspective"] = ppt;
+		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;
 	}
@@ -4680,27 +4678,23 @@ Error GLTFDocument::_parse_cameras(Ref<GLTFState> state) {
 		camera.instantiate();
 		ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
 		const String &type = d["type"];
-		if (type == "orthographic") {
-			camera->set_perspective(false);
-			if (d.has("orthographic")) {
-				const Dictionary &og = d["orthographic"];
-				// GLTF spec is in radians, Godot's camera is in degrees.
-				camera->set_fov_size(Math::rad2deg(real_t(og["ymag"])));
-				camera->set_depth_far(og["zfar"]);
-				camera->set_depth_near(og["znear"]);
-			} else {
-				camera->set_fov_size(10);
-			}
-		} else if (type == "perspective") {
+		if (type == "perspective") {
 			camera->set_perspective(true);
 			if (d.has("perspective")) {
-				const Dictionary &ppt = d["perspective"];
-				// GLTF spec is in radians, Godot's camera is in degrees.
-				camera->set_fov_size(Math::rad2deg(real_t(ppt["yfov"])));
-				camera->set_depth_far(ppt["zfar"]);
-				camera->set_depth_near(ppt["znear"]);
-			} else {
-				camera->set_fov_size(10);
+				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'");
@@ -5204,12 +5198,13 @@ Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, const GLTFNodeInd
 	print_verbose("glTF: Creating camera for: " + gltf_node->get_name());
 
 	Ref<GLTFCamera> c = state->cameras[gltf_node->camera];
-	if (c->get_perspective()) {
-		camera->set_perspective(c->get_fov_size(), c->get_depth_near(), c->get_depth_far());
-	} else {
-		camera->set_orthogonal(c->get_fov_size(), c->get_depth_near(), c->get_depth_far());
-	}
-
+	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::rad2deg(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;
 }
 
@@ -5218,11 +5213,11 @@ GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> state, Camera3D *p_
 
 	Ref<GLTFCamera> c;
 	c.instantiate();
-
-	if (p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE) {
-		c->set_perspective(true);
-	}
-	c->set_fov_size(p_camera->get_fov());
+	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::deg2rad(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());
 	GLTFCameraIndex camera_index = state->cameras.size();

+ 9 - 6
modules/gltf/structures/gltf_camera.cpp

@@ -33,15 +33,18 @@
 void GLTFCamera::_bind_methods() {
 	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_size"), &GLTFCamera::get_fov_size);
-	ClassDB::bind_method(D_METHOD("set_fov_size", "fov_size"), &GLTFCamera::set_fov_size);
+	ClassDB::bind_method(D_METHOD("get_fov"), &GLTFCamera::get_fov);
+	ClassDB::bind_method(D_METHOD("set_fov", "fov"), &GLTFCamera::set_fov);
+	ClassDB::bind_method(D_METHOD("get_size_mag"), &GLTFCamera::get_size_mag);
+	ClassDB::bind_method(D_METHOD("set_size_mag", "size_mag"), &GLTFCamera::set_size_mag);
 	ClassDB::bind_method(D_METHOD("get_depth_far"), &GLTFCamera::get_depth_far);
 	ClassDB::bind_method(D_METHOD("set_depth_far", "zdepth_far"), &GLTFCamera::set_depth_far);
 	ClassDB::bind_method(D_METHOD("get_depth_near"), &GLTFCamera::get_depth_near);
 	ClassDB::bind_method(D_METHOD("set_depth_near", "zdepth_near"), &GLTFCamera::set_depth_near);
 
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "perspective"), "set_perspective", "get_perspective"); // bool
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov_size"), "set_fov_size", "get_fov_size"); // float
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_far"), "set_depth_far", "get_depth_far"); // float
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_near"), "set_depth_near", "get_depth_near"); // float
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "perspective"), "set_perspective", "get_perspective");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov"), "set_fov", "get_fov");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_mag"), "set_size_mag", "get_size_mag");
+	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");
 }

+ 18 - 9
modules/gltf/structures/gltf_camera.h

@@ -32,15 +32,22 @@
 #define GLTF_CAMERA_H
 
 #include "core/io/resource.h"
+#include "scene/3d/camera_3d.h"
+
+// Reference and test file:
+// https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md
 
 class GLTFCamera : public Resource {
 	GDCLASS(GLTFCamera, Resource);
 
 private:
+	// GLTF has no default camera values, they should always be specified in
+	// the GLTF file. Here we default to Godot's default camera settings.
 	bool perspective = true;
-	float fov_size = 75.0;
-	float depth_far = 4000.0;
-	float depth_near = 0.05;
+	real_t fov = Math::deg2rad(75.0);
+	real_t size_mag = 0.5;
+	real_t depth_far = 4000.0;
+	real_t depth_near = 0.05;
 
 protected:
 	static void _bind_methods();
@@ -48,12 +55,14 @@ protected:
 public:
 	bool get_perspective() const { return perspective; }
 	void set_perspective(bool p_val) { perspective = p_val; }
-	float get_fov_size() const { return fov_size; }
-	void set_fov_size(float p_val) { fov_size = p_val; }
-	float get_depth_far() const { return depth_far; }
-	void set_depth_far(float p_val) { depth_far = p_val; }
-	float get_depth_near() const { return depth_near; }
-	void set_depth_near(float p_val) { depth_near = p_val; }
+	real_t get_fov() const { return fov; }
+	void set_fov(real_t p_val) { fov = p_val; }
+	real_t get_size_mag() const { return size_mag; }
+	void set_size_mag(real_t p_val) { size_mag = p_val; }
+	real_t get_depth_far() const { return depth_far; }
+	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; }
 };
 
 #endif // GLTF_CAMERA_H