mirror of
https://github.com/Ttanasart-pt/Pixel-Composer.git
synced 2025-02-16 07:05:14 +01:00
354 lines
10 KiB
Text
354 lines
10 KiB
Text
/// @var {Struct.BBMOD_BaseCamera} The last used camera. Can be `undefined`.
|
|
/// @private
|
|
global.__bbmodCameraCurrent = undefined;
|
|
|
|
/// @func BBMOD_BaseCamera()
|
|
///
|
|
/// @extends BBMOD_Class
|
|
///
|
|
/// @desc A camera with support for both orthographic and perspective
|
|
/// projection.
|
|
///
|
|
/// @see BBMOD_Camera
|
|
function BBMOD_BaseCamera()
|
|
: BBMOD_Class() constructor
|
|
{
|
|
BBMOD_CLASS_GENERATED_BODY;
|
|
|
|
static Class_destroy = destroy;
|
|
|
|
/// @var {camera} An underlying GameMaker camera.
|
|
/// @readonly
|
|
Raw = camera_create();
|
|
|
|
/// @var {Real} The camera's exposure value. Defaults to `1`.
|
|
Exposure = 1.0;
|
|
|
|
/// @var {Struct.BBMOD_Vec3} The camera's positon. Defaults to `(0, 0, 0)`.
|
|
Position = new BBMOD_Vec3(0.0);
|
|
|
|
/// @var {Struct.BBMOD_Vec3} A position where the camera is looking at.
|
|
Target = BBMOD_VEC3_FORWARD;
|
|
|
|
/// @var {Struct.BBMOD_Vec3} The up vector.
|
|
Up = BBMOD_VEC3_UP;
|
|
|
|
/// @var {Real} The camera's field of view. Defaults to `60`.
|
|
/// @note This does not have any effect when {@link BBMOD_BaseCamera.Orthographic}
|
|
/// is enabled.
|
|
Fov = 60.0;
|
|
|
|
/// @var {Real} The camera's aspect ratio. Defaults to
|
|
/// `window_get_width() / window_get_height()`.
|
|
AspectRatio = window_get_width() / window_get_height();
|
|
|
|
/// @var {Real} Distance to the near clipping plane. Anything closer to the
|
|
/// camera than this will not be visible. Defaults to `0.1`.
|
|
/// @note This can be a negative value if {@link BBMOD_BaseCamera.Orthographic}
|
|
/// is enabled.
|
|
ZNear = 0.1;
|
|
|
|
/// @var {Real} Distance to the far clipping plane. Anything farther from
|
|
/// the camera than this will not be visible. Defaults to `32768`.
|
|
ZFar = 32768.0;
|
|
|
|
/// @var {Bool} Use `true` to enable orthographic projection. Defaults to
|
|
/// `false` (perspective projection).
|
|
Orthographic = false;
|
|
|
|
/// @var {Real} The width of the orthographic projection. If `undefined`,
|
|
/// then it is computed from {@link BBMOD_BaseCamera.Height} using
|
|
/// {@link BBMOD_BaseCamera.AspectRatio}. Defaults to the window's width.
|
|
/// @see BBMOD_BaseCamera.Orthographic
|
|
Width = window_get_width();
|
|
|
|
/// @var {Real} The height of the orthographic projection. If `undefined`,
|
|
/// then it is computed from {@link BBMOD_BaseCamera.Width} using
|
|
/// {@link BBMOD_BaseCamera.AspectRatio}. Defaults to `undefined`.
|
|
/// @see BBMOD_BaseCamera.Orthographic
|
|
Height = undefined;
|
|
|
|
/// @var {Bool} If `true` then the camera updates position and orientation
|
|
/// of the 3D audio listener in the {@link BBMOD_BaseCamera.update_matrices}
|
|
/// method. Defaults to `true`.
|
|
AudioListener = true;
|
|
|
|
/// @var {Array<Real>} The `view * projection` matrix.
|
|
/// @note This is updated each time {@link BBMOD_BaseCamera.update_matrices}
|
|
/// is called.
|
|
/// @readonly
|
|
ViewProjectionMatrix = matrix_build_identity();
|
|
|
|
/// @func __build_proj_mat()
|
|
///
|
|
/// @desc Builds a projection matrix based on the camera's properties.
|
|
///
|
|
/// @return {Array<Real>} The projection matrix.
|
|
///
|
|
/// @private
|
|
static __build_proj_mat = function () {
|
|
var _proj;
|
|
if (Orthographic)
|
|
{
|
|
var _width = (Width != undefined) ? Width : (Height * AspectRatio);
|
|
var _height = (Height != undefined) ? Height : (Width / AspectRatio);
|
|
_proj = matrix_build_projection_ortho(_width, -_height, ZNear, ZFar);
|
|
}
|
|
else
|
|
{
|
|
_proj = matrix_build_projection_perspective_fov(
|
|
-Fov, -AspectRatio, ZNear, ZFar);
|
|
}
|
|
return _proj;
|
|
};
|
|
|
|
/// @func update_matrices()
|
|
///
|
|
/// @desc Recomputes camera's view and projection matrices.
|
|
///
|
|
/// @return {Struct.BBMOD_BaseCamera} Returns `self`.
|
|
///
|
|
/// @note This is called automatically in the {@link BBMOD_BaseCamera.update}
|
|
/// method, so you do not need to call this unless you modify
|
|
/// {@link BBMOD_BaseCamera.Position} or {@link BBMOD_BaseCamera.Target}
|
|
/// after the `update` method.
|
|
///
|
|
/// @example
|
|
/// ```gml
|
|
/// /// @desc Step event
|
|
/// camera.set_mouselook(true);
|
|
/// camera.update(delta_time);
|
|
/// if (camera.Position.Z < 0.0)
|
|
/// {
|
|
/// camera.Position.Z = 0.0;
|
|
/// }
|
|
/// camera.update_matrices();
|
|
/// ```
|
|
static update_matrices = function () {
|
|
gml_pragma("forceinline");
|
|
|
|
var _view = matrix_build_lookat(
|
|
Position.X, Position.Y, Position.Z,
|
|
Target.X, Target.Y, Target.Z,
|
|
Up.X, Up.Y, Up.Z);
|
|
camera_set_view_mat(Raw, _view);
|
|
|
|
var _proj = __build_proj_mat();
|
|
camera_set_proj_mat(Raw, _proj);
|
|
|
|
// Note: Using _view and _proj mat straight away leads into a weird result...
|
|
ViewProjectionMatrix = matrix_multiply(
|
|
get_view_mat(),
|
|
get_proj_mat());
|
|
|
|
if (AudioListener)
|
|
{
|
|
audio_listener_position(Position.X, Position.Y, Position.Z);
|
|
audio_listener_orientation(
|
|
Target.X, Target.Y, Target.Z,
|
|
Up.X, Up.Y, Up.Z);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
/// @func update(_deltaTime)
|
|
///
|
|
/// @desc Updates camera's matrices.
|
|
///
|
|
/// @param {Real} _deltaTime How much time has passed since the last frame
|
|
/// (in microseconds).
|
|
///
|
|
/// @return {Struct.BBMOD_BaseCamera} Returns `self`.
|
|
static update = function (_deltaTime) {
|
|
update_matrices();
|
|
return self;
|
|
};
|
|
|
|
/// @func get_view_mat()
|
|
///
|
|
/// @desc Retrieves camera's view matrix.
|
|
///
|
|
/// @return {Array<Real>} The view matrix.
|
|
static get_view_mat = function () {
|
|
gml_pragma("forceinline");
|
|
|
|
if (os_browser == browser_not_a_browser)
|
|
{
|
|
// This returns a struct in HTML5 for some reason...
|
|
return camera_get_view_mat(Raw);
|
|
}
|
|
|
|
var _view = matrix_get(matrix_view);
|
|
var _proj = matrix_get(matrix_projection);
|
|
camera_apply(Raw);
|
|
var _retval = matrix_get(matrix_view);
|
|
matrix_set(matrix_view, _view);
|
|
matrix_set(matrix_projection, _proj);
|
|
return _retval;
|
|
};
|
|
|
|
/// @func get_proj_mat()
|
|
///
|
|
/// @desc Retrieves camera's projection matrix.
|
|
///
|
|
/// @return {Array<Real>} The projection matrix.
|
|
static get_proj_mat = function () {
|
|
gml_pragma("forceinline");
|
|
|
|
if (os_browser == browser_not_a_browser)
|
|
{
|
|
// This returns a struct in HTML5 for some reason...
|
|
return camera_get_proj_mat(Raw);
|
|
}
|
|
|
|
var _view = matrix_get(matrix_view);
|
|
var _proj = matrix_get(matrix_projection);
|
|
camera_apply(Raw);
|
|
var _retval = matrix_get(matrix_projection);
|
|
matrix_set(matrix_view, _view);
|
|
matrix_set(matrix_projection, _proj);
|
|
return _retval;
|
|
};
|
|
|
|
/// @func get_right()
|
|
///
|
|
/// @desc Retrieves a vector pointing right relative to the camera's
|
|
/// direction.
|
|
///
|
|
/// @return {Struct.BBMOD_Vec3} The right vector.
|
|
static get_right = function () {
|
|
gml_pragma("forceinline");
|
|
var _view = get_view_mat();
|
|
return new BBMOD_Vec3(
|
|
_view[0],
|
|
_view[4],
|
|
_view[8]
|
|
);
|
|
};
|
|
|
|
/// @func get_up()
|
|
///
|
|
/// @desc Retrieves a vector pointing up relative to the camera's
|
|
/// direction.
|
|
///
|
|
/// @return {Struct.BBMOD_Vec3} The up vector.
|
|
static get_up = function () {
|
|
gml_pragma("forceinline");
|
|
var _view = get_view_mat();
|
|
return new BBMOD_Vec3(
|
|
_view[1],
|
|
_view[5],
|
|
_view[9]
|
|
);
|
|
};
|
|
|
|
/// @func get_forward()
|
|
///
|
|
/// @desc Retrieves a vector pointing forward in the camera's direction.
|
|
///
|
|
/// @return {Struct.BBMOD_Vec3} The forward vector.
|
|
static get_forward = function () {
|
|
gml_pragma("forceinline");
|
|
var _view = get_view_mat();
|
|
return new BBMOD_Vec3(
|
|
_view[2],
|
|
_view[6],
|
|
_view[10]
|
|
);
|
|
};
|
|
|
|
/// @func world_to_screen(_position[, _screenWidth[, _screenHeight]])
|
|
///
|
|
/// @desc Computes screen-space position of a point in world-space.
|
|
///
|
|
/// @param {Struct.BBMOD_Vec3} _position The world-space position.
|
|
/// @param {Real} [_screenWidth] The width of the screen. If `undefined`, it
|
|
/// is retrieved using `window_get_width`.
|
|
/// @param {Real} [_screenHeight] The height of the screen. If `undefined`,
|
|
/// it is retrieved using `window_get_height`.
|
|
///
|
|
/// @return {Struct.BBMOD_Vec4} The screen-space position or `undefined` if
|
|
/// the point is outside of the screen.
|
|
///
|
|
/// @note This requires {@link BBMOD_BaseCamera.ViewProjectionMatrix}, so you
|
|
/// should use this *after* {@link BBMOD_BaseCamera.update_matrices} (or
|
|
/// {@link BBMOD_BaseCamera.update}) is called!
|
|
static world_to_screen = function (_position, _screenWidth=undefined, _screenHeight=undefined) {
|
|
gml_pragma("forceinline");
|
|
_screenWidth ??= window_get_width();
|
|
_screenHeight ??= window_get_height();
|
|
var _screenPos = new BBMOD_Vec4(_position.X, _position.Y, _position.Z, 1.0)
|
|
.Transform(ViewProjectionMatrix);
|
|
if (_screenPos.Z < 0.0)
|
|
{
|
|
return undefined;
|
|
}
|
|
_screenPos = _screenPos.Scale(1.0 / _screenPos.W);
|
|
_screenPos.X = ((_screenPos.X * 0.5) + 0.5) * _screenWidth;
|
|
_screenPos.Y = (1.0 - ((_screenPos.Y * 0.5) + 0.5)) * _screenHeight;
|
|
return _screenPos;
|
|
};
|
|
|
|
/// @func screen_point_to_vec3(_vector[, _renderer])
|
|
///
|
|
/// @desc Unprojects a position on the screen into a direction in world-space.
|
|
///
|
|
/// @param {Struct.BBMOD_Vector2} _vector The position on the screen.
|
|
/// @param {Struct.BBMOD_Renderer} [_renderer] A renderer or `undefined`.
|
|
///
|
|
/// @return {Struct.BBMOD_Vec3} The world-space direction.
|
|
static screen_point_to_vec3 = function (_vector, _renderer=undefined) {
|
|
var _forward = get_forward();
|
|
var _up = get_up();
|
|
var _right = get_right();
|
|
var _tFov = dtan(Fov * 0.5);
|
|
_up = _up.Scale(_tFov);
|
|
_right = _right.Scale(_tFov * AspectRatio);
|
|
var _screenWidth = _renderer ? _renderer.get_width() : window_get_width();
|
|
var _screenHeight = _renderer ? _renderer.get_height() : window_get_height();
|
|
var _screenX = _vector.X - (_renderer ? _renderer.X : 0);
|
|
var _screenY = _vector.Y - (_renderer ? _renderer.Y : 0);
|
|
var _ray = _forward.Add(_up.Scale(1.0 - 2.0 * (_screenY / _screenHeight))
|
|
.Add(_right.Scale(2.0 * (_screenX / _screenWidth) - 1.0)));
|
|
return _ray.Normalize();
|
|
};
|
|
|
|
/// @func apply()
|
|
///
|
|
/// @desc Applies the camera.
|
|
///
|
|
/// @return {Struct.BBMOD_BaseCamera} Returns `self`.
|
|
///
|
|
/// @example
|
|
/// Following code renders a model from the camera's view.
|
|
/// ```gml
|
|
/// camera.apply();
|
|
/// bbmod_material_reset();
|
|
/// model.submit();
|
|
/// bbmod_material_reset();
|
|
/// ```
|
|
///
|
|
/// @note This also overrides the camera position and exposure passed to
|
|
/// shaders using {@link bbmod_camera_set_position} and
|
|
/// {@link bbmod_camera_set_exposure} respectively!
|
|
static apply = function () {
|
|
gml_pragma("forceinline");
|
|
global.__bbmodCameraCurrent = self;
|
|
camera_apply(Raw);
|
|
bbmod_camera_set_position(Position.Clone());
|
|
bbmod_camera_set_zfar(ZFar);
|
|
bbmod_camera_set_exposure(Exposure);
|
|
return self;
|
|
};
|
|
|
|
static destroy = function () {
|
|
Class_destroy();
|
|
camera_destroy(Raw);
|
|
if (global.__bbmodCameraCurrent == self)
|
|
{
|
|
global.__bbmodCameraCurrent = undefined;
|
|
}
|
|
return undefined;
|
|
};
|
|
}
|