Przeglądaj źródła

Add ray casting

Miloslav Ciz 3 lat temu
rodzic
commit
619e0443ae
5 zmienionych plików z 180 dodań i 57 usunięć
  1. 2 2
      programs/cubes.c
  2. 23 2
      programs/helper.h
  3. 5 5
      programs/player.c
  4. 4 36
      programs/stack.c
  5. 146 12
      tinyphysicsengine.h

+ 2 - 2
programs/cubes.c

@@ -100,7 +100,7 @@ tpe_world.bodies[tpe_world.bodyCount - 1].friction = 256;
     TPE_worldStep(&tpe_world);
 
     helper_set3dColor(100,100,100);
-    helper_draw3dCubeInside(TPE_vec3(0,ROOM_SIZE / 4,0),TPE_vec3(ROOM_SIZE,ROOM_SIZE / 2,ROOM_SIZE),TPE_vec3(0,0,0));
+    helper_draw3dBoxInside(TPE_vec3(0,ROOM_SIZE / 4,0),TPE_vec3(ROOM_SIZE,ROOM_SIZE / 2,ROOM_SIZE),TPE_vec3(0,0,0));
 
     helper_set3dColor(200,10,10);
 
@@ -111,7 +111,7 @@ tpe_world.bodies[tpe_world.bodyCount - 1].friction = 256;
       if (!(tpe_world.bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED))
         updateOrientPos(i);
 
-      helper_draw3dCube(cubePositions[i],TPE_vec3(CUBE_SIZE,CUBE_SIZE,CUBE_SIZE),cubeOrientations[i]);
+      helper_draw3dBox(cubePositions[i],TPE_vec3(CUBE_SIZE,CUBE_SIZE,CUBE_SIZE),cubeOrientations[i]);
     }
 
     tpe_world.bodies[6].joints[3].velocity[1] -= 5;

+ 23 - 2
programs/helper.h

@@ -464,6 +464,27 @@ void helper_drawLine2D(int x1, int y1, int x2, int y2, uint8_t r, uint8_t g,
     sdl_drawPixel(x1 + (x2 * i) / max,y1 + (y2 * i) / max,r,g,b);
 }
 
+void helper_drawPoint3D(TPE_Vec3 p, uint8_t r, uint8_t g, uint8_t b)
+{
+  S3L_Vec4 p2, p3;
+
+  p2.x = p.x;
+  p2.y = p.y;
+  p2.z = p.z;
+  p2.w = 0;
+
+  S3L_project3DPointToScreen(p2,s3l_scene.camera,&p3);
+  
+  if (p3.x >= 0 && p3.x < S3L_RESOLUTION_X - 1 && 
+    p3.y >= 0 && p3.y < S3L_RESOLUTION_Y - 1 && p3.z > 0)
+  {
+    sdl_drawPixel(p3.x,p3.y,r,g,b); 
+    sdl_drawPixel(p3.x + 1,p3.y,r,g,b); 
+    sdl_drawPixel(p3.x,p3.y + 1,r,g,b); 
+    sdl_drawPixel(p3.x + 1,p3.y + 1,r,g,b); 
+  }
+}
+
 void helper_drawLine3D(TPE_Vec3 p1, TPE_Vec3 p2, uint8_t rr, uint8_t gg,
   uint8_t bb)
 {
@@ -612,7 +633,7 @@ void helper_draw3dTriangle(TPE_Vec3 v1, TPE_Vec3 v2, TPE_Vec3 v3)
     TPE_vec3(0,0,0)); 
 }
 
-void helper_draw3dCube(TPE_Vec3 pos, TPE_Vec3 scale, TPE_Vec3 rot)
+void helper_draw3dBox(TPE_Vec3 pos, TPE_Vec3 scale, TPE_Vec3 rot)
 {
   cubeModel.config.backfaceCulling = 2;
   helper_drawModel(&cubeModel,pos,scale,rot);
@@ -623,7 +644,7 @@ void helper_draw3dCylinder(TPE_Vec3 pos, TPE_Vec3 scale, TPE_Vec3 rot)
   helper_drawModel(&cylinderModel,pos,scale,rot);
 }
 
