Browse Source

Merge pull request #26064 from JFonS/add_frustum_camera_mode

Add FRUSTUM camera mode, allowing tilted frustums
Hein-Pieter van Braam 6 years ago
parent
commit
5d33f241f0

+ 8 - 0
core/math/camera_matrix.cpp

@@ -210,6 +210,14 @@ void CameraMatrix::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, r
 	te[15] = 0;
 }
 
+void CameraMatrix::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) {
+	if (!p_flip_fov) {
+		p_size *= p_aspect;
+	}
+
+	set_frustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset.y, p_near, p_far);
+}
+
 real_t CameraMatrix::get_z_far() const {
 
 	const real_t *matrix = (const real_t *)this->matrix;

+ 1 - 0
core/math/camera_matrix.h

@@ -61,6 +61,7 @@ struct CameraMatrix {
 	void set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar);
 	void set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false);
 	void set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far);
+	void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false);
 
 	static real_t get_fovy(real_t p_fovx, real_t p_aspect) {
 

+ 45 - 21
editor/spatial_editor_gizmos.cpp

@@ -1309,6 +1309,28 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 	Ref<Material> material = get_material("camera_material", p_gizmo);
 	Ref<Material> icon = get_material("camera_icon", p_gizmo);
 
+#define ADD_TRIANGLE(m_a, m_b, m_c) \
+	{                               \
+		lines.push_back(m_a);       \
+		lines.push_back(m_b);       \
+		lines.push_back(m_b);       \
+		lines.push_back(m_c);       \
+		lines.push_back(m_c);       \
+		lines.push_back(m_a);       \
+	}
+
+#define ADD_QUAD(m_a, m_b, m_c, m_d) \
+	{                                \
+		lines.push_back(m_a);        \
+		lines.push_back(m_b);        \
+		lines.push_back(m_b);        \
+		lines.push_back(m_c);        \
+		lines.push_back(m_c);        \
+		lines.push_back(m_d);        \
+		lines.push_back(m_d);        \
+		lines.push_back(m_a);        \
+	}
+
 	switch (camera->get_projection()) {
 
 		case Camera::PROJECTION_PERSPECTIVE: {
@@ -1321,16 +1343,6 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 			nside.x = -nside.x;
 			Vector3 up = Vector3(0, side.x, 0);
 
-#define ADD_TRIANGLE(m_a, m_b, m_c) \
-	{                               \
-		lines.push_back(m_a);       \
-		lines.push_back(m_b);       \
-		lines.push_back(m_b);       \
-		lines.push_back(m_c);       \
-		lines.push_back(m_c);       \
-		lines.push_back(m_a);       \
-	}
-
 			ADD_TRIANGLE(Vector3(), side + up, side - up);
 			ADD_TRIANGLE(Vector3(), nside + up, nside - up);
 			ADD_TRIANGLE(Vector3(), side + up, nside + up);
@@ -1345,17 +1357,6 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 		} break;
 		case Camera::PROJECTION_ORTHOGONAL: {
 
-#define ADD_QUAD(m_a, m_b, m_c, m_d) \
-	{                                \
-		lines.push_back(m_a);        \
-		lines.push_back(m_b);        \
-		lines.push_back(m_b);        \
-		lines.push_back(m_c);        \
-		lines.push_back(m_c);        \
-		lines.push_back(m_d);        \
-		lines.push_back(m_d);        \
-		lines.push_back(m_a);        \
-	}
 			float size = camera->get_size();
 
 			float hsize = size * 0.5;
@@ -1368,6 +1369,7 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 			ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back);
 			ADD_QUAD(up + right, up + right + back, up - right + back, up - right);
 			ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right);
+
 			handles.push_back(right + back);
 
 			right.x *= 0.25;
@@ -1375,8 +1377,30 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 			ADD_TRIANGLE(tup, right + up + back, -right + up + back);
 
 		} break;
