tinyphysicsengine.h 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120
  1. #ifndef _TINYPHYSICSENGINE_H
  2. #define _TINYPHYSICSENGINE_H
  3. /**
  4. WORK IN PROGRESS, UNUSABLE YET
  5. Simple/suckless header-only hybrid 3D physics engine with no floating point,
  6. only 32 bit int arithmetic, similar to e.g. small3dlib.
  7. Conventions and formats are the same or similar to those of small3dlib so as
  8. to make them easily integrate with each other.
  9. Orientations/rotations are in extrinsic Euler angles in the ZXY order (by Z,
  10. then by X, then by Y). Angles are in TPE_Units, TPE_FRACTIONS_PER_UNIT is
  11. full angle (2 PI). Sometimes rotations can also be specified in the
  12. "about axis" format: here the object is rotated CW by given axis by an angle
  13. that's specified by the magnitude of the vector.
  14. Where it matters (e.g. rotations about axes) we consider a left-handed coord.
  15. system (x right, y up, z forward).
  16. --------------------
  17. by drummyfish, 2022
  18. This work's goal is to never be encumbered by any exclusive intellectual
  19. property rights. The work is therefore provided under CC0 1.0 + additional
  20. WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
  21. intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
  22. INTELLECTUAL PROPERTY RGHTS is as follows:
  23. Each contributor to this work agrees that they waive any exclusive rights,
  24. including but not limited to copyright, patents, trademark, trade dress,
  25. industrial design, plant varieties and trade secrets, to any and all ideas,
  26. concepts, processes, discoveries, improvements and inventions conceived,
  27. discovered, made, designed, researched or developed by the contributor either
  28. solely or jointly with others, which relate to this work or result from this
  29. work. Should any waiver of such right be judged legally invalid or
  30. ineffective under applicable law, the contributor hereby grants to each
  31. affected person a royalty-free, non transferable, non sublicensable, non
  32. exclusive, irrevocable and unconditional license to this right.
  33. */
  34. #include <stdint.h>
  35. typedef int32_t TPE_Unit;
  36. typedef int16_t TPE_UnitReduced; ///< Like TPE_Unit but saving space
  37. #define TPE_FRACTIONS_PER_UNIT 512
  38. #define TPE_JOINT_SIZE_MULTIPLIER 32
  39. #define TPE_INFINITY 2147483647
  40. #define TPE_JOINT_SIZE(joint) ((joint).sizeDivided * TPE_JOINT_SIZE_MULTIPLIER)
  41. #ifndef TPE_APPROXIMATE_LENGTH
  42. #define TPE_APPROXIMATE_LENGTH 0 /**< whether or not use length/distance
  43. approximation rather than exact
  44. calculation (1 is faster but less
  45. accurate) */
  46. #endif
  47. #if !TPE_APPROXIMATE_LENGTH
  48. #define TPE_DISTANCE TPE_dist
  49. #define TPE_LENGTH TPE_vec3Len
  50. #else
  51. #define TPE_DISTANCE TPE_distApprox
  52. #define TPE_LENGTH TPE_vec3LenApprox
  53. #endif
  54. // TODO: faster and more accurate distance approx function based on regions/LUT
  55. #ifndef TPE_LOG
  56. #define TPE_LOG(s) ;
  57. #endif
  58. #ifndef TPE_LOW_SPEED
  59. /** Speed, in TPE_Units per ticks, that is considered low (used e.g. for auto
  60. disabling bodies). */
  61. #define TPE_LOW_SPEED 30
  62. #endif
  63. #ifndef TPE_RESHAPE_TENSION_LIMIT
  64. /** Tension limit, in TPE_Units, after which a non-soft body will be reshaped.
  65. Smaller number will keep more stable shapes but will cost more performance. */
  66. #define TPE_RESHAPE_TENSION_LIMIT 20
  67. #endif
  68. #ifndef TPE_RESHAPE_ITERATIONS
  69. /** How many iterations of reshaping will be performed by the step function if
  70. the body's shape needs to be reshaped. Greater number will keep shapes more
  71. stable but will cost some performance. */
  72. #define TPE_RESHAPE_ITERATIONS 3
  73. #endif
  74. #ifndef TPE_DEACTIVATE_AFTER
  75. /** After how many ticks of low speed should a body be disabled. This mustn't
  76. be greater than 255. */
  77. #define TPE_DEACTIVATE_AFTER 128
  78. #endif
  79. #ifndef TPE_LIGHT_DEACTIVATION
  80. /** When a body is activated by a collision, its deactivation counter will be
  81. set to this value, i.e. after a collision the body will be prone to deactivate
  82. sooner than normally. This is to handle situations with many bodies touching
  83. each other that would normally keep activating each other, never coming to
  84. rest. */
  85. #define TPE_LIGHT_DEACTIVATION \
  86. (TPE_DEACTIVATE_AFTER - TPE_DEACTIVATE_AFTER / 10)
  87. #endif
  88. #ifndef TPE_TENSION_ACCELERATION_DIVIDER
  89. /** Number by which the base acceleration (TPE_FRACTIONS_PER_UNIT per tick
  90. squared) caused by the connection tension will be divided. This should be power
  91. of 2. */
  92. #define TPE_TENSION_ACCELERATION_DIVIDER 32
  93. #endif
  94. #ifndef TPE_TENSION_ACCELERATION_THRESHOLD
  95. /** Limit within which acceleration caused by connection tension won't be
  96. applied. */
  97. #define TPE_TENSION_ACCELERATION_THRESHOLD 5
  98. #endif
  99. #ifndef TPE_COLLISION_RESOLUTION_ITERATIONS
  100. /** Maximum number of iterations to try to uncollide two colliding bodies. */
  101. #define TPE_COLLISION_RESOLUTION_ITERATIONS 3
  102. #endif
  103. #ifndef TPE_COLLISION_RESOLUTION_MARGIN
  104. /** Margin, in TPE_Units, by which a body will be shifted back to get out of
  105. collision. */
  106. #define TPE_COLLISION_RESOLUTION_MARGIN (TPE_FRACTIONS_PER_UNIT / 64)
  107. #endif
  108. #ifndef TPE_NONROTATING_COLLISION_RESOLVE_ATTEMPTS
  109. /** Number of times a collision of nonrotating bodies with environment will be
  110. attempted to resolve. This probably won't have great performance implications
  111. as complex collisions of this kind should be relatively rare. */
  112. #define TPE_NONROTATING_COLLISION_RESOLVE_ATTEMPTS 3
  113. #endif
  114. #define TPE_PRINTF_VEC3(v) printf("[%d %d %d]",(v).x,(v).y,(v).z);
  115. typedef struct
  116. {
  117. TPE_Unit x;
  118. TPE_Unit y;
  119. TPE_Unit z;
  120. } TPE_Vec3;
  121. typedef struct
  122. {
  123. TPE_Vec3 position;
  124. TPE_UnitReduced velocity[3];
  125. uint8_t sizeDivided; /**< size (radius, ...), for saving space divided by
  126. TPE_JOINT_SIZE_MULTIPLIER */
  127. } TPE_Joint;
  128. typedef struct
  129. {
  130. uint8_t joint1;
  131. uint16_t joint2;
  132. uint16_t length; ///< connection's preferred length, uint16_t saves space
  133. } TPE_Connection;
  134. #define TPE_BODY_FLAG_DEACTIVATED 1 /**< Not being updated due to low energy,
  135. "sleeping", will be woken by
  136. collisions etc. */
  137. #define TPE_BODY_FLAG_NONROTATING 2 /**< When set, the body won't rotate, will
  138. only move linearly. */
  139. #define TPE_BODY_FLAG_DISABLED 4 /**< Disabled, not taking part in
  140. simulation. */
  141. #define TPE_BODY_FLAG_SOFT 8 /**< Soft connections, effort won't be made
  142. to keep the body's shape. */
  143. /** Function used for defining static environment, working similarly to an SDF
  144. (signed distance function). The parameters are: 3D point P, max distance D.
  145. The function should behave like this: if P is inside the solid environment
  146. volume, P will be returned; otherwise closest point (by Euclidean distance) to
  147. the solid environment volume from P will be returned, except for a case when
  148. this closest point would be further away than D, in which case any arbitrary
  149. point further away than D may be returned (this allows for potentially
  150. potentially faster implementation). */
  151. typedef TPE_Vec3 (*TPE_ClosestPointFunction)(TPE_Vec3, TPE_Unit);
  152. /** Function that can be used as a joint-joint or joint-environment collision
  153. callback, parameters are following: body1 index, joint1 index, body2 index,
  154. joint2 index, collision world position. If body1 index is the same as body1
  155. index, then collision type is body-environment, otherwise it is body-body type.
  156. The function has to return either 1 if the collision is to be allowed or 0 if
  157. it is to be discarded. */
  158. typedef uint8_t (*TPE_CollisionCallback)(uint16_t, uint16_t, uint16_t, uint16_t,
  159. TPE_Vec3);
  160. /** Function used by the debug drawing functions to draw individual pixels to
  161. the screen. The parameters are following: pixel x, pixel y, pixel color. */
  162. typedef void (*TPE_DebugDrawFunction)(uint16_t, uint16_t, uint8_t);
  163. typedef struct
  164. {
  165. TPE_Joint *joints;
  166. uint8_t jointCount;
  167. TPE_Connection *connections;
  168. uint8_t connectionCount;
  169. TPE_UnitReduced jointMass;
  170. TPE_UnitReduced friction;
  171. TPE_UnitReduced elasticity;
  172. uint8_t flags;
  173. uint8_t deactivateCount;
  174. } TPE_Body;
  175. typedef struct
  176. {
  177. TPE_Body *bodies;
  178. uint16_t bodyCount;
  179. TPE_ClosestPointFunction environmentFunction;
  180. TPE_CollisionCallback collisionCallback;
  181. } TPE_World;
  182. void TPE_bodyInit(TPE_Body *body,
  183. TPE_Joint *joints, uint8_t jointCount,
  184. TPE_Connection *connections, uint8_t connectionCount,
  185. TPE_Unit mass);
  186. void TPE_worldInit(TPE_World *world,
  187. TPE_Body *bodies, uint16_t bodyCount,
  188. TPE_ClosestPointFunction environmentFunction);
  189. /** Gets orientation (rotation) of a body from a position of three of its
  190. joints. The vector from joint1 to joint2 is considered the body's forward
  191. direction, the vector from joint1 to joint3 its right direction. */
  192. TPE_Vec3 TPE_bodyGetOrientation(const TPE_Body *body, uint16_t joint1,
  193. uint16_t joint2, uint16_t joint3);
  194. void TPE_vec3Normalize(TPE_Vec3 *v);
  195. TPE_Vec3 TPE_pointRotate(TPE_Vec3 point, TPE_Vec3 rotation);
  196. TPE_Vec3 TPE_rotationInverse(TPE_Vec3 rotation);
  197. TPE_Vec3 TPE_rotationRotateByAxis(TPE_Vec3 rotation, TPE_Vec3 rotationByAxis);
  198. void TPE_getVelocitiesAfterCollision(
  199. TPE_Unit *v1,
  200. TPE_Unit *v2,
  201. TPE_Unit m1,
  202. TPE_Unit m2,
  203. TPE_Unit elasticity);
  204. TPE_Unit TPE_keepInRange(TPE_Unit x, TPE_Unit xMin, TPE_Unit xMax);
  205. TPE_Vec3 TPE_vec3(TPE_Unit x, TPE_Unit y, TPE_Unit z);
  206. TPE_Vec3 TPE_vec3Minus(TPE_Vec3 v1, TPE_Vec3 v2);
  207. TPE_Vec3 TPE_vec3Plus(TPE_Vec3 v1, TPE_Vec3 v2);
  208. TPE_Vec3 TPE_vec3Cross(TPE_Vec3 v1, TPE_Vec3 v2);
  209. TPE_Vec3 TPE_vec3Project(TPE_Vec3 v, TPE_Vec3 base);
  210. TPE_Vec3 TPE_vec3ProjectNormalized(TPE_Vec3 v, TPE_Vec3 baseNormalized);
  211. TPE_Vec3 TPE_vec3Times(TPE_Vec3 v, TPE_Unit units);
  212. TPE_Vec3 TPE_vec3TimesNonNormalized(TPE_Vec3 v, TPE_Unit q);
  213. TPE_Vec3 TPE_vec3Normalized(TPE_Vec3 v);
  214. /** Keeps given point inside specified axis-aligned box. This can be used e.g.
  215. to smooth rendered movement of jittering physics bodies. */
  216. TPE_Vec3 TPE_vec3KeepWithinBox(TPE_Vec3 point, TPE_Vec3 boxCenter,
  217. TPE_Vec3 boxMaxVect);
  218. /** Computes orientation/rotation (see docs for orientation format) from two
  219. vectors (which should be at least a close to being perpensicular and do NOT
  220. need to be normalized). */
  221. TPE_Vec3 TPE_orientationFromVecs(TPE_Vec3 forward, TPE_Vec3 right);
  222. TPE_Unit TPE_vec3Dot(TPE_Vec3 v1, TPE_Vec3 v2);
  223. TPE_Unit TPE_sqrt(TPE_Unit value);
  224. TPE_Unit TPE_vec3Len(TPE_Vec3 v);
  225. TPE_Unit TPE_vec3LenApprox(TPE_Vec3 v);
  226. static inline TPE_Unit TPE_nonZero(TPE_Unit x);
  227. static inline TPE_Unit TPE_dist(TPE_Vec3 p1, TPE_Vec3 p2);
  228. static inline TPE_Unit TPE_distApprox(TPE_Vec3 p1, TPE_Vec3 p2);
  229. TPE_Joint TPE_joint(TPE_Vec3 position, TPE_Unit size);
  230. uint8_t TPE_jointsResolveCollision(TPE_Joint *j1, TPE_Joint *j2,
  231. TPE_Unit mass1, TPE_Unit mass2, TPE_Unit elasticity, TPE_Unit friction);
  232. /** Tests and potentially resolves a collision between a joint and environment,
  233. returns 0 if no collision happened, 1 if it happened and was resolved normally
  234. and 2 if it couldn't be resolved normally. */
  235. uint8_t TPE_jointEnvironmentResolveCollision(TPE_Joint *joint, TPE_Unit
  236. elasticity, TPE_Unit friction, TPE_ClosestPointFunction env);
  237. uint8_t TPE_bodyEnvironmentCollide(const TPE_Body *body,
  238. TPE_ClosestPointFunction env);
  239. uint8_t TPE_bodyEnvironmentResolveCollision(TPE_Body *body,
  240. TPE_ClosestPointFunction env);
  241. void TPE_bodyGetAABB(const TPE_Body *body, TPE_Vec3 *vMin, TPE_Vec3 *vMax);
  242. uint8_t TPE_checkOverlapAABB(TPE_Vec3 v1Min, TPE_Vec3 v1Max, TPE_Vec3 v2Min,
  243. TPE_Vec3 v2Max);
  244. uint8_t TPE_bodiesResolveCollision(TPE_Body *b1, TPE_Body *b2);
  245. void TPE_jointPin(TPE_Joint *joint, TPE_Vec3 position);
  246. /** "Fakes" a rotation of a moving sphere by rotating it in the direction of
  247. its movement; this can create the illusion of the sphere actually rotating
  248. due to friction even if the physics sphere object (a body with a single joint)
  249. isn't rotating at all. Returns a rotating in the "about axis" format (see
  250. library conventions). */
  251. TPE_Vec3 TPE_fakeSphereRotation(TPE_Vec3 position1, TPE_Vec3 position2,
  252. TPE_Unit radius);
  253. // -----------------------------------------------------------------------------
  254. // body generation functions:
  255. void TPE_makeBox(TPE_Joint joints[8], TPE_Connection connections[16],
  256. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize);
  257. void TPE_makeCenterBox(TPE_Joint joints[9], TPE_Connection connections[18],
  258. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize);
  259. void TPE_makeRect(TPE_Joint joints[4], TPE_Connection connections[6],
  260. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize);
  261. void TPE_makeTriangle(TPE_Joint joints[3], TPE_Connection connections[3],
  262. TPE_Unit sideLength, TPE_Unit jointSize);
  263. void TPE_makeCenterRect(TPE_Joint joints[5], TPE_Connection connections[8],
  264. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize);
  265. void TPE_make2Line(TPE_Joint joints[2], TPE_Connection connections[1],
  266. TPE_Unit length, TPE_Unit jointSize);
  267. //------------------------------------------------------------------------------
  268. // environment functions:
  269. TPE_Vec3 TPE_envAABoxInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 size);
  270. TPE_Vec3 TPE_envAABox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec);
  271. TPE_Vec3 TPE_envBox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec,
  272. TPE_Vec3 rotation);
  273. TPE_Vec3 TPE_envSphere(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius);
  274. TPE_Vec3 TPE_envHalfPlane(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 normal);
  275. TPE_Vec3 TPE_envInfiniteCylinder(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3
  276. direction, TPE_Unit radius);
  277. #define TPE_ENV_START(test,point) TPE_Vec3 _pBest = test, _pTest; \
  278. TPE_Unit _dBest = TPE_DISTANCE(_pBest,point), _dTest;
  279. #define TPE_ENV_NEXT(test,point) \
  280. { if (_pBest.x == point.x && _pBest.y == point.y && _pBest.z == point.z) \
  281. return _pBest; \
  282. _pTest = test; _dTest = TPE_DISTANCE(_pTest,point); \
  283. if (_dTest < _dBest) { _pBest = _pTest; _dBest = _dTest; } }
  284. #define TPE_ENV_END return _pBest;
  285. //---------------------------
  286. void TPE_worldStep(TPE_World *world);
  287. TPE_Unit TPE_bodyNetSpeed(const TPE_Body *body);
  288. TPE_Unit TPE_bodyAverageSpeed(const TPE_Body *body);
  289. void TPE_bodyDeactivate(TPE_Body *body);
  290. void TPE_bodyLimitAverageSpeed(TPE_Body *body, TPE_Unit speedMin,
  291. TPE_Unit speedMax);
  292. void TPE_bodyMultiplyNetSpeed(TPE_Body *body, TPE_Unit factor);
  293. /** Attempts to shift the joints of a soft body so that the tension of all
  294. strings becomes zero while keeping the joints near their current position. This
  295. function performs one iteration of the equalizing algorithm and doesn't
  296. guarantee a perfect solution, it may help to run multiple iterations (call this
  297. function multiple times). */
  298. void TPE_bodyReshape(TPE_Body *body, TPE_ClosestPointFunction
  299. environmentFunction);
  300. /** Move a soft body by certain offset. */
  301. void TPE_bodyMove(TPE_Body *body, TPE_Vec3 offset);
  302. /** Zero velocities of all soft body joints. */
  303. void TPE_bodyStop(TPE_Body *body);
  304. void TPE_bodyActivate(TPE_Body *body);
  305. /** Add velocity to a soft body. */
  306. void TPE_bodyAccelerate(TPE_Body *body, TPE_Vec3 velocity);
  307. void TPE_bodyApplyGravity(TPE_Body *body, TPE_Unit downwardsAccel);
  308. /** Add angular velocity to a soft body. The rotation vector specifies the axis
  309. of rotation by its direction and angular velocity by its magnitude (magnitude
  310. of TPE_FRACTIONS_PER_UNIT will add linear velocity of TPE_FRACTIONS_PER_UNIT per
  311. tick to a point in the distance of TPE_FRACTIONS_PER_UNIT from the rotation
  312. axis). */
  313. void TPE_bodySpin(TPE_Body *body, TPE_Vec3 rotation);
  314. /** Instantly rotate soft body about an axis (see library conventions for
  315. the rotation format). */
  316. void TPE_bodyRotateByAxis(TPE_Body *body, TPE_Vec3 rotation);
  317. /** Compute the center of mass of a soft body. */
  318. TPE_Vec3 TPE_bodyGetCenter(const TPE_Body *body);
  319. /** Compute sine, TPE_FRACTIONS_PER_UNIT as argument corresponds to 2 * PI
  320. radians. Returns a number from -TPE_FRACTIONS_PER_UNIT to
  321. TPE_FRACTIONS_PER_UNIT. */
  322. TPE_Unit TPE_sin(TPE_Unit x);
  323. TPE_Unit TPE_cos(TPE_Unit x);
  324. TPE_Unit TPE_atan(TPE_Unit x);
  325. /** Draws a debug view of a 3D physics world using a provided pixel drawing
  326. function. This can be used to overlay a simple visualization of the physics
  327. objects to your main render, to spot exact borders of objects etc. The
  328. function draws simple dotted lines and circles with different "colors" for
  329. different types of objects (joints, connections, environemnt). camPos, camRot
  330. and camView should match the camera settings of your main renderer. CamView.x
  331. is horizontal resolution in pixels, camView.y is the vertical resolution,
  332. CamView.z says the camera focal length (~FOV) in TPE_Units. envGridRes is the
  333. resolution of an environment probe grid (the function will probe points in
  334. space and draw borders of the physics environemnt), envGridSize is the size
  335. (int TPE_Units) of the grid cell. Note the function may be slow (reducing
  336. envGridRes can help, workable value can be e.g. 16). */
  337. void TPE_worldDebugDraw(TPE_World *world, TPE_DebugDrawFunction drawFunc,
  338. TPE_Vec3 camPos, TPE_Vec3 camRot, TPE_Vec3 camView, uint16_t envGridRes,
  339. TPE_Unit envGridSize);
  340. //------------------------------------------------------------------------------
  341. // privates:
  342. uint16_t _TPE_body1Index, _TPE_body2Index, _TPE_joint1Index, _TPE_joint2Index;
  343. TPE_CollisionCallback _TPE_collisionCallback;
  344. static inline TPE_Unit TPE_nonZero(TPE_Unit x)
  345. {
  346. return x != 0 ? x : 1;
  347. }
  348. TPE_Joint TPE_joint(TPE_Vec3 position, TPE_Unit size)
  349. {
  350. TPE_Joint result;
  351. result.velocity[0] = 0;
  352. result.velocity[1] = 0;
  353. result.velocity[2] = 0;
  354. result.position = position;
  355. size /= TPE_JOINT_SIZE_MULTIPLIER;
  356. if (size > 0xff)
  357. TPE_LOG("WARNING: joint size too big in TPE_joint");
  358. result.sizeDivided = size;
  359. return result;
  360. }
  361. TPE_Vec3 TPE_vec3(TPE_Unit x, TPE_Unit y, TPE_Unit z)
  362. {
  363. TPE_Vec3 r;
  364. r.x = x;
  365. r.y = y;
  366. r.z = z;
  367. return r;
  368. }
  369. TPE_Unit TPE_sqrt(TPE_Unit value)
  370. {
  371. int8_t sign = 1;
  372. if (value < 0)
  373. {
  374. sign = -1;
  375. value *= -1;
  376. }
  377. uint32_t result = 0;
  378. uint32_t a = value;
  379. uint32_t b = 1u << 30;
  380. while (b > a)
  381. b >>= 2;
  382. while (b != 0)
  383. {
  384. if (a >= result + b)
  385. {
  386. a -= result + b;
  387. result = result + 2 * b;
  388. }
  389. b >>= 2;
  390. result >>= 1;
  391. }
  392. return result * sign;
  393. }
  394. TPE_Unit TPE_vec3Len(TPE_Vec3 v)
  395. {
  396. return TPE_sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
  397. }
  398. TPE_Unit TPE_vec3LenApprox(TPE_Vec3 v)
  399. {
  400. if (v.x < 0)
  401. v.x *= -1;
  402. if (v.y < 0)
  403. v.y *= -1;
  404. if (v.z < 0)
  405. v.z *= -1;
  406. TPE_Unit sum = v.x + v.y + v.z;
  407. v.x = (v.x > v.y) ?
  408. (v.x > v.z ? v.x : v.z) :
  409. (v.y > v.z ? v.y : v.z);
  410. return (v.x + sum) / 2;
  411. }
  412. TPE_Unit TPE_dist(TPE_Vec3 p1, TPE_Vec3 p2)
  413. {
  414. p1 = TPE_vec3Minus(p1,p2);
  415. return TPE_vec3Len(p1);
  416. }
  417. TPE_Unit TPE_distApprox(TPE_Vec3 p1, TPE_Vec3 p2)
  418. {
  419. p1 = TPE_vec3Minus(p1,p2);
  420. return TPE_vec3LenApprox(p1);
  421. }
  422. void TPE_bodyInit(TPE_Body *body,
  423. TPE_Joint *joints, uint8_t jointCount,
  424. TPE_Connection *connections, uint8_t connectionCount,
  425. TPE_Unit mass)
  426. {
  427. body->joints = joints;
  428. body->jointCount = jointCount;
  429. body->connections = connections;
  430. body->connectionCount = connectionCount;
  431. body->deactivateCount = 0;
  432. body->friction = TPE_FRACTIONS_PER_UNIT / 2;
  433. body->elasticity = TPE_FRACTIONS_PER_UNIT / 2;
  434. body->flags = 0;
  435. body->jointMass = mass / jointCount;
  436. if (body->jointMass == 0)
  437. body->jointMass = 1;
  438. for (uint32_t i = 0; i < connectionCount; ++i)
  439. {
  440. TPE_Unit d = TPE_DISTANCE(
  441. joints[connections[i].joint1].position,
  442. joints[connections[i].joint2].position);
  443. if (d > 0xffff)
  444. TPE_LOG("WARNING: joint distance too long in TPE_bodyInit");
  445. connections[i].length = d != 0 ? d : 1; // prevent later division by zero
  446. }
  447. }
  448. void TPE_worldInit(TPE_World *world,
  449. TPE_Body *bodies, uint16_t bodyCount,
  450. TPE_ClosestPointFunction environmentFunction)
  451. {
  452. world->bodies = bodies;
  453. world->bodyCount = bodyCount;
  454. world->environmentFunction = environmentFunction;
  455. world->collisionCallback = 0;
  456. }
  457. #define C(n,a,b) connections[n].joint1 = a; connections[n].joint2 = b;
  458. void TPE_make2Line(TPE_Joint joints[2], TPE_Connection connections[1],
  459. TPE_Unit length, TPE_Unit jointSize)
  460. {
  461. joints[0] = TPE_joint(TPE_vec3(length / 2,0,0),jointSize);
  462. joints[1] = TPE_joint(TPE_vec3(length / -2,0,0),jointSize);
  463. C(0, 0,1)
  464. }
  465. void TPE_makeRect(TPE_Joint joints[4], TPE_Connection connections[6],
  466. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize)
  467. {
  468. width /= 2;
  469. depth /= 2;
  470. for (uint8_t i = 0; i < 4; ++i)
  471. joints[i] = TPE_joint(
  472. TPE_vec3((i % 2) ? -1 * width : width,
  473. 0,(i / 2) ? - 1 * depth : depth),
  474. jointSize);
  475. C(0, 0,1) C(1, 0,2) C (2, 3,1) C(3, 3,2)
  476. C(4, 0,3) C(5, 1,2)
  477. }
  478. void TPE_makeCenterRect(TPE_Joint joints[5], TPE_Connection connections[8],
  479. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize)
  480. {
  481. TPE_makeRect(joints,connections,width,depth,jointSize);
  482. joints[4] = TPE_joint(TPE_vec3(0,0,0),jointSize);
  483. C(6, 0,4) C(7, 3,4)
  484. }
  485. void TPE_makeTriangle(TPE_Joint joints[3], TPE_Connection connections[3],
  486. TPE_Unit sideLength, TPE_Unit jointSize)
  487. {
  488. joints[0] = TPE_joint(TPE_vec3(sideLength / 2,0,
  489. TPE_sqrt((sideLength * sideLength) / 2) / 2),
  490. jointSize);
  491. joints[1] = joints[0];
  492. joints[1].position.x *= -1;
  493. joints[2] = TPE_joint(TPE_vec3(0,0,-1 * joints[0].position.z),jointSize);
  494. C(0, 0,1) C(1, 1,2) C(2, 2,0)
  495. }
  496. void TPE_makeBox(TPE_Joint joints[8], TPE_Connection connections[16],
  497. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize)
  498. {
  499. width /= 2;
  500. depth /= 2;
  501. height /= 2;
  502. for (uint8_t i = 0; i < 8; ++i)
  503. joints[i] = TPE_joint(
  504. TPE_vec3(
  505. (i % 2) ? width : (-1 * width),
  506. ((i >> 2) % 2) ? height : (-1 * height),
  507. ((i >> 1) % 2) ? depth : (-1 * depth)),
  508. jointSize);
  509. C(0, 0,1) C(1, 1,3) C(2, 3,2) C(3, 2,0) // top
  510. C(4, 4,5) C(5, 5,7) C(6, 7,6) C(7, 6,4) // bottom
  511. C(8, 0,4) C(9, 1,5) C(10,3,7) C(11,2,6) // middle
  512. C(12,0,7) C(13,1,6) C(14,2,5) C(15,3,4) // diagonal
  513. }
  514. void TPE_makeCenterBox(TPE_Joint joints[9], TPE_Connection connections[18],
  515. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize)
  516. {
  517. TPE_makeBox(joints,connections,width,depth,height,jointSize);
  518. joints[8] = TPE_joint(TPE_vec3(0,0,0),jointSize);
  519. C(16, 0,8) C(17, 7,8)
  520. }
  521. #undef C
  522. void TPE_bodyDeactivate(TPE_Body *body)
  523. {
  524. body->flags |= TPE_BODY_FLAG_DEACTIVATED;
  525. }
  526. void TPE_worldStep(TPE_World *world)
  527. {
  528. _TPE_collisionCallback = world->collisionCallback;
  529. for (uint16_t i = 0; i < world->bodyCount; ++i)
  530. {
  531. TPE_Body *body = world->bodies + i;
  532. if (body->flags & (TPE_BODY_FLAG_DEACTIVATED | TPE_BODY_FLAG_DISABLED))
  533. continue;
  534. TPE_Joint *joint = body->joints, *joint2;
  535. TPE_Vec3 origPos = body->joints[0].position;
  536. for (uint16_t j = 0; j < body->jointCount; ++j) // apply velocities
  537. {
  538. // non-rotating bodies will copy the 1st joint's velocity
  539. if (body->flags & TPE_BODY_FLAG_NONROTATING)
  540. for (uint8_t k = 0; k < 3; ++k)
  541. joint->velocity[k] = body->joints[0].velocity[k];
  542. joint->position.x += joint->velocity[0];
  543. joint->position.y += joint->velocity[1];
  544. joint->position.z += joint->velocity[2];
  545. joint++;
  546. }
  547. TPE_Connection *connection = body->connections;
  548. TPE_Vec3 aabbMin, aabbMax;
  549. TPE_bodyGetAABB(body,&aabbMin,&aabbMax);
  550. _TPE_body1Index = i;
  551. for (uint16_t j = 0; j < world->bodyCount; ++j)
  552. {
  553. if (j > i || (world->bodies[j].flags & TPE_BODY_FLAG_DEACTIVATED))
  554. {
  555. // firstly quick-check collision of body AA bounding boxes
  556. TPE_Vec3 aabbMin2, aabbMax2;
  557. TPE_bodyGetAABB(&world->bodies[j],&aabbMin2,&aabbMax2);
  558. _TPE_body2Index = j;
  559. if (TPE_checkOverlapAABB(aabbMin,aabbMax,aabbMin2,aabbMax2) &&
  560. TPE_bodiesResolveCollision(body,world->bodies + j))
  561. {
  562. TPE_bodyActivate(body);
  563. body->deactivateCount = TPE_LIGHT_DEACTIVATION;
  564. TPE_bodyActivate(world->bodies + j);
  565. world->bodies[j].deactivateCount = TPE_LIGHT_DEACTIVATION;
  566. }
  567. }
  568. }
  569. _TPE_body2Index = _TPE_body1Index;
  570. uint8_t collided =
  571. TPE_bodyEnvironmentResolveCollision(body,world->environmentFunction);
  572. if (body->flags & TPE_BODY_FLAG_NONROTATING)
  573. {
  574. /* Non-rotating bodies may end up still colliding after environment coll
  575. resolvement (unlike rotating bodies where each joint is ensured separately
  576. to not collide). So if still in collision, we try a few more times. If not
  577. successful, we simply undo any shifts we've done. This should absolutely
  578. prevent any body escaping out of environment bounds. */
  579. for (uint8_t i = 0; i < TPE_NONROTATING_COLLISION_RESOLVE_ATTEMPTS; ++i)
  580. {
  581. if (!collided)
  582. break;
  583. collided =
  584. TPE_bodyEnvironmentResolveCollision(body,world->environmentFunction);
  585. }
  586. if (collided &&
  587. TPE_bodyEnvironmentCollide(body,world->environmentFunction))
  588. TPE_bodyMove(body,TPE_vec3Minus(origPos,body->joints[0].position));
  589. }
  590. else
  591. {
  592. TPE_Unit bodyTension = 0;
  593. for (uint16_t j = 0; j < body->connectionCount; ++j) // joint tension
  594. {
  595. joint = &(body->joints[connection->joint1]);
  596. joint2 = &(body->joints[connection->joint2]);
  597. TPE_Vec3 dir = TPE_vec3Minus(joint2->position,joint->position);
  598. TPE_Unit len = TPE_LENGTH(dir);
  599. len = (len * TPE_FRACTIONS_PER_UNIT) /
  600. connection->length - TPE_FRACTIONS_PER_UNIT;
  601. bodyTension += len > 0 ? len : -len;
  602. if (len > TPE_TENSION_ACCELERATION_THRESHOLD ||
  603. len < -1 * TPE_TENSION_ACCELERATION_THRESHOLD)
  604. {
  605. TPE_vec3Normalize(&dir);
  606. dir.x /= TPE_TENSION_ACCELERATION_DIVIDER;
  607. dir.y /= TPE_TENSION_ACCELERATION_DIVIDER;
  608. dir.z /= TPE_TENSION_ACCELERATION_DIVIDER;
  609. if (len < 0)
  610. {
  611. dir.x *= -1;
  612. dir.y *= -1;
  613. dir.z *= -1;
  614. }
  615. joint->velocity[0] += dir.x;
  616. joint->velocity[1] += dir.y;
  617. joint->velocity[2] += dir.z;
  618. joint2->velocity[0] -= dir.x;
  619. joint2->velocity[1] -= dir.y;
  620. joint2->velocity[2] -= dir.z;
  621. }
  622. connection++;
  623. }
  624. if (body->connectionCount > 0 && !(body->flags & TPE_BODY_FLAG_SOFT))
  625. {
  626. TPE_bodyReshape(body,world->environmentFunction);
  627. bodyTension /= body->connectionCount;
  628. if (bodyTension > TPE_RESHAPE_TENSION_LIMIT)
  629. for (uint8_t k = 0; k < TPE_RESHAPE_ITERATIONS; ++k)
  630. TPE_bodyReshape(body,world->environmentFunction);
  631. }
  632. }
  633. if (body->deactivateCount >= TPE_DEACTIVATE_AFTER)
  634. {
  635. TPE_bodyStop(body);
  636. body->deactivateCount = 0;
  637. body->flags |= TPE_BODY_FLAG_DEACTIVATED;
  638. }
  639. else if (TPE_bodyAverageSpeed(body) <= TPE_LOW_SPEED) // TODO: optimize
  640. body->deactivateCount++;
  641. else
  642. body->deactivateCount = 0;
  643. }
  644. }
  645. void TPE_bodyActivate(TPE_Body *body)
  646. {
  647. // the if check has to be here, don't remove it
  648. if (body->flags & TPE_BODY_FLAG_DEACTIVATED)
  649. {
  650. TPE_bodyStop(body);
  651. body->flags &= ~TPE_BODY_FLAG_DEACTIVATED;
  652. body->deactivateCount = 0;
  653. }
  654. }
  655. TPE_Unit TPE_bodyNetSpeed(const TPE_Body *body)
  656. {
  657. TPE_Unit velocity = 0;
  658. const TPE_Joint *joint = body->joints;
  659. for (uint16_t i = 0; i < body->jointCount; ++i)
  660. {
  661. velocity += TPE_LENGTH(
  662. TPE_vec3(joint->velocity[0],joint->velocity[1],joint->velocity[2]));
  663. joint++;
  664. }
  665. return velocity;
  666. }
  667. TPE_Unit TPE_bodyAverageSpeed(const TPE_Body *body)
  668. {
  669. return TPE_bodyNetSpeed(body) / body->jointCount;
  670. }
  671. void TPE_bodyMultiplyNetSpeed(TPE_Body *body, TPE_Unit factor)
  672. {
  673. TPE_Joint *joint = body->joints;
  674. for (uint16_t j = 0; j < body->jointCount; ++j)
  675. {
  676. for (uint8_t k = 0; k < 3; ++k)
  677. joint->velocity[k] =
  678. (((TPE_Unit) joint->velocity[k]) * factor) /
  679. TPE_FRACTIONS_PER_UNIT;
  680. joint++;
  681. }
  682. }
  683. void TPE_bodyLimitAverageSpeed(TPE_Body *body, TPE_Unit speedMin,
  684. TPE_Unit speedMax)
  685. {
  686. for (uint8_t i = 0; i < 16; ++i)
  687. {
  688. TPE_Unit speed = TPE_bodyAverageSpeed(body);
  689. if (speed >= speedMin && speed <= speedMax)
  690. return;
  691. TPE_Unit fraction =
  692. (((speedMax + speedMin) / 2) * TPE_FRACTIONS_PER_UNIT) /
  693. TPE_nonZero(speed);
  694. TPE_bodyMultiplyNetSpeed(body,fraction);
  695. }
  696. }
  697. void TPE_bodyReshape(TPE_Body *body,
  698. TPE_ClosestPointFunction environmentFunction)
  699. {
  700. for (uint16_t i = 0; i < body->connectionCount; ++i)
  701. {
  702. TPE_Connection *c = &body->connections[i];
  703. TPE_Joint *j1 = &(body->joints[c->joint1]);
  704. TPE_Joint *j2 = &(body->joints[c->joint2]);
  705. TPE_Vec3 dir = TPE_vec3Minus(j2->position,j1->position);
  706. TPE_Vec3 middle = TPE_vec3Plus(j1->position,j2->position);
  707. middle.x /= 2;
  708. middle.y /= 2;
  709. middle.z /= 2;
  710. TPE_vec3Normalize(&dir);
  711. dir.x = (dir.x * c->length) / TPE_FRACTIONS_PER_UNIT;
  712. dir.y = (dir.y * c->length) / TPE_FRACTIONS_PER_UNIT;
  713. dir.z = (dir.z * c->length) / TPE_FRACTIONS_PER_UNIT;
  714. TPE_Vec3 positionBackup = j1->position;
  715. j1->position.x = middle.x - dir.x / 2;
  716. j1->position.y = middle.y - dir.y / 2;
  717. j1->position.z = middle.z - dir.z / 2;
  718. if (environmentFunction != 0 && TPE_LENGTH(TPE_vec3Minus(j1->position,
  719. environmentFunction(j1->position,TPE_JOINT_SIZE(*j1))))
  720. < TPE_JOINT_SIZE(*j1))
  721. j1->position = positionBackup;
  722. positionBackup = j2->position;
  723. j2->position.x = j1->position.x + dir.x;
  724. j2->position.y = j1->position.y + dir.y;
  725. j2->position.z = j1->position.z + dir.z;
  726. if (environmentFunction != 0 && TPE_LENGTH(TPE_vec3Minus(j2->position,
  727. environmentFunction(j2->position,TPE_JOINT_SIZE(*j2))))
  728. < TPE_JOINT_SIZE(*j2))
  729. j2->position = positionBackup;
  730. }
  731. }
  732. TPE_Vec3 TPE_vec3Plus(TPE_Vec3 v1, TPE_Vec3 v2)
  733. {
  734. v1.x += v2.x;
  735. v1.y += v2.y;
  736. v1.z += v2.z;
  737. return v1;
  738. }
  739. TPE_Vec3 TPE_vec3Minus(TPE_Vec3 v1, TPE_Vec3 v2)
  740. {
  741. v1.x -= v2.x;
  742. v1.y -= v2.y;
  743. v1.z -= v2.z;
  744. return v1;
  745. }
  746. void TPE_vec3Normalize(TPE_Vec3 *v)
  747. {
  748. TPE_Unit l = TPE_LENGTH(*v);
  749. if (l == 0)
  750. *v = TPE_vec3(TPE_FRACTIONS_PER_UNIT,0,0);
  751. else
  752. {
  753. if (l < 16) // TODO: const, for too short
  754. {
  755. v->x *= 8;
  756. v->y *= 8;
  757. v->z *= 8;
  758. l = TPE_LENGTH(*v);
  759. }
  760. v->x = (v->x * TPE_FRACTIONS_PER_UNIT) / l;
  761. v->y = (v->y * TPE_FRACTIONS_PER_UNIT) / l;
  762. v->z = (v->z * TPE_FRACTIONS_PER_UNIT) / l;
  763. }
  764. }
  765. TPE_Vec3 TPE_bodyGetOrientation(const TPE_Body *body, uint16_t joint1,
  766. uint16_t joint2, uint16_t joint3)
  767. {
  768. return TPE_orientationFromVecs(
  769. TPE_vec3Minus(
  770. body->joints[joint2].position,
  771. body->joints[joint1].position),
  772. TPE_vec3Minus(
  773. body->joints[joint3].position,
  774. body->joints[joint1].position));
  775. }
  776. TPE_Vec3 TPE_bodyGetCenter(const TPE_Body *body)
  777. {
  778. // TODO: take into account possibly different joint sizes? does it even matter?
  779. TPE_Vec3 result = TPE_vec3(0,0,0);
  780. const TPE_Joint *j = body->joints;
  781. for (uint16_t i = 0; i < body->jointCount; ++i)
  782. {
  783. result = TPE_vec3Plus(result,j->position);
  784. j++;
  785. }
  786. result.x /= body->jointCount;
  787. result.y /= body->jointCount;
  788. result.z /= body->jointCount;
  789. return result;
  790. }
  791. void TPE_bodySpin(TPE_Body *body, TPE_Vec3 rotation)
  792. {
  793. TPE_Vec3 center = TPE_bodyGetCenter(body);
  794. for (uint16_t i = 0; i < body->jointCount; ++i)
  795. {
  796. TPE_Joint *j = body->joints + i;
  797. TPE_Vec3 toPoint = TPE_vec3Minus(j->position,center);
  798. toPoint = TPE_vec3Project(toPoint,rotation);
  799. toPoint = TPE_vec3Plus(center,toPoint);
  800. toPoint = TPE_vec3Minus(j->position,toPoint);
  801. toPoint = TPE_vec3Cross(toPoint,rotation);
  802. j->velocity[0] += toPoint.x;
  803. j->velocity[1] += toPoint.y;
  804. j->velocity[2] += toPoint.z;
  805. }
  806. }
  807. TPE_Vec3 _TPE_rotateByAxis(TPE_Vec3 p, TPE_Vec3 axisNormalized, TPE_Unit angle)
  808. {
  809. TPE_Vec3 projected = TPE_vec3ProjectNormalized(p,axisNormalized);
  810. TPE_Vec3 a = TPE_vec3Minus(p,projected);
  811. if (a.x == 0 && a.y == 0 && a.z == 0)
  812. return p;
  813. TPE_Vec3 b = TPE_vec3Cross(a,axisNormalized);
  814. return TPE_vec3Plus(projected,TPE_vec3Plus(
  815. TPE_vec3Times(a,TPE_cos(angle)),
  816. TPE_vec3Times(b,TPE_sin(angle))));
  817. }
  818. void TPE_bodyRotateByAxis(TPE_Body *body, TPE_Vec3 rotation)
  819. {
  820. TPE_Vec3 bodyCenter = TPE_bodyGetCenter(body);
  821. TPE_Unit angle = TPE_LENGTH(rotation);
  822. TPE_vec3Normalize(&rotation);
  823. for (uint16_t i = 0; i < body->jointCount; ++i)
  824. {
  825. TPE_Vec3 toPoint = TPE_vec3Minus(body->joints[i].position,bodyCenter);
  826. body->joints[i].position = TPE_vec3Plus(bodyCenter,
  827. _TPE_rotateByAxis(toPoint,rotation,angle));
  828. }
  829. }
  830. TPE_Vec3 TPE_vec3Cross(TPE_Vec3 v1, TPE_Vec3 v2)
  831. {
  832. TPE_Vec3 r;
  833. r.x = (v1.y * v2.z - v1.z * v2.y) / TPE_FRACTIONS_PER_UNIT;
  834. r.y = (v1.z * v2.x - v1.x * v2.z) / TPE_FRACTIONS_PER_UNIT;
  835. r.z = (v1.x * v2.y - v1.y * v2.x) / TPE_FRACTIONS_PER_UNIT;
  836. return r;
  837. }
  838. TPE_Vec3 TPE_vec3ProjectNormalized(TPE_Vec3 v, TPE_Vec3 baseNormalized)
  839. {
  840. TPE_Vec3 r;
  841. TPE_Unit p = TPE_vec3Dot(v,baseNormalized);
  842. r.x = (p * baseNormalized.x) / TPE_FRACTIONS_PER_UNIT;
  843. r.y = (p * baseNormalized.y) / TPE_FRACTIONS_PER_UNIT;
  844. r.z = (p * baseNormalized.z) / TPE_FRACTIONS_PER_UNIT;
  845. return r;
  846. }
  847. TPE_Vec3 TPE_vec3Project(TPE_Vec3 v, TPE_Vec3 base)
  848. {
  849. TPE_vec3Normalize(&base);
  850. return TPE_vec3ProjectNormalized(v,base);
  851. }
  852. void TPE_bodyMove(TPE_Body *body, TPE_Vec3 offset)
  853. {
  854. for (uint16_t i = 0; i < body->jointCount; ++i)
  855. body->joints[i].position = TPE_vec3Plus(body->joints[i].position,
  856. offset);
  857. }
  858. void TPE_bodyApplyGravity(TPE_Body *body, TPE_Unit downwardsAccel)
  859. {
  860. if ((body->flags & TPE_BODY_FLAG_DEACTIVATED) ||
  861. (body->flags & TPE_BODY_FLAG_DISABLED))
  862. return;
  863. for (uint16_t i = 0; i < body->jointCount; ++i)
  864. body->joints[i].velocity[1] -= downwardsAccel;
  865. }
  866. void TPE_bodyAccelerate(TPE_Body *body, TPE_Vec3 velocity)
  867. {
  868. TPE_bodyActivate(body);
  869. for (uint16_t i = 0; i < body->jointCount; ++i)
  870. {
  871. body->joints[i].velocity[0] += velocity.x;
  872. body->joints[i].velocity[1] += velocity.y;
  873. body->joints[i].velocity[2] += velocity.z;
  874. }
  875. }
  876. void TPE_bodyStop(TPE_Body *body)
  877. {
  878. for (uint16_t i = 0; i < body->jointCount; ++i)
  879. {
  880. body->joints[i].velocity[0] = 0;
  881. body->joints[i].velocity[1] = 0;
  882. body->joints[i].velocity[2] = 0;
  883. }
  884. }
  885. void _TPE_bodyNonrotatingJointCollided(TPE_Body *b, int16_t jointIndex,
  886. TPE_Vec3 origPos, uint8_t success)
  887. {
  888. origPos = TPE_vec3Minus(b->joints[jointIndex].position,origPos);
  889. for (uint16_t i = 0; i < b->jointCount; ++i)
  890. if (i != jointIndex)
  891. {
  892. b->joints[i].position = TPE_vec3Plus(b->joints[i].position,origPos);
  893. if (success)
  894. for (uint8_t j = 0; j < 3; ++j)
  895. b->joints[i].velocity[j] = b->joints[jointIndex].velocity[j];
  896. }
  897. }
  898. TPE_Unit TPE_vec3Dot(TPE_Vec3 v1, TPE_Vec3 v2)
  899. {
  900. return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z) / TPE_FRACTIONS_PER_UNIT;
  901. }
  902. TPE_Unit TPE_cos(TPE_Unit x)
  903. {
  904. return TPE_sin(x + TPE_FRACTIONS_PER_UNIT / 4);
  905. }
  906. TPE_Unit TPE_sin(TPE_Unit x)
  907. {
  908. int8_t sign = 1;
  909. if (x < 0) // odd function
  910. {
  911. x *= -1;
  912. sign = -1;
  913. }
  914. x %= TPE_FRACTIONS_PER_UNIT;
  915. if (x > TPE_FRACTIONS_PER_UNIT / 2)
  916. {
  917. x -= TPE_FRACTIONS_PER_UNIT / 2;
  918. sign *= -1;
  919. }
  920. TPE_Unit tmp = TPE_FRACTIONS_PER_UNIT - 2 * x;
  921. #define _PI2 ((TPE_Unit) (9.8696044 * TPE_FRACTIONS_PER_UNIT))
  922. return sign * // Bhaskara's approximation
  923. (((32 * x * _PI2) / TPE_FRACTIONS_PER_UNIT) * tmp) /
  924. ((_PI2 * (5 * TPE_FRACTIONS_PER_UNIT - (8 * x * tmp) /
  925. TPE_FRACTIONS_PER_UNIT)) / TPE_FRACTIONS_PER_UNIT);
  926. #undef _PI2
  927. }
  928. uint8_t TPE_bodiesResolveCollision(TPE_Body *b1, TPE_Body *b2)
  929. {
  930. uint8_t r = 0;
  931. for (uint16_t i = 0; i < b1->jointCount; ++i)
  932. for (uint16_t j = 0; j < b2->jointCount; ++j)
  933. {
  934. TPE_Vec3 origPos2 = b2->joints[j].position;
  935. TPE_Vec3 origPos1 = b1->joints[i].position;
  936. _TPE_joint1Index = i;
  937. _TPE_joint2Index = j;
  938. if (TPE_jointsResolveCollision(&(b1->joints[i]),&(b2->joints[j]),
  939. b1->jointMass,b2->jointMass,(b1->elasticity + b2->elasticity) / 2,
  940. (b1->friction + b2->friction) / 2))
  941. {
  942. r = 1;
  943. if (b1->flags & TPE_BODY_FLAG_NONROTATING)
  944. _TPE_bodyNonrotatingJointCollided(b1,i,origPos1,1);
  945. if (b2->flags & TPE_BODY_FLAG_NONROTATING)
  946. _TPE_bodyNonrotatingJointCollided(b2,j,origPos2,1);
  947. }
  948. }
  949. return r;
  950. }
  951. uint8_t TPE_jointsResolveCollision(TPE_Joint *j1, TPE_Joint *j2,
  952. TPE_Unit mass1, TPE_Unit mass2, TPE_Unit elasticity, TPE_Unit friction)
  953. {
  954. TPE_Vec3 dir = TPE_vec3Minus(j2->position,j1->position);
  955. TPE_Unit d = TPE_LENGTH(dir) - TPE_JOINT_SIZE(*j1) - TPE_JOINT_SIZE(*j2);
  956. if (d < 0) // collision?
  957. {
  958. if (_TPE_collisionCallback != 0)
  959. if (!_TPE_collisionCallback(_TPE_body1Index,_TPE_joint1Index,
  960. _TPE_body2Index,_TPE_joint2Index,TPE_vec3Plus(j1->position,dir)))
  961. return 0;
  962. // separate bodies, the shift distance will depend on the weight ratio:
  963. d = -1 * d + TPE_COLLISION_RESOLUTION_MARGIN;
  964. TPE_vec3Normalize(&dir);
  965. TPE_Unit ratio = (mass2 * TPE_FRACTIONS_PER_UNIT) /
  966. TPE_nonZero(mass1 + mass2);
  967. TPE_Unit shiftDistance = (ratio * d) / TPE_FRACTIONS_PER_UNIT;
  968. TPE_Vec3 shift = TPE_vec3Times(dir,shiftDistance);
  969. j1->position = TPE_vec3Minus(j1->position,shift);
  970. shiftDistance = d - shiftDistance;
  971. shift = TPE_vec3Times(dir,shiftDistance);
  972. j2->position = TPE_vec3Plus(j2->position,shift);
  973. // compute new velocities:
  974. TPE_Unit v1, v2;
  975. TPE_Vec3 vel = TPE_vec3(j1->velocity[0],j1->velocity[1],j1->velocity[2]);
  976. vel = TPE_vec3Project(vel,dir);
  977. j1->velocity[0] = j1->velocity[0] - vel.x;
  978. j1->velocity[1] = j1->velocity[1] - vel.y;
  979. j1->velocity[2] = j1->velocity[2] - vel.z;
  980. /* friction explanation: Not physically correct (doesn't depend on load),
  981. friction basically means we weighted average the velocities of the bodies
  982. in the direction perpendicular to the hit normal, in the ratio of their
  983. masses, friction coefficient just says how much of this effect we apply
  984. (it multiplies the friction vectors we are subtracting) */
  985. TPE_Vec3 frictionVec =
  986. TPE_vec3(j1->velocity[0],j1->velocity[1],j1->velocity[2]);
  987. v1 = TPE_vec3Dot(vel,dir);
  988. vel = TPE_vec3(j2->velocity[0],j2->velocity[1],j2->velocity[2]);
  989. vel = TPE_vec3Project(vel,dir);
  990. j2->velocity[0] = j2->velocity[0] - vel.x;
  991. j2->velocity[1] = j2->velocity[1] - vel.y;
  992. j2->velocity[2] = j2->velocity[2] - vel.z;
  993. frictionVec = TPE_vec3Minus(
  994. TPE_vec3(j2->velocity[0],j2->velocity[1],j2->velocity[2]),
  995. frictionVec);
  996. v2 = TPE_vec3Dot(vel,dir);
  997. TPE_getVelocitiesAfterCollision(&v1,&v2,mass1,mass2,elasticity);
  998. vel = TPE_vec3Times(dir,v1);
  999. #define assignVec(j,i,d,o) \
  1000. j->velocity[i] = j->velocity[i] + vel.d o (((frictionVec.d * ratio) / \
  1001. TPE_FRACTIONS_PER_UNIT) * friction) / TPE_FRACTIONS_PER_UNIT;
  1002. assignVec(j1,0,x,+)
  1003. assignVec(j1,1,y,+)
  1004. assignVec(j1,2,z,+)
  1005. vel = TPE_vec3Times(dir,v2);
  1006. ratio = TPE_FRACTIONS_PER_UNIT - ratio;
  1007. assignVec(j2,0,x,-)
  1008. assignVec(j2,1,y,-)
  1009. assignVec(j2,2,z,-)
  1010. #undef assignVec
  1011. return 1;
  1012. }
  1013. return 0;
  1014. }
  1015. TPE_Vec3 TPE_vec3Times(TPE_Vec3 v, TPE_Unit units)
  1016. {
  1017. v.x = (v.x * units) / TPE_FRACTIONS_PER_UNIT;
  1018. v.y = (v.y * units) / TPE_FRACTIONS_PER_UNIT;
  1019. v.z = (v.z * units) / TPE_FRACTIONS_PER_UNIT;
  1020. return v;
  1021. }
  1022. TPE_Vec3 TPE_vec3TimesNonNormalized(TPE_Vec3 v, TPE_Unit q)
  1023. {
  1024. v.x *= q;
  1025. v.y *= q;
  1026. v.z *= q;
  1027. return v;
  1028. }
  1029. void TPE_getVelocitiesAfterCollision(
  1030. TPE_Unit *v1,
  1031. TPE_Unit *v2,
  1032. TPE_Unit m1,
  1033. TPE_Unit m2,
  1034. TPE_Unit elasticity
  1035. )
  1036. {
  1037. /* In the following a lot of TPE_FRACTIONS_PER_UNIT cancel out, feel free to
  1038. check if confused. */
  1039. TPE_Unit m1Pm2 = TPE_nonZero(m1 + m2);
  1040. TPE_Unit v2Mv1 = TPE_nonZero(*v2 - *v1);
  1041. TPE_Unit m1v1Pm2v2 = ((m1 * *v1) + (m2 * *v2));
  1042. *v1 = (((elasticity * m2 / TPE_FRACTIONS_PER_UNIT) * v2Mv1)
  1043. + m1v1Pm2v2) / m1Pm2;
  1044. *v2 = (((elasticity * m1 / TPE_FRACTIONS_PER_UNIT) * -1 * v2Mv1)
  1045. + m1v1Pm2v2) / m1Pm2;
  1046. }
  1047. uint8_t TPE_jointEnvironmentResolveCollision(TPE_Joint *joint, TPE_Unit elasticity,
  1048. TPE_Unit friction, TPE_ClosestPointFunction env)
  1049. {
  1050. TPE_Vec3 toJoint = TPE_vec3Minus(joint->position,env(joint->position,TPE_JOINT_SIZE(*joint)));
  1051. TPE_Unit len = TPE_LENGTH(toJoint);
  1052. if (len <= TPE_JOINT_SIZE(*joint))
  1053. {
  1054. if (_TPE_collisionCallback != 0)
  1055. if (!_TPE_collisionCallback(_TPE_body1Index,
  1056. _TPE_joint1Index,_TPE_body2Index,_TPE_joint2Index,
  1057. TPE_vec3Minus(joint->position,toJoint)))
  1058. return 0;
  1059. // colliding
  1060. TPE_Vec3 positionBackup = joint->position, shift;
  1061. uint8_t success = 0;
  1062. if (len > 0)
  1063. {
  1064. /* Joint center is still outside the geometry so we can determine the
  1065. normal and use it to shift it outside. This can still leave the joint
  1066. colliding though, so try to repeat it a few times. */
  1067. for (int i = 0; i < TPE_COLLISION_RESOLUTION_ITERATIONS; ++i)
  1068. {
  1069. shift = toJoint;
  1070. TPE_vec3Normalize(&shift);
  1071. shift = TPE_vec3Times(shift,TPE_JOINT_SIZE(*joint) - len +
  1072. TPE_COLLISION_RESOLUTION_MARGIN);
  1073. joint->position = TPE_vec3Plus(joint->position,shift);
  1074. toJoint = TPE_vec3Minus(joint->position,env(joint->position,
  1075. TPE_JOINT_SIZE(*joint)));
  1076. len = TPE_LENGTH(toJoint); // still colliding?
  1077. if (len >= TPE_JOINT_SIZE(*joint))
  1078. {
  1079. success = 1;
  1080. break;
  1081. }
  1082. }
  1083. }
  1084. if (!success)
  1085. {
  1086. /* Shifting along normal was unsuccessfull, now try different approach:
  1087. shift back by joint velocity. */
  1088. shift = TPE_vec3(-1 * joint->velocity[0],-1 * joint->velocity[1],
  1089. -1 * joint->velocity[2]);
  1090. for (int i = 0; i < TPE_COLLISION_RESOLUTION_ITERATIONS; ++i)
  1091. {
  1092. joint->position = TPE_vec3Plus(joint->position,shift);
  1093. toJoint = TPE_vec3Minus(joint->position,env(joint->position,TPE_JOINT_SIZE(*joint)));
  1094. len = TPE_LENGTH(toJoint); // still colliding?
  1095. if (len >= TPE_JOINT_SIZE(*joint))
  1096. {
  1097. success = 1;
  1098. break;
  1099. }
  1100. shift.x /= 2; // decrease the step a bit
  1101. shift.y /= 2;
  1102. shift.z /= 2;
  1103. }
  1104. }
  1105. if (success)
  1106. {
  1107. TPE_Vec3 vel = TPE_vec3(joint->velocity[0],joint->velocity[1],
  1108. joint->velocity[2]);
  1109. vel = TPE_vec3Project(vel,shift); // parallel part of velocity
  1110. TPE_Vec3 vel2 = TPE_vec3Minus( // perpendicular part of velocity
  1111. TPE_vec3(joint->velocity[0],joint->velocity[1],joint->velocity[2]),vel);
  1112. vel2 = TPE_vec3Times(vel2,friction);
  1113. vel = TPE_vec3Times(vel,TPE_FRACTIONS_PER_UNIT + elasticity);
  1114. joint->velocity[0] -= vel.x + vel2.x;
  1115. joint->velocity[1] -= vel.y + vel2.y;
  1116. joint->velocity[2] -= vel.z + vel2.z;
  1117. }
  1118. else
  1119. {
  1120. TPE_LOG("WARNING: joint-environment collision couldn't be resolved");
  1121. joint->position = positionBackup;
  1122. joint->velocity[0] = 0;
  1123. joint->velocity[1] = 0;
  1124. joint->velocity[2] = 0;
  1125. return 2;
  1126. }
  1127. return 1;
  1128. }
  1129. return 0;
  1130. }
  1131. uint8_t TPE_bodyEnvironmentCollide(const TPE_Body *body,
  1132. TPE_ClosestPointFunction env)
  1133. {
  1134. // TODO: should bounding vol check be here? maybe in param?
  1135. for (uint16_t i = 0; i < body->jointCount; ++i)
  1136. {
  1137. const TPE_Joint *joint = body->joints + i;
  1138. TPE_Unit size = TPE_JOINT_SIZE(*joint);
  1139. if (TPE_DISTANCE(joint->position,env(joint->position,size)) <= size)
  1140. return 1;
  1141. }
  1142. return 0;
  1143. }
  1144. uint8_t TPE_bodyEnvironmentResolveCollision(TPE_Body *body,
  1145. TPE_ClosestPointFunction env)
  1146. {
  1147. /* Bounding sphere test first. NOTE: This is not a minimal bounding sphere
  1148. but one created from the bounding box. Hopes are that this can be faster.
  1149. TODO: actually test if the minimal B sphere is faster */
  1150. TPE_Vec3 v1, v2;
  1151. TPE_bodyGetAABB(body,&v1,&v2);
  1152. v1.x = (v1.x + v2.x) / 2;
  1153. v1.y = (v1.y + v2.y) / 2;
  1154. v1.z = (v1.z + v2.z) / 2;
  1155. TPE_Unit d = TPE_DISTANCE(v1,v2);
  1156. if (TPE_DISTANCE(v1,env(v1,d)) > d)
  1157. return 0;
  1158. // now test the full body collision:
  1159. uint8_t collision = 0;
  1160. for (uint16_t i = 0; i < body->jointCount; ++i)
  1161. {
  1162. TPE_Vec3 previousPos = body->joints[i].position;
  1163. _TPE_joint1Index = i;
  1164. uint8_t r = TPE_jointEnvironmentResolveCollision(
  1165. body->joints + i,body->elasticity,body->friction,env);
  1166. if (r)
  1167. {
  1168. collision = 1;
  1169. if (body->flags & TPE_BODY_FLAG_NONROTATING)
  1170. _TPE_bodyNonrotatingJointCollided(body,i,previousPos,r == 1);
  1171. }
  1172. }
  1173. return collision;
  1174. }
  1175. TPE_Vec3 TPE_vec3Normalized(TPE_Vec3 v)
  1176. {
  1177. TPE_vec3Normalize(&v);
  1178. return v;
  1179. }
  1180. TPE_Unit TPE_atan(TPE_Unit x)
  1181. {
  1182. /* atan approximation by polynomial
  1183. WARNING: this will break with different value of TPE_FRACTIONS_PER_UNIT */
  1184. TPE_Unit sign = 1, x2 = x * x;
  1185. if (x < 0)
  1186. {
  1187. x *= -1;
  1188. sign = -1;
  1189. }
  1190. if (x > 30000) // anti overflow
  1191. return sign * (TPE_FRACTIONS_PER_UNIT / 4);
  1192. return sign *
  1193. (307 * x + x2) / ((267026 + 633 * x + x2) / 128);
  1194. }
  1195. void _TPE_vec2Rotate(TPE_Unit *x, TPE_Unit *y, TPE_Unit angle)
  1196. {
  1197. TPE_Unit tmp = *x;
  1198. TPE_Unit s = TPE_sin(angle);
  1199. TPE_Unit c = TPE_cos(angle);
  1200. *x = (c * *x - s * *y) / TPE_FRACTIONS_PER_UNIT;
  1201. *y = (s * tmp + c * *y) / TPE_FRACTIONS_PER_UNIT;
  1202. }
  1203. TPE_Unit _TPE_vec2Angle(TPE_Unit x, TPE_Unit y)
  1204. {
  1205. TPE_Unit r = 0;
  1206. if (x != 0)
  1207. {
  1208. r = TPE_atan((y * TPE_FRACTIONS_PER_UNIT) / x);
  1209. if (x < 0)
  1210. r += TPE_FRACTIONS_PER_UNIT / 2;
  1211. else if (r < 0)
  1212. r += TPE_FRACTIONS_PER_UNIT;
  1213. }
  1214. else
  1215. {
  1216. if (y < 0)
  1217. r = (3 * TPE_FRACTIONS_PER_UNIT) / 4;
  1218. else if (y > 0)
  1219. r = TPE_FRACTIONS_PER_UNIT / 4;
  1220. // else (y == 0) r stays 0
  1221. }
  1222. return r;
  1223. }
  1224. TPE_Vec3 TPE_orientationFromVecs(TPE_Vec3 forward, TPE_Vec3 right)
  1225. {
  1226. TPE_Vec3 result;
  1227. // get rotation around Y:
  1228. result.y = _TPE_vec2Angle(forward.z,-1 * forward.x);
  1229. // now rotate back by this angle to align with x = 0 plane:
  1230. _TPE_vec2Rotate(&forward.z,&forward.x,result.y);
  1231. _TPE_vec2Rotate(&right.z,&right.x,result.y);
  1232. // now do the same for the second axis:
  1233. result.x =
  1234. _TPE_vec2Angle(forward.z,forward.y);
  1235. _TPE_vec2Rotate(&right.z,&right.y,-1 * result.x);
  1236. result.z = _TPE_vec2Angle(right.x,-1 * right.y);
  1237. return result;
  1238. }
  1239. TPE_Vec3 _TPE_project3DPoint(TPE_Vec3 p, TPE_Vec3 camPos, TPE_Vec3 camRot,
  1240. TPE_Vec3 camView)
  1241. {
  1242. // transform to camera space:
  1243. p = TPE_vec3Minus(p,camPos);
  1244. _TPE_vec2Rotate(&p.z,&p.x,camRot.y);
  1245. _TPE_vec2Rotate(&p.z,&p.y,-1 * camRot.x);
  1246. _TPE_vec2Rotate(&p.y,&p.x,-1 * camRot.z);
  1247. if (p.z <= 0)
  1248. return p;
  1249. p.x = (p.x * camView.z) / p.z;
  1250. p.y = (p.y * camView.z) / p.z;
  1251. p.x = camView.x / 2 + (p.x * camView.x) / (2 * TPE_FRACTIONS_PER_UNIT);
  1252. p.y = camView.y / 2 - (p.y * camView.x) / (2 * TPE_FRACTIONS_PER_UNIT);
  1253. // ^ x here intentional
  1254. return p;
  1255. }
  1256. void _TPE_drawDebugPixel(
  1257. TPE_Unit x, TPE_Unit y, TPE_Unit w, TPE_Unit h, uint8_t c,
  1258. TPE_DebugDrawFunction f)
  1259. {
  1260. if (x >= 0 && x < w && y >= 0 && y < h)
  1261. f(x,y,c);
  1262. }
  1263. void TPE_worldDebugDraw(TPE_World *world, TPE_DebugDrawFunction drawFunc,
  1264. TPE_Vec3 camPos, TPE_Vec3 camRot, TPE_Vec3 camView, uint16_t envGridRes,
  1265. TPE_Unit envGridSize)
  1266. {
  1267. #define Z_LIMIT 250
  1268. if (world->environmentFunction != 0)
  1269. {
  1270. // environment:
  1271. TPE_Vec3 testPoint;
  1272. TPE_Vec3 center = TPE_vec3(0,TPE_sin(camRot.x),TPE_cos(camRot.x));
  1273. _TPE_vec2Rotate(&center.x,&center.z,camRot.y);
  1274. TPE_Unit gridHalfSize = (envGridSize * envGridRes) / 2;
  1275. center = TPE_vec3Times(center,gridHalfSize);
  1276. center = TPE_vec3Plus(camPos,center);
  1277. center.x = (center.x / envGridSize) * envGridSize;
  1278. center.y = (center.y / envGridSize) * envGridSize;
  1279. center.z = (center.z / envGridSize) * envGridSize;
  1280. testPoint.y = center.y - gridHalfSize;
  1281. for (uint8_t j = 0; j < envGridRes; ++j)
  1282. {
  1283. testPoint.x = center.x - gridHalfSize;
  1284. for (uint8_t k = 0; k < envGridRes; ++k)
  1285. {
  1286. testPoint.z = center.z - gridHalfSize;
  1287. for (uint8_t l = 0; l < envGridRes; ++l)
  1288. {
  1289. TPE_Vec3 r = world->environmentFunction(testPoint,envGridSize);
  1290. if ((r.x != testPoint.x || r.y != testPoint.y || r.z != testPoint.z))
  1291. {
  1292. // TODO: accel. by testing cheb dist first?
  1293. r = _TPE_project3DPoint(r,camPos,camRot,camView);
  1294. if (r.z > Z_LIMIT)
  1295. _TPE_drawDebugPixel(r.x,r.y,camView.x,camView.y,2,drawFunc);
  1296. }
  1297. testPoint.z += envGridSize;
  1298. }
  1299. testPoint.x += envGridSize;
  1300. }
  1301. testPoint.y += envGridSize;
  1302. }
  1303. }
  1304. for (uint16_t i = 0; i < world->bodyCount; ++i)
  1305. {
  1306. // connections:
  1307. for (uint16_t j = 0; j < world->bodies[i].connectionCount; ++j)
  1308. {
  1309. TPE_Vec3
  1310. p1 = world->bodies[i].joints[world->bodies[i].connections[j].joint1].position,
  1311. p2 = world->bodies[i].joints[world->bodies[i].connections[j].joint2].position;
  1312. p1 = _TPE_project3DPoint(p1,camPos,camRot,camView);
  1313. p2 = _TPE_project3DPoint(p2,camPos,camRot,camView);
  1314. if (p1.z <= Z_LIMIT || p2.z <= Z_LIMIT)
  1315. continue;
  1316. TPE_Vec3 diff = TPE_vec3Minus(p2,p1);
  1317. #define SEGS 16
  1318. for (uint16_t k = 0; k < SEGS; ++k)
  1319. {
  1320. p2.x = p1.x + (diff.x * k) / SEGS;
  1321. p2.y = p1.y + (diff.y * k) / SEGS;
  1322. _TPE_drawDebugPixel(p2.x,p2.y,camView.x,camView.y,0,drawFunc);
  1323. }
  1324. #undef SEGS
  1325. }
  1326. // joints:
  1327. for (uint16_t j = 0; j < world->bodies[i].jointCount; ++j)
  1328. {
  1329. TPE_Vec3 p = _TPE_project3DPoint(world->bodies[i].joints[j].position,
  1330. camPos,camRot,camView);
  1331. if (p.z > Z_LIMIT)
  1332. {
  1333. _TPE_drawDebugPixel(p.x,p.y,camView.x,camView.y,1,drawFunc);
  1334. TPE_Unit size = TPE_JOINT_SIZE(world->bodies[i].joints[j]) / 2;
  1335. size = (size * camView.x) / TPE_FRACTIONS_PER_UNIT;
  1336. size = (size * camView.z) / p.z;
  1337. #define SEGS 4
  1338. for (uint8_t k = 0; k < SEGS + 1; ++k)
  1339. {
  1340. TPE_Unit
  1341. dx = (TPE_sin(TPE_FRACTIONS_PER_UNIT * k / (8 * SEGS)) * size)
  1342. / TPE_FRACTIONS_PER_UNIT,
  1343. dy = (TPE_cos(TPE_FRACTIONS_PER_UNIT * k / (8 * SEGS)) * size)
  1344. / TPE_FRACTIONS_PER_UNIT;
  1345. #define dp(a,b,c,d) \
  1346. _TPE_drawDebugPixel(p.x a b,p.y c d,camView.x,camView.y,1,drawFunc);
  1347. dp(+,dx,+,dy) dp(+,dx,-,dy) dp(-,dx,+,dy) dp(-,dx,-,dy)
  1348. dp(+,dy,+,dx) dp(+,dy,-,dx) dp(-,dy,+,dx) dp(-,dy,-,dx)
  1349. #undef dp
  1350. #undef SEGS
  1351. }
  1352. }
  1353. }
  1354. }
  1355. #undef Z_LIMIT
  1356. }
  1357. TPE_Vec3 TPE_envBox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec,
  1358. TPE_Vec3 rotation)
  1359. {
  1360. point = TPE_pointRotate(TPE_vec3Minus(point,center),
  1361. TPE_rotationInverse(rotation));
  1362. return TPE_vec3Plus(center,TPE_pointRotate(TPE_envAABox(point,TPE_vec3(0,0,0),
  1363. maxCornerVec),rotation));
  1364. }
  1365. TPE_Vec3 TPE_envAABox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec)
  1366. {
  1367. TPE_Vec3 shifted = TPE_vec3Minus(point,center);
  1368. int8_t sign[3] = {1, 1, 1};
  1369. if (shifted.x < 0)
  1370. {
  1371. shifted.x *= -1;
  1372. sign[0] = -1;
  1373. }
  1374. if (shifted.y < 0)
  1375. {
  1376. shifted.y *= -1;
  1377. sign[1] = -1;
  1378. }
  1379. if (shifted.z < 0)
  1380. {
  1381. shifted.z *= -1;
  1382. sign[2] = -1;
  1383. }
  1384. uint8_t region =
  1385. (shifted.x > maxCornerVec.x) |
  1386. ((shifted.y > maxCornerVec.y) << 1) |
  1387. ((shifted.z > maxCornerVec.z) << 2);
  1388. switch (region)
  1389. {
  1390. #define align(c,i) point.c = center.c + sign[i] * maxCornerVec.c
  1391. case 0x01: align(x,0); break;
  1392. case 0x02: align(y,1); break;
  1393. case 0x04: align(z,2); break;
  1394. case 0x03: align(x,0); align(y,1); break;
  1395. case 0x05: align(x,0); align(z,2); break;
  1396. case 0x06: align(y,1); align(z,2); break;
  1397. case 0x07: align(x,0); align(y,1); align(z,2); break;
  1398. default: break;
  1399. #undef align
  1400. }
  1401. return point;
  1402. }
  1403. TPE_Vec3 TPE_envAABoxInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 size)
  1404. {
  1405. size.x /= 2;
  1406. size.y /= 2;
  1407. size.z /= 2;
  1408. TPE_Vec3 shifted = TPE_vec3Minus(point,center);
  1409. TPE_Vec3 a = TPE_vec3Minus(size,shifted),
  1410. b = TPE_vec3Plus(shifted,size);
  1411. int8_t sx = 1, sy = 1, sz = 1;
  1412. if (b.x < a.x)
  1413. {
  1414. a.x = b.x;
  1415. sx = -1;
  1416. }
  1417. if (b.y < a.y)
  1418. {
  1419. a.y = b.y;
  1420. sy = -1;
  1421. }
  1422. if (b.z < a.z)
  1423. {
  1424. a.z = b.z;
  1425. sz = -1;
  1426. }
  1427. if (a.x < 0 || a.y < 0 || a.z < 0)
  1428. return point;
  1429. if (a.x < a.y)
  1430. {
  1431. if (a.x < a.z)
  1432. point.x = center.x + sx * size.x;
  1433. else
  1434. point.z = center.z + sz * size.z;
  1435. }
  1436. else
  1437. {
  1438. if (a.y < a.z)
  1439. point.y = center.y + sy * size.y;
  1440. else
  1441. point.z = center.z + sz * size.z;
  1442. }
  1443. return point;
  1444. }
  1445. TPE_Vec3 TPE_envSphere(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius)
  1446. {
  1447. // TODO: optim?
  1448. TPE_Vec3 dir = TPE_vec3Minus(point,center);
  1449. TPE_Unit l = TPE_LENGTH(dir);
  1450. if (l <= radius)
  1451. return point;
  1452. dir.x = (dir.x * radius) / l;
  1453. dir.y = (dir.y * radius) / l;
  1454. dir.z = (dir.z * radius) / l;
  1455. return TPE_vec3Plus(center,dir);
  1456. }
  1457. TPE_Vec3 TPE_envHalfPlane(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 normal)
  1458. {
  1459. TPE_Vec3 point2 = TPE_vec3Minus(point,center);
  1460. TPE_Unit tmp = point2.x * normal.x + point2.y * normal.y + point2.z * normal.z;
  1461. if (tmp < 0)
  1462. return point;
  1463. TPE_Unit l = TPE_LENGTH(normal);
  1464. tmp /= l;
  1465. normal.x = (normal.x * TPE_FRACTIONS_PER_UNIT) / l;
  1466. normal.y = (normal.y * TPE_FRACTIONS_PER_UNIT) / l;
  1467. normal.z = (normal.z * TPE_FRACTIONS_PER_UNIT) / l;
  1468. return TPE_vec3Minus(point,
  1469. TPE_vec3Times(normal,tmp));
  1470. }
  1471. uint8_t TPE_checkOverlapAABB(TPE_Vec3 v1Min, TPE_Vec3 v1Max, TPE_Vec3 v2Min,
  1472. TPE_Vec3 v2Max)
  1473. {
  1474. TPE_Unit dist;
  1475. #define test(c) \
  1476. dist = v1Min.c + v1Max.c - v2Max.c - v2Min.c; \
  1477. if (dist < 0) dist *= -1; \
  1478. if (dist > v1Max.c - v1Min.c + v2Max.c - v2Min.c) return 0;
  1479. test(x)
  1480. test(y)
  1481. test(z)
  1482. #undef test
  1483. return 1;
  1484. }
  1485. void TPE_bodyGetAABB(const TPE_Body *body, TPE_Vec3 *vMin, TPE_Vec3 *vMax)
  1486. {
  1487. *vMin = body->joints[0].position;
  1488. *vMax = *vMin;
  1489. TPE_Unit js = TPE_JOINT_SIZE(body->joints[0]);
  1490. vMin->x -= js;
  1491. vMin->y -= js;
  1492. vMin->z -= js;
  1493. vMax->x += js;
  1494. vMax->y += js;
  1495. vMax->z += js;
  1496. for (uint16_t i = 1; i < body->jointCount; ++i)
  1497. {
  1498. TPE_Unit v;
  1499. js = TPE_JOINT_SIZE(body->joints[i]);
  1500. #define test(c) \
  1501. v = body->joints[i].position.c - js; \
  1502. if (v < vMin->c) \
  1503. vMin->c = v; \
  1504. v += 2 * js; \
  1505. if (v > vMax->c) \
  1506. vMax->c = v;
  1507. test(x)
  1508. test(y)
  1509. test(z)
  1510. #undef test
  1511. }
  1512. }
  1513. void TPE_jointPin(TPE_Joint *joint, TPE_Vec3 position)
  1514. {
  1515. joint->position = position;
  1516. joint->velocity[0] = 0;
  1517. joint->velocity[1] = 0;
  1518. joint->velocity[2] = 0;
  1519. }
  1520. TPE_Vec3 TPE_pointRotate(TPE_Vec3 point, TPE_Vec3 rotation)
  1521. {
  1522. _TPE_vec2Rotate(&point.y,&point.x,rotation.z);
  1523. _TPE_vec2Rotate(&point.z,&point.y,rotation.x);
  1524. _TPE_vec2Rotate(&point.x,&point.z,rotation.y);
  1525. return point;
  1526. }
  1527. TPE_Vec3 TPE_rotationInverse(TPE_Vec3 rotation)
  1528. {
  1529. /* If r1 = (X,Y,Z) is rotation in convention ABC then r1^-1 = (-X,-Y,-Z) in
  1530. convention CBA is its inverse rotation. We exploit this, i.e. we rotate
  1531. forward/right vectors in opposite axis order and then turn the result
  1532. into normal rotation/orientation. */
  1533. TPE_Vec3 f = TPE_vec3(0,0,TPE_FRACTIONS_PER_UNIT);
  1534. TPE_Vec3 r = TPE_vec3(TPE_FRACTIONS_PER_UNIT,0,0);
  1535. rotation.x *= -1;
  1536. rotation.y *= -1;
  1537. rotation.z *= -1;
  1538. _TPE_vec2Rotate(&f.x,&f.z,rotation.y);
  1539. _TPE_vec2Rotate(&f.z,&f.y,rotation.x);
  1540. _TPE_vec2Rotate(&f.y,&f.x,rotation.z);
  1541. _TPE_vec2Rotate(&r.x,&r.z,rotation.y);
  1542. _TPE_vec2Rotate(&r.z,&r.y,rotation.x);
  1543. _TPE_vec2Rotate(&r.y,&r.x,rotation.z);
  1544. return TPE_orientationFromVecs(f,r);
  1545. }
  1546. TPE_Vec3 TPE_rotationRotateByAxis(TPE_Vec3 rotation, TPE_Vec3 rotationByAxis)
  1547. {
  1548. TPE_Vec3 f = TPE_pointRotate(TPE_vec3(0,0,TPE_FRACTIONS_PER_UNIT),rotation);
  1549. TPE_Vec3 r = TPE_pointRotate(TPE_vec3(TPE_FRACTIONS_PER_UNIT,0,0),rotation);
  1550. TPE_Unit a = TPE_LENGTH(rotationByAxis);
  1551. TPE_vec3Normalize(&rotationByAxis);
  1552. f = _TPE_rotateByAxis(f,rotationByAxis,a);
  1553. r = _TPE_rotateByAxis(r,rotationByAxis,a);
  1554. return TPE_orientationFromVecs(f,r);
  1555. }
  1556. TPE_Unit TPE_keepInRange(TPE_Unit x, TPE_Unit xMin, TPE_Unit xMax)
  1557. {
  1558. return x > xMin ? (x < xMax ? x : xMax) : xMin;
  1559. }
  1560. TPE_Vec3 TPE_vec3KeepWithinBox(TPE_Vec3 point, TPE_Vec3 boxCenter,
  1561. TPE_Vec3 boxMaxVect)
  1562. {
  1563. point.x = TPE_keepInRange(point.x,
  1564. boxCenter.x - boxMaxVect.x,boxCenter.x + boxMaxVect.x);
  1565. point.y = TPE_keepInRange(point.y,
  1566. boxCenter.y - boxMaxVect.y,boxCenter.y + boxMaxVect.y);
  1567. point.z = TPE_keepInRange(point.z,
  1568. boxCenter.z - boxMaxVect.z,boxCenter.z + boxMaxVect.z);
  1569. return point;
  1570. }
  1571. TPE_Vec3 TPE_envInfiniteCylinder(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3
  1572. direction, TPE_Unit radius)
  1573. {
  1574. TPE_Vec3 d = TPE_vec3Minus(point,center);
  1575. d = TPE_vec3Minus(d,TPE_vec3Project(d,direction));
  1576. TPE_Unit l = TPE_LENGTH(d);
  1577. if (l <= radius)
  1578. return point;
  1579. radius = l - radius;
  1580. d.x = (d.x * radius) / l;
  1581. d.y = (d.y * radius) / l;
  1582. d.z = (d.z * radius) / l;
  1583. return TPE_vec3Minus(point,d);
  1584. }
  1585. TPE_Vec3 TPE_fakeSphereRotation(TPE_Vec3 position1, TPE_Vec3 position2,
  1586. TPE_Unit radius)
  1587. {
  1588. TPE_Vec3 m;
  1589. m.x = position1.z - position2.z;
  1590. m.y = 0;
  1591. m.z = position2.x - position1.x;
  1592. TPE_Unit l = TPE_sqrt(m.x * m.x + m.z * m.z);
  1593. if (l == 0)
  1594. return TPE_vec3(0,0,0);
  1595. TPE_Unit d = (TPE_DISTANCE(position1,position2) *
  1596. TPE_FRACTIONS_PER_UNIT) / (radius * 4);
  1597. m.x = (m.x * d) / l;
  1598. m.z = (m.z * d) / l;
  1599. return m;
  1600. }
  1601. #endif // guard