-void helper_draw3dCubeInside(TPE_Vec3 pos, TPE_Vec3 scale, TPE_Vec3 rot)
+void helper_draw3dBoxInside(TPE_Vec3 pos, TPE_Vec3 scale, TPE_Vec3 rot)
 {
   cubeModel.config.backfaceCulling = 1;
   helper_drawModel(&cubeModel,pos,scale,rot);

+ 5 - 5
programs/player.c

@@ -163,10 +163,10 @@ ballPreviousPos = tpe_world.bodies[1].joints[0].position;
     updateDirection();
 
     helper_set3dColor(100,100,100);
-    helper_draw3dCubeInside(TPE_vec3(0,ROOM_SIZE / 4,0),TPE_vec3(ROOM_SIZE,ROOM_SIZE / 2,ROOM_SIZE),TPE_vec3(0,0,0));
-    helper_draw3dCube(TPE_vec3(4000,160,4000),TPE_vec3(2000,320,2000),TPE_vec3(0,0,0));
-    helper_draw3dCube(TPE_vec3(4000,80,2500),TPE_vec3(2000,160,1000),TPE_vec3(0,0,0));
-    helper_draw3dCube(TPE_vec3(-1000,270,4500),TPE_vec3(8000,540,500),TPE_vec3(0,0,0));
+    helper_draw3dBoxInside(TPE_vec3(0,ROOM_SIZE / 4,0),TPE_vec3(ROOM_SIZE,ROOM_SIZE / 2,ROOM_SIZE),TPE_vec3(0,0,0));
+    helper_draw3dBox(TPE_vec3(4000,160,4000),TPE_vec3(2000,320,2000),TPE_vec3(0,0,0));
+    helper_draw3dBox(TPE_vec3(4000,80,2500),TPE_vec3(2000,160,1000),TPE_vec3(0,0,0));
+    helper_draw3dBox(TPE_vec3(-1000,270,4500),TPE_vec3(8000,540,500),TPE_vec3(0,0,0));
 helper_draw3dCylinder(
 TPE_vec3(2000,
 5000,-1100),TPE_vec3(400,10000,400),TPE_vec3(0,0,0));
@@ -178,7 +178,7 @@ TPE_vec3(-64,0,0));
     
 helper_set3dColor(200,50,0);
 
-helper_draw3dCube(
+helper_draw3dBox(
 TPE_bodyGetCenterOfMass(&tpe_world.bodies[2]),
 TPE_vec3(1200,800,1200),
 TPE_bodyGetRotation(&tpe_world.bodies[2],0,2,1)

+ 4 - 36
programs/stack.c

@@ -2,42 +2,10 @@
 
 TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD)
 {
-
 TPE_ENV_START( TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(256,256,0)),p )
 TPE_ENV_NEXT( TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(-256,256,-256)),p )
-TPE_ENV_NEXT(  TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(-256,256,256)),p )
+TPE_ENV_NEXT( TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(-256,256,256)),p )
 TPE_ENV_END
-
-/*
-  TPE_Vec3 pTest, pBest;
-  TPE_Unit dTest, dBest;
-
-  pBest = TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(256,256,0));
-
-  dBest = TPE_DISTANCE(p,pBest);
-
-  pTest = TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(-256,256,-256));
-
-  dTest = TPE_DISTANCE(p,pTest);
-
-  if (dTest < dBest)
-  {
-    pBest = pTest;
-    dBest = dTest;
-  }
-
-  pTest = TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(-256,256,256));
-
-  dTest = TPE_DISTANCE(p,pTest);
-
-  if (dTest < dBest)
-  {
-    pBest = pTest;
-    dBest = dTest;
-  }
-
-  return pBest;
-*/
 }
 
 uint8_t debugDrawOn = 1;
@@ -126,11 +94,11 @@ timeMeasure += helper_getMicroSecs() - t1;
 
       switch (i % 5)
       {
-        case 0: helper_draw3dCube(pos,TPE_vec3(1200,1200,1200),orient); break;
+        case 0: helper_draw3dBox(pos,TPE_vec3(1200,1200,1200),orient); break;
         case 1: helper_draw3dTriangle(joints[0].position,joints[1].position,joints[2].position); break;
         case 2: helper_draw3dSphere(pos,TPE_vec3(500,500,500),orient); break;
-        case 3: helper_draw3dCube(pos,TPE_vec3(1200,400,1200),orient); break; 
-        case 4: helper_draw3dCube(pos,TPE_vec3(200,200,1200),orient); break;
+        case 3: helper_draw3dBox(pos,TPE_vec3(1200,400,1200),orient); break; 
+        case 4: helper_draw3dBox(pos,TPE_vec3(200,200,1200),orient); break;
         default: break;
       }
     }

