Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions core/math/projection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "core/math/aabb.h"
#include "core/math/math_funcs.h"
#include "core/math/plane.h"
#include "core/math/quaternion.h"
#include "core/math/rect2.h"
#include "core/math/transform_3d.h"
#include "core/string/ustring.h"
Expand Down Expand Up @@ -313,6 +314,62 @@ void Projection::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t
*this = *this * cm;
}

void Projection::set_oblique(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, Quaternion p_oblique_plane, bool p_flip_fov) {
if (p_flip_fov) {
p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect);
}

// real_t *columns = (real_t *)columns;

real_t sine, cotangent, deltaZ;
real_t radians = p_fovy_degrees / 2.0 * Math_PI / 180.0;

deltaZ = p_z_far - p_z_near;
sine = Math::sin(radians);

if ((deltaZ == 0) || (sine == 0) || (p_aspect == 0)) {
return;
}
cotangent = Math::cos(radians) / sine;

set_identity();

columns[0][0] = cotangent / p_aspect;
columns[1][1] = cotangent;
columns[2][2] = -(p_z_far + p_z_near) / deltaZ;
columns[2][3] = -1;
columns[3][2] = -2 * p_z_near * p_z_far / deltaZ;
columns[3][3] = 0;

// Here goes oblique magic!
// Eric Lengyel Solution: http://terathon.com/code/oblique.html
// i < 0 ? -1 : (i > 0 ? +1 : 0) = sign()
Quaternion q;

/* clang-format off */
q.x = ((
p_oblique_plane.x < 0 ? -1 :
p_oblique_plane.x > 0 ? +1 : 0
) + columns[2][0]
) / columns[0][0];

q.y = ((
p_oblique_plane.y < 0 ? -1 :
p_oblique_plane.y > 0 ? +1 : 0
) + columns[2][1]
) / columns[1][1];
/* clang-format on */

q.z = -1.0F;
q.w = (1.0F + columns[2][2]) / columns[3][2];

Quaternion c = p_oblique_plane * (2.0F / p_oblique_plane.dot(q));
columns[0][2] = c.x;
columns[1][2] = c.y;
columns[2][2] = c.z + 1.0F;
columns[3][2] = c.w;
}

