tinyphysicsengine.h 92 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311
  1. #ifndef _TINYPHYSICSENGINE_H
  2. #define _TINYPHYSICSENGINE_H
  3. /**
  4. tinyphysicsengine (TPE)
  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. The library works with bodies made of spheres connected by elastic springs,
  10. i.e. soft bodies which however behave as "stiff" bodies by default and can
  11. be used to fake rigid body physics as well. Bodies are placed in environemnts
  12. specified by a distance function that allows to implement any mathematical
  13. shape.
  14. Orientations/rotations are in extrinsic Euler angles in the ZXY order (by Z,
  15. then by X, then by Y), if not mentioned otherwise. Angles are in TPE_Units,
  16. TPE_FRACTIONS_PER_UNIT is full angle (2 PI). Sometimes rotations can also be
  17. specified in the "about axis" format: here the object is rotated CW by given
  18. axis by an angle that's specified by the magnitude of the vector.
  19. Where it matters (e.g. rotations about axes) we consider a left-handed coord.
  20. system (x right, y up, z forward).
  21. ------------------------------------------------------------------------------
  22. by drummyfish, 2022
  23. version 0.8d
  24. This work's goal is to never be encumbered by any exclusive intellectual
  25. property rights. The work is therefore provided under CC0 1.0
  26. (https://creativecommons.org/publicdomain/zero/1.0/) + additional WAIVER OF
  27. ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of intellectual property
  28. rights not already waived by CC0 1.0. The WAIVER OF ALL INTELLECTUAL PROPERTY
  29. RGHTS is as follows:
  30. Each contributor to this work agrees that they waive any exclusive rights,
  31. including but not limited to copyright, patents, trademark, trade dress,
  32. industrial design, plant varieties and trade secrets, to any and all ideas,
  33. concepts, processes, discoveries, improvements and inventions conceived,
  34. discovered, made, designed, researched or developed by the contributor either
  35. solely or jointly with others, which relate to this work or result from this
  36. work. Should any waiver of such right be judged legally invalid or
  37. ineffective under applicable law, the contributor hereby grants to each
  38. affected person a royalty-free, non transferable, non sublicensable, non
  39. exclusive, irrevocable and unconditional license to this right.
  40. */
  41. #include <stdint.h>
  42. typedef int32_t TPE_Unit; ///< Basic fixed point unit type.
  43. typedef int16_t TPE_UnitReduced; ///< Like TPE_Unit but saving space
  44. #define TPE_FRACTIONS_PER_UNIT 512 ///< one fixed point unit, don't change
  45. #define TPE_F TPE_FRACTIONS_PER_UNIT ///< short for TPE_FRACTIONS_PER_UNIT
  46. #define TPE_JOINT_SIZE_MULTIPLIER 32 ///< joint size is scaled (size saving)
  47. #define TPE_INFINITY 2147483647
  48. #define TPE_JOINT_SIZE(joint) ((joint).sizeDivided * TPE_JOINT_SIZE_MULTIPLIER)
  49. #ifndef TPE_APPROXIMATE_LENGTH
  50. #define TPE_APPROXIMATE_LENGTH 0 /**< whether or not use length/distance
  51. approximation rather than exact
  52. calculation (1 is faster but less
  53. accurate), beware of possible lower
  54. stability */
  55. #endif
  56. #if !TPE_APPROXIMATE_LENGTH
  57. #define TPE_DISTANCE TPE_dist
  58. #define TPE_LENGTH TPE_vec3Len
  59. #else
  60. #define TPE_DISTANCE TPE_distApprox
  61. #define TPE_LENGTH TPE_vec3LenApprox
  62. #endif
  63. #ifndef TPE_LOG
  64. #define TPE_LOG(s) ; // redefine to some print function to show debug logs
  65. #endif
  66. #ifndef TPE_LOW_SPEED
  67. /** Speed, in TPE_Units per ticks, that is considered low (used e.g. for auto
  68. deactivation of bodies). */
  69. #define TPE_LOW_SPEED 30
  70. #endif
  71. #ifndef TPE_RESHAPE_TENSION_LIMIT
  72. /** Tension limit, in TPE_Units, after which a non-soft body will be reshaped.
  73. Smaller number will keep more stable shapes but will cost more performance. */
  74. #define TPE_RESHAPE_TENSION_LIMIT 20
  75. #endif
  76. #ifndef TPE_RESHAPE_ITERATIONS
  77. /** How many iterations of reshaping will be performed by the step function if
  78. the body's shape needs to be reshaped. Greater number will keep shapes more
  79. stable but will cost some performance. */
  80. #define TPE_RESHAPE_ITERATIONS 3
  81. #endif
  82. #ifndef TPE_DEACTIVATE_AFTER
  83. /** After how many ticks of low speed should a body be disabled. This mustn't
  84. be greater than 255. */
  85. #define TPE_DEACTIVATE_AFTER 128
  86. #endif
  87. #ifndef TPE_LIGHT_DEACTIVATION
  88. /** When a body is activated by a collision, its deactivation counter will be
  89. set to this value, i.e. after a collision the body will be prone to deactivate
  90. sooner than normally. This is to handle situations with many bodies touching
  91. each other that would normally keep activating each other, never coming to
  92. rest. */
  93. #define TPE_LIGHT_DEACTIVATION \
  94. (TPE_DEACTIVATE_AFTER - TPE_DEACTIVATE_AFTER / 10)
  95. #endif
  96. #ifndef TPE_TENSION_ACCELERATION_DIVIDER
  97. /** Number by which the base acceleration (TPE_FRACTIONS_PER_UNIT per tick
  98. squared) caused by the connection tension will be divided. This should be
  99. power of 2. */
  100. #define TPE_TENSION_ACCELERATION_DIVIDER 32
  101. #endif
  102. #ifndef TPE_TENSION_ACCELERATION_THRESHOLD
  103. /** Limit within which acceleration caused by connection tension won't be
  104. applied. */
  105. #define TPE_TENSION_ACCELERATION_THRESHOLD 5
  106. #endif
  107. #ifndef TPE_TENSION_GREATER_ACCELERATION_THRESHOLD
  108. /** Connection tension threshold after which twice as much acceleration will
  109. be applied. This helps prevent diverting joints that are "impaled" by
  110. environment.*/
  111. #define TPE_TENSION_GREATER_ACCELERATION_THRESHOLD \
  112. (TPE_TENSION_ACCELERATION_THRESHOLD * 3)
  113. #endif
  114. #ifndef TPE_COLLISION_RESOLUTION_ITERATIONS
  115. /** Maximum number of iterations to try to uncollide two colliding bodies. */
  116. #define TPE_COLLISION_RESOLUTION_ITERATIONS 16
  117. #endif
  118. #ifndef TPE_COLLISION_RESOLUTION_MARGIN
  119. /** Margin, in TPE_Units, by which a body will be shifted back to get out of
  120. collision. */
  121. #define TPE_COLLISION_RESOLUTION_MARGIN (TPE_F / 64)
  122. #endif
  123. #ifndef TPE_NONROTATING_COLLISION_RESOLVE_ATTEMPTS
  124. /** Number of times a collision of nonrotating bodies with environment will be
  125. attempted to resolve. This probably won't have great performance implications
  126. as complex collisions of this kind should be relatively rare. */
  127. #define TPE_NONROTATING_COLLISION_RESOLVE_ATTEMPTS 8
  128. #endif
  129. #ifndef TPE_APPROXIMATE_NET_SPEED
  130. /** Whether to use a fast approximation for calculating net speed of bodies
  131. which increases performance a bit. */
  132. #define TPE_APPROXIMATE_NET_SPEED 1
  133. #endif
  134. #define TPE_PRINTF_VEC3(v) printf("[%d %d %d]",(v).x,(v).y,(v).z);
  135. typedef struct
  136. {
  137. TPE_Unit x;
  138. TPE_Unit y;
  139. TPE_Unit z;
  140. } TPE_Vec3;
  141. /** Keeps given point within specified axis-aligned box. This can be used e.g.
  142. to smooth rendered movement of jittering physics bodies. */
  143. TPE_Vec3 TPE_vec3KeepWithinBox(TPE_Vec3 point, TPE_Vec3 boxCenter,
  144. TPE_Vec3 boxMaxVect);
  145. TPE_Vec3 TPE_vec3KeepWithinDistanceBand(TPE_Vec3 point, TPE_Vec3 center,
  146. TPE_Unit minDistance, TPE_Unit maxDistance);
  147. TPE_Vec3 TPE_vec3(TPE_Unit x, TPE_Unit y, TPE_Unit z);
  148. TPE_Vec3 TPE_vec3Minus(TPE_Vec3 v1, TPE_Vec3 v2);
  149. TPE_Vec3 TPE_vec3Plus(TPE_Vec3 v1, TPE_Vec3 v2);
  150. TPE_Vec3 TPE_vec3Cross(TPE_Vec3 v1, TPE_Vec3 v2);
  151. TPE_Vec3 TPE_vec3Project(TPE_Vec3 v, TPE_Vec3 base);
  152. TPE_Vec3 TPE_vec3ProjectNormalized(TPE_Vec3 v, TPE_Vec3 baseNormalized);
  153. TPE_Vec3 TPE_vec3Times(TPE_Vec3 v, TPE_Unit units);
  154. TPE_Vec3 TPE_vec3TimesPlain(TPE_Vec3 v, TPE_Unit q);
  155. TPE_Vec3 TPE_vec3Normalized(TPE_Vec3 v);
  156. TPE_Unit TPE_vec3Dot(TPE_Vec3 v1, TPE_Vec3 v2);
  157. TPE_Unit TPE_vec3Len(TPE_Vec3 v);
  158. TPE_Unit TPE_vec3LenApprox(TPE_Vec3 v);
  159. /** Returns an angle in TPE_Units (see angle conventions) of a 2D vector with
  160. the X axis, CCW. */
  161. TPE_Unit TPE_vec2Angle(TPE_Unit x, TPE_Unit y);
  162. /** Keeps given value within specified range. This can be used e.g. for movement
  163. smoothing. */
  164. TPE_Unit TPE_keepInRange(TPE_Unit x, TPE_Unit xMin, TPE_Unit xMax);
  165. static inline TPE_Unit TPE_abs(TPE_Unit x);
  166. static inline TPE_Unit TPE_max(TPE_Unit a, TPE_Unit b);
  167. static inline TPE_Unit TPE_min(TPE_Unit a, TPE_Unit b);
  168. static inline TPE_Unit TPE_nonZero(TPE_Unit x);
  169. static inline TPE_Unit TPE_dist(TPE_Vec3 p1, TPE_Vec3 p2);
  170. static inline TPE_Unit TPE_distApprox(TPE_Vec3 p1, TPE_Vec3 p2);
  171. TPE_Unit TPE_sqrt(TPE_Unit x);
  172. /** Compute sine, TPE_FRACTIONS_PER_UNIT as argument corresponds to 2 * PI
  173. radians. Returns a number from -TPE_FRACTIONS_PER_UNIT to
  174. TPE_FRACTIONS_PER_UNIT. */
  175. TPE_Unit TPE_sin(TPE_Unit x);
  176. TPE_Unit TPE_cos(TPE_Unit x);
  177. TPE_Unit TPE_atan(TPE_Unit x);
  178. typedef struct
  179. {
  180. TPE_Vec3 position;
  181. TPE_UnitReduced velocity[3]; ///< not TPE_Vec3 to save size
  182. uint8_t sizeDivided; /**< size (radius, ...), for saving space divided by
  183. TPE_JOINT_SIZE_MULTIPLIER */
  184. } TPE_Joint;
  185. typedef struct
  186. {
  187. uint8_t joint1;
  188. uint8_t joint2;
  189. uint16_t length; ///< connection's preferred length, uint16_t saves space
  190. } TPE_Connection;
  191. #define TPE_BODY_FLAG_DEACTIVATED 1 /**< Not being updated due to low energy,
  192. "sleeping", will be woken by
  193. collisions etc. */
  194. #define TPE_BODY_FLAG_NONROTATING 2 /**< When set, the body won't rotate,
  195. will only move linearly. Here the
  196. velocity of the body's first joint
  197. is the velocity of the whole
  198. body. */
  199. #define TPE_BODY_FLAG_DISABLED 4 /**< Disabled, not taking part in
  200. simulation. */
  201. #define TPE_BODY_FLAG_SOFT 8 /**< Soft connections, effort won't be
  202. made to keep the body's shape. */
  203. #define TPE_BODY_FLAG_SIMPLE_CONN 16 /**< Simple connections, don't zero out
  204. antagonist forces or apply
  205. connection friction, can increase
  206. performance. */
  207. #define TPE_BODY_FLAG_ALWAYS_ACTIVE 32 /**< Will never deactivate due to low
  208. energy. */
  209. #define TPE_BODY_FLAG_NO_BSPHERE 64 /**< Stops quick bounding sphere checks
  210. against environment. */
  211. /** Function used for defining static environment, working similarly to an SDF
  212. (signed distance function). The parameters are: 3D point P, max distance D.
  213. The function should behave like this: if P is inside the solid environment
  214. volume, P will be returned; otherwise closest point (by Euclidean distance) to
  215. the solid environment volume from P will be returned, except for a case when
  216. this closest point would be further away than D, in which case any arbitrary
  217. point further away than D may be returned (this allows for optimizations). */
  218. typedef TPE_Vec3 (*TPE_ClosestPointFunction)(TPE_Vec3, TPE_Unit);
  219. /** Function that can be used as a joint-joint or joint-environment collision
  220. callback, parameters are following: body1 index, joint1 index, body2 index,
  221. joint2 index, collision world position. If body1 index is the same as body1
  222. index, then collision type is body-environment, otherwise it is body-body
  223. type. The function has to return either 1 if the collision is to be allowed
  224. or 0 if it is to be discarded. This can besides others be used to disable
  225. collisions between some bodies. */
  226. typedef uint8_t (*TPE_CollisionCallback)(uint16_t, uint16_t, uint16_t, uint16_t,
  227. TPE_Vec3);
  228. /** Function used by the debug drawing functions to draw individual pixels to
  229. the screen. The parameters are following: pixel x, pixel y, pixel color. */
  230. typedef void (*TPE_DebugDrawFunction)(uint16_t, uint16_t, uint8_t);
  231. /** Physics body made of spheres (each of same weight but possibly different
  232. radia) connected by elastic springs. */
  233. typedef struct
  234. {
  235. TPE_Joint *joints;
  236. uint8_t jointCount;
  237. TPE_Connection *connections;
  238. uint8_t connectionCount;
  239. TPE_UnitReduced jointMass; ///< mass of a single joint
  240. TPE_UnitReduced friction; ///< friction of each joint
  241. TPE_UnitReduced elasticity; ///< elasticity of each joint
  242. uint8_t flags;
  243. uint8_t deactivateCount;
  244. } TPE_Body;
  245. typedef struct
  246. {
  247. TPE_Body *bodies;
  248. uint16_t bodyCount;
  249. TPE_ClosestPointFunction environmentFunction;
  250. TPE_CollisionCallback collisionCallback;
  251. } TPE_World;
  252. /** Tests the mathematical validity of given closest point function (function
  253. representing the physics environment), i.e. whether for example approaching
  254. some closest point in a straight line keeps approximately the same closest
  255. point. Note that this function may take a long time to complete, especially
  256. with higher gridResolution values and more complex environment functions. You
  257. should use this function to test your environment function, especially if you
  258. create functions for your own shapes etc. The cornerFrom and cornerTo points
  259. are corners of an axis-aligned box within which testing will take place,
  260. gridResolution defines numbers of points (i.e. step length) along each
  261. dimension to test (recommended e.g. 64), allowedError says error within which
  262. points will be considered the same (recommended range approx. 10 to 200). If
  263. testing is successful, 1 is returned, otherwise 0 is returned and the point
  264. around which error was detected is returned in errorPoint (unless the pointer
  265. is 0 in which case it is ignored). */
  266. uint8_t TPE_testClosestPointFunction(TPE_ClosestPointFunction f,
  267. TPE_Vec3 cornerFrom, TPE_Vec3 cornerTo, uint8_t gridResolution,
  268. TPE_UnitReduced allowedError, TPE_Vec3 *errorPoint);
  269. void TPE_bodyInit(TPE_Body *body,
  270. TPE_Joint *joints, uint8_t jointCount,
  271. TPE_Connection *connections, uint8_t connectionCount,
  272. TPE_Unit mass);
  273. void TPE_worldInit(TPE_World *world,
  274. TPE_Body *bodies, uint16_t bodyCount,
  275. TPE_ClosestPointFunction environmentFunction);
  276. /** Gets orientation (rotation) of a body from a position of three of its
  277. joints. The vector from joint1 to joint2 is considered the body's forward
  278. direction, the vector from joint1 to joint3 its right direction. The returned
  279. rotation is in Euler angles (see rotation conventions). */
  280. TPE_Vec3 TPE_bodyGetRotation(const TPE_Body *body, uint16_t joint1,
  281. uint16_t joint2, uint16_t joint3);
  282. void TPE_vec3Normalize(TPE_Vec3 *v);
  283. /** Rotates a 3D point by given Euler angle rotation (see rotation
  284. conventions). */
  285. TPE_Vec3 TPE_pointRotate(TPE_Vec3 point, TPE_Vec3 rotation);
  286. /** Returns an inverse rotation to given rotation, in Euler angles (see rotation
  287. conventions). */
  288. TPE_Vec3 TPE_rotationInverse(TPE_Vec3 rotation);
  289. /** Returns a connection tension, i.e. a signed percentage difference against
  290. desired length (TPE_FRACTIONS_PER_UNIT means 100%). */
  291. static inline TPE_Unit TPE_connectionTension(TPE_Unit length,
  292. TPE_Unit desiredLength);
  293. /** Rotates a rotation specified in Euler angles by given axis + angle (see
  294. rotation conventions). Returns a rotation in Eurler angles. */
  295. TPE_Vec3 TPE_rotationRotateByAxis(TPE_Vec3 rotation, TPE_Vec3 rotationByAxis);
  296. /** Computes the formula of a 1D collision of rigid bodies. */
  297. void TPE_getVelocitiesAfterCollision(TPE_Unit *v1, TPE_Unit *v2, TPE_Unit m1,
  298. TPE_Unit m2, TPE_Unit elasticity);
  299. /** Computes orientation/rotation (see docs for orientation format) from two
  300. vectors (which should be at least close to being perpendicular and do NOT
  301. need to be normalized). This can be used to determine orientation of a body
  302. from a relative position of its joints. */
  303. TPE_Vec3 TPE_rotationFromVecs(TPE_Vec3 forward, TPE_Vec3 right);
  304. TPE_Joint TPE_joint(TPE_Vec3 position, TPE_Unit size);
  305. /** Mostly for internal use, resolves a potential collision of two joints in a
  306. way that keeps the joints outside provided environment (if the function
  307. pointer is not 0). Returns 1 if joints collided or 0 otherwise. */
  308. uint8_t TPE_jointsResolveCollision(TPE_Joint *j1, TPE_Joint *j2,
  309. TPE_Unit mass1, TPE_Unit mass2, TPE_Unit elasticity, TPE_Unit friction,
  310. TPE_ClosestPointFunction env);
  311. /** Mostly for internal use, tests and potentially resolves a collision between
  312. a joint and environment, returns 0 if no collision happened, 1 if it happened
  313. and was resolved normally and 2 if it couldn't be resolved normally. */
  314. uint8_t TPE_jointEnvironmentResolveCollision(TPE_Joint *joint, TPE_Unit
  315. elasticity, TPE_Unit friction, TPE_ClosestPointFunction env);
  316. /** Tests whether a body is currently colliding with the environment. */
  317. uint8_t TPE_bodyEnvironmentCollide(const TPE_Body *body,
  318. TPE_ClosestPointFunction env);
  319. /** Mostly for internal use, tests and potentially resolves a collision of a
  320. body with the environment, returns 1 if collision happened or 0 otherwise. */
  321. uint8_t TPE_bodyEnvironmentResolveCollision(TPE_Body *body,
  322. TPE_ClosestPointFunction env);
  323. TPE_Vec3 TPE_bodyGetLinearVelocity(const TPE_Body *body);
  324. /** Computes the minimum bounding box of given body. */
  325. void TPE_bodyGetAABB(const TPE_Body *body, TPE_Vec3 *vMin, TPE_Vec3 *vMax);
  326. /** Computes a bounding sphere of a body which is not minimal but faster to
  327. compute than the minimum bounding sphere. */
  328. void TPE_bodyGetFastBSphere(const TPE_Body *body, TPE_Vec3 *center,
  329. TPE_Unit *radius);
  330. /** Computes the minimum bounding sphere of a body (there is another function
  331. for a faster approximate bounding sphere). */
  332. void TPE_bodyGetBSphere(const TPE_Body *body, TPE_Vec3 *center,
  333. TPE_Unit *radius);
  334. uint8_t TPE_checkOverlapAABB(TPE_Vec3 v1Min, TPE_Vec3 v1Max, TPE_Vec3 v2Min,
  335. TPE_Vec3 v2Max);
  336. /** Mostly for internal use, checks and potentiall resolves collision of two
  337. bodies so as to keep them outside given environment. Returns 1 if collision
  338. happened or 0 otherwise. */
  339. uint8_t TPE_bodiesResolveCollision(TPE_Body *b1, TPE_Body *b2,
  340. TPE_ClosestPointFunction env);
  341. /** Pins a joint of a body to specified location in space (sets its location
  342. and zeros its velocity). */
  343. void TPE_jointPin(TPE_Joint *joint, TPE_Vec3 position);
  344. /** "Fakes" a rotation of a moving sphere by rotating it in the direction of
  345. its movement; this can create the illusion of the sphere actually rotating
  346. due to friction even if the physics sphere object (a body with a single joint)
  347. isn't rotating at all. Returns a rotation in the "about axis" format (see
  348. library conventions). */
  349. TPE_Vec3 TPE_fakeSphereRotation(TPE_Vec3 position1, TPE_Vec3 position2,
  350. TPE_Unit radius);
  351. /** Casts a ray against environment and returns the closest hit of a surface. If
  352. no surface was hit, a vector with all elements equal to TPE_INFINITY will be
  353. returned. The function internally works differently for outside rays (rays
  354. cast from the outside of the environment) and inside rays. Outside rays can
  355. be traced with raymarching and will be processed very quickly and precisely;
  356. in this case if any intersection is found, the function will try to return a
  357. point outside (not guaranteed) the environment that's just in front of the hit
  358. surface. Inside rays are difficult and slow to trace because environment
  359. function won't provide distance, so the results aren't guaranteed to be
  360. precise (the ray may miss some intersections); here rays will be traced by
  361. given step (insideStepSize) and eventually iterated a bit towards the
  362. intersection -- if any intersection is found, the function will try to return
  363. a point inside (not guaranteed) the environment just before the hit
  364. surface. */
  365. TPE_Vec3 TPE_castEnvironmentRay(TPE_Vec3 rayPos, TPE_Vec3 rayDir,
  366. TPE_ClosestPointFunction environment, TPE_Unit insideStepSize,
  367. TPE_Unit rayMarchMaxStep, uint32_t maxSteps);
  368. /** Casts a ray against bodies in a world (ignoring the environment), returns
  369. the position of the closest hit as well as the hit body's index in bodyIndex
  370. (unless the bodyIndex pointer is 0 in which case it is ignored). Similarly
  371. with jointIndex. If no hit is found a vector with all elements equal to
  372. TPE_INFINITY will be returned and bodyIndex will be -1. A specific body can be
  373. excluded with excludeBody (negative value will just make this parameter
  374. ignored). */
  375. TPE_Vec3 TPE_castBodyRay(TPE_Vec3 rayPos, TPE_Vec3 rayDir, int16_t excludeBody,
  376. const TPE_World *world, int16_t *bodyIndex, int16_t *jointIndex);
  377. /** Performs one step (tick, frame, ...) of the physics world simulation
  378. including updating positions and velocities of bodies, collision detection and
  379. resolution, possible reshaping or deactivation of inactive bodies etc. The
  380. time length of the step is relative to all other units but it's ideal if it is
  381. 1/30th of a second. */
  382. void TPE_worldStep(TPE_World *world);
  383. void TPE_worldDeactivateAll(TPE_World *world);
  384. void TPE_worldActivateAll(TPE_World *world);
  385. TPE_Unit TPE_worldGetNetSpeed(const TPE_World *world);
  386. TPE_Unit TPE_bodyGetNetSpeed(const TPE_Body *body);
  387. TPE_Unit TPE_bodyGetAverageSpeed(const TPE_Body *body);
  388. void TPE_bodyMultiplyNetSpeed(TPE_Body *body, TPE_Unit factor);
  389. void TPE_bodyLimitAverageSpeed(TPE_Body *body, TPE_Unit speedMin,
  390. TPE_Unit speedMax);
  391. /** Deactivates a body (puts it to sleep until another collision or force wake
  392. up). */
  393. void TPE_bodyDeactivate(TPE_Body *body);
  394. static inline uint8_t TPE_bodyIsActive(const TPE_Body *body);
  395. /** Attempts to shift the joints of a soft body so that the tension of all
  396. springs becomes zero while keeping the joints near their current position.
  397. This function performs one iteration of the equalizing algorithm and doesn't
  398. guarantee a perfect solution, it may help to run multiple iterations (call
  399. this function multiple times). */
  400. void TPE_bodyReshape(TPE_Body *body, TPE_ClosestPointFunction
  401. environmentFunction);
  402. /** Mostly for internal use, performs some "magic" on body connections, mainly
  403. cancelling out of velocities going against each other and also applying
  404. connection friction in soft bodies. The strong parameter indicates if the
  405. body is soft or not. */
  406. void TPE_bodyCancelOutVelocities(TPE_Body *body, uint8_t strong);
  407. /** Moves a body by certain offset. */
  408. void TPE_bodyMoveBy(TPE_Body *body, TPE_Vec3 offset);
  409. /** Moves a body (its center of mass) to given position. */
  410. void TPE_bodyMoveTo(TPE_Body *body, TPE_Vec3 position);
  411. /** Zeros velocities of all soft body joints. */
  412. void TPE_bodyStop(TPE_Body *body);
  413. void TPE_bodyActivate(TPE_Body *body);
  414. /** Adds velocity to a soft body. */
  415. void TPE_bodyAccelerate(TPE_Body *body, TPE_Vec3 velocity);
  416. void TPE_bodyApplyGravity(TPE_Body *body, TPE_Unit downwardsAccel);
  417. /** Adds angular velocity to a soft body. The rotation vector specifies the axis
  418. of rotation by its direction and angular velocity by its magnitude (magnitude
  419. of TPE_FRACTIONS_PER_UNIT will add linear velocity of TPE_FRACTIONS_PER_UNIT
  420. per tick to a point in the distance of TPE_FRACTIONS_PER_UNIT from the
  421. rotation axis). */
  422. void TPE_bodySpin(TPE_Body *body, TPE_Vec3 rotation);
  423. /** Same as TPE_bodySpin but additionally allows to specify the center of
  424. the spin. */
  425. void TPE_bodySpinWithCenter(TPE_Body *body, TPE_Vec3 rotation, TPE_Vec3 center);
  426. /** Instantly rotates a body about an axis (see library conventions for
  427. the rotation format). */
  428. void TPE_bodyRotateByAxis(TPE_Body *body, TPE_Vec3 rotation);
  429. /** Computes the center of mass of a body. This averages the position of all
  430. joints; note that if you need, you may estimate the center of the body faster,
  431. e.g. by taking a position of a single "center joint", or averaging just 2
  432. extreme points. */
  433. TPE_Vec3 TPE_bodyGetCenterOfMass(const TPE_Body *body);
  434. /** Draws a debug view of a 3D physics world using a provided pixel drawing
  435. function. This can be used to overlay a simple visualization of the physics
  436. objects to your main render, to spot exact borders of objects etc. The
  437. function draws simple dotted lines and circles with different "colors" for
  438. different types of objects (joints, connections, environemnt). camPos, camRot
  439. and camView should match the camera settings of your main renderer. CamView.x
  440. is horizontal resolution in pixels, camView.y is the vertical resolution,
  441. CamView.z says the camera focal length (~FOV) in TPE_Units (0 means
  442. orthographic projection). envGridRes is the resolution of an environment probe
  443. grid (the function will probe points in space and draw borders of the physics
  444. environemnt), envGridSize is the size (int TPE_Units) of the grid cell. Note
  445. the function may be slow (reducing envGridRes can help, workable value can be
  446. e.g. 16). */
  447. void TPE_worldDebugDraw(TPE_World *world, TPE_DebugDrawFunction drawFunc,
  448. TPE_Vec3 camPos, TPE_Vec3 camRot, TPE_Vec3 camView, uint16_t envGridRes,
  449. TPE_Unit envGridSize, TPE_Unit offset);
  450. #define TPE_DEBUG_COLOR_CONNECTION 0
  451. #define TPE_DEBUG_COLOR_JOINT 1
  452. #define TPE_DEBUG_COLOR_ENVIRONMENT 2
  453. #define TPE_DEBUG_COLOR_INACTIVE 3
  454. uint32_t TPE_jointHash(const TPE_Joint *joint);
  455. uint32_t TPE_connectionHash(const TPE_Connection *connection);
  456. uint32_t TPE_bodyHash(const TPE_Body *body);
  457. /** Computes 32 bit hash of the world, useful for checking if two states of the
  458. world differ. The function takes into account most of the relevant state but
  459. possibly not all of it, for details check the code. */
  460. uint32_t TPE_worldHash(const TPE_World *world);
  461. // FUNCTIONS FOR GENERATING BODIES
  462. void TPE_makeBox(TPE_Joint joints[8], TPE_Connection connections[16],
  463. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize);
  464. void TPE_makeCenterBox(TPE_Joint joints[9], TPE_Connection connections[18],
  465. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize);
  466. void TPE_makeRect(TPE_Joint joints[4], TPE_Connection connections[6],
  467. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize);
  468. void TPE_makeTriangle(TPE_Joint joints[3], TPE_Connection connections[3],
  469. TPE_Unit sideLength, TPE_Unit jointSize);
  470. void TPE_makeCenterRect(TPE_Joint joints[5], TPE_Connection connections[8],
  471. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize);
  472. void TPE_makeCenterRectFull(TPE_Joint joints[5], TPE_Connection connections[10],
  473. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize);
  474. void TPE_make2Line(TPE_Joint joints[2], TPE_Connection connections[1],
  475. TPE_Unit length, TPE_Unit jointSize);
  476. // FUNCTIONS FOR BUILDING ENVIRONMENT
  477. TPE_Vec3 TPE_envAABoxInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 size);
  478. TPE_Vec3 TPE_envAABox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec);
  479. TPE_Vec3 TPE_envBox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec,
  480. TPE_Vec3 rotation);
  481. TPE_Vec3 TPE_envSphere(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius);
  482. TPE_Vec3 TPE_envSphereInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius);
  483. TPE_Vec3 TPE_envHalfPlane(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 normal);
  484. TPE_Vec3 TPE_envGround(TPE_Vec3 point, TPE_Unit height);
  485. TPE_Vec3 TPE_envInfiniteCylinder(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3
  486. direction, TPE_Unit radius);
  487. TPE_Vec3 TPE_envCylinder(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 direction,
  488. TPE_Unit radius);
  489. TPE_Vec3 TPE_envCone(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 direction,
  490. TPE_Unit radius);
  491. TPE_Vec3 TPE_envLineSegment(TPE_Vec3 point, TPE_Vec3 a, TPE_Vec3 b);
  492. TPE_Vec3 TPE_envHeightmap(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit gridSize,
  493. TPE_Unit (*heightFunction)(int32_t x, int32_t y), TPE_Unit maxDist);
  494. /** Environment function for triangular prism, e.g. for ramps. The sides array
  495. contains three 2D coordinates of points of the triangle in given plane with
  496. respect to the center. WARNING: the points must be specified in counter
  497. clowckwise direction! The direction var specified axis direction (0, 1 or
  498. 2).*/
  499. TPE_Vec3 TPE_envAATriPrism(TPE_Vec3 point, TPE_Vec3 center,
  500. const TPE_Unit sides[6], TPE_Unit depth, uint8_t direction);
  501. /* The following are helper macros for creating a union of shapes inside an
  502. environment function and accelerating them with bounding volumes. */
  503. #define TPE_ENV_START(test,point) TPE_Vec3 _pBest = test, _pTest; \
  504. TPE_Unit _dBest = TPE_DISTANCE(_pBest,point), _dTest; \
  505. (void)(_pBest); (void)(_dBest); (void)(_dTest); (void)(_pTest); // supress war
  506. #define TPE_ENV_NEXT(test,point) \
  507. { if (_pBest.x == point.x && _pBest.y == point.y && _pBest.z == point.z) \
  508. return _pBest; \
  509. _pTest = test; _dTest = TPE_DISTANCE(_pTest,point); \
  510. if (_dTest < _dBest) { _pBest = _pTest; _dBest = _dTest; } }
  511. #define TPE_ENV_END return _pBest;
  512. #define TPE_ENV_BCUBE_TEST(bodyBCubeC,bodyBCubeR,envBCubeC,envBCubeR) ( \
  513. (TPE_abs(envBCubeC.x - bodyBCubeC.x) <= ((bodyBCubeR) + (envBCubeR))) && \
  514. (TPE_abs(envBCubeC.y - bodyBCubeC.y) <= ((bodyBCubeR) + (envBCubeR))) && \
  515. (TPE_abs(envBCubeC.z - bodyBCubeC.z) <= ((bodyBCubeR) + (envBCubeR))))
  516. #define TPE_ENV_BSPHERE_TEST(bodyBSphereC,bodyBSphereR,envBSphereC,envBSphereR)\
  517. (TPE_DISTANCE(bodyBSphereC,envBSphereC) <= ((bodyBSphereR) + (envBSphereR)))
  518. //------------------------------------------------------------------------------
  519. // privates:
  520. uint16_t _TPE_body1Index, _TPE_body2Index, _TPE_joint1Index, _TPE_joint2Index;
  521. TPE_CollisionCallback _TPE_collisionCallback;
  522. static inline TPE_Unit TPE_nonZero(TPE_Unit x)
  523. {
  524. return x != 0 ? x : 1;
  525. }
  526. static inline TPE_Unit TPE_connectionTension(TPE_Unit length,
  527. TPE_Unit desiredLength)
  528. {
  529. return (length * TPE_F) / desiredLength
  530. - TPE_F;
  531. }
  532. TPE_Joint TPE_joint(TPE_Vec3 position, TPE_Unit size)
  533. {
  534. TPE_Joint result;
  535. result.velocity[0] = 0;
  536. result.velocity[1] = 0;
  537. result.velocity[2] = 0;
  538. result.position = position;
  539. size /= TPE_JOINT_SIZE_MULTIPLIER;
  540. if (size > 0xff)
  541. {
  542. TPE_LOG("WARNING: joint size too big in TPE_joint");
  543. }
  544. result.sizeDivided = size;
  545. return result;
  546. }
  547. TPE_Vec3 TPE_vec3(TPE_Unit x, TPE_Unit y, TPE_Unit z)
  548. {
  549. TPE_Vec3 r;
  550. r.x = x;
  551. r.y = y;
  552. r.z = z;
  553. return r;
  554. }
  555. TPE_Unit TPE_sqrt(TPE_Unit x)
  556. {
  557. int8_t sign = 1;
  558. if (x < 0)
  559. {
  560. sign = -1;
  561. x *= -1;
  562. }
  563. uint32_t result = 0;
  564. uint32_t a = x;
  565. uint32_t b = 1u << 30;
  566. while (b > a)
  567. b >>= 2;
  568. while (b != 0)
  569. {
  570. if (a >= result + b)
  571. {
  572. a -= result + b;
  573. result = result + 2 * b;
  574. }
  575. b >>= 2;
  576. result >>= 1;
  577. }
  578. return result * sign;
  579. }
  580. TPE_Unit TPE_vec3Len(TPE_Vec3 v)
  581. {
  582. #define ANTI_OVERFLOW 25000
  583. if (v.x < ANTI_OVERFLOW && v.x > -1 * ANTI_OVERFLOW &&
  584. v.y < ANTI_OVERFLOW && v.y > -1 * ANTI_OVERFLOW &&
  585. v.z < ANTI_OVERFLOW && v.z > -1 * ANTI_OVERFLOW)
  586. {
  587. return TPE_sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
  588. }
  589. else
  590. {
  591. v.x /= 32; v.y /= 32; v.z /= 32;
  592. return TPE_sqrt(v.x * v.x + v.y * v.y + v.z * v.z) * 32;
  593. }
  594. #undef ANTI_OVERFLOW
  595. }
  596. TPE_Unit TPE_vec3LenApprox(TPE_Vec3 v)
  597. {
  598. // 48 sided polyhedron approximation
  599. if (v.x < 0) v.x *= -1;
  600. if (v.y < 0) v.y *= -1;
  601. if (v.z < 0) v.z *= -1;
  602. if (v.x < v.y) // order the coordinates
  603. {
  604. if (v.x < v.z)
  605. {
  606. if (v.y < v.z)
  607. { // v.x < v.y < v.z
  608. int32_t t = v.x; v.x = v.z; v.z = t;
  609. }
  610. else
  611. { // v.x < v.z < v.y
  612. int32_t t = v.x; v.x = v.y; v.y = t;
  613. t = v.z; v.z = v.y; v.y = t;
  614. }
  615. }
  616. else
  617. { // v.z < v.x < v.y
  618. int32_t t = v.x; v.x = v.y; v.y = t;
  619. }
  620. }
  621. else
  622. {
  623. if (v.y < v.z)
  624. {
  625. if (v.x < v.z)
  626. { // v.y < v.x < v.z
  627. int32_t t = v.y; v.y = v.z; v.z = t;
  628. t = v.x; v.x = v.y; v.y = t;
  629. }
  630. else
  631. { // v.y < v.z < v.x
  632. int32_t t = v.y; v.y = v.z; v.z = t;
  633. }
  634. }
  635. }
  636. return (893 * v.x + 446 * v.y + 223 * v.z) / 1024;
  637. }
  638. TPE_Unit TPE_dist(TPE_Vec3 p1, TPE_Vec3 p2)
  639. {
  640. p1 = TPE_vec3Minus(p1,p2);
  641. return TPE_vec3Len(p1);
  642. }
  643. TPE_Unit TPE_distApprox(TPE_Vec3 p1, TPE_Vec3 p2)
  644. {
  645. p1 = TPE_vec3Minus(p1,p2);
  646. return TPE_vec3LenApprox(p1);
  647. }
  648. void TPE_bodyInit(TPE_Body *body,
  649. TPE_Joint *joints, uint8_t jointCount,
  650. TPE_Connection *connections, uint8_t connectionCount,
  651. TPE_Unit mass)
  652. {
  653. body->joints = joints;
  654. body->jointCount = jointCount;
  655. body->connections = connections;
  656. body->connectionCount = connectionCount;
  657. body->deactivateCount = 0;
  658. body->friction = TPE_F / 2;
  659. body->elasticity = TPE_F / 2;
  660. body->flags = 0;
  661. body->jointMass = TPE_nonZero(mass / jointCount);
  662. for (uint32_t i = 0; i < connectionCount; ++i)
  663. {
  664. TPE_Unit d = TPE_DISTANCE(
  665. joints[connections[i].joint1].position,
  666. joints[connections[i].joint2].position);
  667. if (d > 0xffff)
  668. {
  669. TPE_LOG("WARNING: joint distance too long in TPE_bodyInit");
  670. }
  671. connections[i].length = d != 0 ? d : 1; // prevent later division by zero
  672. }
  673. }
  674. void TPE_worldInit(TPE_World *world, TPE_Body *bodies, uint16_t bodyCount,
  675. TPE_ClosestPointFunction environmentFunction)
  676. {
  677. world->bodies = bodies;
  678. world->bodyCount = bodyCount;
  679. world->environmentFunction = environmentFunction;
  680. world->collisionCallback = 0;
  681. }
  682. #define C(n,a,b) connections[n].joint1 = a; connections[n].joint2 = b;
  683. void TPE_make2Line(TPE_Joint joints[2], TPE_Connection connections[1],
  684. TPE_Unit length, TPE_Unit jointSize)
  685. {
  686. joints[0] = TPE_joint(TPE_vec3(length / 2,0,0),jointSize);
  687. joints[1] = TPE_joint(TPE_vec3(length / -2,0,0),jointSize);
  688. C(0, 0,1)
  689. }
  690. void TPE_makeRect(TPE_Joint joints[4], TPE_Connection connections[6],
  691. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize)
  692. {
  693. width /= 2;
  694. depth /= 2;
  695. for (uint8_t i = 0; i < 4; ++i)
  696. joints[i] = TPE_joint(TPE_vec3((i % 2) ? -1 * width : width,
  697. 0,(i / 2) ? - 1 * depth : depth),jointSize);
  698. C(0, 0,1) C(1, 0,2) C (2, 3,1) C(3, 3,2)
  699. C(4, 0,3) C(5, 1,2)
  700. }
  701. void TPE_makeCenterRect(TPE_Joint joints[5], TPE_Connection connections[8],
  702. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize)
  703. {
  704. TPE_makeRect(joints,connections,width,depth,jointSize);
  705. joints[4] = TPE_joint(TPE_vec3(0,0,0),jointSize);
  706. C(6, 0,4) C(7, 3,4)
  707. }
  708. void TPE_makeCenterRectFull(TPE_Joint joints[5], TPE_Connection connections[10],
  709. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize)
  710. {
  711. TPE_makeCenterRect(joints,connections,width,depth,jointSize);
  712. C(8, 1,4) C(9, 2,4)
  713. }
  714. void TPE_makeTriangle(TPE_Joint joints[3], TPE_Connection connections[3],
  715. TPE_Unit sideLength, TPE_Unit jointSize)
  716. {
  717. joints[0] = TPE_joint(TPE_vec3(sideLength / 2,0,
  718. TPE_sqrt((sideLength * sideLength) / 2) / 2),jointSize);
  719. joints[1] = joints[0];
  720. joints[1].position.x *= -1;
  721. joints[2] = TPE_joint(TPE_vec3(0,0,-1 * joints[0].position.z),jointSize);
  722. C(0, 0,1) C(1, 1,2) C(2, 2,0)
  723. }
  724. void TPE_makeBox(TPE_Joint joints[8], TPE_Connection connections[16],
  725. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize)
  726. {
  727. width /= 2;
  728. depth /= 2;
  729. height /= 2;
  730. for (uint8_t i = 0; i < 8; ++i)
  731. joints[i] = TPE_joint(
  732. TPE_vec3(
  733. (i % 2) ? width : (-1 * width),
  734. ((i >> 2) % 2) ? height : (-1 * height),
  735. ((i >> 1) % 2) ? depth : (-1 * depth)),
  736. jointSize);
  737. C(0, 0,1) C(1, 1,3) C(2, 3,2) C(3, 2,0) // top
  738. C(4, 4,5) C(5, 5,7) C(6, 7,6) C(7, 6,4) // bottom
  739. C(8, 0,4) C(9, 1,5) C(10,3,7) C(11,2,6) // middle
  740. C(12,0,7) C(13,1,6) C(14,2,5) C(15,3,4) // diagonal
  741. }
  742. void TPE_makeCenterBox(TPE_Joint joints[9], TPE_Connection connections[18],
  743. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize)
  744. {
  745. TPE_makeBox(joints,connections,width,depth,height,jointSize);
  746. joints[8] = TPE_joint(TPE_vec3(0,0,0),jointSize);
  747. C(16, 0,8) C(17, 7,8)
  748. }
  749. #undef C
  750. void TPE_bodyDeactivate(TPE_Body *body)
  751. {
  752. body->flags |= TPE_BODY_FLAG_DEACTIVATED;
  753. }
  754. void TPE_worldStep(TPE_World *world)
  755. {
  756. _TPE_collisionCallback = world->collisionCallback;
  757. for (uint16_t i = 0; i < world->bodyCount; ++i)
  758. {
  759. TPE_Body *body = world->bodies + i;
  760. if (body->flags & (TPE_BODY_FLAG_DEACTIVATED | TPE_BODY_FLAG_DISABLED))
  761. continue;
  762. TPE_Joint *joint = body->joints, *joint2;
  763. TPE_Vec3 origPos = body->joints[0].position;
  764. for (uint16_t j = 0; j < body->jointCount; ++j) // apply velocities
  765. {
  766. // non-rotating bodies will copy the 1st joint's velocity
  767. if (body->flags & TPE_BODY_FLAG_NONROTATING)
  768. for (uint8_t k = 0; k < 3; ++k)
  769. joint->velocity[k] = body->joints[0].velocity[k];
  770. joint->position.x += joint->velocity[0];
  771. joint->position.y += joint->velocity[1];
  772. joint->position.z += joint->velocity[2];
  773. joint++;
  774. }
  775. TPE_Connection *connection = body->connections;
  776. TPE_Vec3 aabbMin, aabbMax;
  777. TPE_bodyGetAABB(body,&aabbMin,&aabbMax);
  778. _TPE_body1Index = i;
  779. _TPE_body2Index = _TPE_body1Index;
  780. uint8_t collided =
  781. TPE_bodyEnvironmentResolveCollision(body,world->environmentFunction);
  782. if (body->flags & TPE_BODY_FLAG_NONROTATING)
  783. {
  784. /* Non-rotating bodies may end up still colliding after environment coll
  785. resolvement (unlike rotating bodies where each joint is ensured separately
  786. to not collide). So if still in collision, we try a few more times. If not
  787. successful, we simply undo any shifts we've done. This should absolutely
  788. prevent any body escaping out of environment bounds. */
  789. for (uint8_t i = 0; i < TPE_NONROTATING_COLLISION_RESOLVE_ATTEMPTS; ++i)
  790. {
  791. if (!collided)
  792. break;
  793. collided =
  794. TPE_bodyEnvironmentResolveCollision(body,world->environmentFunction);
  795. }
  796. if (collided &&
  797. TPE_bodyEnvironmentCollide(body,world->environmentFunction))
  798. TPE_bodyMoveBy(body,TPE_vec3Minus(origPos,body->joints[0].position));
  799. }
  800. else // normal, rotating bodies
  801. {
  802. TPE_Unit bodyTension = 0;
  803. for (uint16_t j = 0; j < body->connectionCount; ++j) // joint tension
  804. {
  805. joint = &(body->joints[connection->joint1]);
  806. joint2 = &(body->joints[connection->joint2]);
  807. TPE_Vec3 dir = TPE_vec3Minus(joint2->position,joint->position);
  808. TPE_Unit tension = TPE_connectionTension(TPE_LENGTH(dir),
  809. connection->length);
  810. bodyTension += tension > 0 ? tension : -tension;
  811. if (tension > TPE_TENSION_ACCELERATION_THRESHOLD ||
  812. tension < -1 * TPE_TENSION_ACCELERATION_THRESHOLD)
  813. {
  814. TPE_vec3Normalize(&dir);
  815. if (tension > TPE_TENSION_GREATER_ACCELERATION_THRESHOLD ||
  816. tension < -1 * TPE_TENSION_GREATER_ACCELERATION_THRESHOLD)
  817. {
  818. /* apply twice the acceleration after a second threshold, not so
  819. elegant but seems to work :) */
  820. dir.x *= 2;
  821. dir.y *= 2;
  822. dir.z *= 2;
  823. }
  824. dir.x /= TPE_TENSION_ACCELERATION_DIVIDER;
  825. dir.y /= TPE_TENSION_ACCELERATION_DIVIDER;
  826. dir.z /= TPE_TENSION_ACCELERATION_DIVIDER;
  827. if (tension < 0)
  828. {
  829. dir.x *= -1;
  830. dir.y *= -1;
  831. dir.z *= -1;
  832. }
  833. joint->velocity[0] += dir.x;
  834. joint->velocity[1] += dir.y;
  835. joint->velocity[2] += dir.z;
  836. joint2->velocity[0] -= dir.x;
  837. joint2->velocity[1] -= dir.y;
  838. joint2->velocity[2] -= dir.z;
  839. }
  840. connection++;
  841. }
  842. if (body->connectionCount > 0)
  843. {
  844. uint8_t hard = !(body->flags & TPE_BODY_FLAG_SOFT);
  845. if (hard)
  846. {
  847. TPE_bodyReshape(body,world->environmentFunction);
  848. bodyTension /= body->connectionCount;
  849. if (bodyTension > TPE_RESHAPE_TENSION_LIMIT)
  850. for (uint8_t k = 0; k < TPE_RESHAPE_ITERATIONS; ++k)
  851. TPE_bodyReshape(body,world->environmentFunction);
  852. }
  853. if (!(body->flags & TPE_BODY_FLAG_SIMPLE_CONN))
  854. TPE_bodyCancelOutVelocities(body,hard);
  855. }
  856. }
  857. for (uint16_t j = 0; j < world->bodyCount; ++j)
  858. {
  859. if (j > i || (world->bodies[j].flags & TPE_BODY_FLAG_DEACTIVATED))
  860. {
  861. // firstly quick-check collision of body AA bounding boxes
  862. TPE_Vec3 aabbMin2, aabbMax2;
  863. TPE_bodyGetAABB(&world->bodies[j],&aabbMin2,&aabbMax2);
  864. _TPE_body2Index = j;
  865. if (TPE_checkOverlapAABB(aabbMin,aabbMax,aabbMin2,aabbMax2) &&
  866. TPE_bodiesResolveCollision(body,world->bodies + j,
  867. world->environmentFunction))
  868. {
  869. TPE_bodyActivate(body);
  870. body->deactivateCount = TPE_LIGHT_DEACTIVATION;
  871. TPE_bodyActivate(world->bodies + j);
  872. world->bodies[j].deactivateCount = TPE_LIGHT_DEACTIVATION;
  873. }
  874. }
  875. }
  876. if (!(body->flags & TPE_BODY_FLAG_ALWAYS_ACTIVE))
  877. {
  878. if (body->deactivateCount >= TPE_DEACTIVATE_AFTER)
  879. {
  880. TPE_bodyStop(body);
  881. body->deactivateCount = 0;
  882. body->flags |= TPE_BODY_FLAG_DEACTIVATED;
  883. }
  884. else if (TPE_bodyGetAverageSpeed(body) <= TPE_LOW_SPEED)
  885. body->deactivateCount++;
  886. else
  887. body->deactivateCount = 0;
  888. }
  889. }
  890. }
  891. void TPE_bodyActivate(TPE_Body *body)
  892. {
  893. // the if check has to be here, don't remove it
  894. if (body->flags & TPE_BODY_FLAG_DEACTIVATED)
  895. {
  896. TPE_bodyStop(body);
  897. body->flags &= ~TPE_BODY_FLAG_DEACTIVATED;
  898. body->deactivateCount = 0;
  899. }
  900. }
  901. TPE_Unit TPE_bodyGetNetSpeed(const TPE_Body *body)
  902. {
  903. #if TPE_APPROXIMATE_NET_SPEED
  904. TPE_Vec3 netV = TPE_vec3(0,0,0);
  905. const TPE_Joint *joint = body->joints;
  906. for (uint16_t i = 0; i < body->jointCount; ++i)
  907. {
  908. netV.x += TPE_abs(joint->velocity[0]);
  909. netV.y += TPE_abs(joint->velocity[1]);
  910. netV.z += TPE_abs(joint->velocity[2]);
  911. joint++;
  912. }
  913. return TPE_vec3LenApprox(netV);
  914. #else
  915. TPE_Unit velocity = 0;
  916. const TPE_Joint *joint = body->joints;
  917. for (uint16_t i = 0; i < body->jointCount; ++i)
  918. {
  919. velocity += TPE_LENGTH(
  920. TPE_vec3(joint->velocity[0],joint->velocity[1],joint->velocity[2]));
  921. joint++;
  922. }
  923. return velocity;
  924. #endif
  925. }
  926. TPE_Unit TPE_bodyGetAverageSpeed(const TPE_Body *body)
  927. {
  928. return TPE_bodyGetNetSpeed(body) / body->jointCount;
  929. }
  930. void TPE_bodyMultiplyNetSpeed(TPE_Body *body, TPE_Unit factor)
  931. {
  932. TPE_Joint *joint = body->joints;
  933. for (uint16_t j = 0; j < body->jointCount; ++j)
  934. {
  935. for (uint8_t k = 0; k < 3; ++k)
  936. joint->velocity[k] =
  937. (((TPE_Unit) joint->velocity[k]) * factor) /
  938. TPE_F;
  939. joint++;
  940. }
  941. }
  942. void TPE_bodyLimitAverageSpeed(TPE_Body *body, TPE_Unit speedMin,
  943. TPE_Unit speedMax)
  944. {
  945. for (uint8_t i = 0; i < 16; ++i)
  946. {
  947. TPE_Unit speed = TPE_bodyGetAverageSpeed(body);
  948. if (speed >= speedMin && speed <= speedMax)
  949. return;
  950. TPE_Unit fraction =
  951. (((speedMax + speedMin) / 2) * TPE_F) /
  952. TPE_nonZero(speed);
  953. TPE_bodyMultiplyNetSpeed(body,fraction);
  954. }
  955. }
  956. void TPE_bodyCancelOutVelocities(TPE_Body *body, uint8_t strong)
  957. {
  958. for (uint16_t i = 0; i < body->connectionCount; ++i)
  959. {
  960. TPE_Connection *c = &body->connections[i];
  961. TPE_Joint *j1 = &(body->joints[c->joint1]);
  962. TPE_Joint *j2 = &(body->joints[c->joint2]);
  963. TPE_Vec3 dir = TPE_vec3Minus(j2->position,j1->position);
  964. TPE_Unit len = TPE_nonZero(TPE_LENGTH(dir));
  965. uint8_t cancel = 1;
  966. if (strong)
  967. {
  968. TPE_Unit tension = TPE_connectionTension(len,c->length);
  969. cancel = tension <= TPE_TENSION_ACCELERATION_THRESHOLD &&
  970. tension >= -1 * TPE_TENSION_ACCELERATION_THRESHOLD;
  971. }
  972. if (cancel)
  973. {
  974. TPE_Vec3
  975. v1 = TPE_vec3(j1->velocity[0],j1->velocity[1],j1->velocity[2]),
  976. v2 = TPE_vec3(j2->velocity[0],j2->velocity[1],j2->velocity[2]);
  977. dir.x = (dir.x * TPE_F) / len; // normalize
  978. dir.y = (dir.y * TPE_F) / len;
  979. dir.z = (dir.z * TPE_F) / len;
  980. v1 = TPE_vec3ProjectNormalized(v1,dir);
  981. v2 = TPE_vec3ProjectNormalized(v2,dir);
  982. TPE_Vec3 avg = TPE_vec3Plus(v1,v2);
  983. avg.x /= 2;
  984. avg.y /= 2;
  985. avg.z /= 2;
  986. if (strong)
  987. {
  988. j1->velocity[0] = j1->velocity[0] - v1.x + avg.x;
  989. j1->velocity[1] = j1->velocity[1] - v1.y + avg.y;
  990. j1->velocity[2] = j1->velocity[2] - v1.z + avg.z;
  991. j2->velocity[0] = j2->velocity[0] - v2.x + avg.x;
  992. j2->velocity[1] = j2->velocity[1] - v2.y + avg.y;
  993. j2->velocity[2] = j2->velocity[2] - v2.z + avg.z;
  994. }
  995. else
  996. {
  997. j1->velocity[0] = j1->velocity[0] - v1.x + (v1.x * 3 + avg.x) / 4;
  998. j1->velocity[1] = j1->velocity[1] - v1.y + (v1.y * 3 + avg.y) / 4;
  999. j1->velocity[2] = j1->velocity[2] - v1.z + (v1.z * 3 + avg.z) / 4;
  1000. j2->velocity[0] = j2->velocity[0] - v2.x + (v2.x * 3 + avg.x) / 4;
  1001. j2->velocity[1] = j2->velocity[1] - v2.y + (v2.y * 3 + avg.y) / 4;
  1002. j2->velocity[2] = j2->velocity[2] - v2.z + (v2.z * 3 + avg.z) / 4;
  1003. }
  1004. }
  1005. }
  1006. }
  1007. void TPE_bodyReshape(TPE_Body *body,
  1008. TPE_ClosestPointFunction environmentFunction)
  1009. {
  1010. for (uint16_t i = 0; i < body->connectionCount; ++i)
  1011. {
  1012. TPE_Connection *c = &body->connections[i];
  1013. TPE_Joint *j1 = &(body->joints[c->joint1]);
  1014. TPE_Joint *j2 = &(body->joints[c->joint2]);
  1015. TPE_Vec3 dir = TPE_vec3Minus(j2->position,j1->position);
  1016. TPE_Vec3 middle = TPE_vec3Plus(j1->position,j2->position);
  1017. middle.x /= 2;
  1018. middle.y /= 2;
  1019. middle.z /= 2;
  1020. TPE_vec3Normalize(&dir);
  1021. dir.x = (dir.x * c->length) / TPE_F;
  1022. dir.y = (dir.y * c->length) / TPE_F;
  1023. dir.z = (dir.z * c->length) / TPE_F;
  1024. TPE_Vec3 positionBackup = j1->position;
  1025. j1->position.x = middle.x - dir.x / 2;
  1026. j1->position.y = middle.y - dir.y / 2;
  1027. j1->position.z = middle.z - dir.z / 2;
  1028. if (environmentFunction != 0 && TPE_LENGTH(TPE_vec3Minus(j1->position,
  1029. environmentFunction(j1->position,TPE_JOINT_SIZE(*j1))))
  1030. < TPE_JOINT_SIZE(*j1))
  1031. j1->position = positionBackup;
  1032. positionBackup = j2->position;
  1033. j2->position.x = j1->position.x + dir.x;
  1034. j2->position.y = j1->position.y + dir.y;
  1035. j2->position.z = j1->position.z + dir.z;
  1036. if (environmentFunction != 0 && TPE_LENGTH(TPE_vec3Minus(j2->position,
  1037. environmentFunction(j2->position,TPE_JOINT_SIZE(*j2))))
  1038. < TPE_JOINT_SIZE(*j2))
  1039. j2->position = positionBackup;
  1040. }
  1041. }
  1042. TPE_Vec3 TPE_vec3Plus(TPE_Vec3 v1, TPE_Vec3 v2)
  1043. {
  1044. v1.x += v2.x;
  1045. v1.y += v2.y;
  1046. v1.z += v2.z;
  1047. return v1;
  1048. }
  1049. TPE_Vec3 TPE_vec3Minus(TPE_Vec3 v1, TPE_Vec3 v2)
  1050. {
  1051. v1.x -= v2.x;
  1052. v1.y -= v2.y;
  1053. v1.z -= v2.z;
  1054. return v1;
  1055. }
  1056. void TPE_vec3Normalize(TPE_Vec3 *v)
  1057. {
  1058. TPE_Unit l = TPE_LENGTH(*v);
  1059. if (l == 0)
  1060. *v = TPE_vec3(TPE_F,0,0);
  1061. else
  1062. {
  1063. if (l < 16) // too short vec would cause inacurracte normalization
  1064. {
  1065. v->x *= 8;
  1066. v->y *= 8;
  1067. v->z *= 8;
  1068. l = TPE_LENGTH(*v);
  1069. }
  1070. v->x = (v->x * TPE_F) / l;
  1071. v->y = (v->y * TPE_F) / l;
  1072. v->z = (v->z * TPE_F) / l;
  1073. }
  1074. }
  1075. TPE_Vec3 TPE_bodyGetRotation(const TPE_Body *body, uint16_t joint1,
  1076. uint16_t joint2, uint16_t joint3)
  1077. {
  1078. return TPE_rotationFromVecs(
  1079. TPE_vec3Minus(
  1080. body->joints[joint2].position,
  1081. body->joints[joint1].position),
  1082. TPE_vec3Minus(
  1083. body->joints[joint3].position,
  1084. body->joints[joint1].position));
  1085. }
  1086. TPE_Vec3 TPE_bodyGetCenterOfMass(const TPE_Body *body)
  1087. {
  1088. // note that joint sizes don't play a role as all weight the same
  1089. TPE_Vec3 result = TPE_vec3(0,0,0);
  1090. const TPE_Joint *j = body->joints;
  1091. for (uint16_t i = 0; i < body->jointCount; ++i)
  1092. {
  1093. result = TPE_vec3Plus(result,j->position);
  1094. j++;
  1095. }
  1096. result.x /= body->jointCount;
  1097. result.y /= body->jointCount;
  1098. result.z /= body->jointCount;
  1099. return result;
  1100. }
  1101. void TPE_bodySpinWithCenter(TPE_Body *body, TPE_Vec3 rotation, TPE_Vec3 center)
  1102. {
  1103. for (uint16_t i = 0; i < body->jointCount; ++i)
  1104. {
  1105. TPE_Joint *j = body->joints + i;
  1106. TPE_Vec3 toPoint = TPE_vec3Minus(j->position,center);
  1107. toPoint = TPE_vec3Project(toPoint,rotation);
  1108. toPoint = TPE_vec3Plus(center,toPoint);
  1109. toPoint = TPE_vec3Minus(j->position,toPoint);
  1110. toPoint = TPE_vec3Cross(toPoint,rotation);
  1111. j->velocity[0] += toPoint.x;
  1112. j->velocity[1] += toPoint.y;
  1113. j->velocity[2] += toPoint.z;
  1114. }
  1115. }
  1116. void TPE_bodySpin(TPE_Body *body, TPE_Vec3 rotation)
  1117. {
  1118. TPE_bodySpinWithCenter(body,rotation,TPE_bodyGetCenterOfMass(body));
  1119. }
  1120. TPE_Vec3 _TPE_rotateByAxis(TPE_Vec3 p, TPE_Vec3 axisNormalized, TPE_Unit angle)
  1121. {
  1122. TPE_Vec3 projected = TPE_vec3ProjectNormalized(p,axisNormalized);
  1123. TPE_Vec3 a = TPE_vec3Minus(p,projected);
  1124. if (a.x == 0 && a.y == 0 && a.z == 0)
  1125. return p;
  1126. TPE_Vec3 b = TPE_vec3Cross(a,axisNormalized);
  1127. return TPE_vec3Plus(projected,TPE_vec3Plus(
  1128. TPE_vec3Times(a,TPE_cos(angle)),
  1129. TPE_vec3Times(b,TPE_sin(angle))));
  1130. }
  1131. void TPE_bodyRotateByAxis(TPE_Body *body, TPE_Vec3 rotation)
  1132. {
  1133. TPE_Vec3 bodyCenter = TPE_bodyGetCenterOfMass(body);
  1134. TPE_Unit angle = TPE_LENGTH(rotation);
  1135. TPE_vec3Normalize(&rotation);
  1136. for (uint16_t i = 0; i < body->jointCount; ++i)
  1137. {
  1138. TPE_Vec3 toPoint = TPE_vec3Minus(body->joints[i].position,bodyCenter);
  1139. body->joints[i].position = TPE_vec3Plus(bodyCenter,
  1140. _TPE_rotateByAxis(toPoint,rotation,angle));
  1141. }
  1142. }
  1143. TPE_Vec3 TPE_vec3Cross(TPE_Vec3 v1, TPE_Vec3 v2)
  1144. {
  1145. TPE_Vec3 r;
  1146. r.x = (v1.y * v2.z - v1.z * v2.y) / TPE_F;
  1147. r.y = (v1.z * v2.x - v1.x * v2.z) / TPE_F;
  1148. r.z = (v1.x * v2.y - v1.y * v2.x) / TPE_F;
  1149. return r;
  1150. }
  1151. TPE_Vec3 TPE_vec3ProjectNormalized(TPE_Vec3 v, TPE_Vec3 baseNormalized)
  1152. {
  1153. TPE_Vec3 r;
  1154. TPE_Unit p = TPE_vec3Dot(v,baseNormalized);
  1155. r.x = (p * baseNormalized.x) / TPE_F;
  1156. r.y = (p * baseNormalized.y) / TPE_F;
  1157. r.z = (p * baseNormalized.z) / TPE_F;
  1158. return r;
  1159. }
  1160. TPE_Vec3 TPE_vec3Project(TPE_Vec3 v, TPE_Vec3 base)
  1161. {
  1162. TPE_vec3Normalize(&base);
  1163. return TPE_vec3ProjectNormalized(v,base);
  1164. }
  1165. void TPE_bodyMoveBy(TPE_Body *body, TPE_Vec3 offset)
  1166. {
  1167. for (uint16_t i = 0; i < body->jointCount; ++i)
  1168. body->joints[i].position = TPE_vec3Plus(body->joints[i].position,
  1169. offset);
  1170. }
  1171. void TPE_bodyApplyGravity(TPE_Body *body, TPE_Unit downwardsAccel)
  1172. {
  1173. if ((body->flags & TPE_BODY_FLAG_DEACTIVATED) ||
  1174. (body->flags & TPE_BODY_FLAG_DISABLED))
  1175. return;
  1176. for (uint16_t i = 0; i < body->jointCount; ++i)
  1177. body->joints[i].velocity[1] -= downwardsAccel;
  1178. }
  1179. void TPE_bodyAccelerate(TPE_Body *body, TPE_Vec3 velocity)
  1180. {
  1181. TPE_bodyActivate(body);
  1182. for (uint16_t i = 0; i < body->jointCount; ++i)
  1183. {
  1184. body->joints[i].velocity[0] += velocity.x;
  1185. body->joints[i].velocity[1] += velocity.y;
  1186. body->joints[i].velocity[2] += velocity.z;
  1187. }
  1188. }
  1189. void TPE_bodyStop(TPE_Body *body)
  1190. {
  1191. for (uint16_t i = 0; i < body->jointCount; ++i)
  1192. {
  1193. body->joints[i].velocity[0] = 0;
  1194. body->joints[i].velocity[1] = 0;
  1195. body->joints[i].velocity[2] = 0;
  1196. }
  1197. }
  1198. void _TPE_bodyNonrotatingJointCollided(TPE_Body *b, int16_t jointIndex,
  1199. TPE_Vec3 origPos, uint8_t success)
  1200. {
  1201. origPos = TPE_vec3Minus(b->joints[jointIndex].position,origPos);
  1202. for (uint16_t i = 0; i < b->jointCount; ++i)
  1203. if (i != jointIndex)
  1204. {
  1205. b->joints[i].position = TPE_vec3Plus(b->joints[i].position,origPos);
  1206. if (success)
  1207. for (uint8_t j = 0; j < 3; ++j)
  1208. b->joints[i].velocity[j] = b->joints[jointIndex].velocity[j];
  1209. }
  1210. }
  1211. TPE_Unit TPE_vec3Dot(TPE_Vec3 v1, TPE_Vec3 v2)
  1212. {
  1213. return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z) / TPE_F;
  1214. }
  1215. TPE_Unit TPE_cos(TPE_Unit x)
  1216. {
  1217. return TPE_sin(x + TPE_F / 4);
  1218. }
  1219. TPE_Unit TPE_sin(TPE_Unit x)
  1220. {
  1221. int8_t sign = 1;
  1222. if (x < 0) // odd function
  1223. {
  1224. x *= -1;
  1225. sign = -1;
  1226. }
  1227. x %= TPE_F;
  1228. if (x > TPE_F / 2)
  1229. {
  1230. x -= TPE_F / 2;
  1231. sign *= -1;
  1232. }
  1233. TPE_Unit tmp = TPE_F - 2 * x;
  1234. #define _PI2 5053 // 9.8696044 * TPE_F
  1235. return sign * // Bhaskara's approximation
  1236. (((32 * x * _PI2) / TPE_F) * tmp) /
  1237. ((_PI2 * (5 * TPE_F - (8 * x * tmp) /
  1238. TPE_F)) / TPE_F);
  1239. #undef _PI2
  1240. }
  1241. uint8_t TPE_bodiesResolveCollision(TPE_Body *b1, TPE_Body *b2,
  1242. TPE_ClosestPointFunction env)
  1243. {
  1244. uint8_t r = 0;
  1245. for (uint16_t i = 0; i < b1->jointCount; ++i)
  1246. for (uint16_t j = 0; j < b2->jointCount; ++j)
  1247. {
  1248. TPE_Vec3 origPos2 = b2->joints[j].position;
  1249. TPE_Vec3 origPos1 = b1->joints[i].position;
  1250. _TPE_joint1Index = i;
  1251. _TPE_joint2Index = j;
  1252. if (TPE_jointsResolveCollision(&(b1->joints[i]),&(b2->joints[j]),
  1253. b1->jointMass,b2->jointMass,(b1->elasticity + b2->elasticity) / 2,
  1254. (b1->friction + b2->friction) / 2,env))
  1255. {
  1256. r = 1;
  1257. if (b1->flags & TPE_BODY_FLAG_NONROTATING)
  1258. _TPE_bodyNonrotatingJointCollided(b1,i,origPos1,1);
  1259. if (b2->flags & TPE_BODY_FLAG_NONROTATING)
  1260. _TPE_bodyNonrotatingJointCollided(b2,j,origPos2,1);
  1261. }
  1262. }
  1263. return r;
  1264. }
  1265. uint8_t TPE_jointsResolveCollision(TPE_Joint *j1, TPE_Joint *j2,
  1266. TPE_Unit mass1, TPE_Unit mass2, TPE_Unit elasticity, TPE_Unit friction,
  1267. TPE_ClosestPointFunction env)
  1268. {
  1269. TPE_Vec3 dir = TPE_vec3Minus(j2->position,j1->position);
  1270. TPE_Unit d = TPE_LENGTH(dir) - TPE_JOINT_SIZE(*j1) - TPE_JOINT_SIZE(*j2);
  1271. if (d < 0) // collision?
  1272. {
  1273. if (_TPE_collisionCallback != 0 && !_TPE_collisionCallback(
  1274. _TPE_body1Index,_TPE_joint1Index,_TPE_body2Index,_TPE_joint2Index,
  1275. TPE_vec3Plus(j1->position,dir)))
  1276. return 0;
  1277. TPE_Vec3
  1278. pos1Backup = j1->position,
  1279. pos2Backup = j2->position;
  1280. // separate joints, the shift distance will depend on the weight ratio:
  1281. d = -1 * d + TPE_COLLISION_RESOLUTION_MARGIN;
  1282. TPE_vec3Normalize(&dir);
  1283. TPE_Unit ratio = (mass2 * TPE_F) /
  1284. TPE_nonZero(mass1 + mass2);
  1285. TPE_Unit shiftDistance = (ratio * d) / TPE_F;
  1286. TPE_Vec3 shift = TPE_vec3Times(dir,shiftDistance);
  1287. j1->position = TPE_vec3Minus(j1->position,shift);
  1288. shiftDistance = d - shiftDistance;
  1289. shift = TPE_vec3Times(dir,shiftDistance);
  1290. j2->position = TPE_vec3Plus(j2->position,shift);
  1291. // compute new velocities:
  1292. TPE_Unit v1, v2;
  1293. TPE_Vec3 vel = TPE_vec3(j1->velocity[0],j1->velocity[1],j1->velocity[2]);
  1294. vel = TPE_vec3Project(vel,dir);
  1295. j1->velocity[0] = j1->velocity[0] - vel.x;
  1296. j1->velocity[1] = j1->velocity[1] - vel.y;
  1297. j1->velocity[2] = j1->velocity[2] - vel.z;
  1298. /* friction explanation: Not physically correct (doesn't depend on load),
  1299. friction basically means we weighted average the velocities of the bodies
  1300. in the direction perpendicular to the hit normal, in the ratio of their
  1301. masses, friction coefficient just says how much of this effect we apply
  1302. (it multiplies the friction vectors we are subtracting) */
  1303. TPE_Vec3 frictionVec =
  1304. TPE_vec3(j1->velocity[0],j1->velocity[1],j1->velocity[2]);
  1305. v1 = TPE_vec3Dot(vel,dir);
  1306. vel = TPE_vec3(j2->velocity[0],j2->velocity[1],j2->velocity[2]);
  1307. vel = TPE_vec3Project(vel,dir);
  1308. j2->velocity[0] = j2->velocity[0] - vel.x;
  1309. j2->velocity[1] = j2->velocity[1] - vel.y;
  1310. j2->velocity[2] = j2->velocity[2] - vel.z;
  1311. frictionVec = TPE_vec3Minus(
  1312. TPE_vec3(j2->velocity[0],j2->velocity[1],j2->velocity[2]),
  1313. frictionVec);
  1314. v2 = TPE_vec3Dot(vel,dir);
  1315. TPE_getVelocitiesAfterCollision(&v1,&v2,mass1,mass2,elasticity);
  1316. vel = TPE_vec3Times(dir,v1);
  1317. #define assignVec(j,i,d,o) \
  1318. j->velocity[i] = j->velocity[i] + vel.d o (((frictionVec.d * ratio) / \
  1319. TPE_F) * friction) / TPE_F;
  1320. assignVec(j1,0,x,+)
  1321. assignVec(j1,1,y,+)
  1322. assignVec(j1,2,z,+)
  1323. vel = TPE_vec3Times(dir,v2);
  1324. ratio = TPE_F - ratio;
  1325. assignVec(j2,0,x,-)
  1326. assignVec(j2,1,y,-)
  1327. assignVec(j2,2,z,-)
  1328. #undef assignVec
  1329. if (env != 0)
  1330. {
  1331. // ensure the joints aren't colliding with environment
  1332. if (TPE_jointEnvironmentResolveCollision(j1,elasticity,friction,env) == 2)
  1333. j1->position = pos1Backup;
  1334. if (TPE_jointEnvironmentResolveCollision(j2,elasticity,friction,env) == 2)
  1335. j2->position = pos2Backup;
  1336. }
  1337. return 1;
  1338. }
  1339. return 0;
  1340. }
  1341. TPE_Vec3 TPE_vec3Times(TPE_Vec3 v, TPE_Unit units)
  1342. {
  1343. v.x = (v.x * units) / TPE_F;
  1344. v.y = (v.y * units) / TPE_F;
  1345. v.z = (v.z * units) / TPE_F;
  1346. return v;
  1347. }
  1348. TPE_Vec3 TPE_vec3TimesPlain(TPE_Vec3 v, TPE_Unit q)
  1349. {
  1350. v.x *= q;
  1351. v.y *= q;
  1352. v.z *= q;
  1353. return v;
  1354. }
  1355. void TPE_getVelocitiesAfterCollision(TPE_Unit *v1, TPE_Unit *v2,
  1356. TPE_Unit m1, TPE_Unit m2, TPE_Unit elasticity)
  1357. {
  1358. /* In the following a lot of TPE_F cancel out, feel free to
  1359. check if confused. */
  1360. TPE_Unit m1Pm2 = TPE_nonZero(m1 + m2);
  1361. TPE_Unit v2Mv1 = TPE_nonZero(*v2 - *v1);
  1362. TPE_Unit m1v1Pm2v2 = ((m1 * *v1) + (m2 * *v2));
  1363. *v1 = (((elasticity * m2 / TPE_F) * v2Mv1)
  1364. + m1v1Pm2v2) / m1Pm2;
  1365. *v2 = (((elasticity * m1 / TPE_F) * -1 * v2Mv1)
  1366. + m1v1Pm2v2) / m1Pm2;
  1367. }
  1368. uint8_t TPE_jointEnvironmentResolveCollision(TPE_Joint *joint,
  1369. TPE_Unit elasticity, TPE_Unit friction, TPE_ClosestPointFunction env)
  1370. {
  1371. TPE_Vec3 toJoint =
  1372. TPE_vec3Minus(joint->position,env(joint->position,TPE_JOINT_SIZE(*joint)));
  1373. TPE_Unit len = TPE_LENGTH(toJoint);
  1374. if (len <= TPE_JOINT_SIZE(*joint))
  1375. {
  1376. if (_TPE_collisionCallback != 0)
  1377. if (!_TPE_collisionCallback(_TPE_body1Index,
  1378. _TPE_joint1Index,_TPE_body2Index,_TPE_joint2Index,
  1379. TPE_vec3Minus(joint->position,toJoint)))
  1380. return 0;
  1381. // colliding
  1382. TPE_Vec3 positionBackup = joint->position, shift;
  1383. uint8_t success = 0;
  1384. if (len > 0)
  1385. {
  1386. /* Joint center is still outside the geometry so we can determine the
  1387. normal and use it to shift it outside. This can still leave the joint
  1388. colliding though, so try to repeat it a few times. */
  1389. for (int i = 0; i < TPE_COLLISION_RESOLUTION_ITERATIONS; ++i)
  1390. {
  1391. shift = toJoint;
  1392. TPE_vec3Normalize(&shift);
  1393. shift = TPE_vec3Times(shift,TPE_JOINT_SIZE(*joint) - len +
  1394. TPE_COLLISION_RESOLUTION_MARGIN);
  1395. joint->position = TPE_vec3Plus(joint->position,shift);
  1396. toJoint = TPE_vec3Minus(joint->position,env(joint->position,
  1397. TPE_JOINT_SIZE(*joint)));
  1398. len = TPE_LENGTH(toJoint); // still colliding?
  1399. if (len >= TPE_JOINT_SIZE(*joint))
  1400. {
  1401. success = 1;
  1402. break;
  1403. }
  1404. }
  1405. }
  1406. if (!success)
  1407. {
  1408. /* Shifting along normal was unsuccessfull, now try different approach:
  1409. shift back by joint velocity. */
  1410. shift = TPE_vec3(-1 * joint->velocity[0],-1 * joint->velocity[1],
  1411. -1 * joint->velocity[2]);
  1412. for (int i = 0; i < TPE_COLLISION_RESOLUTION_ITERATIONS; ++i)
  1413. {
  1414. joint->position = TPE_vec3Plus(joint->position,shift);
  1415. toJoint = TPE_vec3Minus(joint->position,
  1416. env(joint->position,TPE_JOINT_SIZE(*joint)));
  1417. len = TPE_LENGTH(toJoint); // still colliding?
  1418. if (len >= TPE_JOINT_SIZE(*joint))
  1419. {
  1420. success = 1;
  1421. break;
  1422. }
  1423. shift.x /= 2; // decrease the step a bit
  1424. shift.y /= 2;
  1425. shift.z /= 2;
  1426. }
  1427. }
  1428. if (success)
  1429. {
  1430. TPE_Vec3 vel = TPE_vec3(joint->velocity[0],joint->velocity[1],
  1431. joint->velocity[2]);
  1432. vel = TPE_vec3Project(vel,shift); // parallel part of velocity
  1433. TPE_Vec3 vel2 = TPE_vec3Minus( // perpendicular part of velocity
  1434. TPE_vec3(joint->velocity[0],joint->velocity[1],joint->velocity[2]),vel);
  1435. vel2 = TPE_vec3Times(vel2,friction);
  1436. vel = TPE_vec3Times(vel,TPE_F + elasticity);
  1437. joint->velocity[0] -= vel.x + vel2.x;
  1438. joint->velocity[1] -= vel.y + vel2.y;
  1439. joint->velocity[2] -= vel.z + vel2.z;
  1440. }
  1441. else
  1442. {
  1443. TPE_LOG("WARNING: joint-environment collision couldn't be resolved");
  1444. joint->position = positionBackup;
  1445. joint->velocity[0] = 0;
  1446. joint->velocity[1] = 0;
  1447. joint->velocity[2] = 0;
  1448. return 2;
  1449. }
  1450. return 1;
  1451. }
  1452. return 0;
  1453. }
  1454. uint8_t TPE_bodyEnvironmentCollide(const TPE_Body *body,
  1455. TPE_ClosestPointFunction env)
  1456. {
  1457. for (uint16_t i = 0; i < body->jointCount; ++i)
  1458. {
  1459. const TPE_Joint *joint = body->joints + i;
  1460. TPE_Unit size = TPE_JOINT_SIZE(*joint);
  1461. if (TPE_DISTANCE(joint->position,env(joint->position,size)) <= size)
  1462. return 1;
  1463. }
  1464. return 0;
  1465. }
  1466. void TPE_bodyGetFastBSphere(const TPE_Body *body, TPE_Vec3 *center,
  1467. TPE_Unit *radius)
  1468. {
  1469. TPE_Vec3 b;
  1470. TPE_bodyGetAABB(body,center,&b);
  1471. center->x = (center->x + b.x) / 2;
  1472. center->y = (center->y + b.y) / 2;
  1473. center->z = (center->z + b.z) / 2;
  1474. *radius = TPE_DISTANCE(*center,b);
  1475. }
  1476. void TPE_bodyGetBSphere(const TPE_Body *body, TPE_Vec3 *center,
  1477. TPE_Unit *radius)
  1478. {
  1479. *radius = TPE_INFINITY;
  1480. *center = TPE_bodyGetCenterOfMass(body);
  1481. const TPE_Joint *j = body->joints;
  1482. for (uint16_t i = 0; i < body->jointCount; ++i)
  1483. {
  1484. TPE_Vec3 diff;
  1485. TPE_Unit js = TPE_JOINT_SIZE(*j);
  1486. /* Sadly we have to have these conditions here which slow this down. If we
  1487. were only computing a BB sphere of a point cloud, we wouldn't have to
  1488. compute abs vals (as squaring would effectively compute them), but here
  1489. we need to add joint size which needs to know about the sign. */
  1490. diff.x = ((center->x > j->position.x) ?
  1491. (center->x - j->position.x) : (j->position.x - center->x)) + js;
  1492. diff.y = ((center->y > j->position.y) ?
  1493. (center->y - j->position.y) : (j->position.y - center->y)) + js;
  1494. diff.z = ((center->z > j->position.z) ?
  1495. (center->z - j->position.z) : (j->position.z - center->z)) + js;
  1496. TPE_Unit distSquared =
  1497. diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
  1498. if (distSquared < *radius)
  1499. *radius = distSquared;
  1500. j++;
  1501. }
  1502. *radius = TPE_sqrt(*radius);
  1503. }
  1504. uint8_t TPE_bodyEnvironmentResolveCollision(TPE_Body *body,
  1505. TPE_ClosestPointFunction env)
  1506. {
  1507. TPE_Vec3 c;
  1508. TPE_Unit d;
  1509. if (!(body->flags & TPE_BODY_FLAG_NO_BSPHERE))
  1510. {
  1511. TPE_bodyGetFastBSphere(body,&c,&d);
  1512. if (TPE_DISTANCE(c,env(c,d)) > d)
  1513. return 0;
  1514. }
  1515. // now test the full body collision:
  1516. uint8_t collision = 0;
  1517. for (uint16_t i = 0; i < body->jointCount; ++i)
  1518. {
  1519. TPE_Vec3 previousPos = body->joints[i].position;
  1520. _TPE_joint1Index = i;
  1521. uint8_t r = TPE_jointEnvironmentResolveCollision(
  1522. body->joints + i,body->elasticity,body->friction,env);
  1523. if (r)
  1524. {
  1525. collision = 1;
  1526. if (body->flags & TPE_BODY_FLAG_NONROTATING)
  1527. _TPE_bodyNonrotatingJointCollided(body,i,previousPos,r == 1);
  1528. }
  1529. }
  1530. return collision;
  1531. }
  1532. TPE_Vec3 TPE_vec3Normalized(TPE_Vec3 v)
  1533. {
  1534. TPE_vec3Normalize(&v);
  1535. return v;
  1536. }
  1537. TPE_Unit TPE_atan(TPE_Unit x)
  1538. {
  1539. /* atan approximation by polynomial
  1540. WARNING: this will break with different value of TPE_FRACTIONS_PER_UNIT */
  1541. TPE_Unit sign = 1, x2 = x * x;
  1542. if (x < 0)
  1543. {
  1544. x *= -1;
  1545. sign = -1;
  1546. }
  1547. if (x > 30000) // anti overflow
  1548. return sign * (TPE_F / 4);
  1549. return sign *
  1550. (307 * x + x2) / ((267026 + 633 * x + x2) / 128);
  1551. }
  1552. void _TPE_vec2Rotate(TPE_Unit *x, TPE_Unit *y, TPE_Unit angle)
  1553. {
  1554. TPE_Unit tmp = *x;
  1555. TPE_Unit s = TPE_sin(angle);
  1556. TPE_Unit c = TPE_cos(angle);
  1557. *x = (c * *x - s * *y) / TPE_F;
  1558. *y = (s * tmp + c * *y) / TPE_F;
  1559. }
  1560. TPE_Unit TPE_vec2Angle(TPE_Unit x, TPE_Unit y)
  1561. {
  1562. TPE_Unit r = 0;
  1563. if (x != 0)
  1564. {
  1565. r = TPE_atan((y * TPE_F) / x);
  1566. if (x < 0)
  1567. r += TPE_F / 2;
  1568. else if (r < 0)
  1569. r += TPE_F;
  1570. }
  1571. else
  1572. {
  1573. if (y < 0)
  1574. r = (3 * TPE_F) / 4;
  1575. else if (y > 0)
  1576. r = TPE_F / 4;
  1577. // else (y == 0) r stays 0
  1578. }
  1579. return r;
  1580. }
  1581. TPE_Vec3 TPE_rotationFromVecs(TPE_Vec3 forward, TPE_Vec3 right)
  1582. {
  1583. TPE_Vec3 result;
  1584. // get rotation around Y:
  1585. result.y = TPE_vec2Angle(forward.z,-1 * forward.x);
  1586. // now rotate back by this angle to align with x = 0 plane:
  1587. _TPE_vec2Rotate(&forward.z,&forward.x,result.y);
  1588. _TPE_vec2Rotate(&right.z,&right.x,result.y);
  1589. // now do the same for the second axis:
  1590. result.x =
  1591. TPE_vec2Angle(forward.z,forward.y);
  1592. _TPE_vec2Rotate(&right.z,&right.y,-1 * result.x);
  1593. result.z = TPE_vec2Angle(right.x,-1 * right.y);
  1594. return result;
  1595. }
  1596. TPE_Vec3 _TPE_project3DPoint(TPE_Vec3 p, TPE_Vec3 camPos, TPE_Vec3 camRot,
  1597. TPE_Vec3 camView)
  1598. {
  1599. // transform to camera space:
  1600. p = TPE_vec3Minus(p,camPos);
  1601. _TPE_vec2Rotate(&p.z,&p.x,camRot.y);
  1602. _TPE_vec2Rotate(&p.z,&p.y,-1 * camRot.x);
  1603. _TPE_vec2Rotate(&p.y,&p.x,-1 * camRot.z);
  1604. if (p.z <= 0)
  1605. return p;
  1606. if (camView.z != 0)
  1607. {
  1608. // perspective
  1609. p.x = (p.x * camView.z) / p.z;
  1610. p.y = (p.y * camView.z) / p.z;
  1611. p.x = camView.x / 2 + (p.x * camView.x) / (2 * TPE_F);
  1612. p.y = camView.y / 2 - (p.y * camView.x) / (2 * TPE_F);
  1613. // ^ x here intentional
  1614. }
  1615. else
  1616. {
  1617. // ortho
  1618. p.x = camView.x / 2 + p.x;
  1619. p.y = camView.y / 2 - p.y;
  1620. }
  1621. return p;
  1622. }
  1623. void _TPE_drawDebugPixel(
  1624. TPE_Unit x, TPE_Unit y, TPE_Unit w, TPE_Unit h, uint8_t c,
  1625. TPE_DebugDrawFunction f)
  1626. {
  1627. if (x >= 0 && x < w && y >= 0 && y < h)
  1628. f(x,y,c);
  1629. }
  1630. void TPE_worldDebugDraw(TPE_World *world, TPE_DebugDrawFunction drawFunc,
  1631. TPE_Vec3 camPos, TPE_Vec3 camRot, TPE_Vec3 camView, uint16_t envGridRes,
  1632. TPE_Unit envGridSize, TPE_Unit offset)
  1633. {
  1634. #define Z_LIMIT 250
  1635. if (world->environmentFunction != 0)
  1636. {
  1637. // environment:
  1638. TPE_Vec3 testPoint;
  1639. TPE_Unit gridHalfSize = (envGridSize * envGridRes) / 2;
  1640. TPE_Vec3 center;
  1641. offset %= envGridSize;
  1642. if (envGridRes != 0)
  1643. {
  1644. center = TPE_vec3(0,TPE_sin(camRot.x),TPE_cos(camRot.x));
  1645. _TPE_vec2Rotate(&center.x,&center.z,camRot.y);
  1646. center = TPE_vec3Times(center,gridHalfSize);
  1647. center = TPE_vec3Plus(camPos,center);
  1648. center.x = (center.x / envGridSize) * envGridSize + offset;
  1649. center.y = (center.y / envGridSize) * envGridSize + offset;
  1650. center.z = (center.z / envGridSize) * envGridSize + offset;
  1651. }
  1652. testPoint.y = center.y - gridHalfSize;
  1653. for (uint8_t j = 0; j < envGridRes; ++j)
  1654. {
  1655. testPoint.x = center.x - gridHalfSize;
  1656. for (uint8_t k = 0; k < envGridRes; ++k)
  1657. {
  1658. testPoint.z = center.z - gridHalfSize;
  1659. for (uint8_t l = 0; l < envGridRes; ++l)
  1660. {
  1661. TPE_Vec3 r = world->environmentFunction(testPoint,envGridSize);
  1662. if (r.x != testPoint.x || r.y != testPoint.y || r.z != testPoint.z)
  1663. {
  1664. r = _TPE_project3DPoint(r,camPos,camRot,camView);
  1665. if (r.z > Z_LIMIT)
  1666. _TPE_drawDebugPixel(r.x,r.y,camView.x,camView.y,
  1667. TPE_DEBUG_COLOR_ENVIRONMENT,drawFunc);
  1668. }
  1669. testPoint.z += envGridSize;
  1670. }
  1671. testPoint.x += envGridSize;
  1672. }
  1673. testPoint.y += envGridSize;
  1674. }
  1675. }
  1676. for (uint16_t i = 0; i < world->bodyCount; ++i)
  1677. {
  1678. // connections:
  1679. for (uint16_t j = 0; j < world->bodies[i].connectionCount; ++j)
  1680. {
  1681. TPE_Vec3
  1682. p1 = world->bodies[i].joints[
  1683. world->bodies[i].connections[j].joint1].position,
  1684. p2 = world->bodies[i].joints[
  1685. world->bodies[i].connections[j].joint2].position;
  1686. p1 = _TPE_project3DPoint(p1,camPos,camRot,camView);
  1687. p2 = _TPE_project3DPoint(p2,camPos,camRot,camView);
  1688. if (p1.z <= Z_LIMIT || p2.z <= Z_LIMIT)
  1689. continue;
  1690. TPE_Vec3 diff = TPE_vec3Minus(p2,p1);
  1691. #define SEGS 16
  1692. uint8_t c = (world->bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED) ?
  1693. TPE_DEBUG_COLOR_INACTIVE : TPE_DEBUG_COLOR_CONNECTION;
  1694. for (uint16_t k = 0; k < SEGS; ++k)
  1695. {
  1696. p2.x = p1.x + (diff.x * k) / SEGS;
  1697. p2.y = p1.y + (diff.y * k) / SEGS;
  1698. _TPE_drawDebugPixel(p2.x,p2.y,camView.x,camView.y,c,drawFunc);
  1699. }
  1700. #undef SEGS
  1701. }
  1702. // joints:
  1703. for (uint16_t j = 0; j < world->bodies[i].jointCount; ++j)
  1704. {
  1705. TPE_Vec3 p = _TPE_project3DPoint(world->bodies[i].joints[j].position,
  1706. camPos,camRot,camView);
  1707. if (p.z > Z_LIMIT)
  1708. {
  1709. uint8_t color = (world->bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED) ?
  1710. TPE_DEBUG_COLOR_INACTIVE : TPE_DEBUG_COLOR_JOINT;
  1711. _TPE_drawDebugPixel(p.x,p.y,camView.x,camView.y,color,drawFunc);
  1712. TPE_Unit size = TPE_JOINT_SIZE(world->bodies[i].joints[j]);
  1713. if (camView.z != 0) // not ortho?
  1714. {
  1715. size /= 2;
  1716. size = (size * camView.x) / TPE_F;
  1717. size = (size * camView.z) / p.z;
  1718. }
  1719. #define SEGS 4
  1720. for (uint8_t k = 0; k < SEGS + 1; ++k)
  1721. {
  1722. TPE_Unit
  1723. dx = (TPE_sin(TPE_F * k / (8 * SEGS)) * size)
  1724. / TPE_F,
  1725. dy = (TPE_cos(TPE_F * k / (8 * SEGS)) * size)
  1726. / TPE_F;
  1727. #define dp(a,b,c,d) \
  1728. _TPE_drawDebugPixel(p.x a b,p.y c d,camView.x,camView.y,color,drawFunc);
  1729. dp(+,dx,+,dy) dp(+,dx,-,dy) dp(-,dx,+,dy) dp(-,dx,-,dy)
  1730. dp(+,dy,+,dx) dp(+,dy,-,dx) dp(-,dy,+,dx) dp(-,dy,-,dx)
  1731. #undef dp
  1732. #undef SEGS
  1733. }
  1734. }
  1735. }
  1736. }
  1737. #undef Z_LIMIT
  1738. }
  1739. TPE_Vec3 TPE_envBox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec,
  1740. TPE_Vec3 rotation)
  1741. {
  1742. point = TPE_pointRotate(TPE_vec3Minus(point,center),
  1743. TPE_rotationInverse(rotation));
  1744. return TPE_vec3Plus(center,TPE_pointRotate(TPE_envAABox(point,TPE_vec3(0,0,0),
  1745. maxCornerVec),rotation));
  1746. }
  1747. TPE_Vec3 TPE_envAABox(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 maxCornerVec)
  1748. {
  1749. TPE_Vec3 shifted = TPE_vec3Minus(point,center);
  1750. int8_t sign[3] = {1, 1, 1};
  1751. if (shifted.x < 0)
  1752. {
  1753. shifted.x *= -1;
  1754. sign[0] = -1;
  1755. }
  1756. if (shifted.y < 0)
  1757. {
  1758. shifted.y *= -1;
  1759. sign[1] = -1;
  1760. }
  1761. if (shifted.z < 0)
  1762. {
  1763. shifted.z *= -1;
  1764. sign[2] = -1;
  1765. }
  1766. uint8_t region =
  1767. (shifted.x > maxCornerVec.x) |
  1768. ((shifted.y > maxCornerVec.y) << 1) |
  1769. ((shifted.z > maxCornerVec.z) << 2);
  1770. switch (region)
  1771. {
  1772. #define align(c,i) point.c = center.c + sign[i] * maxCornerVec.c
  1773. case 0x01: align(x,0); break;
  1774. case 0x02: align(y,1); break;
  1775. case 0x04: align(z,2); break;
  1776. case 0x03: align(x,0); align(y,1); break;
  1777. case 0x05: align(x,0); align(z,2); break;
  1778. case 0x06: align(y,1); align(z,2); break;
  1779. case 0x07: align(x,0); align(y,1); align(z,2); break;
  1780. default: break;
  1781. #undef align
  1782. }
  1783. return point;
  1784. }
  1785. TPE_Vec3 TPE_envAABoxInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 size)
  1786. {
  1787. size.x /= 2;
  1788. size.y /= 2;
  1789. size.z /= 2;
  1790. TPE_Vec3 shifted = TPE_vec3Minus(point,center);
  1791. TPE_Vec3 a = TPE_vec3Minus(size,shifted),
  1792. b = TPE_vec3Plus(shifted,size);
  1793. int8_t sx = 1, sy = 1, sz = 1;
  1794. if (b.x < a.x)
  1795. {
  1796. a.x = b.x;
  1797. sx = -1;
  1798. }
  1799. if (b.y < a.y)
  1800. {
  1801. a.y = b.y;
  1802. sy = -1;
  1803. }
  1804. if (b.z < a.z)
  1805. {
  1806. a.z = b.z;
  1807. sz = -1;
  1808. }
  1809. if (a.x < 0 || a.y < 0 || a.z < 0)
  1810. return point;
  1811. if (a.x < a.y)
  1812. {
  1813. if (a.x < a.z)
  1814. point.x = center.x + sx * size.x;
  1815. else
  1816. point.z = center.z + sz * size.z;
  1817. }
  1818. else
  1819. {
  1820. if (a.y < a.z)
  1821. point.y = center.y + sy * size.y;
  1822. else
  1823. point.z = center.z + sz * size.z;
  1824. }
  1825. return point;
  1826. }
  1827. TPE_Vec3 TPE_envSphereInside(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius)
  1828. {
  1829. TPE_Vec3 shifted = TPE_vec3Minus(point,center);
  1830. TPE_Unit l = TPE_LENGTH(shifted);
  1831. if (l >= radius)
  1832. return point;
  1833. else if (l < 0)
  1834. return TPE_vec3(center.x + radius,center.y,center.z);
  1835. TPE_vec3Normalize(&shifted);
  1836. return TPE_vec3Plus(center,TPE_vec3Times(shifted,radius));
  1837. }
  1838. TPE_Vec3 TPE_envSphere(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit radius)
  1839. {
  1840. TPE_Vec3 dir = TPE_vec3Minus(point,center);
  1841. TPE_Unit l = TPE_LENGTH(dir);
  1842. if (l <= radius)
  1843. return point;
  1844. dir.x = (dir.x * radius) / l;
  1845. dir.y = (dir.y * radius) / l;
  1846. dir.z = (dir.z * radius) / l;
  1847. return TPE_vec3Plus(center,dir);
  1848. }
  1849. TPE_Vec3 TPE_envHalfPlane(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 normal)
  1850. {
  1851. TPE_Vec3 point2 = TPE_vec3Minus(point,center);
  1852. TPE_Unit tmp =
  1853. point2.x * normal.x + point2.y * normal.y + point2.z * normal.z;
  1854. if (tmp < 0)
  1855. return point;
  1856. TPE_Unit l = TPE_LENGTH(normal);
  1857. tmp /= l;
  1858. normal.x = (normal.x * TPE_F) / l;
  1859. normal.y = (normal.y * TPE_F) / l;
  1860. normal.z = (normal.z * TPE_F) / l;
  1861. return TPE_vec3Minus(point,
  1862. TPE_vec3Times(normal,tmp));
  1863. }
  1864. uint8_t TPE_checkOverlapAABB(TPE_Vec3 v1Min, TPE_Vec3 v1Max, TPE_Vec3 v2Min,
  1865. TPE_Vec3 v2Max)
  1866. {
  1867. TPE_Unit dist;
  1868. #define test(c) \
  1869. dist = v1Min.c + v1Max.c - v2Max.c - v2Min.c; \
  1870. if (dist < 0) dist *= -1; \
  1871. if (dist > v1Max.c - v1Min.c + v2Max.c - v2Min.c) return 0;
  1872. test(x)
  1873. test(y)
  1874. test(z)
  1875. #undef test
  1876. return 1;
  1877. }
  1878. void TPE_bodyGetAABB(const TPE_Body *body, TPE_Vec3 *vMin, TPE_Vec3 *vMax)
  1879. {
  1880. *vMin = body->joints[0].position;
  1881. *vMax = *vMin;
  1882. TPE_Unit js = TPE_JOINT_SIZE(body->joints[0]);
  1883. vMin->x -= js;
  1884. vMin->y -= js;
  1885. vMin->z -= js;
  1886. vMax->x += js;
  1887. vMax->y += js;
  1888. vMax->z += js;
  1889. for (uint16_t i = 1; i < body->jointCount; ++i)
  1890. {
  1891. TPE_Unit v;
  1892. js = TPE_JOINT_SIZE(body->joints[i]);
  1893. #define test(c) \
  1894. v = body->joints[i].position.c - js; \
  1895. if (v < vMin->c) \
  1896. vMin->c = v; \
  1897. v += 2 * js; \
  1898. if (v > vMax->c) \
  1899. vMax->c = v;
  1900. test(x)
  1901. test(y)
  1902. test(z)
  1903. #undef test
  1904. }
  1905. }
  1906. void TPE_jointPin(TPE_Joint *joint, TPE_Vec3 position)
  1907. {
  1908. joint->position = position;
  1909. joint->velocity[0] = 0;
  1910. joint->velocity[1] = 0;
  1911. joint->velocity[2] = 0;
  1912. }
  1913. TPE_Vec3 TPE_pointRotate(TPE_Vec3 point, TPE_Vec3 rotation)
  1914. {
  1915. _TPE_vec2Rotate(&point.y,&point.x,rotation.z);
  1916. _TPE_vec2Rotate(&point.z,&point.y,rotation.x);
  1917. _TPE_vec2Rotate(&point.x,&point.z,rotation.y);
  1918. return point;
  1919. }
  1920. TPE_Vec3 TPE_rotationInverse(TPE_Vec3 rotation)
  1921. {
  1922. /* If r1 = (X,Y,Z) is rotation in convention ABC then r1^-1 = (-X,-Y,-Z) in
  1923. convention CBA is its inverse rotation. We exploit this, i.e. we rotate
  1924. forward/right vectors in opposite axis order and then turn the result
  1925. into normal rotation/orientation. */
  1926. TPE_Vec3 f = TPE_vec3(0,0,TPE_F);
  1927. TPE_Vec3 r = TPE_vec3(TPE_F,0,0);
  1928. rotation.x *= -1;
  1929. rotation.y *= -1;
  1930. rotation.z *= -1;
  1931. _TPE_vec2Rotate(&f.x,&f.z,rotation.y);
  1932. _TPE_vec2Rotate(&f.z,&f.y,rotation.x);
  1933. _TPE_vec2Rotate(&f.y,&f.x,rotation.z);
  1934. _TPE_vec2Rotate(&r.x,&r.z,rotation.y);
  1935. _TPE_vec2Rotate(&r.z,&r.y,rotation.x);
  1936. _TPE_vec2Rotate(&r.y,&r.x,rotation.z);
  1937. return TPE_rotationFromVecs(f,r);
  1938. }
  1939. TPE_Vec3 TPE_rotationRotateByAxis(TPE_Vec3 rotation, TPE_Vec3 rotationByAxis)
  1940. {
  1941. TPE_Vec3 f = TPE_pointRotate(TPE_vec3(0,0,TPE_F),rotation);
  1942. TPE_Vec3 r = TPE_pointRotate(TPE_vec3(TPE_F,0,0),rotation);
  1943. TPE_Unit a = TPE_LENGTH(rotationByAxis);
  1944. TPE_vec3Normalize(&rotationByAxis);
  1945. f = _TPE_rotateByAxis(f,rotationByAxis,a);
  1946. r = _TPE_rotateByAxis(r,rotationByAxis,a);
  1947. return TPE_rotationFromVecs(f,r);
  1948. }
  1949. TPE_Unit TPE_keepInRange(TPE_Unit x, TPE_Unit xMin, TPE_Unit xMax)
  1950. {
  1951. return x > xMin ? (x < xMax ? x : xMax) : xMin;
  1952. }
  1953. TPE_Vec3 TPE_vec3KeepWithinDistanceBand(TPE_Vec3 point, TPE_Vec3 center,
  1954. TPE_Unit minDistance, TPE_Unit maxDistance)
  1955. {
  1956. TPE_Vec3 toPoint = TPE_vec3Minus(point,center);
  1957. TPE_Unit l = TPE_LENGTH(toPoint);
  1958. if (l <= maxDistance)
  1959. {
  1960. if (l >= minDistance)
  1961. return point;
  1962. l = minDistance;
  1963. }
  1964. else
  1965. l = maxDistance;
  1966. return TPE_vec3Plus(center,
  1967. TPE_vec3Times(TPE_vec3Normalized(toPoint),l));
  1968. }
  1969. TPE_Vec3 TPE_vec3KeepWithinBox(TPE_Vec3 point, TPE_Vec3 boxCenter,
  1970. TPE_Vec3 boxMaxVect)
  1971. {
  1972. point.x = TPE_keepInRange(point.x,
  1973. boxCenter.x - boxMaxVect.x,boxCenter.x + boxMaxVect.x);
  1974. point.y = TPE_keepInRange(point.y,
  1975. boxCenter.y - boxMaxVect.y,boxCenter.y + boxMaxVect.y);
  1976. point.z = TPE_keepInRange(point.z,
  1977. boxCenter.z - boxMaxVect.z,boxCenter.z + boxMaxVect.z);
  1978. return point;
  1979. }
  1980. TPE_Vec3 TPE_envInfiniteCylinder(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3
  1981. direction, TPE_Unit radius)
  1982. {
  1983. TPE_Vec3 d = TPE_vec3Minus(point,center);
  1984. d = TPE_vec3Minus(d,TPE_vec3Project(d,direction));
  1985. TPE_Unit l = TPE_LENGTH(d);
  1986. if (l <= radius)
  1987. return point;
  1988. radius = l - radius;
  1989. d.x = (d.x * radius) / l;
  1990. d.y = (d.y * radius) / l;
  1991. d.z = (d.z * radius) / l;
  1992. return TPE_vec3Minus(point,d);
  1993. }
  1994. TPE_Vec3 TPE_envCylinder(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 direction,
  1995. TPE_Unit radius)
  1996. {
  1997. point = TPE_vec3Minus(point,center);
  1998. TPE_Vec3 projected = TPE_vec3Project(point,direction);
  1999. point = TPE_envInfiniteCylinder(point,TPE_vec3(0,0,0),direction,radius);
  2000. TPE_Unit lDir = TPE_nonZero(TPE_LENGTH(direction));
  2001. TPE_Unit lDiff = TPE_LENGTH(projected) - lDir;
  2002. if (lDiff > 0)
  2003. {
  2004. direction.x = (direction.x * lDiff) / lDir;
  2005. direction.y = (direction.y * lDiff) / lDir;
  2006. direction.z = (direction.z * lDiff) / lDir;
  2007. point = (TPE_vec3Dot(projected,direction)) >= 0 ?
  2008. TPE_vec3Minus(point,direction) : TPE_vec3Plus(point,direction);
  2009. }
  2010. return TPE_vec3Plus(center,point);
  2011. }
  2012. TPE_Vec3 TPE_fakeSphereRotation(TPE_Vec3 position1, TPE_Vec3 position2,
  2013. TPE_Unit radius)
  2014. {
  2015. TPE_Vec3 m;
  2016. m.x = position1.z - position2.z;
  2017. m.y = 0;
  2018. m.z = position2.x - position1.x;
  2019. TPE_Unit l = TPE_sqrt(m.x * m.x + m.z * m.z);
  2020. if (l == 0)
  2021. return TPE_vec3(0,0,0);
  2022. TPE_Unit d = (TPE_DISTANCE(position1,position2) *
  2023. TPE_F) / (radius * 4);
  2024. m.x = (m.x * d) / l;
  2025. m.z = (m.z * d) / l;
  2026. return m;
  2027. }
  2028. TPE_Vec3 TPE_castEnvironmentRay(TPE_Vec3 rayPos, TPE_Vec3 rayDir,
  2029. TPE_ClosestPointFunction environment, TPE_Unit insideStepSize,
  2030. TPE_Unit rayMarchMaxStep, uint32_t maxSteps)
  2031. {
  2032. TPE_Vec3 p = rayPos;
  2033. TPE_Vec3 p2 = environment(rayPos,rayMarchMaxStep);
  2034. TPE_Unit totalD = 0;
  2035. TPE_vec3Normalize(&rayDir);
  2036. uint8_t found = 0; // 0 = nothing found, 1 = out/in found, 2 = in/out found
  2037. if (p2.x != p.x || p2.y != p.y || p2.z != p.z)
  2038. {
  2039. // outside ray: ray march
  2040. for (uint32_t i = 0; i < maxSteps; ++i)
  2041. {
  2042. TPE_Unit d = TPE_DISTANCE(p,p2);
  2043. if (d > rayMarchMaxStep)
  2044. d = rayMarchMaxStep;
  2045. totalD += d;
  2046. p2 = TPE_vec3Plus(rayPos,TPE_vec3Times(rayDir,totalD));
  2047. if (d == 0 ||
  2048. (p2.x == p.x && p2.y == p.y && p2.z == p.z))
  2049. return p2; // point not inside env but dist == 0, ideal case
  2050. TPE_Vec3 pTest = environment(p2,rayMarchMaxStep);
  2051. if (pTest.x == p2.x && pTest.y == p2.y && pTest.z == p2.z)
  2052. {
  2053. // stepped into env, will have to iterate
  2054. found = 1;
  2055. break;
  2056. }
  2057. p = p2;
  2058. p2 = pTest;
  2059. }
  2060. }
  2061. else if (insideStepSize != 0)
  2062. {
  2063. // inside ray: iterate by fixed steps
  2064. for (uint32_t i = 0; i < maxSteps; ++i)
  2065. {
  2066. totalD += insideStepSize;
  2067. p2 = TPE_vec3Plus(rayPos,TPE_vec3Times(rayDir,totalD));
  2068. TPE_Vec3 pTest = environment(p2,16);
  2069. if (p2.x != pTest.x || p2.y != pTest.y || p2.z != pTest.z)
  2070. {
  2071. found = 2;
  2072. break;
  2073. }
  2074. p = p2;
  2075. p2 = pTest;
  2076. }
  2077. }
  2078. if (found)
  2079. {
  2080. /* Here we've found two points (p, p2), each one the other side of the
  2081. env surface. Now iterate (binary search) to find the exact surface
  2082. pos. */
  2083. for (uint8_t i = 0; i < 128; ++i) // upper limit just in case
  2084. {
  2085. TPE_Vec3 middle = TPE_vec3Plus(p,p2);
  2086. middle.x /= 2;
  2087. middle.y /= 2;
  2088. middle.z /= 2;
  2089. if ((middle.x == p.x && middle.y == p.y && middle.z == p.z) ||
  2090. (middle.x == p2.x && middle.y == p2.y && middle.z == p2.z))
  2091. break; // points basically next to each other, don't continue
  2092. TPE_Vec3 pTest = environment(middle,16); // 16: just a small number
  2093. if ((found == 1) ==
  2094. (pTest.x == middle.x && pTest.y == middle.y && pTest.z == middle.z))
  2095. p2 = middle;
  2096. else
  2097. p = middle;
  2098. }
  2099. return (found == 1) ? p : p2;
  2100. }
  2101. return TPE_vec3(TPE_INFINITY,TPE_INFINITY,TPE_INFINITY);
  2102. }
  2103. TPE_Vec3 TPE_castBodyRay(TPE_Vec3 rayPos, TPE_Vec3 rayDir, int16_t excludeBody,
  2104. const TPE_World *world, int16_t *bodyIndex, int16_t *jointIndex)
  2105. {
  2106. TPE_Vec3 bestP = TPE_vec3(TPE_INFINITY,TPE_INFINITY,TPE_INFINITY);
  2107. TPE_Unit bestD = TPE_INFINITY;
  2108. if (bodyIndex != 0)
  2109. *bodyIndex = -1;
  2110. if (jointIndex != 0)
  2111. *jointIndex = -1;
  2112. TPE_vec3Normalize(&rayDir);
  2113. for (uint16_t i = 0; i < world->bodyCount; ++i)
  2114. {
  2115. TPE_Vec3 c, p;
  2116. TPE_Unit r, d;
  2117. TPE_bodyGetFastBSphere(&world->bodies[i],&c,&r);
  2118. c = TPE_vec3Minus(c,rayPos);
  2119. p = TPE_vec3ProjectNormalized(c,rayDir);
  2120. if (TPE_vec3Dot(p,rayDir) >= 0) // point is in ray's forward dir?
  2121. {
  2122. d = TPE_DISTANCE(p,c);
  2123. if (d <= r)
  2124. {
  2125. // bounding sphere hit, now check all joints:
  2126. const TPE_Joint *joint = world->bodies[i].joints;
  2127. for (uint16_t j = 0; j < world->bodies[i].jointCount; ++j)
  2128. {
  2129. c = joint->position;
  2130. c = TPE_vec3Minus(c,rayPos);
  2131. p = TPE_vec3ProjectNormalized(c,rayDir);
  2132. if (TPE_vec3Dot(p,rayDir) >= 0)
  2133. {
  2134. d = TPE_DISTANCE(p,c);
  2135. TPE_Unit js = TPE_JOINT_SIZE(*joint);
  2136. if (d <= js)
  2137. {
  2138. // joint hit, compute exact coordinates:
  2139. if (bodyIndex != 0)
  2140. *bodyIndex = i;
  2141. if (jointIndex != 0)
  2142. *jointIndex = j;
  2143. c = TPE_vec3Times(rayDir,TPE_sqrt(js * js - d * d));
  2144. // ^ offset vector to two intersections
  2145. p = TPE_vec3Plus(p,rayPos);
  2146. TPE_Vec3
  2147. i1 = TPE_vec3Plus(p,c), // intersection points
  2148. i2 = TPE_vec3Minus(p,c);
  2149. d = TPE_DISTANCE(rayPos,i1);
  2150. TPE_Unit d2 = TPE_DISTANCE(rayPos,i2);
  2151. if (d2 < d) // take the closer one
  2152. {
  2153. d = d2;
  2154. i1 = i2;
  2155. }
  2156. if (d < bestD)
  2157. {
  2158. bestD = d;
  2159. bestP = i1;
  2160. }
  2161. }
  2162. }
  2163. joint++;
  2164. }
  2165. }
  2166. }
  2167. }
  2168. return bestP;
  2169. }
  2170. void TPE_worldDeactivateAll(TPE_World *world)
  2171. {
  2172. for (uint16_t i = 0; i < world->bodyCount; ++i)
  2173. TPE_bodyDeactivate(&world->bodies[i]);
  2174. }
  2175. void TPE_worldActivateAll(TPE_World *world)
  2176. {
  2177. for (uint16_t i = 0; i < world->bodyCount; ++i)
  2178. TPE_bodyActivate(&world->bodies[i]);
  2179. }
  2180. TPE_Unit TPE_worldGetNetSpeed(const TPE_World *world)
  2181. {
  2182. TPE_Unit result = 0;
  2183. for (uint16_t i = 0; i < world->bodyCount; ++i)
  2184. result += TPE_bodyGetNetSpeed(world->bodies + i);
  2185. return result;
  2186. }
  2187. TPE_Vec3 TPE_bodyGetLinearVelocity(const TPE_Body *body)
  2188. {
  2189. TPE_Vec3 r = TPE_vec3(0,0,0);
  2190. for (uint16_t i = 0; i < body->jointCount; ++i)
  2191. {
  2192. TPE_UnitReduced *v = body->joints[i].velocity;
  2193. r = TPE_vec3Plus(r,TPE_vec3(v[0],v[1],v[2]));
  2194. }
  2195. r.x /= body->jointCount;
  2196. r.y /= body->jointCount;
  2197. r.z /= body->jointCount;
  2198. return r;
  2199. }
  2200. TPE_Unit TPE_abs(TPE_Unit x)
  2201. {
  2202. return x >= 0 ? x : (-1 * x);
  2203. }
  2204. TPE_Unit TPE_max(TPE_Unit a, TPE_Unit b)
  2205. {
  2206. return (a > b) ? a : b;
  2207. }
  2208. TPE_Unit TPE_min(TPE_Unit a, TPE_Unit b)
  2209. {
  2210. return (a < b) ? a : b;
  2211. }
  2212. TPE_Vec3 TPE_envAATriPrism(TPE_Vec3 point, TPE_Vec3 center,
  2213. const TPE_Unit sides[6], TPE_Unit depth, uint8_t direction)
  2214. {
  2215. point = TPE_vec3Minus(point,center);
  2216. if (direction == 1)
  2217. {
  2218. TPE_Unit tmp = point.z;
  2219. point.z = point.y;
  2220. point.y = tmp;
  2221. }
  2222. else if (direction == 2)
  2223. {
  2224. TPE_Unit tmp = point.z;
  2225. point.z = point.x;
  2226. point.x = tmp;
  2227. }
  2228. depth /= 2;
  2229. if (point.z > depth)
  2230. point.z = depth;
  2231. else
  2232. {
  2233. depth *= -1;
  2234. if (point.z < depth)
  2235. point.z = depth;
  2236. }
  2237. for (uint8_t i = 0; i < 6; i += 2)
  2238. {
  2239. uint8_t i2 = i < 4 ? i + 2 : 0;
  2240. TPE_Vec3 p =
  2241. TPE_envHalfPlane(point,TPE_vec3(sides[i],sides[i + 1],0),
  2242. TPE_vec3(sides[i2 + 1] - sides[i + 1],sides[i] - sides[i2],0));
  2243. if (p.x != point.x || p.y != point.y)
  2244. {
  2245. point = p;
  2246. if ( // dot product to determine which side the point is on
  2247. (sides[i2] - sides[i]) * (point.x - sides[i]) +
  2248. (sides[i2 + 1] - sides[i + 1]) * (point.y - sides[i + 1]) < 0)
  2249. {
  2250. point.x = sides[i]; point.y = sides[i + 1];
  2251. }
  2252. else if ( // same but for the other vertex
  2253. (sides[i] - sides[i2]) * (point.x - sides[i2]) +
  2254. (sides[i + 1] - sides[i2 + 1]) * (point.y - sides[i2 + 1]) < 0)
  2255. {
  2256. point.x = sides[i2]; point.y = sides[i2 + 1];
  2257. }
  2258. break;
  2259. }
  2260. }
  2261. if (direction == 1)
  2262. {
  2263. TPE_Unit tmp = point.z;
  2264. point.z = point.y;
  2265. point.y = tmp;
  2266. }
  2267. else if (direction == 2)
  2268. {
  2269. TPE_Unit tmp = point.z;
  2270. point.z = point.x;
  2271. point.x = tmp;
  2272. }
  2273. return TPE_vec3Plus(point,center);
  2274. }
  2275. TPE_Vec3 TPE_envGround(TPE_Vec3 point, TPE_Unit height)
  2276. {
  2277. if (point.y > height)
  2278. point.y = height;
  2279. return point;
  2280. }
  2281. uint32_t _TPE_hash(uint32_t n)
  2282. {
  2283. // parameters found by hash-prospector project
  2284. n = 250009959 * (n ^ (n >> 17));
  2285. n = 2626308659 * (n ^ (n >> 15));
  2286. return n ^ (n >> 16);
  2287. }
  2288. uint32_t TPE_jointHash(const TPE_Joint *joint)
  2289. {
  2290. uint32_t
  2291. r = _TPE_hash(joint->position.x);
  2292. r = _TPE_hash(r ^ joint->position.y);
  2293. r = _TPE_hash(r ^ joint->position.z);
  2294. r = _TPE_hash(r ^
  2295. (((uint32_t) joint->velocity[0]) |
  2296. (((uint32_t) joint->velocity[1]) << 16)));
  2297. r = _TPE_hash(r ^
  2298. (((uint32_t) joint->velocity[2]) |
  2299. ((uint32_t) joint->sizeDivided)));
  2300. return r;
  2301. }
  2302. uint32_t TPE_connectionHash(const TPE_Connection *connection)
  2303. {
  2304. return _TPE_hash(
  2305. ((uint32_t) connection->length) |
  2306. (((uint32_t) connection->joint1) << 16) |
  2307. (((uint32_t) connection->joint2) << 24));
  2308. }
  2309. uint32_t TPE_bodyHash(const TPE_Body *body)
  2310. {
  2311. uint32_t r = _TPE_hash(
  2312. ((uint32_t) body->jointMass) |
  2313. (((uint32_t) body->flags) << 16) |
  2314. (((uint32_t) body->deactivateCount) << 24)) ^
  2315. _TPE_hash(
  2316. ((uint32_t) body->friction) |
  2317. (((uint32_t) body->elasticity) << 16));
  2318. for (uint8_t i = 0; i < body->jointCount; ++i)
  2319. r = _TPE_hash(r ^ TPE_jointHash(&body->joints[i]));
  2320. for (uint8_t i = 0; i < body->connectionCount; ++i)
  2321. r = _TPE_hash(r ^ TPE_connectionHash(&body->connections[i]));
  2322. return r;
  2323. }
  2324. uint32_t TPE_worldHash(const TPE_World *world)
  2325. {
  2326. uint32_t r = 0;
  2327. for (uint8_t i = 0; i < world->bodyCount; ++i)
  2328. r = _TPE_hash(r ^ TPE_bodyHash(&world->bodies[i]));
  2329. return r;
  2330. }
  2331. void TPE_bodyMoveTo(TPE_Body *body, TPE_Vec3 position)
  2332. {
  2333. position = TPE_vec3Minus(position,TPE_bodyGetCenterOfMass(body));
  2334. for (uint8_t i = 0; i < body->jointCount; ++i)
  2335. body->joints[i].position = TPE_vec3Plus(body->joints[i].position,position);
  2336. }
  2337. uint8_t TPE_testClosestPointFunction(TPE_ClosestPointFunction f,
  2338. TPE_Vec3 cornerFrom, TPE_Vec3 cornerTo, uint8_t gridResolution,
  2339. TPE_UnitReduced allowedError, TPE_Vec3 *errorPoint)
  2340. {
  2341. TPE_Vec3 p;
  2342. cornerTo = TPE_vec3Minus(cornerTo,cornerFrom);
  2343. for (uint16_t z = 0; z < gridResolution; ++z)
  2344. {
  2345. p.z = cornerFrom.z + (z * cornerTo.z) / gridResolution;
  2346. for (uint16_t y = 0; y < gridResolution; ++y)
  2347. {
  2348. p.y = cornerFrom.y + (y * cornerTo.y) / gridResolution;
  2349. for (uint16_t x = 0; x < gridResolution; ++x)
  2350. {
  2351. p.x = cornerFrom.x + (x * cornerTo.x) / gridResolution;
  2352. TPE_Vec3 p2 = f(p,TPE_INFINITY);
  2353. if (p.x != p2.x || p.y != p2.y || p.z != p2.z) // only test outside
  2354. {
  2355. // 1st try to approach the closest point and see if it stays the same:
  2356. TPE_Vec3 p3 = p;
  2357. for (uint8_t i = 0; i < 3; ++i)
  2358. {
  2359. p3 =
  2360. TPE_vec3((p3.x + p2.x) / 2,(p3.y + p2.y) / 2,(p3.z + p2.z) / 2);
  2361. TPE_Vec3 p4 = f(p3,TPE_INFINITY);
  2362. if (TPE_abs(p4.x - p2.x) + TPE_abs(p4.y - p2.y)
  2363. + TPE_abs(p4.z - p2.z) > allowedError) // taxicab dist. for speed
  2364. {
  2365. if (errorPoint != 0)
  2366. *errorPoint = p;
  2367. return 0;
  2368. }
  2369. }
  2370. // now test 8 points inside the sphere of radius:
  2371. TPE_Unit d = TPE_DISTANCE(p,p2);
  2372. p3.z = p.z - d / 2;
  2373. for (uint8_t zz = 0; zz < 2; ++zz)
  2374. {
  2375. p3.y = p.y - d / 2;
  2376. for (uint8_t yy = 0; yy < 2; ++yy)
  2377. {
  2378. p3.x = p.x - d / 2;
  2379. for (uint8_t zz = 0; zz < 2; ++zz)
  2380. {
  2381. if (TPE_DISTANCE(p,f(p3,TPE_INFINITY)) + allowedError < d)
  2382. {
  2383. /* In the sphere of distance radius to the original point's
  2384. closest point we've gotten a closer point which should
  2385. never happen. */
  2386. if (errorPoint != 0)
  2387. *errorPoint = p;
  2388. return 0;
  2389. }
  2390. p3.x += d;
  2391. }
  2392. p3.y += d;
  2393. }
  2394. p3.z += d;
  2395. }
  2396. }
  2397. }
  2398. }
  2399. }
  2400. return 1;
  2401. }
  2402. TPE_Vec3 TPE_envLineSegment(TPE_Vec3 point, TPE_Vec3 a, TPE_Vec3 b)
  2403. {
  2404. point = TPE_vec3Minus(point,a);
  2405. b = TPE_vec3Minus(b,a);
  2406. point = TPE_vec3Project(point,b);
  2407. if (TPE_vec3Dot(point,b) < 0)
  2408. point = TPE_vec3(0,0,0);
  2409. else if (TPE_abs(point.x) + TPE_abs(point.y) + TPE_abs(point.z) >
  2410. TPE_abs(b.x) + TPE_abs(b.y) + TPE_abs(b.z))
  2411. point = b;
  2412. point = TPE_vec3Plus(point,a);
  2413. return point;
  2414. }
  2415. TPE_Vec3 TPE_envHeightmap(TPE_Vec3 point, TPE_Vec3 center, TPE_Unit gridSize,
  2416. TPE_Unit (*heightFunction)(int32_t x, int32_t y), TPE_Unit maxDist)
  2417. {
  2418. point = TPE_vec3Minus(point,center);
  2419. TPE_Vec3 closestP = TPE_vec3(TPE_INFINITY,TPE_INFINITY,TPE_INFINITY);
  2420. TPE_Unit closestD = TPE_INFINITY;
  2421. int16_t startSquareX = point.x / gridSize - (point.x < 0),
  2422. startSquareY = point.z / gridSize - (point.z < 0);
  2423. int16_t squareX = startSquareX,
  2424. squareY = startSquareY;
  2425. uint8_t spiralDir = 1;
  2426. uint16_t spiralStep = 1, spiralStepsLeft = 1;
  2427. TPE_Vec3 // 4 corners of the current square
  2428. bl = TPE_vec3(squareX * gridSize,heightFunction(squareX,squareY),
  2429. squareY * gridSize),
  2430. br = TPE_vec3(bl.x + gridSize,heightFunction(squareX + 1,squareY),bl.z),
  2431. tl = TPE_vec3(bl.x,heightFunction(squareX,squareY + 1),bl.z + gridSize),
  2432. tr = TPE_vec3(br.x,heightFunction(squareX + 1,squareY + 1),tl.z);
  2433. for (uint16_t i = 0; i < 1024; ++i) // while (1) should work in theory but...
  2434. {
  2435. if ((TPE_min(TPE_abs(squareX - startSquareX),
  2436. TPE_abs(squareY - startSquareY)) - 1) * gridSize
  2437. > TPE_min(maxDist,closestD))
  2438. break; // here we can no longer find the dist we're looking for => end
  2439. for (uint8_t j = 0; j < 2; ++j) // check the two triangles of the segment
  2440. {
  2441. TPE_Vec3 testP = TPE_envHalfPlane(point,j == 0 ? bl : tr,
  2442. TPE_vec3Normalized(j == 0 ?
  2443. TPE_vec3Cross(TPE_vec3Minus(tl,bl),TPE_vec3Minus(br,bl)) :
  2444. TPE_vec3Cross(TPE_vec3Minus(br,tr),TPE_vec3Minus(tl,tr))));
  2445. TPE_Unit testD = TPE_DISTANCE(testP,point);
  2446. if (testD < closestD)
  2447. {
  2448. if (j == 0 ? // point is inside the triangle?
  2449. (testP.x >= bl.x && testP.z >= bl.z &&
  2450. (testP.x - bl.x <= tl.z - testP.z)) :
  2451. (testP.x <= tr.x && testP.z <= tr.z &&
  2452. (testP.x - bl.x >= tl.z - testP.z)))
  2453. {
  2454. closestP = testP;
  2455. closestD = testD;
  2456. }
  2457. else
  2458. {
  2459. // point outside the triangle, check individual boundary sides
  2460. #define testEdge(a,b) \
  2461. testP = TPE_envLineSegment(point,a,b); testD = TPE_DISTANCE(testP,point); \
  2462. if (testD < closestD) { closestP = testP; closestD = testD; }
  2463. testEdge(j == 0 ? bl : tr,br)
  2464. testEdge(j == 0 ? bl : tr,tl)
  2465. testEdge(br,tl)
  2466. #undef testEdge
  2467. }
  2468. }
  2469. }
  2470. // now step to another square, in spiralling way:
  2471. switch (spiralDir)
  2472. {
  2473. case 0: // moving up
  2474. squareY++;
  2475. bl = tl; br = tr;
  2476. tl = TPE_vec3(bl.x,heightFunction(squareX,squareY + 1),bl.z + gridSize);
  2477. tr = TPE_vec3(br.x,heightFunction(squareX + 1,squareY + 1),bl.z
  2478. + gridSize);
  2479. break;
  2480. case 1: // moving right
  2481. squareX++;
  2482. bl = br; tl = tr;
  2483. tr = TPE_vec3(tl.x + gridSize,heightFunction(squareX + 1,squareY + 1),
  2484. tl.z);
  2485. br = TPE_vec3(bl.x + gridSize,heightFunction(squareX + 1,squareY),bl.z);
  2486. break;
  2487. case 2: // moving down
  2488. squareY--;
  2489. tl = bl; tr = br;
  2490. bl = TPE_vec3(tl.x,heightFunction(squareX,squareY),tl.z - gridSize);
  2491. br = TPE_vec3(tr.x,heightFunction(squareX + 1,squareY),tr.z - gridSize);
  2492. break;
  2493. case 3: // moving left
  2494. squareX--;
  2495. br = bl; tr = tl;
  2496. tl = TPE_vec3(tr.x - gridSize,heightFunction(squareX,squareY + 1),tr.z);
  2497. bl = TPE_vec3(br.x - gridSize,heightFunction(squareX,squareY),br.z);
  2498. break;
  2499. default: break;
  2500. }
  2501. spiralStepsLeft--;
  2502. if (spiralStepsLeft == 0)
  2503. {
  2504. spiralDir = spiralDir != 0 ? spiralDir - 1 : 3;
  2505. if (spiralDir == 3 || spiralDir == 1)
  2506. spiralStep++;
  2507. spiralStepsLeft = spiralStep;
  2508. }
  2509. }
  2510. return TPE_vec3Plus(closestP,center);
  2511. }
  2512. TPE_Vec3 TPE_envCone(TPE_Vec3 point, TPE_Vec3 center, TPE_Vec3 direction,
  2513. TPE_Unit radius)
  2514. {
  2515. point = TPE_vec3Minus(point,center);
  2516. if (TPE_vec3Dot(point,direction) <= 0)
  2517. {
  2518. // underneath the cone
  2519. direction.x *= -1;
  2520. direction.y *= -1;
  2521. direction.z *= -1;
  2522. point = TPE_envHalfPlane(point,TPE_vec3(0,0,0),direction);
  2523. TPE_Unit dist = TPE_LENGTH(point);
  2524. if (dist > radius)
  2525. {
  2526. point.x = (point.x * radius) / dist;
  2527. point.y = (point.y * radius) / dist;
  2528. point.z = (point.z * radius) / dist;
  2529. }
  2530. }
  2531. else
  2532. {
  2533. TPE_Unit height = TPE_LENGTH(direction);
  2534. TPE_Vec3 helper = TPE_vec3Project(point,direction);
  2535. TPE_Unit y = TPE_LENGTH(helper);
  2536. helper = TPE_vec3Minus(point,helper);
  2537. TPE_Unit x = TPE_LENGTH(helper);
  2538. if (x < 20)
  2539. {
  2540. // for such small distance big numeric errors occur in the other branch
  2541. if (y >= height)
  2542. point = direction;
  2543. }
  2544. else
  2545. {
  2546. TPE_Unit scaledRadius = radius - ((y * radius) / height);
  2547. if (y > height || x > scaledRadius) // outside?
  2548. {
  2549. if (x <= 0)
  2550. {
  2551. TPE_LOG("WARNING: arithmetic error in envCone (library bug)");
  2552. x = 1; // shouldn't happen but just in case, to prevent div by zero
  2553. }
  2554. helper.x = (helper.x * radius) / x;
  2555. helper.y = (helper.y * radius) / x;
  2556. helper.z = (helper.z * radius) / x;
  2557. point = TPE_envLineSegment(point,helper,direction);
  2558. }
  2559. }
  2560. }
  2561. return TPE_vec3Plus(point,center);
  2562. }
  2563. static inline uint8_t TPE_bodyIsActive(const TPE_Body *body)
  2564. {
  2565. return !(body->flags & TPE_BODY_FLAG_DEACTIVATED);
  2566. }
  2567. #endif // guard