Browse Source

Clean some examples

Miloslav Ciz 3 năm trước cách đây
mục cha
commit
cd7b210451
11 tập tin đã thay đổi với 879 bổ sung172 xóa
  1. 5 4
      TODO.txt
  2. 44 63
      programs/cubes.c
  3. 16 18
      programs/envaccel.c
  4. 24 24
      programs/heightmap.c
  5. 44 0
      programs/hello.c
  6. 127 0
      programs/hello2.c
  7. 586 0
      programs/main.c
  8. 17 17
      programs/player.c
  9. 2 1
      programs/test.c
  10. 1 1
      programs/testGeneral.c
  11. 13 44
      programs/water.c

+ 5 - 4
TODO.txt

@@ -1,11 +1,8 @@
 TODO:
+- demo: ragdoll <--- maybe later
 - add/remove log with TPE_LOG so that its nice, useful and not spammy
-- demo: ragdoll
 - demos for showing basic use (without using helper.h)
 - FOR GODS SAKE clean the code
-- current dist approx fails e.g. with heightmaps -- maybe just use the
-  non-approx version at all times in places where the approx fails? Also
-  TEST THE APPROX DIST with different env functions
 
 DONE:
 - update the player demo (new 3D environment)
@@ -32,6 +29,10 @@ DONE:
 - test ray casting (e.g. the hit of an outside ray should always be outside)
 - BUG: envBox function doesn't pass the env function test! <-- just needed a
   bigger epsilon
+- current dist approx fails e.g. with heightmaps -- maybe just use the
+  non-approx version at all times in places where the approx fails? Also
+  TEST THE APPROX DIST with different env functions <--- the improved version
+  works alright, leave the rest for the decision of the library user
 - ray casting against bodies
 - world state hash? for testing determinism etc.
 - check if using fastBSphere vs normal BSphere doesn't affect the simulation

+ 44 - 63
programs/cubes.c

@@ -1,26 +1,26 @@
+/** Demo showing a wrecking ball hitting a few boxes. */
+
 #include "helper.h"
 
 #define ROOM_SIZE 7000
 #define CUBE_SIZE 800
-
-TPE_Unit sides[6] =
-{
-  0,0,
-  1000,1000,
-  -1000,2000
-};
+#define GRAVITY (TPE_F / 100)
 
 TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD)
 {
-  return TPE_envAABoxInside(p,TPE_vec3(0,ROOM_SIZE / 4,0),TPE_vec3(ROOM_SIZE,ROOM_SIZE / 2,ROOM_SIZE));
+  return TPE_envAABoxInside(p,TPE_vec3(0,ROOM_SIZE / 4,0),
+    TPE_vec3(ROOM_SIZE,ROOM_SIZE / 2,ROOM_SIZE));
 }
 
 TPE_Vec3 cubeOrientations[6];
 TPE_Vec3 cubePositions[6];
 
-TPE_Joint ballJoints[4];
+TPE_Joint ballJoints[4];  // for the wrecking ball body
 TPE_Connection ballConnections[3];
 
+/** Updates the orientation and position of the ith cube's 3D model accordint to
+  the physics model. This will only be done for active bodies to help
+  performance a bit. */
 void updateOrientPos(int i)
 {
   cubeOrientations[i] = TPE_bodyGetRotation(&tpe_world.bodies[i],0,2,1);
@@ -29,29 +29,6 @@ void updateOrientPos(int i)
 
 int main(void)
 {
-/*
-
-TPE_Vec3 bbb = TPE_envLineSegment(
-  TPE_vec3(1304,280,0),
-  TPE_vec3(714,714,0),
-  TPE_vec3(500,106,0)
-);
-
-TPE_PRINTF_VEC3(bbb)
-
-printf("\n");
-return 0;
-*/
-
-/*
-
-   
-   
-
- [1304 280 0][714 714 0][500 106 0][644 514 0]
-*/
-
-
   helper_init();
 
   tpe_world.environmentFunction = environmentDistance;
@@ -61,17 +38,11 @@ return 0;
   s3l_scene.camera.transform.translation.x -= ROOM_SIZE / 4;
   s3l_scene.camera.transform.rotation.y = -1 * TPE_F / 16;
 
-  for (int i = 0; i < 6; ++i)
-{
-    helper_addBox(CUBE_SIZE / 2,CUBE_SIZE / 2,CUBE_SIZE / 2,CUBE_SIZE / 4,10);
-//helper_addCenterBox(CUBE_SIZE / 2,CUBE_SIZE / 2,CUBE_SIZE / 2,CUBE_SIZE / 4,50);
-
-tpe_world.bodies[tpe_world.bodyCount - 1].elasticity = 200;
-tpe_world.bodies[tpe_world.bodyCount - 1].friction = 0;
-
-//if (i % 2)
-//tpe_world.bodies[tpe_world.bodyCount - 1].flags |= TPE_BODY_FLAG_NONROTATING;
-}
+  for (int i = 0; i < 6; ++i) // add 6 cubes
+  {
+    helper_addBox(CUBE_SIZE / 2,CUBE_SIZE / 2,CUBE_SIZE / 2,CUBE_SIZE / 4,TPE_F / 5);
+    helper_lastBody.friction = TPE_F / 20; // decrease friction for fun
+  }
 
 #define move(i,x,y) \
   TPE_bodyMoveBy(&tpe_world.bodies[i],TPE_vec3((CUBE_SIZE / 2 + 10) * x,10 + CUBE_SIZE / 2 + y * (CUBE_SIZE + 10),0));
@@ -88,25 +59,33 @@ tpe_world.bodies[tpe_world.bodyCount - 1].friction = 0;
   for (int i = 0; i < 6; ++i)
   {
     updateOrientPos(i);
+
+    /* normally the cubes wouldn't stand on each other as they're really made of
+       spheres, so we deactivate them to keep them in place until the wrecking
+       ball hits them: */
     TPE_bodyDeactivate(&tpe_bodies[i]);
   }
 
+  // now create the wrecking ball body:
+
   ballJoints[0] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2,0),0);
-  ballJoints[1] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - 100,-650),0);
-  ballJoints[2] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - 300,-1300),0);
-  ballJoints[3] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - 500,-1950),400);
-  ballJoints[3].velocity[0] = 4;
-  ballJoints[3].velocity[1] = -200;
-  ballJoints[3].velocity[2] = -200;
+  ballJoints[1] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - TPE_F / 5,-1 * TPE_F),0);
+  ballJoints[2] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - TPE_F / 2,-2 * TPE_F),0);
+  ballJoints[3] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - TPE_F,-4 * TPE_F),TPE_F);
+  ballJoints[3].velocity[1] = -1 * TPE_F / 2;
+  ballJoints[3].velocity[2] = -1 * TPE_F / 2;
 
   ballConnections[0].joint1 = 0; ballConnections[0].joint2 = 1;
   ballConnections[1].joint1 = 1; ballConnections[1].joint2 = 2;
   ballConnections[2].joint1 = 2; ballConnections[2].joint2 = 3;
 
-  TPE_bodyInit(&tpe_world.bodies[6],ballJoints,4,ballConnections,3,1000);
+  TPE_Body *ballBody = &tpe_world.bodies[6];
+
+  TPE_bodyInit(ballBody,ballJoints,4,ballConnections,3,2 * TPE_F);
 
-tpe_world.bodies[6].friction = 0;
-tpe_world.bodies[6].elasticity = 512;
+  ballBody->flags |= TPE_BODY_FLAG_SIMPLE_CONN;
+  ballBody->friction = 0;
+  ballBody->elasticity = TPE_F;
 
   tpe_world.bodyCount++;
     