void Projection::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) {
// we first calculate our base frustum on our values without taking our lens magnification into account.
real_t f1 = (p_intraocular_dist * 0.5) / p_display_to_lens;
Expand Down
3 changes: 3 additions & 0 deletions core/math/projection.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#ifndef PROJECTION_H
#define PROJECTION_H

#include "core/math/math_defs.h"
#include "core/math/quaternion.h"
#include "core/math/vector3.h"
#include "core/math/vector4.h"

Expand Down Expand Up @@ -74,6 +76,7 @@ struct _NO_DISCARD_ Projection {
void set_light_atlas_rect(const Rect2 &p_rect);
void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false);
void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist);
void set_oblique(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, Quaternion p_oblique_plane, bool p_flip_fov = false);
void set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far);
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);
Expand Down
34 changes: 34 additions & 0 deletions doc/classes/Camera3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,28 @@
Sets the camera projection to frustum mode (see [constant PROJECTION_FRUSTUM]), by specifying a [param size], an [param offset], and the [param z_near] and [param z_far] clip planes in world space units. See also [member frustum_offset].
</description>
</method>
<method name="set_oblique">
<return type="void" />
<param index="0" name="fov" type="float" />
<param index="1" name="oblique_data" type="Dictionary" />
<param index="2" name="z_near" type="float" />
<param index="3" name="z_far" type="float" />
<description>
Sets the camera projection to perspective with oblique near clipping (see [constant PROJECTION_OBLIQUE]), by specifying a [code]fov[/code] (field of view) angle in degrees, the [code]oblique_data[/code] used to transform the projection frustum, and the [code]z_near[/code] and [code]z_far[/code] clip planes in world space before transformation.
[b]Note:[/b] [code]oblique_data[/code] is a dictionary containing the following fields:
[code]camera_gt[/code]: The camera global transform.
[code]normal[/code]: The desired normal vector of the plane.
[code]position[/code]: The world-space position of the plane relative to the camera
[code]offset[/code]: An adjustable offset for the oblique plane distance value
</description>
</method>
<method name="set_oblique_plane_from_transform">
<return type="void" />
<param index="0" name="oblique_transform" type="Transform3D" />
<description>
Sets the Oblique_normal and oblique_position values of an oblique projection camera using a the origin and upward vector of the transform argument. For ease of using plane meshes as portals and mirrors.
</description>
</method>
<method name="set_orthogonal">
<return type="void" />
<param index="0" name="size" type="float" />
Expand Down Expand Up @@ -190,6 +212,15 @@
<member name="near" type="float" setter="set_near" getter="get_near" default="0.05">
The distance to the near culling boundary for this camera relative to its local Z axis.
</member>
<member name="oblique_normal" type="Vector3" setter="set_oblique_normal" getter="get_oblique_normal" default="Vector3(0, 1, 0)">
The desired normal vector of the world space plane used by an oblique camera as an oblique near clipping plane.
</member>
<member name="oblique_offset" type="float" setter="set_oblique_offset" getter="get_oblique_offset" default="0.0">
The an offset value for the oblique plane along it's forward vector.
</member>
<member name="oblique_position" type="Vector3" setter="set_oblique_position" getter="get_oblique_position" default="Vector3(0, 0, 0)">
The world space position of the plane used by an oblique camera as an oblique near clipping plane.
</member>
<member name="projection" type="int" setter="set_projection" getter="get_projection" enum="Camera3D.ProjectionType" default="0">
The camera's projection mode. In [constant PROJECTION_PERSPECTIVE] mode, objects' Z distance from the camera's local space scales their perceived size.
</member>
Expand All @@ -210,6 +241,9 @@
<constant name="PROJECTION_FRUSTUM" value="2" enum="ProjectionType">
Frustum projection. This mode allows adjusting [member frustum_offset] to create "tilted frustum" effects.
</constant>
<constant name="PROJECTION_OBLIQUE" value="3" enum="ProjectionType">
Oblique Near Plane projection. This mode allows adjusting the near clipping plane to align with a given world plane.
</constant>
<constant name="KEEP_WIDTH" value="0" enum="KeepAspect">
Preserves the horizontal aspect ratio; also known as Vert- scaling. This is usually the best option for projects running in portrait mode, as taller aspect ratios will benefit from a wider vertical FOV.
</constant>
Expand Down
14 changes: 14 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@
Sets camera to use frustum projection. This mode allows adjusting the [param offset] argument to create "tilted frustum" effects.
</description>
</method>
<method name="camera_set_oblique">
<return type="void" />
<param index="0" name="camera" type="RID" />
<param index="1" name="fovy_degrees" type="float" />
<param index="2" name="camera_gt" type="Transform3D" />
<param index="3" name="oblique_normal" type="Vector3" />
<param index="4" name="oblique_position" type="Vector3" />
<param index="5" name="oblique_offset" type="float" />
<param index="6" name="z_near" type="float" />
<param index="7" name="z_far" type="float" />
<description>
Sets camera to use oblique projection. This mode allows adjusting the [code]normal[/code], [code]position[/code], and [code]offset[/code] arguments to create an oblique near clipping plane.
</description>
</method>
<method name="camera_set_orthogonal">
<return type="void" />
<param index="0" name="camera" type="RID" />
Expand Down
20 changes: 20 additions & 0 deletions editor/plugins/node_3d_editor_gizmos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1941,6 +1941,26 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector3 tup(0, up.y + hsize / 2, side.z);
ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset);
} break;
case Camera3D::PROJECTION_OBLIQUE: {
// The real FOV is halved for accurate representation
float fov = camera->get_fov() / 2.0;

Vector3 side = Vector3(Math::sin(Math::deg_to_rad(fov)), 0, -Math::cos(Math::deg_to_rad(fov)));
Vector3 nside = side;
nside.x = -nside.x;
Vector3 up = Vector3(0, side.x, 0);

ADD_TRIANGLE(Vector3(), side + up, side - up);
ADD_TRIANGLE(Vector3(), nside + up, nside - up);
ADD_TRIANGLE(Vector3(), side + up, nside + up);
ADD_TRIANGLE(Vector3(), side - up, nside - up);

handles.push_back(side);
side.x *= 0.25;
nside.x *= 0.25;
Vector3 tup(0, up.y * 3 / 2, side.z);
ADD_TRIANGLE(tup, side + up, nside + up);
} break;
}