+		case Camera::PROJECTION_FRUSTUM: {
+			float hsize = camera->get_size() / 2.0;
+
+			Vector3 side = Vector3(hsize, 0, -camera->get_znear()).normalized();
+			Vector3 nside = side;
+			nside.x = -nside.x;
+			Vector3 up = Vector3(0, side.x, 0);
+			Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0);
+
+			ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset);
+			ADD_TRIANGLE(Vector3(), nside + up + offset, nside - up + offset);
+			ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset);
+			ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset);
+
+			side.x *= 0.25;
+			nside.x *= 0.25;
+			Vector3 tup(0, up.y * 3 / 2, side.z);
+			ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset);
+		}
 	}
 
+#undef ADD_TRIANGLE
+#undef ADD_QUAD
+
 	p_gizmo->add_lines(lines, material);
 	p_gizmo->add_unscaled_billboard(icon, 0.05);
 	p_gizmo->add_handles(handles, get_material("handles"));

+ 42 - 4
scene/3d/camera.cpp

@@ -55,16 +55,23 @@ void Camera::_update_camera_mode() {
 		case PROJECTION_ORTHOGONAL: {
 			set_orthogonal(size, near, far);
 		} break;
+		case PROJECTION_FRUSTUM: {
+			set_frustum(size, frustum_offset, near, far);
+		} break;
 	}
 }
 
 void Camera::_validate_property(PropertyInfo &p_property) const {
 	if (p_property.name == "fov") {
-		if (mode == PROJECTION_ORTHOGONAL) {
+		if (mode != PROJECTION_PERSPECTIVE) {
 			p_property.usage = PROPERTY_USAGE_NOEDITOR;
 		}
 	} else if (p_property.name == "size") {
-		if (mode == PROJECTION_PERSPECTIVE) {
+		if (mode != PROJECTION_ORTHOGONAL && mode != PROJECTION_FRUSTUM) {
+			p_property.usage = PROPERTY_USAGE_NOEDITOR;
+		}
+	} else if (p_property.name == "frustum_offset") {
+		if (mode != PROJECTION_FRUSTUM) {
 			p_property.usage = PROPERTY_USAGE_NOEDITOR;
 		}
 	}
@@ -177,8 +184,24 @@ void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) {
 	update_gizmo();
 }
 
+void Camera::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far) {
+	if (!force_change && size == p_size && frustum_offset == p_offset && p_z_near == near && p_z_far == far && mode == PROJECTION_FRUSTUM)
+		return;
+
+	size = p_size;
+	frustum_offset = p_offset;
+
+	near = p_z_near;
+	far = p_z_far;
+	mode = PROJECTION_FRUSTUM;
+	force_change = false;
+
+	VisualServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, near, far);
+	update_gizmo();
+}
+
 void Camera::set_projection(Camera::Projection p_mode) {
-	if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL) {
+	if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
 		mode = p_mode;
 		_update_camera_mode();
 		_change_notify();
@@ -470,16 +493,19 @@ void Camera::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("project_position", "screen_point"), &Camera::project_position);
 	ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera::set_perspective);
 	ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal);
+	ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera::set_frustum);
 	ClassDB::bind_method(D_METHOD("make_current"), &Camera::make_current);
 	ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera::clear_current, DEFVAL(true));
 	ClassDB::bind_method(D_METHOD("set_current"), &Camera::set_current);
 	ClassDB::bind_method(D_METHOD("is_current"), &Camera::is_current);
 	ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera::get_camera_transform);
 	ClassDB::bind_method(D_METHOD("get_fov"), &Camera::get_fov);
+	ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera::get_frustum_offset);
 	ClassDB::bind_method(D_METHOD("get_size"), &Camera::get_size);
 	ClassDB::bind_method(D_METHOD("get_zfar"), &Camera::get_zfar);
 	ClassDB::bind_method(D_METHOD("get_znear"), &Camera::get_znear);
 	ClassDB::bind_method(D_METHOD("set_fov"), &Camera::set_fov);
