From 765c9015ae2830d5cc454c5b37bd07fe6ed0f239 Mon Sep 17 00:00:00 2001
From: Dominic Masters <dominic@domsplace.com>
Date: Sat, 18 Mar 2023 22:58:26 -0700
Subject: [PATCH] Fixed raycast sphere.

---
 src/dawn/display/Transform.cpp                |   2 +-
 src/dawn/physics/3d/Ray3D.cpp                 |  29 +-
 .../components/physics/3d/CMakeLists.txt      |   1 +
 .../components/physics/3d/Collider3D.hpp      |   3 +-
 .../components/physics/3d/SphereCollider.cpp  |  31 ++
 .../components/physics/3d/SphereCollider.hpp  |  24 ++
 src/dawn/scene/debug/SceneDebugLine.cpp       | 328 +++++++++---------
 .../scene/components/PlayerController.cpp     |   9 +-
 src/dawnrose/scenes/HelloWorldScene.hpp       |  11 +-
 9 files changed, 249 insertions(+), 189 deletions(-)
 create mode 100644 src/dawn/scene/components/physics/3d/SphereCollider.cpp
 create mode 100644 src/dawn/scene/components/physics/3d/SphereCollider.hpp

diff --git a/src/dawn/display/Transform.cpp b/src/dawn/display/Transform.cpp
index ba1ce810..c01a9218 100644
--- a/src/dawn/display/Transform.cpp
+++ b/src/dawn/display/Transform.cpp
@@ -131,7 +131,7 @@ void Transform::setLocalTransform(glm::mat4 transform) {
 }
 
 glm::vec3 Transform::getWorldPosition() {
-  return glm::inverse(this->transformWorld)[3];
+  return this->transformWorld[3];
 }
 
 glm::mat4 Transform::getWorldTransform() {
diff --git a/src/dawn/physics/3d/Ray3D.cpp b/src/dawn/physics/3d/Ray3D.cpp
index 85d9dc53..41b22af3 100644
--- a/src/dawn/physics/3d/Ray3D.cpp
+++ b/src/dawn/physics/3d/Ray3D.cpp
@@ -14,20 +14,23 @@ bool_t Dawn::raytestSphere(
   glm::vec3 *normal,
   float_t *distance
 ) {
-  assertNotNull(hit);
-  assertNotNull(normal);
+  float_t a = glm::dot(ray.direction, ray.direction);
+  float_t b = 2.0f * glm::dot(ray.direction, ray.origin - sphere.center);
+  float_t c = glm::dot(ray.origin - sphere.center, ray.origin - sphere.center);
+  c -= sphere.radius * sphere.radius;
 
-  glm::vec3 h, n;
-  auto result = glm::intersectRaySphere(
-    ray.origin, ray.direction,
-    sphere.center, sphere.radius,
-    h, n
-  );
-  if(!result) return result;
-  *hit = h;
-  *normal = n;
-  *distance = glm::distance(ray.origin, h);
-  return result;
+  float_t dt = b * b - 4.0f * a * c;
+  if(dt < 0.0f) return false;
+
+  float_t t0 = (-b - sqrtf(dt)) / (a * 2.0f);
+  if(t0 < 0.0f) return false;
+
+  *hit = ray.origin + t0 * ray.direction;
+  *normal = glm::normalize(*hit - sphere.center);
+  *distance = t0;
+
+
+  return true;
 }
 
 bool_t Dawn::raytestTriangle(
diff --git a/src/dawn/scene/components/physics/3d/CMakeLists.txt b/src/dawn/scene/components/physics/3d/CMakeLists.txt
index d49caf35..458e0e71 100644
--- a/src/dawn/scene/components/physics/3d/CMakeLists.txt
+++ b/src/dawn/scene/components/physics/3d/CMakeLists.txt
@@ -9,4 +9,5 @@ target_sources(${DAWN_TARGET_NAME}
     Collider3D.cpp
     CubeCollider.cpp
     CapsuleCollider.cpp
+    SphereCollider.cpp
 )
\ No newline at end of file
diff --git a/src/dawn/scene/components/physics/3d/Collider3D.hpp b/src/dawn/scene/components/physics/3d/Collider3D.hpp
index d188765f..82bdaee6 100644
--- a/src/dawn/scene/components/physics/3d/Collider3D.hpp
+++ b/src/dawn/scene/components/physics/3d/Collider3D.hpp
@@ -17,7 +17,8 @@ namespace Dawn {
 
   enum Collider3DType {
     COLLIDER3D_TYPE_CUBE,
-    COLLIDER3D_TYPE_CAPSULE
+    COLLIDER3D_TYPE_CAPSULE,
+    COLLIDER3D_TYPE_SPHERE
   };
 
   class Collider3D : public SceneItemComponent {
diff --git a/src/dawn/scene/components/physics/3d/SphereCollider.cpp b/src/dawn/scene/components/physics/3d/SphereCollider.cpp
new file mode 100644
index 00000000..271108dd
--- /dev/null
+++ b/src/dawn/scene/components/physics/3d/SphereCollider.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#include "SphereCollider.hpp"
+
+using namespace Dawn;
+
+SphereCollider::SphereCollider(SceneItem *item) : Collider3D(item) {
+
+}
+
+enum Collider3DType SphereCollider::getColliderType() {
+  return COLLIDER3D_TYPE_SPHERE;
+}
+
+bool_t SphereCollider::performRaycast(
+  struct Collider3DRayResult *result,
+  struct Ray3D ray
+) {
+  assertNotNull(result);
+
+  return raytestSphere(
+    ray,
+    { .center = transform->getLocalPosition(), .radius = this->radius },
+    &result->point,
+    &result->normal,
+    &result->distance
+  );
+}
\ No newline at end of file
diff --git a/src/dawn/scene/components/physics/3d/SphereCollider.hpp b/src/dawn/scene/components/physics/3d/SphereCollider.hpp
new file mode 100644
index 00000000..31f119fb
--- /dev/null
+++ b/src/dawn/scene/components/physics/3d/SphereCollider.hpp
@@ -0,0 +1,24 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "Collider3D.hpp"
+
+namespace Dawn {
+  class SphereCollider : public Collider3D {
+    protected:
+      bool_t performRaycast(
+        struct Collider3DRayResult *result,
+        struct Ray3D ray
+      ) override;
+
+    public:
+      float_t radius = 1.0f;
+      
+      SphereCollider(SceneItem *item);
+
+      enum Collider3DType getColliderType() override;
+  };
+}
\ No newline at end of file
diff --git a/src/dawn/scene/debug/SceneDebugLine.cpp b/src/dawn/scene/debug/SceneDebugLine.cpp
index b543d6a4..3cbc2416 100644
--- a/src/dawn/scene/debug/SceneDebugLine.cpp
+++ b/src/dawn/scene/debug/SceneDebugLine.cpp
@@ -1,165 +1,165 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#include "SceneDebugLine.hpp"
-#include "display/shader/SimpleTexturedShader.hpp"
-#include "scene/components/display/Camera.hpp"
-#include "scene/Scene.hpp"
-#include "scene/components/physics/3d/Collider3D.hpp"
-#include "scene/components/physics/3d/CubeCollider.hpp"
-
-using namespace Dawn;
-
-struct ShaderPassItem SceneDebugLine::createShaderItem(
-  Mesh *mesh,
-  int32_t *lineIndex,
-  Camera *camera,
-  SimpleTexturedShader *shader
-) {
-  struct ShaderPassItem item;
-  item.priority = this->priority;
-
-  item.shaderProgram = &shader->program;
-  item.colorValues[shader->program.paramColor] = this->color;
-  item.matrixValues[shader->program.paramModel] = this->transform;
-  item.matrixValues[shader->program.paramView] = camera->transform->getWorldTransform();
-  item.matrixValues[shader->program.paramProjection] = camera->getProjection();
-  item.boolValues[shader->program.paramHasTexture] = false;
-
-  auto i = *lineIndex;
-  item.mesh = mesh;
-  item.drawMode = MESH_DRAW_MODE_LINES;
-  item.renderFlags = 0x00;
-  item.start = i * SCENE_DEBUG_LINE_INDICE_COUNT;
-  item.count = SCENE_DEBUG_LINE_INDICE_COUNT;
-
-  glm::vec3 positions[SCENE_DEBUG_LINE_VERTICE_COUNT] = { this->v0, this->v1 };
-  mesh->bufferPositions(
-    i * SCENE_DEBUG_LINE_VERTICE_COUNT,
-    positions,
-    SCENE_DEBUG_LINE_VERTICE_COUNT
-  );
-
-  meshindice_t indices[SCENE_DEBUG_LINE_INDICE_COUNT] = {
-    i * SCENE_DEBUG_LINE_VERTICE_COUNT,
-    (i*SCENE_DEBUG_LINE_VERTICE_COUNT) + 1
-  };
-  mesh->bufferIndices(
-    i * SCENE_DEBUG_LINE_INDICE_COUNT,
-    indices,
-    SCENE_DEBUG_LINE_INDICE_COUNT
-  );
-  return item;
-}
-
-// Scene Implementations (done here to keep things cleaner in scene)
-void Scene::debugLine(struct SceneDebugLine line) {
-  this->debugLines.push_back(line);
-}
-
-void Scene::debugRay(struct SceneDebugRay ray) {
-  this->debugLine((struct SceneDebugLine){
-    .v0 = ray.start,
-    .v1 = ray.start + ray.direction,
-    .color = ray.color
-  });
-}
-
-void Scene::debugCube(struct SceneDebugCube cube) {
-  auto min = cube.min;
-  auto max = cube.max;
-
-  struct SceneDebugLine line;
-  line.color = cube.color;
-  line.transform = cube.transform;
-
-  // Bottom Face
-  line.v0 = glm::vec3(min.x, min.y, min.z), line.v1 = glm::vec3(max.x, min.y, min.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(min.x, min.y, min.z), line.v1 = glm::vec3(min.x, min.y, max.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(max.x, min.y, min.z), line.v1 = glm::vec3(max.x, min.y, max.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(min.x, min.y, max.z), line.v1 = glm::vec3(max.x, min.y, max.z);
-  this->debugLine(line);
-
-  // Top Face
-  line.v0 = glm::vec3(min.x, max.y, min.z), line.v1 = glm::vec3(max.x, max.y, min.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(min.x, max.y, min.z), line.v1 = glm::vec3(min.x, max.y, max.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(max.x, max.y, min.z), line.v1 = glm::vec3(max.x, max.y, max.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(min.x, max.y, max.z), line.v1 = glm::vec3(max.x, max.y, max.z);
-  this->debugLine(line);
-
-  // Rails
-  line.v0 = glm::vec3(min.x, min.y, min.z), line.v1 = glm::vec3(min.x, max.y, min.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(max.x, min.y, min.z), line.v1 = glm::vec3(max.x, max.y, min.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(min.x, min.y, max.z), line.v1 = glm::vec3(min.x, max.y, max.z);
-  this->debugLine(line);
-  line.v0 = glm::vec3(max.x, min.y, max.z), line.v1 = glm::vec3(max.x, max.y, max.z);
-  this->debugLine(line);
-}
-
-void Scene::debugOrigin() {
-  struct SceneDebugLine line;
-  line.v1 = glm::vec3(1, 0, 0), line.color = COLOR_RED;
-  this->debugLine(line);
-  line.v1 = glm::vec3(0, 1, 0), line.color = COLOR_GREEN;
-  this->debugLine(line);
-  line.v1 = glm::vec3(0, 0, 1), line.color = COLOR_BLUE;
-  this->debugLine(line);
-}
-
-void Scene::debugGrid() {
-  float_t s = 10.0f;
-  float_t t = 1.0f;
-
-  struct SceneDebugLine line;
-  line.color = COLOR_LIGHT_GREY;
-  line.color.a = 0.2f;
-
-  line.v0 = glm::vec3(-s, 0, -s), line.v1 = glm::vec3(s, 0, -s);
-  this->debugLine(line);
-  line.v0 = glm::vec3(-s, 0, -s), line.v1 = glm::vec3(-s, 0, s);
-  this->debugLine(line);
-  line.v0 = glm::vec3(s, 0, -s), line.v1 = glm::vec3(s, 0, s);
-  this->debugLine(line);
-  line.v0 = glm::vec3(-s, 0, s), line.v1 = glm::vec3(s, 0, s);
-  this->debugLine(line);
-
-  for(float_t x = -s+t; x < s; x += t) {
-    line.v0 = glm::vec3(x, 0, -s), line.v1 = glm::vec3(x, 0, s);
-    this->debugLine(line);
-  }
-
-  for(float_t z = -s+t; z < s; z += t) {
-    line.v0 = glm::vec3(-s, 0, z), line.v1 = glm::vec3(s, 0, z);
-    this->debugLine(line);
-  }
-}
-
-void Scene::debugHitboxes() {
-  auto colliders = this->findComponents<Collider3D>();
-  auto itColliders = colliders.begin();
-  while(itColliders != colliders.end()) {
-    auto c = *itColliders;
-    switch(c->getColliderType()) {
-      case COLLIDER3D_TYPE_CUBE:
-        auto asCube = dynamic_cast<CubeCollider*>(c);
-        this->debugCube((struct SceneDebugCube){
-          .min = asCube->min,
-          .max = asCube->max,
-          .color = COLOR_BLUE,
-          .transform = asCube->transform->getWorldTransform()
-        });
-        break;
-    }
-    ++itColliders;
-  }
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#include "SceneDebugLine.hpp"
+#include "display/shader/SimpleTexturedShader.hpp"
+#include "scene/components/display/Camera.hpp"
+#include "scene/Scene.hpp"
+#include "scene/components/physics/3d/Collider3D.hpp"
+#include "scene/components/physics/3d/CubeCollider.hpp"
+
+using namespace Dawn;
+
+struct ShaderPassItem SceneDebugLine::createShaderItem(
+  Mesh *mesh,
+  int32_t *lineIndex,
+  Camera *camera,
+  SimpleTexturedShader *shader
+) {
+  struct ShaderPassItem item;
+  item.priority = this->priority;
+
+  item.shaderProgram = &shader->program;
+  item.colorValues[shader->program.paramColor] = this->color;
+  item.matrixValues[shader->program.paramModel] = this->transform;
+  item.matrixValues[shader->program.paramView] = camera->transform->getWorldTransform();
+  item.matrixValues[shader->program.paramProjection] = camera->getProjection();
+  item.boolValues[shader->program.paramHasTexture] = false;
+
+  auto i = *lineIndex;
+  item.mesh = mesh;
+  item.drawMode = MESH_DRAW_MODE_LINES;
+  item.renderFlags = RENDER_MANAGER_RENDER_FLAG_DEPTH_TEST;
+  item.start = i * SCENE_DEBUG_LINE_INDICE_COUNT;
+  item.count = SCENE_DEBUG_LINE_INDICE_COUNT;
+
+  glm::vec3 positions[SCENE_DEBUG_LINE_VERTICE_COUNT] = { this->v0, this->v1 };
+  mesh->bufferPositions(
+    i * SCENE_DEBUG_LINE_VERTICE_COUNT,
+    positions,
+    SCENE_DEBUG_LINE_VERTICE_COUNT
+  );
+
+  meshindice_t indices[SCENE_DEBUG_LINE_INDICE_COUNT] = {
+    i * SCENE_DEBUG_LINE_VERTICE_COUNT,
+    (i*SCENE_DEBUG_LINE_VERTICE_COUNT) + 1
+  };
+  mesh->bufferIndices(
+    i * SCENE_DEBUG_LINE_INDICE_COUNT,
+    indices,
+    SCENE_DEBUG_LINE_INDICE_COUNT
+  );
+  return item;
+}
+
+// Scene Implementations (done here to keep things cleaner in scene)
+void Scene::debugLine(struct SceneDebugLine line) {
+  this->debugLines.push_back(line);
+}
+
+void Scene::debugRay(struct SceneDebugRay ray) {
+  this->debugLine((struct SceneDebugLine){
+    .v0 = ray.start,
+    .v1 = ray.start + ray.direction,
+    .color = ray.color
+  });
+}
+
+void Scene::debugCube(struct SceneDebugCube cube) {
+  auto min = cube.min;
+  auto max = cube.max;
+
+  struct SceneDebugLine line;
+  line.color = cube.color;
+  line.transform = cube.transform;
+
+  // Bottom Face
+  line.v0 = glm::vec3(min.x, min.y, min.z), line.v1 = glm::vec3(max.x, min.y, min.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(min.x, min.y, min.z), line.v1 = glm::vec3(min.x, min.y, max.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(max.x, min.y, min.z), line.v1 = glm::vec3(max.x, min.y, max.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(min.x, min.y, max.z), line.v1 = glm::vec3(max.x, min.y, max.z);
+  this->debugLine(line);
+
+  // Top Face
+  line.v0 = glm::vec3(min.x, max.y, min.z), line.v1 = glm::vec3(max.x, max.y, min.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(min.x, max.y, min.z), line.v1 = glm::vec3(min.x, max.y, max.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(max.x, max.y, min.z), line.v1 = glm::vec3(max.x, max.y, max.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(min.x, max.y, max.z), line.v1 = glm::vec3(max.x, max.y, max.z);
+  this->debugLine(line);
+
+  // Rails
+  line.v0 = glm::vec3(min.x, min.y, min.z), line.v1 = glm::vec3(min.x, max.y, min.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(max.x, min.y, min.z), line.v1 = glm::vec3(max.x, max.y, min.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(min.x, min.y, max.z), line.v1 = glm::vec3(min.x, max.y, max.z);
+  this->debugLine(line);
+  line.v0 = glm::vec3(max.x, min.y, max.z), line.v1 = glm::vec3(max.x, max.y, max.z);
+  this->debugLine(line);
+}
+
+void Scene::debugOrigin() {
+  struct SceneDebugLine line;
+  line.v1 = glm::vec3(1, 0, 0), line.color = COLOR_RED;
+  this->debugLine(line);
+  line.v1 = glm::vec3(0, 1, 0), line.color = COLOR_GREEN;
+  this->debugLine(line);
+  line.v1 = glm::vec3(0, 0, 1), line.color = COLOR_BLUE;
+  this->debugLine(line);
+}
+
+void Scene::debugGrid() {
+  float_t s = 10.0f;
+  float_t t = 1.0f;
+
+  struct SceneDebugLine line;
+  line.color = COLOR_LIGHT_GREY;
+  line.color.a = 0.2f;
+
+  line.v0 = glm::vec3(-s, 0, -s), line.v1 = glm::vec3(s, 0, -s);
+  this->debugLine(line);
+  line.v0 = glm::vec3(-s, 0, -s), line.v1 = glm::vec3(-s, 0, s);
+  this->debugLine(line);
+  line.v0 = glm::vec3(s, 0, -s), line.v1 = glm::vec3(s, 0, s);
+  this->debugLine(line);
+  line.v0 = glm::vec3(-s, 0, s), line.v1 = glm::vec3(s, 0, s);
+  this->debugLine(line);
+
+  for(float_t x = -s+t; x < s; x += t) {
+    line.v0 = glm::vec3(x, 0, -s), line.v1 = glm::vec3(x, 0, s);
+    this->debugLine(line);
+  }
+
+  for(float_t z = -s+t; z < s; z += t) {
+    line.v0 = glm::vec3(-s, 0, z), line.v1 = glm::vec3(s, 0, z);
+    this->debugLine(line);
+  }
+}
+
+void Scene::debugHitboxes() {
+  auto colliders = this->findComponents<Collider3D>();
+  auto itColliders = colliders.begin();
+  while(itColliders != colliders.end()) {
+    auto c = *itColliders;
+    switch(c->getColliderType()) {
+      case COLLIDER3D_TYPE_CUBE:
+        auto asCube = dynamic_cast<CubeCollider*>(c);
+        this->debugCube((struct SceneDebugCube){
+          .min = asCube->min,
+          .max = asCube->max,
+          .color = COLOR_BLUE,
+          .transform = asCube->transform->getWorldTransform()
+        });
+        break;
+    }
+    ++itColliders;
+  }
 }
\ No newline at end of file
diff --git a/src/dawnrose/scene/components/PlayerController.cpp b/src/dawnrose/scene/components/PlayerController.cpp
index 75d47749..90adaa18 100644
--- a/src/dawnrose/scene/components/PlayerController.cpp
+++ b/src/dawnrose/scene/components/PlayerController.cpp
@@ -39,12 +39,13 @@ void PlayerController::onStart() {
 
 
     // tEST
-    auto collider = item->getComponent<CapsuleCollider>();
+    auto collider = item->getComponent<Collider3D>();
+    assertNotNull(collider);
     Collider3DRayResult result;
 
     struct Ray3D ray;
-    ray.origin = glm::vec3(0, 0.5f, 1);
-    ray.direction = glm::vec3(10, 0, 0);
+    ray.origin = glm::vec3(0, 0.0f, 5);
+    ray.direction = glm::vec3(0, 0.0f, -4);
 
     struct Color color = COLOR_MAGENTA;
 
@@ -54,7 +55,7 @@ void PlayerController::onStart() {
 
       getScene()->debugRay({
         .start = result.point,
-        .direction = (result.normal * 100.0f),
+        .direction = result.normal,
         .color = COLOR_GREEN
       });
     }
diff --git a/src/dawnrose/scenes/HelloWorldScene.hpp b/src/dawnrose/scenes/HelloWorldScene.hpp
index ca4eaa9b..a0eb49c3 100644
--- a/src/dawnrose/scenes/HelloWorldScene.hpp
+++ b/src/dawnrose/scenes/HelloWorldScene.hpp
@@ -7,14 +7,14 @@
 #include "scene/Scene.hpp"
 #include "scene/components/PlayerController.hpp"
 #include "scene/components/GameCamera.hpp"
-#include "scene/components/physics/3d/CubeCollider.hpp"
-#include "scene/components/physics/3d/CapsuleCollider.hpp"
 #include "scene/components/GameCamera.hpp"
 #include "scene/components/display/MeshRenderer.hpp"
 #include "scene/components/display/MeshHost.hpp"
 #include "scene/components/display/material/SimpleTexturedMaterial.hpp"
-#include "display/mesh/CapsuleMesh.hpp"
 #include "scene/components/example/ExampleSpin.hpp"
+#include "scene/components/physics/3d/SphereCollider.hpp"
+#include "scene/components/physics/3d/CubeCollider.hpp"
+#include "display/mesh/SphereMesh.hpp"
 
 namespace Dawn {
   class HelloWorldScene : public Scene {
@@ -25,12 +25,11 @@ namespace Dawn {
 
         auto playerItem = this->createSceneItem();
         auto player = playerItem->addComponent<PlayerController>();
-        auto hitbox = playerItem->addComponent<CapsuleCollider>();
         playerItem->addComponent<MeshRenderer>();
         auto meshHost = playerItem->addComponent<MeshHost>();
         playerItem->addComponent<SimpleTexturedMaterial>();
-
-        CapsuleMesh::create(&meshHost->mesh, hitbox->radius, hitbox->height);
+        auto hitbox = playerItem->addComponent<SphereCollider>();
+        SphereMesh::createSphere(&meshHost->mesh, hitbox->radius, 32, 32);
 
         auto wall = this->createSceneItem();
         auto wallHitbox = wall->addComponent<CubeCollider>();