@@ -120,18 +99,19 @@ tpe_world.bodies[6].elasticity = 512;
     {
       helper_printCPU();
 
-      if (sdl_keyboard[SDL_SCANCODE_L])
+      if (sdl_keyboard[SDL_SCANCODE_L]) // pressing L explodes the cubes :)
         for (int i = 0; i < 6; ++i)
         {
           TPE_bodyActivate(&tpe_world.bodies[i]);
           TPE_bodyAccelerate(&tpe_world.bodies[i],
-          TPE_vec3Plus(TPE_vec3(0,100,0),TPE_vec3Times(cubePositions[i],128)));
+          TPE_vec3Plus(TPE_vec3(0,TPE_F / 5,0),TPE_vec3Times(cubePositions[i],TPE_F / 4)));
         }
     }
 
-    TPE_jointPin(&tpe_world.bodies[6].joints[0],TPE_vec3(0,ROOM_SIZE / 2 - 10,0));
+    // pin the top point of the wrecking ball to the ceiling:
+    TPE_jointPin(&ballBody->joints[0],TPE_vec3(0,ROOM_SIZE / 2 - TPE_F / 100,0));
 
-    tpe_world.bodies[6].deactivateCount = 0;
+    TPE_bodyActivate(ballBody); // don't let the wrecking ball deactivate
 
     TPE_worldStep(&tpe_world);
 
@@ -142,22 +122,23 @@ tpe_world.bodies[6].elasticity = 512;
 
     for (int i = 0; i < 6; ++i)
     {
-      TPE_bodyApplyGravity(&tpe_world.bodies[i],7);
+      TPE_bodyApplyGravity(&tpe_world.bodies[i],GRAVITY);
 
-      if (!(tpe_world.bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED))
+      if (TPE_bodyIsActive(&tpe_world.bodies[i]))
         updateOrientPos(i);
 
-      helper_draw3DBox(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;
+    ballBody->joints[3].velocity[1] -= GRAVITY; // apply gravity only to one joint
 
-    helper_draw3DSphere(tpe_world.bodies[6].joints[3].position,TPE_vec3(400,400,400),TPE_vec3(0,0,0)  );
+    helper_draw3DSphere(ballBody->joints[3].position,TPE_vec3(400,400,400),TPE_vec3(0,0,0));
 
     for (int i = 0; i < 3; ++i)
       helper_drawLine3D(
-        tpe_world.bodies[6].joints[tpe_world.bodies[6].connections[i].joint1].position,
-        tpe_world.bodies[6].joints[tpe_world.bodies[6].connections[i].joint2].position,
+        ballBody->joints[ballBody->connections[i].joint1].position,
+        ballBody->joints[ballBody->connections[i].joint2].position,
         255,0,0);
 
     if (helper_debugDrawOn)

+ 16 - 18
programs/envaccel.c

@@ -7,38 +7,40 @@
 
 TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD)
 {
+  /* this is our highly complex environment function that find the closest point
+     against  a grid of shape chunks -- with acceleration we apply bounding
+     volume checks to the big chunks and each smaller subchunk */
+
   TPE_ENV_START( TPE_envAABoxInside(p,TPE_vec3(0,0,0),TPE_vec3(30000,30000,30000)), p )
 
-  for (int bigZ = -1; bigZ < 1; ++bigZ)
+  for (int bigZ = -1; bigZ < 1; ++bigZ) // big chunks
     for (int bigY = -1; bigY < 1; ++bigY)
       for (int bigX = -1; bigX < 1; ++bigX)
       {
         TPE_Vec3 bigCenter =
-        TPE_vec3(bigX * 10000,bigY * 10000,bigZ * 10000);
+        TPE_vec3(bigX * 20 * TPE_F,bigY * 20 * TPE_F,bigZ * 20 * TPE_F);
 
 #if ACCELERATE
         // bouding volume check for the big chunks of environment
-        if (TPE_ENV_BCUBE_TEST(p,maxD,bigCenter,10000))
+        if (TPE_ENV_BCUBE_TEST(p,maxD,bigCenter,20 * TPE_F))
 #endif
-          for (int smallZ = 0; smallZ < 2; ++smallZ)
+          for (int smallZ = 0; smallZ < 2; ++smallZ) // smaller chunks
             for (int smallY = 0; smallY < 2; ++smallY)
               for (int smallX = 0; smallX < 2; ++smallX)
               {
-                TPE_Vec3 smallCenter = TPE_vec3Plus(
-                  bigCenter,
-                  TPE_vec3(smallX * 4096,smallY * 4096,smallZ * 4096));
+                TPE_Vec3 smallCenter = TPE_vec3Plus(bigCenter,
+                  TPE_vec3(smallX * 8 * TPE_F,smallY * 8 * TPE_F,smallZ * 8 * TPE_F));
 
 #if ACCELERATE
                 // bouding volume check for the smaller subchanks of environment
-                if (TPE_ENV_BSPHERE_TEST(p,maxD,smallCenter,4096))
+                if (TPE_ENV_BSPHERE_TEST(p,maxD,smallCenter,8 * TPE_F))
 #endif
                 {
-                  TPE_ENV_NEXT( TPE_envBox(p, smallCenter, TPE_vec3(1000,800,900) ,
-                    TPE_vec3(100,20,30) ) ,p );
-
-                  TPE_ENV_NEXT( TPE_envCylinder(p, smallCenter, TPE_vec3(2000,1500,300) , 300 ), p );
+                  TPE_ENV_NEXT( TPE_envBox(p,smallCenter,TPE_vec3(2 * TPE_F,4 * TPE_F / 3,4 * TPE_F / 3),
+                    TPE_vec3(TPE_F / 100,TPE_F / 20,TPE_F / 15)), p );
 
-                  TPE_ENV_NEXT( TPE_envSphere(p, TPE_vec3Minus(smallCenter,TPE_vec3  (-300,400,0)), 1100  )  , p );
+                  TPE_ENV_NEXT( TPE_envCylinder(p,smallCenter,TPE_vec3(TPE_F * 4,TPE_F * 3,3 * TPE_F / 4),3 * TPE_F / 4), p);
+                  TPE_ENV_NEXT( TPE_envSphere(p,TPE_vec3Minus(smallCenter,TPE_vec3(-3 * TPE_F / 4,TPE_F,0)), 2 * TPE_F  )  , p );
                 }
               }
       }
@@ -51,17 +53,13 @@ int main(void)
   helper_init();
 
   helper_debugDrawOn = 1;
-
   tpe_world.environmentFunction = environmentDistance;
-
-  s3l_scene.camera.transform.translation.z -= 2000;
+  s3l_scene.camera.transform.translation.z -= TPE_F * 4;
 
   while (helper_running)
   {
     helper_frameStart();
-
     helper_cameraFreeMovement();
-
     TPE_worldStep(&tpe_world);
 
     if (helper_frame % 32 == 0)

+ 24 - 24
programs/heightmap.c

@@ -1,26 +1,27 @@
-#define CAMERA_STEP 200
-#define GRID_SIZE 1000
+/** Demo showing the heightmap environment. */
 
+#define CAMERA_STEP 200
 #define HEIGHTMAP_3D_RESOLUTION 32
-#define HEIGHTMAP_3D_STEP GRID_SIZE
-
+#define HEIGHTMAP_3D_STEP (TPE_F * 2)
 #define MAP_LIMIT ((HEIGHTMAP_3D_RESOLUTION * HEIGHTMAP_3D_STEP) / 2)
 
 #include "helper.h"
 
+/** For given heightmap node at [x,y] returns its height. Here the function uses
+  sines/cosines to generate a simple procedural heightmap but it could also
+  retrieve the height e.g. from an image. */
 TPE_Unit height(int32_t x, int32_t y)
 {
   x *= 8;
   y *= 8;
 
-  return 
-    TPE_sin(x + TPE_cos(y * 2)) * TPE_sin(y * 2 + TPE_cos(x * 4)) /
-     (TPE_F / 2);
+  return
+    TPE_sin(x + TPE_cos(y * 2)) * TPE_sin(y * 2 + TPE_cos(x * 4)) / (TPE_F / 2);
 }
 
 TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD)
 {
-  return TPE_envHeightmap(p,TPE_vec3(0,0,0),GRID_SIZE,height,maxD);
+  return TPE_envHeightmap(p,TPE_vec3(0,0,0),HEIGHTMAP_3D_STEP,height,maxD);
 }
 
 int main(void)
@@ -29,6 +30,8 @@ int main(void)
 
   helper_debugDrawOn = 0;
 
+  // here we just set up the graphical 3D model of the heigtmap: 
+
   for (int y = 0; y < HEIGHTMAP_3D_RESOLUTION; ++y)
     for (int x = 0; x < HEIGHTMAP_3D_RESOLUTION; ++x)
       helper_setHeightmapPoint(x,y,height(x - HEIGHTMAP_3D_RESOLUTION / 2,y - HEIGHTMAP_3D_RESOLUTION / 2));
@@ -39,14 +42,12 @@ int main(void)
 
   TPE_bodyMoveTo(&helper_lastBody,TPE_vec3(0,5000,0));
 
-  s3l_scene.camera.transform.rotation.x = -50;
+  s3l_scene.camera.transform.rotation.x = -1 * TPE_F / 8;
 
   while (helper_running)
   {
     helper_frameStart();
 
-    //helper_cameraFreeMovement();
-
     TPE_Vec3 bodyCenter = TPE_bodyGetCenterOfMass(&tpe_world.bodies[0]);
 
     s3l_scene.camera.transform.translation.x = bodyCenter.x;
@@ -54,24 +55,23 @@ int main(void)
     s3l_scene.camera.transform.translation.z = bodyCenter.z - 3000;
 
     if (bodyCenter.x < -1 * MAP_LIMIT)
-      TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(2 * MAP_LIMIT,0,0));
+      TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(2 * MAP_LIMIT,TPE_F,0));
     else if (bodyCenter.x > MAP_LIMIT)
-      TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(-2 * MAP_LIMIT,0,0));
+      TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(-2 * MAP_LIMIT,TPE_F,0));
 
     if (bodyCenter.z < -1 * MAP_LIMIT)