+	ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera::set_frustum_offset);
 	ClassDB::bind_method(D_METHOD("set_size"), &Camera::set_size);
 	ClassDB::bind_method(D_METHOD("set_zfar"), &Camera::set_zfar);
 	ClassDB::bind_method(D_METHOD("set_znear"), &Camera::set_znear);
@@ -510,15 +536,17 @@ void Camera::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal"), "set_projection", "get_projection");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar");
 
 	BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
 	BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
+	BIND_ENUM_CONSTANT(PROJECTION_FRUSTUM);
 
 	BIND_ENUM_CONSTANT(KEEP_WIDTH);
 	BIND_ENUM_CONSTANT(KEEP_HEIGHT);
@@ -543,6 +571,10 @@ float Camera::get_znear() const {
 	return near;
 }
 
+Vector2 Camera::get_frustum_offset() const {
+	return frustum_offset;
+}
+
 float Camera::get_zfar() const {
 
 	return far;
@@ -570,6 +602,11 @@ void Camera::set_znear(float p_znear) {
 	_update_camera_mode();
 }
 
+void Camera::set_frustum_offset(Vector2 p_offset) {
+	frustum_offset = p_offset;
+	_update_camera_mode();
+}
+
 void Camera::set_zfar(float p_zfar) {
 	far = p_zfar;
 	_update_camera_mode();
@@ -648,6 +685,7 @@ Camera::Camera() {
 	camera = VisualServer::get_singleton()->camera_create();
 	size = 1;
 	fov = 0;
+	frustum_offset = Vector2();
 	near = 0;
 	far = 0;
 	current = false;

+ 7 - 1
scene/3d/camera.h

@@ -46,7 +46,8 @@ public:
 	enum Projection {
 
 		PROJECTION_PERSPECTIVE,
-		PROJECTION_ORTHOGONAL
+		PROJECTION_ORTHOGONAL,
+		PROJECTION_FRUSTUM
 	};
 
 	enum KeepAspect {
@@ -68,6 +69,7 @@ private:
 
 	float fov;
 	float size;
+	Vector2 frustum_offset;
 	float near, far;
 	float v_offset;
 	float h_offset;
@@ -110,6 +112,7 @@ public:
 
 	void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far);
 	void set_orthogonal(float p_size, float p_z_near, float p_z_far);
+	void set_frustum(float p_size, Vector2 p_offset, float p_near, float p_far);
 	void set_projection(Camera::Projection p_mode);
 
 	void make_current();
@@ -123,12 +126,15 @@ public:
 	float get_size() const;
 	float get_zfar() const;
 	float get_znear() const;
+	Vector2 get_frustum_offset() const;
+
 	Projection get_projection() const;
 
 	void set_fov(float p_fov);
 	void set_size(float p_size);
 	void set_zfar(float p_zfar);
 	void set_znear(float p_znear);
+	void set_frustum_offset(Vector2 p_offset);
 
 	virtual Transform get_camera_transform() const;
 

+ 1 - 0
servers/visual/visual_server_raster.h

@@ -430,6 +430,7 @@ public:
 	BIND0R(RID, camera_create)
 	BIND4(camera_set_perspective, RID, float, float, float)
 	BIND4(camera_set_orthogonal, RID, float, float, float)
+	BIND5(camera_set_frustum, RID, float, Vector2, float, float)
 	BIND2(camera_set_transform, RID, const Transform &)
 	BIND2(camera_set_cull_mask, RID, uint32_t)
 	BIND2(camera_set_environment, RID, RID)

+ 21 - 0
servers/visual/visual_server_scene.cpp

@@ -61,6 +61,16 @@ void VisualServerScene::camera_set_orthogonal(RID p_camera, float p_size, float
 	camera->zfar = p_z_far;
 }
 
+void VisualServerScene::camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) {
+	Camera *camera = camera_owner.get(p_camera);
+	ERR_FAIL_COND(!camera);
+	camera->type = Camera::FRUSTUM;
+	camera->size = p_size;
+	camera->offset = p_offset;
+	camera->znear = p_z_near;
+	camera->zfar = p_z_far;
+}
+
 void VisualServerScene::camera_set_transform(RID p_camera, const Transform &p_transform) {
 
 	Camera *camera = camera_owner.get(p_camera);
@@ -1730,6 +1740,17 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view
 			ortho = false;
 
 		} break;
+		case Camera::FRUSTUM: {
+
+			camera_matrix.set_frustum(
+					camera->size,
+					p_viewport_size.width / (float)p_viewport_size.height,
+					camera->offset,
+					camera->znear,
+					camera->zfar,
+					camera->vaspect);
+			ortho = false;
+		} break;
 	}
 
 	_prepare_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID());

