something

Dependencies:   PokittoLib

Fork of HelloWorld by Pokitto Community Team

Committer:
drummyfish
Date:
Sat Sep 01 18:14:19 2018 +0000
Revision:
15:a91849bc5265
Init

Who changed what in which revision?

UserRevisionLine numberNew contents of line
drummyfish 15:a91849bc5265 1 #ifndef RAYCASTLIB_H
drummyfish 15:a91849bc5265 2 #define RAYCASTLIB_H
drummyfish 15:a91849bc5265 3
drummyfish 15:a91849bc5265 4 /**
drummyfish 15:a91849bc5265 5 raycastlib - Small C header-only raycasting library for embedded and low
drummyfish 15:a91849bc5265 6 performance computers, such as Arduino. Only uses integer math and stdint
drummyfish 15:a91849bc5265 7 standard library.
drummyfish 15:a91849bc5265 8
drummyfish 15:a91849bc5265 9 author: Miloslav "drummyfish" Ciz
drummyfish 15:a91849bc5265 10 license: CC0
drummyfish 15:a91849bc5265 11
drummyfish 15:a91849bc5265 12 - Game field's bottom left corner is at [0,0].
drummyfish 15:a91849bc5265 13 - X axis goes right.
drummyfish 15:a91849bc5265 14 - Y axis goes up.
drummyfish 15:a91849bc5265 15 - Each game square is UNITS_PER_SQUARE * UNITS_PER_SQUARE.
drummyfish 15:a91849bc5265 16 - Angles are in Units, 0 means pointing right (x+) and positively rotates
drummyfish 15:a91849bc5265 17 clockwise, a full angle has UNITS_PER_SQUARE Units.
drummyfish 15:a91849bc5265 18 */
drummyfish 15:a91849bc5265 19
drummyfish 15:a91849bc5265 20 #include <stdio.h>
drummyfish 15:a91849bc5265 21
drummyfish 15:a91849bc5265 22 #include <stdint.h>
drummyfish 15:a91849bc5265 23
drummyfish 15:a91849bc5265 24 #define UNITS_PER_SQUARE 1024 ///< No. of Units in a side of a spatial square.
drummyfish 15:a91849bc5265 25
drummyfish 15:a91849bc5265 26 typedef int32_t Unit; /**< Smallest spatial unit, there is UNITS_PER_SQUARE
drummyfish 15:a91849bc5265 27 units in a square's length. This effectively
drummyfish 15:a91849bc5265 28 serves the purpose of a fixed-point arithmetic. */
drummyfish 15:a91849bc5265 29
drummyfish 15:a91849bc5265 30 #define logVector2D(v)\
drummyfish 15:a91849bc5265 31 printf("[%d,%d]\n",v.x,v.y);
drummyfish 15:a91849bc5265 32
drummyfish 15:a91849bc5265 33 #define logRay(r){\
drummyfish 15:a91849bc5265 34 printf("ray:\n");\
drummyfish 15:a91849bc5265 35 printf(" start: ");\
drummyfish 15:a91849bc5265 36 logVector2D(r.start);\
drummyfish 15:a91849bc5265 37 printf(" dir: ");\
drummyfish 15:a91849bc5265 38 logVector2D(r.direction);}\
drummyfish 15:a91849bc5265 39
drummyfish 15:a91849bc5265 40 #define logHitResult(h){\
drummyfish 15:a91849bc5265 41 printf("hit:\n");\
drummyfish 15:a91849bc5265 42 printf(" sqaure: ");\
drummyfish 15:a91849bc5265 43 logVector2D(h.square);\
drummyfish 15:a91849bc5265 44 printf(" pos: ");\
drummyfish 15:a91849bc5265 45 logVector2D(h.position);\
drummyfish 15:a91849bc5265 46 printf(" dist: %d\n", h.distance);\
drummyfish 15:a91849bc5265 47 printf(" texcoord: %d\n", h.textureCoord);}\
drummyfish 15:a91849bc5265 48
drummyfish 15:a91849bc5265 49 /// Position in 2D space.
drummyfish 15:a91849bc5265 50 typedef struct
drummyfish 15:a91849bc5265 51 {
drummyfish 15:a91849bc5265 52 int32_t y;
drummyfish 15:a91849bc5265 53 int32_t x;
drummyfish 15:a91849bc5265 54 } Vector2D;
drummyfish 15:a91849bc5265 55
drummyfish 15:a91849bc5265 56 typedef struct
drummyfish 15:a91849bc5265 57 {
drummyfish 15:a91849bc5265 58 Vector2D start;
drummyfish 15:a91849bc5265 59 Vector2D direction;
drummyfish 15:a91849bc5265 60 } Ray;
drummyfish 15:a91849bc5265 61
drummyfish 15:a91849bc5265 62 typedef struct
drummyfish 15:a91849bc5265 63 {
drummyfish 15:a91849bc5265 64 Vector2D square; ///< Collided square coordinates.
drummyfish 15:a91849bc5265 65 Vector2D position; ///< Exact collision position in Units.
drummyfish 15:a91849bc5265 66 Unit distance; /**< Euclidean distance to the hit position, or -1 if
drummyfish 15:a91849bc5265 67 no collision happened. */
drummyfish 15:a91849bc5265 68 Unit textureCoord; /**< Normalized (0 to UNITS_PER_SQUARE - 1) texture
drummyfish 15:a91849bc5265 69 coordinate. */
drummyfish 15:a91849bc5265 70 uint8_t direction; ///< Direction of hit.
drummyfish 15:a91849bc5265 71 } HitResult;
drummyfish 15:a91849bc5265 72
drummyfish 15:a91849bc5265 73 typedef struct
drummyfish 15:a91849bc5265 74 {
drummyfish 15:a91849bc5265 75 Vector2D position;
drummyfish 15:a91849bc5265 76 Unit direction;
drummyfish 15:a91849bc5265 77 Vector2D resolution;
drummyfish 15:a91849bc5265 78 Unit fovAngle;
drummyfish 15:a91849bc5265 79 Unit height;
drummyfish 15:a91849bc5265 80 } Camera;
drummyfish 15:a91849bc5265 81
drummyfish 15:a91849bc5265 82 typedef struct
drummyfish 15:a91849bc5265 83 {
drummyfish 15:a91849bc5265 84 Vector2D position; ///< On-screen position.
drummyfish 15:a91849bc5265 85 int8_t isWall; ///< Whether the pixel is a wall or a floor(/ceiling).
drummyfish 15:a91849bc5265 86 Unit depth; ///< Corrected depth.
drummyfish 15:a91849bc5265 87 HitResult hit; ///< Corresponding ray hit.
drummyfish 15:a91849bc5265 88 } PixelInfo;
drummyfish 15:a91849bc5265 89
drummyfish 15:a91849bc5265 90 typedef struct
drummyfish 15:a91849bc5265 91 {
drummyfish 15:a91849bc5265 92 uint16_t maxHits;
drummyfish 15:a91849bc5265 93 uint16_t maxSteps;
drummyfish 15:a91849bc5265 94 } RayConstraints;
drummyfish 15:a91849bc5265 95
drummyfish 15:a91849bc5265 96 /**
drummyfish 15:a91849bc5265 97 Function used to retrieve the cells of the rendered scene. It should return
drummyfish 15:a91849bc5265 98 a "type" of given square as an integer (e.g. square height) - between squares
drummyfish 15:a91849bc5265 99 that return different numbers there is considered to be a collision.
drummyfish 15:a91849bc5265 100 */
drummyfish 15:a91849bc5265 101 typedef Unit (*ArrayFunction)(int16_t x, int16_t y);
drummyfish 15:a91849bc5265 102
drummyfish 15:a91849bc5265 103 typedef void (*PixelFunction)(PixelInfo info);
drummyfish 15:a91849bc5265 104
drummyfish 15:a91849bc5265 105 typedef void
drummyfish 15:a91849bc5265 106 (*ColumnFunction)(HitResult *hits, uint16_t hitCount, uint16_t x, Ray ray);
drummyfish 15:a91849bc5265 107
drummyfish 15:a91849bc5265 108 /**
drummyfish 15:a91849bc5265 109 Simple-interface function to cast a single ray.
drummyfish 15:a91849bc5265 110
drummyfish 15:a91849bc5265 111 @return The first collision result.
drummyfish 15:a91849bc5265 112 */
drummyfish 15:a91849bc5265 113 HitResult castRay(Ray ray, ArrayFunction arrayFunc);
drummyfish 15:a91849bc5265 114
drummyfish 15:a91849bc5265 115 /**
drummyfish 15:a91849bc5265 116 Casts a single ray and returns a list of collisions.
drummyfish 15:a91849bc5265 117 */
drummyfish 15:a91849bc5265 118 void castRayMultiHit(Ray ray, ArrayFunction arrayFunc, HitResult *hitResults,
drummyfish 15:a91849bc5265 119 uint16_t *hitResultsLen, RayConstraints constraints);
drummyfish 15:a91849bc5265 120
drummyfish 15:a91849bc5265 121 Vector2D angleToDirection(Unit angle);
drummyfish 15:a91849bc5265 122 Unit cosInt(Unit input);
drummyfish 15:a91849bc5265 123 Unit sinInt(Unit input);
drummyfish 15:a91849bc5265 124
drummyfish 15:a91849bc5265 125 /// Normalizes given vector to have UNITS_PER_SQUARE length.
drummyfish 15:a91849bc5265 126 Vector2D normalize(Vector2D v);
drummyfish 15:a91849bc5265 127
drummyfish 15:a91849bc5265 128 /// Computes a cos of an angle between two vectors.
drummyfish 15:a91849bc5265 129 Unit vectorsAngleCos(Vector2D v1, Vector2D v2);
drummyfish 15:a91849bc5265 130
drummyfish 15:a91849bc5265 131 uint16_t sqrtInt(uint32_t value);
drummyfish 15:a91849bc5265 132 Unit dist(Vector2D p1, Vector2D p2);
drummyfish 15:a91849bc5265 133 Unit len(Vector2D v);
drummyfish 15:a91849bc5265 134
drummyfish 15:a91849bc5265 135 /**
drummyfish 15:a91849bc5265 136 Converts an angle in whole degrees to an angle in Units that this library
drummyfish 15:a91849bc5265 137 uses.
drummyfish 15:a91849bc5265 138 */
drummyfish 15:a91849bc5265 139 Unit degreesToUnitsAngle(int16_t degrees);
drummyfish 15:a91849bc5265 140
drummyfish 15:a91849bc5265 141 ///< Computes the change in size of an object due to perspective.
drummyfish 15:a91849bc5265 142 Unit perspectiveScale(Unit originalSize, Unit distance, Unit fov);
drummyfish 15:a91849bc5265 143
drummyfish 15:a91849bc5265 144 /**
drummyfish 15:a91849bc5265 145 Casts rays for given camera view and for each hit calls a user provided
drummyfish 15:a91849bc5265 146 function.
drummyfish 15:a91849bc5265 147 */
drummyfish 15:a91849bc5265 148 void castRaysMultiHit(Camera cam, ArrayFunction arrayFunc,
drummyfish 15:a91849bc5265 149 ColumnFunction columnFunc, RayConstraints constraints);
drummyfish 15:a91849bc5265 150
drummyfish 15:a91849bc5265 151 void render(Camera cam, ArrayFunction arrayFunc, PixelFunction pixelFunc,
drummyfish 15:a91849bc5265 152 RayConstraints constraints);
drummyfish 15:a91849bc5265 153
drummyfish 15:a91849bc5265 154 //=============================================================================
drummyfish 15:a91849bc5265 155 // privates
drummyfish 15:a91849bc5265 156
drummyfish 15:a91849bc5265 157 #ifdef RAYCASTLIB_PROFILE
drummyfish 15:a91849bc5265 158 // function call counters for profiling
drummyfish 15:a91849bc5265 159 uint32_t profile_sqrtInt = 0;
drummyfish 15:a91849bc5265 160 uint32_t profile_clamp = 0;
drummyfish 15:a91849bc5265 161 uint32_t profile_cosInt = 0;
drummyfish 15:a91849bc5265 162 uint32_t profile_angleToDirection = 0;
drummyfish 15:a91849bc5265 163 uint32_t profile_dist = 0;
drummyfish 15:a91849bc5265 164 uint32_t profile_len = 0;
drummyfish 15:a91849bc5265 165 uint32_t profile_pointIsLeftOfRay = 0;
drummyfish 15:a91849bc5265 166 uint32_t profile_castRaySquare = 0;
drummyfish 15:a91849bc5265 167 uint32_t profile_castRayMultiHit = 0;
drummyfish 15:a91849bc5265 168 uint32_t profile_castRay = 0;
drummyfish 15:a91849bc5265 169 uint16_t profile_normalize = 0;
drummyfish 15:a91849bc5265 170 uint16_t profile_vectorsAngleCos = 0;
drummyfish 15:a91849bc5265 171
drummyfish 15:a91849bc5265 172 #define profileCall(c) profile_##c += 1
drummyfish 15:a91849bc5265 173
drummyfish 15:a91849bc5265 174 #define printProfile() {\
drummyfish 15:a91849bc5265 175 printf("profile:\n");\
drummyfish 15:a91849bc5265 176 printf(" sqrtInt: %d\n",profile_sqrtInt);\
drummyfish 15:a91849bc5265 177 printf(" clamp: %d\n",profile_clamp);\
drummyfish 15:a91849bc5265 178 printf(" cosInt: %d\n",profile_cosInt);\
drummyfish 15:a91849bc5265 179 printf(" angleToDirection: %d\n",profile_angleToDirection);\
drummyfish 15:a91849bc5265 180 printf(" dist: %d\n",profile_dist);\
drummyfish 15:a91849bc5265 181 printf(" len: %d\n",profile_len);\
drummyfish 15:a91849bc5265 182 printf(" pointIsLeftOfRay: %d\n",profile_pointIsLeftOfRay);\
drummyfish 15:a91849bc5265 183 printf(" castRaySquare: %d\n",profile_castRaySquare);\
drummyfish 15:a91849bc5265 184 printf(" castRayMultiHit : %d\n",profile_castRayMultiHit);\
drummyfish 15:a91849bc5265 185 printf(" castRay: %d\n",profile_castRay);\
drummyfish 15:a91849bc5265 186 printf(" normalize: %d\n",profile_normalize);\
drummyfish 15:a91849bc5265 187 printf(" vectorsAngleCos: %d\n",profile_vectorsAngleCos); }
drummyfish 15:a91849bc5265 188 #else
drummyfish 15:a91849bc5265 189 #define profileCall(c)
drummyfish 15:a91849bc5265 190 #endif
drummyfish 15:a91849bc5265 191
drummyfish 15:a91849bc5265 192 Unit clamp(Unit value, Unit valueMin, Unit valueMax)
drummyfish 15:a91849bc5265 193 {
drummyfish 15:a91849bc5265 194 profileCall(clamp);
drummyfish 15:a91849bc5265 195
drummyfish 15:a91849bc5265 196 if (value < valueMin)
drummyfish 15:a91849bc5265 197 return valueMin;
drummyfish 15:a91849bc5265 198
drummyfish 15:a91849bc5265 199 if (value > valueMax)
drummyfish 15:a91849bc5265 200 return valueMax;
drummyfish 15:a91849bc5265 201
drummyfish 15:a91849bc5265 202 return value;
drummyfish 15:a91849bc5265 203 }
drummyfish 15:a91849bc5265 204
drummyfish 15:a91849bc5265 205 // Bhaskara's cosine approximation formula
drummyfish 15:a91849bc5265 206 #define trigHelper(x) (UNITS_PER_SQUARE *\
drummyfish 15:a91849bc5265 207 (UNITS_PER_SQUARE / 2 * UNITS_PER_SQUARE / 2 - 4 * (x) * (x)) /\
drummyfish 15:a91849bc5265 208 (UNITS_PER_SQUARE / 2 * UNITS_PER_SQUARE / 2 + (x) * (x)))
drummyfish 15:a91849bc5265 209
drummyfish 15:a91849bc5265 210 Unit cosInt(Unit input)
drummyfish 15:a91849bc5265 211 {
drummyfish 15:a91849bc5265 212 profileCall(cosInt);
drummyfish 15:a91849bc5265 213
drummyfish 15:a91849bc5265 214 input = input % UNITS_PER_SQUARE;
drummyfish 15:a91849bc5265 215
drummyfish 15:a91849bc5265 216 if (input < 0)
drummyfish 15:a91849bc5265 217 input = UNITS_PER_SQUARE + input;
drummyfish 15:a91849bc5265 218
drummyfish 15:a91849bc5265 219 if (input < UNITS_PER_SQUARE / 4)
drummyfish 15:a91849bc5265 220 return trigHelper(input);
drummyfish 15:a91849bc5265 221 else if (input < UNITS_PER_SQUARE / 2)
drummyfish 15:a91849bc5265 222 return -1 * trigHelper(UNITS_PER_SQUARE / 2 - input);
drummyfish 15:a91849bc5265 223 else if (input < 3 * UNITS_PER_SQUARE / 4)
drummyfish 15:a91849bc5265 224 return -1 * trigHelper(input - UNITS_PER_SQUARE / 2);
drummyfish 15:a91849bc5265 225 else
drummyfish 15:a91849bc5265 226 return trigHelper(UNITS_PER_SQUARE - input);
drummyfish 15:a91849bc5265 227 }
drummyfish 15:a91849bc5265 228
drummyfish 15:a91849bc5265 229 #undef trigHelper
drummyfish 15:a91849bc5265 230
drummyfish 15:a91849bc5265 231 Unit sinInt(Unit input)
drummyfish 15:a91849bc5265 232 {
drummyfish 15:a91849bc5265 233 return cosInt(input - UNITS_PER_SQUARE / 4);
drummyfish 15:a91849bc5265 234 }
drummyfish 15:a91849bc5265 235
drummyfish 15:a91849bc5265 236 Vector2D angleToDirection(Unit angle)
drummyfish 15:a91849bc5265 237 {
drummyfish 15:a91849bc5265 238 profileCall(angleToDirection);
drummyfish 15:a91849bc5265 239
drummyfish 15:a91849bc5265 240 Vector2D result;
drummyfish 15:a91849bc5265 241
drummyfish 15:a91849bc5265 242 result.x = cosInt(angle);
drummyfish 15:a91849bc5265 243 result.y = -1 * sinInt(angle);
drummyfish 15:a91849bc5265 244
drummyfish 15:a91849bc5265 245 return result;
drummyfish 15:a91849bc5265 246 }
drummyfish 15:a91849bc5265 247
drummyfish 15:a91849bc5265 248 uint16_t sqrtInt(uint32_t value)
drummyfish 15:a91849bc5265 249 {
drummyfish 15:a91849bc5265 250 profileCall(sqrtInt);
drummyfish 15:a91849bc5265 251
drummyfish 15:a91849bc5265 252 uint32_t result = 0;
drummyfish 15:a91849bc5265 253
drummyfish 15:a91849bc5265 254 uint32_t a = value;
drummyfish 15:a91849bc5265 255 uint32_t b = 1u << 30;
drummyfish 15:a91849bc5265 256
drummyfish 15:a91849bc5265 257 while (b > a)
drummyfish 15:a91849bc5265 258 b >>= 2;
drummyfish 15:a91849bc5265 259
drummyfish 15:a91849bc5265 260 while (b != 0)
drummyfish 15:a91849bc5265 261 {
drummyfish 15:a91849bc5265 262 if (a >= result + b)
drummyfish 15:a91849bc5265 263 {
drummyfish 15:a91849bc5265 264 a -= result + b;
drummyfish 15:a91849bc5265 265 result = result + 2 * b;
drummyfish 15:a91849bc5265 266 }
drummyfish 15:a91849bc5265 267
drummyfish 15:a91849bc5265 268 b >>= 2;
drummyfish 15:a91849bc5265 269 result >>= 1;
drummyfish 15:a91849bc5265 270 }
drummyfish 15:a91849bc5265 271
drummyfish 15:a91849bc5265 272 return result;
drummyfish 15:a91849bc5265 273 }
drummyfish 15:a91849bc5265 274
drummyfish 15:a91849bc5265 275 Unit dist(Vector2D p1, Vector2D p2)
drummyfish 15:a91849bc5265 276 {
drummyfish 15:a91849bc5265 277 profileCall(dist);
drummyfish 15:a91849bc5265 278
drummyfish 15:a91849bc5265 279 int32_t dx = p2.x - p1.x;
drummyfish 15:a91849bc5265 280 int32_t dy = p2.y - p1.y;
drummyfish 15:a91849bc5265 281
drummyfish 15:a91849bc5265 282 dx = dx * dx;
drummyfish 15:a91849bc5265 283 dy = dy * dy;
drummyfish 15:a91849bc5265 284
drummyfish 15:a91849bc5265 285 return sqrtInt((uint32_t) (dx + dy));
drummyfish 15:a91849bc5265 286 }
drummyfish 15:a91849bc5265 287
drummyfish 15:a91849bc5265 288 Unit len(Vector2D v)
drummyfish 15:a91849bc5265 289 {
drummyfish 15:a91849bc5265 290 profileCall(len);
drummyfish 15:a91849bc5265 291
drummyfish 15:a91849bc5265 292 v.x *= v.x;
drummyfish 15:a91849bc5265 293 v.y *= v.y;
drummyfish 15:a91849bc5265 294
drummyfish 15:a91849bc5265 295 return sqrtInt(((uint32_t) v.x) + ((uint32_t) v.y));
drummyfish 15:a91849bc5265 296 }
drummyfish 15:a91849bc5265 297
drummyfish 15:a91849bc5265 298 int8_t pointIsLeftOfRay(Vector2D point, Ray ray)
drummyfish 15:a91849bc5265 299 {
drummyfish 15:a91849bc5265 300 profileCall(pointIsLeftOfRay);
drummyfish 15:a91849bc5265 301
drummyfish 15:a91849bc5265 302 Unit dX = point.x - ray.start.x;
drummyfish 15:a91849bc5265 303 Unit dY = point.y - ray.start.y;
drummyfish 15:a91849bc5265 304 return (ray.direction.x * dY - ray.direction.y * dX) > 0;
drummyfish 15:a91849bc5265 305 // ^ Z component of cross-product
drummyfish 15:a91849bc5265 306 }
drummyfish 15:a91849bc5265 307
drummyfish 15:a91849bc5265 308 /**
drummyfish 15:a91849bc5265 309 Casts a ray within a single square, to collide with the square borders.
drummyfish 15:a91849bc5265 310 */
drummyfish 15:a91849bc5265 311 void castRaySquare(Ray localRay, Vector2D *nextCellOff, Vector2D *collOff)
drummyfish 15:a91849bc5265 312 {
drummyfish 15:a91849bc5265 313 profileCall(castRaySquare);
drummyfish 15:a91849bc5265 314
drummyfish 15:a91849bc5265 315 nextCellOff->x = 0;
drummyfish 15:a91849bc5265 316 nextCellOff->y = 0;
drummyfish 15:a91849bc5265 317
drummyfish 15:a91849bc5265 318 Ray criticalLine = localRay;
drummyfish 15:a91849bc5265 319
drummyfish 15:a91849bc5265 320 #define helper(c1,c2,n)\
drummyfish 15:a91849bc5265 321 {\
drummyfish 15:a91849bc5265 322 nextCellOff->c1 = n;\
drummyfish 15:a91849bc5265 323 collOff->c1 = criticalLine.start.c1 - localRay.start.c1;\
drummyfish 15:a91849bc5265 324 collOff->c2 = \
drummyfish 15:a91849bc5265 325 (((int32_t) collOff->c1) * localRay.direction.c2) /\
drummyfish 15:a91849bc5265 326 ((localRay.direction.c1 == 0) ? 1 : localRay.direction.c1);\
drummyfish 15:a91849bc5265 327 }
drummyfish 15:a91849bc5265 328
drummyfish 15:a91849bc5265 329 #define helper2(n1,n2,c)\
drummyfish 15:a91849bc5265 330 if (pointIsLeftOfRay(localRay.start,criticalLine) == c)\
drummyfish 15:a91849bc5265 331 helper(y,x,n1)\
drummyfish 15:a91849bc5265 332 else\
drummyfish 15:a91849bc5265 333 helper(x,y,n2)
drummyfish 15:a91849bc5265 334
drummyfish 15:a91849bc5265 335 if (localRay.direction.x > 0)
drummyfish 15:a91849bc5265 336 {
drummyfish 15:a91849bc5265 337 criticalLine.start.x = UNITS_PER_SQUARE - 1;
drummyfish 15:a91849bc5265 338
drummyfish 15:a91849bc5265 339 if (localRay.direction.y > 0)
drummyfish 15:a91849bc5265 340 {
drummyfish 15:a91849bc5265 341 // top right
drummyfish 15:a91849bc5265 342 criticalLine.start.y = UNITS_PER_SQUARE - 1;
drummyfish 15:a91849bc5265 343 helper2(1,1,1)
drummyfish 15:a91849bc5265 344 }
drummyfish 15:a91849bc5265 345 else
drummyfish 15:a91849bc5265 346 {
drummyfish 15:a91849bc5265 347 // bottom right
drummyfish 15:a91849bc5265 348 criticalLine.start.y = 0;
drummyfish 15:a91849bc5265 349 helper2(-1,1,0)
drummyfish 15:a91849bc5265 350 }
drummyfish 15:a91849bc5265 351 }
drummyfish 15:a91849bc5265 352 else
drummyfish 15:a91849bc5265 353 {
drummyfish 15:a91849bc5265 354 criticalLine.start.x = 0;
drummyfish 15:a91849bc5265 355
drummyfish 15:a91849bc5265 356 if (localRay.direction.y > 0)
drummyfish 15:a91849bc5265 357 {
drummyfish 15:a91849bc5265 358 // top left
drummyfish 15:a91849bc5265 359 criticalLine.start.y = UNITS_PER_SQUARE - 1;
drummyfish 15:a91849bc5265 360 helper2(1,-1,0)
drummyfish 15:a91849bc5265 361 }
drummyfish 15:a91849bc5265 362 else
drummyfish 15:a91849bc5265 363 {
drummyfish 15:a91849bc5265 364 // bottom left
drummyfish 15:a91849bc5265 365 criticalLine.start.y = 0;
drummyfish 15:a91849bc5265 366 helper2(-1,-1,1)
drummyfish 15:a91849bc5265 367 }
drummyfish 15:a91849bc5265 368 }
drummyfish 15:a91849bc5265 369
drummyfish 15:a91849bc5265 370 #undef helper2
drummyfish 15:a91849bc5265 371 #undef helper
drummyfish 15:a91849bc5265 372
drummyfish 15:a91849bc5265 373 collOff->x += nextCellOff->x;
drummyfish 15:a91849bc5265 374 collOff->y += nextCellOff->y;
drummyfish 15:a91849bc5265 375 }
drummyfish 15:a91849bc5265 376
drummyfish 15:a91849bc5265 377 void castRayMultiHit(Ray ray, ArrayFunction arrayFunc, HitResult *hitResults,
drummyfish 15:a91849bc5265 378 uint16_t *hitResultsLen, RayConstraints constraints)
drummyfish 15:a91849bc5265 379 {
drummyfish 15:a91849bc5265 380 profileCall(castRayMultiHit);
drummyfish 15:a91849bc5265 381
drummyfish 15:a91849bc5265 382 Vector2D initialPos = ray.start;
drummyfish 15:a91849bc5265 383 Vector2D currentPos = ray.start;
drummyfish 15:a91849bc5265 384
drummyfish 15:a91849bc5265 385 Vector2D currentSquare;
drummyfish 15:a91849bc5265 386
drummyfish 15:a91849bc5265 387 currentSquare.x = ray.start.x / UNITS_PER_SQUARE;
drummyfish 15:a91849bc5265 388 currentSquare.y = ray.start.y / UNITS_PER_SQUARE;
drummyfish 15:a91849bc5265 389
drummyfish 15:a91849bc5265 390 *hitResultsLen = 0;
drummyfish 15:a91849bc5265 391
drummyfish 15:a91849bc5265 392 int16_t squareType = arrayFunc(currentSquare.x,currentSquare.y);
drummyfish 15:a91849bc5265 393
drummyfish 15:a91849bc5265 394 Vector2D no, co; // next cell offset, collision offset
drummyfish 15:a91849bc5265 395
drummyfish 15:a91849bc5265 396 no.x = 0; // just to supress a warning
drummyfish 15:a91849bc5265 397 no.y = 0;
drummyfish 15:a91849bc5265 398
drummyfish 15:a91849bc5265 399 for (uint16_t i = 0; i < constraints.maxSteps; ++i)
drummyfish 15:a91849bc5265 400 {
drummyfish 15:a91849bc5265 401 int16_t currentType = arrayFunc(currentSquare.x,currentSquare.y);
drummyfish 15:a91849bc5265 402
drummyfish 15:a91849bc5265 403 if (currentType != squareType)
drummyfish 15:a91849bc5265 404 {
drummyfish 15:a91849bc5265 405 // collision
drummyfish 15:a91849bc5265 406
drummyfish 15:a91849bc5265 407 HitResult h;
drummyfish 15:a91849bc5265 408
drummyfish 15:a91849bc5265 409 h.position = currentPos;
drummyfish 15:a91849bc5265 410 h.square = currentSquare;
drummyfish 15:a91849bc5265 411 h.distance = dist(initialPos,currentPos);
drummyfish 15:a91849bc5265 412
drummyfish 15:a91849bc5265 413 if (no.y > 0)
drummyfish 15:a91849bc5265 414 h.direction = 0;
drummyfish 15:a91849bc5265 415 else if (no.x > 0)
drummyfish 15:a91849bc5265 416 h.direction = 1;
drummyfish 15:a91849bc5265 417 else if (no.y < 0)
drummyfish 15:a91849bc5265 418 h.direction = 2;
drummyfish 15:a91849bc5265 419 else
drummyfish 15:a91849bc5265 420 h.direction = 3;
drummyfish 15:a91849bc5265 421
drummyfish 15:a91849bc5265 422 hitResults[*hitResultsLen] = h;
drummyfish 15:a91849bc5265 423
drummyfish 15:a91849bc5265 424 *hitResultsLen += 1;
drummyfish 15:a91849bc5265 425
drummyfish 15:a91849bc5265 426 squareType = currentType;
drummyfish 15:a91849bc5265 427
drummyfish 15:a91849bc5265 428 if (*hitResultsLen >= constraints.maxHits)
drummyfish 15:a91849bc5265 429 break;
drummyfish 15:a91849bc5265 430 }
drummyfish 15:a91849bc5265 431
drummyfish 15:a91849bc5265 432 ray.start.x = currentPos.x % UNITS_PER_SQUARE;
drummyfish 15:a91849bc5265 433 ray.start.y = currentPos.y % UNITS_PER_SQUARE;
drummyfish 15:a91849bc5265 434
drummyfish 15:a91849bc5265 435 castRaySquare(ray,&no,&co);
drummyfish 15:a91849bc5265 436
drummyfish 15:a91849bc5265 437 currentSquare.x += no.x;
drummyfish 15:a91849bc5265 438 currentSquare.y += no.y;
drummyfish 15:a91849bc5265 439
drummyfish 15:a91849bc5265 440 // offset into the next cell
drummyfish 15:a91849bc5265 441 currentPos.x += co.x;
drummyfish 15:a91849bc5265 442 currentPos.y += co.y;
drummyfish 15:a91849bc5265 443 }
drummyfish 15:a91849bc5265 444 }
drummyfish 15:a91849bc5265 445
drummyfish 15:a91849bc5265 446 HitResult castRay(Ray ray, ArrayFunction arrayFunc)
drummyfish 15:a91849bc5265 447 {
drummyfish 15:a91849bc5265 448 profileCall(castRay);
drummyfish 15:a91849bc5265 449
drummyfish 15:a91849bc5265 450 HitResult result;
drummyfish 15:a91849bc5265 451 uint16_t len;
drummyfish 15:a91849bc5265 452 RayConstraints c;
drummyfish 15:a91849bc5265 453
drummyfish 15:a91849bc5265 454 c.maxSteps = 1000;
drummyfish 15:a91849bc5265 455 c.maxHits = 1;
drummyfish 15:a91849bc5265 456
drummyfish 15:a91849bc5265 457 castRayMultiHit(ray,arrayFunc,&result,&len,c);
drummyfish 15:a91849bc5265 458
drummyfish 15:a91849bc5265 459 if (len == 0)
drummyfish 15:a91849bc5265 460 result.distance = -1;
drummyfish 15:a91849bc5265 461
drummyfish 15:a91849bc5265 462 return result;
drummyfish 15:a91849bc5265 463 }
drummyfish 15:a91849bc5265 464
drummyfish 15:a91849bc5265 465 void castRaysMultiHit(Camera cam, ArrayFunction arrayFunc,
drummyfish 15:a91849bc5265 466 ColumnFunction columnFunc, RayConstraints constraints)
drummyfish 15:a91849bc5265 467 {
drummyfish 15:a91849bc5265 468 uint16_t fovHalf = cam.fovAngle / 2;
drummyfish 15:a91849bc5265 469
drummyfish 15:a91849bc5265 470 Vector2D dir1 = angleToDirection(cam.direction - fovHalf);
drummyfish 15:a91849bc5265 471 Vector2D dir2 = angleToDirection(cam.direction + fovHalf);
drummyfish 15:a91849bc5265 472
drummyfish 15:a91849bc5265 473 Unit dX = dir2.x - dir1.x;
drummyfish 15:a91849bc5265 474 Unit dY = dir2.y - dir1.y;
drummyfish 15:a91849bc5265 475
drummyfish 15:a91849bc5265 476 HitResult hits[constraints.maxHits];
drummyfish 15:a91849bc5265 477 Ray rays[constraints.maxHits];
drummyfish 15:a91849bc5265 478 uint16_t hitCount;
drummyfish 15:a91849bc5265 479
drummyfish 15:a91849bc5265 480 Ray r;
drummyfish 15:a91849bc5265 481 r.start = cam.position;
drummyfish 15:a91849bc5265 482
drummyfish 15:a91849bc5265 483 for (uint16_t i = 0; i < cam.resolution.x; ++i)
drummyfish 15:a91849bc5265 484 {
drummyfish 15:a91849bc5265 485 r.direction.x = dir1.x + (dX * i) / cam.resolution.x;
drummyfish 15:a91849bc5265 486 r.direction.y = dir1.y + (dY * i) / cam.resolution.x;
drummyfish 15:a91849bc5265 487
drummyfish 15:a91849bc5265 488 castRayMultiHit(r,arrayFunc,hits,&hitCount,constraints);
drummyfish 15:a91849bc5265 489
drummyfish 15:a91849bc5265 490 columnFunc(hits,hitCount,i,r);
drummyfish 15:a91849bc5265 491 }
drummyfish 15:a91849bc5265 492 }
drummyfish 15:a91849bc5265 493
drummyfish 15:a91849bc5265 494 PixelFunction _pixelFunction = 0;
drummyfish 15:a91849bc5265 495 ArrayFunction _arrayFunction = 0;
drummyfish 15:a91849bc5265 496 Camera _camera;
drummyfish 15:a91849bc5265 497 Unit _floorDepthStep = 0;
drummyfish 15:a91849bc5265 498
drummyfish 15:a91849bc5265 499 Unit _startHeight = 0;
drummyfish 15:a91849bc5265 500
drummyfish 15:a91849bc5265 501 void _columnFunction(HitResult *hits, uint16_t hitCount, uint16_t x, Ray ray)
drummyfish 15:a91849bc5265 502 {
drummyfish 15:a91849bc5265 503 int32_t y = _camera.resolution.y - 1; // on screen y, will only go upwards
drummyfish 15:a91849bc5265 504
drummyfish 15:a91849bc5265 505 Unit worldZPrev = _startHeight;
drummyfish 15:a91849bc5265 506
drummyfish 15:a91849bc5265 507 Unit previousDepth = 1;
drummyfish 15:a91849bc5265 508
drummyfish 15:a91849bc5265 509 uint16_t middleRow = _camera.resolution.y / 2;
drummyfish 15:a91849bc5265 510
drummyfish 15:a91849bc5265 511 PixelInfo p;
drummyfish 15:a91849bc5265 512 p.position.x = x;
drummyfish 15:a91849bc5265 513
drummyfish 15:a91849bc5265 514 for (uint32_t j = 0; j < hitCount; ++j)
drummyfish 15:a91849bc5265 515 {
drummyfish 15:a91849bc5265 516 HitResult hit = hits[j];
drummyfish 15:a91849bc5265 517
drummyfish 15:a91849bc5265 518 /* FIXME/TODO: The adjusted (=orthogonal, camera-space) distance could
drummyfish 15:a91849bc5265 519 possibly be computed more efficiently by not computing Euclidean
drummyfish 15:a91849bc5265 520 distance at all, but rather compute the distance of the collision
drummyfish 15:a91849bc5265 521 point from the projection plane (line). */
drummyfish 15:a91849bc5265 522
drummyfish 15:a91849bc5265 523 Unit dist = // adjusted distance
drummyfish 15:a91849bc5265 524 (hit.distance * vectorsAngleCos(angleToDirection(_camera.direction),
drummyfish 15:a91849bc5265 525 ray.direction)) / UNITS_PER_SQUARE;
drummyfish 15:a91849bc5265 526
drummyfish 15:a91849bc5265 527 dist = dist == 0 ? 1 : dist; // prevent division by zero
drummyfish 15:a91849bc5265 528
drummyfish 15:a91849bc5265 529 Unit wallHeight = _arrayFunction(hit.square.x,hit.square.y);
drummyfish 15:a91849bc5265 530
drummyfish 15:a91849bc5265 531 Unit worldZ2 = -1 * _camera.height + wallHeight;
drummyfish 15:a91849bc5265 532
drummyfish 15:a91849bc5265 533 int16_t z1Screen = middleRow -
drummyfish 15:a91849bc5265 534 perspectiveScale(
drummyfish 15:a91849bc5265 535 (worldZPrev * _camera.resolution.y) / UNITS_PER_SQUARE,
drummyfish 15:a91849bc5265 536 dist,1);
drummyfish 15:a91849bc5265 537
drummyfish 15:a91849bc5265 538 z1Screen = clamp(z1Screen,0,_camera.resolution.y - 1);
drummyfish 15:a91849bc5265 539
drummyfish 15:a91849bc5265 540 int16_t z2Screen = middleRow -
drummyfish 15:a91849bc5265 541 perspectiveScale(
drummyfish 15:a91849bc5265 542 (worldZ2 * _camera.resolution.y) / UNITS_PER_SQUARE,
drummyfish 15:a91849bc5265 543 dist,1);
drummyfish 15:a91849bc5265 544
drummyfish 15:a91849bc5265 545 z2Screen = clamp(z2Screen,0,_camera.resolution.y - 1);
drummyfish 15:a91849bc5265 546
drummyfish 15:a91849bc5265 547 Unit zTop = z1Screen < z2Screen ? z1Screen : z2Screen;
drummyfish 15:a91849bc5265 548
drummyfish 15:a91849bc5265 549 // draw floor until the wall
drummyfish 15:a91849bc5265 550
drummyfish 15:a91849bc5265 551 p.isWall = 0;
drummyfish 15:a91849bc5265 552 Unit depthDiff = dist - previousDepth;
drummyfish 15:a91849bc5265 553
drummyfish 15:a91849bc5265 554 Unit floorCameraDiff = _camera.height - worldZPrev;
drummyfish 15:a91849bc5265 555
drummyfish 15:a91849bc5265 556 for (int32_t i = y; i > zTop; --i)
drummyfish 15:a91849bc5265 557 {
drummyfish 15:a91849bc5265 558 p.position.y = i;
drummyfish 15:a91849bc5265 559 p.depth = (_camera.resolution.y - i) * _floorDepthStep + floorCameraDiff;
drummyfish 15:a91849bc5265 560 _pixelFunction(p);
drummyfish 15:a91849bc5265 561 }
drummyfish 15:a91849bc5265 562
drummyfish 15:a91849bc5265 563 // draw the wall
drummyfish 15:a91849bc5265 564
drummyfish 15:a91849bc5265 565 p.isWall = 1;
drummyfish 15:a91849bc5265 566 p.depth = dist;
drummyfish 15:a91849bc5265 567
drummyfish 15:a91849bc5265 568 for (int32_t i = z1Screen < y ? z1Screen : y; i > z2Screen; --i)
drummyfish 15:a91849bc5265 569 {
drummyfish 15:a91849bc5265 570 p.position.y = i;
drummyfish 15:a91849bc5265 571 p.hit = hit;
drummyfish 15:a91849bc5265 572 _pixelFunction(p);
drummyfish 15:a91849bc5265 573 }
drummyfish 15:a91849bc5265 574
drummyfish 15:a91849bc5265 575 y = y > zTop ? zTop : y;
drummyfish 15:a91849bc5265 576 worldZPrev = worldZ2;
drummyfish 15:a91849bc5265 577 previousDepth = dist;
drummyfish 15:a91849bc5265 578 }
drummyfish 15:a91849bc5265 579
drummyfish 15:a91849bc5265 580 // draw floor until horizon
drummyfish 15:a91849bc5265 581
drummyfish 15:a91849bc5265 582 p.isWall = 0;
drummyfish 15:a91849bc5265 583
drummyfish 15:a91849bc5265 584 Unit floorCameraDiff = _camera.height - worldZPrev;
drummyfish 15:a91849bc5265 585
drummyfish 15:a91849bc5265 586 for (int32_t i = y; i >= middleRow; --i)
drummyfish 15:a91849bc5265 587 {
drummyfish 15:a91849bc5265 588 p.position.y = i;
drummyfish 15:a91849bc5265 589 p.depth = (_camera.resolution.y - i) * _floorDepthStep + floorCameraDiff;
drummyfish 15:a91849bc5265 590 _pixelFunction(p);
drummyfish 15:a91849bc5265 591 }
drummyfish 15:a91849bc5265 592 }
drummyfish 15:a91849bc5265 593
drummyfish 15:a91849bc5265 594 void render(Camera cam, ArrayFunction arrayFunc, PixelFunction pixelFunc,
drummyfish 15:a91849bc5265 595 RayConstraints constraints)
drummyfish 15:a91849bc5265 596 {
drummyfish 15:a91849bc5265 597 _pixelFunction = pixelFunc;
drummyfish 15:a91849bc5265 598 _arrayFunction = arrayFunc;
drummyfish 15:a91849bc5265 599 _camera = cam;
drummyfish 15:a91849bc5265 600
drummyfish 15:a91849bc5265 601 _startHeight = arrayFunc(
drummyfish 15:a91849bc5265 602 cam.position.x / UNITS_PER_SQUARE,
drummyfish 15:a91849bc5265 603 cam.position.y / UNITS_PER_SQUARE) -1 * cam.height;
drummyfish 15:a91849bc5265 604
drummyfish 15:a91849bc5265 605 // TODO
drummyfish 15:a91849bc5265 606 _floorDepthStep = (16 * UNITS_PER_SQUARE) / cam.resolution.y;
drummyfish 15:a91849bc5265 607
drummyfish 15:a91849bc5265 608 castRaysMultiHit(cam,arrayFunc,_columnFunction,constraints);
drummyfish 15:a91849bc5265 609 }
drummyfish 15:a91849bc5265 610
drummyfish 15:a91849bc5265 611 Vector2D normalize(Vector2D v)
drummyfish 15:a91849bc5265 612 {
drummyfish 15:a91849bc5265 613 profileCall(normalize);
drummyfish 15:a91849bc5265 614
drummyfish 15:a91849bc5265 615 Vector2D result;
drummyfish 15:a91849bc5265 616
drummyfish 15:a91849bc5265 617 Unit l = len(v);
drummyfish 15:a91849bc5265 618
drummyfish 15:a91849bc5265 619 result.x = (v.x * UNITS_PER_SQUARE) / l;
drummyfish 15:a91849bc5265 620 result.y = (v.y * UNITS_PER_SQUARE) / l;
drummyfish 15:a91849bc5265 621
drummyfish 15:a91849bc5265 622 return result;
drummyfish 15:a91849bc5265 623 }
drummyfish 15:a91849bc5265 624
drummyfish 15:a91849bc5265 625 Unit vectorsAngleCos(Vector2D v1, Vector2D v2)
drummyfish 15:a91849bc5265 626 {
drummyfish 15:a91849bc5265 627 profileCall(vectorsAngleCos);
drummyfish 15:a91849bc5265 628
drummyfish 15:a91849bc5265 629 v1 = normalize(v1);
drummyfish 15:a91849bc5265 630 v2 = normalize(v2);
drummyfish 15:a91849bc5265 631
drummyfish 15:a91849bc5265 632 return (v1.x * v2.x + v1.y * v2.y) / UNITS_PER_SQUARE;
drummyfish 15:a91849bc5265 633 }
drummyfish 15:a91849bc5265 634
drummyfish 15:a91849bc5265 635 Unit degreesToUnitsAngle(int16_t degrees)
drummyfish 15:a91849bc5265 636 {
drummyfish 15:a91849bc5265 637 return (degrees * UNITS_PER_SQUARE) / 360;
drummyfish 15:a91849bc5265 638 }
drummyfish 15:a91849bc5265 639
drummyfish 15:a91849bc5265 640 Unit perspectiveScale(Unit originalSize, Unit distance, Unit fov)
drummyfish 15:a91849bc5265 641 {
drummyfish 15:a91849bc5265 642 return (originalSize * UNITS_PER_SQUARE) / distance;
drummyfish 15:a91849bc5265 643
drummyfish 15:a91849bc5265 644 distance *= fov;
drummyfish 15:a91849bc5265 645 distance = distance == 0 ? 1 : distance; // prevent division by zero
drummyfish 15:a91849bc5265 646
drummyfish 15:a91849bc5265 647 return originalSize / distance;
drummyfish 15:a91849bc5265 648 }
drummyfish 15:a91849bc5265 649
drummyfish 15:a91849bc5265 650 #endif