-      TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(0,0,2 * MAP_LIMIT));
+      TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(0,TPE_F,2 * MAP_LIMIT));
     else if (bodyCenter.z > MAP_LIMIT)
-      TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(0,0,-2 * MAP_LIMIT));
+      TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(0,TPE_F,-2 * MAP_LIMIT));
 
     if (helper_frame % 32 == 0)
       helper_printCPU();
 
     helper_set3DColor(0,200,100);
-    helper_drawModel(&heightmapModel,TPE_vec3(-GRID_SIZE / 2,0,-GRID_SIZE / 2),TPE_vec3(512,512,512),TPE_vec3(0,0,0));
+    helper_drawModel(&heightmapModel,TPE_vec3(-HEIGHTMAP_3D_STEP / 2,0,-HEIGHTMAP_3D_STEP / 2),TPE_vec3(512,512,512),TPE_vec3(0,0,0));
 
     helper_set3DColor(100,100,200);
-
-    helper_draw3DBox(bodyCenter,TPE_vec3(1200,1200,1200),
+    helper_draw3DBox(bodyCenter,TPE_vec3(2 * TPE_F,2 * TPE_F,2 * TPE_F),
       TPE_bodyGetRotation(&tpe_world.bodies[0],0,1,2));
 
     for (int i = 0; i < tpe_world.bodyCount; ++i)
@@ -79,20 +79,20 @@ int main(void)
 
     TPE_worldStep(&tpe_world);
 
-#define ACC 10
-
-    if (sdl_keyboard[SDL_SCANCODE_W])
+#define ACC (TPE_F / 50)
+    if (sdl_keyboard[SDL_SCANCODE_UP])
       TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(0,0,ACC));
-    else if (sdl_keyboard[SDL_SCANCODE_S])
+    else if (sdl_keyboard[SDL_SCANCODE_DOWN])
       TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(0,0,-1 * ACC));
 
-    if (sdl_keyboard[SDL_SCANCODE_A])
+    if (sdl_keyboard[SDL_SCANCODE_LEFT])
       TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(-1 * ACC,0,0));
-    else if (sdl_keyboard[SDL_SCANCODE_D])
+    else if (sdl_keyboard[SDL_SCANCODE_RIGHT])
       TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(ACC,0,0));
 
     if (helper_debugDrawOn)
       helper_debugDraw(1);
+#undef ACC
 
     helper_frameEnd();
   }

+ 44 - 0
programs/hello.c

@@ -0,0 +1,44 @@
+/** Completely basic program showing an initialization of a body consisting of
+a single joint and dropping it on the floor, then plotting the body vertical
+position over time. */
+
+#include "../tinyphysicsengine.h"
+#include <stdio.h>
+
+TPE_Vec3 environmentDistance(TPE_Vec3 point, TPE_Unit maxDistance)
+{
+  return TPE_envGround(point,0); // just an infinite flat plane
+}
+
+int main(void)
+{
+  TPE_Body body;
+  TPE_World world;
+  TPE_Joint joint;
+  int frame = 0;
+
+  joint = TPE_joint(TPE_vec3(0,TPE_F * 8,0),TPE_F);
+  TPE_bodyInit(&body,&joint,1,0,0,2 * TPE_F);
+  TPE_worldInit(&world,&body,1,environmentDistance);
+
+  while (TPE_bodyIsActive(&body))
+  {
+    if (frame % 6 == 0) // print once in 6 frames
+    {
+      TPE_Unit height = TPE_bodyGetCenterOfMass(&body).y;
+
+      for (int i = 0; i < (height * 4) / TPE_F; ++i)
+        putchar(' ');
+
+      puts("*");
+    }
+
+    TPE_bodyApplyGravity(&body,TPE_F / 100);
+    TPE_worldStep(&world);
+    frame++;
+  }
+
+  puts("body deactivated");
+
+  return 0;
+}

+ 127 - 0
programs/hello2.c