+ 146 - 12
tinyphysicsengine.h

@@ -181,21 +181,21 @@ typedef struct
                                           to keep the body's shape. */
 
 /** Function used for defining static environment, working similarly to an SDF
-(signed distance function). The parameters are: 3D point P, max distance D.
-The function should behave like this: if P is inside the solid environment
-volume, P will be returned; otherwise closest point (by Euclidean distance) to
-the solid environment volume from P will be returned, except for a case when
-this closest point would be further away than D, in which case any arbitrary
-point further away than D may be returned (this allows for potentially 
-potentially faster implementation). */
+  (signed distance function). The parameters are: 3D point P, max distance D.
+  The function should behave like this: if P is inside the solid environment
+  volume, P will be returned; otherwise closest point (by Euclidean distance) to
+  the solid environment volume from P will be returned, except for a case when
+  this closest point would be further away than D, in which case any arbitrary
+  point further away than D may be returned (this allows for potentially 
+  potentially faster implementation). */
 typedef TPE_Vec3 (*TPE_ClosestPointFunction)(TPE_Vec3, TPE_Unit);
 
 /** Function that can be used as a joint-joint or joint-environment collision
-callback, parameters are following: body1 index, joint1 index, body2 index,
-joint2 index, collision world position. If body1 index is the same as body1
-index, then collision type is body-environment, otherwise it is body-body type.
-The function has to return either 1 if the collision is to be allowed or 0 if
-it is to be discarded. */
+  callback, parameters are following: body1 index, joint1 index, body2 index,
+  joint2 index, collision world position. If body1 index is the same as body1
+  index, then collision type is body-environment, otherwise it is body-body
+  type. The function has to return either 1 if the collision is to be allowed
+  or 0 if it is to be discarded. */
 typedef uint8_t (*TPE_CollisionCallback)(uint16_t, uint16_t, uint16_t, uint16_t,
   TPE_Vec3);
 
@@ -346,6 +346,23 @@ void TPE_makeCenterRect(TPE_Joint joints[5], TPE_Connection connections[8],
 void TPE_make2Line(TPE_Joint joints[2], TPE_Connection connections[1],
   TPE_Unit length, TPE_Unit jointSize);
 
+/** Casts a ray against environment and returns the first hit of a surface. If
+  no surface was hit, a vector with all elements equal to TPE_INFINITY will be
+  returned. The function internally works differently for outside rays (rays
+  cast from the outside of the environment) and inside rays. Outside rays can
+  be traced with raymarching and will be processed very quickly and precisely;
+  in this case if any intersection is found, the function will return a point
+  outside the environment that's just in front of the hit surface. Inside rays
+  are difficult and slow to trace because environment function won't provide
+  distance, so the results aren't guaranteed to be precise (the ray may miss
+  some intersections); here rays will be traced by given step (insideStepSize)
+  and eventually iterated a bit towards the intersection -- if any intersection
+  is found, the function will return a point inside the environment just before
+  the hit surface. */
+TPE_Vec3 TPE_castEnvironmentRay(TPE_Vec3 rayPos, TPE_Vec3 rayDir,
+  TPE_ClosestPointFunction environment, TPE_Unit insideStepSize,
+  TPE_Unit rayMarchMaxStep, uint32_t maxSteps);
+
 // environment building functions:
 
 TPE_Vec3 TPE_envAABoxInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 size);
@@ -353,6 +370,7 @@ TPE_Vec3 TPE_envAABox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec);
 TPE_Vec3 TPE_envBox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec,
   TPE_Vec3 rotation);
 TPE_Vec3 TPE_envSphere(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius);
+TPE_Vec3 TPE_envSphereInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius);
 TPE_Vec3 TPE_envHalfPlane(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 normal);
 TPE_Vec3 TPE_envInfiniteCylinder(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3
   direction, TPE_Unit radius);
@@ -1913,6 +1931,22 @@ TPE_Vec3 TPE_envAABoxInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 size)
   return point;
 }
 
