From 231d59b7615be32d845bc9fb6ef3fe9e06e11c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Do=CC=88ring?= Date: Wed, 24 Apr 2024 00:58:50 +0200 Subject: [PATCH 1/2] Basics for frustum culling --- src/Geo/Frustum.php | 101 ++++++++++++++++++ src/Geo/Plane.php | 18 ++++ src/Graphics/Camera.php | 27 +++-- src/Graphics/Rendering/Pass/CameraData.php | 22 ++++ .../Rendering/Renderer/Debug3DRenderer.php | 90 ++++++++++++++++ src/Quickstart/QuickstartApp.php | 3 +- src/System/VISUCameraSystem.php | 9 +- 7 files changed, 261 insertions(+), 9 deletions(-) create mode 100644 src/Geo/Frustum.php create mode 100644 src/Geo/Plane.php diff --git a/src/Geo/Frustum.php b/src/Geo/Frustum.php new file mode 100644 index 0000000..adfb904 --- /dev/null +++ b/src/Geo/Frustum.php @@ -0,0 +1,101 @@ +planes[] = $plane; + } + } + + /** + * Returns boolean if a given sphere is visible in the frustum + */ + public function isSphereInView(Vec3 $center, float $radius): bool + { + foreach ($this->planes as $plane) { + $distance = Vec3::dot($plane->normal, $center) + $plane->distance; + if ($distance < -$radius) { + return false; + } + } + + return true; + } +} diff --git a/src/Geo/Plane.php b/src/Geo/Plane.php new file mode 100644 index 0000000..d16d609 --- /dev/null +++ b/src/Geo/Plane.php @@ -0,0 +1,18 @@ +normal->length(); + $this->normal = $this->normal / $mag; + $this->distance /= $mag; + } +} \ No newline at end of file diff --git a/src/Graphics/Camera.php b/src/Graphics/Camera.php index cb963d4..8f6943a 100644 --- a/src/Graphics/Camera.php +++ b/src/Graphics/Camera.php @@ -2,12 +2,14 @@ namespace VISU\Graphics; +use GL\Math\GLM; use GL\Math\Mat4; use GL\Math\Quat; use GL\Math\Vec2; use GL\Math\Vec3; use GL\Math\Vec4; use GL\VectorGraphics\VGContext; +use VISU\Geo\Frustum; use VISU\Geo\Ray; use VISU\Geo\Transform; @@ -225,23 +227,34 @@ public function getViewport(RenderTarget $renderTarget) : Viewport } /** - * Calculates and returns the cameras view matrix. The matrix is inverted on the fly - * so cache the result if you need to use it multiple times. + * Returns the model matrix of the camera + * This is basically the inverse of the cameras view matrix. * - * @param float $deltaTime The delta time since the last frame - * @return Mat4 A copy of the calculated view matrix + * @return Mat4 The model matrix of the camera */ - public function getViewMatrix(float $deltaTime = 0.0) : Mat4 + public function getModelMatrix(float $deltaTime = 0.0) : Mat4 { if ($this->allowInterpolation) { $this->interpolationTransformAlloc->position = Vec3::mix($this->lfCameraPos, $this->transform->position, $deltaTime); $this->interpolationTransformAlloc->orientation = Quat::slerp($this->lfCameraRot, $this->transform->orientation, $deltaTime); $this->interpolationTransformAlloc->markDirty(); - return Mat4::inverted($this->interpolationTransformAlloc->getLocalMatrix()); + return $this->interpolationTransformAlloc->getLocalMatrix(); } - return Mat4::inverted($this->transform->getLocalMatrix()); + return $this->transform->getLocalMatrix(); + } + + /** + * Calculates and returns the cameras view matrix. The matrix is inverted on the fly + * so cache the result if you need to use it multiple times. + * + * @param float $deltaTime The delta time since the last frame + * @return Mat4 A copy of the calculated view matrix + */ + public function getViewMatrix(float $deltaTime = 0.0) : Mat4 + { + return Mat4::inverted($this->getModelMatrix($deltaTime)); } /** diff --git a/src/Graphics/Rendering/Pass/CameraData.php b/src/Graphics/Rendering/Pass/CameraData.php index b9f3cde..9ff57bf 100644 --- a/src/Graphics/Rendering/Pass/CameraData.php +++ b/src/Graphics/Rendering/Pass/CameraData.php @@ -4,6 +4,7 @@ use GL\Math\Mat4; use GL\Math\Vec2; +use VISU\Geo\Frustum; use VISU\Graphics\Camera; use VISU\Graphics\Rendering\RenderResource; use VISU\Graphics\Viewport; @@ -33,6 +34,21 @@ class CameraData */ public readonly Mat4 $view; + /** + * Projection view matrix + */ + public readonly Mat4 $projectionView; + + /** + * Inverse projection view matrix + */ + public readonly Mat4 $inverseProjectionView; + + /** + * Frustum of the current frame + */ + public readonly Frustum $frustum; + /** * Compensation / alpha value for the current frame */ @@ -71,6 +87,9 @@ public function __construct( Camera $renderCamera, Mat4 $projection, Mat4 $view, + Mat4 $projectionView, + Mat4 $inverseProjectionView, + Frustum $frustum, float $compensation, int $resolutionX, int $resolutionY, @@ -83,6 +102,9 @@ public function __construct( $this->renderCamera = $renderCamera; $this->projection = $projection; $this->view = $view; + $this->projectionView = $projectionView; + $this->inverseProjectionView = $inverseProjectionView; + $this->frustum = $frustum; $this->compensation = $compensation; $this->resolutionX = $resolutionX; $this->resolutionY = $resolutionY; diff --git a/src/Graphics/Rendering/Renderer/Debug3DRenderer.php b/src/Graphics/Rendering/Renderer/Debug3DRenderer.php index 359606f..a9a437d 100644 --- a/src/Graphics/Rendering/Renderer/Debug3DRenderer.php +++ b/src/Graphics/Rendering/Renderer/Debug3DRenderer.php @@ -3,8 +3,11 @@ namespace VISU\Graphics\Rendering\Renderer; use GL\Buffer\FloatBuffer; +use GL\Math\Mat4; use GL\Math\Vec2; use GL\Math\Vec3; +use GL\Math\Vec4; +use VISU\Geo\Frustum; use VISU\Graphics\GLState; use VISU\Graphics\Rendering\Pass\CallbackPass; use VISU\Graphics\Rendering\Pass\CameraData; @@ -156,6 +159,30 @@ public static function bezier(Vec3 $origin, Vec3 $p0, Vec3 $destination, Vec3 $c static::getGlobalInstance()->addBezierCurve($origin, $p0, $destination, $color, $segments); } + /** + * Draws a line representation of the given frustum + * + * @param Mat4 $ivp The inverse view projection matrix + * @param Vec3 $color The color of the frustum + */ + public static function frustum(Mat4 $ivp, Vec3 $color) : void + { + static::getGlobalInstance()->addFrustum($ivp, $color); + } + + /** + * Draws a plane in 3D space + * + * @param Vec3 $origin The origin of the plane + * @param Vec3 $normal The normal of the plane + * @param float $size The size of the plane + * @param Vec3 $color The color of the plane + */ + public static function plane(Vec3 $origin, Vec3 $normal, float $size, Vec3 $color) : void + { + static::getGlobalInstance()->addPlane($origin, $normal, $size, $color); + } + /** * Constructor * @@ -398,4 +425,67 @@ public function addBezierCurve( $this->addLine($last, $destination, static::$colorGreen); $this->addCross($p0, static::$colorMagenta); } + + /** + * Draws a Frustum + * + * @param Mat4 $mat The view projection matrix to build the frustum from + * @param Vec3 $color The color of the frustum + */ + public function addFrustum(Mat4 $ivp, Vec3 $color) + { + static $planes = [ + // near + new Vec4(-1, -1, -1, 1), new Vec4(1, -1, -1, 1), + new Vec4(1, 1, -1, 1), new Vec4(-1, 1, -1, 1), + + // far + new Vec4(-1, -1, 1, 1), new Vec4(1, -1, 1, 1), + new Vec4(1, 1, 1, 1), new Vec4(-1, 1, 1, 1), + ]; + + $points = []; + foreach ($planes as $plane) { + $p = $ivp * $plane; + $points[] = new Vec3($p->x / $p->w, $p->y / $p->w, $p->z / $p->w); + } + + $this->addLine($points[0], $points[1], $color); + $this->addLine($points[1], $points[2], $color); + $this->addLine($points[2], $points[3], $color); + $this->addLine($points[3], $points[0], $color); + + $this->addLine($points[4], $points[5], $color); + $this->addLine($points[5], $points[6], $color); + $this->addLine($points[6], $points[7], $color); + $this->addLine($points[7], $points[4], $color); + + $this->addLine($points[0], $points[4], $color); + $this->addLine($points[1], $points[5], $color); + $this->addLine($points[2], $points[6], $color); + $this->addLine($points[3], $points[7], $color); + } + + /** + * Draws a plane in 3D space + * + * @param Vec3 $origin The origin of the plane + * @param Vec3 $normal The normal of the plane + * @param float $size The size of the plane + * @param Vec3 $color The color of the plane + */ + public function addPlane(Vec3 $origin, Vec3 $normal, float $size, Vec3 $color) : void + { + $up = new Vec3(0, 1, 0); + + $right = Vec3::cross($normal, $up); + $right->normalize(); + $up = Vec3::cross($right, $normal); + $up->normalize(); + + $this->addLine($origin - $right * $size + $up * $size, $origin + $right * $size + $up * $size, $color); + $this->addLine($origin + $right * $size + $up * $size, $origin + $right * $size - $up * $size, $color); + $this->addLine($origin + $right * $size - $up * $size, $origin - $right * $size - $up * $size, $color); + $this->addLine($origin - $right * $size - $up * $size, $origin - $right * $size + $up * $size, $color); + } } diff --git a/src/Quickstart/QuickstartApp.php b/src/Quickstart/QuickstartApp.php index 1499390..e19c316 100644 --- a/src/Quickstart/QuickstartApp.php +++ b/src/Quickstart/QuickstartApp.php @@ -3,7 +3,7 @@ namespace VISU\Quickstart; use ClanCats\Container\Container; - +use GL\Math\Vec2; use VISU\ECS\EntityRegisty; use VISU\Graphics\GLState; use VISU\Graphics\RenderTarget; @@ -26,6 +26,7 @@ use GL\VectorGraphics\{VGContext, VGColor}; use VISU\Graphics\Rendering\Resource\RenderTargetResource; +use VISU\Graphics\Viewport; class QuickstartApp implements GameLoopDelegate { diff --git a/src/System/VISUCameraSystem.php b/src/System/VISUCameraSystem.php index 48be434..cbdb4df 100644 --- a/src/System/VISUCameraSystem.php +++ b/src/System/VISUCameraSystem.php @@ -2,10 +2,11 @@ namespace VISU\System; -use GL\Math\{GLM, Quat, Vec2, Vec3}; +use GL\Math\{GLM, Mat4, Quat, Vec2, Vec3}; use VISU\ECS\EntitiesInterface; use VISU\ECS\SystemInterface; use VISU\Exception\VISUException; +use VISU\Geo\Frustum; use VISU\Graphics\Camera; use VISU\Graphics\CameraProjectionMode; use VISU\Graphics\Rendering\Pass\CameraData; @@ -321,11 +322,17 @@ public function getCameraData(EntitiesInterface $entities, RenderTarget $renderT $viewMatrix = $camera->getViewMatrix($compensation); $projectionMatrix = $camera->getProjectionMatrix($renderTarget); + /** @var Mat4 */ + $projectionViewMatrix = $projectionMatrix * $viewMatrix; + $inverseProjectionViewMatrix = Mat4::inverted($projectionViewMatrix); return new CameraData( frameCamera: $camera, renderCamera: $camera, projection: $projectionMatrix, view: $viewMatrix, + projectionView: $projectionViewMatrix, + inverseProjectionView: $inverseProjectionViewMatrix, + frustum: Frustum::fromMat4($projectionViewMatrix), compensation: $compensation, resolutionX: $renderTarget->width(), resolutionY: $renderTarget->height(), From ac48ce776c153c75155024180dd459d82f731e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Do=CC=88ring?= Date: Thu, 6 Jun 2024 11:58:39 +0200 Subject: [PATCH 2/2] Missing functions --- src/Geo/AABB.php | 19 +++++++++++++++++++ src/System/VISUCameraSystem.php | 25 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/Geo/AABB.php b/src/Geo/AABB.php index f5fe296..2361423 100644 --- a/src/Geo/AABB.php +++ b/src/Geo/AABB.php @@ -213,4 +213,23 @@ public function intersectRayDistance(Ray $ray) : ?float return $tmin; } + + /** + * Returns an array of 8 Vec3 representing the corners of the AABB + * + * @return array + */ + public function getCorners() : array + { + return [ + new Vec3($this->min->x, $this->min->y, $this->min->z), + new Vec3($this->max->x, $this->min->y, $this->min->z), + new Vec3($this->max->x, $this->max->y, $this->min->z), + new Vec3($this->min->x, $this->max->y, $this->min->z), + new Vec3($this->min->x, $this->min->y, $this->max->z), + new Vec3($this->max->x, $this->min->y, $this->max->z), + new Vec3($this->max->x, $this->max->y, $this->max->z), + new Vec3($this->min->x, $this->max->y, $this->max->z), + ]; + } } diff --git a/src/System/VISUCameraSystem.php b/src/System/VISUCameraSystem.php index cbdb4df..3ee734a 100644 --- a/src/System/VISUCameraSystem.php +++ b/src/System/VISUCameraSystem.php @@ -305,6 +305,8 @@ public function updateVISUFlyingCamera(EntitiesInterface $entities, Camera $came $camera->transform->markDirty(); } + private ?Mat4 $frozenView = null; + /** * Create a camera data structure for the given render target. * @@ -325,6 +327,29 @@ public function getCameraData(EntitiesInterface $entities, RenderTarget $renderT /** @var Mat4 */ $projectionViewMatrix = $projectionMatrix * $viewMatrix; $inverseProjectionViewMatrix = Mat4::inverted($projectionViewMatrix); + + if ($this->input->isMouseButtonPressed(MouseButton::RIGHT)) { + $this->frozenView = $viewMatrix->copy(); + } elseif ($this->input->isMouseButtonPressed(MouseButton::MIDDLE)) { + $this->frozenView = null; + } + + global $showFrustum; + if ($this->frozenView) { + // // debug + // $testView = new Mat4; + // $testView->rotate(GLM::radians(45.0), new Vec3(1.0, 0.0, 0.0)); + // $testView->rotate(GLM::radians(sin(glfwGetTime()) * 90), new Vec3(0.0, 1.0, 0.0)); + + $fakeView = $this->frozenView->copy(); + $projectionViewMatrix = $projectionMatrix * $fakeView; + $inverseProjectionViewMatrix = Mat4::inverted($projectionViewMatrix); + + $showFrustum = true; + } else { + $showFrustum = false; + } + return new CameraData( frameCamera: $camera, renderCamera: $camera,