@@ -0,0 +1,127 @@
+/** Simple demo showing 2 bodies thrown inside a room, the world is rendered
+  as a simple ASCII side view. */
+
+#include "../tinyphysicsengine.h"
+#include <stdio.h>
+
+#define ROOM_SIZE (20 * TPE_F)
+
+TPE_Vec3 environmentDistance(TPE_Vec3 point, TPE_Unit maxDistance)
+{
+  // our environemnt: just a simple room
+  return TPE_envAABoxInside(point,TPE_vec3(ROOM_SIZE / 2,ROOM_SIZE / 2,0),
+    TPE_vec3(ROOM_SIZE,ROOM_SIZE,ROOM_SIZE));
+}
+
+// the following functions are just for ASCII drawing the world
+
+#define SCREEN_W 32
+#define SCREEN_H 16
+
+char screen[SCREEN_W * SCREEN_H];
+
+void clearScreen(void)
+{
+  for (int i = 0; i < SCREEN_W * SCREEN_H; ++i)
+    screen[i] = (i < SCREEN_W || i >= SCREEN_W * (SCREEN_H - 1)) ? '-' :
+      ((i % SCREEN_W) == 0 || (i % SCREEN_W) == SCREEN_W - 1 ? '|' : ' ');
+}
+
+void setPixel(int x, int y, char c)
+{
+  if (x < 0 || x >= SCREEN_W || y < 0 || y >= SCREEN_H)
+    return;
+
+  y = SCREEN_H - 1 - y;
+
+  screen[y * SCREEN_W + x] = c;
+}
+
+void printScreen(void)
+{
+  for (int i = 0; i < 20; ++i)
+    putchar('\n');
+
+  for (int y = 0; y < SCREEN_H; ++y)
+  {
+    for (int x = 0; x < SCREEN_W; ++x)
+      putchar(screen[y * SCREEN_W + x]);
+
+    putchar('\n');
+  }
+}
+
+int main(void)
+{
+  TPE_Body bodies[2];             // we'll have two bodies
+  TPE_World world;
+
+  TPE_Joint joints[32];           // joint buffer
+  TPE_Connection connections[64]; // connection buffer
+
+  /* we'll create the first body "by hand", just two joints (spheres) with one
+     connection: */
+  joints[0] = TPE_joint(TPE_vec3(3 * ROOM_SIZE / 4,ROOM_SIZE / 2,0),TPE_F);
+  joints[1] = 
+    TPE_joint(TPE_vec3(3 * ROOM_SIZE / 4 + TPE_F * 4,ROOM_SIZE / 2,0),TPE_F);
+
+  connections[0].joint1 = 0;
+  connections[0].joint2 = 1;
+
+  TPE_bodyInit(&bodies[0],joints,2,connections,1,TPE_F);
+
+  /* the other (a "box" approximated by spheres) will be made by the library
+     function: */
+  TPE_makeBox(joints + 2,connections + 1,2 * TPE_F, 2 * TPE_F, 2 * TPE_F,TPE_F);
+  TPE_bodyInit(&bodies[1],joints + 2,8,connections + 1,16,TPE_F);
+  TPE_bodyMoveTo(&bodies[1],TPE_vec3(ROOM_SIZE / 2,ROOM_SIZE / 2,0));
+
+  TPE_worldInit(&world,bodies,2,environmentDistance);
+
+  // give some initial velocities and spins to the bodies:
+
+  TPE_bodyAccelerate(&world.bodies[0],TPE_vec3(-1 * TPE_F / 8,TPE_F / 3,0));
+  TPE_bodySpin(&world.bodies[0],TPE_vec3(0,0,-1 * TPE_F / 25));
+  TPE_bodyAccelerate(&world.bodies[1],TPE_vec3(-1 * TPE_F / 2,50,0));
+  TPE_bodySpin(&world.bodies[1],TPE_vec3(0,0,TPE_F / 23));
+
+#define FRAMES 200
+
+  for (int i = 0; i <= FRAMES; ++i) // simulate 200 steps
+  {
+    if (i % 10 == 0) // draw the world every 10 frames
+    {
+      clearScreen();
+
+      for (int j = 0; j < world.bodyCount; ++j)
+      {
+        // draw body joints:
+        for (int k = 0; k < world.bodies[j].jointCount; ++k)
+        {
+          TPE_Vec3 pos = world.bodies[j].joints[k].position;
+
+          setPixel((pos.x * SCREEN_W) / ROOM_SIZE,
+            (pos.y * SCREEN_H) / ROOM_SIZE,'.');
+        }
+
+        // draw the body center:
+        TPE_Vec3 pos = TPE_bodyGetCenterOfMass(&world.bodies[j]);
+          
+        setPixel((pos.x * SCREEN_W) / ROOM_SIZE,(pos.y * SCREEN_H) / ROOM_SIZE,
+          'A' + j);
+      }
+
+      printScreen();
+      printf("frame %d/%d\n",i,FRAMES);
+      puts("press return to step");
+      getchar();
+    }
+
+    TPE_worldStep(&world); // simulate next tick
+
+    for (int j = 0; j < world.bodyCount; ++j)
+      TPE_bodyApplyGravity(&world.bodies[j],TPE_F / 100);
+  }
+
+  return 0;
+}

+ 586 - 0
programs/main.c