+ 5 - 1
servers/visual/visual_server_scene.h

@@ -77,12 +77,14 @@ public:
 
 		enum Type {
 			PERSPECTIVE,
-			ORTHOGONAL
+			ORTHOGONAL,
+			FRUSTUM
 		};
 		Type type;
 		float fov;
 		float znear, zfar;
 		float size;
+		Vector2 offset;
 		uint32_t visible_layers;
 		bool vaspect;
 		RID env;
@@ -97,6 +99,7 @@ public:
 			znear = 0.05;
 			zfar = 100;
 			size = 1.0;
+			offset = Vector2();
 			vaspect = false;
 		}
 	};
@@ -106,6 +109,7 @@ public:
 	virtual RID camera_create();
 	virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far);
 	virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far);
+	virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far);
 	virtual void camera_set_transform(RID p_camera, const Transform &p_transform);
 	virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers);
 	virtual void camera_set_environment(RID p_camera, RID p_env);

+ 1 - 0
servers/visual/visual_server_wrap_mt.h

@@ -360,6 +360,7 @@ public:
 	FUNCRID(camera)
 	FUNC4(camera_set_perspective, RID, float, float, float)
 	FUNC4(camera_set_orthogonal, RID, float, float, float)
+	FUNC5(camera_set_frustum, RID, float, Vector2, float, float)
 	FUNC2(camera_set_transform, RID, const Transform &)
 	FUNC2(camera_set_cull_mask, RID, uint32_t)
 	FUNC2(camera_set_environment, RID, RID)

+ 1 - 0
servers/visual_server.cpp

@@ -1863,6 +1863,7 @@ void VisualServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("camera_create"), &VisualServer::camera_create);
 	ClassDB::bind_method(D_METHOD("camera_set_perspective", "camera", "fovy_degrees", "z_near", "z_far"), &VisualServer::camera_set_perspective);
 	ClassDB::bind_method(D_METHOD("camera_set_orthogonal", "camera", "size", "z_near", "z_far"), &VisualServer::camera_set_orthogonal);
+	ClassDB::bind_method(D_METHOD("camera_set_frustum", "camera", "size", "offset", "z_near", "z_far"), &VisualServer::camera_set_frustum);
 	ClassDB::bind_method(D_METHOD("camera_set_transform", "camera", "transform"), &VisualServer::camera_set_transform);
 	ClassDB::bind_method(D_METHOD("camera_set_cull_mask", "camera", "layers"), &VisualServer::camera_set_cull_mask);
 	ClassDB::bind_method(D_METHOD("camera_set_environment", "camera", "env"), &VisualServer::camera_set_environment);

+ 1 - 0
servers/visual_server.h

@@ -584,6 +584,7 @@ public:
 	virtual RID camera_create() = 0;
 	virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far) = 0;
 	virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far) = 0;
+	virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) = 0;
 	virtual void camera_set_transform(RID p_camera, const Transform &p_transform) = 0;
 	virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0;
 	virtual void camera_set_environment(RID p_camera, RID p_env) = 0;