├── .gitattributes ├── Doxyfile ├── LICENSE.txt ├── README.md ├── TODO.txt ├── old ├── README.md ├── test.c ├── test_sdl.c └── tinyphysicsengine.h ├── programs ├── 2d.c ├── car.c ├── carArenaModel.h ├── carModel.h ├── conservation.c ├── cubes.c ├── envaccel.c ├── heightmap.c ├── hello.c ├── hello2.c ├── helper.h ├── levelModel.h ├── main.c ├── make.sh ├── player.c ├── pokitto │ ├── car.bin │ ├── carModel.h │ ├── cubes.bin │ ├── main.cpp │ ├── main2.cpp │ ├── saf.h │ ├── tpe_pok.gif │ └── tpe_pok2.gif ├── shoot.c ├── small3dlib.h ├── stack.c ├── test.c ├── testGeneral.c └── water.c ├── tinyphysicsengine.d ├── tinyphysicsengine.h ├── tpe1.gif ├── tpe2.gif ├── tpe3.gif ├── tpe4.gif ├── tpe5.gif ├── tpe6.gif └── tpe7.gif /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile (a configuration for automatic docs generator Doxygen) 2 | 3 | PROJECT_NAME = "tinyphysicsengine" 4 | DOXYFILE_ENCODING = UTF-8 5 | INPUT_ENCODING = UTF-8 6 | INPUT = "tinyphysicsengine.h" 7 | PROJECT_BRIEF = "simple public domain 3D physics engine for (not only) resource-limited computers" 8 | PROJECT_LOGO = 9 | OUTPUT_DIRECTORY = ./doc 10 | CREATE_SUBDIRS = NO 11 | ALLOW_UNICODE_NAMES = NO 12 | OUTPUT_LANGUAGE = English 13 | OPTIMIZE_OUTPUT_JAVA = NO 14 | OPTIMIZE_FOR_FORTRAN = NO 15 | OPTIMIZE_OUTPUT_VHDL = NO 16 | OPTIMIZE_OUTPUT_FOR_C = YES 17 | HTML_OUTPUT = html 18 | HTML_FILE_EXTENSION = .html 19 | HTML_COLORSTYLE_HUE = 220 20 | HTML_COLORSTYLE_SAT = 100 21 | HTML_COLORSTYLE_GAMMA = 80 22 | HTML_TIMESTAMP = YES 23 | HTML_DYNAMIC_SECTIONS = NO 24 | HTML_INDEX_NUM_ENTRIES = 100 25 | GENERATE_HTML = YES 26 | GENERATE_DOCSET = NO 27 | GENERATE_LATEX = NO 28 | GENERATE_AUTOGEN_DEF = NO 29 | GENERATE_HTMLHELP = NO 30 | GENERATE_CHI = NO 31 | BRIEF_MEMBER_DESC = YES 32 | REPEAT_BRIEF = YES 33 | FULL_PATH_NAMES = YES 34 | INHERIT_DOCS = YES 35 | SEPARATE_MEMBER_PAGES = NO 36 | TAB_SIZE = 4 37 | MARKDOWN_SUPPORT = YES 38 | SUBGROUPING = YES 39 | TYPEDEF_HIDES_STRUCT = NO 40 | HIDE_UNDOC_MEMBERS = NO 41 | HIDE_UNDOC_CLASSES = NO 42 | INTERNAL_DOCS = NO 43 | CASE_SENSE_NAMES = YES 44 | HIDE_SCOPE_NAMES = NO 45 | HIDE_COMPOUND_REFERENCE = NO 46 | SHOW_INCLUDE_FILES = YES 47 | INLINE_INFO = YES 48 | SORT_MEMBER_DOCS = YES 49 | SHOW_USED_FILES = YES 50 | SHOW_FILES = YES 51 | SHOW_NAMESPACES = YES 52 | QUIET = NO 53 | WARNINGS = YES 54 | WARN_IF_UNDOCUMENTED = YES 55 | WARN_IF_DOC_ERROR = YES 56 | EXTRACT_ALL = YES 57 | EXTRACT_PACKAGE = NO 58 | EXTRACT_PRIVATE = YES 59 | EXTRACT_LOCAL_CLASSES = YES 60 | EXTRACT_LOCAL_METHODS = NO 61 | EXTRACT_STATIC = YES 62 | EXTRACT_ANON_NSPACES = NO 63 | RECURSIVE = NO 64 | EXCLUDE_SYMLINKS = NO 65 | EXAMPLE_RECURSIVE = NO 66 | FILTER_SOURCE_FILES = NO 67 | SOURCE_BROWSER = NO 68 | INLINE_SOURCES = NO 69 | STRIP_CODE_COMMENTS = YES 70 | REFERENCED_BY_RELATION = NO 71 | REFERENCES_RELATION = NO 72 | REFERENCES_LINK_SOURCE = YES 73 | SOURCE_TOOLTIPS = YES 74 | USE_HTAGS = NO 75 | VERBATIM_HEADERS = YES 76 | CLANG_ASSISTED_PARSING = NO 77 | ALPHABETICAL_INDEX = YES 78 | COLS_IN_ALPHA_INDEX = 5 79 | BINARY_TOC = NO 80 | TOC_EXPAND = NO 81 | DISABLE_INDEX = NO 82 | ENUM_VALUES_PER_LINE = 4 83 | EXT_LINKS_IN_WINDOW = NO 84 | SEARCHENGINE = YES 85 | SEARCHDATA_FILE = searchdata.xml 86 | ENABLE_PREPROCESSING = YES 87 | MACRO_EXPANSION = NO 88 | EXPAND_ONLY_PREDEF = NO 89 | SEARCH_INCLUDES = YES 90 | SKIP_FUNCTION_MACROS = NO 91 | ALLEXTERNALS = NO 92 | EXTERNAL_GROUPS = YES 93 | EXTERNAL_PAGES = YES 94 | HAVE_DOT = NO 95 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tinyphysicsengine 2 | 3 | Original: https://codeberg.org/drummyfish/tinyphysicsengine/src/branch/master 4 | 5 | Translated with: https://github.com/dkorpel/ctod 6 | 7 | # Todo: Turn this thing into a DUB project! 8 | 9 | ![](tpe1.gif)![](tpe2.gif)![](tpe3.gif)![](tpe4.gif)![](tpe5.gif)![](tpe6.gif)![](tpe7.gif) 10 | 11 | This is tinyphysicsengine (TPE), a small, completely public domain KISS/suckless, fixed point physically inaccurate pure C header only 3D physics engine (or rather a library) mostly for entertainment purposes that's supposed to run even on tiny computers such as embedded, even **bare metal**. It's written in the same style/philosophy as [small3dlib](https://codeberg.org/drummyfish/small3dlib), [raycastlib](https://codeberg.org/drummyfish/raycastlib) etc. 12 | 13 | Keep in mind the library is not version 1.0 yet, but it's already completely usable. Basically just more polishing and testing should be done. 14 | 15 | TPE is NOT a "robust framework" and it is **NOT physically accurate**; basic things follow physics equations but a lot of other things are empirical approximations, the main goal is to achieve SIMPLE behavior that LOOKS LIKE real world physics. TPE can be used to **fake** many things (even such as e.g. car physics) just as in computer graphics we fake things such as reflections because in games we simply don't really notice they're inaccurate. This approach has been chosen on purpose after trying and failing to create a traditional physically accurate engine which is archived in the *old* folder; I already had physically correct collision detections and responses of rigid bodies programmed but eventually failed on dealing with the complexity of handling imprecisions of fixed point in very low/high energy cases. At that point I started over with a completely new approach: just use soft bodies made of spheres connected with springs and fake what is possible to fake. 16 | 17 | **The basic principles in short**: TPE uses soft body physics, bodies are modelled as **spheres connected by springs** but the springs can be made stiff so that the bodies behave almost like rigid bodies, so you can simulate (*fake*) both soft and rigid physics. Environment in which bodies are placed is modelled by **distance functions**, i.e. you can in theory create any environment as long as you can create a function that for any point in space returns the closest point to the environment (functions for basic and some more complex shapes are included in TPE). 18 | 19 | **Why does this exist?** Because all other engines suck, they are either trivial or bloated, have licensing conditions, dependencies, require floating point unit, complex build systems, bad languages, PhD level math etcetc. TPE is a keep it simple engine for people who just want to add simple physics to their tiny game without being bothered by bullshit. 20 | 21 | ## features 22 | 23 | - KISS, **suckless**, pure C99, header only 24 | - **compatible with small3dlib**, easy integration (same data types, conventions, ...) 25 | - **no dependencies**, not even standard library (except for stdint header) 26 | - **no build system** 27 | - **no dynamic allocation** (malloc), not using files etc. 28 | - completely **public domain** free software, no legal worries and burdens, do whatever you want 29 | - **no floating point**, only 32 bit integer (fixed point) math 30 | - **nice performance** for smaller simulations, runs even on embedded devices such as Pokitto (32 kB RAM, 48 MHz CPU) 31 | - **discrete collision detection** with **simple acceleration** by bounding volumes that use **no precomputation** 32 | - **soft/stiff body** physics that can be used to also **fake rigid body** physics, built-in functions for constructing bodies 33 | - bodies are **spheres connected by springs** with simple attributes (mass, stiffness, elasticity, friction, ...), can be **soft or stiff** 34 | - environments are **distance functions** (can possibly be animated, i.e. dynamic, no precomputation is needed), support for any mathematically definable environment (even e.g. 3D fractals), predefined functions for: 35 | - **sphere, plane, line segment, ...** 36 | - **box** (axis-aligned and arbitrary rotation) 37 | - **cylinder** and **cone** (arbitrary rotation) 38 | - **heightmap** 39 | - trivial **unions of shapes** 40 | - axis-aligned **triangular prism** (ramp) 41 | - **simple bounding sphere/box acceleration** 42 | - functions for **rotations**, mostly in Euler angles (no quaternions) 43 | - **deterministic behavior** 44 | - **ray casting** support (both against bodies and environments) 45 | - **simple deactivation of bodies** that don't move much for a while 46 | - **debug render**, a function that renders the 3D view of the world with a provided pixel drawing function (independent of any rendering system) 47 | - **optional non-rotating bodies**, e.g. for representing the player 48 | - **faking ball rotation** (to make single joint bodies look as if they rotate even though internally they don't) 49 | - built-in **vector math**, trigonometric functions, gravity etc. 50 | - **compile time options** to tune in specific parameters (such as body deactivation time or use distance approximation for better performance) 51 | - **world hash** 52 | - may in theory also be used for 2D physics in a limited way 53 | - simple, well commented code and examples, just a simple math, you can easily make any changes if you want 54 | 55 | You can probably use this to: 56 | 57 | - make simple to mid-complex games and entertainment visualizations such as: 58 | - a first person game such as a shooter or adventure 59 | - maybe a simple racing game 60 | - marble racing? 61 | - probably even a simple 2D game 62 | - camera collisions in 3D visualizations 63 | - etc. 64 | - spark your game with effects like flying objects after explosions, waving water surface, maybe even a super simple cloth and ragdoll physics 65 | - impress girls by showing them 3D physics running on embedded devices 66 | - etc. 67 | 68 | ## limitations 69 | 70 | - generally **NOT physically correct**, for God's sake do not use this for computation of your space rocket's trajectory! 71 | - even though **performance** is good for simple simulations, it is not best possible and **won't scale** very much because the soft body model requires to make more computations and also takes more RAM (however the code is still a reasonably optimized C that should run alright) 72 | - yes, bodies sometimes vibrate and do weird things 73 | - **fixed point is not extremely precise** of course 74 | - **NOT a robust framework** that solves everything for you, more of a library with a set of tools that will help you easily create simple physics, you may need to handle many things yourself 75 | - the library **does NOT really consider correct energies** as bodies don't store information about their angular velocity etc., usually some approximation such as a sum of speed/velocities of all joints is used to e.g. determine if a body should be deactivated 76 | - **do NOT create extreme situations** such as extremely big or extremely small bodies or extreme velocities (probably also positions far away from the origin), fixed point will overflow or lose precision, this is not handled at all 77 | - **body joint and connection count should be kept low**, do not create bodies with dozens or even hundreds of joints, that will be very slow 78 | - made for a time tick length corresponding to **about 30 FPS**, hugely different ticks (like 200 or 2) lengths may behave weird or be unusable, half and double should probably be workable, you can try to apply interpolation etc. 79 | - there is **no neat UBER integration method**, only a "dirty" empirical algorithm that may fail in weird situations but is tested to behave nicely and stably in most common situations (only current joint positions are considered, connections of joints create basically just a constant acceleration after a threshold of their tension is passed, some basic friction and cancelling of forces is applied). Tricks are used to help stability, such as force-reshaping stiff bodies when their shape diverges too much from their ideal shape. 80 | - quantities such as energy and momentum are generally **NOT conserved**, they shouldn't go up but will probably go down, however you can force conservation of a quantity if you want (see the examples) 81 | - **bodies are only approximated with spheres** (so your 3D models won't collide EXACTLY at their bounds), other shapes (boxes, cylinders, ...) are usable only for the environment 82 | - **deactivation of bodies is imperfect** and may sometimes lead to e.g. a body freezing mid air, you need to handle this yourself in case it shows to be a problem for you 83 | - things such as stable contacts are not handled at all, you may encounter issues with stacking object etc., again handle this yourself (there are functions provided for smoothing our movement etc., these are usually enough to mostly eliminate shaking) 84 | - **body parameters**, mostly elasticity, may sometimes not work as expected because they only define the parameters of joints, however added connections also add elasticity etc., keep this in mind 85 | - the **environment distance functions are not signed** (no SDFs) so as to make them easier to build, i.e. inside the solid environment they don't provide any information about where to escape -- this is mostly okay but can sometimes cause trouble (a body stuck deep in environment won't get out by itself, rays casted from within a solid environment won't work accurately etc.) 86 | - the library **doesn't try to be too smart**, it is up to you to decide if you e.g. need smoothing of movement and apply it yourself (however helper functions for this are provided) 87 | - just simple discrete collision detection and resolution 88 | 89 | ## how to 90 | 91 | For a basic use see the `hello.c` example program and `hello2.c` as the next, then take a look at the more complex ones. Also see the library file itself, it is highly commented and is supposed to serve as its own documentation. 92 | 93 | Very basic sum up: TODO, coming soon :) 94 | 95 | ## usage rights 96 | 97 | tl;dr: CC0 public domain, I hate intellectual property, do whatever you want 98 | 99 | I (drummyfish) have made this project completely myself from scratch. Everything in this repository is available under CC0 1.0 (public domain, https://creativecommons.org/publicdomain/zero/1.0/) + a waiver of all other IP rights (including patents and trademarks). 100 | 101 | This project is made out of love and to be truly helpful to everyone, not for any self interest. I want it to forever stay completely in the public domain, not owned by anyone. 102 | 103 | This is not mandatory but please consider supporting free software and free culture by using free licenses and/or waivers. 104 | 105 | If you'd like to support me or just read something about me and my projects, visit my site: www.tastyfish.cz. 106 | 107 | You can also choose to use this under the following waiver which is here to just ensure more legal safety and waiving of additional IP such as patents: 108 | 109 | The intent of this waiver is to ensure that this work will never be encumbered by any exclusive intellectual property rights and will always be in the public domain world-wide, i.e. not putting any restrictions on its use. 110 | 111 | Each contributor to this work agrees that they waive any exclusive rights, including but not limited to copyright, patents, trademark, trade dress, industrial design, plant varieties and trade secrets, to any and all ideas, concepts, processes, discoveries, improvements and inventions conceived, discovered, made, designed, researched or developed by the contributor either solely or jointly with others, which relate to this work or result from this work. Should any waiver of such right be judged legally invalid or ineffective under applicable law, the contributor hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to this right. 112 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | TODO: 2 | - demo: ragdoll <--- maybe later 3 | - add/remove log with TPE_LOG so that its nice, useful and not spammy 4 | 5 | DONE: 6 | - FOR GODS SAKE clean the code 7 | - valgrind, cppcheck, read the whole code, refactor, test on embedded, ... 8 | - demos for showing basic use (without using helper.h) 9 | - expand basic tests a bit 10 | - update the player demo (new 3D environment) 11 | - bug: catapult demo doesn't work as expected with soft body velocity 12 | cancelling 13 | - bounding box/sphere test functions/macros for optimization of environment 14 | building, plus a demo that tests if it actually accelerates it 15 | - env function: heightmap 16 | - demo: heightmap 17 | - demo: angry-birds-like game 18 | - demo: car 19 | - env function: cone 20 | - zero weight joints should behave how? <-- disallowed 0 mass 21 | - try to make better car physics 22 | - demo: testing different frictions on skewed plane, also different elasticities 23 | - test env functions with a single distinct point near camera 24 | - fast and accurate distance computation as a compile time option, dist comp is 25 | prolly a bottleneck 26 | - function that tests validity of an environemnt function, i.e. if it behaves 27 | mathematically correct + test built in env functions with it 28 | - bug? bodies stuck inside each other resist falling down by gravity (stack.c) 29 | ^ Seems to be okay now? 30 | - test different tick lengths (demo) <-- seems OK 31 | - test ray casting (e.g. the hit of an outside ray should always be outside) 32 | - BUG: envBox function doesn't pass the env function test! <-- just needed a 33 | bigger epsilon 34 | - current dist approx fails e.g. with heightmaps -- maybe just use the 35 | non-approx version at all times in places where the approx fails? Also 36 | TEST THE APPROX DIST with different env functions <--- the improved version 37 | works alright, leave the rest for the decision of the library user 38 | - ray casting against bodies 39 | - world state hash? for testing determinism etc. 40 | - check if using fastBSphere vs normal BSphere doesn't affect the simulation 41 | result (it shouldn't) <-- on stacks.c gave the same world hash 42 | - fine tune the number of reshapes needed for nice behavior 43 | - elasticity doesn't really work in cases when there is an opposing joint to the 44 | colliding joint, the speeds always cancel out -- leave as is or somehow fix? 45 | ^ let's just ignore it 46 | - body being pushed (e.g. by gravity) onto a sharp edge will likely not resist 47 | (due to only linear forces in connections) and will very easily be split very 48 | wide -- TRY AND FIX! Fix could be e.g. in a special function that checks 49 | whether any connection is over some tension limit and if so inverts velocities 50 | of its joints? OR MAYBE RATHER start applying proportional (as opposed to 51 | current linear) acceleration due to tension after some tension limit. 52 | - demo: soft net (water surface?) 53 | - try this: when reshaping body, also try to subtract velocities of joints that 54 | go against each other along a connection, i.e. velocities that should cancel 55 | out... maybe this reduces shaking? 56 | - air friction (can be done by multiplying energy) 57 | - collision callbacks, ability to custom handle collision response 58 | - zero sized joints should never collide (can be useful) <- NO because 0 size 59 | can also mean just small size (due to size being stored divided) 60 | - debug draw inactive bodies with different color? 61 | - when waking a body by collision, maybe set the disable count to lower values, 62 | to handle situations in which two touching bodies wouldn't allow each other 63 | to ever get disabled 64 | - non-rotating bodies 65 | - demo: 2D physics 66 | - unify naming: rotation vs orientation 67 | - function for "faking" rotation of a single-joint body (ball): it will simply 68 | rotate by the axis perpendicular to its linear velocity vec 69 | - BUG? makeCenterBox doesnt seem to add the center joint: check it out <--- 70 | only hard to see 71 | - tip: body center can quickly be approximated by just averaging two extreme 72 | points rather than averaging all points -- write this somewhere 73 | - test/handle: collision where the velocity goes "away" from the collision (can 74 | happen e.g. if one body is hit by a faster body OR by manual manipulation with 75 | velocities) <-- seemed OK 76 | - orthographics projection for debug draw 77 | - demo: momentum conservation 78 | -------------------------------------------------------------------------------- /old/README.md: -------------------------------------------------------------------------------- 1 | WIP 2 | 3 | NOTE to self and other: This probably isn't gonna work, 32bit fixed point has 4 | showed to be too low precision for a 3D physics engine (even though it is enough 5 | for 3D rendering). The issue is that we need to represent both very high and 6 | very low energies so we can allocate about 9 bits to the fractional part of the 7 | number, however this is too little to deal with slow rotations and low energies; 8 | it happens too often that for example objects balance on their edges because 9 | their energy gets quite small and rounded to zero. The project probably needs to 10 | be rewritten from scratch with more precision (still wouldn't use floating point 11 | but rather some tiny custon fixed point library). 12 | -------------------------------------------------------------------------------- /old/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tinyphysicsengine.h" 3 | 4 | #define F TPE_FRACTIONS_PER_UNIT 5 | 6 | #define TOLERANCE 10 7 | 8 | int tolerance(TPE_Unit x, TPE_Unit expX) 9 | { 10 | return (x <= (expX + TOLERANCE)) && (x >= (expX - TOLERANCE)); 11 | } 12 | 13 | int testRotToQuat( 14 | TPE_Unit x, TPE_Unit y, TPE_Unit z, TPE_Unit angle, 15 | TPE_Unit expX, TPE_Unit expY, TPE_Unit expZ, TPE_Unit expW) 16 | { 17 | printf("testing axis + rot -> quaternion ([%d,%d,%d] %d -> %d %d %d %d): ", 18 | x,y,z,angle,expX,expY,expZ,expW); 19 | 20 | TPE_Vec4 q, axis; 21 | 22 | TPE_vec4Set(&axis,x,y,z,0); 23 | TPE_rotationToQuaternion(axis,angle,&q); 24 | 25 | if (!tolerance(q.x,expX) || 26 | !tolerance(q.y,expY) || 27 | !tolerance(q.z,expZ) || 28 | !tolerance(q.w,expW)) 29 | { 30 | printf("bad (%d %d %d %d)\n",q.x,q.y,q.z,q.w); 31 | return 0; 32 | } 33 | 34 | puts("OK"); 35 | return 1; 36 | } 37 | 38 | int ass(const char *what, int cond) 39 | { 40 | printf("testing %s: %s\n",what,cond ? "OK" : "ERROR"); 41 | return cond; 42 | } 43 | 44 | int testColl(const TPE_Body *b1, const TPE_Body *b2, 45 | TPE_Unit expRet, TPE_Unit expX, TPE_Unit expY, TPE_Unit expZ, 46 | TPE_Unit expNX, TPE_Unit expNY, TPE_Unit expNZ) 47 | { 48 | printf("testing collision detection, %d %d: ",b1->shape,b2->shape); 49 | 50 | TPE_Vec4 p, n; 51 | 52 | TPE_vec4Set(&p,0,0,0,0); 53 | TPE_vec4Set(&n,0,0,0,0); 54 | 55 | TPE_Unit ret = TPE_bodyCollides(b1,b2,&p,&n); 56 | 57 | #if 1 58 | printf("\nret: %d\n",ret); 59 | TPE_PRINTF_VEC4(p); 60 | TPE_PRINTF_VEC4(n); 61 | printf("\n"); 62 | #endif 63 | 64 | if (!tolerance(ret,expRet) || 65 | ((ret != 0) && 66 | (!tolerance(p.x,expX) || 67 | !tolerance(p.y,expY) || 68 | !tolerance(p.z,expZ) || 69 | !tolerance(n.x,expNX) || 70 | !tolerance(n.y,expNY) || 71 | !tolerance(n.z,expNZ)))) 72 | return 0; 73 | 74 | puts("OK"); 75 | 76 | return 1; 77 | } 78 | 79 | int main(void) 80 | { 81 | #define ASS(what) if (!what) { puts("ERROR"); return 0; } 82 | 83 | { 84 | ASS(ass("shape ID",TPE_COLLISION_TYPE(TPE_SHAPE_SPHERE,TPE_SHAPE_CUBOID) == TPE_COLLISION_TYPE(TPE_SHAPE_CUBOID,TPE_SHAPE_SPHERE))) 85 | 86 | ASS(testRotToQuat(F,0,0, 0, 0,0,0,F)); 87 | ASS(testRotToQuat(F,0,0, F/4, 361,0,0,361)); 88 | ASS(testRotToQuat(0,F,0, F/4, 0,361,0,361)); 89 | ASS(testRotToQuat(0,0,F, F/2, 0,0,F,0)); 90 | ASS(testRotToQuat(-F,F,F, -F/8, 112,-112,-112,472)); 91 | 92 | 93 | TPE_Vec4 p = TPE_vec4(10,200,100,0), p2, q; 94 | 95 | 96 | p2 = p; 97 | TPE_rotationToQuaternion(TPE_vec4(512,0,0,0),F/4,&q); 98 | TPE_rotatePoint(&p2,q); 99 | 100 | #define TEST_LINE_SEGMENT_CLOSE(ax,ay,az,bx,by,bz,px,py,pz,rx,ry,rz) \ 101 | ASS(ass("line segment closest",TPE_vec3Dist(TPE_lineSegmentClosestPoint(\ 102 | TPE_vec4(ax,ay,az,0),TPE_vec4(bx,by,bz,0),TPE_vec4(px,py,pz,0)),\ 103 | TPE_vec4(rx,ry,rz,0)) < 10)) 104 | 105 | TEST_LINE_SEGMENT_CLOSE(0,0,0, 100,0,0, 50,0,0, 50,0,0) 106 | TEST_LINE_SEGMENT_CLOSE(-100,-100,20, 1000,10000,20000, -3000,-5000,-1000, -100,-100,20) 107 | 108 | return 0; 109 | } 110 | 111 | { 112 | puts("collisions:"); 113 | 114 | TPE_Body sphere, cylinder; 115 | TPE_Vec4 collPoint, collNorm; 116 | 117 | TPE_bodyInit(&sphere); 118 | TPE_bodyInit(&cylinder); 119 | 120 | // sphere, sphere: 121 | 122 | sphere.shape = TPE_SHAPE_SPHERE; 123 | cylinder.shape = TPE_SHAPE_SPHERE; 124 | 125 | sphere.shapeParams[0] = TPE_FRACTIONS_PER_UNIT; 126 | cylinder.shapeParams[1] = TPE_FRACTIONS_PER_UNIT; 127 | 128 | sphere.position = TPE_vec4(F,F / 2,0,0); 129 | cylinder.position = TPE_vec4(F + F / 2,F / 2,0,0); 130 | 131 | ASS(testColl(&sphere,&cylinder,256,640,256,0,512,0,0)); 132 | ASS(testColl(&cylinder,&sphere,256,640,256,0,-512,0,0)); 133 | 134 | // sphere, cylinder: 135 | 136 | sphere.shape = TPE_SHAPE_SPHERE; 137 | cylinder.shape = TPE_SHAPE_CYLINDER; 138 | 139 | sphere.shapeParams[0] = F; 140 | cylinder.shapeParams[0] = F * 2; 141 | cylinder.shapeParams[1] = F * 3; 142 | 143 | sphere.position.y = 6 * F; 144 | ASS(testColl(&sphere,&cylinder,0,0,0,0,0,0,0)); // no collision 145 | 146 | sphere.position = TPE_vec4(F * 3 + F / 2,F,0,0); 147 | cylinder.position = TPE_vec4(F,0,0,0); 148 | ASS(testColl(&sphere,&cylinder,F / 2,3 * F,F,0,-1 * F,0,0)); // collision with cyl. body 149 | ASS(testColl(&cylinder,&sphere,F / 2,3 * F,F,0,F,0,0)); 150 | 151 | sphere.position.x = F + F / 2; 152 | sphere.position.y = 2 * F; 153 | ASS(testColl(&sphere,&cylinder,F / 2,F + F / 2,F + F / 2,0,0,-1 * F,0)); // collision with cyl. body 154 | ASS(testColl(&cylinder,&sphere,F / 2,F + F / 2,F + F / 2,0,0,F,0)); 155 | 156 | sphere.position.x = 3 * F + F / 2; 157 | sphere.position.y = 2 * F; 158 | ASS(testColl(&sphere,&cylinder,150,3 * F,F + F / 2,0,-362,-362,0)); // collision with cyl. edge 159 | 160 | 161 | 162 | } 163 | 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /old/test_sdl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define S3L_RESOLUTION_X 640 6 | #define S3L_RESOLUTION_Y 480 7 | #define S3L_PIXEL_FUNCTION drawPixel 8 | 9 | #define S3L_FLAT 0 10 | #define S3L_NEAR_CROSS_STRATEGY 1 11 | #define S3L_PERSPECTIVE_CORRECTION 2 12 | #define S3L_SORT 0 13 | #define S3L_STENCIL_BUFFER 0 14 | #define S3L_Z_BUFFER 1 15 | 16 | #define FPS 30 17 | #define MSPF (1000 / (FPS)) 18 | 19 | #include "small3dlib.h" 20 | 21 | #include "tinyphysicsengine.h" 22 | 23 | #define PIXELS_SIZE (S3L_RESOLUTION_X * S3L_RESOLUTION_Y * 4) 24 | 25 | uint8_t pixels[PIXELS_SIZE]; 26 | 27 | void draw2DPoint(int x, int y, int r, int g, int b) 28 | { 29 | if (x < 1 || x > S3L_RESOLUTION_X - 1 || 30 | y < 1 || y > S3L_RESOLUTION_Y - 1) 31 | return; 32 | 33 | uint32_t index = ((y - 1) * S3L_RESOLUTION_X + x) * 4; 34 | 35 | #define d pixels[index] = 0; pixels[index + 1] = b; pixels[index + 2] = g; pixels[index + 3] = r; 36 | 37 | d 38 | index += S3L_RESOLUTION_X * 4 - 4; 39 | d 40 | index += 4; 41 | d 42 | index += 4; 43 | d 44 | index += S3L_RESOLUTION_X * 4 - 4; 45 | d 46 | 47 | #undef d 48 | } 49 | 50 | void drawPixel(S3L_PixelInfo *p) 51 | { 52 | uint32_t index = (p->y * S3L_RESOLUTION_X + p->x) * 4; 53 | pixels[index + 1] = p->triangleIndex * 16; 54 | pixels[index + 2] = 255 - p->triangleIndex * 16; 55 | } 56 | 57 | S3L_Unit cubeVertices[] = { S3L_CUBE_VERTICES(S3L_FRACTIONS_PER_UNIT) }; 58 | S3L_Index cubeTriangles[] = { S3L_CUBE_TRIANGLES }; 59 | 60 | #define CYLINDER_VERTEX_COUNT 20 61 | const S3L_Unit cylinderVertices[CYLINDER_VERTEX_COUNT * 3] = { 62 | 0, -256, 512, // 0 63 | 0, 256, 512, // 3 64 | 300, -256, 414, // 6 65 | 300, 256, 414, // 9 66 | 486, -256, 158, // 12 67 | 486, 256, 158, // 15 68 | 486, -256, -158, // 18 69 | 486, 256, -158, // 21 70 | 300, -256, -414, // 24 71 | 300, 256, -414, // 27 72 | 0, -256, -512, // 30 73 | 0, 256, -512, // 33 74 | -300, -256, -414, // 36 75 | -300, 256, -414, // 39 76 | -486, -256, -158, // 42 77 | -486, 256, -158, // 45 78 | -486, -256, 158, // 48 79 | -486, 256, 158, // 51 80 | -300, -256, 414, // 54 81 | -300, 256, 414 // 57 82 | }; // cylinderVertices 83 | 84 | #define CYLINDER_TRIANGLE_COUNT 36 85 | const S3L_Index cylinderTriangleIndices[CYLINDER_TRIANGLE_COUNT * 3] = { 86 | 1, 2, 0, // 0 87 | 3, 4, 2, // 3 88 | 5, 6, 4, // 6 89 | 7, 8, 6, // 9 90 | 9, 10, 8, // 12 91 | 11, 12, 10, // 15 92 | 13, 14, 12, // 18 93 | 15, 16, 14, // 21 94 | 17, 7, 5, // 24 95 | 17, 18, 16, // 27 96 | 19, 0, 18, // 30 97 | 6, 14, 18, // 33 98 | 1, 3, 2, // 36 99 | 3, 5, 4, // 39 100 | 5, 7, 6, // 42 101 | 7, 9, 8, // 45 102 | 9, 11, 10, // 48 103 | 11, 13, 12, // 51 104 | 13, 15, 14, // 54 105 | 15, 17, 16, // 57 106 | 5, 3, 17, // 60 107 | 1, 19, 17, // 63 108 | 17, 15, 13, // 66 109 | 13, 11, 17, // 69 110 | 9, 7, 17, // 72 111 | 3, 1, 17, // 75 112 | 17, 11, 9, // 78 113 | 17, 19, 18, // 81 114 | 19, 1, 0, // 84 115 | 18, 0, 2, // 87 116 | 2, 4, 6, // 90 117 | 6, 8, 10, // 93 118 | 10, 12, 14, // 96 119 | 14, 16, 18, // 99 120 | 18, 2, 6, // 102 121 | 6, 10, 14 // 105 122 | }; // cylinderTriangleIndices 123 | 124 | #define SPHERE_VERTEX_COUNT 42 125 | const S3L_Unit sphereVertices[SPHERE_VERTEX_COUNT * 3] = { 126 | 0, -512, 0, // 0 127 | 370, -228, -269, // 3 128 | -141, -228, -435, // 6 129 | -457, -228, 0, // 9 130 | -141, -228, 435, // 12 131 | 370, -228, 269, // 15 132 | 141, 228, -435, // 18 133 | -370, 228, -269, // 21 134 | -370, 228, 269, // 24 135 | 141, 228, 435, // 27 136 | 457, 228, 0, // 30 137 | 0, 512, 0, // 33 138 | -83, -435, -255, // 36 139 | 217, -435, -158, // 39 140 | 134, -269, -414, // 42 141 | 435, -269, 0, // 45 142 | 217, -435, 158, // 48 143 | -269, -435, 0, // 51 144 | -352, -269, -255, // 54 145 | -83, -435, 255, // 57 146 | -352, -269, 255, // 60 147 | 134, -269, 414, // 63 148 | 486, 0, -158, // 66 149 | 486, 0, 158, // 69 150 | 0, 0, -512, // 72 151 | 300, 0, -414, // 75 152 | -486, 0, -158, // 78 153 | -300, 0, -414, // 81 154 | -300, 0, 414, // 84 155 | -486, 0, 158, // 87 156 | 300, 0, 414, // 90 157 | 0, 0, 512, // 93 158 | 352, 269, -255, // 96 159 | -134, 269, -414, // 99 160 | -435, 269, 0, // 102 161 | -134, 269, 414, // 105 162 | 352, 269, 255, // 108 163 | 83, 435, -255, // 111 164 | 269, 435, 0, // 114 165 | -217, 435, -158, // 117 166 | -217, 435, 158, // 120 167 | 83, 435, 255 // 123 168 | }; // sphereVertices 169 | 170 | #define SPHERE_TRIANGLE_COUNT 80 171 | const S3L_Index sphereTriangleIndices[SPHERE_TRIANGLE_COUNT * 3] = { 172 | 0, 13, 12, // 0 173 | 1, 13, 15, // 3 174 | 0, 12, 17, // 6 175 | 0, 17, 19, // 9 176 | 0, 19, 16, // 12 177 | 1, 15, 22, // 15 178 | 2, 14, 24, // 18 179 | 3, 18, 26, // 21 180 | 4, 20, 28, // 24 181 | 5, 21, 30, // 27 182 | 1, 22, 25, // 30 183 | 2, 24, 27, // 33 184 | 3, 26, 29, // 36 185 | 4, 28, 31, // 39 186 | 5, 30, 23, // 42 187 | 6, 32, 37, // 45 188 | 7, 33, 39, // 48 189 | 8, 34, 40, // 51 190 | 9, 35, 41, // 54 191 | 10, 36, 38, // 57 192 | 38, 41, 11, // 60 193 | 38, 36, 41, // 63 194 | 36, 9, 41, // 66 195 | 41, 40, 11, // 69 196 | 41, 35, 40, // 72 197 | 35, 8, 40, // 75 198 | 40, 39, 11, // 78 199 | 40, 34, 39, // 81 200 | 34, 7, 39, // 84 201 | 39, 37, 11, // 87 202 | 39, 33, 37, // 90 203 | 33, 6, 37, // 93 204 | 37, 38, 11, // 96 205 | 37, 32, 38, // 99 206 | 32, 10, 38, // 102 207 | 23, 36, 10, // 105 208 | 23, 30, 36, // 108 209 | 30, 9, 36, // 111 210 | 31, 35, 9, // 114 211 | 31, 28, 35, // 117 212 | 28, 8, 35, // 120 213 | 29, 34, 8, // 123 214 | 29, 26, 34, // 126 215 | 26, 7, 34, // 129 216 | 27, 33, 7, // 132 217 | 27, 24, 33, // 135 218 | 24, 6, 33, // 138 219 | 25, 32, 6, // 141 220 | 25, 22, 32, // 144 221 | 22, 10, 32, // 147 222 | 30, 31, 9, // 150 223 | 30, 21, 31, // 153 224 | 21, 4, 31, // 156 225 | 28, 29, 8, // 159 226 | 28, 20, 29, // 162 227 | 20, 3, 29, // 165 228 | 26, 27, 7, // 168 229 | 26, 18, 27, // 171 230 | 18, 2, 27, // 174 231 | 24, 25, 6, // 177 232 | 24, 14, 25, // 180 233 | 14, 1, 25, // 183 234 | 22, 23, 10, // 186 235 | 22, 15, 23, // 189 236 | 15, 5, 23, // 192 237 | 16, 21, 5, // 195 238 | 16, 19, 21, // 198 239 | 19, 4, 21, // 201 240 | 19, 20, 4, // 204 241 | 19, 17, 20, // 207 242 | 17, 3, 20, // 210 243 | 17, 18, 3, // 213 244 | 17, 12, 18, // 216 245 | 12, 2, 18, // 219 246 | 15, 16, 5, // 222 247 | 15, 13, 16, // 225 248 | 13, 0, 16, // 228 249 | 12, 14, 2, // 231 250 | 12, 13, 14, // 234 251 | 13, 1, 14 // 237 252 | }; // sphereTriangleIndices 253 | 254 | typedef struct 255 | { 256 | S3L_Mat4 matrix; 257 | S3L_Mat4 scaleMatrix; 258 | } BodyModelExtra; 259 | 260 | int bodyCount = 0; 261 | 262 | S3L_Scene scene; 263 | S3L_Model3D models[1024]; 264 | 265 | TPE_World world; 266 | TPE_Body bodies[1024]; 267 | 268 | BodyModelExtra extra[1024]; 269 | 270 | void addBody(uint8_t shape, TPE_Unit mass, TPE_Unit param1, TPE_Unit param2, TPE_Unit param3) 271 | { 272 | BodyModelExtra e; 273 | TPE_Body b; 274 | S3L_Model3D m; 275 | 276 | TPE_bodyInit(&b); 277 | 278 | b.shape = shape; 279 | b.mass = mass; 280 | b.shapeParams[0] = param1; 281 | b.shapeParams[1] = param2; 282 | b.shapeParams[2] = param3; 283 | 284 | TPE_bodyRecomputeBounds(&b); 285 | 286 | const S3L_Unit *v; 287 | const S3L_Index *t; 288 | 289 | S3L_Index vc, tc; 290 | S3L_Unit sx = S3L_FRACTIONS_PER_UNIT, sy = S3L_FRACTIONS_PER_UNIT, sz = S3L_FRACTIONS_PER_UNIT; 291 | 292 | switch (shape) 293 | { 294 | case TPE_SHAPE_CYLINDER: 295 | case TPE_SHAPE_CAPSULE: 296 | v = cylinderVertices; 297 | t = cylinderTriangleIndices; 298 | vc = CYLINDER_VERTEX_COUNT; 299 | tc = CYLINDER_TRIANGLE_COUNT; 300 | sx = param1; sy = param2; sz = param1; 301 | break; 302 | 303 | case TPE_SHAPE_SPHERE: 304 | default: 305 | v = sphereVertices; 306 | t = sphereTriangleIndices; 307 | vc = SPHERE_VERTEX_COUNT; 308 | tc = SPHERE_TRIANGLE_COUNT; 309 | sx = param1; sy = param1; sz = param1; 310 | break; 311 | 312 | case TPE_SHAPE_CUBOID: 313 | v = cubeVertices; 314 | t = cubeTriangles; 315 | vc = S3L_CUBE_VERTEX_COUNT; 316 | tc = S3L_CUBE_TRIANGLE_COUNT; 317 | sx = param1; sy = param2; sz = param3; 318 | break; 319 | } 320 | 321 | S3L_initModel3D(v,vc,t,tc,&m); 322 | S3L_makeScaleMatrix(sx,sy,sz,e.scaleMatrix); 323 | 324 | S3L_initMat4(e.matrix); 325 | extra[bodyCount] = e; 326 | 327 | m.customTransformMatrix = &(extra[bodyCount].matrix); 328 | 329 | bodies[bodyCount] = b; 330 | models[bodyCount] = m; 331 | 332 | scene.modelCount++; 333 | world.bodyCount++; 334 | 335 | bodyCount++; 336 | } 337 | 338 | void updateBodies() 339 | { 340 | for (int i = 0; i < bodyCount; ++i) 341 | { 342 | BodyModelExtra *e = &(extra[i]); 343 | 344 | S3L_Mat4 m; 345 | 346 | TPE_bodyGetTransformMatrix(&(bodies[i]),m); 347 | S3L_copyMat4(e->scaleMatrix,e->matrix); 348 | S3L_mat4Xmat4(e->matrix,m); 349 | } 350 | } 351 | 352 | int main() 353 | { 354 | SDL_Window *window = SDL_CreateWindow("test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, S3L_RESOLUTION_X, S3L_RESOLUTION_Y, SDL_WINDOW_SHOWN); 355 | SDL_Renderer *renderer = SDL_CreateRenderer(window,-1,0); 356 | SDL_Texture *textureSDL = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STATIC, S3L_RESOLUTION_X, S3L_RESOLUTION_Y); 357 | SDL_Surface *screenSurface = SDL_GetWindowSurface(window); 358 | SDL_Event event; 359 | 360 | int running = 1; 361 | 362 | S3L_initScene(models,0,&scene); 363 | TPE_worldInit(&world); 364 | world.bodies = bodies; 365 | 366 | TPE_Unit frame = 0; 367 | 368 | //------- 369 | 370 | scene.camera.transform.translation.z = -8 * S3L_FRACTIONS_PER_UNIT; 371 | scene.camera.transform.translation.y = 2 * S3L_FRACTIONS_PER_UNIT; 372 | 373 | addBody(TPE_SHAPE_CUBOID,4024,1000,2000,3000); 374 | addBody(TPE_SHAPE_CUBOID,TPE_INFINITY,5000,1000,5000); 375 | addBody(TPE_SHAPE_CUBOID,4024,1000,1000,1500); 376 | 377 | bodies[0].position = TPE_vec4(0,3000,0,0); 378 | bodies[1].position = TPE_vec4(0,-1000,0,0); 379 | bodies[0].velocity = TPE_vec4(0,0,0,0); 380 | bodies[2].position.x = 2000; 381 | bodies[2].position.y = 2000; 382 | 383 | TPE_Vec4 qqq; 384 | TPE_rotationToQuaternion(TPE_vec4(0,100,255,0),50,&qqq); 385 | 386 | TPE_bodySetOrientation(&(bodies[0]),qqq); 387 | 388 | int collided = 0; 389 | int time; 390 | 391 | while (running) 392 | { 393 | time = SDL_GetTicks(); 394 | 395 | // TPE_worldApplyGravityCenter(&world,TPE_vec4(0,0,0,0),4); 396 | TPE_worldApplyGravityDown(&world,4); 397 | 398 | for (uint32_t i = 0; i < PIXELS_SIZE; ++i) 399 | pixels[i] = 0; 400 | 401 | S3L_newFrame(); 402 | 403 | TPE_worldStepBodies(&world); 404 | 405 | updateBodies(); 406 | 407 | S3L_drawScene(scene); 408 | 409 | TPE_Vec4 p, n; 410 | 411 | TPE_worldResolveCollisionNaive(&world); 412 | 413 | SDL_UpdateTexture(textureSDL,NULL,pixels,S3L_RESOLUTION_X * sizeof(uint32_t)); 414 | 415 | while (SDL_PollEvent(&event)) 416 | { 417 | if (event.type == SDL_QUIT) 418 | running = 0; 419 | else if (event.type == SDL_KEYDOWN) 420 | { 421 | if (event.key.keysym.scancode == SDL_SCANCODE_Q || event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) 422 | running = 0; 423 | } 424 | } 425 | 426 | const uint8_t *state = SDL_GetKeyboardState(NULL); 427 | 428 | S3L_Vec4 camF, camR; 429 | 430 | #define SHIFT_STEP 50 431 | #define ROT_STEP 5 432 | 433 | S3L_rotationToDirections(scene.camera.transform.rotation,SHIFT_STEP,&camF,&camR,0); 434 | 435 | if (state[SDL_SCANCODE_LSHIFT]) 436 | { 437 | if (state[SDL_SCANCODE_UP]) 438 | S3L_vec3Add(&scene.camera.transform.translation,camF); 439 | else if (state[SDL_SCANCODE_DOWN]) 440 | S3L_vec3Sub(&scene.camera.transform.translation,camF); 441 | else if (state[SDL_SCANCODE_LEFT]) 442 | S3L_vec3Sub(&scene.camera.transform.translation,camR); 443 | else if (state[SDL_SCANCODE_RIGHT]) 444 | S3L_vec3Add(&scene.camera.transform.translation,camR); 445 | } 446 | else 447 | { 448 | if (state[SDL_SCANCODE_UP]) 449 | scene.camera.transform.rotation.x += ROT_STEP; 450 | else if (state[SDL_SCANCODE_DOWN]) 451 | scene.camera.transform.rotation.x -= ROT_STEP; 452 | else if (state[SDL_SCANCODE_LEFT]) 453 | scene.camera.transform.rotation.y += ROT_STEP; 454 | else if (state[SDL_SCANCODE_RIGHT]) 455 | scene.camera.transform.rotation.y -= ROT_STEP; 456 | } 457 | 458 | #define SHIFT_STEP 50 459 | 460 | if (state[SDL_SCANCODE_L]) 461 | bodies[1].position.x += SHIFT_STEP; 462 | else if (state[SDL_SCANCODE_J]) 463 | bodies[1].position.x -= SHIFT_STEP; 464 | else if (state[SDL_SCANCODE_I]) 465 | bodies[1].position.z += SHIFT_STEP; 466 | else if (state[SDL_SCANCODE_K]) 467 | bodies[1].position.z -= SHIFT_STEP; 468 | else if (state[SDL_SCANCODE_N]) 469 | bodies[1].position.y += SHIFT_STEP; 470 | else if (state[SDL_SCANCODE_M]) 471 | bodies[1].position.y -= SHIFT_STEP; 472 | 473 | if (state[SDL_SCANCODE_P]) 474 | scene.camera.transform.translation.y += SHIFT_STEP; 475 | else if (state[SDL_SCANCODE_O]) 476 | scene.camera.transform.translation.y -= SHIFT_STEP; 477 | 478 | #undef SHIFT_STEP 479 | 480 | SDL_RenderClear(renderer); 481 | SDL_RenderCopy(renderer,textureSDL,NULL,NULL); 482 | SDL_RenderPresent(renderer); 483 | 484 | time = time + MSPF - SDL_GetTicks(); 485 | 486 | if (time > 1) 487 | usleep(time * 1000); 488 | 489 | frame++; 490 | } 491 | 492 | return 0; 493 | } 494 | 495 | -------------------------------------------------------------------------------- /programs/2d.c: -------------------------------------------------------------------------------- 1 | /** Demo showing how 2D physics can be implemented. */ 2 | 3 | #define DEBUG_DRAW_DIVIDE 8 4 | 5 | #include "helper.h" 6 | 7 | #define ROOM_W (TPE_F * 10) 8 | #define ROOM_H ((RES_Y * ROOM_W) / RES_X) 9 | 10 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 11 | { 12 | return TPE_envAABoxInside(p,TPE_vec3(0,0,0),TPE_vec3(ROOM_W,ROOM_H,ROOM_W)); 13 | } 14 | 15 | int inactiveCount = 0; 16 | 17 | int main(void) 18 | { 19 | helper_init(); 20 | 21 | tpe_world.environmentFunction = environmentDistance; 22 | 23 | s3l_scene.camera.transform.translation.z -= ROOM_W / 2; 24 | 25 | s3l_scene.camera.focalLength = 0; // set orthographic projection 26 | 27 | for (int i = 0; i < 4; ++i) // add bodies 28 | { 29 | if (i != 2) 30 | { 31 | helper_addCenterRectFull(TPE_F,TPE_F,TPE_F / 5,TPE_F / 5); 32 | TPE_bodyRotateByAxis(&helper_lastBody,TPE_vec3(TPE_F / 4,0,0)); 33 | helper_lastBody.joints[4].sizeDivided *= 3; // make center point bigger 34 | } 35 | else 36 | helper_addBall(6 * TPE_F / 5,TPE_F / 5); 37 | 38 | helper_lastBody.friction = 4 * TPE_F / 5; 39 | helper_lastBody.elasticity = TPE_F / 5; 40 | 41 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(-2 * TPE_F + i * 2 * TPE_F,0,0)); 42 | } 43 | 44 | while (helper_running) 45 | { 46 | helper_frameStart(); 47 | 48 | #define ACCELERATION (TPE_F / 25) 49 | if (sdl_keyboard[SDL_SCANCODE_LEFT]) 50 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(-1 * ACCELERATION,0,0)); 51 | else if (sdl_keyboard[SDL_SCANCODE_RIGHT]) 52 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(ACCELERATION,0,0)); 53 | 54 | if (sdl_keyboard[SDL_SCANCODE_UP]) 55 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(0,ACCELERATION,0)); 56 | #undef ACCELERATION 57 | 58 | TPE_worldStep(&tpe_world); 59 | 60 | for (int i = 0; i < tpe_world.bodyCount; ++i) 61 | TPE_bodyApplyGravity(&tpe_world.bodies[i],TPE_F / 100); 62 | 63 | /* Here we implement our own improvement of deactivation; after some time of 64 | all bodies having low speed we disable them all at once. */ 65 | TPE_Unit speed, speedMax = 0; 66 | int anyActive = 0; 67 | 68 | for (int i = 0; i < tpe_world.bodyCount; ++i) 69 | { 70 | // as we're in 2D we'll keep all joint Z positions and velocities at 0 71 | for (int j = 0; j < tpe_world.bodies[i].jointCount; ++j) 72 | { 73 | tpe_world.bodies[i].joints[j].position.z = 0; 74 | tpe_world.bodies[i].joints[j].velocity[2] = 0; 75 | } 76 | 77 | if (!(tpe_world.bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED)) 78 | anyActive = 1; 79 | 80 | speed = TPE_bodyGetAverageSpeed(&tpe_world.bodies[i]); 81 | 82 | if (speed > speedMax) 83 | speedMax = speed; 84 | } 85 | 86 | if (anyActive && speedMax < TPE_F / 10) 87 | inactiveCount++; 88 | else 89 | inactiveCount = 0; 90 | 91 | if (inactiveCount > 100) 92 | { 93 | TPE_worldDeactivateAll(&tpe_world); 94 | inactiveCount = 0; 95 | } 96 | 97 | helper_debugDraw(0); 98 | 99 | helper_frameEnd(); 100 | } 101 | 102 | helper_end(); 103 | 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /programs/car.c: -------------------------------------------------------------------------------- 1 | #define S3L_NEAR_CROSS_STRATEGY 2 2 | #define S3L_PERSPECTIVE_CORRECTION 2 3 | 4 | #include "helper.h" 5 | #include "carArenaModel.h" 6 | #include "carModel.h" 7 | 8 | #define ACCELERATION (TPE_F / 14) 9 | #define TURN_RATE (3 * TPE_F / 4) 10 | #define TURN_FRICTION (3 * TPE_F / 4) // wheel side friction 11 | #define FORW_FRICTION (TPE_F / 14) // wheel forward friction 12 | 13 | TPE_Unit rampPoits[6] = { 0,0, -2400,1400, -2400,0 }; 14 | 15 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 16 | { 17 | TPE_ENV_START( TPE_envGround(p,0),p ) 18 | TPE_ENV_NEXT( TPE_envSphereInside(p,TPE_vec3(0,10000,0),20000),p ) 19 | TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(-8700,100,-800),TPE_vec3(2200,1000,800)),p ) 20 | TPE_ENV_NEXT( TPE_envAATriPrism(p,TPE_vec3(8700,0,0),rampPoits,5000,2),p ) 21 | TPE_ENV_NEXT( TPE_envSphere(p,TPE_vec3(0,-200,0),1700),p ) 22 | TPE_ENV_END 23 | } 24 | 25 | uint8_t steering = 0; // 0: none, 1: right, 2: left 26 | uint8_t jointCollisions; /* bit mask of colliding joints (i.e. wheels that are 27 | currently touching the ground). */ 28 | uint8_t jointCollisionsPrev; // for averaging 29 | 30 | TPE_Body *carBody; 31 | 32 | TPE_Vec3 carForw, carSide, carUp, carPos, carRot; 33 | 34 | uint8_t collisionCallback(uint16_t b1, uint16_t j1, uint16_t b2, uint16_t j2, 35 | TPE_Vec3 p) 36 | { 37 | // here we record which wheels (joints) are currently touching the ground 38 | 39 | if (b1 == 1 && b1 == b2 && j1 < 4) 40 | jointCollisions |= 0x01 << j1; 41 | 42 | return 1; 43 | } 44 | 45 | int main(void) 46 | { 47 | arenaModelInit(); 48 | carModelInit(); 49 | 50 | helper_init(); 51 | 52 | carPos = TPE_vec3(0,0,0); 53 | 54 | tpe_world.environmentFunction = environmentDistance; 55 | tpe_world.collisionCallback = collisionCallback; 56 | 57 | // add an interactive body: 58 | 59 | helper_addRect(6 * TPE_F / 5,6 * TPE_F / 5,6 * TPE_F / 5,TPE_F / 2); 60 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(2000,1000,3000)); 61 | helper_lastBody.friction = TPE_F / 5; 62 | 63 | // create the car body, start with a "center rect": 64 | 65 | helper_addCenterRectFull(1000,1800,400,2000); 66 | 67 | carBody = &helper_lastBody; 68 | 69 | // scale and shift the middle joint a bit 70 | carBody->joints[4].position.y += 600; 71 | carBody->joints[4].sizeDivided *= 3; 72 | carBody->joints[4].sizeDivided /= 2; 73 | 74 | // we need to reinit the body so that it recomputes its connection lengths 75 | TPE_bodyInit(carBody,carBody->joints,carBody->jointCount,carBody->connections, 76 | carBody->connectionCount,TPE_F / 2); 77 | 78 | TPE_bodyMoveBy(carBody,TPE_vec3(6 * TPE_F,2 * TPE_F,0)); 79 | carBody->elasticity = TPE_F / 100; 80 | carBody->friction = FORW_FRICTION; 81 | carBody->flags |= TPE_BODY_FLAG_ALWAYS_ACTIVE; 82 | 83 | s3l_scene.camera.transform.rotation.x = -35; 84 | 85 | while (helper_running) 86 | { 87 | helper_frameStart(); 88 | 89 | jointCollisions = 0; 90 | 91 | if (sdl_keyboard[SDL_SCANCODE_RIGHT]) 92 | steering = 1; 93 | else if (sdl_keyboard[SDL_SCANCODE_LEFT]) 94 | steering = 2; 95 | else 96 | steering = 0; 97 | 98 | TPE_worldStep(&tpe_world); 99 | 100 | // get and smooth the car position: 101 | 102 | carPos = TPE_vec3KeepWithinBox(carPos,carBody->joints[4].position, 103 | TPE_vec3(TPE_F / 50,TPE_F / 50,TPE_F / 50)); 104 | 105 | // compute the car direction vectors from positions of its joints: 106 | 107 | carForw = TPE_vec3Normalized(TPE_vec3Plus( 108 | TPE_vec3Minus(carBody->joints[2].position,carBody->joints[0].position), 109 | TPE_vec3Minus(carBody->joints[3].position,carBody->joints[1].position))); 110 | 111 | carSide = TPE_vec3Normalized(TPE_vec3Plus( 112 | TPE_vec3Minus(carBody->joints[1].position,carBody->joints[0].position), 113 | TPE_vec3Minus(carBody->joints[3].position,carBody->joints[2].position))); 114 | 115 | carUp = TPE_vec3Cross(carForw,carSide); 116 | 117 | /* now we'll check each joint separately and if it is touching the ground, 118 | we apply directional friction (friction that's dependent on the direction 119 | of the wheel which e.g. allows turning); TPE has only non-directional 120 | friction programmed in so we do it ourselves */ 121 | for (int i = 0; i < 4; ++i) 122 | if (jointCollisions & (0x01 << i)) // wheel touches the ground? 123 | { 124 | TPE_Vec3 jv = TPE_vec3( // joint velocity 125 | carBody->joints[i].velocity[0], 126 | carBody->joints[i].velocity[1], 127 | carBody->joints[i].velocity[2]); 128 | 129 | TPE_Vec3 ja = carSide; // wheel axis of rotation 130 | 131 | if (i >= 2 && steering) 132 | { 133 | // for front wheels with turning we tilt the wheel axis 45 degrees 134 | 135 | if (steering == 2) 136 | ja = TPE_vec3Plus(TPE_vec3Times(carForw,TURN_RATE),carSide); 137 | else 138 | ja = TPE_vec3Minus(TPE_vec3Times(carForw,TURN_RATE),carSide); 139 | 140 | ja = TPE_vec3Normalized(ja); 141 | } 142 | 143 | /* friction is in the direction if the axis and its magnitude is 144 | determined by the dot product (angle) of the axis and velocity */ 145 | TPE_Vec3 fric = TPE_vec3Times(ja,(TPE_vec3Dot(ja,jv) * TURN_FRICTION) 146 | / TPE_F); 147 | 148 | jv = TPE_vec3Minus(jv,fric); // subtract the friction 149 | 150 | carBody->joints[i].velocity[0] = jv.x; 151 | carBody->joints[i].velocity[1] = jv.y; 152 | carBody->joints[i].velocity[2] = jv.z; 153 | } 154 | 155 | if (TPE_vec3Dot(carUp,TPE_vec3Minus(carBody->joints[4].position, 156 | carBody->joints[0].position)) < 0) 157 | { 158 | /* if the car falls on its roof the center joint may flip to the other 159 | side, here we fix it */ 160 | 161 | puts("car geometry flipped over, fixing..."); 162 | 163 | carBody->joints[4].position = TPE_vec3Plus(TPE_vec3Times(carUp,300), 164 | carBody->joints[0].position); 165 | } 166 | 167 | for (int i = 0; i < tpe_world.bodyCount; ++i) 168 | TPE_bodyApplyGravity(&tpe_world.bodies[i],5); 169 | 170 | if ((jointCollisions | jointCollisionsPrev) & 0x03) // back wheels on ground? 171 | { 172 | if (sdl_keyboard[SDL_SCANCODE_UP]) 173 | { 174 | carBody->joints[0].velocity[0] += (carForw.x * ACCELERATION) / TPE_F; 175 | carBody->joints[0].velocity[1] += (carForw.y * ACCELERATION) / TPE_F; 176 | carBody->joints[0].velocity[2] += (carForw.z * ACCELERATION) / TPE_F; 177 | carBody->joints[1].velocity[0] += (carForw.x * ACCELERATION) / TPE_F; 178 | carBody->joints[1].velocity[1] += (carForw.y * ACCELERATION) / TPE_F; 179 | carBody->joints[1].velocity[2] += (carForw.z * ACCELERATION) / TPE_F; 180 | } 181 | else if (sdl_keyboard[SDL_SCANCODE_DOWN]) 182 | { 183 | carBody->joints[0].velocity[0] -= (carForw.x * ACCELERATION) / TPE_F; 184 | carBody->joints[0].velocity[1] -= (carForw.y * ACCELERATION) / TPE_F; 185 | carBody->joints[0].velocity[2] -= (carForw.z * ACCELERATION) / TPE_F; 186 | carBody->joints[1].velocity[0] -= (carForw.x * ACCELERATION) / TPE_F; 187 | carBody->joints[1].velocity[1] -= (carForw.y * ACCELERATION) / TPE_F; 188 | carBody->joints[1].velocity[2] -= (carForw.z * ACCELERATION) / TPE_F; 189 | } 190 | } 191 | 192 | jointCollisionsPrev = jointCollisions; 193 | 194 | carRot = TPE_bodyGetRotation(carBody,0,2,1); 195 | 196 | // draw: 197 | 198 | helper_set3DColor(20,150,150); 199 | 200 | helper_drawModel(&arenaModel,TPE_vec3(0,0,0),TPE_vec3(512 * 32,512 * 32,512 * 32), 201 | TPE_vec3(0,0,0)); 202 | 203 | helper_set3DColor(20,50,250); 204 | 205 | helper_draw3DBox(TPE_bodyGetCenterOfMass(&tpe_world.bodies[0]), 206 | TPE_vec3(1000,800,1000),TPE_bodyGetRotation(&tpe_world.bodies[0],0,2,1)); 207 | 208 | S3L_zBufferClear(); 209 | 210 | helper_set3DColor(200,200,200); 211 | 212 | helper_drawModel(&carModel,TPE_vec3Minus(carPos,TPE_vec3Times(carUp,400)), 213 | TPE_vec3(600,600,600),carRot); 214 | 215 | if (helper_debugDrawOn) 216 | helper_debugDraw(1); 217 | 218 | // handle camera: 219 | 220 | s3l_scene.camera.transform.translation.y = carPos.y + 800; 221 | 222 | TPE_Vec3 cPos = TPE_vec3KeepWithinDistanceBand( 223 | TPE_vec3( 224 | s3l_scene.camera.transform.translation.x, 225 | s3l_scene.camera.transform.translation.y, 226 | s3l_scene.camera.transform.translation.z 227 | ),carBody->joints[4].position,4 * TPE_F,6 * TPE_F); 228 | 229 | s3l_scene.camera.transform.translation.x = cPos.x; 230 | s3l_scene.camera.transform.translation.y = cPos.y; 231 | s3l_scene.camera.transform.translation.z = cPos.z; 232 | 233 | S3L_Vec4 toCar; 234 | 235 | toCar.x = carPos.x - s3l_scene.camera.transform.translation.x; 236 | toCar.y = carPos.y - s3l_scene.camera.transform.translation.y; 237 | toCar.z = carPos.z - s3l_scene.camera.transform.translation.z; 238 | toCar.w = 0; 239 | 240 | TPE_Unit angleDiff = s3l_scene.camera.transform.rotation.y - 241 | (TPE_vec2Angle(toCar.x,toCar.z) - 128); 242 | 243 | s3l_scene.camera.transform.rotation.y -= 244 | (angleDiff < 100 && angleDiff > -100) ? angleDiff / 2 : angleDiff; 245 | 246 | helper_frameEnd(); 247 | } 248 | 249 | helper_end(); 250 | 251 | return 0; 252 | } 253 | -------------------------------------------------------------------------------- /programs/carArenaModel.h: -------------------------------------------------------------------------------- 1 | #define ARENA_VERTEX_COUNT 208 2 | const S3L_Unit arenaVertices[ARENA_VERTEX_COUNT * 3] = { 3 | 195, 0, -473, // 0 4 | 69, 0, -347, // 3 5 | 99, 0, -502, // 6 6 | -294, 0, 196, // 9 7 | -361, 0, 361, // 12 8 | -424, 0, 283, // 15 9 | 0, 0, 354, // 18 10 | 99, 0, 501, // 21 11 | 0, 0, 512, // 24 12 | 196, 0, -294, // 27 13 | 284, 0, -425, // 30 14 | -327, 0, 135, // 33 15 | -473, 0, 195, // 36 16 | 250, 0, -250, // 39 17 | 362, 0, -362, // 42 18 | -347, 0, 69, // 45 19 | -502, 0, 99, // 48 20 | 425, 0, -284, // 51 21 | -510, 0, 0, // 54 22 | 510, 0, 0, // 57 23 | 354, 0, -71, // 60 24 | 501, 0, -99, // 63 25 | -327, 0, -135, // 66 26 | -200, 0, -52, // 69 27 | -354, 0, -52, // 72 28 | -354, 34, -52, // 75 29 | 354, 0, 0, // 78 30 | 354, 42, -71, // 81 31 | -294, 0, -196, // 84 32 | -473, 0, -195, // 87 33 | -425, 0, -284, // 90 34 | 501, 0, 99, // 93 35 | -250, 0, -250, // 96 36 | -362, 0, -362, // 99 37 | 472, 0, 195, // 102 38 | 347, 0, 69, // 105 39 | -196, 0, -294, // 108 40 | -284, 0, -425, // 111 41 | 424, 0, 283, // 114 42 | 327, 0, 135, // 117 43 | -135, 0, -327, // 120 44 | -195, 0, -473, // 123 45 | -99, 0, 501, // 126 46 | 361, 0, 361, // 129 47 | 294, 0, 196, // 132 48 | -69, 0, -347, // 135 49 | -99, 0, -502, // 138 50 | -135, 0, 327, // 141 51 | -195, 0, 473, // 144 52 | 283, 0, 424, // 147 53 | 250, 0, 250, // 150 54 | 0, 0, -510, // 153 55 | -283, 0, 424, // 156 56 | 195, 0, 472, // 159 57 | 196, 0, 294, // 162 58 | -250, 0, 250, // 165 59 | 135, 0, 327, // 168 60 | 167, 0, 111, // 171 61 | -111, 0, 167, // 174 62 | -196, 0, 294, // 177 63 | 0, 0, 200, // 180 64 | 0, 2, 73, // 183 65 | 76, 0, 185, // 186 66 | -185, 0, 76, // 189 67 | -52, 2, 52, // 192 68 | -69, 0, 347, // 195 69 | -76, 0, 185, // 198 70 | -142, 0, -142, // 201 71 | -52, 2, -52, // 204 72 | -185, 0, -76, // 207 73 | 69, 0, 347, // 210 74 | 0, 0, -200, // 213 75 | 0, 2, -73, // 216 76 | -76, 0, -185, // 219 77 | 52, 2, -52, // 222 78 | 76, 0, -185, // 225 79 | 142, 0, -142, // 228 80 | 294, 0, -196, // 231 81 | 473, 0, -195, // 234 82 | -354, 0, 0, // 237 83 | -200, 34, 0, // 240 84 | -200, 0, 0, // 243 85 | 200, 0, -71, // 246 86 | 167, 0, -111, // 249 87 | 185, 0, 76, // 252 88 | 200, 0, 0, // 255 89 | 73, 2, 0, // 258 90 | 200, 42, -71, // 261 91 | 117, 109, 589, // 264 92 | 0, 109, 600, // 267 93 | 130, 234, 654, // 270 94 | 0, 370, 708, // 273 95 | 0, 234, 667, // 276 96 | 255, 234, 616, // 279 97 | 229, 109, 555, // 282 98 | 138, 370, 695, // 285 99 | 370, 234, 555, // 288 100 | 271, 370, 654, // 291 101 | 333, 109, 499, // 294 102 | 472, 234, 472, // 297 103 | 393, 370, 589, // 300 104 | 555, 234, 370, // 303 105 | 501, 370, 501, // 306 106 | 424, 109, 424, // 309 107 | 499, 109, 333, // 312 108 | 616, 234, 255, // 315 109 | 589, 370, 393, // 318 110 | 654, 234, 130, // 321 111 | 654, 370, 271, // 324 112 | 555, 109, 229, // 327 113 | 667, 234, 0, // 330 114 | 695, 370, 138, // 333 115 | 589, 109, 117, // 336 116 | 600, 109, 0, // 339 117 | 654, 234, -130, // 342 118 | 708, 370, 0, // 345 119 | 616, 234, -255, // 348 120 | 695, 370, -138, // 351 121 | 589, 109, -117, // 354 122 | 555, 234, -370, // 357 123 | 654, 370, -271, // 360 124 | 555, 109, -229, // 363 125 | 472, 234, -472, // 366 126 | 589, 370, -393, // 369 127 | 499, 109, -333, // 372 128 | 424, 109, -424, // 375 129 | 370, 234, -555, // 378 130 | 393, 370, -589, // 381 131 | 501, 370, -501, // 384 132 | 255, 234, -616, // 387 133 | 333, 109, -499, // 390 134 | 229, 109, -555, // 393 135 | 130, 234, -654, // 396 136 | 271, 370, -654, // 399 137 | 0, 234, -667, // 402 138 | 138, 370, -695, // 405 139 | 117, 109, -589, // 408 140 | 0, 109, -600, // 411 141 | -130, 234, -654, // 414 142 | 0, 370, -708, // 417 143 | -255, 234, -616, // 420 144 | -138, 370, -695, // 423 145 | -117, 109, -589, // 426 146 | -370, 234, -555, // 429 147 | -271, 370, -654, // 432 148 | -229, 109, -555, // 435 149 | -472, 234, -472, // 438 150 | -393, 370, -589, // 441 151 | -333, 109, -499, // 444 152 | -424, 109, -424, // 447 153 | -555, 234, -370, // 450 154 | -501, 370, -501, // 453 155 | -499, 109, -333, // 456 156 | -616, 234, -255, // 459 157 | -589, 370, -393, // 462 158 | -555, 109, -229, // 465 159 | -654, 234, -130, // 468 160 | -654, 370, -271, // 471 161 | -589, 109, -117, // 474 162 | -667, 234, 0, // 477 163 | -695, 370, -138, // 480 164 | -502, 0, -99, // 483 165 | -600, 109, 0, // 486 166 | -654, 234, 130, // 489 167 | -708, 370, 0, // 492 168 | -589, 109, 117, // 495 169 | -616, 234, 255, // 498 170 | -555, 109, 229, // 501 171 | -695, 370, 138, // 504 172 | -555, 234, 370, // 507 173 | -654, 370, 271, // 510 174 | -499, 109, 333, // 513 175 | -472, 234, 472, // 516 176 | -589, 370, 393, // 519 177 | -370, 234, 555, // 522 178 | -501, 370, 501, // 525 179 | -424, 109, 424, // 528 180 | -333, 109, 499, // 531 181 | -255, 234, 616, // 534 182 | -393, 370, 589, // 537 183 | -130, 234, 654, // 540 184 | -271, 370, 654, // 543 185 | -229, 109, 555, // 546 186 | -117, 109, 589, // 549 187 | -138, 370, 695, // 552 188 | 0, 45, 30, // 555 189 | 39, 28, 39, // 558 190 | 0, 28, 56, // 561 191 | 52, 2, 52, // 564 192 | 0, 51, 0, // 567 193 | 21, 45, 21, // 570 194 | 56, 28, 0, // 573 195 | 30, 45, 0, // 576 196 | 21, 45, -21, // 579 197 | 39, 28, -39, // 582 198 | 0, 45, -30, // 585 199 | 0, 28, -56, // 588 200 | -39, 28, -39, // 591 201 | -21, 45, -21, // 594 202 | -73, 2, 0, // 597 203 | -30, 45, 0, // 600 204 | -56, 28, 0, // 603 205 | -21, 45, 21, // 606 206 | -39, 28, 39, // 609 207 | -354, 34, 0, // 612 208 | -200, 34, -52, // 615 209 | 0, 0, -354, // 618 210 | 135, 0, -327 // 621 211 | }; // modelVertices 212 | 213 | #define ARENA_TRIANGLE_COUNT 382 214 | const S3L_Index arenaTriangleIndices[ARENA_TRIANGLE_COUNT * 3] = { 215 | 0, 1, 2, // 0 216 | 3, 4, 5, // 3 217 | 6, 7, 8, // 6 218 | 9, 0, 10, // 9 219 | 11, 5, 12, // 12 220 | 13, 10, 14, // 15 221 | 15, 12, 16, // 18 222 | 17, 13, 14, // 21 223 | 18, 15, 16, // 24 224 | 19, 20, 21, // 27 225 | 22, 23, 24, // 30 226 | 23, 25, 24, // 33 227 | 26, 27, 20, // 36 228 | 28, 29, 30, // 39 229 | 31, 26, 19, // 42 230 | 32, 30, 33, // 45 231 | 34, 35, 31, // 48 232 | 36, 33, 37, // 51 233 | 38, 39, 34, // 54 234 | 40, 37, 41, // 57 235 | 42, 6, 8, // 60 236 | 43, 44, 38, // 63 237 | 45, 41, 46, // 66 238 | 47, 42, 48, // 69 239 | 49, 50, 43, // 72 240 | 51, 45, 46, // 75 241 | 52, 47, 48, // 78 242 | 53, 54, 49, // 81 243 | 1, 51, 2, // 84 244 | 55, 52, 4, // 87 245 | 7, 56, 53, // 90 246 | 57, 39, 44, // 93 247 | 55, 58, 59, // 96 248 | 60, 61, 62, // 99 249 | 58, 55, 63, // 102 250 | 58, 63, 64, // 105 251 | 65, 60, 6, // 108 252 | 59, 66, 47, // 111 253 | 67, 68, 69, // 114 254 | 70, 6, 60, // 117 255 | 71, 72, 73, // 120 256 | 56, 70, 62, // 123 257 | 74, 75, 76, // 126 258 | 56, 62, 54, // 129 259 | 57, 50, 62, // 132 260 | 77, 76, 13, // 135 261 | 78, 20, 77, // 138 262 | 18, 24, 79, // 141 263 | 79, 80, 81, // 144 264 | 77, 82, 83, // 147 265 | 35, 39, 84, // 150 266 | 85, 84, 86, // 153 267 | 85, 27, 26, // 156 268 | 87, 20, 27, // 159 269 | 87, 85, 82, // 162 270 | 8, 88, 89, // 165 271 | 90, 91, 92, // 168 272 | 88, 92, 89, // 171 273 | 88, 93, 90, // 174 274 | 7, 94, 88, // 177 275 | 93, 95, 90, // 180 276 | 96, 97, 93, // 183 277 | 94, 96, 93, // 186 278 | 49, 94, 53, // 189 279 | 43, 98, 49, // 192 280 | 99, 100, 96, // 195 281 | 98, 99, 96, // 198 282 | 101, 102, 99, // 201 283 | 103, 101, 99, // 204 284 | 38, 103, 43, // 207 285 | 34, 104, 38, // 210 286 | 105, 106, 101, // 213 287 | 104, 105, 101, // 216 288 | 107, 108, 105, // 219 289 | 109, 107, 105, // 222 290 | 31, 109, 34, // 225 291 | 110, 111, 107, // 228 292 | 112, 110, 107, // 231 293 | 31, 113, 112, // 234 294 | 114, 115, 110, // 237 295 | 113, 114, 110, // 240 296 | 21, 113, 19, // 243 297 | 116, 117, 114, // 246 298 | 118, 116, 114, // 249 299 | 78, 118, 21, // 252 300 | 119, 120, 116, // 255 301 | 121, 119, 116, // 258 302 | 17, 121, 78, // 261 303 | 122, 123, 119, // 264 304 | 124, 122, 119, // 267 305 | 14, 124, 17, // 270 306 | 125, 126, 122, // 273 307 | 10, 125, 14, // 276 308 | 122, 127, 128, // 279 309 | 129, 127, 126, // 282 310 | 130, 129, 126, // 285 311 | 0, 130, 10, // 288 312 | 131, 132, 129, // 291 313 | 2, 131, 0, // 294 314 | 132, 133, 129, // 297 315 | 134, 135, 132, // 300 316 | 136, 134, 132, // 303 317 | 2, 137, 136, // 306 318 | 46, 137, 51, // 309 319 | 138, 139, 134, // 312 320 | 137, 138, 134, // 315 321 | 140, 141, 138, // 318 322 | 142, 140, 138, // 321 323 | 41, 142, 46, // 324 324 | 143, 144, 140, // 327 325 | 145, 143, 140, // 330 326 | 37, 145, 41, // 333 327 | 146, 147, 143, // 336 328 | 148, 146, 143, // 339 329 | 37, 149, 148, // 342 330 | 150, 151, 146, // 345 331 | 149, 150, 146, // 348 332 | 33, 152, 149, // 351 333 | 153, 154, 150, // 354 334 | 152, 153, 150, // 357 335 | 30, 155, 152, // 360 336 | 156, 157, 153, // 363 337 | 155, 156, 153, // 366 338 | 29, 158, 155, // 369 339 | 159, 160, 156, // 372 340 | 158, 159, 156, // 375 341 | 161, 162, 158, // 378 342 | 163, 164, 159, // 381 343 | 162, 163, 159, // 384 344 | 16, 162, 18, // 387 345 | 165, 166, 163, // 390 346 | 16, 167, 165, // 393 347 | 166, 168, 163, // 396 348 | 169, 170, 166, // 399 349 | 167, 169, 166, // 402 350 | 12, 171, 167, // 405 351 | 4, 171, 5, // 408 352 | 172, 173, 169, // 411 353 | 171, 172, 169, // 414 354 | 174, 175, 172, // 417 355 | 176, 174, 172, // 420 356 | 52, 176, 4, // 423 357 | 48, 177, 52, // 426 358 | 178, 179, 174, // 429 359 | 177, 178, 174, // 432 360 | 180, 181, 178, // 435 361 | 182, 180, 178, // 438 362 | 48, 183, 182, // 441 363 | 92, 184, 180, // 444 364 | 183, 92, 180, // 447 365 | 8, 183, 42, // 450 366 | 17, 78, 77, // 453 367 | 21, 20, 78, // 456 368 | 185, 186, 187, // 459 369 | 187, 188, 61, // 462 370 | 185, 189, 190, // 465 371 | 190, 191, 186, // 468 372 | 186, 86, 188, // 471 373 | 190, 189, 192, // 474 374 | 191, 74, 86, // 477 375 | 192, 189, 193, // 480 376 | 192, 194, 191, // 483 377 | 193, 189, 195, // 486 378 | 194, 195, 196, // 489 379 | 194, 72, 74, // 492 380 | 195, 197, 196, // 495 381 | 196, 68, 72, // 498 382 | 195, 189, 198, // 501 383 | 197, 199, 68, // 504 384 | 198, 189, 200, // 507 385 | 198, 201, 197, // 510 386 | 201, 64, 199, // 513 387 | 200, 189, 202, // 516 388 | 200, 203, 201, // 519 389 | 202, 187, 203, // 522 390 | 203, 61, 64, // 525 391 | 202, 189, 185, // 528 392 | 58, 64, 61, // 531 393 | 188, 62, 61, // 534 394 | 68, 73, 72, // 537 395 | 24, 204, 79, // 540 396 | 63, 199, 64, // 543 397 | 82, 86, 74, // 546 398 | 84, 188, 86, // 549 399 | 75, 74, 72, // 552 400 | 50, 57, 44, // 555 401 | 55, 3, 63, // 558 402 | 204, 205, 80, // 561 403 | 11, 63, 3, // 564 404 | 47, 66, 65, // 567 405 | 60, 65, 66, // 570 406 | 62, 70, 60, // 573 407 | 50, 54, 62, // 576 408 | 76, 83, 74, // 579 409 | 83, 82, 74, // 582 410 | 82, 85, 86, // 585 411 | 35, 85, 26, // 588 412 | 85, 35, 84, // 591 413 | 62, 188, 57, // 594 414 | 57, 188, 84, // 597 415 | 66, 61, 60, // 600 416 | 199, 63, 81, // 603 417 | 73, 68, 67, // 606 418 | 71, 75, 72, // 609 419 | 66, 58, 61, // 612 420 | 81, 205, 23, // 615 421 | 18, 161, 24, // 618 422 | 161, 22, 24, // 621 423 | 81, 23, 199, // 624 424 | 68, 199, 69, // 627 425 | 69, 199, 23, // 630 426 | 63, 15, 81, // 633 427 | 15, 63, 11, // 636 428 | 79, 81, 15, // 639 429 | 69, 28, 67, // 642 430 | 28, 69, 22, // 645 431 | 28, 32, 67, // 648 432 | 67, 36, 73, // 651 433 | 40, 73, 36, // 654 434 | 36, 67, 32, // 657 435 | 73, 45, 71, // 660 436 | 206, 71, 45, // 663 437 | 45, 73, 40, // 666 438 | 71, 1, 75, // 669 439 | 207, 75, 1, // 672 440 | 1, 71, 206, // 675 441 | 75, 9, 76, // 678 442 | 13, 76, 9, // 681 443 | 9, 75, 207, // 684 444 | 0, 207, 1, // 687 445 | 3, 55, 4, // 690 446 | 6, 70, 7, // 693 447 | 9, 207, 0, // 696 448 | 11, 3, 5, // 699 449 | 13, 9, 10, // 702 450 | 15, 11, 12, // 705 451 | 17, 77, 13, // 708 452 | 18, 79, 15, // 711 453 | 19, 26, 20, // 714 454 | 22, 69, 23, // 717 455 | 23, 205, 25, // 720 456 | 28, 22, 29, // 723 457 | 31, 35, 26, // 726 458 | 32, 28, 30, // 729 459 | 34, 39, 35, // 732 460 | 36, 32, 33, // 735 461 | 38, 44, 39, // 738 462 | 40, 36, 37, // 741 463 | 42, 65, 6, // 744 464 | 43, 50, 44, // 747 465 | 45, 40, 41, // 750 466 | 47, 65, 42, // 753 467 | 49, 54, 50, // 756 468 | 51, 206, 45, // 759 469 | 52, 59, 47, // 762 470 | 53, 56, 54, // 765 471 | 1, 206, 51, // 768 472 | 55, 59, 52, // 771 473 | 7, 70, 56, // 774 474 | 57, 84, 39, // 777 475 | 59, 58, 66, // 780 476 | 77, 83, 76, // 783 477 | 79, 204, 80, // 786 478 | 77, 20, 82, // 789 479 | 85, 87, 27, // 792 480 | 87, 82, 20, // 795 481 | 8, 7, 88, // 798 482 | 90, 95, 91, // 801 483 | 88, 90, 92, // 804 484 | 88, 94, 93, // 807 485 | 7, 53, 94, // 810 486 | 93, 97, 95, // 813 487 | 96, 100, 97, // 816 488 | 94, 98, 96, // 819 489 | 49, 98, 94, // 822 490 | 43, 103, 98, // 825 491 | 99, 102, 100, // 828 492 | 98, 103, 99, // 831 493 | 101, 106, 102, // 834 494 | 103, 104, 101, // 837 495 | 38, 104, 103, // 840 496 | 34, 109, 104, // 843 497 | 105, 108, 106, // 846 498 | 104, 109, 105, // 849 499 | 107, 111, 108, // 852 500 | 109, 112, 107, // 855 501 | 31, 112, 109, // 858 502 | 110, 115, 111, // 861 503 | 112, 113, 110, // 864 504 | 31, 19, 113, // 867 505 | 114, 117, 115, // 870 506 | 113, 118, 114, // 873 507 | 21, 118, 113, // 876 508 | 116, 120, 117, // 879 509 | 118, 121, 116, // 882 510 | 78, 121, 118, // 885 511 | 119, 123, 120, // 888 512 | 121, 124, 119, // 891 513 | 17, 124, 121, // 894 514 | 122, 128, 123, // 897 515 | 124, 125, 122, // 900 516 | 14, 125, 124, // 903 517 | 125, 130, 126, // 906 518 | 10, 130, 125, // 909 519 | 122, 126, 127, // 912 520 | 129, 133, 127, // 915 521 | 130, 131, 129, // 918 522 | 0, 131, 130, // 921 523 | 131, 136, 132, // 924 524 | 2, 136, 131, // 927 525 | 132, 135, 133, // 930 526 | 134, 139, 135, // 933 527 | 136, 137, 134, // 936 528 | 2, 51, 137, // 939 529 | 46, 142, 137, // 942 530 | 138, 141, 139, // 945 531 | 137, 142, 138, // 948 532 | 140, 144, 141, // 951 533 | 142, 145, 140, // 954 534 | 41, 145, 142, // 957 535 | 143, 147, 144, // 960 536 | 145, 148, 143, // 963 537 | 37, 148, 145, // 966 538 | 146, 151, 147, // 969 539 | 148, 149, 146, // 972 540 | 37, 33, 149, // 975 541 | 150, 154, 151, // 978 542 | 149, 152, 150, // 981 543 | 33, 30, 152, // 984 544 | 153, 157, 154, // 987 545 | 152, 155, 153, // 990 546 | 30, 29, 155, // 993 547 | 156, 160, 157, // 996 548 | 155, 158, 156, // 999 549 | 29, 161, 158, // 1002 550 | 159, 164, 160, // 1005 551 | 158, 162, 159, // 1008 552 | 161, 18, 162, // 1011 553 | 163, 168, 164, // 1014 554 | 162, 165, 163, // 1017 555 | 16, 165, 162, // 1020 556 | 165, 167, 166, // 1023 557 | 16, 12, 167, // 1026 558 | 166, 170, 168, // 1029 559 | 169, 173, 170, // 1032 560 | 167, 171, 169, // 1035 561 | 12, 5, 171, // 1038 562 | 4, 176, 171, // 1041 563 | 172, 175, 173, // 1044 564 | 171, 176, 172, // 1047 565 | 174, 179, 175, // 1050 566 | 176, 177, 174, // 1053 567 | 52, 177, 176, // 1056 568 | 48, 182, 177, // 1059 569 | 178, 181, 179, // 1062 570 | 177, 182, 178, // 1065 571 | 180, 184, 181, // 1068 572 | 182, 183, 180, // 1071 573 | 48, 42, 183, // 1074 574 | 92, 91, 184, // 1077 575 | 183, 89, 92, // 1080 576 | 8, 89, 183, // 1083 577 | 185, 190, 186, // 1086 578 | 187, 186, 188, // 1089 579 | 190, 192, 191, // 1092 580 | 186, 191, 86, // 1095 581 | 191, 194, 74, // 1098 582 | 192, 193, 194, // 1101 583 | 194, 193, 195, // 1104 584 | 194, 196, 72, // 1107 585 | 195, 198, 197, // 1110 586 | 196, 197, 68, // 1113 587 | 197, 201, 199, // 1116 588 | 198, 200, 201, // 1119 589 | 201, 203, 64, // 1122 590 | 200, 202, 203, // 1125 591 | 202, 185, 187, // 1128 592 | 203, 187, 61, // 1131 593 | 24, 25, 204, // 1134 594 | 204, 25, 205, // 1137 595 | 81, 80, 205, // 1140 596 | 161, 29, 22 // 1143 597 | }; // modelTriangleIndices 598 | 599 | S3L_Model3D arenaModel; 600 | 601 | void arenaModelInit() 602 | { 603 | S3L_model3DInit( 604 | arenaVertices, 605 | ARENA_VERTEX_COUNT, 606 | arenaTriangleIndices, 607 | ARENA_TRIANGLE_COUNT, 608 | &arenaModel); 609 | } 610 | -------------------------------------------------------------------------------- /programs/carModel.h: -------------------------------------------------------------------------------- 1 | #define CAR_VERTEX_COUNT 124 2 | const S3L_Unit carVertices[CAR_VERTEX_COUNT * 3] = { 3 | -706, 166, -901, // 0 4 | -706, -352, -686, // 3 5 | -706, -48, -990, // 6 6 | -706, 255, -686, // 9 7 | -489, -302, -432, // 12 8 | -706, -48, -382, // 15 9 | -489, -48, -328, // 18 10 | -706, 166, -471, // 21 11 | -489, 204, -432, // 24 12 | -282, 160, -477, // 27 13 | -282, -48, -390, // 30 14 | -282, -257, -477, // 33 15 | -282, -344, -686, // 36 16 | -489, -407, -686, // 39 17 | -706, -263, -901, // 42 18 | -489, -302, -939, // 45 19 | -282, -257, -895, // 48 20 | -282, -48, -981, // 51 21 | -489, -48, -1044, // 54 22 | -282, 160, -895, // 57 23 | -489, 204, -939, // 60 24 | -489, 309, -686, // 63 25 | -282, 246, -686, // 66 26 | -706, -263, -471, // 69 27 | 705, 166, -471, // 72 28 | 705, -352, -686, // 75 29 | 705, -48, -382, // 78 30 | 705, 255, -686, // 81 31 | 487, -302, -939, // 84 32 | 705, -48, -990, // 87 33 | 487, -48, -1044, // 90 34 | 705, 166, -901, // 93 35 | 487, 204, -939, // 96 36 | 281, 160, -895, // 99 37 | 281, -48, -981, // 102 38 | 281, -257, -895, // 105 39 | 281, -344, -686, // 108 40 | 487, -407, -686, // 111 41 | 705, -263, -471, // 114 42 | 487, -302, -432, // 117 43 | 281, -257, -477, // 120 44 | 281, -48, -390, // 123 45 | 487, -48, -328, // 126 46 | 281, 160, -477, // 129 47 | 487, 204, -432, // 132 48 | 487, 309, -686, // 135 49 | 281, 246, -686, // 138 50 | 705, -263, -901, // 141 51 | -706, 166, 480, // 144 52 | -706, -352, 695, // 147 53 | -706, -48, 391, // 150 54 | -706, 255, 695, // 153 55 | -489, -302, 948, // 156 56 | -706, -48, 999, // 159 57 | -489, -48, 1053, // 162 58 | -706, 166, 909, // 165 59 | -489, 204, 948, // 168 60 | -282, 160, 904, // 171 61 | -282, -48, 990, // 174 62 | -282, -257, 904, // 177 63 | -282, -344, 695, // 180 64 | -489, -407, 695, // 183 65 | -706, -263, 480, // 186 66 | -489, -302, 441, // 189 67 | -282, -257, 486, // 192 68 | -282, -48, 399, // 195 69 | -489, -48, 336, // 198 70 | -282, 160, 486, // 201 71 | -489, 204, 441, // 204 72 | -489, 309, 695, // 207 73 | -282, 246, 695, // 210 74 | -706, -263, 909, // 213 75 | 705, 166, 909, // 216 76 | 705, -352, 695, // 219 77 | 705, -48, 999, // 222 78 | 705, 255, 695, // 225 79 | 487, -302, 441, // 228 80 | 705, -48, 391, // 231 81 | 487, -48, 336, // 234 82 | 705, 166, 480, // 237 83 | 487, 204, 441, // 240 84 | 281, 160, 486, // 243 85 | 281, -48, 399, // 246 86 | 281, -257, 486, // 249 87 | 281, -344, 695, // 252 88 | 487, -407, 695, // 255 89 | 705, -263, 909, // 258 90 | 487, -302, 948, // 261 91 | 281, -257, 904, // 264 92 | 281, -48, 990, // 267 93 | 487, -48, 1053, // 270 94 | 281, 160, 904, // 273 95 | 487, 204, 948, // 276 96 | 487, 309, 695, // 279 97 | 281, 246, 695, // 282 98 | 705, -263, 480, // 285 99 | -335, -142, -276, // 288 100 | -424, 204, -350, // 291 101 | -481, 214, 301, // 294 102 | -234, 656, -288, // 297 103 | -275, 656, 218, // 300 104 | -219, -36, -834, // 303 105 | -237, -140, -370, // 306 106 | 225, -36, -834, // 309 107 | 260, 275, -591, // 312 108 | -228, 232, -814, // 315 109 | 235, 232, -814, // 318 110 | -229, 380, 577, // 321 111 | 241, 656, -288, // 324 112 | -254, 275, -591, // 327 113 | 281, 656, 218, // 330 114 | -240, 343, 772, // 333 115 | 246, 343, 772, // 336 116 | 232, 28, 840, // 339 117 | -225, 28, 840, // 342 118 | -229, -167, 356, // 345 119 | -360, -161, 249, // 348 120 | 235, 380, 577, // 351 121 | 236, -167, 356, // 354 122 | 244, -140, -370, // 357 123 | 342, -142, -276, // 360 124 | 367, -161, 249, // 363 125 | 487, 214, 301, // 366 126 | 430, 204, -350 // 369 127 | }; // modelVertices 128 | 129 | #define CAR_TRIANGLE_COUNT 228 130 | const S3L_Index carTriangleIndices[CAR_TRIANGLE_COUNT * 3] = { 131 | 0, 1, 2, // 0 132 | 1, 0, 3, // 3 133 | 4, 5, 6, // 6 134 | 6, 7, 8, // 9 135 | 6, 9, 10, // 12 136 | 6, 11, 4, // 15 137 | 4, 12, 13, // 18 138 | 1, 4, 13, // 21 139 | 14, 13, 15, // 24 140 | 13, 16, 15, // 27 141 | 15, 17, 18, // 30 142 | 2, 15, 18, // 33 143 | 18, 19, 20, // 36 144 | 0, 18, 20, // 39 145 | 3, 20, 21, // 42 146 | 3, 8, 7, // 45 147 | 22, 8, 21, // 48 148 | 21, 19, 22, // 51 149 | 1, 14, 2, // 54 150 | 3, 7, 1, // 57 151 | 7, 5, 1, // 60 152 | 5, 23, 1, // 63 153 | 19, 12, 22, // 66 154 | 19, 17, 12, // 69 155 | 12, 17, 16, // 72 156 | 22, 12, 9, // 75 157 | 9, 12, 10, // 78 158 | 11, 10, 12, // 81 159 | 4, 23, 5, // 84 160 | 6, 5, 7, // 87 161 | 6, 8, 9, // 90 162 | 6, 10, 11, // 93 163 | 4, 11, 12, // 96 164 | 1, 23, 4, // 99 165 | 14, 1, 13, // 102 166 | 13, 12, 16, // 105 167 | 15, 16, 17, // 108 168 | 2, 14, 15, // 111 169 | 18, 17, 19, // 114 170 | 0, 2, 18, // 117 171 | 3, 0, 20, // 120 172 | 3, 21, 8, // 123 173 | 22, 9, 8, // 126 174 | 21, 20, 19, // 129 175 | 24, 25, 26, // 132 176 | 25, 24, 27, // 135 177 | 28, 29, 30, // 138 178 | 30, 31, 32, // 141 179 | 30, 33, 34, // 144 180 | 30, 35, 28, // 147 181 | 28, 36, 37, // 150 182 | 25, 28, 37, // 153 183 | 38, 37, 39, // 156 184 | 37, 40, 39, // 159 185 | 39, 41, 42, // 162 186 | 26, 39, 42, // 165 187 | 42, 43, 44, // 168 188 | 24, 42, 44, // 171 189 | 27, 44, 45, // 174 190 | 27, 32, 31, // 177 191 | 46, 32, 45, // 180 192 | 45, 43, 46, // 183 193 | 25, 38, 26, // 186 194 | 27, 31, 25, // 189 195 | 31, 29, 25, // 192 196 | 29, 47, 25, // 195 197 | 43, 36, 46, // 198 198 | 43, 41, 36, // 201 199 | 36, 41, 40, // 204 200 | 46, 36, 33, // 207 201 | 33, 36, 34, // 210 202 | 35, 34, 36, // 213 203 | 28, 47, 29, // 216 204 | 30, 29, 31, // 219 205 | 30, 32, 33, // 222 206 | 30, 34, 35, // 225 207 | 28, 35, 36, // 228 208 | 25, 47, 28, // 231 209 | 38, 25, 37, // 234 210 | 37, 36, 40, // 237 211 | 39, 40, 41, // 240 212 | 26, 38, 39, // 243 213 | 42, 41, 43, // 246 214 | 24, 26, 42, // 249 215 | 27, 24, 44, // 252 216 | 27, 45, 32, // 255 217 | 46, 33, 32, // 258 218 | 45, 44, 43, // 261 219 | 48, 49, 50, // 264 220 | 49, 48, 51, // 267 221 | 52, 53, 54, // 270 222 | 54, 55, 56, // 273 223 | 54, 57, 58, // 276 224 | 54, 59, 52, // 279 225 | 52, 60, 61, // 282 226 | 49, 52, 61, // 285 227 | 62, 61, 63, // 288 228 | 61, 64, 63, // 291 229 | 63, 65, 66, // 294 230 | 50, 63, 66, // 297 231 | 66, 67, 68, // 300 232 | 48, 66, 68, // 303 233 | 51, 68, 69, // 306 234 | 51, 56, 55, // 309 235 | 70, 56, 69, // 312 236 | 69, 67, 70, // 315 237 | 49, 62, 50, // 318 238 | 51, 55, 49, // 321 239 | 55, 53, 49, // 324 240 | 53, 71, 49, // 327 241 | 67, 60, 70, // 330 242 | 67, 65, 60, // 333 243 | 60, 65, 64, // 336 244 | 70, 60, 57, // 339 245 | 57, 60, 58, // 342 246 | 59, 58, 60, // 345 247 | 52, 71, 53, // 348 248 | 54, 53, 55, // 351 249 | 54, 56, 57, // 354 250 | 54, 58, 59, // 357 251 | 52, 59, 60, // 360 252 | 49, 71, 52, // 363 253 | 62, 49, 61, // 366 254 | 61, 60, 64, // 369 255 | 63, 64, 65, // 372 256 | 50, 62, 63, // 375 257 | 66, 65, 67, // 378 258 | 48, 50, 66, // 381 259 | 51, 48, 68, // 384 260 | 51, 69, 56, // 387 261 | 70, 57, 56, // 390 262 | 69, 68, 67, // 393 263 | 72, 73, 74, // 396 264 | 73, 72, 75, // 399 265 | 76, 77, 78, // 402 266 | 78, 79, 80, // 405 267 | 78, 81, 82, // 408 268 | 78, 83, 76, // 411 269 | 76, 84, 85, // 414 270 | 73, 76, 85, // 417 271 | 86, 85, 87, // 420 272 | 85, 88, 87, // 423 273 | 87, 89, 90, // 426 274 | 74, 87, 90, // 429 275 | 90, 91, 92, // 432 276 | 72, 90, 92, // 435 277 | 75, 92, 93, // 438 278 | 75, 80, 79, // 441 279 | 94, 80, 93, // 444 280 | 93, 91, 94, // 447 281 | 73, 86, 74, // 450 282 | 75, 79, 73, // 453 283 | 79, 77, 73, // 456 284 | 77, 95, 73, // 459 285 | 91, 84, 94, // 462 286 | 91, 89, 84, // 465 287 | 84, 89, 88, // 468 288 | 94, 84, 81, // 471 289 | 81, 84, 82, // 474 290 | 83, 82, 84, // 477 291 | 76, 95, 77, // 480 292 | 78, 77, 79, // 483 293 | 78, 80, 81, // 486 294 | 78, 82, 83, // 489 295 | 76, 83, 84, // 492 296 | 73, 95, 76, // 495 297 | 86, 73, 85, // 498 298 | 85, 84, 88, // 501 299 | 87, 88, 89, // 504 300 | 74, 86, 87, // 507 301 | 90, 89, 91, // 510 302 | 72, 74, 90, // 513 303 | 75, 72, 92, // 516 304 | 75, 93, 80, // 519 305 | 94, 81, 80, // 522 306 | 93, 92, 91, // 525 307 | 96, 97, 98, // 528 308 | 97, 99, 100, // 531 309 | 101, 102, 103, // 534 310 | 104, 105, 106, // 537 311 | 100, 107, 98, // 540 312 | 104, 108, 109, // 543 313 | 103, 106, 101, // 546 314 | 99, 97, 109, // 549 315 | 108, 110, 99, // 552 316 | 111, 107, 112, // 555 317 | 111, 113, 114, // 558 318 | 102, 101, 109, // 561 319 | 107, 115, 98, // 564 320 | 98, 115, 116, // 567 321 | 115, 102, 116, // 570 322 | 111, 114, 107, // 573 323 | 115, 107, 114, // 576 324 | 109, 97, 102, // 579 325 | 97, 96, 102, // 582 326 | 105, 109, 101, // 585 327 | 107, 100, 117, // 588 328 | 118, 114, 113, // 591 329 | 119, 115, 118, // 594 330 | 96, 116, 102, // 597 331 | 120, 121, 122, // 600 332 | 123, 122, 110, // 603 333 | 102, 119, 103, // 606 334 | 105, 104, 109, // 609 335 | 110, 122, 117, // 612 336 | 99, 109, 108, // 615 337 | 106, 105, 101, // 618 338 | 108, 104, 123, // 621 339 | 100, 99, 110, // 624 340 | 112, 107, 117, // 627 341 | 113, 111, 112, // 630 342 | 119, 104, 103, // 633 343 | 117, 122, 118, // 636 344 | 122, 121, 118, // 639 345 | 118, 121, 119, // 642 346 | 112, 117, 113, // 645 347 | 118, 113, 117, // 648 348 | 104, 119, 123, // 651 349 | 123, 119, 120, // 654 350 | 106, 103, 104, // 657 351 | 110, 117, 100, // 660 352 | 102, 115, 119, // 663 353 | 118, 115, 114, // 666 354 | 120, 119, 121, // 669 355 | 96, 98, 116, // 672 356 | 97, 100, 98, // 675 357 | 120, 122, 123, // 678 358 | 123, 110, 108 // 681 359 | }; // modelTriangleIndices 360 | 361 | S3L_Model3D carModel; 362 | 363 | void carModelInit() 364 | { 365 | S3L_model3DInit( 366 | carVertices, 367 | CAR_VERTEX_COUNT, 368 | carTriangleIndices, 369 | CAR_TRIANGLE_COUNT, 370 | &carModel); 371 | } 372 | -------------------------------------------------------------------------------- /programs/conservation.c: -------------------------------------------------------------------------------- 1 | /** Example that fakes the momentum/energy conservation. We simply keep the 2 | record of total speed in the system (as an approximation of 3 | momentum/energy/whatever) and if it changes too much, we multiply them 4 | accordingly to get them back :) */ 5 | 6 | #include "helper.h" 7 | 8 | #define SPHERE_R (7 * TPE_F) 9 | 10 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 11 | { 12 | TPE_ENV_START( TPE_envSphereInside(p,TPE_vec3(0,0,0),SPHERE_R),p ) 13 | TPE_ENV_END 14 | } 15 | 16 | int main(void) 17 | { 18 | helper_init(); 19 | 20 | tpe_world.environmentFunction = environmentDistance; 21 | 22 | s3l_scene.camera.transform.translation.z = -1 * SPHERE_R - 2 * TPE_F; 23 | 24 | for (int i = 0; i < 4; ++i) // add bodies 25 | { 26 | switch (i) 27 | { 28 | case 0: helper_addBox(5 * TPE_F / 4,5 * TPE_F / 4,5 * TPE_F / 4,TPE_F,2 * TPE_F); break; 29 | case 1: helper_addBall(TPE_F,TPE_F / 4); break; 30 | case 2: helper_addRect(5 * TPE_F / 4,5 * TPE_F / 4,TPE_F,2 * TPE_F); break; 31 | case 3: helper_add2Line(2 * TPE_F,TPE_F / 2,TPE_F); break; 32 | default: break; 33 | } 34 | 35 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3((i - 2) * TPE_F * 2,-1 * TPE_F * i,TPE_F / 2)); 36 | 37 | /* We don't want any energy losses due to friction and non-elastic collision, 38 | so we'll turn them off. This alone won't be enough though as numeric errors 39 | will still change the total energy. */ 40 | 41 | helper_lastBody.friction = 0; 42 | helper_lastBody.elasticity = TPE_FRACTIONS_PER_UNIT; 43 | helper_lastBody.flags |= TPE_BODY_FLAG_ALWAYS_ACTIVE; 44 | 45 | TPE_bodyAccelerate(&helper_lastBody, // give some initial velocity 46 | TPE_vec3Plus(TPE_vec3Times(TPE_bodyGetCenterOfMass(&helper_lastBody),TPE_F / 10),TPE_vec3(0,TPE_F / 100,0))); 47 | } 48 | 49 | TPE_Unit sTotal = TPE_worldGetNetSpeed(&tpe_world); 50 | // ^ keep the record of total speed 51 | 52 | while (helper_running) 53 | { 54 | helper_frameStart(); 55 | 56 | helper_cameraFreeMovement(); 57 | 58 | TPE_worldStep(&tpe_world); 59 | 60 | TPE_Unit s = TPE_worldGetNetSpeed(&tpe_world); 61 | TPE_Unit ratio = (sTotal * TPE_FRACTIONS_PER_UNIT) / TPE_nonZero(s); 62 | 63 | if (ratio < (4 * TPE_FRACTIONS_PER_UNIT) / 5 || 64 | ratio > (6 * TPE_FRACTIONS_PER_UNIT) / 5) 65 | { 66 | // if total speed changed by more than 1/5, we adjust it 67 | 68 | printf("net speed is now %d but needs to be %d, correcting!\n",s,sTotal); 69 | 70 | for (int i = 0; i < tpe_world.bodyCount; ++i) 71 | TPE_bodyMultiplyNetSpeed(&tpe_world.bodies[i],ratio); 72 | } 73 | 74 | helper_set3DColor(200,10,10); 75 | 76 | for (int i = 0; i < tpe_world.bodyCount; ++i) // draw the bodies 77 | { 78 | TPE_Joint *joints = tpe_world.bodies[i].joints; 79 | TPE_Vec3 pos = TPE_bodyGetCenterOfMass(&tpe_world.bodies[i]); 80 | TPE_Vec3 right = TPE_vec3(TPE_F,0,0); 81 | TPE_Vec3 forw = TPE_vec3(0,0,TPE_F); 82 | 83 | if (i != 1) // ugly code to get the correct orientation :) 84 | { 85 | if (i != 3) 86 | { 87 | forw = TPE_vec3Minus(joints[2].position,joints[0].position); 88 | right = TPE_vec3Minus(joints[1].position,joints[0].position); 89 | } 90 | else 91 | forw = TPE_vec3Minus(joints[1].position,joints[0].position); 92 | } 93 | 94 | TPE_Vec3 orient = TPE_rotationFromVecs(forw,right); 95 | 96 | switch (i % 5) // and draw the correct shape 97 | { 98 | case 0: helper_draw3DBox(pos,TPE_vec3(1200,1200,1200),orient); break; 99 | case 1: helper_draw3DSphere(pos,TPE_vec3(500,500,500),orient); break; 100 | case 2: helper_draw3DBox(pos,TPE_vec3(1200,400,1200),orient); break; 101 | case 3: helper_draw3DBox(pos,TPE_vec3(200,200,1200),orient); break; 102 | default: break; 103 | } 104 | } 105 | 106 | helper_set3DColor(200,200,200); 107 | 108 | helper_draw3DSphereInside(TPE_vec3(0,0,0), 109 | TPE_vec3(SPHERE_R,SPHERE_R,SPHERE_R),TPE_vec3(0,0,0)); 110 | 111 | if (helper_debugDrawOn) 112 | helper_debugDraw(1); 113 | 114 | helper_frameEnd(); 115 | } 116 | 117 | helper_end(); 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /programs/cubes.c: -------------------------------------------------------------------------------- 1 | /** Demo showing a wrecking ball hitting a few boxes. */ 2 | 3 | #include "helper.h" 4 | 5 | #define ROOM_SIZE (14 * TPE_F) 6 | #define CUBE_SIZE (3 * TPE_F / 2) 7 | #define GRAVITY (TPE_F / 100) 8 | 9 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 10 | { 11 | return TPE_envAABoxInside(p,TPE_vec3(0,ROOM_SIZE / 4,0), 12 | TPE_vec3(ROOM_SIZE,ROOM_SIZE / 2,ROOM_SIZE)); 13 | } 14 | 15 | TPE_Vec3 cubeOrientations[6]; 16 | TPE_Vec3 cubePositions[6]; 17 | 18 | TPE_Joint ballJoints[4]; // for the wrecking ball body 19 | TPE_Connection ballConnections[3]; 20 | 21 | /** Updates the orientation and position of the ith cube's 3D model accordint to 22 | the physics model. This will only be done for active bodies to help 23 | performance a bit. */ 24 | void updateOrientPos(int i) 25 | { 26 | cubeOrientations[i] = TPE_bodyGetRotation(&tpe_world.bodies[i],0,2,1); 27 | cubePositions[i] = TPE_bodyGetCenterOfMass(&tpe_world.bodies[i]); 28 | } 29 | 30 | int main(void) 31 | { 32 | helper_init(); 33 | 34 | tpe_world.environmentFunction = environmentDistance; 35 | 36 | s3l_scene.camera.transform.translation.z -= ROOM_SIZE / 2; 37 | s3l_scene.camera.transform.translation.y += ROOM_SIZE / 3; 38 | s3l_scene.camera.transform.translation.x -= ROOM_SIZE / 4; 39 | s3l_scene.camera.transform.rotation.y = -1 * TPE_F / 16; 40 | 41 | for (int i = 0; i < 6; ++i) // add 6 cubes 42 | { 43 | helper_addBox(CUBE_SIZE / 2,CUBE_SIZE / 2,CUBE_SIZE / 2,CUBE_SIZE / 4,TPE_F / 5); 44 | helper_lastBody.friction = TPE_F / 20; // decrease friction for fun 45 | } 46 | 47 | #define move(i,x,y) \ 48 | TPE_bodyMoveBy(&tpe_world.bodies[i],TPE_vec3((CUBE_SIZE / 2 + 10) * x,10 + CUBE_SIZE / 2 + y * (CUBE_SIZE + 10),0)); 49 | 50 | move(0,0,0) 51 | move(1,-2,0) 52 | move(2,2,0) 53 | move(3,-1,1) 54 | move(4,1,1) 55 | move(5,0,2) 56 | 57 | #undef move 58 | 59 | for (int i = 0; i < 6; ++i) 60 | { 61 | updateOrientPos(i); 62 | 63 | /* normally the cubes wouldn't stand on each other as they're really made of 64 | spheres, so we deactivate them to keep them in place until the wrecking 65 | ball hits them: */ 66 | TPE_bodyDeactivate(&tpe_bodies[i]); 67 | } 68 | 69 | // now create the wrecking ball body: 70 | 71 | ballJoints[0] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2,0),0); 72 | ballJoints[1] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - TPE_F / 5,-1 * TPE_F),0); 73 | ballJoints[2] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - TPE_F / 2,-2 * TPE_F),0); 74 | ballJoints[3] = TPE_joint(TPE_vec3(0,ROOM_SIZE / 2 - TPE_F,-4 * TPE_F),TPE_F); 75 | ballJoints[3].velocity[1] = -1 * TPE_F / 2; 76 | ballJoints[3].velocity[2] = -1 * TPE_F / 2; 77 | 78 | ballConnections[0].joint1 = 0; ballConnections[0].joint2 = 1; 79 | ballConnections[1].joint1 = 1; ballConnections[1].joint2 = 2; 80 | ballConnections[2].joint1 = 2; ballConnections[2].joint2 = 3; 81 | 82 | TPE_Body *ballBody = &tpe_world.bodies[6]; 83 | 84 | TPE_bodyInit(ballBody,ballJoints,4,ballConnections,3,2 * TPE_F); 85 | 86 | ballBody->flags |= TPE_BODY_FLAG_SIMPLE_CONN; 87 | ballBody->flags |= TPE_BODY_FLAG_ALWAYS_ACTIVE; 88 | ballBody->friction = 0; 89 | ballBody->elasticity = TPE_F; 90 | 91 | tpe_world.bodyCount++; 92 | 93 | while (helper_running) 94 | { 95 | helper_frameStart(); 96 | 97 | helper_cameraFreeMovement(); 98 | 99 | if (helper_frame % 16 == 0) 100 | { 101 | helper_printCPU(); 102 | 103 | if (sdl_keyboard[SDL_SCANCODE_L]) // pressing L explodes the cubes :) 104 | for (int i = 0; i < 6; ++i) 105 | { 106 | TPE_bodyActivate(&tpe_world.bodies[i]); 107 | TPE_bodyAccelerate(&tpe_world.bodies[i], 108 | TPE_vec3Plus(TPE_vec3(0,TPE_F / 5,0),TPE_vec3Times(cubePositions[i],TPE_F / 4))); 109 | } 110 | } 111 | 112 | // pin the top point of the wrecking ball to the ceiling: 113 | TPE_jointPin(&ballBody->joints[0],TPE_vec3(0,ROOM_SIZE / 2 - TPE_F / 100,0)); 114 | 115 | TPE_worldStep(&tpe_world); 116 | 117 | helper_set3DColor(170,170,170); 118 | helper_draw3DBoxInside(TPE_vec3(0,ROOM_SIZE / 4,0),TPE_vec3(ROOM_SIZE,ROOM_SIZE / 2,ROOM_SIZE),TPE_vec3(0,0,0)); 119 | 120 | helper_set3DColor(200,10,10); 121 | 122 | for (int i = 0; i < 6; ++i) 123 | { 124 | TPE_bodyApplyGravity(&tpe_world.bodies[i],GRAVITY); 125 | 126 | if (TPE_bodyIsActive(&tpe_world.bodies[i])) 127 | updateOrientPos(i); 128 | 129 | helper_draw3DBox(cubePositions[i],TPE_vec3(CUBE_SIZE,CUBE_SIZE,CUBE_SIZE), 130 | cubeOrientations[i]); 131 | } 132 | 133 | ballBody->joints[3].velocity[1] -= GRAVITY; // apply gravity only to one joint 134 | 135 | helper_draw3DSphere(ballBody->joints[3].position,TPE_vec3(400,400,400),TPE_vec3(0,0,0)); 136 | 137 | for (int i = 0; i < 3; ++i) 138 | helper_drawLine3D( 139 | ballBody->joints[ballBody->connections[i].joint1].position, 140 | ballBody->joints[ballBody->connections[i].joint2].position, 141 | 255,0,0); 142 | 143 | if (helper_debugDrawOn) 144 | helper_debugDraw(1); 145 | 146 | helper_frameEnd(); 147 | } 148 | 149 | helper_end(); 150 | 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /programs/envaccel.c: -------------------------------------------------------------------------------- 1 | /** Boring demo, shows how to accelerate environment functions with bounding 2 | volume checks. */ 3 | 4 | #include "helper.h" 5 | 6 | #define ACCELERATE 1 // if you turn this off, FPS will drastically drop 7 | 8 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 9 | { 10 | /* this is our highly complex environment function that find the closest point 11 | against a grid of shape chunks -- with acceleration we apply bounding 12 | volume checks to the big chunks and each smaller subchunk */ 13 | 14 | TPE_ENV_START( TPE_envAABoxInside(p,TPE_vec3(0,0,0),TPE_vec3(30000,30000,30000)), p ) 15 | 16 | for (int bigZ = -1; bigZ < 1; ++bigZ) // big chunks 17 | for (int bigY = -1; bigY < 1; ++bigY) 18 | for (int bigX = -1; bigX < 1; ++bigX) 19 | { 20 | TPE_Vec3 bigCenter = 21 | TPE_vec3(bigX * 20 * TPE_F,bigY * 20 * TPE_F,bigZ * 20 * TPE_F); 22 | 23 | #if ACCELERATE 24 | // bouding volume check for the big chunks of environment 25 | if (TPE_ENV_BCUBE_TEST(p,maxD,bigCenter,20 * TPE_F)) 26 | #endif 27 | for (int smallZ = 0; smallZ < 2; ++smallZ) // smaller chunks 28 | for (int smallY = 0; smallY < 2; ++smallY) 29 | for (int smallX = 0; smallX < 2; ++smallX) 30 | { 31 | TPE_Vec3 smallCenter = TPE_vec3Plus(bigCenter, 32 | TPE_vec3(smallX * 8 * TPE_F,smallY * 8 * TPE_F,smallZ * 8 * TPE_F)); 33 | 34 | #if ACCELERATE 35 | // bouding volume check for the smaller subchanks of environment 36 | if (TPE_ENV_BSPHERE_TEST(p,maxD,smallCenter,8 * TPE_F)) 37 | #endif 38 | { 39 | TPE_ENV_NEXT( TPE_envBox(p,smallCenter,TPE_vec3(2 * TPE_F,4 * TPE_F / 3,4 * TPE_F / 3), 40 | TPE_vec3(TPE_F / 100,TPE_F / 20,TPE_F / 15)), p ); 41 | 42 | TPE_ENV_NEXT( TPE_envCylinder(p,smallCenter,TPE_vec3(TPE_F * 4,TPE_F * 3,3 * TPE_F / 4),3 * TPE_F / 4), p); 43 | TPE_ENV_NEXT( TPE_envSphere(p,TPE_vec3Minus(smallCenter,TPE_vec3(-3 * TPE_F / 4,TPE_F,0)), 2 * TPE_F ) , p ); 44 | } 45 | } 46 | } 47 | 48 | TPE_ENV_END 49 | } 50 | 51 | int main(void) 52 | { 53 | helper_init(); 54 | 55 | helper_debugDrawOn = 1; 56 | tpe_world.environmentFunction = environmentDistance; 57 | s3l_scene.camera.transform.translation.z -= TPE_F * 4; 58 | 59 | while (helper_running) 60 | { 61 | helper_frameStart(); 62 | helper_cameraFreeMovement(); 63 | TPE_worldStep(&tpe_world); 64 | 65 | if (helper_frame % 32 == 0) 66 | helper_printCPU(); 67 | 68 | if (helper_debugDrawOn) 69 | helper_debugDraw(1); 70 | 71 | helper_frameEnd(); 72 | } 73 | 74 | helper_end(); 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /programs/heightmap.c: -------------------------------------------------------------------------------- 1 | /** Demo showing the heightmap environment. */ 2 | 3 | #define CAMERA_STEP 200 4 | #define HEIGHTMAP_3D_RESOLUTION 32 5 | #define HEIGHTMAP_3D_STEP (TPE_F * 2) 6 | #define MAP_LIMIT ((HEIGHTMAP_3D_RESOLUTION * HEIGHTMAP_3D_STEP) / 2) 7 | 8 | #include "helper.h" 9 | 10 | /** For given heightmap node at [x,y] returns its height. Here the function uses 11 | sines/cosines to generate a simple procedural heightmap but it could also 12 | retrieve the height e.g. from an image. */ 13 | TPE_Unit height(int32_t x, int32_t y) 14 | { 15 | x *= 8; 16 | y *= 8; 17 | 18 | return 19 | TPE_sin(x + TPE_cos(y * 2)) * TPE_sin(y * 2 + TPE_cos(x * 4)) / (TPE_F / 2); 20 | } 21 | 22 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 23 | { 24 | return TPE_envHeightmap(p,TPE_vec3(0,0,0),HEIGHTMAP_3D_STEP,height,maxD); 25 | } 26 | 27 | int main(void) 28 | { 29 | helper_init(); 30 | 31 | helper_debugDrawOn = 0; 32 | 33 | // here we just set up the graphical 3D model of the heigtmap: 34 | 35 | for (int y = 0; y < HEIGHTMAP_3D_RESOLUTION; ++y) 36 | for (int x = 0; x < HEIGHTMAP_3D_RESOLUTION; ++x) 37 | helper_setHeightmapPoint(x,y,height(x - HEIGHTMAP_3D_RESOLUTION / 2,y - HEIGHTMAP_3D_RESOLUTION / 2)); 38 | 39 | tpe_world.environmentFunction = environmentDistance; 40 | 41 | helper_addBox(700,700,700,300,1000); 42 | 43 | TPE_bodyMoveTo(&helper_lastBody,TPE_vec3(0,5000,0)); 44 | 45 | s3l_scene.camera.transform.rotation.x = -1 * TPE_F / 8; 46 | 47 | while (helper_running) 48 | { 49 | helper_frameStart(); 50 | 51 | TPE_Vec3 bodyCenter = TPE_bodyGetCenterOfMass(&tpe_world.bodies[0]); 52 | 53 | s3l_scene.camera.transform.translation.x = bodyCenter.x; 54 | s3l_scene.camera.transform.translation.y = bodyCenter.y + 3000; 55 | s3l_scene.camera.transform.translation.z = bodyCenter.z - 3000; 56 | 57 | if (bodyCenter.x < -1 * MAP_LIMIT) 58 | TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(2 * MAP_LIMIT,TPE_F,0)); 59 | else if (bodyCenter.x > MAP_LIMIT) 60 | TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(-2 * MAP_LIMIT,TPE_F,0)); 61 | 62 | if (bodyCenter.z < -1 * MAP_LIMIT) 63 | TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(0,TPE_F,2 * MAP_LIMIT)); 64 | else if (bodyCenter.z > MAP_LIMIT) 65 | TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(0,TPE_F,-2 * MAP_LIMIT)); 66 | 67 | if (helper_frame % 32 == 0) 68 | helper_printCPU(); 69 | 70 | helper_set3DColor(0,200,100); 71 | helper_drawModel(&heightmapModel,TPE_vec3(-HEIGHTMAP_3D_STEP / 2,0,-HEIGHTMAP_3D_STEP / 2),TPE_vec3(512,512,512),TPE_vec3(0,0,0)); 72 | 73 | helper_set3DColor(100,100,200); 74 | helper_draw3DBox(bodyCenter,TPE_vec3(2 * TPE_F,2 * TPE_F,2 * TPE_F), 75 | TPE_bodyGetRotation(&tpe_world.bodies[0],0,1,2)); 76 | 77 | for (int i = 0; i < tpe_world.bodyCount; ++i) 78 | TPE_bodyApplyGravity(&tpe_world.bodies[i],7); 79 | 80 | TPE_worldStep(&tpe_world); 81 | 82 | #define ACC (TPE_F / 50) 83 | if (sdl_keyboard[SDL_SCANCODE_UP]) 84 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(0,0,ACC)); 85 | else if (sdl_keyboard[SDL_SCANCODE_DOWN]) 86 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(0,0,-1 * ACC)); 87 | 88 | if (sdl_keyboard[SDL_SCANCODE_LEFT]) 89 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(-1 * ACC,0,0)); 90 | else if (sdl_keyboard[SDL_SCANCODE_RIGHT]) 91 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(ACC,0,0)); 92 | 93 | if (helper_debugDrawOn) 94 | helper_debugDraw(1); 95 | #undef ACC 96 | 97 | helper_frameEnd(); 98 | } 99 | 100 | helper_end(); 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /programs/hello.c: -------------------------------------------------------------------------------- 1 | /** Completely basic program showing an initialization of a body consisting of 2 | a single joint and dropping it on the floor, then plotting the body vertical 3 | position over time. */ 4 | 5 | #include "../tinyphysicsengine.h" 6 | #include 7 | 8 | TPE_Vec3 environmentDistance(TPE_Vec3 point, TPE_Unit maxDistance) 9 | { 10 | return TPE_envGround(point,0); // just an infinite flat plane 11 | } 12 | 13 | int main(void) 14 | { 15 | TPE_Body body; 16 | TPE_World world; 17 | TPE_Joint joint; 18 | int frame = 0; 19 | 20 | joint = TPE_joint(TPE_vec3(0,TPE_F * 8,0),TPE_F); 21 | TPE_bodyInit(&body,&joint,1,0,0,2 * TPE_F); 22 | TPE_worldInit(&world,&body,1,environmentDistance); 23 | 24 | while (TPE_bodyIsActive(&body)) 25 | { 26 | if (frame % 6 == 0) // print once in 6 frames 27 | { 28 | TPE_Unit height = TPE_bodyGetCenterOfMass(&body).y; 29 | 30 | for (int i = 0; i < (height * 4) / TPE_F; ++i) 31 | putchar(' '); 32 | 33 | puts("*"); 34 | } 35 | 36 | TPE_bodyApplyGravity(&body,TPE_F / 100); 37 | TPE_worldStep(&world); 38 | frame++; 39 | } 40 | 41 | puts("body deactivated"); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /programs/hello2.c: -------------------------------------------------------------------------------- 1 | /** Simple demo showing 2 bodies thrown inside a room, the world is rendered 2 | as a simple ASCII side view. */ 3 | 4 | #include "../tinyphysicsengine.h" 5 | #include 6 | 7 | #define ROOM_SIZE (20 * TPE_F) 8 | 9 | TPE_Vec3 environmentDistance(TPE_Vec3 point, TPE_Unit maxDistance) 10 | { 11 | // our environemnt: just a simple room 12 | return TPE_envAABoxInside(point,TPE_vec3(ROOM_SIZE / 2,ROOM_SIZE / 2,0), 13 | TPE_vec3(ROOM_SIZE,ROOM_SIZE,ROOM_SIZE)); 14 | } 15 | 16 | // the following functions are just for ASCII drawing the world 17 | 18 | #define SCREEN_W 32 19 | #define SCREEN_H 16 20 | 21 | char screen[SCREEN_W * SCREEN_H]; 22 | 23 | void clearScreen(void) 24 | { 25 | for (int i = 0; i < SCREEN_W * SCREEN_H; ++i) 26 | screen[i] = (i < SCREEN_W || i >= SCREEN_W * (SCREEN_H - 1)) ? '-' : 27 | ((i % SCREEN_W) == 0 || (i % SCREEN_W) == SCREEN_W - 1 ? '|' : ' '); 28 | } 29 | 30 | void setPixel(int x, int y, char c) 31 | { 32 | if (x < 0 || x >= SCREEN_W || y < 0 || y >= SCREEN_H) 33 | return; 34 | 35 | y = SCREEN_H - 1 - y; 36 | 37 | screen[y * SCREEN_W + x] = c; 38 | } 39 | 40 | void printScreen(void) 41 | { 42 | for (int i = 0; i < 20; ++i) 43 | putchar('\n'); 44 | 45 | for (int y = 0; y < SCREEN_H; ++y) 46 | { 47 | for (int x = 0; x < SCREEN_W; ++x) 48 | putchar(screen[y * SCREEN_W + x]); 49 | 50 | putchar('\n'); 51 | } 52 | } 53 | 54 | int main(void) 55 | { 56 | TPE_Body bodies[2]; // we'll have two bodies 57 | TPE_World world; 58 | 59 | TPE_Joint joints[32]; // joint buffer 60 | TPE_Connection connections[64]; // connection buffer 61 | 62 | /* we'll create the first body "by hand", just two joints (spheres) with one 63 | connection: */ 64 | joints[0] = TPE_joint(TPE_vec3(3 * ROOM_SIZE / 4,ROOM_SIZE / 2,0),TPE_F); 65 | joints[1] = 66 | TPE_joint(TPE_vec3(3 * ROOM_SIZE / 4 + TPE_F * 4,ROOM_SIZE / 2,0),TPE_F); 67 | 68 | connections[0].joint1 = 0; 69 | connections[0].joint2 = 1; 70 | 71 | TPE_bodyInit(&bodies[0],joints,2,connections,1,TPE_F); 72 | 73 | /* the other (a "box" approximated by spheres) will be made by the library 74 | function: */ 75 | TPE_makeBox(joints + 2,connections + 1,2 * TPE_F, 2 * TPE_F, 2 * TPE_F,TPE_F); 76 | TPE_bodyInit(&bodies[1],joints + 2,8,connections + 1,16,TPE_F); 77 | TPE_bodyMoveTo(&bodies[1],TPE_vec3(ROOM_SIZE / 2,ROOM_SIZE / 2,0)); 78 | 79 | TPE_worldInit(&world,bodies,2,environmentDistance); 80 | 81 | // give some initial velocities and spins to the bodies: 82 | 83 | TPE_bodyAccelerate(&world.bodies[0],TPE_vec3(-1 * TPE_F / 8,TPE_F / 3,0)); 84 | TPE_bodySpin(&world.bodies[0],TPE_vec3(0,0,-1 * TPE_F / 25)); 85 | TPE_bodyAccelerate(&world.bodies[1],TPE_vec3(-1 * TPE_F / 2,50,0)); 86 | TPE_bodySpin(&world.bodies[1],TPE_vec3(0,0,TPE_F / 23)); 87 | 88 | #define FRAMES 200 89 | 90 | for (int i = 0; i <= FRAMES; ++i) // simulate 200 steps 91 | { 92 | if (i % 10 == 0) // draw the world every 10 frames 93 | { 94 | clearScreen(); 95 | 96 | for (int j = 0; j < world.bodyCount; ++j) 97 | { 98 | // draw body joints: 99 | for (int k = 0; k < world.bodies[j].jointCount; ++k) 100 | { 101 | TPE_Vec3 pos = world.bodies[j].joints[k].position; 102 | 103 | setPixel((pos.x * SCREEN_W) / ROOM_SIZE, 104 | (pos.y * SCREEN_H) / ROOM_SIZE,'.'); 105 | } 106 | 107 | // draw the body center: 108 | TPE_Vec3 pos = TPE_bodyGetCenterOfMass(&world.bodies[j]); 109 | 110 | setPixel((pos.x * SCREEN_W) / ROOM_SIZE,(pos.y * SCREEN_H) / ROOM_SIZE, 111 | 'A' + j); 112 | } 113 | 114 | printScreen(); 115 | printf("frame %d/%d\n",i,FRAMES); 116 | puts("press return to step"); 117 | getchar(); 118 | } 119 | 120 | TPE_worldStep(&world); // simulate next tick 121 | 122 | for (int j = 0; j < world.bodyCount; ++j) 123 | TPE_bodyApplyGravity(&world.bodies[j],TPE_F / 100); 124 | } 125 | 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /programs/levelModel.h: -------------------------------------------------------------------------------- 1 | #ifndef LEVEL_MODEL_H 2 | #define LEVEL_MODEL_H 3 | 4 | #define LEVEL_VERTEX_COUNT 84 5 | const S3L_Unit levelVertices[LEVEL_VERTEX_COUNT * 3] = { 6 | -1990, 1285, -2716, // 0 7 | -1422, 1285, -2951, // 3 8 | 5395, 4, -2716, // 6 9 | -5409, 4626, 2716, // 9 10 | -5409, 4, 2716, // 12 11 | -5409, 4626, -2716, // 15 12 | -5409, 0, -598, // 18 13 | -1187, 4, 2716, // 21 14 | -1187, 1285, -3519, // 24 15 | 964, 1285, -2716, // 27 16 | -5409, 2326, 2716, // 30 17 | 964, 1285, -6473, // 33 18 | -5409, 4626, 0, // 36 19 | 5395, 4, 513, // 39 20 | 964, 1285, -598, // 42 21 | -3302, 1285, -598, // 45 22 | -3302, 4, -598, // 48 23 | 3179, 2102, -6473, // 51 24 | 5395, 4626, -6473, // 54 25 | -1187, 4626, -6473, // 57 26 | -1187, 1285, -6473, // 60 27 | 964, 4, -2716, // 63 28 | -5409, 1285, -2716, // 66 29 | 3194, 4, 2716, // 69 30 | -5409, 1285, -598, // 72 31 | 964, 4, -598, // 75 32 | 2103, 4626, -6473, // 78 33 | -1187, 4626, -3519, // 81 34 | -1990, 4626, -2716, // 84 35 | -1422, 4626, -2951, // 87 36 | -3302, 4, 1353, // 90 37 | -5409, 4, 1353, // 93 38 | 5395, 4, -4594, // 96 39 | 964, 4, -4594, // 99 40 | 3179, 4, -4594, // 102 41 | 5395, 2102, -6473, // 105 42 | 5395, 2102, -4594, // 108 43 | 3179, 2102, -4594, // 111 44 | 964, 1285, -4594, // 114 45 | 196, 8, 681, // 117 46 | 196, 4626, 681, // 120 47 | 558, 8, 531, // 123 48 | 558, 4626, 531, // 126 49 | 708, 8, 169, // 129 50 | 708, 4626, 169, // 132 51 | 558, 8, -192, // 135 52 | 558, 4626, -192, // 138 53 | 196, 8, -342, // 141 54 | 196, 4626, -342, // 144 55 | -165, 8, -192, // 147 56 | -165, 4626, -192, // 150 57 | -315, 8, 169, // 153 58 | -315, 4626, 169, // 156 59 | -165, 8, 531, // 159 60 | -165, 4626, 531, // 162 61 | 5395, 4626, 513, // 165 62 | 3194, 4626, 2716, // 168 63 | 2216, 869, -2759, // 171 64 | 2216, 1002, -3306, // 174 65 | 2603, 869, -2919, // 177 66 | 2887, 507, -2636, // 180 67 | 2990, 13, -2532, // 183 68 | 2764, 869, -3306, // 186 69 | 3164, 507, -3306, // 189 70 | 3311, 13, -3306, // 192 71 | 2603, 869, -3693, // 195 72 | 2887, 507, -3976, // 198 73 | 2990, 13, -4080, // 201 74 | 2216, 869, -3853, // 204 75 | 2216, 507, -4254, // 207 76 | 2216, 13, -4400, // 210 77 | 1830, 869, -3693, // 213 78 | 1546, 507, -3976, // 216 79 | 1443, 13, -4080, // 219 80 | 1669, 869, -3306, // 222 81 | 1269, 507, -3306, // 225 82 | 1122, 13, -3306, // 228 83 | 1830, 869, -2919, // 231 84 | 1546, 507, -2636, // 234 85 | 1443, 13, -2532, // 237 86 | 2216, 507, -2358, // 240 87 | 2216, 13, -2212, // 243 88 | -1187, 4626, 2716, // 246 89 | 5395, 4626, -2716 // 249 90 | }; // levelVertices 91 | 92 | #define LEVEL_TRIANGLE_COUNT 146 93 | const S3L_Index levelTriangleIndices[LEVEL_TRIANGLE_COUNT * 3] = { 94 | 25, 9, 21, // 0 95 | 10, 12, 24, // 3 96 | 1, 14, 0, // 6 97 | 16, 14, 25, // 9 98 | 8, 9, 1, // 12 99 | 24, 30, 31, // 15 100 | 22, 15, 24, // 18 101 | 13, 23, 25, // 21 102 | 15, 16, 30, // 24 103 | 24, 5, 22, // 27 104 | 27, 20, 8, // 30 105 | 11, 38, 20, // 33 106 | 14, 1, 9, // 36 107 | 37, 35, 36, // 39 108 | 14, 15, 0, // 42 109 | 29, 0, 28, // 45 110 | 27, 1, 29, // 48 111 | 22, 28, 0, // 51 112 | 4, 30, 7, // 54 113 | 24, 31, 10, // 57 114 | 10, 31, 4, // 60 115 | 17, 38, 11, // 63 116 | 20, 38, 8, // 66 117 | 9, 8, 38, // 69 118 | 38, 34, 33, // 72 119 | 9, 38, 21, // 75 120 | 38, 33, 21, // 78 121 | 38, 37, 34, // 81 122 | 36, 34, 37, // 84 123 | 16, 47, 49, // 87 124 | 18, 17, 26, // 90 125 | 11, 26, 17, // 93 126 | 7, 30, 23, // 96 127 | 47, 25, 45, // 99 128 | 40, 41, 39, // 102 129 | 41, 44, 43, // 105 130 | 44, 45, 43, // 108 131 | 46, 47, 45, // 111 132 | 48, 49, 47, // 114 133 | 50, 51, 49, // 117 134 | 52, 53, 51, // 120 135 | 54, 39, 53, // 123 136 | 25, 43, 45, // 126 137 | 43, 23, 41, // 129 138 | 41, 23, 39, // 132 139 | 23, 30, 39, // 135 140 | 39, 30, 53, // 138 141 | 53, 30, 51, // 141 142 | 49, 51, 16, // 144 143 | 30, 16, 51, // 147 144 | 13, 56, 23, // 150 145 | 80, 61, 81, // 153 146 | 57, 58, 59, // 156 147 | 80, 59, 60, // 159 148 | 60, 64, 61, // 162 149 | 59, 58, 62, // 165 150 | 60, 62, 63, // 168 151 | 63, 67, 64, // 171 152 | 62, 58, 65, // 174 153 | 62, 66, 63, // 177 154 | 65, 58, 68, // 180 155 | 65, 69, 66, // 183 156 | 67, 69, 70, // 186 157 | 68, 72, 69, // 189 158 | 69, 73, 70, // 192 159 | 68, 58, 71, // 195 160 | 71, 75, 72, // 198 161 | 72, 76, 73, // 201 162 | 71, 58, 74, // 204 163 | 74, 78, 75, // 207 164 | 76, 78, 79, // 210 165 | 74, 58, 77, // 213 166 | 77, 80, 78, // 216 167 | 79, 80, 81, // 219 168 | 77, 58, 57, // 222 169 | 70, 73, 67, // 225 170 | 33, 34, 70, // 228 171 | 73, 33, 70, // 231 172 | 76, 33, 73, // 234 173 | 33, 76, 21, // 237 174 | 76, 79, 21, // 240 175 | 21, 79, 25, // 243 176 | 79, 81, 25, // 246 177 | 25, 81, 13, // 249 178 | 81, 61, 13, // 252 179 | 13, 61, 2, // 255 180 | 61, 64, 2, // 258 181 | 2, 64, 32, // 261 182 | 64, 67, 32, // 264 183 | 34, 32, 67, // 267 184 | 67, 70, 34, // 270 185 | 82, 10, 4, // 273 186 | 4, 7, 82, // 276 187 | 10, 82, 3, // 279 188 | 7, 23, 82, // 282 189 | 23, 56, 82, // 285 190 | 13, 2, 83, // 288 191 | 55, 13, 83, // 291 192 | 36, 83, 2, // 294 193 | 32, 36, 2, // 297 194 | 36, 18, 83, // 300 195 | 35, 18, 36, // 303 196 | 25, 14, 9, // 306 197 | 10, 3, 12, // 309 198 | 16, 15, 14, // 312 199 | 24, 15, 30, // 315 200 | 22, 0, 15, // 318 201 | 24, 12, 5, // 321 202 | 27, 19, 20, // 324 203 | 37, 17, 35, // 327 204 | 29, 1, 0, // 330 205 | 27, 8, 1, // 333 206 | 22, 5, 28, // 336 207 | 4, 31, 30, // 339 208 | 17, 37, 38, // 342 209 | 36, 32, 34, // 345 210 | 16, 25, 47, // 348 211 | 18, 35, 17, // 351 212 | 40, 42, 41, // 354 213 | 41, 42, 44, // 357 214 | 44, 46, 45, // 360 215 | 46, 48, 47, // 363 216 | 48, 50, 49, // 366 217 | 50, 52, 51, // 369 218 | 52, 54, 53, // 372 219 | 54, 40, 39, // 375 220 | 25, 23, 43, // 378 221 | 13, 55, 56, // 381 222 | 80, 60, 61, // 384 223 | 80, 57, 59, // 387 224 | 60, 63, 64, // 390 225 | 60, 59, 62, // 393 226 | 63, 66, 67, // 396 227 | 62, 65, 66, // 399 228 | 65, 68, 69, // 402 229 | 67, 66, 69, // 405 230 | 68, 71, 72, // 408 231 | 69, 72, 73, // 411 232 | 71, 74, 75, // 414 233 | 72, 75, 76, // 417 234 | 74, 77, 78, // 420 235 | 76, 75, 78, // 423 236 | 77, 57, 80, // 426 237 | 79, 78, 80, // 429 238 | 19, 11, 20, // 432 239 | 19, 26, 11 // 435 240 | }; // levelTriangleIndices 241 | 242 | S3L_Model3D levelModel; 243 | 244 | void levelModelInit(void) 245 | { 246 | S3L_model3DInit( 247 | levelVertices, 248 | LEVEL_VERTEX_COUNT, 249 | levelTriangleIndices, 250 | LEVEL_TRIANGLE_COUNT, 251 | &levelModel); 252 | } 253 | 254 | #endif // guard 255 | -------------------------------------------------------------------------------- /programs/main.c: -------------------------------------------------------------------------------- 1 | #define TPE_LOG puts 2 | 3 | #include "tinyphysicsengine.h" 4 | #include 5 | #include 6 | 7 | #define S3L_RESOLUTION_X 640 8 | #define S3L_RESOLUTION_Y 480 9 | #define S3L_PIXEL_FUNCTION drawPixel 10 | 11 | #define S3L_Z_BUFFER 1 12 | #include "small3dlib.h" 13 | 14 | #define PIXELS_SIZE (S3L_RESOLUTION_X * S3L_RESOLUTION_Y * 4) 15 | 16 | #define FPS 30 17 | #define MSPF (1000 / (FPS)) 18 | 19 | TPE_World world; 20 | TPE_Body bodies[128]; 21 | 22 | S3L_Unit cubeVertices[] = { S3L_CUBE_VERTICES(1024) }; 23 | S3L_Index cubeTriangles[] = { S3L_CUBE_TRIANGLES }; 24 | S3L_Model3D cube; 25 | 26 | uint8_t pixels[PIXELS_SIZE]; 27 | 28 | uint8_t red = 100; 29 | 30 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit distLim) 31 | { 32 | 33 | 34 | TPE_Vec3 r, rMin = p; 35 | TPE_Unit d, dMin = 10000000; 36 | 37 | #define testShape(c) \ 38 | r = c; if (p.x == r.x && p.y == r.y && p.z == r.z) return p; \ 39 | d = TPE_DISTANCE(p,r); if (d < dMin) { dMin = d; rMin = r; } 40 | 41 | /* 42 | testShape( TPE_envHalfPlane(p,TPE_vec3(-1000,0,0),TPE_vec3(20,20,0)) ) 43 | testShape( TPE_envHalfPlane(p,TPE_vec3(+1000,0,0),TPE_vec3(-20,20,0)) ) 44 | */ 45 | testShape( TPE_envAABoxInside(p,TPE_vec3(0,0,0),TPE_vec3(16000,6000,16000)) ) 46 | testShape( TPE_envSphere(p,TPE_vec3(2000,-3500,2000),2000) ) 47 | testShape( TPE_envSphere(p,TPE_vec3(-2000,-3500,0),2000) ) 48 | 49 | #undef testShape 50 | 51 | return rMin; 52 | } 53 | 54 | void drawPixel(S3L_PixelInfo *p) 55 | { 56 | uint32_t index = (p->y * S3L_RESOLUTION_X + p->x) * 4; 57 | pixels[index + 1] = p->triangleIndex * 16; 58 | pixels[index + 2] = 255 - p->triangleIndex * 16; 59 | pixels[index + 3] = red; 60 | } 61 | 62 | void draw2DPoint(int x, int y, int r, int g, int b) 63 | { 64 | if (x < 1 || x > S3L_RESOLUTION_X - 3 || 65 | y < 1 || y > S3L_RESOLUTION_Y - 3) 66 | return; 67 | 68 | uint32_t index = ((y - 1) * S3L_RESOLUTION_X + x) * 4; 69 | 70 | #define d pixels[index] = 0; pixels[index + 1] = b; pixels[index + 2] = g; pixels[index + 3] = r; 71 | 72 | d 73 | index += S3L_RESOLUTION_X * 4 - 4; 74 | d 75 | index += 4; 76 | d 77 | index += 4; 78 | d 79 | index += S3L_RESOLUTION_X * 4 - 4; 80 | d 81 | 82 | #undef d 83 | } 84 | 85 | void debugDrawPixel(uint16_t x, uint16_t y, uint8_t color) 86 | { 87 | draw2DPoint(x,y,255 - color * 64,color * 128,color * 64); 88 | } 89 | 90 | void drawLine(int x1, int y1, int x2, int y2, int r, int g, int b) 91 | { 92 | #define STEPS 20 93 | 94 | float dx = (x2 - x1) / ((float) STEPS); 95 | float dy = (y2 - y1) / ((float) STEPS); 96 | 97 | for (int i = 0; i < STEPS; ++i) 98 | draw2DPoint(x1 + dx * i, y1 + dy * i,r,g,b); 99 | 100 | #undef STEPS 101 | } 102 | 103 | S3L_Scene sphereScene; 104 | 105 | void draw3DLine(int x1, int y1, int z1, int x2, int y2, int z2) 106 | { 107 | S3L_Vec4 p1, p2, r1, r2; 108 | 109 | S3L_vec4Set(&p1,x1,y1,z1,0); 110 | S3L_vec4Set(&p2,x2,y2,z2,0); 111 | 112 | S3L_project3DPointToScreen(p1,sphereScene.camera,&r1); 113 | S3L_project3DPointToScreen(p2,sphereScene.camera,&r2); 114 | 115 | if (r1.z > 0 && r2.z > 0) 116 | drawLine(r1.x,r1.y,r2.x,r2.y,200,100,200); 117 | } 118 | 119 | void drawSphere(S3L_Unit x, S3L_Unit y, S3L_Unit z, S3L_Unit r) 120 | { 121 | sphereScene.models[0].transform.translation.x = x; 122 | sphereScene.models[0].transform.translation.y = y; 123 | sphereScene.models[0].transform.translation.z = z; 124 | sphereScene.models[0].transform.scale.x = r; 125 | sphereScene.models[0].transform.scale.y = r; 126 | sphereScene.models[0].transform.scale.z = r; 127 | S3L_drawScene(sphereScene); 128 | } 129 | 130 | void drawBody(TPE_Body *body, uint8_t color) 131 | { 132 | red = color; 133 | 134 | for (int i = 0; i < body->jointCount; ++i) 135 | drawSphere( 136 | body->joints[i].position.x, 137 | body->joints[i].position.y, 138 | body->joints[i].position.z, 139 | TPE_JOINT_SIZE(body->joints[i])); 140 | 141 | for (int i = 0; i < body->connectionCount; ++i) 142 | { 143 | S3L_Vec4 p1, p2, r1, r2; 144 | 145 | S3L_vec4Set(&p1, 146 | body->joints[body->connections[i].joint1].position.x, 147 | body->joints[body->connections[i].joint1].position.y, 148 | body->joints[body->connections[i].joint1].position.z,0); 149 | 150 | S3L_vec4Set(&p2, 151 | body->joints[body->connections[i].joint2].position.x, 152 | body->joints[body->connections[i].joint2].position.y, 153 | body->joints[body->connections[i].joint2].position.z,0); 154 | 155 | S3L_project3DPointToScreen(p1,sphereScene.camera,&r1); 156 | S3L_project3DPointToScreen(p2,sphereScene.camera,&r2); 157 | 158 | if (r1.z > 0 && r2.z > 0) 159 | drawLine(r1.x,r1.y,r2.x,r2.y,100,200,300); 160 | } 161 | } 162 | 163 | #define SPHERE_VERTEX_COUNT 42 164 | const S3L_Unit sphereVertices[SPHERE_VERTEX_COUNT * 3] = { 165 | 0, -512, 0, // 0 166 | 370, -228, -269, // 3 167 | -141, -228, -435, // 6 168 | -457, -228, 0, // 9 169 | -141, -228, 435, // 12 170 | 370, -228, 269, // 15 171 | 141, 228, -435, // 18 172 | -370, 228, -269, // 21 173 | -370, 228, 269, // 24 174 | 141, 228, 435, // 27 175 | 457, 228, 0, // 30 176 | 0, 512, 0, // 33 177 | -83, -435, -255, // 36 178 | 217, -435, -158, // 39 179 | 134, -269, -414, // 42 180 | 435, -269, 0, // 45 181 | 217, -435, 158, // 48 182 | -269, -435, 0, // 51 183 | -352, -269, -255, // 54 184 | -83, -435, 255, // 57 185 | -352, -269, 255, // 60 186 | 134, -269, 414, // 63 187 | 486, 0, -158, // 66 188 | 486, 0, 158, // 69 189 | 0, 0, -512, // 72 190 | 300, 0, -414, // 75 191 | -486, 0, -158, // 78 192 | -300, 0, -414, // 81 193 | -300, 0, 414, // 84 194 | -486, 0, 158, // 87 195 | 300, 0, 414, // 90 196 | 0, 0, 512, // 93 197 | 352, 269, -255, // 96 198 | -134, 269, -414, // 99 199 | -435, 269, 0, // 102 200 | -134, 269, 414, // 105 201 | 352, 269, 255, // 108 202 | 83, 435, -255, // 111 203 | 269, 435, 0, // 114 204 | -217, 435, -158, // 117 205 | -217, 435, 158, // 120 206 | 83, 435, 255 // 123 207 | }; // sphereVertices 208 | 209 | #define SPHERE_TRIANGLE_COUNT 80 210 | const S3L_Index sphereTriangleIndices[SPHERE_TRIANGLE_COUNT * 3] = { 211 | 0, 13, 12, // 0 212 | 1, 13, 15, // 3 213 | 0, 12, 17, // 6 214 | 0, 17, 19, // 9 215 | 0, 19, 16, // 12 216 | 1, 15, 22, // 15 217 | 2, 14, 24, // 18 218 | 3, 18, 26, // 21 219 | 4, 20, 28, // 24 220 | 5, 21, 30, // 27 221 | 1, 22, 25, // 30 222 | 2, 24, 27, // 33 223 | 3, 26, 29, // 36 224 | 4, 28, 31, // 39 225 | 5, 30, 23, // 42 226 | 6, 32, 37, // 45 227 | 7, 33, 39, // 48 228 | 8, 34, 40, // 51 229 | 9, 35, 41, // 54 230 | 10, 36, 38, // 57 231 | 38, 41, 11, // 60 232 | 38, 36, 41, // 63 233 | 36, 9, 41, // 66 234 | 41, 40, 11, // 69 235 | 41, 35, 40, // 72 236 | 35, 8, 40, // 75 237 | 40, 39, 11, // 78 238 | 40, 34, 39, // 81 239 | 34, 7, 39, // 84 240 | 39, 37, 11, // 87 241 | 39, 33, 37, // 90 242 | 33, 6, 37, // 93 243 | 37, 38, 11, // 96 244 | 37, 32, 38, // 99 245 | 32, 10, 38, // 102 246 | 23, 36, 10, // 105 247 | 23, 30, 36, // 108 248 | 30, 9, 36, // 111 249 | 31, 35, 9, // 114 250 | 31, 28, 35, // 117 251 | 28, 8, 35, // 120 252 | 29, 34, 8, // 123 253 | 29, 26, 34, // 126 254 | 26, 7, 34, // 129 255 | 27, 33, 7, // 132 256 | 27, 24, 33, // 135 257 | 24, 6, 33, // 138 258 | 25, 32, 6, // 141 259 | 25, 22, 32, // 144 260 | 22, 10, 32, // 147 261 | 30, 31, 9, // 150 262 | 30, 21, 31, // 153 263 | 21, 4, 31, // 156 264 | 28, 29, 8, // 159 265 | 28, 20, 29, // 162 266 | 20, 3, 29, // 165 267 | 26, 27, 7, // 168 268 | 26, 18, 27, // 171 269 | 18, 2, 27, // 174 270 | 24, 25, 6, // 177 271 | 24, 14, 25, // 180 272 | 14, 1, 25, // 183 273 | 22, 23, 10, // 186 274 | 22, 15, 23, // 189 275 | 15, 5, 23, // 192 276 | 16, 21, 5, // 195 277 | 16, 19, 21, // 198 278 | 19, 4, 21, // 201 279 | 19, 20, 4, // 204 280 | 19, 17, 20, // 207 281 | 17, 3, 20, // 210 282 | 17, 18, 3, // 213 283 | 17, 12, 18, // 216 284 | 12, 2, 18, // 219 285 | 15, 16, 5, // 222 286 | 15, 13, 16, // 225 287 | 13, 0, 16, // 228 288 | 12, 14, 2, // 231 289 | 12, 13, 14, // 234 290 | 13, 1, 14 // 237 291 | }; // sphereTriangleIndices 292 | 293 | int main(void) 294 | { 295 | SDL_Window *window = SDL_CreateWindow("test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, S3L_RESOLUTION_X, S3L_RESOLUTION_Y, SDL_WINDOW_SHOWN); 296 | SDL_Renderer *renderer = SDL_CreateRenderer(window,-1,0); 297 | SDL_Texture *textureSDL = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STATIC, S3L_RESOLUTION_X, S3L_RESOLUTION_Y); 298 | SDL_Surface *screenSurface = SDL_GetWindowSurface(window); 299 | SDL_Event event; 300 | 301 | int running = 1; 302 | 303 | S3L_Model3D sphereModel; 304 | 305 | S3L_model3DInit(sphereVertices,SPHERE_VERTEX_COUNT,sphereTriangleIndices, 306 | SPHERE_TRIANGLE_COUNT,&sphereModel); 307 | 308 | S3L_model3DInit(cubeVertices,S3L_CUBE_VERTEX_COUNT,cubeTriangles, 309 | S3L_CUBE_TRIANGLE_COUNT,&cube); 310 | 311 | S3L_sceneInit(&sphereModel,1,&sphereScene); 312 | 313 | sphereScene.camera.transform.translation.z = -512 -512 -512; 314 | 315 | int frame = 0; 316 | 317 | TPE_Joint joints[100]; 318 | TPE_Connection connections[100]; 319 | 320 | #define MASS 200 321 | 322 | switch (1) 323 | { 324 | case 0: 325 | TPE_make2Line(joints,connections,1500,512); 326 | TPE_bodyInit(bodies,joints,2,connections,1,MASS); 327 | break; 328 | 329 | case 1: 330 | TPE_makeBox(joints,connections,1000,1000,1000,500); 331 | // TPE_bodyInit(bodies,joints,8,connections,16,MASS); 332 | TPE_bodyInit(bodies,joints,1,connections,0,MASS); 333 | break; 334 | 335 | case 2: 336 | TPE_makeCenterRect(joints,connections,1300,1000,512); 337 | TPE_bodyInit(bodies,joints,5,connections,8,MASS); 338 | break; 339 | 340 | case 3: 341 | TPE_makeCenterBox(joints,connections,1000,1000,1000,512); 342 | joints[8].sizeDivided *= 3; 343 | joints[8].sizeDivided /= 2; 344 | TPE_bodyInit(bodies,joints,9,connections,18,MASS); 345 | break; 346 | 347 | case 4: 348 | TPE_makeTriangle(joints,connections,2000,512); 349 | TPE_bodyInit(bodies,joints,3,connections,3,MASS); 350 | break; 351 | 352 | case 5: 353 | TPE_make2Line(joints,connections,1500,200); 354 | TPE_bodyInit(bodies,joints,1,connections,0,MASS); 355 | break; 356 | 357 | default: break; 358 | } 359 | 360 | //TPE_make2Line(joints + 20,connections + 20,1000,200); 361 | //TPE_bodyInit(bodies + 1,joints + 20,1,connections + 20,0,MASS); 362 | 363 | TPE_makeBox(joints + 20,connections + 20,1000,1000,1000,500); 364 | TPE_bodyInit(bodies + 1,joints + 20,8,connections + 20,16,100); 365 | 366 | //bodies[0].flags |= TPE_BODY_FLAG_SOFT; 367 | 368 | //bodies[0].elasticity = 256; 369 | 370 | 371 | 372 | 373 | TPE_worldInit(&world,bodies,2,environmentDistance); 374 | 375 | #define FR 256 376 | world.bodies[0].friction = FR; 377 | world.bodies[1].friction = FR; 378 | 379 | TPE_bodyMove(world.bodies,TPE_vec3(500,-200,400)); 380 | TPE_bodyMove(&world.bodies[1],TPE_vec3(-500,800,400)); 381 | 382 | 383 | TPE_bodyStop(world.bodies); 384 | TPE_bodyStop(world.bodies + 1); 385 | 386 | TPE_bodyAccelerate(world.bodies,TPE_vec3(-50,0,0)); 387 | TPE_bodyAccelerate(world.bodies + 1,TPE_vec3(50,0,0)); 388 | 389 | //TPE_bodyRotate(world.bodies,TPE_vec3(0,0,200)); 390 | 391 | 392 | 393 | 394 | //------- 395 | 396 | int time; 397 | 398 | while (running) 399 | { 400 | time = SDL_GetTicks(); 401 | 402 | for (uint32_t i = 0; i < PIXELS_SIZE; ++i) 403 | pixels[i] = 0; 404 | 405 | S3L_newFrame(); 406 | 407 | 408 | //TPE_bodiesResolveCollision(world.bodies,world.bodies + 1); 409 | 410 | 411 | 412 | for (int i = 0; i < world.bodyCount; ++i) 413 | { 414 | 415 | 416 | if (!(world.bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED)) 417 | TPE_bodyAccelerate(world.bodies + i,TPE_vec3(0,-6,0)); 418 | 419 | } 420 | 421 | TPE_worldStep(&world); 422 | 423 | 424 | while (SDL_PollEvent(&event)) 425 | { 426 | if (event.type == SDL_QUIT) 427 | running = 0; 428 | else if (event.type == SDL_KEYDOWN) 429 | { 430 | if (event.key.keysym.scancode == SDL_SCANCODE_Q || event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) 431 | running = 0; 432 | } 433 | } 434 | 435 | const uint8_t *state = SDL_GetKeyboardState(NULL); 436 | 437 | S3L_Vec4 camF, camR; 438 | 439 | #define SHIFT_STEP 50 440 | #define ROT_STEP 5 441 | 442 | S3L_rotationToDirections(sphereScene.camera.transform.rotation,SHIFT_STEP,&camF,&camR,0); 443 | 444 | TPE_Vec3 forw = TPE_vec3Minus( 445 | bodies[0].joints[2].position, 446 | bodies[0].joints[0].position); 447 | 448 | TPE_Vec3 righ = TPE_vec3Minus( 449 | bodies[0].joints[0].position, 450 | bodies[0].joints[1].position); 451 | /* 452 | forw = TPE_vec3Plus(forw, 453 | TPE_vec3Minus( 454 | bodies[0].joints[6].position, 455 | bodies[0].joints[4].position) 456 | ); 457 | 458 | righ = TPE_vec3Plus(righ, 459 | TPE_vec3Minus( 460 | bodies[0].joints[4].position, 461 | bodies[0].joints[5].position) 462 | ); 463 | */ 464 | TPE_Vec3 rrrr = TPE_orientationFromVecs(forw,righ); 465 | 466 | cube.transform.rotation.x = rrrr.x; 467 | cube.transform.rotation.y = rrrr.y; 468 | cube.transform.rotation.z = rrrr.z; 469 | 470 | cube.transform.scale.x = 600; 471 | cube.transform.scale.y = cube.transform.scale.x; 472 | cube.transform.scale.z = cube.transform.scale.x; 473 | 474 | TPE_Vec3 ppp = TPE_bodyGetCenter(&bodies[0]); 475 | 476 | cube.transform.translation.x = ppp.x; 477 | cube.transform.translation.y = ppp.y; 478 | cube.transform.translation.z = ppp.z; 479 | 480 | TPE_Vec3 camPos = TPE_vec3( 481 | sphereScene.camera.transform.translation.x, 482 | sphereScene.camera.transform.translation.y, 483 | sphereScene.camera.transform.translation.z); 484 | TPE_Vec3 camRot = TPE_vec3( 485 | sphereScene.camera.transform.rotation.x, 486 | sphereScene.camera.transform.rotation.y, 487 | sphereScene.camera.transform.rotation.z); 488 | 489 | //drawEnv(TPE_vec3(-100,-100,-100),100,5); 490 | 491 | //for (int i = 0; i < world.bodyCount; ++i) 492 | // drawBody(&(world.bodies[i]),100 * i); 493 | 494 | 495 | sphereScene.models = &cube; 496 | S3L_newFrame(); 497 | //S3L_drawScene(sphereScene); 498 | sphereScene.models = &sphereModel; 499 | 500 | TPE_worldDebugDraw(&world,debugDrawPixel, 501 | camPos,camRot,TPE_vec3(S3L_RESOLUTION_X,S3L_RESOLUTION_Y,sphereScene.camera.focalLength)); 502 | 503 | 504 | /* 505 | draw3DLine(0,0,0,forw.x,forw.y,forw.z); 506 | draw3DLine(0,0,0,righ.x,righ.y,righ.z); 507 | */ 508 | 509 | SDL_UpdateTexture(textureSDL,NULL,pixels,S3L_RESOLUTION_X * sizeof(uint32_t)); 510 | 511 | 512 | 513 | if (state[SDL_SCANCODE_LSHIFT]) 514 | { 515 | if (state[SDL_SCANCODE_UP]) 516 | S3L_vec3Add(&sphereScene.camera.transform.translation,camF); 517 | else if (state[SDL_SCANCODE_DOWN]) 518 | S3L_vec3Sub(&sphereScene.camera.transform.translation,camF); 519 | else if (state[SDL_SCANCODE_LEFT]) 520 | S3L_vec3Sub(&sphereScene.camera.transform.translation,camR); 521 | else if (state[SDL_SCANCODE_RIGHT]) 522 | S3L_vec3Add(&sphereScene.camera.transform.translation,camR); 523 | } 524 | else 525 | { 526 | if (state[SDL_SCANCODE_UP]) 527 | sphereScene.camera.transform.rotation.x += ROT_STEP; 528 | else if (state[SDL_SCANCODE_DOWN]) 529 | sphereScene.camera.transform.rotation.x -= ROT_STEP; 530 | else if (state[SDL_SCANCODE_LEFT]) 531 | sphereScene.camera.transform.rotation.y += ROT_STEP; 532 | else if (state[SDL_SCANCODE_RIGHT]) 533 | sphereScene.camera.transform.rotation.y -= ROT_STEP; 534 | else if (state[SDL_SCANCODE_K]) 535 | sphereScene.camera.transform.rotation.z += ROT_STEP; 536 | else if (state[SDL_SCANCODE_L]) 537 | sphereScene.camera.transform.rotation.z -= ROT_STEP; 538 | } 539 | 540 | if (state[SDL_SCANCODE_M]) 541 | { 542 | TPE_bodyWake(world.bodies); 543 | 544 | TPE_bodySpin(bodies,TPE_vec3(50,40,100)); 545 | 546 | TPE_bodyAccelerate(bodies,TPE_vec3( 547 | 500, 548 | 500, 549 | 30)); 550 | } 551 | 552 | #define S 10 553 | if (state[SDL_SCANCODE_J]) 554 | TPE_bodyAccelerate(bodies,TPE_vec3(S,0,0)); 555 | else if (state[SDL_SCANCODE_G]) 556 | TPE_bodyAccelerate(bodies,TPE_vec3(-S,0,0)); 557 | 558 | if (state[SDL_SCANCODE_Y]) 559 | TPE_bodyAccelerate(bodies,TPE_vec3(0,0,S)); 560 | else if (state[SDL_SCANCODE_H]) 561 | TPE_bodyAccelerate(bodies,TPE_vec3(0,0,-S)); 562 | #undef S 563 | 564 | #define SHIFT_STEP 50 565 | 566 | if (state[SDL_SCANCODE_P]) 567 | sphereScene.camera.transform.translation.y += SHIFT_STEP; 568 | else if (state[SDL_SCANCODE_O]) 569 | sphereScene.camera.transform.translation.y -= SHIFT_STEP; 570 | 571 | #undef SHIFT_STEP 572 | 573 | SDL_RenderClear(renderer); 574 | SDL_RenderCopy(renderer,textureSDL,NULL,NULL); 575 | SDL_RenderPresent(renderer); 576 | 577 | time = time + MSPF - SDL_GetTicks(); 578 | 579 | if (time > 1) 580 | usleep(time * 1000); 581 | 582 | frame++; 583 | } 584 | 585 | return 0; 586 | } 587 | -------------------------------------------------------------------------------- /programs/make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROGRAM=car # change this to name of a program you want to compile :) 4 | 5 | clear; clear; g++ -x c -g -fmax-errors=5 -pedantic -O3 -Wall -Wextra -Wstrict-prototypes -Wold-style-definition -Wno-unused-parameter -Wno-missing-field-initializers -o $PROGRAM $PROGRAM.c -lm -lSDL2 && ./$PROGRAM 6 | -------------------------------------------------------------------------------- /programs/player.c: -------------------------------------------------------------------------------- 1 | /** Demo showing a simple first person movement. */ 2 | 3 | #define S3L_NEAR_CROSS_STRATEGY 2 4 | #define S3L_PERSPECTIVE_CORRECTION 2 5 | 6 | #include "helper.h" 7 | #include "levelModel.h" 8 | 9 | TPE_Unit elevatorHeight; 10 | TPE_Unit ramp[6] = { 1600,0, -500,1400, -700,0 }; 11 | TPE_Unit ramp2[6] = { 2000,-5000, 1500,1700, -5000,-500 }; 12 | 13 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 14 | { 15 | // manually created environment to match the 3D model of it 16 | TPE_ENV_START( TPE_envAABoxInside(p,TPE_vec3(0,2450,-2100),TPE_vec3(12600,5000,10800)),p ) 17 | TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(-5693,0,-6580),TPE_vec3(4307,20000,3420)),p ) 18 | TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(-10000,-1000,-10000),TPE_vec3(11085,2500,9295)),p ) 19 | TPE_ENV_NEXT( TPE_envAATriPrism(p,TPE_vec3(-5400,0,0),ramp,3000,2), p) 20 | TPE_ENV_NEXT( TPE_envAATriPrism(p,TPE_vec3(2076,651,-6780),ramp2,3000,0), p) 21 | TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(7000,0,-8500),TPE_vec3(3405,2400,3183)),p ) 22 | TPE_ENV_NEXT( TPE_envSphere(p,TPE_vec3(2521,-100,-3799),1200),p ) 23 | TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(5300,elevatorHeight,-4400),TPE_vec3(1000,elevatorHeight,1000)),p ) 24 | TPE_ENV_NEXT( TPE_envHalfPlane(p,TPE_vec3(5051,0,1802),TPE_vec3(-255,0,-255)),p ) 25 | TPE_ENV_NEXT( TPE_envInfiniteCylinder(p,TPE_vec3(320,0,170),TPE_vec3(0,255,0),530),p ) 26 | TPE_ENV_END 27 | } 28 | 29 | int jumpCountdown = 0, onGround = 0; 30 | TPE_Unit playerRotation = 0, groundDist; 31 | TPE_Vec3 ballRot, ballPreviousPos, playerDirectionVec; 32 | TPE_Body *playerBody = 0; 33 | 34 | void updateDirection(void) // updates player direction vector 35 | { 36 | playerDirectionVec.x = TPE_sin(playerRotation); 37 | playerDirectionVec.z = TPE_cos(playerRotation); 38 | playerDirectionVec.y = 0; 39 | } 40 | 41 | int main(void) 42 | { 43 | helper_init(); 44 | levelModelInit(); 45 | updateDirection(); 46 | 47 | ballRot = TPE_vec3(0,0,0); 48 | 49 | tpe_world.environmentFunction = environmentDistance; 50 | 51 | /* normally player bodies are approximated with capsules -- since we don't 52 | have these, we'll use a body consisting of two spheres: */ 53 | helper_add2Line(400,300,400); 54 | 55 | playerBody = &(tpe_world.bodies[0]); 56 | 57 | TPE_bodyMoveBy(&tpe_world.bodies[0],TPE_vec3(1000,1000,1500)); 58 | TPE_bodyRotateByAxis(&tpe_world.bodies[0],TPE_vec3(0,0,TPE_F / 4)); 59 | playerBody->elasticity = 0; 60 | playerBody->friction = 0; 61 | playerBody->flags |= TPE_BODY_FLAG_NONROTATING; // make it always upright 62 | playerBody->flags |= TPE_BODY_FLAG_ALWAYS_ACTIVE; 63 | groundDist = TPE_JOINT_SIZE(playerBody->joints[0]) + 30; 64 | 65 | // add two interactive bodies: 66 | 67 | helper_addBall(1000,100); 68 | TPE_bodyMoveBy(&tpe_world.bodies[1],TPE_vec3(-1000,1000,0)); 69 | tpe_world.bodies[1].elasticity = 400; 70 | tpe_world.bodies[1].friction = 100; 71 | ballPreviousPos = tpe_world.bodies[1].joints[0].position; 72 | 73 | helper_addCenterRect(600,600,400,50); 74 | TPE_bodyMoveBy(&tpe_world.bodies[2],TPE_vec3(-3000,1000,2000)); 75 | tpe_world.bodies[2].elasticity = 100; 76 | tpe_world.bodies[2].friction = 50; 77 | 78 | while (helper_running) 79 | { 80 | helper_frameStart(); 81 | 82 | TPE_worldStep(&tpe_world); 83 | 84 | if (jumpCountdown > 0) 85 | jumpCountdown--; 86 | 87 | TPE_Vec3 groundPoint = 88 | environmentDistance(playerBody->joints[0].position,groundDist); 89 | 90 | onGround = (playerBody->flags & TPE_BODY_FLAG_DEACTIVATED) || 91 | (TPE_DISTANCE(playerBody->joints[0].position,groundPoint) 92 | <= groundDist && groundPoint.y < playerBody->joints[0].position.y - groundDist / 2); 93 | 94 | if (!onGround) 95 | { 96 | /* it's possible that the closest point is e.g. was a perpend wall so also 97 | additionally check directly below */ 98 | 99 | onGround = TPE_DISTANCE( playerBody->joints[0].position, 100 | TPE_castEnvironmentRay(playerBody->joints[0].position, 101 | TPE_vec3(0,-1 * TPE_F,0),tpe_world.environmentFunction, 102 | 128,512,512)) <= groundDist; 103 | } 104 | 105 | elevatorHeight = (1250 * (TPE_sin(helper_frame * 4) + TPE_F)) / (2 * TPE_F); 106 | 107 | s3l_scene.camera.transform.translation.x = playerBody->joints[0].position.x; 108 | s3l_scene.camera.transform.translation.z = playerBody->joints[0].position.z; 109 | s3l_scene.camera.transform.translation.y = TPE_keepInRange( 110 | s3l_scene.camera.transform.translation.y, 111 | playerBody->joints[1].position.y, 112 | playerBody->joints[1].position.y + 10); 113 | 114 | TPE_bodyMultiplyNetSpeed(&tpe_world.bodies[0],onGround ? 300 : 505); 115 | 116 | s3l_scene.camera.transform.rotation.y = -1 * playerRotation; 117 | 118 | // fake the sphere rotation (since a single joint doesn't rotate itself): 119 | TPE_Vec3 ballRoll = TPE_fakeSphereRotation(ballPreviousPos, 120 | tpe_world.bodies[1].joints[0].position,1000); 121 | 122 | ballRot = TPE_rotationRotateByAxis(ballRot,ballRoll); 123 | 124 | ballPreviousPos = tpe_world.bodies[1].joints[0].position; 125 | 126 | for (int i = 0; i < tpe_world.bodyCount; ++i) 127 | TPE_bodyApplyGravity(&tpe_world.bodies[i],5); 128 | 129 | if (onGround) 130 | { 131 | if (sdl_keyboard[SDL_SCANCODE_SPACE] && jumpCountdown == 0) 132 | { 133 | playerBody->joints[0].velocity[1] = 90; 134 | jumpCountdown = 8; 135 | } 136 | 137 | #define D 15 // just some vector divisor to make the speed slower 138 | if (sdl_keyboard[SDL_SCANCODE_UP] || sdl_keyboard[SDL_SCANCODE_W]) 139 | { 140 | playerBody->joints[0].velocity[0] += playerDirectionVec.x / D; 141 | playerBody->joints[0].velocity[2] += playerDirectionVec.z / D; 142 | } 143 | else if (sdl_keyboard[SDL_SCANCODE_DOWN] || sdl_keyboard[SDL_SCANCODE_S]) 144 | { 145 | playerBody->joints[0].velocity[0] -= playerDirectionVec.x / D; 146 | playerBody->joints[0].velocity[2] -= playerDirectionVec.z / D; 147 | } 148 | 149 | if (sdl_keyboard[SDL_SCANCODE_A]) 150 | { 151 | playerBody->joints[0].velocity[2] += playerDirectionVec.x / D; 152 | playerBody->joints[0].velocity[0] -= playerDirectionVec.z / D; 153 | } 154 | else if (sdl_keyboard[SDL_SCANCODE_D]) 155 | { 156 | playerBody->joints[0].velocity[2] -= playerDirectionVec.x / D; 157 | playerBody->joints[0].velocity[0] += playerDirectionVec.z / D; 158 | } 159 | #undef D 160 | } 161 | 162 | updateDirection(); 163 | 164 | if (sdl_keyboard[SDL_SCANCODE_LEFT]) 165 | playerRotation -= 8; 166 | else if (sdl_keyboard[SDL_SCANCODE_RIGHT]) 167 | playerRotation += 8; 168 | 169 | if (helper_frame % 64 == 0) 170 | { 171 | helper_printCPU(); 172 | helper_printCamera(); 173 | } 174 | 175 | // draw the 3D environment 176 | 177 | helper_set3DColor(200,200,200); 178 | 179 | helper_drawModel(&levelModel,TPE_vec3(0,0,0),TPE_vec3(600,600,600), 180 | TPE_vec3(0,0,0)); 181 | 182 | helper_draw3DBox(TPE_vec3(5300,elevatorHeight,-4400), 183 | TPE_vec3(2000,2 * elevatorHeight,2000),TPE_vec3(0,0,0)); 184 | 185 | helper_set3DColor(200,50,0); 186 | 187 | helper_draw3DBox(TPE_bodyGetCenterOfMass(&tpe_world.bodies[2]), 188 | TPE_vec3(1200,800,1200),TPE_bodyGetRotation(&tpe_world.bodies[2],0,2,1)); 189 | 190 | helper_draw3DSphere(tpe_world.bodies[1].joints[0].position, 191 | TPE_vec3(1000,1000,1000),ballRot); 192 | 193 | if (helper_debugDrawOn) 194 | helper_debugDraw(1); 195 | 196 | helper_frameEnd(); 197 | } 198 | 199 | helper_end(); 200 | 201 | return 0; 202 | } 203 | -------------------------------------------------------------------------------- /programs/pokitto/car.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/programs/pokitto/car.bin -------------------------------------------------------------------------------- /programs/pokitto/carModel.h: -------------------------------------------------------------------------------- 1 | #define CAR_VERTEX_COUNT 38 2 | const S3L_Unit carVertices[CAR_VERTEX_COUNT * 3] = { 3 | -706, -352, -686, // 0 4 | -706, -48, -990, // 3 5 | -706, 255, -686, // 6 6 | -706, -48, -382, // 9 7 | -282, -48, -390, // 12 8 | -282, -344, -686, // 15 9 | -282, -48, -981, // 18 10 | -282, 246, -686, // 21 11 | 705, -352, -686, // 24 12 | 705, -48, -382, // 27 13 | 705, 255, -686, // 30 14 | 705, -48, -990, // 33 15 | 281, -48, -981, // 36 16 | 281, -344, -686, // 39 17 | 281, -48, -390, // 42 18 | 281, 246, -686, // 45 19 | -706, -352, 695, // 48 20 | -706, -48, 391, // 51 21 | -706, 255, 695, // 54 22 | -706, -48, 999, // 57 23 | -282, -48, 990, // 60 24 | -282, -344, 695, // 63 25 | -282, -48, 399, // 66 26 | -282, 246, 695, // 69 27 | 705, -352, 695, // 72 28 | 705, -48, 999, // 75 29 | 705, -48, 391, // 78 30 | 703, 251, 688, // 81 31 | 281, 245, 690, // 84 32 | 281, -48, 399, // 87 33 | 281, -344, 695, // 90 34 | 281, -48, 990, // 93 35 | -234, 656, -288, // 96 36 | -219, -36, -834, // 99 37 | 225, -36, -834, // 102 38 | 241, 656, -288, // 105 39 | 232, 28, 840, // 108 40 | -225, 28, 840 // 111 41 | }; // carVertices 42 | 43 | #define CAR_TRIANGLE_COUNT 56 44 | const S3L_Index carTriangleIndices[CAR_TRIANGLE_COUNT * 3] = { 45 | 18, 16, 17, // 0 46 | 28, 30, 29, // 3 47 | 11, 9, 10, // 6 48 | 34, 32, 33, // 9 49 | 11, 13, 8, // 12 50 | 5, 7, 6, // 15 51 | 23, 19, 18, // 18 52 | 33, 36, 34, // 21 53 | 29, 27, 28, // 24 54 | 25, 30, 31, // 27 55 | 15, 13, 12, // 30 56 | 6, 0, 5, // 33 57 | 18, 22, 23, // 36 58 | 20, 22, 21, // 39 59 | 9, 13, 14, // 42 60 | 0, 4, 5, // 45 61 | 15, 11, 10, // 48 62 | 34, 36, 35, // 51 63 | 15, 9, 14, // 54 64 | 37, 33, 32, // 57 65 | 31, 27, 25, // 60 66 | 1, 3, 0, // 63 67 | 36, 32, 35, // 66 68 | 7, 3, 2, // 69 69 | 29, 24, 26, // 72 70 | 16, 22, 17, // 75 71 | 1, 7, 2, // 78 72 | 27, 24, 25, // 81 73 | 20, 16, 19, // 84 74 | 18, 19, 16, // 87 75 | 28, 31, 30, // 90 76 | 11, 8, 9, // 93 77 | 34, 35, 32, // 96 78 | 11, 12, 13, // 99 79 | 5, 4, 7, // 102 80 | 23, 20, 19, // 105 81 | 33, 37, 36, // 108 82 | 29, 26, 27, // 111 83 | 25, 24, 30, // 114 84 | 15, 14, 13, // 117 85 | 6, 1, 0, // 120 86 | 18, 17, 22, // 123 87 | 20, 23, 22, // 126 88 | 9, 8, 13, // 129 89 | 0, 3, 4, // 132 90 | 15, 12, 11, // 135 91 | 15, 10, 9, // 138 92 | 31, 28, 27, // 141 93 | 1, 2, 3, // 144 94 | 36, 37, 32, // 147 95 | 7, 4, 3, // 150 96 | 29, 30, 24, // 153 97 | 16, 21, 22, // 156 98 | 1, 6, 7, // 159 99 | 27, 26, 24, // 162 100 | 20, 21, 16 // 165 101 | }; // carTriangleIndices 102 | 103 | S3L_Model3D carModel; 104 | 105 | void carModelInit(void) 106 | { 107 | S3L_model3DInit( 108 | carVertices, 109 | CAR_VERTEX_COUNT, 110 | carTriangleIndices, 111 | CAR_TRIANGLE_COUNT, 112 | &carModel); 113 | } 114 | -------------------------------------------------------------------------------- /programs/pokitto/cubes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/programs/pokitto/cubes.bin -------------------------------------------------------------------------------- /programs/pokitto/main.cpp: -------------------------------------------------------------------------------- 1 | #define SAF_PROGRAM_NAME "tpe1" 2 | #define SAF_PLATFORM_POKITTO 3 | #define SAF_SETTING_ENABLE_SOUND 0 4 | #define SAF_SETTING_ENABLE_SAVES 0 5 | 6 | #define S3L_RESOLUTION_X SAF_SCREEN_WIDTH 7 | #define S3L_RESOLUTION_Y SAF_SCREEN_HEIGHT 8 | #define S3L_PIXEL_FUNCTION s3l_drawPixel 9 | 10 | #include "saf.h" 11 | 12 | #define S3L_Z_BUFFER 0 13 | #define S3L_SORT 1 14 | #define S3L_FLAT 1 15 | #define S3L_NEAR_CROSS_STRATEGY 1 16 | 17 | #define MAP_BOUND 15000 18 | 19 | //#define TPE_RESHAPE_ITERATIONS 2 20 | #define TPE_APPROXIMATE_NET_SPEED 1 21 | 22 | #define ACCELERATION (TPE_F / 13) 23 | #define TURN_RATE (3 * TPE_F / 4) 24 | #define TURN_FRICTION (3 * TPE_F / 4) 25 | #define FORW_FRICTION (TPE_F / 14) 26 | 27 | #include "../tinyphysicsengine.h" 28 | #include "../programs/small3dlib.h" 29 | #include "carModel.h" 30 | 31 | TPE_World tpe_world; 32 | TPE_Joint tpe_joints[5]; 33 | TPE_Connection tpe_connections[10]; 34 | TPE_Body tpe_body; 35 | 36 | S3L_Scene s3l_scene; 37 | 38 | uint8_t debugDraw = 0; 39 | 40 | #define PYRAMID_VERTICES 5 41 | const S3L_Unit pyramidVertices[PYRAMID_VERTICES * 3] = 42 | { 43 | 512,0,-512, 44 | 512,0,512, 45 | -512,0,512, 46 | -512,0,-512, 47 | 0,512,0 48 | }; 49 | 50 | #define PYRAMID_TRIANGLES 4 51 | const S3L_Index pyramidTriangles[PYRAMID_TRIANGLES * 3] = 52 | { 53 | 0,1,4, 54 | 1,2,4, 55 | 2,3,4, 56 | 3,0,4 57 | }; 58 | 59 | #define RAMP_VERTICES 4 60 | const S3L_Unit rampVertices[RAMP_VERTICES * 3] = 61 | { 62 | 512,0,-512, 63 | 512,512,512, 64 | -512,512,512, 65 | -512,0,-512 66 | }; 67 | 68 | #define RAMP_TRIANGLES 4 69 | const S3L_Index rampTriangles[RAMP_TRIANGLES * 3] = 70 | { 71 | 0,1,2, 72 | 2,3,0, 73 | 1,0,2, 74 | 3,2,0 75 | }; 76 | 77 | S3L_Model3D models[4]; 78 | 79 | void tpe_debugDrawPixel(uint16_t x, uint16_t y, uint8_t color) 80 | { 81 | SAF_drawPixel(x,y,(color + 2) * 32 + color); 82 | } 83 | 84 | const TPE_Unit rampPoits[6] = { 0,0, -2400,1400, -2400,0 }; 85 | 86 | TPE_Vec3 tpe_environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 87 | { 88 | TPE_ENV_START (TPE_envGround(p,0), p) 89 | TPE_ENV_NEXT (TPE_envSphere(p,TPE_vec3(0,0,-4000),1000), p) 90 | TPE_ENV_NEXT( TPE_envAATriPrism(p,TPE_vec3(-6500,0,0),rampPoits,5000,2),p ) 91 | TPE_ENV_END 92 | } 93 | 94 | void s3l_drawPixel(S3L_PixelInfo *p) 95 | { 96 | SAF_drawPixel(p->x,p->y,(p->triangleIndex + 100) % 256); 97 | } 98 | 99 | uint8_t steering = 0; 100 | uint8_t jointCollisions; 101 | uint8_t jointCollisionsPrev; 102 | TPE_Vec3 carForw, carSide, carUp, carPos, carRot; 103 | 104 | uint8_t collisionCallback(uint16_t b1, uint16_t j1, uint16_t b2, uint16_t j2, 105 | TPE_Vec3 p) 106 | { 107 | if (b1 == 0 && b1 == b2 && j1 < 4) 108 | jointCollisions |= 0x01 << j1; 109 | 110 | return 1; 111 | } 112 | 113 | void carReset(void) 114 | { 115 | TPE_bodyStop(&tpe_body); 116 | 117 | tpe_joints[3].position = TPE_vec3(0,3000,10000); 118 | tpe_joints[2].position = TPE_vec3(512,3000,9500); 119 | tpe_joints[1].position = TPE_vec3(-512,3000,9500); 120 | tpe_joints[0].position = TPE_vec3(512,3000,10500); 121 | tpe_joints[4].position = TPE_vec3(512,3500,10500); 122 | 123 | s3l_scene.camera.transform.translation.z = tpe_body.joints[4].position.z + 3000; 124 | } 125 | 126 | void SAF_init(void) 127 | { 128 | carModelInit(); 129 | 130 | models[0] = carModel; 131 | 132 | S3L_model3DInit(pyramidVertices,PYRAMID_VERTICES,pyramidTriangles,PYRAMID_TRIANGLES,&models[1]); 133 | 134 | models[1].transform.translation.z = -4000; 135 | models[1].transform.scale.x = 1300; 136 | models[1].transform.scale.y = models[1].transform.scale.x; 137 | models[1].transform.scale.z = models[1].transform.scale.x; 138 | 139 | S3L_model3DInit(rampVertices,RAMP_VERTICES,rampTriangles,RAMP_TRIANGLES,&models[2]); 140 | 141 | models[2].transform.translation.x = -6500; 142 | models[2].transform.translation.z = -800; 143 | models[2].transform.scale.x = 2500; 144 | models[2].transform.scale.y = 1300; 145 | models[2].transform.scale.z = 1200; 146 | models[2].transform.rotation.y = 255; 147 | 148 | S3L_sceneInit(models,3,&s3l_scene); 149 | 150 | s3l_scene.camera.transform.rotation.x -= TPE_F / 14; 151 | 152 | TPE_makeCenterRectFull(tpe_joints,tpe_connections,1000,1800,400); 153 | 154 | tpe_joints[4].position.y += 600; 155 | tpe_joints[4].sizeDivided *= 4; 156 | tpe_joints[4].sizeDivided /= 3; 157 | 158 | TPE_bodyInit(&tpe_body,tpe_joints,5,tpe_connections,10,TPE_F / 2); 159 | 160 | tpe_body.elasticity = TPE_F / 100; 161 | tpe_body.friction = FORW_FRICTION; 162 | tpe_body.flags |= TPE_BODY_FLAG_ALWAYS_ACTIVE; 163 | 164 | carReset(); 165 | 166 | TPE_worldInit(&tpe_world,&tpe_body,1,tpe_environmentDistance); 167 | tpe_world.collisionCallback = collisionCallback; 168 | } 169 | 170 | uint8_t SAF_loop(void) 171 | { 172 | SAF_clearScreen(SAF_COLOR_GRAY); 173 | 174 | for (uint8_t i = 0; i < 4; ++i) 175 | { 176 | S3L_Vec4 boundPoint, screenPoint; 177 | S3L_vec4Set(&boundPoint, 178 | i % 2 == 0 ? -1 * MAP_BOUND : MAP_BOUND,0, 179 | i / 2 == 0 ? -1 * MAP_BOUND : MAP_BOUND,1024); 180 | 181 | S3L_project3DPointToScreen(boundPoint,s3l_scene.camera,&screenPoint); 182 | 183 | if (screenPoint.w != 0) 184 | SAF_drawPixel(screenPoint.x,screenPoint.y,SAF_COLOR_RED); 185 | } 186 | 187 | jointCollisions = 0; 188 | 189 | if (SAF_buttonPressed(SAF_BUTTON_RIGHT)) 190 | steering = 1; 191 | else if (SAF_buttonPressed(SAF_BUTTON_LEFT)) 192 | steering = 2; 193 | else 194 | steering = 0; 195 | 196 | TPE_worldStep(&tpe_world); 197 | 198 | TPE_bodyApplyGravity(&tpe_body,TPE_F / 50); 199 | 200 | carPos = TPE_bodyGetCenterOfMass(&tpe_body); 201 | 202 | carForw = TPE_vec3Normalized( 203 | TPE_vec3Minus(tpe_body.joints[2].position,tpe_body.joints[0].position)); 204 | 205 | carSide = TPE_vec3Normalized( 206 | TPE_vec3Minus(tpe_body.joints[1].position,tpe_body.joints[0].position)); 207 | 208 | carUp = TPE_vec3Cross(carForw,carSide); 209 | 210 | for (int i = 0; i < 4; ++i) 211 | if (jointCollisions & (0x01 << i)) 212 | { 213 | TPE_Vec3 jv = TPE_vec3( 214 | tpe_body.joints[i].velocity[0], 215 | tpe_body.joints[i].velocity[1], 216 | tpe_body.joints[i].velocity[2]); 217 | 218 | TPE_Vec3 ja = carSide; 219 | 220 | if (i >= 2 && steering) 221 | { 222 | if (steering == 2) 223 | ja = TPE_vec3Plus(TPE_vec3Times(carForw,TURN_RATE),carSide); 224 | else 225 | ja = TPE_vec3Minus(TPE_vec3Times(carForw,TURN_RATE),carSide); 226 | 227 | ja = TPE_vec3Normalized(ja); 228 | } 229 | 230 | TPE_Vec3 fric = TPE_vec3Times(ja,(TPE_vec3Dot(ja,jv) * TURN_FRICTION) 231 | / TPE_F); 232 | 233 | jv = TPE_vec3Minus(jv,fric); 234 | 235 | tpe_body.joints[i].velocity[0] = jv.x; 236 | tpe_body.joints[i].velocity[1] = jv.y; 237 | tpe_body.joints[i].velocity[2] = jv.z; 238 | } 239 | 240 | if (TPE_vec3Dot(carUp,TPE_vec3Minus(tpe_body.joints[4].position, 241 | tpe_body.joints[0].position)) < 0) 242 | tpe_body.joints[4].position = TPE_vec3Plus(TPE_vec3Times(carUp,300), 243 | tpe_body.joints[0].position); 244 | 245 | if ((jointCollisions | jointCollisionsPrev) & 0x03) // back wheels on ground? 246 | { 247 | if (SAF_buttonPressed(SAF_BUTTON_UP)) 248 | { 249 | tpe_body.joints[0].velocity[0] += (carForw.x * ACCELERATION) / TPE_F; 250 | tpe_body.joints[0].velocity[1] += (carForw.y * ACCELERATION) / TPE_F; 251 | tpe_body.joints[0].velocity[2] += (carForw.z * ACCELERATION) / TPE_F; 252 | tpe_body.joints[1].velocity[0] += (carForw.x * ACCELERATION) / TPE_F; 253 | tpe_body.joints[1].velocity[1] += (carForw.y * ACCELERATION) / TPE_F; 254 | tpe_body.joints[1].velocity[2] += (carForw.z * ACCELERATION) / TPE_F; 255 | } 256 | else if (SAF_buttonPressed(SAF_BUTTON_DOWN)) 257 | { 258 | tpe_body.joints[0].velocity[0] -= (carForw.x * ACCELERATION) / TPE_F; 259 | tpe_body.joints[0].velocity[1] -= (carForw.y * ACCELERATION) / TPE_F; 260 | tpe_body.joints[0].velocity[2] -= (carForw.z * ACCELERATION) / TPE_F; 261 | tpe_body.joints[1].velocity[0] -= (carForw.x * ACCELERATION) / TPE_F; 262 | tpe_body.joints[1].velocity[1] -= (carForw.y * ACCELERATION) / TPE_F; 263 | tpe_body.joints[1].velocity[2] -= (carForw.z * ACCELERATION) / TPE_F; 264 | } 265 | } 266 | 267 | if (SAF_buttonJustPressed(SAF_BUTTON_B)) 268 | carReset(); 269 | 270 | jointCollisionsPrev = jointCollisions; 271 | 272 | carRot = TPE_bodyGetRotation(&tpe_body,0,2,1); 273 | 274 | S3L_newFrame(); 275 | 276 | models[0].transform.translation.x = carPos.x; 277 | models[0].transform.translation.y = carPos.y; 278 | models[0].transform.translation.z = carPos.z; 279 | 280 | models[0].transform.rotation.x = carRot.x; 281 | models[0].transform.rotation.y = carRot.y; 282 | models[0].transform.rotation.z = carRot.z; 283 | 284 | if (carPos.x > MAP_BOUND) 285 | { 286 | TPE_bodyMoveBy(&tpe_body,TPE_vec3(-2 * MAP_BOUND,0,0)); 287 | s3l_scene.camera.transform.translation.x -= 2 * MAP_BOUND; 288 | } 289 | else if (carPos.x < -1 * MAP_BOUND) 290 | { 291 | TPE_bodyMoveBy(&tpe_body,TPE_vec3(2 * MAP_BOUND,0,0)); 292 | s3l_scene.camera.transform.translation.x += 2 * MAP_BOUND; 293 | } 294 | 295 | if (carPos.z > MAP_BOUND) 296 | { 297 | TPE_bodyMoveBy(&tpe_body,TPE_vec3(0,0,-2 * MAP_BOUND)); 298 | s3l_scene.camera.transform.translation.z -= 2 * MAP_BOUND; 299 | } 300 | else if (carPos.z < -1 * MAP_BOUND) 301 | { 302 | TPE_bodyMoveBy(&tpe_body,TPE_vec3(0,0,2 * MAP_BOUND)); 303 | s3l_scene.camera.transform.translation.z += 2 * MAP_BOUND; 304 | } 305 | 306 | S3L_drawScene(s3l_scene); 307 | 308 | if (SAF_buttonJustPressed(SAF_BUTTON_C)) 309 | debugDraw = !debugDraw; 310 | 311 | if (debugDraw) 312 | TPE_worldDebugDraw(&tpe_world,tpe_debugDrawPixel, 313 | TPE_vec3( 314 | s3l_scene.camera.transform.translation.x, 315 | s3l_scene.camera.transform.translation.y, 316 | s3l_scene.camera.transform.translation.z), 317 | TPE_vec3(s3l_scene.camera.transform.rotation.x,s3l_scene.camera.transform.rotation.y,0), 318 | TPE_vec3(SAF_SCREEN_WIDTH,SAF_SCREEN_HEIGHT,TPE_F),4,TPE_F); 319 | 320 | s3l_scene.camera.transform.translation.y = carPos.y + 900; 321 | 322 | TPE_Vec3 v = TPE_vec3KeepWithinDistanceBand( 323 | TPE_vec3( 324 | s3l_scene.camera.transform.translation.x, 325 | s3l_scene.camera.transform.translation.y, 326 | s3l_scene.camera.transform.translation.z 327 | ),tpe_body.joints[4].position,2 * TPE_F,4 * TPE_F); 328 | 329 | s3l_scene.camera.transform.translation.x = v.x; 330 | s3l_scene.camera.transform.translation.y = v.y; 331 | s3l_scene.camera.transform.translation.z = v.z; 332 | 333 | S3L_Vec4 toCar; 334 | 335 | toCar.x = carPos.x - s3l_scene.camera.transform.translation.x; 336 | toCar.y = carPos.y - s3l_scene.camera.transform.translation.y; 337 | toCar.z = carPos.z - s3l_scene.camera.transform.translation.z; 338 | toCar.w = 0; 339 | 340 | TPE_Unit angleDiff = s3l_scene.camera.transform.rotation.y - 341 | (TPE_vec2Angle(toCar.x,toCar.z) - 128); 342 | 343 | s3l_scene.camera.transform.rotation.y -= 344 | (angleDiff < 100 && angleDiff > -100) ? angleDiff / 2 : angleDiff; 345 | 346 | return 1; 347 | } 348 | -------------------------------------------------------------------------------- /programs/pokitto/main2.cpp: -------------------------------------------------------------------------------- 1 | #define SAF_PROGRAM_NAME "tpe1" 2 | #define SAF_PLATFORM_POKITTO 3 | #define SAF_SETTING_ENABLE_SOUND 0 4 | #define SAF_SETTING_ENABLE_SAVES 0 5 | 6 | #define S3L_RESOLUTION_X SAF_SCREEN_WIDTH 7 | #define S3L_RESOLUTION_Y SAF_SCREEN_HEIGHT 8 | #define S3L_PIXEL_FUNCTION s3l_drawPixel 9 | 10 | #include "saf.h" 11 | 12 | #define S3L_Z_BUFFER 0 13 | #define S3L_SORT 1 14 | #define S3L_FLAT 1 15 | #define S3L_NEAR_CROSS_STRATEGY 1 16 | 17 | #define ROOM_SIZE (30 * TPE_F) 18 | 19 | #define TPE_RESHAPE_ITERATIONS 2 20 | #define TPE_APPROXIMATE_NET_SPEED 1 21 | 22 | #include "../tinyphysicsengine.h" 23 | #include "../programs/small3dlib.h" 24 | 25 | TPE_World tpe_world; 26 | TPE_Joint tpe_joints[30]; 27 | TPE_Connection tpe_connections[35]; 28 | TPE_Body tpe_bodies[4]; 29 | 30 | static const S3L_Unit s3l_cubeVertices[S3L_CUBE_VERTEX_COUNT * 3] = { S3L_CUBE_VERTICES(S3L_FRACTIONS_PER_UNIT) }; 31 | static const S3L_Index s3l_cubeTriangles[S3L_CUBE_TRIANGLE_COUNT * 3] = { S3L_CUBE_TRIANGLES }; 32 | 33 | S3L_Model3D s3l_models[3]; 34 | S3L_Scene s3l_scene; 35 | 36 | uint8_t debugDraw = 0; 37 | 38 | void tpe_debugDrawPixel(uint16_t x, uint16_t y, uint8_t color) 39 | { 40 | SAF_drawPixel(x,y,(color + 2) * 32 + color); 41 | } 42 | 43 | TPE_Vec3 tpe_environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 44 | { 45 | return TPE_envAABoxInside(p,TPE_vec3(0,0,0), 46 | TPE_vec3(ROOM_SIZE,ROOM_SIZE,ROOM_SIZE)); 47 | } 48 | 49 | static uint8_t sideColors[12] = 50 | { 32, 32, 64, 64, 96, 96, 128, 128, 160, 160, 192, 192 }; 51 | 52 | uint8_t colorAdd; 53 | 54 | void s3l_drawPixel(S3L_PixelInfo *p) 55 | { 56 | SAF_drawPixel(p->x,p->y,sideColors[p->triangleIndex] + colorAdd); 57 | } 58 | 59 | void SAF_init(void) 60 | { 61 | S3L_model3DInit(s3l_cubeVertices,S3L_CUBE_VERTEX_COUNT,s3l_cubeTriangles,S3L_CUBE_TRIANGLE_COUNT,&s3l_models[0]); 62 | 63 | s3l_models[0].transform.scale.x = 3 * S3L_FRACTIONS_PER_UNIT; 64 | s3l_models[0].transform.scale.y = s3l_models[0].transform.scale.x; 65 | s3l_models[0].transform.scale.z = s3l_models[0].transform.scale.x; 66 | 67 | s3l_models[1] = s3l_models[0]; 68 | s3l_models[2] = s3l_models[0]; 69 | 70 | s3l_models[2].transform.scale.x = ROOM_SIZE; 71 | s3l_models[2].transform.scale.y = ROOM_SIZE; 72 | s3l_models[2].transform.scale.z = ROOM_SIZE; 73 | s3l_models[2].config.backfaceCulling = 1; 74 | 75 | s3l_models[2].triangleCount -= 2; 76 | 77 | S3L_sceneInit(s3l_models,2,&s3l_scene); 78 | 79 | s3l_scene.camera.transform.rotation.x -= TPE_F / 16; 80 | 81 | TPE_makeBox(tpe_joints,tpe_connections, 82 | 2 * TPE_F,2 * TPE_F,2 * TPE_F,TPE_F); 83 | 84 | TPE_bodyInit(&tpe_bodies[0],tpe_joints,8,tpe_connections,16,TPE_F); 85 | 86 | TPE_makeBox(tpe_joints + 8,tpe_connections + 16, 87 | 2 * TPE_F,2 * TPE_F,2 * TPE_F,TPE_F); 88 | 89 | TPE_bodyInit(&tpe_bodies[1],tpe_joints + 8,8,tpe_connections + 16,16,TPE_F); 90 | 91 | TPE_bodyMoveBy(&tpe_bodies[1],TPE_vec3(4 * TPE_F,0,0)); 92 | 93 | TPE_worldInit(&tpe_world,tpe_bodies,2,tpe_environmentDistance); 94 | } 95 | 96 | uint8_t SAF_loop(void) 97 | { 98 | SAF_clearScreen(SAF_COLOR_GRAY); 99 | 100 | TPE_worldStep(&tpe_world); 101 | 102 | for (uint8_t i = 0; i < tpe_world.bodyCount; ++i) 103 | TPE_bodyApplyGravity(&tpe_world.bodies[i],TPE_F / 50); 104 | 105 | for (uint8_t i = 0; i < 2; ++i) 106 | { 107 | s3l_models[i].transform.translation.x = 108 | (tpe_bodies[i].joints[0].position.x + 109 | tpe_bodies[i].joints[7].position.x) / 2; 110 | 111 | s3l_models[i].transform.translation.y = 112 | (tpe_bodies[i].joints[0].position.y + 113 | tpe_bodies[i].joints[7].position.y) / 2; 114 | 115 | s3l_models[i].transform.translation.z = 116 | (tpe_bodies[i].joints[0].position.z + 117 | tpe_bodies[i].joints[7].position.z) / 2; 118 | 119 | TPE_Vec3 orient = TPE_bodyGetRotation(&tpe_bodies[i],0,1,2); 120 | 121 | s3l_models[i].transform.rotation.x = orient.x; 122 | s3l_models[i].transform.rotation.y = orient.y; 123 | s3l_models[i].transform.rotation.z = orient.z; 124 | } 125 | 126 | S3L_newFrame(); 127 | colorAdd = 19; 128 | s3l_scene.models = s3l_models + 2; 129 | s3l_scene.modelCount = 1; 130 | S3L_drawScene(s3l_scene); 131 | 132 | colorAdd = 0; 133 | S3L_newFrame(); 134 | s3l_scene.models = s3l_models; 135 | s3l_scene.modelCount = 2; 136 | S3L_drawScene(s3l_scene); 137 | 138 | if (SAF_buttonJustPressed(SAF_BUTTON_C)) 139 | debugDraw = !debugDraw; 140 | 141 | if (debugDraw) 142 | TPE_worldDebugDraw(&tpe_world,tpe_debugDrawPixel, 143 | TPE_vec3( 144 | s3l_scene.camera.transform.translation.x, 145 | s3l_scene.camera.transform.translation.y, 146 | s3l_scene.camera.transform.translation.z), 147 | TPE_vec3(s3l_scene.camera.transform.rotation.x,s3l_scene.camera.transform.rotation.y,0), 148 | TPE_vec3(SAF_SCREEN_WIDTH,SAF_SCREEN_HEIGHT,TPE_F),4,TPE_F); 149 | 150 | s3l_scene.camera.transform.translation = s3l_models[0].transform.translation; 151 | s3l_scene.camera.transform.translation.y += 3 * TPE_F; 152 | s3l_scene.camera.transform.translation.z -= 5 * TPE_F; 153 | 154 | #define ACC 20 155 | if (SAF_buttonPressed(SAF_BUTTON_LEFT)) 156 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(-1 * ACC,0,0)); 157 | else if (SAF_buttonPressed(SAF_BUTTON_RIGHT)) 158 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(ACC,0,0)); 159 | else if (SAF_buttonPressed(SAF_BUTTON_UP)) 160 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(0,0,ACC)); 161 | else if (SAF_buttonPressed(SAF_BUTTON_DOWN)) 162 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(0,0,-1 * ACC)); 163 | 164 | if (SAF_buttonJustPressed(SAF_BUTTON_A)) 165 | TPE_bodyAccelerate(&tpe_world.bodies[0],TPE_vec3(0,20 * ACC,0)); 166 | #undef ACC 167 | 168 | return 1; 169 | } 170 | -------------------------------------------------------------------------------- /programs/pokitto/tpe_pok.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/programs/pokitto/tpe_pok.gif -------------------------------------------------------------------------------- /programs/pokitto/tpe_pok2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/programs/pokitto/tpe_pok2.gif -------------------------------------------------------------------------------- /programs/shoot.c: -------------------------------------------------------------------------------- 1 | /** Tiny catapult Angry Birds like shooter demo. */ 2 | 3 | #include "helper.h" 4 | 5 | #define CATAPULT_HEIGHT (TPE_F * 2) 6 | #define CATAPULT_WIDTH (3 * TPE_F / 2) 7 | #define BALL_RADIUS (2 * TPE_F / 5) 8 | 9 | uint8_t collisionCallback(uint16_t b1, uint16_t j1, uint16_t b2, uint16_t j2, 10 | TPE_Vec3 p) 11 | { 12 | // disable collision between the catapult and ball 13 | return !((b1 == 0 && b2 == 1) || (b2 == 1 && b1 == 0)); 14 | } 15 | 16 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 17 | { 18 | return TPE_envGround(p,0); 19 | } 20 | 21 | TPE_Joint joints[3]; 22 | TPE_Connection connections[2]; 23 | TPE_Body catapult; 24 | 25 | uint8_t released = 0; 26 | uint8_t anythingHit = 0; 27 | 28 | int main(void) 29 | { 30 | helper_init(); 31 | 32 | tpe_world.collisionCallback = collisionCallback; 33 | 34 | tpe_world.environmentFunction = environmentDistance; 35 | 36 | s3l_scene.camera.transform.translation.x = -2032; 37 | s3l_scene.camera.transform.translation.y = 1838; 38 | s3l_scene.camera.transform.translation.z = -1705; 39 | s3l_scene.camera.transform.rotation.x = -5; 40 | s3l_scene.camera.transform.rotation.y = -102; 41 | 42 | joints[0] = TPE_joint(TPE_vec3(0,CATAPULT_HEIGHT,CATAPULT_WIDTH / 2),10); 43 | joints[1] = TPE_joint(TPE_vec3(0,CATAPULT_HEIGHT,0),10); 44 | joints[2] = TPE_joint(TPE_vec3(0,CATAPULT_HEIGHT,-1 * CATAPULT_WIDTH / 2),10); 45 | 46 | connections[0].joint1 = 0; 47 | connections[0].joint2 = 1; 48 | connections[1].joint1 = 1; 49 | connections[1].joint2 = 2; 50 | 51 | TPE_bodyInit(&tpe_bodies[0],joints,3,connections,2,10); 52 | 53 | joints[1].position.x -= 2 * TPE_F; 54 | joints[1].position.y -= TPE_F / 2; 55 | 56 | tpe_bodies[0].flags |= TPE_BODY_FLAG_SOFT; 57 | tpe_bodies[0].flags |= TPE_BODY_FLAG_SIMPLE_CONN; // this makes the string faster 58 | 59 | tpe_world.bodyCount++; 60 | 61 | helper_addBall(BALL_RADIUS,2 * TPE_F); 62 | 63 | helper_addCenterBox(TPE_F,TPE_F,3 * TPE_F / 2,2 * TPE_F / 5,TPE_F); 64 | helper_lastBody.joints[8].sizeDivided *= 2; 65 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(8 * TPE_F,6 * TPE_F / 5,-6 * TPE_F / 5)); 66 | TPE_bodyDeactivate(&helper_lastBody); 67 | 68 | helper_addCenterBox(TPE_F,TPE_F,3 * TPE_F / 2,2 * TPE_F / 5,TPE_F); 69 | helper_lastBody.joints[8].sizeDivided *= 2; 70 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(8 * TPE_F,6 * TPE_F / 5,6 * TPE_F / 5)); 71 | TPE_bodyDeactivate(&helper_lastBody); 72 | 73 | helper_addCenterRect(TPE_F,3 * TPE_F,TPE_F / 2,TPE_F); 74 | helper_lastBody.joints[4].sizeDivided *= 3; 75 | helper_lastBody.joints[4].sizeDivided /= 2; 76 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(8 * TPE_F,3 * TPE_F - TPE_F / 4,0)); 77 | TPE_bodyDeactivate(&helper_lastBody); 78 | 79 | helper_addCenterBox(6 * TPE_F / 5,6 * TPE_F / 5,2 * TPE_F - TPE_F / 6,TPE_F / 3,TPE_F); 80 | helper_lastBody.joints[8].sizeDivided *= 2; 81 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(8 * TPE_F,4 * TPE_F + TPE_F / 3,0)); 82 | TPE_bodyDeactivate(&helper_lastBody); 83 | 84 | while (helper_running) 85 | { 86 | helper_frameStart(); 87 | 88 | helper_cameraFreeMovement(); 89 | 90 | if (released) 91 | { 92 | TPE_worldStep(&tpe_world); 93 | TPE_jointPin(&joints[0],TPE_vec3(0,CATAPULT_HEIGHT,CATAPULT_WIDTH / 2)); 94 | TPE_jointPin(&joints[2],TPE_vec3(0,CATAPULT_HEIGHT,-1 * CATAPULT_WIDTH / 2)); 95 | 96 | for (int i = 1; i < tpe_world.bodyCount; ++i) 97 | TPE_bodyApplyGravity(&tpe_world.bodies[i],6); 98 | 99 | // make the string lose energy over time: 100 | TPE_bodyMultiplyNetSpeed(&tpe_world.bodies[0],(19 * TPE_F) / 20); 101 | } 102 | 103 | if (!released || tpe_world.bodies[1].joints[0].position.x < 0) 104 | { 105 | tpe_world.bodies[1].joints[0].position = joints[1].position; 106 | 107 | for (int i = 0; i < 3; ++i) 108 | tpe_world.bodies[1].joints[0].velocity[i] = joints[1].velocity[i]; 109 | } 110 | 111 | if (sdl_keyboard[SDL_SCANCODE_SPACE]) 112 | released = 1; 113 | 114 | if (!released) 115 | { 116 | #define OFFSET (TPE_F / 20) 117 | if (sdl_keyboard[SDL_SCANCODE_W]) 118 | joints[1].position.y += OFFSET; 119 | else if (sdl_keyboard[SDL_SCANCODE_S] && joints[1].position.y > 0) 120 | joints[1].position.y -= OFFSET; 121 | 122 | if (sdl_keyboard[SDL_SCANCODE_F]) 123 | joints[1].position.x -= OFFSET; 124 | else if (sdl_keyboard[SDL_SCANCODE_G] && joints[1].position.x < 5) 125 | joints[1].position.x += OFFSET; 126 | 127 | if (sdl_keyboard[SDL_SCANCODE_D]) 128 | joints[1].position.z -= OFFSET; 129 | else if (sdl_keyboard[SDL_SCANCODE_A]) 130 | joints[1].position.z += OFFSET; 131 | #undef OFFSET 132 | } 133 | else if (!anythingHit) 134 | { 135 | /* here we check if any of the tower bodies is active which means it has 136 | been hit in which case we activate all the bodies so that they do their 137 | thing and none stays hanging in the air :) */ 138 | for (int i = 2; i < tpe_world.bodyCount; ++i) 139 | { 140 | if (TPE_bodyIsActive(&tpe_world.bodies[i])) 141 | { 142 | puts("someting was hit, activating all bodies"); 143 | TPE_worldActivateAll(&tpe_world); 144 | anythingHit = 1; 145 | break; 146 | } 147 | } 148 | } 149 | 150 | helper_set3DColor(200,200,255); 151 | 152 | helper_draw3DSphere(tpe_world.bodies[1].joints[0].position,TPE_vec3(BALL_RADIUS,BALL_RADIUS,BALL_RADIUS),TPE_vec3(0,0,0)); 153 | 154 | helper_set3DColor(200,255,200); 155 | 156 | helper_draw3DBox(TPE_bodyGetCenterOfMass(&tpe_world.bodies[2]),TPE_vec3(700,1000,700), 157 | TPE_bodyGetRotation(&tpe_world.bodies[2],0,1,2)); 158 | 159 | helper_draw3DBox(TPE_bodyGetCenterOfMass(&tpe_world.bodies[3]),TPE_vec3(700,1000,700), 160 | TPE_bodyGetRotation(&tpe_world.bodies[3],0,1,2)); 161 | 162 | helper_draw3DBox(TPE_bodyGetCenterOfMass(&tpe_world.bodies[4]),TPE_vec3(2000,500,700), 163 | TPE_bodyGetRotation(&tpe_world.bodies[4],0,1,2)); 164 | 165 | helper_draw3DBox(TPE_bodyGetCenterOfMass(&tpe_world.bodies[5]),TPE_vec3(800,1100,800), 166 | TPE_bodyGetRotation(&tpe_world.bodies[5],0,1,2)); 167 | 168 | helper_drawLine3D(TPE_vec3(0,0,0),TPE_vec3(0,CATAPULT_HEIGHT / 2,0),255,0,0 ); 169 | helper_drawLine3D(TPE_vec3(0,CATAPULT_HEIGHT / 2,0),TPE_vec3(0,CATAPULT_HEIGHT,-1 * CATAPULT_WIDTH / 2),255,0,0); 170 | helper_drawLine3D(TPE_vec3(0,CATAPULT_HEIGHT / 2,0),TPE_vec3(0,CATAPULT_HEIGHT,CATAPULT_WIDTH / 2),255,0,0); 171 | helper_drawLine3D(TPE_vec3(0,CATAPULT_HEIGHT,-1 * CATAPULT_WIDTH / 2),joints[1].position,0,255,0); 172 | helper_drawLine3D(TPE_vec3(0,CATAPULT_HEIGHT,CATAPULT_WIDTH / 2),joints[1].position,0,255,0); 173 | 174 | if (helper_debugDrawOn) 175 | helper_debugDraw(1); 176 | 177 | helper_frameEnd(); 178 | } 179 | 180 | helper_end(); 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /programs/stack.c: -------------------------------------------------------------------------------- 1 | /** Program that drops many bodies so that they stack onto each other to test 2 | this kind of behavior as well as a performance during it, also applies some 3 | basic smoothing of movement and rotation. */ 4 | 5 | //#define FPS 10 6 | 7 | #include "helper.h" 8 | 9 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 10 | { 11 | TPE_ENV_START( TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(TPE_F / 2,TPE_F / 2,0)),p ) 12 | TPE_ENV_NEXT( TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(-1 * TPE_F / 2,TPE_F / 2,-1 * TPE_F / 2)),p ) 13 | TPE_ENV_NEXT( TPE_envHalfPlane(p,TPE_vec3(0,0,0),TPE_vec3(-1 * TPE_F / 2,TPE_F / 2,TPE_F / 2)),p ) 14 | TPE_ENV_END 15 | } 16 | 17 | unsigned long timeMeasure = 0; 18 | 19 | TPE_Vec3 bodyPositions[16]; 20 | TPE_Vec3 bodyOrientations[16]; 21 | 22 | // tries to smooth orientation by averaging it over 2 frames 23 | TPE_Unit updateOrientation(TPE_Unit new, TPE_Unit old) 24 | { 25 | return TPE_abs(new - old) < 20 ? (new + old) / 2 : new; 26 | } 27 | 28 | int main(void) 29 | { 30 | helper_init(); 31 | 32 | tpe_world.environmentFunction = environmentDistance; 33 | 34 | s3l_scene.camera.transform.translation.y = 6000; 35 | s3l_scene.camera.transform.translation.z = -2000; 36 | s3l_scene.camera.transform.rotation.x = -70; 37 | 38 | for (int i = 0; i < 16; ++i) 39 | { 40 | switch (i % 5) 41 | { 42 | case 0: helper_addBox(800,800,800,400,700); break; 43 | case 1: helper_addTriangle(1100,200,600); break; 44 | case 2: helper_addBall(500,700); break; 45 | case 3: helper_addRect(800,800,400,800); break; 46 | case 4: helper_add2Line(900,200,600); break; 47 | default: break; 48 | } 49 | 50 | TPE_bodyMoveBy(&tpe_world.bodies[tpe_world.bodyCount - 1],TPE_vec3((1 - (i % 4)) * 1200,8000,(2 - (i / 4)) * 1200)); 51 | } 52 | 53 | while (helper_running) 54 | { 55 | helper_frameStart(); 56 | 57 | helper_cameraFreeMovement(); 58 | 59 | if (helper_frame % 16 == 0) 60 | { 61 | printf("frame %d:\n",helper_frame); 62 | 63 | helper_printCPU(); 64 | 65 | if (sdl_keyboard[SDL_SCANCODE_L]) 66 | for (int i = 0; i < tpe_world.bodyCount; ++i) 67 | { 68 | TPE_bodyActivate(&tpe_world.bodies[i]); 69 | TPE_bodyAccelerate(&tpe_world.bodies[i], 70 | TPE_vec3(0,(500 * 30) / FPS,0)); 71 | } 72 | 73 | printf("world update (us): %lu\n",timeMeasure / 16); 74 | printf("hash: %lu\n",TPE_worldHash(&tpe_world)); 75 | 76 | timeMeasure = 0; 77 | } 78 | 79 | unsigned long t1 = helper_getMicroSecs(); 80 | 81 | TPE_worldStep(&tpe_world); 82 | 83 | timeMeasure += helper_getMicroSecs() - t1; 84 | 85 | for (int i = 0; i < tpe_world.bodyCount; ++i) 86 | { 87 | TPE_bodyApplyGravity(&tpe_world.bodies[i],(5 * 30) / FPS); 88 | 89 | TPE_Joint *joints = tpe_world.bodies[i].joints; 90 | TPE_Vec3 pos = TPE_bodyGetCenterOfMass(&tpe_world.bodies[i]); 91 | TPE_Vec3 right = TPE_vec3(512,0,0); 92 | TPE_Vec3 forw = TPE_vec3(0,0,512); 93 | 94 | if (i % 5 != 2 && i % 5 != 1) // don't mind the ugly code 95 | { 96 | if (i % 5 != 4) 97 | { 98 | forw = TPE_vec3Minus(joints[2].position,joints[0].position); 99 | right = TPE_vec3Minus(joints[1].position,joints[0].position); 100 | } 101 | else 102 | forw = TPE_vec3Minus(joints[1].position,joints[0].position); 103 | } 104 | 105 | TPE_Vec3 orient = TPE_rotationFromVecs(forw,right); 106 | 107 | helper_set3DColor(100 + i * 5,16 - i,100 - i * 5); 108 | 109 | // this smoothes the movement a bit: 110 | bodyPositions[i] = TPE_vec3KeepWithinBox(bodyPositions[i], 111 | TPE_bodyGetCenterOfMass(&tpe_world.bodies[i]),TPE_vec3(20,20,20)); 112 | 113 | bodyOrientations[i].x = updateOrientation(orient.x,bodyOrientations[i].x); 114 | bodyOrientations[i].y = updateOrientation(orient.y,bodyOrientations[i].y); 115 | bodyOrientations[i].z = updateOrientation(orient.z,bodyOrientations[i].z); 116 | 117 | switch (i % 5) 118 | { 119 | case 0: helper_draw3DBox(bodyPositions[i],TPE_vec3(1200,1200,1200),bodyOrientations[i]); break; 120 | case 1: helper_draw3DTriangle(bodyPositions[i],joints[1].position,joints[2].position); break; 121 | case 2: helper_draw3DSphere(bodyPositions[i],TPE_vec3(500,500,500),bodyOrientations[i]); break; 122 | case 3: helper_draw3DBox(bodyPositions[i],TPE_vec3(1200,400,1200),bodyOrientations[i]); break; 123 | case 4: helper_draw3DBox(bodyPositions[i],TPE_vec3(200,200,1200),bodyOrientations[i]); break; 124 | default: break; 125 | } 126 | } 127 | 128 | helper_set3DColor(100,100,100); 129 | helper_draw3DTriangle(TPE_vec3(0,0,0),TPE_vec3(-5000,5000,-10000),TPE_vec3(-5000,5000,10000)); 130 | helper_set3DColor(140,140,140); 131 | helper_draw3DTriangle(TPE_vec3(0,0,0),TPE_vec3(-5000,5000,10000),TPE_vec3(5000,5000,0)); 132 | helper_set3DColor(80,80,80); 133 | helper_draw3DTriangle(TPE_vec3(0,0,0),TPE_vec3(-5000,5000,-10000),TPE_vec3(5000,5000,0)); 134 | 135 | if (helper_debugDrawOn) 136 | helper_debugDraw(1); 137 | 138 | helper_frameEnd(); 139 | } 140 | 141 | helper_end(); 142 | 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /programs/test.c: -------------------------------------------------------------------------------- 1 | /** General automatic test for tinyphysicsengine, this should always be run 2 | and passed before publishing a new version of TPE. */ 3 | 4 | #include "../tinyphysicsengine.h" 5 | #include 6 | 7 | #define ass(cond,text) { printf("[CHECKING] " text ": "); if (!(cond)) { puts("ERROR"); return 1; } else puts("OK"); } 8 | 9 | TPE_Unit rampPoits[6] = 10 | { 11 | 0,0, 12 | -2400,1400, 13 | -2400,0 14 | }; 15 | 16 | TPE_Vec3 envFunc(TPE_Vec3 p, TPE_Unit maxD) 17 | { 18 | TPE_ENV_START( TPE_envAABoxInside(p,TPE_vec3(0,1000,0),TPE_vec3(7000,6000,7000)),p ) 19 | TPE_ENV_NEXT( TPE_envAATriPrism(p,TPE_vec3(100,200,-10),rampPoits,3000,2),p) 20 | TPE_ENV_NEXT( TPE_envBox(p,TPE_vec3(30,200,-10),TPE_vec3(500,600,700),TPE_vec3(10,20,30)), p) 21 | TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(-100,300,200),TPE_vec3(500,600,700)), p) 22 | TPE_ENV_END 23 | } 24 | 25 | TPE_Vec3 envFunc2(TPE_Vec3 p, TPE_Unit maxD) 26 | { 27 | TPE_ENV_START( TPE_envSphereInside(p,TPE_vec3(100,20,-3),5000), p) 28 | TPE_ENV_NEXT( TPE_envGround(p,-500), p) 29 | 30 | if (TPE_ENV_BCUBE_TEST(p,maxD,TPE_vec3(300,-40,50),1100) ) 31 | { 32 | TPE_ENV_NEXT( TPE_envCylinder(p,TPE_vec3(300,-40,50), TPE_vec3(-400,-200,-50), 751), p) 33 | } 34 | 35 | TPE_ENV_NEXT( TPE_envCylinder(p,TPE_vec3(-500,500,-20),TPE_vec3(-400,600,700),500), p) 36 | 37 | TPE_ENV_END 38 | } 39 | 40 | TPE_Vec3 envFuncBad(TPE_Vec3 p, TPE_Unit maxD) 41 | { 42 | p.x += 200; 43 | return p; 44 | } 45 | 46 | TPE_Vec3 envFuncBad2(TPE_Vec3 p, TPE_Unit maxD) 47 | { 48 | if (p.y > p.x) 49 | p.x = p.y; 50 | 51 | return p; 52 | } 53 | 54 | TPE_Vec3 envSimple(TPE_Vec3 p, TPE_Unit maxD) 55 | { 56 | TPE_ENV_START( TPE_envAABoxInside(p,TPE_vec3(0,0,0),TPE_vec3(10000,10000,10000)), p) 57 | TPE_ENV_NEXT( TPE_envHalfPlane(p,TPE_vec3(0,-5000,0),TPE_vec3(500,500,0)), p) 58 | TPE_ENV_END 59 | } 60 | 61 | TPE_Unit heightMap(int32_t x, int32_t y) 62 | { 63 | x *= 16; 64 | y *= 16; 65 | 66 | return 67 | TPE_sin(x + TPE_cos(y * 2)) * TPE_sin(y * 2 + TPE_cos(x * 4)) / 68 | (TPE_FRACTIONS_PER_UNIT / 2); 69 | } 70 | 71 | TPE_Vec3 envFuncHeightmap(TPE_Vec3 p, TPE_Unit maxD) 72 | { 73 | return TPE_envHeightmap(p,TPE_vec3(10,20,30),500,heightMap,maxD); 74 | } 75 | 76 | int main(void) 77 | { 78 | puts("== testing tinyphysicsengine =="); 79 | 80 | { 81 | ass(TPE_vec2Angle(-100,0) == TPE_FRACTIONS_PER_UNIT / 2,"vec2 angle") 82 | 83 | TPE_Unit l; 84 | 85 | l = TPE_LENGTH(TPE_vec3Normalized(TPE_vec3(100,0,0))); 86 | ass(TPE_abs(l - TPE_FRACTIONS_PER_UNIT) < 5,"vec3 normalize") 87 | 88 | l = TPE_LENGTH(TPE_vec3Normalized(TPE_vec3(0,0,0))); 89 | ass(TPE_abs(l - TPE_FRACTIONS_PER_UNIT) < 5,"zero vec3 normalize") 90 | 91 | l = TPE_LENGTH(TPE_vec3Normalized(TPE_vec3(0,-1,0))); 92 | ass(TPE_abs(l - TPE_FRACTIONS_PER_UNIT) < 5,"small vec3 normalize") 93 | 94 | l = TPE_LENGTH(TPE_vec3Normalized(TPE_vec3(500000,300000,-700000))); 95 | 96 | ass(TPE_abs(l - TPE_FRACTIONS_PER_UNIT) < 5,"big vec3 normalize") 97 | } 98 | 99 | { 100 | TPE_Joint joints[16]; 101 | TPE_Connection cons[16]; 102 | 103 | joints[0] = TPE_joint(TPE_vec3(200,100,-400),300); 104 | joints[1] = TPE_joint(TPE_vec3(100,200,-400),300); 105 | joints[2] = TPE_joint(TPE_vec3(200,-400,200),300); 106 | joints[3] = TPE_joint(TPE_vec3(200,100,-400),400); 107 | 108 | cons[0].joint1 = 0; cons[0].joint2 = 1; cons[0].length = 1000; 109 | cons[1].joint1 = 1; cons[1].joint2 = 0; cons[1].length = 1000; 110 | cons[2].joint1 = 0; cons[2].joint2 = 1; cons[2].length = 1100; 111 | cons[3].joint1 = 2; cons[3].joint2 = 3; cons[3].length = 100; 112 | 113 | uint32_t jHashes[4], cHashes[4]; 114 | 115 | for (int i = 0; i < 4; ++i) 116 | { 117 | jHashes[i] = TPE_jointHash(&joints[i]); 118 | cHashes[i] = TPE_connectionHash(&cons[i]); 119 | } 120 | 121 | for (int i = 0; i < 4; ++i) 122 | for (int j = i + 1; j < 4; ++j) 123 | ass(jHashes[i] != jHashes[j] && cHashes[i] != cHashes[j],"joints/connection hash"); 124 | 125 | TPE_Body bodies[8]; 126 | uint32_t bHashes[4]; 127 | 128 | TPE_bodyInit(&bodies[0],joints,4,cons,4,300); 129 | TPE_bodyInit(&bodies[1],joints + 1,3,cons,4,300); 130 | TPE_bodyInit(&bodies[2],joints,4,cons,4,300); 131 | bodies[2].flags |= TPE_BODY_FLAG_SOFT | TPE_BODY_FLAG_NONROTATING; 132 | TPE_bodyInit(&bodies[3],joints,4,cons,4,200); 133 | 134 | for (int i = 0; i < 4; ++i) 135 | bHashes[i] = TPE_bodyHash(&bodies[i]); 136 | 137 | for (int i = 0; i < 4; ++i) 138 | for (int j = i + 1; j < 4; ++j) 139 | ass(bHashes[i] != bHashes[j],"body hash"); 140 | 141 | TPE_World world; 142 | 143 | uint32_t wHashes[4]; 144 | 145 | TPE_worldInit(&world,bodies,4,0); 146 | 147 | wHashes[0] = TPE_worldHash(&world); 148 | bodies[0].jointCount--; 149 | wHashes[1] = TPE_worldHash(&world); 150 | bodies[4] = bodies[0]; bodies[0] = bodies[3]; bodies[3] = bodies[4]; 151 | wHashes[2] = TPE_worldHash(&world); 152 | world.bodyCount--; 153 | wHashes[3] = TPE_worldHash(&world); 154 | 155 | for (int i = 0; i < 4; ++i) 156 | for (int j = i + 1; j < 4; ++j) 157 | ass(wHashes[i] != wHashes[j],"world hash"); 158 | } 159 | 160 | { 161 | puts("-- environment functions --"); 162 | 163 | ass(TPE_testClosestPointFunction(envFunc,TPE_vec3(-3000,-3000,-3000), 164 | TPE_vec3(3000,3000,3000),32,40,0),"env function"); 165 | 166 | ass(TPE_testClosestPointFunction(envFunc2,TPE_vec3(-2000,-1000,-5000), 167 | TPE_vec3(5000,6000,7000),32,30,0),"env function"); 168 | 169 | ass(TPE_testClosestPointFunction(envFuncHeightmap, 170 | TPE_vec3(-2000,-1000,-5000),TPE_vec3(4000,6000,7000),6,50,0), 171 | "env function (heightmap)"); 172 | 173 | ass(!TPE_testClosestPointFunction(envFuncBad,TPE_vec3(-1000,-1000,-1000), 174 | TPE_vec3(2000,3000,100),32,40,0),"env function bad"); 175 | 176 | ass(!TPE_testClosestPointFunction(envFuncBad2,TPE_vec3(-1000,-2000,-200), 177 | TPE_vec3(1000,1000,2000),32,40,0),"env function bad"); 178 | 179 | } 180 | 181 | { 182 | puts("-- simulation --"); 183 | 184 | TPE_World w; 185 | TPE_Joint j[64]; 186 | TPE_Connection c[128]; 187 | TPE_Body b[4]; 188 | 189 | j[0] = TPE_joint(TPE_vec3(0,0,0),320); 190 | TPE_bodyInit(&b[0],j,1,0,0,2124); 191 | TPE_bodyMoveBy(&b[0],TPE_vec3(-2000,3000,3000)); 192 | 193 | TPE_makeBox(j + 1,c,800,900,850,320); 194 | TPE_bodyInit(&b[1],j + 1,8,c,16,1300); 195 | b[1].friction = 400; 196 | b[1].elasticity = 350; 197 | TPE_bodyMoveBy(&b[1],TPE_vec3(-2000,3800,1500)); 198 | 199 | TPE_make2Line(j + 20,c + 32,1000,300); 200 | TPE_bodyInit(&b[2],j + 20,2,c + 32,1,1300); 201 | b[2].flags |= TPE_BODY_FLAG_NONROTATING; 202 | TPE_bodyMoveBy(&b[2],TPE_vec3(-3000,4100,-1500)); 203 | 204 | TPE_makeCenterBox(j + 32,c + 64,600,500,510,300); 205 | TPE_bodyInit(&b[3],j + 32,9,c + 64,18,1200); 206 | b[3].flags |= TPE_BODY_FLAG_SOFT; 207 | TPE_bodyMoveBy(&b[3],TPE_vec3(-1500,3500,-3000)); 208 | 209 | TPE_worldInit(&w,b,4,envSimple); 210 | 211 | int16_t bi; 212 | 213 | TPE_castBodyRay(TPE_vec3(-1857,3743,-4800),TPE_vec3(0,0,100),-1,&w,&bi,0); 214 | 215 | ass(bi >= 0,"body ray hit"); 216 | 217 | puts("dropping bodies onto a ramp..."); 218 | 219 | for (int i = 0; i < 300; ++i) 220 | { 221 | for (uint8_t j = 0; j < w.bodyCount; ++j) 222 | TPE_bodyApplyGravity(&w.bodies[j],8); 223 | 224 | if (i == 100) 225 | ass(TPE_worldGetNetSpeed(&w) > 100,"world net speed"); 226 | 227 | TPE_worldStep(&w); 228 | } 229 | 230 | puts("simulation finished"); 231 | 232 | uint32_t hash = TPE_worldHash(&w); 233 | 234 | printf("world hash: %lu\n",hash); 235 | 236 | // change the hash if functionality changes: 237 | 238 | ass(hash == 3411004027,"world hash"); 239 | 240 | for (int i = 0; i < w.bodyCount; ++i) 241 | { 242 | ass(TPE_bodyGetCenterOfMass(&w.bodies[i]).x > 0,"x position > 0"); 243 | ass(w.bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED,"deactivated?"); 244 | } 245 | 246 | ass(TPE_worldGetNetSpeed(&w) < 100,"world net speed"); 247 | 248 | TPE_bodyAccelerate(&w.bodies[0],TPE_vec3(200,300,-20)); 249 | TPE_bodyAccelerate(&w.bodies[1],TPE_vec3(-700,400,0)); 250 | TPE_bodyAccelerate(&w.bodies[2],TPE_vec3(20,-300,-100)); 251 | TPE_bodyAccelerate(&w.bodies[3],TPE_vec3(0,30,-900)); 252 | 253 | puts("exploding bodies..."); 254 | 255 | for (int i = 0; i < 100; ++i) 256 | { 257 | for (uint8_t j = 0; j < w.bodyCount; ++j) 258 | TPE_bodyApplyGravity(&w.bodies[j],8); 259 | 260 | TPE_worldStep(&w); 261 | } 262 | 263 | // check if within environment 264 | 265 | for (int i = 0; i < w.bodyCount; ++i) 266 | { 267 | TPE_Vec3 p = TPE_bodyGetCenterOfMass(&w.bodies[i]); 268 | 269 | ass(p.x < 5000 && p.x > -5000 && p.y < 5000 && p.y > -5000 && 270 | p.z < 5000 && p.z > -5000,"body within environment"); 271 | } 272 | } 273 | 274 | { 275 | /* Here we'll be casting environment rays and checking if inside rays return 276 | inside results and outside rays return outside results. The function doesn't 277 | guarantee this but at least mostly this should hold. */ 278 | 279 | int correct = 0; 280 | int incorrect = 0; 281 | 282 | for (int y = -3200; y < 1000; y += 52) 283 | for (int x = -4000; x < 3000; x += 40) 284 | { 285 | TPE_Vec3 p = TPE_vec3(x,y,x / 16); 286 | TPE_Vec3 p2 = envFunc(p,300); 287 | 288 | uint8_t inside = p.x == p2.x && p.y == p2.y && p.z == p2.z; 289 | 290 | TPE_Vec3 p3 = TPE_castEnvironmentRay(p,TPE_vec3(x / 64 + 1,y / 32 -2,3), 291 | envFunc,30,100,32); 292 | 293 | p2 = envFunc(p3,300); 294 | 295 | uint8_t inside2 = p3.x == p2.x && p3.y == p2.y && p3.z == p2.z; 296 | 297 | if (inside != inside2) 298 | incorrect++; 299 | else 300 | correct++; 301 | } 302 | 303 | ass(correct > incorrect * 5,"environment rays behave kind of OK?"); 304 | printf("(%d OK, %d not so OK)\n",correct,incorrect); 305 | 306 | } 307 | 308 | puts("DONE, all OK"); 309 | 310 | return 0; 311 | } 312 | -------------------------------------------------------------------------------- /programs/testGeneral.c: -------------------------------------------------------------------------------- 1 | /** A bigger testing playground, just to see that everyhing works OK. The code 2 | is not very nice :) */ 3 | 4 | #define CAMERA_STEP 200 5 | 6 | #include "helper.h" 7 | 8 | TPE_Unit 9 | ramp1[6] = { 5000,0, 5000,5000, 0,0 }, 10 | ramp2[6] = { 5000,0, 5000,3000, 0,0 }, 11 | ramp3[6] = { 5000,0, 5000,1500, 0,0 }, 12 | impale[6] = { 1500,0, 0,6000, -1500,0 }; 13 | 14 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 15 | { 16 | TPE_ENV_START( TPE_envGround(p,0), p) 17 | TPE_ENV_NEXT ( TPE_envAATriPrism(p,TPE_vec3(1000,0,0),ramp1,5000,2), p) 18 | TPE_ENV_NEXT ( TPE_envAATriPrism(p,TPE_vec3(-4000,0,0),ramp2,3500,2), p) 19 | TPE_ENV_NEXT ( TPE_envAATriPrism(p,TPE_vec3(-7500,0,0),ramp3,3500,2), p) 20 | TPE_ENV_NEXT ( TPE_envAATriPrism(p,TPE_vec3(8000,0,-2400),impale,3500,2), p) 21 | TPE_ENV_END 22 | } 23 | 24 | uint8_t simulating = 0; 25 | 26 | TPE_Body *controlledBody; 27 | 28 | int main(void) 29 | { 30 | helper_init(); 31 | 32 | puts("press P to start"); 33 | 34 | helper_debugDrawOn = 1; 35 | 36 | tpe_world.environmentFunction = environmentDistance; 37 | 38 | s3l_scene.camera.transform.translation.z = -3000; 39 | s3l_scene.camera.transform.translation.y = 2000; 40 | s3l_scene.camera.transform.translation.x = 0; 41 | s3l_scene.camera.transform.rotation.y = TPE_F / 16; 42 | 43 | #define addBody(x,y,z,f) \ 44 | helper_addBox(700,700,700,300,500); \ 45 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(x,y,z)); \ 46 | helper_lastBody.elasticity = 255; \ 47 | helper_lastBody.friction = f; 48 | 49 | // cubes on ramps: 50 | 51 | addBody(1800,5200,4000,0) 52 | addBody(400,5200,4000,128) 53 | addBody(-1100,5200,4000,511) 54 | addBody(-2800,3600,4000,300) 55 | addBody(-4300,3600,4000,10) 56 | addBody(-6700,2000,4000,300) 57 | addBody(-8400,2000,4000,10) 58 | 59 | addBody(7500,6200,-2400,10) // impaled cube 60 | 61 | #define addBody(x,y,z,e) \ 62 | helper_addRect(700,700,300,500); \ 63 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(x,y,z)); \ 64 | helper_lastBody.elasticity = e; 65 | 66 | // falling bodies 67 | 68 | addBody(7000,5000,0,511) 69 | addBody(8800,5000,0,255) 70 | addBody(10600,5000,0,0) 71 | 72 | helper_addBox(5000,5000,5000,2000,2000); // big box 73 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(-20000,10000,8000)); 74 | TPE_bodySpin(&helper_lastBody,TPE_vec3(100,200,-20)); 75 | 76 | // two colliding spheres: 77 | 78 | helper_addBall(800,2000); 79 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(200,4000,-4800)); 80 | helper_lastBody.elasticity = 255; 81 | helper_lastBody.friction = 255; 82 | helper_lastBody.joints[0].velocity[0] = 10; 83 | 84 | helper_addBall(800,200); 85 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(3200,3800,-4800)); 86 | helper_lastBody.elasticity = 255; 87 | helper_lastBody.friction = 255; 88 | helper_lastBody.joints[0].velocity[0] = -300; 89 | 90 | helper_addBall(1000,1000); 91 | controlledBody = &helper_lastBody; 92 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(200,2000,-3000)); 93 | 94 | // two colliding bodies: 95 | 96 | helper_addCenterBox(700,700,700,300,500); 97 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(-4000,5000,-3000)); 98 | helper_lastBody.elasticity = 255; 99 | helper_lastBody.friction = 255; 100 | //helper_lastBody.flags |= TPE_BODY_FLAG_SOFT; 101 | TPE_bodyAccelerate(&helper_lastBody,TPE_vec3(-300,0,0)); 102 | 103 | helper_addCenterBox(700,700,700,300,500); 104 | TPE_bodyMoveBy(&helper_lastBody,TPE_vec3(-8000,5000,-3000)); 105 | helper_lastBody.elasticity = 255; 106 | helper_lastBody.friction = 255; 107 | helper_lastBody.flags |= TPE_BODY_FLAG_SOFT; 108 | TPE_bodyAccelerate(&helper_lastBody,TPE_vec3(300,0,0)); 109 | 110 | while (helper_running) 111 | { 112 | helper_frameStart(); 113 | 114 | helper_cameraFreeMovement(); 115 | 116 | if (simulating) 117 | { 118 | TPE_worldStep(&tpe_world); 119 | 120 | for (int i = 0; i < 12; ++i) 121 | TPE_bodyApplyGravity(&tpe_world.bodies[i],5); 122 | } 123 | 124 | if (helper_debugDrawOn) 125 | helper_debugDraw(1); 126 | 127 | if (sdl_keyboard[SDL_SCANCODE_P]) 128 | simulating = 1; 129 | 130 | TPE_bodyMultiplyNetSpeed(controlledBody,255); 131 | 132 | #define ACC 100 133 | if (sdl_keyboard[SDL_SCANCODE_W]) 134 | TPE_bodyAccelerate(controlledBody,TPE_vec3(0,0,ACC)); 135 | else if (sdl_keyboard[SDL_SCANCODE_S]) 136 | TPE_bodyAccelerate(controlledBody,TPE_vec3(0,0,-1 * ACC)); 137 | else if (sdl_keyboard[SDL_SCANCODE_D]) 138 | TPE_bodyAccelerate(controlledBody,TPE_vec3(ACC,0,0)); 139 | else if (sdl_keyboard[SDL_SCANCODE_A]) 140 | TPE_bodyAccelerate(controlledBody,TPE_vec3(-1 * ACC,0,0)); 141 | else if (sdl_keyboard[SDL_SCANCODE_C]) 142 | TPE_bodyAccelerate(controlledBody,TPE_vec3(0,ACC,0)); 143 | else if (sdl_keyboard[SDL_SCANCODE_X]) 144 | TPE_bodyAccelerate(controlledBody,TPE_vec3(0,-1 * ACC,0)); 145 | #undef ACC 146 | 147 | helper_frameEnd(); 148 | } 149 | 150 | helper_end(); 151 | 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /programs/water.c: -------------------------------------------------------------------------------- 1 | #define CAMERA_STEP (TPE_F / 2) 2 | #define JOINT_SIZE (TPE_F / 4) 3 | #define BALL_SIZE (5 * TPE_F / 4) 4 | 5 | #define HEIGHTMAP_3D_RESOLUTION 8 6 | #define HEIGHTMAP_3D_STEP (TPE_F * 2) 7 | 8 | #include "helper.h" 9 | 10 | #define ROOM_SIZE (HEIGHTMAP_3D_RESOLUTION * HEIGHTMAP_3D_STEP + JOINT_SIZE) 11 | 12 | TPE_Vec3 environmentDistance(TPE_Vec3 p, TPE_Unit maxD) 13 | { 14 | return TPE_envAABoxInside(p,TPE_vec3(0,0,0),TPE_vec3(ROOM_SIZE,ROOM_SIZE,ROOM_SIZE)); 15 | } 16 | 17 | #define WATER_JOINTS (HEIGHTMAP_3D_RESOLUTION * HEIGHTMAP_3D_RESOLUTION) 18 | #define WATER_CONNECTIONS (2 * ((HEIGHTMAP_3D_RESOLUTION - 1) * HEIGHTMAP_3D_RESOLUTION)) 19 | 20 | TPE_Joint joints[WATER_JOINTS + 1]; 21 | TPE_Connection connections[WATER_CONNECTIONS]; 22 | TPE_Body bodies[2]; 23 | 24 | int main(void) 25 | { 26 | helper_init(); 27 | 28 | puts("WSAD, XC: move the ball"); 29 | 30 | s3l_scene.camera.transform.translation.z = -6 * TPE_F; 31 | s3l_scene.camera.transform.translation.y = 4 * TPE_F; 32 | s3l_scene.camera.transform.translation.x = 0; 33 | s3l_scene.camera.transform.rotation.y = TPE_F / 16; 34 | 35 | // build the water body: 36 | 37 | for (int i = 0; i < HEIGHTMAP_3D_POINTS; ++i) 38 | joints[i] = TPE_joint(helper_heightmapPointLocation(i),JOINT_SIZE); 39 | 40 | int index = 0; 41 | 42 | for (int j = 0; j < HEIGHTMAP_3D_RESOLUTION; ++j) 43 | for (int i = 0; i < HEIGHTMAP_3D_RESOLUTION - 1; ++i) 44 | { 45 | connections[index].joint1 = j * HEIGHTMAP_3D_RESOLUTION + i; 46 | connections[index].joint2 = connections[index].joint1 + 1; 47 | 48 | index++; 49 | 50 | connections[index].joint1 = i * HEIGHTMAP_3D_RESOLUTION + j; 51 | connections[index].joint2 = connections[index].joint1 + HEIGHTMAP_3D_RESOLUTION; 52 | 53 | index++; 54 | } 55 | 56 | TPE_bodyInit(&bodies[0],joints,WATER_JOINTS,connections,WATER_CONNECTIONS, 57 | 2 * TPE_F); 58 | 59 | bodies[0].flags |= TPE_BODY_FLAG_SOFT; 60 | bodies[0].flags |= TPE_BODY_FLAG_ALWAYS_ACTIVE; 61 | 62 | // create the ball body: 63 | joints[WATER_JOINTS] = TPE_joint(TPE_vec3(0,0,ROOM_SIZE / 4),BALL_SIZE); 64 | TPE_bodyInit(&bodies[1],joints + WATER_JOINTS,1,connections,0,200); 65 | 66 | bodies[1].flags |= TPE_BODY_FLAG_ALWAYS_ACTIVE; 67 | 68 | TPE_worldInit(&tpe_world,bodies,2,environmentDistance); 69 | 70 | while (helper_running) 71 | { 72 | helper_frameStart(); 73 | 74 | helper_cameraFreeMovement(); 75 | 76 | // update the 3D model vertex positions: 77 | 78 | S3L_Unit *v = heightmapVertices; 79 | 80 | for (int i = 0; i < WATER_JOINTS; ++i) 81 | { 82 | *v = joints[i].position.x; 83 | v++; 84 | *v = joints[i].position.y; 85 | v++; 86 | *v = joints[i].position.z; 87 | v++; 88 | } 89 | 90 | // pin the joints at the edges of the grid: 91 | 92 | for (int index = 0; index < WATER_JOINTS; ++index) 93 | if (index % HEIGHTMAP_3D_RESOLUTION == 0 || index % HEIGHTMAP_3D_RESOLUTION == HEIGHTMAP_3D_RESOLUTION - 1 || 94 | index / HEIGHTMAP_3D_RESOLUTION == 0 || index / HEIGHTMAP_3D_RESOLUTION == HEIGHTMAP_3D_RESOLUTION - 1) 95 | TPE_jointPin(&joints[index],helper_heightmapPointLocation(index)); 96 | 97 | TPE_worldStep(&tpe_world); 98 | 99 | #define G ((5 * 30) / FPS) 100 | TPE_bodyApplyGravity(&tpe_world.bodies[1], 101 | bodies[1].joints[0].position.y > 0 ? G : (-2 * G)); 102 | 103 | #define ACC ((25 * 30) / FPS ) 104 | if (sdl_keyboard[SDL_SCANCODE_W]) 105 | TPE_bodyAccelerate(&bodies[1],TPE_vec3(0,0,ACC)); 106 | else if (sdl_keyboard[SDL_SCANCODE_S]) 107 | TPE_bodyAccelerate(&bodies[1],TPE_vec3(0,0,-1 * ACC)); 108 | else if (sdl_keyboard[SDL_SCANCODE_D]) 109 | TPE_bodyAccelerate(&bodies[1],TPE_vec3(ACC,0,0)); 110 | else if (sdl_keyboard[SDL_SCANCODE_A]) 111 | TPE_bodyAccelerate(&bodies[1],TPE_vec3(-1 * ACC,0,0)); 112 | else if (sdl_keyboard[SDL_SCANCODE_C]) 113 | TPE_bodyAccelerate(&bodies[1],TPE_vec3(0,ACC,0)); 114 | else if (sdl_keyboard[SDL_SCANCODE_X]) 115 | TPE_bodyAccelerate(&bodies[1],TPE_vec3(0,-1 * ACC,0)); 116 | 117 | helper_set3DColor(255,0,0); 118 | helper_draw3DSphere(bodies[1].joints[0].position,TPE_vec3(BALL_SIZE,BALL_SIZE,BALL_SIZE),TPE_vec3(0,0,0)); 119 | helper_set3DColor(0,100,255); 120 | helper_drawModel(&heightmapModel,TPE_vec3(0,0,0),TPE_vec3(TPE_F,TPE_F,TPE_F),TPE_vec3(0,0,0)); 121 | 122 | if (helper_debugDrawOn) 123 | helper_debugDraw(1); 124 | 125 | helper_frameEnd(); 126 | } 127 | 128 | helper_end(); 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /tpe1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/tpe1.gif -------------------------------------------------------------------------------- /tpe2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/tpe2.gif -------------------------------------------------------------------------------- /tpe3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/tpe3.gif -------------------------------------------------------------------------------- /tpe4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/tpe4.gif -------------------------------------------------------------------------------- /tpe5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/tpe5.gif -------------------------------------------------------------------------------- /tpe6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/tpe6.gif -------------------------------------------------------------------------------- /tpe7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordan4ibanez/tinyphysicsengine/f8b8e45838368e464e88df5172fa98010b0375fa/tpe7.gif --------------------------------------------------------------------------------