@@ -0,0 +1,586 @@
+#define TPE_LOG puts
+
+#include "tinyphysicsengine.h"
+#include <SDL2/SDL.h>
+#include <math.h>
+
+#define S3L_RESOLUTION_X 640
+#define S3L_RESOLUTION_Y 480
+#define S3L_PIXEL_FUNCTION drawPixel
+
+#define S3L_Z_BUFFER 1
+#include "small3dlib.h"
+
+#define PIXELS_SIZE (S3L_RESOLUTION_X * S3L_RESOLUTION_Y * 4)
+
+#define FPS 30
+#define MSPF (1000 / (FPS))
+
+TPE_World world;
+TPE_Body bodies[128];
+
+S3L_Unit cubeVertices[] = { S3L_CUBE_VERTICES(1024) };  
+S3L_Index cubeTriangles[] = { S3L_CUBE_TRIANGLES };
+S3L_Model3D cube;
+
+uint8_t pixels[PIXELS_SIZE];
+
+uint8_t red = 100;
+
+TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit distLim)
+{
+
+
+  TPE_Vec3 r, rMin = p;
+  TPE_Unit d, dMin = 10000000;
+
+#define testShape(c) \
+  r = c; if (p.x == r.x && p.y == r.y && p.z == r.z) return p; \
+  d = TPE_DISTANCE(p,r); if (d < dMin) { dMin = d; rMin = r; }
+
+/*
+testShape( TPE_envHalfPlane(p,TPE_vec3(-1000,0,0),TPE_vec3(20,20,0)) )
+testShape( TPE_envHalfPlane(p,TPE_vec3(+1000,0,0),TPE_vec3(-20,20,0)) )
+*/
+  testShape( TPE_envAABoxInside(p,TPE_vec3(0,0,0),TPE_vec3(16000,6000,16000)) )
+  testShape( TPE_envSphere(p,TPE_vec3(2000,-3500,2000),2000) )
+  testShape( TPE_envSphere(p,TPE_vec3(-2000,-3500,0),2000) )
+
+#undef testShape
+
+  return rMin;
+}
+
+void drawPixel(S3L_PixelInfo *p)
+{
+  uint32_t index = (p->y * S3L_RESOLUTION_X + p->x) * 4;
+  pixels[index + 1] = p->triangleIndex * 16;
+  pixels[index + 2] = 255 - p->triangleIndex * 16;
+  pixels[index + 3] = red;
+}
+
+void draw2DPoint(int x, int y, int r, int g, int b)
+{
+  if (x < 1 || x > S3L_RESOLUTION_X - 3 ||
+      y < 1 || y > S3L_RESOLUTION_Y - 3)
+    return;
+
+  uint32_t index = ((y - 1) * S3L_RESOLUTION_X + x) * 4;
+
+  #define d pixels[index] = 0; pixels[index + 1] = b; pixels[index + 2] = g; pixels[index + 3] = r;
+
+  d
+  index += S3L_RESOLUTION_X * 4 - 4;
+  d
+  index += 4;
+  d
+  index += 4;
+  d
+  index += S3L_RESOLUTION_X * 4 - 4;
+  d
+
+  #undef d
+}
+
+void debugDrawPixel(uint16_t x, uint16_t y, uint8_t color)
+{
+  draw2DPoint(x,y,255 - color * 64,color * 128,color * 64);
+}
+
+void drawLine(int x1, int y1, int x2, int y2, int r, int g, int b)
+{
+  #define STEPS 20
+
+  float dx = (x2 - x1) / ((float) STEPS);
+  float dy = (y2 - y1) / ((float) STEPS);
+
+  for (int i = 0; i < STEPS; ++i)
+    draw2DPoint(x1 + dx * i, y1 + dy * i,r,g,b);
+
+  #undef STEPS
+}
+
+S3L_Scene sphereScene;
+
+void draw3DLine(int x1, int y1, int z1, int x2, int y2, int z2)
+{
+  S3L_Vec4 p1, p2, r1, r2;
+
+  S3L_vec4Set(&p1,x1,y1,z1,0);
+  S3L_vec4Set(&p2,x2,y2,z2,0);
+
+  S3L_project3DPointToScreen(p1,sphereScene.camera,&r1);
+  S3L_project3DPointToScreen(p2,sphereScene.camera,&r2);
+
+  if (r1.z > 0 && r2.z > 0)
+    drawLine(r1.x,r1.y,r2.x,r2.y,200,100,200);
+}
+
+void drawSphere(S3L_Unit x, S3L_Unit y, S3L_Unit z, S3L_Unit r)
+{
+  sphereScene.models[0].transform.translation.x = x;
+  sphereScene.models[0].transform.translation.y = y;
+  sphereScene.models[0].transform.translation.z = z;
+  sphereScene.models[0].transform.scale.x = r;
+  sphereScene.models[0].transform.scale.y = r;
+  sphereScene.models[0].transform.scale.z = r;
+  S3L_drawScene(sphereScene);
+}
+
+void drawBody(TPE_Body *body, uint8_t color)
+{
+  red = color;
+
+  for (int i = 0; i < body->jointCount; ++i)
+    drawSphere(
+      body->joints[i].position.x,
+      body->joints[i].position.y,
+      body->joints[i].position.z,
+      TPE_JOINT_SIZE(body->joints[i]));
+
+  for (int i = 0; i < body->connectionCount; ++i)
+  {
+    S3L_Vec4 p1, p2, r1, r2;
+
+    S3L_vec4Set(&p1,
+      body->joints[body->connections[i].joint1].position.x,
+      body->joints[body->connections[i].joint1].position.y,
+      body->joints[body->connections[i].joint1].position.z,0);
+
+    S3L_vec4Set(&p2,
+      body->joints[body->connections[i].joint2].position.x,
+      body->joints[body->connections[i].joint2].position.y,
+      body->joints[body->connections[i].joint2].position.z,0);
+
+    S3L_project3DPointToScreen(p1,sphereScene.camera,&r1);
+    S3L_project3DPointToScreen(p2,sphereScene.camera,&r2);
+
+    if (r1.z > 0 && r2.z > 0)
+      drawLine(r1.x,r1.y,r2.x,r2.y,100,200,300);  
+  }
+}
+
+#define SPHERE_VERTEX_COUNT 42
+const S3L_Unit sphereVertices[SPHERE_VERTEX_COUNT * 3] = {
+      0,  -512,     0,        // 0
+    370,  -228,  -269,        // 3
+   -141,  -228,  -435,        // 6
+   -457,  -228,     0,        // 9
+   -141,  -228,   435,        // 12
+    370,  -228,   269,        // 15
+    141,   228,  -435,        // 18
+   -370,   228,  -269,        // 21
+   -370,   228,   269,        // 24
+    141,   228,   435,        // 27
+    457,   228,     0,        // 30
+      0,   512,     0,        // 33
+    -83,  -435,  -255,        // 36
+    217,  -435,  -158,        // 39
+    134,  -269,  -414,        // 42
+    435,  -269,     0,        // 45
+    217,  -435,   158,        // 48
+   -269,  -435,     0,        // 51
+   -352,  -269,  -255,        // 54
+    -83,  -435,   255,        // 57
+   -352,  -269,   255,        // 60
+    134,  -269,   414,        // 63
+    486,     0,  -158,        // 66
+    486,     0,   158,        // 69
+      0,     0,  -512,        // 72
+    300,     0,  -414,        // 75
+   -486,     0,  -158,        // 78
+   -300,     0,  -414,        // 81
+   -300,     0,   414,        // 84
+   -486,     0,   158,        // 87
+    300,     0,   414,        // 90
+      0,     0,   512,        // 93
+    352,   269,  -255,        // 96
+   -134,   269,  -414,        // 99
+   -435,   269,     0,        // 102
+   -134,   269,   414,        // 105
+    352,   269,   255,        // 108
+     83,   435,  -255,        // 111
+    269,   435,     0,        // 114
+   -217,   435,  -158,        // 117
+   -217,   435,   158,        // 120
+     83,   435,   255         // 123
+}; // sphereVertices
+
+#define SPHERE_TRIANGLE_COUNT 80
+const S3L_Index sphereTriangleIndices[SPHERE_TRIANGLE_COUNT * 3] = {
+      0,    13,    12,        // 0
+      1,    13,    15,        // 3
+      0,    12,    17,        // 6
+      0,    17,    19,        // 9
+      0,    19,    16,        // 12
+      1,    15,    22,        // 15
+      2,    14,    24,        // 18
+      3,    18,    26,        // 21
+      4,    20,    28,        // 24
+      5,    21,    30,        // 27
+      1,    22,    25,        // 30
+      2,    24,    27,        // 33
+      3,    26,    29,        // 36
+      4,    28,    31,        // 39
+      5,    30,    23,        // 42
+      6,    32,    37,        // 45
+      7,    33,    39,        // 48
+      8,    34,    40,        // 51
+      9,    35,    41,        // 54
+     10,    36,    38,        // 57
+     38,    41,    11,        // 60
+     38,    36,    41,        // 63
+     36,     9,    41,        // 66
+     41,    40,    11,        // 69
+     41,    35,    40,        // 72
+     35,     8,    40,        // 75
+     40,    39,    11,        // 78
+     40,    34,    39,        // 81
+     34,     7,    39,        // 84
+     39,    37,    11,        // 87
+     39,    33,    37,        // 90
+     33,     6,    37,        // 93
+     37,    38,    11,        // 96
+     37,    32,    38,        // 99
+     32,    10,    38,        // 102
+     23,    36,    10,        // 105
+     23,    30,    36,        // 108
+     30,     9,    36,        // 111
+     31,    35,     9,        // 114
+     31,    28,    35,        // 117
+     28,     8,    35,        // 120
+     29,    34,     8,        // 123
+     29,    26,    34,        // 126
+     26,     7,    34,        // 129
+     27,    33,     7,        // 132
+     27,    24,    33,        // 135
+     24,     6,    33,        // 138
+     25,    32,     6,        // 141
+     25,    22,    32,        // 144
+     22,    10,    32,        // 147
+     30,    31,     9,        // 150
+     30,    21,    31,        // 153
+     21,     4,    31,        // 156
+     28,    29,     8,        // 159
+     28,    20,    29,        // 162
+     20,     3,    29,        // 165
+     26,    27,     7,        // 168
+     26,    18,    27,        // 171
+     18,     2,    27,        // 174
+     24,    25,     6,        // 177
+     24,    14,    25,        // 180
+     14,     1,    25,        // 183
+     22,    23,    10,        // 186
+     22,    15,    23,        // 189
+     15,     5,    23,        // 192
+     16,    21,     5,        // 195
+     16,    19,    21,        // 198
+     19,     4,    21,        // 201
+     19,    20,     4,        // 204
+     19,    17,    20,        // 207
+     17,     3,    20,        // 210
+     17,    18,     3,        // 213
+     17,    12,    18,        // 216
+     12,     2,    18,        // 219
+     15,    16,     5,        // 222
+     15,    13,    16,        // 225
+     13,     0,    16,        // 228
+     12,    14,     2,        // 231
+     12,    13,    14,        // 234
+     13,     1,    14         // 237
+}; // sphereTriangleIndices
+
+int main(void)
+{
+  SDL_Window *window = SDL_CreateWindow("test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, S3L_RESOLUTION_X, S3L_RESOLUTION_Y, SDL_WINDOW_SHOWN); 
+  SDL_Renderer *renderer = SDL_CreateRenderer(window,-1,0);
+  SDL_Texture *textureSDL = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STATIC, S3L_RESOLUTION_X, S3L_RESOLUTION_Y);
+  SDL_Surface *screenSurface = SDL_GetWindowSurface(window);
+  SDL_Event event;
+
+  int running = 1;
+
+  S3L_Model3D sphereModel;
+
+  S3L_model3DInit(sphereVertices,SPHERE_VERTEX_COUNT,sphereTriangleIndices,
+    SPHERE_TRIANGLE_COUNT,&sphereModel);
+
+  S3L_model3DInit(cubeVertices,S3L_CUBE_VERTEX_COUNT,cubeTriangles,
+    S3L_CUBE_TRIANGLE_COUNT,&cube);
+
+  S3L_sceneInit(&sphereModel,1,&sphereScene);
+  
+  sphereScene.camera.transform.translation.z = -512 -512 -512;
+
+  int frame = 0;
+
+TPE_Joint joints[100];
+TPE_Connection connections[100];
+
+#define MASS 200
+
+switch (1)
+{
+  case 0:
+    TPE_make2Line(joints,connections,1500,512);
+    TPE_bodyInit(bodies,joints,2,connections,1,MASS);
+    break;
+
+  case 1:
+    TPE_makeBox(joints,connections,1000,1000,1000,500);
+//    TPE_bodyInit(bodies,joints,8,connections,16,MASS);
+TPE_bodyInit(bodies,joints,1,connections,0,MASS);
+    break;
+
+  case 2:
+    TPE_makeCenterRect(joints,connections,1300,1000,512);
+    TPE_bodyInit(bodies,joints,5,connections,8,MASS);
+    break;
+
+  case 3:
+    TPE_makeCenterBox(joints,connections,1000,1000,1000,512);
+    joints[8].sizeDivided *= 3;
+    joints[8].sizeDivided /= 2;
+    TPE_bodyInit(bodies,joints,9,connections,18,MASS);
+    break;
+
+  case 4:
+    TPE_makeTriangle(joints,connections,2000,512);
+    TPE_bodyInit(bodies,joints,3,connections,3,MASS);
+    break;
+
+  case 5:
+    TPE_make2Line(joints,connections,1500,200);
+    TPE_bodyInit(bodies,joints,1,connections,0,MASS);
+    break;
+
+  default: break;
+}
+
+//TPE_make2Line(joints + 20,connections + 20,1000,200);
+//TPE_bodyInit(bodies + 1,joints + 20,1,connections + 20,0,MASS);
+
+TPE_makeBox(joints + 20,connections + 20,1000,1000,1000,500);
+TPE_bodyInit(bodies + 1,joints + 20,8,connections + 20,16,100);
+
+//bodies[0].flags |= TPE_BODY_FLAG_SOFT;
+
+//bodies[0].elasticity = 256;
+
+
+
+
+TPE_worldInit(&world,bodies,2,environmentDistance);
+
+#define FR 256
+world.bodies[0].friction = FR;
+world.bodies[1].friction = FR;
+
+TPE_bodyMove(world.bodies,TPE_vec3(500,-200,400));
+TPE_bodyMove(&world.bodies[1],TPE_vec3(-500,800,400));
+
+
+TPE_bodyStop(world.bodies);
+TPE_bodyStop(world.bodies + 1);
+
+TPE_bodyAccelerate(world.bodies,TPE_vec3(-50,0,0));
+TPE_bodyAccelerate(world.bodies + 1,TPE_vec3(50,0,0));
+
+//TPE_bodyRotate(world.bodies,TPE_vec3(0,0,200));
+
+
+
+
+  //-------
+
+  int time;
+
+  while (running)
+  {
+    time = SDL_GetTicks();
+
+    for (uint32_t i = 0; i < PIXELS_SIZE; ++i)
+      pixels[i] = 0;
+
+    S3L_newFrame();
+
+
+//TPE_bodiesResolveCollision(world.bodies,world.bodies + 1);
+
+
+
+for (int i = 0; i < world.bodyCount; ++i)
+{
+
+
+if (!(world.bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED))
+  TPE_bodyAccelerate(world.bodies + i,TPE_vec3(0,-6,0));
+
+}
+
+TPE_worldStep(&world);
+
+
+    while (SDL_PollEvent(&event))
+    {
+      if (event.type == SDL_QUIT)
+        running = 0;
+      else if (event.type == SDL_KEYDOWN)
+      {
+        if (event.key.keysym.scancode == SDL_SCANCODE_Q || event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
+          running = 0;
+      }
+    }
+
+    const uint8_t *state = SDL_GetKeyboardState(NULL);
+
+    S3L_Vec4 camF, camR;
+ 
+#define SHIFT_STEP 50
+#define ROT_STEP 5
+
+   S3L_rotationToDirections(sphereScene.camera.transform.rotation,SHIFT_STEP,&camF,&camR,0);
+
+TPE_Vec3 forw = TPE_vec3Minus( 
+  bodies[0].joints[2].position,
+  bodies[0].joints[0].position);
+
+TPE_Vec3 righ = TPE_vec3Minus( 
+  bodies[0].joints[0].position,
+  bodies[0].joints[1].position);
+/*
+forw = TPE_vec3Plus(forw,
+  TPE_vec3Minus(
+  bodies[0].joints[6].position,
+  bodies[0].joints[4].position)
+  );
+
+righ = TPE_vec3Plus(righ,
+  TPE_vec3Minus(
+  bodies[0].joints[4].position,
+  bodies[0].joints[5].position)
+  );
+*/
+TPE_Vec3 rrrr = TPE_orientationFromVecs(forw,righ);
+
+cube.transform.rotation.x = rrrr.x;
+cube.transform.rotation.y = rrrr.y;
+cube.transform.rotation.z = rrrr.z;
+
+cube.transform.scale.x = 600;
+cube.transform.scale.y = cube.transform.scale.x;
+cube.transform.scale.z = cube.transform.scale.x;
+
+TPE_Vec3 ppp = TPE_bodyGetCenter(&bodies[0]);
+
+cube.transform.translation.x = ppp.x;
+cube.transform.translation.y = ppp.y;
+cube.transform.translation.z = ppp.z;
+
+TPE_Vec3 camPos = TPE_vec3(
+  sphereScene.camera.transform.translation.x,
+  sphereScene.camera.transform.translation.y,
+  sphereScene.camera.transform.translation.z);
+TPE_Vec3 camRot = TPE_vec3(
+  sphereScene.camera.transform.rotation.x,
+  sphereScene.camera.transform.rotation.y,
+  sphereScene.camera.transform.rotation.z);
+
+//drawEnv(TPE_vec3(-100,-100,-100),100,5);
+
+//for (int i = 0; i < world.bodyCount; ++i)
+//  drawBody(&(world.bodies[i]),100 * i);
+
+
+sphereScene.models = &cube;
+S3L_newFrame();
+//S3L_drawScene(sphereScene);
+sphereScene.models = &sphereModel;
+
+TPE_worldDebugDraw(&world,debugDrawPixel,
+camPos,camRot,TPE_vec3(S3L_RESOLUTION_X,S3L_RESOLUTION_Y,sphereScene.camera.focalLength));
+
+
+/*
+draw3DLine(0,0,0,forw.x,forw.y,forw.z);
+draw3DLine(0,0,0,righ.x,righ.y,righ.z);
+*/
+
+    SDL_UpdateTexture(textureSDL,NULL,pixels,S3L_RESOLUTION_X * sizeof(uint32_t));
+
+
+
+    if (state[SDL_SCANCODE_LSHIFT])
+    {
+      if (state[SDL_SCANCODE_UP])
+        S3L_vec3Add(&sphereScene.camera.transform.translation,camF);
+      else if (state[SDL_SCANCODE_DOWN])
+        S3L_vec3Sub(&sphereScene.camera.transform.translation,camF);
+      else if (state[SDL_SCANCODE_LEFT])
+        S3L_vec3Sub(&sphereScene.camera.transform.translation,camR);
+      else if (state[SDL_SCANCODE_RIGHT])
+        S3L_vec3Add(&sphereScene.camera.transform.translation,camR);
+    }
+    else
+    {
+      if (state[SDL_SCANCODE_UP])
+        sphereScene.camera.transform.rotation.x += ROT_STEP;
+      else if (state[SDL_SCANCODE_DOWN])
+        sphereScene.camera.transform.rotation.x -= ROT_STEP;
+      else if (state[SDL_SCANCODE_LEFT])
+        sphereScene.camera.transform.rotation.y += ROT_STEP;
+      else if (state[SDL_SCANCODE_RIGHT])
+        sphereScene.camera.transform.rotation.y -= ROT_STEP;
+      else if (state[SDL_SCANCODE_K])
+        sphereScene.camera.transform.rotation.z += ROT_STEP;
+      else if (state[SDL_SCANCODE_L])
+        sphereScene.camera.transform.rotation.z -= ROT_STEP;
+    }
+
+if (state[SDL_SCANCODE_M])
+{
+  TPE_bodyWake(world.bodies);
+
+  TPE_bodySpin(bodies,TPE_vec3(50,40,100));
+
+  TPE_bodyAccelerate(bodies,TPE_vec3(
+   500,
+   500,
+   30));
+}
+
+#define S 10
+if (state[SDL_SCANCODE_J])
+  TPE_bodyAccelerate(bodies,TPE_vec3(S,0,0));
+else if (state[SDL_SCANCODE_G])
+  TPE_bodyAccelerate(bodies,TPE_vec3(-S,0,0));
+
+if (state[SDL_SCANCODE_Y])
+  TPE_bodyAccelerate(bodies,TPE_vec3(0,0,S));
+else if (state[SDL_SCANCODE_H])
+  TPE_bodyAccelerate(bodies,TPE_vec3(0,0,-S));
+#undef S
+
+#define SHIFT_STEP 50
+
+    if (state[SDL_SCANCODE_P])
+      sphereScene.camera.transform.translation.y += SHIFT_STEP;
+    else if (state[SDL_SCANCODE_O])
+      sphereScene.camera.transform.translation.y -= SHIFT_STEP;
+
+#undef SHIFT_STEP
+
+    SDL_RenderClear(renderer);
+    SDL_RenderCopy(renderer,textureSDL,NULL,NULL);
+    SDL_RenderPresent(renderer);
+
+    time = time + MSPF - SDL_GetTicks();
+
+    if (time > 1)
+      usleep(time * 1000);
+
+    frame++;
+  }
+
+  return 0;
+}

+ 17 - 17
programs/player.c

@@ -1,4 +1,4 @@
-//#define SCALE_3D_RENDERING 1
+/** Demo showing a simple first person movement. */
 
 #define S3L_NEAR_CROSS_STRATEGY 2
 #define S3L_PERSPECTIVE_CORRECTION 2
@@ -7,17 +7,17 @@
 #include "levelModel.h"
 
 TPE_Unit elevatorHeight;
-
 TPE_Unit ramp[6] = { 1600,0, -500,1400, -700,0 };
 TPE_Unit ramp2[6] = { 2000,-5000, 1500,1700, -5000,-500 };
 
 TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD)
 {
+  // manually created environment to match the 3D model of it
   TPE_ENV_START( TPE_envAABoxInside(p,TPE_vec3(0,2450,-2100),TPE_vec3(12600,5000,10800)),p )
   TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(-5693,0,-6580),TPE_vec3(4307,20000,3420)),p )
   TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(-10000,-1000,-10000),TPE_vec3(11085,2500,9295)),p )
