something
Fork of HelloWorld by
raycastlib.h@15:a91849bc5265, 2018-09-01 (annotated)
- Committer:
- drummyfish
- Date:
- Sat Sep 01 18:14:19 2018 +0000
- Revision:
- 15:a91849bc5265
Init
Who changed what in which revision?
User | Revision | Line number | New 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 |