#undef ADD_TRIANGLE
Expand Down
99 changes: 90 additions & 9 deletions scene/3d/camera_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ void Camera3D::_update_camera_mode() {
switch (mode) {
case PROJECTION_PERSPECTIVE: {
set_perspective(fov, near, far);

} break;
case PROJECTION_OBLIQUE: {
Dictionary ob_data = Dictionary();
ob_data["camera_gt"] = get_global_transform();
ob_data["normal"] = oblique_normal;
ob_data["position"] = oblique_position;
ob_data["offset"] = oblique_offset;
set_oblique(fov, ob_data, near, far);
} break;
case PROJECTION_ORTHOGONAL: {
set_orthogonal(size, near, far);
Expand All @@ -60,7 +67,7 @@ void Camera3D::_update_camera_mode() {

void Camera3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "fov") {
if (mode != PROJECTION_PERSPECTIVE) {
if (mode != PROJECTION_PERSPECTIVE && mode != PROJECTION_OBLIQUE) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
} else if (p_property.name == "size") {
Expand All @@ -71,6 +78,10 @@ void Camera3D::_validate_property(PropertyInfo &p_property) const {
if (mode != PROJECTION_FRUSTUM) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
} else if (p_property.name == "oblique_normal" || p_property.name == "oblique_position" || p_property.name == "oblique_offset") {
if (mode != PROJECTION_OBLIQUE) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}

if (attributes.is_valid()) {
Expand Down Expand Up @@ -185,6 +196,28 @@ void Camera3D::set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_
force_change = false;
}

void Camera3D::set_oblique(real_t p_fovy_degrees, const Dictionary &p_oblique_data, real_t p_z_near, real_t p_z_far) {
if (!force_change && fov == p_fovy_degrees && p_z_near == near && p_z_far == far && mode == PROJECTION_OBLIQUE) {
return;
}

if (!force_change && (Vector3)p_oblique_data["normal"] == oblique_normal && (Vector3)p_oblique_data["position"] == oblique_position && (real_t)p_oblique_data["offset"] == oblique_offset) {
return;
}

fov = p_fovy_degrees;
oblique_normal = p_oblique_data["normal"];
oblique_position = p_oblique_data["position"];
oblique_offset = p_oblique_data["offset"];
near = p_z_near;
far = p_z_far;
mode = PROJECTION_OBLIQUE;

RenderingServer::get_singleton()->camera_set_oblique(camera, fov, p_oblique_data["camera_gt"], p_oblique_data["normal"], p_oblique_data["position"], p_oblique_data["offset"], near, far);
update_gizmos();
force_change = false;
}

void Camera3D::set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) {
if (!force_change && size == p_size && p_z_near == near && p_z_far == far && mode == PROJECTION_ORTHOGONAL) {
return;
Expand Down Expand Up @@ -218,8 +251,8 @@ void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, rea
update_gizmos();
}

void Camera3D::set_projection(ProjectionType p_mode) {
if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
void Camera3D::set_projection(Camera3D::ProjectionType p_mode) {
if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_OBLIQUE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
mode = p_mode;
_update_camera_mode();
notify_property_list_changed();
Expand Down Expand Up @@ -501,6 +534,7 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_position_behind", "world_point"), &Camera3D::is_position_behind);
ClassDB::bind_method(D_METHOD("project_position", "screen_point", "z_depth"), &Camera3D::project_position);
ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera3D::set_perspective);
ClassDB::bind_method(D_METHOD("set_oblique", "fov", "oblique_data", "z_near", "z_far"), &Camera3D::set_oblique);
ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera3D::set_orthogonal);
ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera3D::set_frustum);
ClassDB::bind_method(D_METHOD("make_current"), &Camera3D::make_current);
Expand All @@ -509,11 +543,18 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_current"), &Camera3D::is_current);
ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera3D::get_camera_transform);
ClassDB::bind_method(D_METHOD("get_fov"), &Camera3D::get_fov);
ClassDB::bind_method(D_METHOD("get_oblique_normal"), &Camera3D::get_oblique_normal);
ClassDB::bind_method(D_METHOD("get_oblique_position"), &Camera3D::get_oblique_position);
ClassDB::bind_method(D_METHOD("get_oblique_offset"), &Camera3D::get_oblique_offset);
ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera3D::get_frustum_offset);
ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size);
ClassDB::bind_method(D_METHOD("get_far"), &Camera3D::get_far);
ClassDB::bind_method(D_METHOD("get_near"), &Camera3D::get_near);
ClassDB::bind_method(D_METHOD("set_fov", "fov"), &Camera3D::set_fov);
ClassDB::bind_method(D_METHOD("set_oblique_normal", "oblique_normal"), &Camera3D::set_oblique_normal);
ClassDB::bind_method(D_METHOD("set_oblique_position", "oblique_position"), &Camera3D::set_oblique_position);
ClassDB::bind_method(D_METHOD("set_oblique_plane_from_transform", "oblique_transform"), &Camera3D::set_oblique_plane_from_transform);
ClassDB::bind_method(D_METHOD("set_oblique_offset", "oblique_offset"), &Camera3D::set_oblique_offset);
ClassDB::bind_method(D_METHOD("set_frustum_offset", "offset"), &Camera3D::set_frustum_offset);
ClassDB::bind_method(D_METHOD("set_size", "size"), &Camera3D::set_size);
ClassDB::bind_method(D_METHOD("set_far", "far"), &Camera3D::set_far);
Expand Down Expand Up @@ -551,17 +592,21 @@ void Camera3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "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,Frustum"), "set_projection", "get_projection");
ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum,Oblique"), "set_projection", "get_projection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.001,suffix:m"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_frustum_offset", "get_frustum_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "oblique_normal"), "set_oblique_normal", "get_oblique_normal");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "oblique_position"), "set_oblique_position", "get_oblique_position");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oblique_offset"), "set_oblique_offset", "get_oblique_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.001"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp"), "set_near", "get_near");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp"), "set_far", "get_far");

BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
BIND_ENUM_CONSTANT(PROJECTION_FRUSTUM);
BIND_ENUM_CONSTANT(PROJECTION_OBLIQUE);

BIND_ENUM_CONSTANT(KEEP_WIDTH);
BIND_ENUM_CONSTANT(KEEP_HEIGHT);
Expand All @@ -575,6 +620,18 @@ real_t Camera3D::get_fov() const {
return fov;
}

Vector3 Camera3D::get_oblique_normal() const {
return oblique_normal;
}

Vector3 Camera3D::get_oblique_position() const {
return oblique_position;
}

real_t Camera3D::get_oblique_offset() const {
return oblique_offset;
}

real_t Camera3D::get_size() const {
return size;
}
Expand All @@ -601,6 +658,27 @@ void Camera3D::set_fov(real_t p_fov) {
_update_camera_mode();
}

void Camera3D::set_oblique_normal(Vector3 p_oblique_normal) {
oblique_normal = p_oblique_normal;
_update_camera_mode();
}

void Camera3D::set_oblique_position(Vector3 p_oblique_position) {
oblique_position = p_oblique_position;
_update_camera_mode();
}

void Camera3D::set_oblique_offset(real_t p_oblique_offset) {
oblique_offset = p_oblique_offset;
_update_camera_mode();
}

void Camera3D::set_oblique_plane_from_transform(Transform3D p_oblique_transform) {
set_oblique_normal(p_oblique_transform.basis.get_column(1));
set_oblique_position(p_oblique_transform.origin);
_update_camera_mode();
}

void Camera3D::set_size(real_t p_size) {
ERR_FAIL_COND(p_size < 0.001 || p_size > 16384);
size = p_size;
Expand Down Expand Up @@ -736,6 +814,9 @@ RID Camera3D::get_pyramid_shape_rid() {
Camera3D::Camera3D() {
camera = RenderingServer::get_singleton()->camera_create();
set_perspective(75.0, 0.05, 4000.0);
oblique_normal = Vector3(0, 1, 0);
oblique_position = Vector3();
oblique_offset = 0;
Comment thread
fire marked this conversation as resolved.
Outdated
RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers);
//active=false;
velocity_tracker.instantiate();
Expand Down
Loading