-  TPE_ENV_NEXT ( TPE_envAATriPrism(p,TPE_vec3(-5400,0,0),ramp,3000,2), p)
-  TPE_ENV_NEXT ( TPE_envAATriPrism(p,TPE_vec3(2076,651,-6780),ramp2,3000,0), p)
+  TPE_ENV_NEXT( TPE_envAATriPrism(p,TPE_vec3(-5400,0,0),ramp,3000,2), p)
+  TPE_ENV_NEXT( TPE_envAATriPrism(p,TPE_vec3(2076,651,-6780),ramp2,3000,0), p)
   TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(7000,0,-8500),TPE_vec3(3405,2400,3183)),p )
   TPE_ENV_NEXT( TPE_envSphere(p,TPE_vec3(2521,-100,-3799),1200),p )
   TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(5300,elevatorHeight,-4400),TPE_vec3(1000,elevatorHeight,1000)),p )
@@ -42,13 +42,14 @@ int main(void)
 {
   helper_init();
   levelModelInit();
-
   updateDirection();
 
   ballRot = TPE_vec3(0,0,0);
 
   tpe_world.environmentFunction = environmentDistance;
 
+  /* normally player bodies are approximated with capsules -- since we don't
+  have these, we'll use a body consisting of two spheres: */
   helper_add2Line(400,300,400);
 
   playerBody = &(tpe_world.bodies[0]);
@@ -57,14 +58,15 @@ int main(void)
   TPE_bodyRotateByAxis(&tpe_world.bodies[0],TPE_vec3(0,0,TPE_F / 4));
   playerBody->elasticity = 0;
   playerBody->friction = 0;   
-  playerBody->flags |= TPE_BODY_FLAG_NONROTATING;
+  playerBody->flags |= TPE_BODY_FLAG_NONROTATING; // make it always upright
   groundDist = TPE_JOINT_SIZE(playerBody->joints[0]) + 30;
 
+  // add two interactive bodies:
+
   helper_addBall(1000,100);
   TPE_bodyMoveBy(&tpe_world.bodies[1],TPE_vec3(-1000,1000,0));
   tpe_world.bodies[1].elasticity = 400;
   tpe_world.bodies[1].friction = 100;
-
   ballPreviousPos = tpe_world.bodies[1].joints[0].position;
 
   helper_addCenterRect(600,600,400,50);
@@ -81,12 +83,12 @@ int main(void)
     if (jumpCountdown > 0)
       jumpCountdown--;
 
-    TPE_Vec3 groundPoint = environmentDistance(playerBody->joints[0].position,groundDist);
+    TPE_Vec3 groundPoint =
+      environmentDistance(playerBody->joints[0].position,groundDist);
 
     onGround = (playerBody->flags & TPE_BODY_FLAG_DEACTIVATED) ||
      (TPE_DISTANCE(playerBody->joints[0].position,groundPoint)
-     <= groundDist && groundPoint.y < playerBody->joints[0].position.y - groundDist / 2 
-      );
+     <= groundDist && groundPoint.y < playerBody->joints[0].position.y - groundDist / 2);
 
     if (!onGround)
     {
@@ -99,9 +101,7 @@ int main(void)
         128,512,512)) <= groundDist;
     }
 