+TPE_Vec3 TPE_envSphereInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius)
+{
+  TPE_Vec3 shifted = TPE_vec3Minus(point,center);
+
+  TPE_Unit l = TPE_LENGTH(shifted);
+
+  if (l >= radius)
+    return point;
+  else if (l < 0)
+    return TPE_vec3(center.x + radius,center.y,center.z);
+
+  TPE_vec3Normalize(&shifted);
+ 
+  return TPE_vec3Plus(center,TPE_vec3Times(shifted,radius));
+}
+
 TPE_Vec3 TPE_envSphere(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius)
 {
   // TODO: optim?
@@ -2127,4 +2161,104 @@ TPE_Vec3 TPE_fakeSphereRotation(TPE_Vec3 position1, TPE_Vec3 position2,
   return m;
 }
 
+TPE_Vec3 TPE_castEnvironmentRay(TPE_Vec3 rayPos, TPE_Vec3 rayDir,
+  TPE_ClosestPointFunction environment, TPE_Unit insideStepSize,
+  TPE_Unit rayMarchMaxStep, uint32_t maxSteps)
+{
+  TPE_Vec3 p = rayPos;
+  TPE_Vec3 p2 = environment(rayPos,rayMarchMaxStep);
+  TPE_Unit totalD = 0;
+
+  TPE_vec3Normalize(&rayDir);
+
+  uint8_t found = 0; // 0 = nothing found, 1 = out/in found, 2 = in/out found
+
+  if (p2.x != p.x || p2.y != p.y || p2.z != p.z)
+  {
+    // outside ray: ray march
+
+    for (uint32_t i = 0; i < maxSteps; ++i)
+    {
+      TPE_Unit d = TPE_DISTANCE(p,p2);
+
+      if (d > rayMarchMaxStep)
+        d = rayMarchMaxStep;
+
+      totalD += d;
+
+      p2 = TPE_vec3Plus(rayPos,TPE_vec3Times(rayDir,totalD));
+
+      if (d == 0 || 
+        (p2.x == p.x && p2.y == p.y && p2.z == p.z))
+        return p2; // point not inside env but dist == 0, ideal case
+
+      TPE_Vec3 pTest = environment(p2,rayMarchMaxStep);
+
+      if (pTest.x == p2.x && pTest.y == p2.y && pTest.z == p2.z)
+      {
+        // stepped into env, will have to iterate
+        found = 1;
+        break;
+      }
+
+      p = p2;
+      p2 = pTest;
+    }
+  }
+  else if (insideStepSize != 0)
+  {
+    // inside ray: iterate by fixed steps
+
+    for (uint32_t i = 0; i < maxSteps; ++i)
+    {
+      totalD += insideStepSize;
+
+      p2 = TPE_vec3Plus(rayPos,TPE_vec3Times(rayDir,totalD));
+
+      TPE_Vec3 pTest = environment(p2,16);
+
+      if (p2.x != pTest.x || p2.y != pTest.y || p2.z != pTest.z)
+      {
+        found = 2;
+        break;
+      }
+
+      p = p2;
+      p2 = pTest;
+    }
+  }
+
+  if (found)
+  {
+    /* Here we've found two points (p, p2), each one the other side of the
+       env surface. Now iterate (binary search) to find the exact surface
+       pos. */
+
+    for (uint8_t i = 0; i < 128; ++i) // upper limit just in case
+    {
+      TPE_Vec3 middle = TPE_vec3Plus(p,p2);
+ 
+      middle.x /= 2;
+      middle.y /= 2;
+      middle.z /= 2;
+
+      if ((middle.x == p.x && middle.y == p.y && middle.z == p.z) ||
+        (middle.x == p2.x && middle.y == p2.y && middle.z == p2.z))
+        break; // points basically next to each other, don't continue
+
+      TPE_Vec3 pTest = environment(middle,16); // 16: just a small number
+
+      if ((found == 1) ==
+        (pTest.x == middle.x && pTest.y == middle.y && pTest.z == middle.z))
+        p2 = middle;
+      else
+        p = middle;
+    }
+
+    return (found == 1) ? p : p2;
+  }
+
+  return TPE_vec3(TPE_INFINITY,TPE_INFINITY,TPE_INFINITY);
+}
+
 #endif // guard