-    elevatorHeight =
-(1250 * (TPE_sin(helper_frame * 4) + TPE_F)) /
-(2 * TPE_F);
+    elevatorHeight = (1250 * (TPE_sin(helper_frame * 4) + TPE_F)) / (2 * TPE_F);
 
     s3l_scene.camera.transform.translation.x = playerBody->joints[0].position.x;
     s3l_scene.camera.transform.translation.z = playerBody->joints[0].position.z;
@@ -114,6 +114,7 @@ int main(void)
 
     s3l_scene.camera.transform.rotation.y = -1 * playerRotation;
 
+    // fake the sphere rotation (since a single joint doesn't rotate itself):
     TPE_Vec3 ballRoll = TPE_fakeSphereRotation(ballPreviousPos,
       tpe_world.bodies[1].joints[0].position,1000);
 
@@ -134,7 +135,7 @@ int main(void)
         jumpCountdown = 8;
       }
 
-#define D 16 // just some vector divisor to make the speed slower
+#define D 15 // just some vector divisor to make the speed slower
       if (sdl_keyboard[SDL_SCANCODE_UP] || sdl_keyboard[SDL_SCANCODE_W])
       {
         playerBody->joints[0].velocity[0] += playerDirectionVec.x / D;
@@ -179,9 +180,8 @@ int main(void)
     helper_drawModel(&levelModel,TPE_vec3(0,0,0),TPE_vec3(600,600,600), 
       TPE_vec3(0,0,0));
 
-helper_draw3DBox(
-TPE_vec3(5300,elevatorHeight,-4400),
-TPE_vec3(2000,2 * elevatorHeight,2000),TPE_vec3(0,0,0));
+    helper_draw3DBox(TPE_vec3(5300,elevatorHeight,-4400),
+      TPE_vec3(2000,2 * elevatorHeight,2000),TPE_vec3(0,0,0));
 
     helper_set3DColor(200,50,0);
 

+ 2 - 1
programs/test.c

@@ -1,4 +1,5 @@
-/** General automatic test for tinyphysicsengine, it should always pass. */
+/** General automatic test for tinyphysicsengine, this should always be run
+  and passed before publishing a new version of TPE. */
 
 #include "../tinyphysicsengine.h"
 #include <stdio.h>

+ 1 - 1
programs/testGeneral.c

@@ -35,7 +35,7 @@ int main(void)
   s3l_scene.camera.transform.translation.z = -3000;
   s3l_scene.camera.transform.translation.y = 2000;
   s3l_scene.camera.transform.translation.x = 0;
-  s3l_scene.camera.transform.rotation.y = TPE_FRACTIONS_PER_UNIT / 16;
+  s3l_scene.camera.transform.rotation.y = TPE_F / 16;
 
 #define addBody(x,y,z,f) \
   helper_addBox(700,700,700,300,500); \

+ 13 - 44
programs/water.c

@@ -2,13 +2,16 @@
 
 #define CAMERA_STEP 200
 
-#include "helper.h"
-
 #define GRID_RESOLUTION 8
 #define GRID_STEP 1000
 #define JOINT_SIZE 100
 #define BALL_SIZE 700
 
+#define HEIGHTMAP_3D_RESOLUTION GRID_RESOLUTION
+#define HEIGHTMAP_3D_STEP GRID_STEP
+
+#include "helper.h"
+
 #define ROOM_SIZE (GRID_RESOLUTION * GRID_STEP + JOINT_SIZE)
 
 TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD)
@@ -22,10 +25,6 @@ TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD)
 TPE_Joint joints[WATER_JOINTS + 1];
 TPE_Connection connections[WATER_CONNECTIONS];
 
-S3L_Unit vertices[WATER_JOINTS * 3];
-S3L_Index triangles[((GRID_RESOLUTION - 1) * (GRID_RESOLUTION - 1) * 2) * 3];
-S3L_Model3D model;
-
 TPE_Body bodies[2];
 
 TPE_Vec3 jointPlace(int index)
@@ -49,16 +48,10 @@ int main(void)
 
   // build the grid:
 
-  int index = 0;
+  for (int i = 0; i < HEIGHTMAP_3D_POINTS; ++i)
+    joints[i] = TPE_joint(helper_heightmapPointLocation(i),JOINT_SIZE);
 
-  for (int j = 0; j < GRID_RESOLUTION; ++j)
-    for (int i = 0; i < GRID_RESOLUTION; ++i)
-    {
-      joints[j * GRID_RESOLUTION + i] = TPE_joint(jointPlace(index),JOINT_SIZE);
-      index++;
-    }
-
-  index = 0;
+  int index = 0;
 
   for (int j = 0; j < GRID_RESOLUTION; ++j)
     for (int i = 0; i < GRID_RESOLUTION - 1; ++i)
@@ -74,30 +67,6 @@ int main(void)
       index++;
     }
 
-index = 0;
-
-for (int j = 0; j < GRID_RESOLUTION - 1; ++j)
-  for (int i = 0; i < GRID_RESOLUTION - 1; ++i)
-  {
-triangles[index] = j * GRID_RESOLUTION + i;
-triangles[index + 1] = triangles[index] + 1;
-triangles[index + 2] = triangles[index + 1] + GRID_RESOLUTION;
-
-triangles[index + 3] = triangles[index];
-triangles[index + 4] = triangles[index + 1] + GRID_RESOLUTION;
-triangles[index + 5] = triangles[index] + GRID_RESOLUTION;
-
-index += 6;
-  }
-
-S3L_model3DInit(
-  vertices,
-  WATER_JOINTS * 3,
-  triangles,
-  ((GRID_RESOLUTION - 1) * (GRID_RESOLUTION - 1) * 2),
-  &model);
-
-
   TPE_bodyInit(&bodies[0],joints,WATER_JOINTS,connections,WATER_CONNECTIONS,
     1000);
 
@@ -118,7 +87,7 @@ S3L_model3DInit(
 TPE_bodyActivate(&bodies[0]);
 TPE_bodyActivate(&bodies[1]);
 
-S3L_Unit *v = vertices;
+S3L_Unit *v = heightmapVertices;
 
 for (int i = 0; i < WATER_JOINTS; ++i)
 {
@@ -154,12 +123,12 @@ for (int index = 0; index < WATER_JOINTS; ++index)
     else if (sdl_keyboard[SDL_SCANCODE_X])
       TPE_bodyAccelerate(&bodies[1],TPE_vec3(0,-1 * ACC,0));
 
-helper_set3dColor(255,0,0);
+helper_set3DColor(255,0,0);
 
-helper_draw3dSphere(bodies[1].joints[0].position,TPE_vec3(BALL_SIZE,BALL_SIZE,BALL_SIZE),TPE_vec3(0,0,0));
+helper_draw3DSphere(bodies[1].joints[0].position,TPE_vec3(BALL_SIZE,BALL_SIZE,BALL_SIZE),TPE_vec3(0,0,0));
 
-helper_set3dColor(0,100,255);
-helper_drawModel(&model,TPE_vec3(0,0,0),TPE_vec3(512,512,512),TPE_vec3(0,0,0));
+helper_set3DColor(0,100,255);
+helper_drawModel(&heightmapModel,TPE_vec3(0,0,0),TPE_vec3(512,512,512),TPE_vec3(0,0,0));
 
     if (helper_debugDrawOn)
       helper_debugDraw(1);