From fca3f24e31b3bbbe81ce0ef00da901480a2a92cc Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Mon, 27 Dec 2021 01:40:29 +0200 Subject: [PATCH] added cgmath, libanim, and libpsys --- Makefile | 8 +- Makefile.android | 4 +- libs/Makefile | 21 +- libs/anim/COPYING | 674 ++++++++++++++++++++++++++++++ libs/anim/COPYING.LESSER | 165 ++++++++ libs/anim/Makefile | 29 ++ libs/anim/README.md | 48 +++ libs/anim/anim.c | 1017 ++++++++++++++++++++++++++++++++++++++++++++++ libs/anim/anim.h | 252 ++++++++++++ libs/anim/config.h | 6 + libs/anim/dynarr.c | 140 +++++++ libs/anim/dynarr.h | 34 ++ libs/anim/track.c | 334 +++++++++++++++ libs/anim/track.h | 119 ++++++ libs/cgmath/LICENSE | 20 + libs/cgmath/README.md | 29 ++ libs/cgmath/cgmath.h | 263 ++++++++++++ libs/cgmath/cgmmat.inl | 623 ++++++++++++++++++++++++++++ libs/cgmath/cgmmisc.inl | 203 +++++++++ libs/cgmath/cgmquat.inl | 159 ++++++++ libs/cgmath/cgmray.inl | 39 ++ libs/cgmath/cgmvec3.inl | 205 ++++++++++ libs/cgmath/cgmvec4.inl | 158 +++++++ libs/psys/Makefile | 28 ++ libs/psys/pattr.c | 447 ++++++++++++++++++++ libs/psys/pattr.h | 84 ++++ libs/psys/pstrack.c | 139 +++++++ libs/psys/pstrack.h | 66 +++ libs/psys/psys.c | 370 +++++++++++++++++ libs/psys/psys.h | 140 +++++++ libs/psys/rndval.c | 134 ++++++ libs/psys/rndval.h | 69 ++++ 32 files changed, 6020 insertions(+), 7 deletions(-) create mode 100644 libs/anim/COPYING create mode 100644 libs/anim/COPYING.LESSER create mode 100644 libs/anim/Makefile create mode 100644 libs/anim/README.md create mode 100644 libs/anim/anim.c create mode 100644 libs/anim/anim.h create mode 100644 libs/anim/config.h create mode 100644 libs/anim/dynarr.c create mode 100644 libs/anim/dynarr.h create mode 100644 libs/anim/track.c create mode 100644 libs/anim/track.h create mode 100644 libs/cgmath/LICENSE create mode 100644 libs/cgmath/README.md create mode 100644 libs/cgmath/cgmath.h create mode 100644 libs/cgmath/cgmmat.inl create mode 100644 libs/cgmath/cgmmisc.inl create mode 100644 libs/cgmath/cgmquat.inl create mode 100644 libs/cgmath/cgmray.inl create mode 100644 libs/cgmath/cgmvec3.inl create mode 100644 libs/cgmath/cgmvec4.inl create mode 100644 libs/psys/Makefile create mode 100644 libs/psys/pattr.c create mode 100644 libs/psys/pattr.h create mode 100644 libs/psys/pstrack.c create mode 100644 libs/psys/pstrack.h create mode 100644 libs/psys/psys.c create mode 100644 libs/psys/psys.h create mode 100644 libs/psys/rndval.c create mode 100644 libs/psys/rndval.h diff --git a/Makefile b/Makefile index 718b7dd..12024ae 100644 --- a/Makefile +++ b/Makefile @@ -7,11 +7,11 @@ warn = -pedantic -Wall dbg = -g #opt = -O3 -ffast-math -fno-strict-aliasing def = -DMINIGLUT_USE_LIBC -DGLEW_STATIC -incdir = -Isrc -Ilibs/imago/src -Ilibs/glew +incdir = -Isrc -Ilibs -Ilibs/imago/src -Ilibs/glew libdir = -Llibs/unix CFLAGS = $(warn) $(dbg) $(opt) $(def) $(incdir) -fcommon -MMD -LDFLAGS = $(libdir) $(libsys) $(libgl) -lm -limago +LDFLAGS = $(libdir) $(libsys) $(libgl) -limago -lpsys -lanim $(libc) sys ?= $(shell uname -s | sed 's/MINGW.*/mingw/') ifeq ($(sys), mingw) @@ -20,11 +20,13 @@ ifeq ($(sys), mingw) libgl = -lopengl32 libsys = -lmingw32 -lgdi32 -lwinmm -mconsole libdir = -Llibs/w32 + libc = -lm else libgl = -lGL -lX11 -lXext + libc = -lm -ldl endif -$(bin): $(obj) +$(bin): $(obj) Makefile $(CC) -o $@ $(obj) $(LDFLAGS) -include $(dep) diff --git a/Makefile.android b/Makefile.android index f3b75eb..336d7c9 100644 --- a/Makefile.android +++ b/Makefile.android @@ -20,12 +20,12 @@ warn = -pedantic -Wall dbg = -g opt = -O3 -ffast-math -fno-strict-aliasing def = -DGLDEF -incdir = -Isrc -Ilibs/imago/src +incdir = -Isrc -Ilibs -Ilibs/imago/src libdir = -Llibs/android CC = $(TC)clang CFLAGS = $(CCSYSROOT) $(ISYS) $(warn) $(dbg) $(opt) $(def) $(incdir) -fPIC -fcommon -MMD -LDFLAGS = $(LDSYSROOT) $(libdir) -lm -landroid -llog -lEGL -lGLESv2 -limago +LDFLAGS = $(LDSYSROOT) $(libdir) -lm -landroid -llog -lEGL -lGLESv2 -limago -lpsys -lanim $(name).apk: $(name).aligned.apk keystore.jks apksigner sign --ks keystore.jks --ks-key-alias androidkey --ks-pass pass:android --key-pass pass:android --out $@ $< diff --git a/libs/Makefile b/libs/Makefile index ed4d4d9..0800384 100644 --- a/libs/Makefile +++ b/libs/Makefile @@ -1,8 +1,9 @@ .PHONY: all -all: imago +all: imago anim psys .PHONY: clean -clean: clean-imago +clean: clean-imago clean-anim clean-psys + .PHONY: imago imago: @@ -11,3 +12,19 @@ imago: .PHONY: clean-imago clean-imago: $(MAKE) -C imago clean + +.PHONY: anim +anim: + $(MAKE) -C anim + +.PHONY: clean-anim +clean-anim: + $(MAKE) -C anim clean + +.PHONY: psys +psys: + $(MAKE) -C psys + +.PHONY: clean-psys +clean-psys: + $(MAKE) -C psys clean diff --git a/libs/anim/COPYING b/libs/anim/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/libs/anim/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/libs/anim/COPYING.LESSER b/libs/anim/COPYING.LESSER new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/libs/anim/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/libs/anim/Makefile b/libs/anim/Makefile new file mode 100644 index 0000000..7e142dd --- /dev/null +++ b/libs/anim/Makefile @@ -0,0 +1,29 @@ +obj = anim.o track.o dynarr.o +lib = ../unix/libanim.a + +sys ?= $(shell uname -s | sed 's/MINGW.*/mingw/') +ifeq ($(sys), mingw) + obj = anim.w32.o track.w32.o dynarr.w32.o + lib = ../w32/libanim.a +endif +ifeq ($(sys), android-arm64) + obj = anim.arm64.o track.arm64.o dynarr.arm64.o + lib = ../android/libanim.a +endif + +CFLAGS = -O3 -ffast-math -fno-strict-aliasing -I.. + +$(lib): $(obj) + +$(lib): $(obj) + $(AR) rcs $@ $(obj) + +%.arm64.o: %.c + $(CC) -o $@ $(CFLAGS) -c $< + +%.w32.o: %.c + $(CC) -o $@ $(CFLAGS) -c $< + +.PHONY: clean +clean: + rm -f $(obj) $(lib) diff --git a/libs/anim/README.md b/libs/anim/README.md new file mode 100644 index 0000000..ddcb2b9 --- /dev/null +++ b/libs/anim/README.md @@ -0,0 +1,48 @@ +libanim +======= + +About +----- +Libanim is a C animation library, which can be used as a generic framework to +add keyframe interpolation tracks for any single-valued or 3-vector parameters, +or at a slightly higher level as a hierarchical PRS (position/rotation/scaling) +animation node framework for 3D graphics programs. + +Version 2 of libanim dropped the dependency to libvmath, and instead carries a +copy of gph-cmath (https://github.com/jtsiomb/gph-cmath) internally. The API +has been reworked to avoid forcing a dependency to any math library to the user +program, relying on floats and float pointers instead, which can be aliased to +any kind contiguous `x,y,z` vector and `x,y,z,w` quaternion, or simple arrays +of floats. Matrix arguments are expected to be arrays of 16 contiguous floats, +in OpenGL-compatible order. + +Programs written for earlier versions of libanim, and using the high-level PRS +interface in `anim.h` are not source-compatible, nor binary-compatible with +libanim 2. Though in practice the API changes are minor, and porting should be +straightforward. Programs using only the low-level keyframe tracks in `track.h` +are unaffected by these changes. + +License +------- +Copyright (C) 2012-2018 John Tsiombikas + +This program is free software. You may use, modify, and redistribute it under +the terms of the GNU Lesser General Public License v3 or (at your option), any +later version published by the Free Software Foundation. See COPYING and +COPYING.LESSER for details. + +Build +----- +To build and install libanim on UNIX, run the usual: + + ./configure + make + make install + +See `./configure --help` for a complete list of build-time options. + +To cross-compile for windows with mingw-w64, try the following incantation: + + ./configure --prefix=/usr/i686-w64-mingw32 + make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar sys=mingw + make install sys=mingw diff --git a/libs/anim/anim.c b/libs/anim/anim.c new file mode 100644 index 0000000..06514e2 --- /dev/null +++ b/libs/anim/anim.c @@ -0,0 +1,1017 @@ +/* +libanim - hierarchical keyframe animation library +Copyright (C) 2012-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include "anim.h" +#include "dynarr.h" + +#include "cgmath/cgmath.h" + +#define ROT_USE_SLERP + +static void invalidate_cache(struct anm_node *node); + +int anm_init_animation(struct anm_animation *anim) +{ + int i, j; + static const float defaults[] = { + 0.0f, 0.0f, 0.0f, /* default position */ + 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */ + 1.0f, 1.0f, 1.0f /* default scale factor */ + }; + + anim->name = 0; + + for(i=0; itracks + i) == -1) { + for(j=0; jtracks + i); + } + } + anm_set_track_default(anim->tracks + i, defaults[i]); + } + return 0; +} + +void anm_destroy_animation(struct anm_animation *anim) +{ + int i; + for(i=0; itracks + i); + } + free(anim->name); +} + +void anm_set_animation_name(struct anm_animation *anim, const char *name) +{ + char *newname = malloc(strlen(name) + 1); + if(!newname) return; + + strcpy(newname, name); + + free(anim->name); + anim->name = newname; +} + +/* ---- node implementation ----- */ + +int anm_init_node(struct anm_node *node) +{ + memset(node, 0, sizeof *node); + + node->cur_anim[1] = -1; + + if(!(node->animations = anm_dynarr_alloc(1, sizeof *node->animations))) { + return -1; + } + if(anm_init_animation(node->animations) == -1) { + anm_dynarr_free(node->animations); + return -1; + } + +#ifdef ANIM_THREAD_SAFE + /* initialize thread-local matrix cache */ + pthread_key_create(&node->cache_key, 0); + pthread_mutex_init(&node->cache_list_lock, 0); +#endif + + return 0; +} + +void anm_destroy_node(struct anm_node *node) +{ + int i, num_anim; + free(node->name); + + num_anim = anm_get_animation_count(node); + for(i=0; ianimations + i); + } + anm_dynarr_free(node->animations); + +#ifdef ANIM_THREAD_SAFE + /* destroy thread-specific cache */ + pthread_key_delete(node->cache_key); + + while(node->cache_list) { + struct mat_cache *tmp = node->cache_list; + node->cache_list = tmp->next; + free(tmp); + } +#endif +} + +void anm_destroy_node_tree(struct anm_node *tree) +{ + struct anm_node *c, *tmp; + + if(!tree) return; + + c = tree->child; + while(c) { + tmp = c; + c = c->next; + + anm_destroy_node_tree(tmp); + } + anm_destroy_node(tree); +} + +struct anm_node *anm_create_node(void) +{ + struct anm_node *n; + + if((n = malloc(sizeof *n))) { + if(anm_init_node(n) == -1) { + free(n); + return 0; + } + } + return n; +} + +void anm_free_node(struct anm_node *node) +{ + anm_destroy_node(node); + free(node); +} + +void anm_free_node_tree(struct anm_node *tree) +{ + struct anm_node *c, *tmp; + + if(!tree) return; + + c = tree->child; + while(c) { + tmp = c; + c = c->next; + + anm_free_node_tree(tmp); + } + + anm_free_node(tree); +} + +int anm_set_node_name(struct anm_node *node, const char *name) +{ + char *str; + + if(!(str = malloc(strlen(name) + 1))) { + return -1; + } + strcpy(str, name); + free(node->name); + node->name = str; + return 0; +} + +const char *anm_get_node_name(struct anm_node *node) +{ + return node->name ? node->name : ""; +} + +void anm_link_node(struct anm_node *p, struct anm_node *c) +{ + c->next = p->child; + p->child = c; + + c->parent = p; + invalidate_cache(c); +} + +int anm_unlink_node(struct anm_node *p, struct anm_node *c) +{ + struct anm_node *iter; + + if(p->child == c) { + p->child = c->next; + c->next = 0; + invalidate_cache(c); + return 0; + } + + iter = p->child; + while(iter->next) { + if(iter->next == c) { + iter->next = c->next; + c->next = 0; + invalidate_cache(c); + return 0; + } + } + return -1; +} + +void anm_set_pivot(struct anm_node *node, float x, float y, float z) +{ + node->pivot[0] = x; + node->pivot[1] = y; + node->pivot[2] = z; +} + +void anm_get_pivot(struct anm_node *node, float *x, float *y, float *z) +{ + *x = node->pivot[0]; + *y = node->pivot[1]; + *z = node->pivot[2]; +} + + +/* animation management */ + +int anm_use_node_animation(struct anm_node *node, int aidx) +{ + if(aidx == node->cur_anim[0] && node->cur_anim[1] == -1) { + return 0; /* no change, no invalidation */ + } + + if(aidx < 0 || aidx >= anm_get_animation_count(node)) { + return -1; + } + + node->cur_anim[0] = aidx; + node->cur_anim[1] = -1; + node->cur_mix = 0; + node->blend_dur = -1; + + invalidate_cache(node); + return 0; +} + +int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t) +{ + int num_anim; + + if(node->cur_anim[0] == aidx && node->cur_anim[1] == bidx && + fabs(t - node->cur_mix) < 1e-6) { + return 0; /* no change, no invalidation */ + } + + num_anim = anm_get_animation_count(node); + if(aidx < 0 || aidx >= num_anim) { + return anm_use_animation(node, bidx); + } + if(bidx < 0 || bidx >= num_anim) { + return anm_use_animation(node, aidx); + } + node->cur_anim[0] = aidx; + node->cur_anim[1] = bidx; + node->cur_mix = t; + + invalidate_cache(node); + return 0; +} + +int anm_use_animation(struct anm_node *node, int aidx) +{ + struct anm_node *child; + + if(anm_use_node_animation(node, aidx) == -1) { + return -1; + } + + child = node->child; + while(child) { + if(anm_use_animation(child, aidx) == -1) { + return -1; + } + child = child->next; + } + return 0; +} + +int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t) +{ + struct anm_node *child; + + if(anm_use_node_animations(node, aidx, bidx, t) == -1) { + return -1; + } + + child = node->child; + while(child) { + if(anm_use_animations(child, aidx, bidx, t) == -1) { + return -1; + } + child = child->next; + } + return 0; + +} + +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which) +{ + if(which < 0 || which >= 2) { + return; + } + node->cur_anim_offset[which] = offs; +} + +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which) +{ + if(which < 0 || which >= 2) { + return 0; + } + return node->cur_anim_offset[which]; +} + +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which) +{ + struct anm_node *c = node->child; + while(c) { + anm_set_animation_offset(c, offs, which); + c = c->next; + } + + anm_set_node_animation_offset(node, offs, which); +} + +int anm_get_active_animation_index(const struct anm_node *node, int which) +{ + if(which < 0 || which >= 2) return -1; + return node->cur_anim[which]; +} + +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which) +{ + int idx = anm_get_active_animation_index(node, which); + if(idx < 0 || idx >= anm_get_animation_count(node)) { + return 0; + } + return node->animations + idx; +} + +float anm_get_active_animation_mix(const struct anm_node *node) +{ + return node->cur_mix; +} + +int anm_get_animation_count(const struct anm_node *node) +{ + return anm_dynarr_size(node->animations); +} + +int anm_add_node_animation(struct anm_node *node) +{ + struct anm_animation newanim; + anm_init_animation(&newanim); + + node->animations = anm_dynarr_push(node->animations, &newanim); + return 0; +} + +int anm_remove_node_animation(struct anm_node *node, int idx) +{ + fprintf(stderr, "anm_remove_animation: unimplemented!"); + abort(); + return 0; +} + +int anm_add_animation(struct anm_node *node) +{ + struct anm_node *child; + + if(anm_add_node_animation(node) == -1) { + return -1; + } + + child = node->child; + while(child) { + if(anm_add_animation(child)) { + return -1; + } + child = child->next; + } + return 0; +} + +int anm_remove_animation(struct anm_node *node, int idx) +{ + struct anm_node *child; + + if(anm_remove_node_animation(node, idx) == -1) { + return -1; + } + + child = node->child; + while(child) { + if(anm_remove_animation(child, idx) == -1) { + return -1; + } + child = child->next; + } + return 0; +} + +struct anm_animation *anm_get_animation(struct anm_node *node, int idx) +{ + if(idx < 0 || idx > anm_get_animation_count(node)) { + return 0; + } + return node->animations + idx; +} + +struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name) +{ + return anm_get_animation(node, anm_find_animation(node, name)); +} + +int anm_find_animation(struct anm_node *node, const char *name) +{ + int i, count = anm_get_animation_count(node); + for(i=0; ianimations[i].name, name) == 0) { + return i; + } + } + return -1; +} + +/* all the rest act on the current animation(s) */ + +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) +{ + int i; + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + for(i=0; itracks + i, in); + } + invalidate_cache(node); +} + +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) +{ + int i; + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + for(i=0; itracks + i, ex); + } + invalidate_cache(node); +} + +void anm_set_node_active_animation_name(struct anm_node *node, const char *name) +{ + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + anm_set_animation_name(anim, name); +} + +void anm_set_active_animation_name(struct anm_node *node, const char *name) +{ + struct anm_node *child; + + anm_set_node_active_animation_name(node, name); + + child = node->child; + while(child) { + anm_set_active_animation_name(child, name); + child = child->next; + } +} + +const char *anm_get_active_animation_name(struct anm_node *node) +{ + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(anim) { + return anim->name; + } + return 0; +} + +/* ---- high level animation blending ---- */ +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur) +{ + struct anm_node *c = node->child; + + if(anmidx == node->cur_anim[0]) { + return; + } + + while(c) { + anm_transition(c, anmidx, start, dur); + c = c->next; + } + + anm_node_transition(node, anmidx, start, dur); +} + +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur) +{ + if(anmidx == node->cur_anim[0]) { + return; + } + + node->cur_anim[1] = anmidx; + node->cur_anim_offset[1] = start; + node->blend_dur = dur; +} + + +#define BLEND_START_TM node->cur_anim_offset[1] + +static anm_time_t animation_time(struct anm_node *node, anm_time_t tm, int which) +{ + float t; + + if(node->blend_dur >= 0) { + /* we're in transition... */ + t = (float)(tm - BLEND_START_TM) / (float)node->blend_dur; + if(t < 0.0) t = 0.0; + + node->cur_mix = t; + + if(t > 1.0) { + /* switch completely over to the target animation and stop blending */ + anm_use_node_animation(node, node->cur_anim[1]); + node->cur_anim_offset[0] = node->cur_anim_offset[1]; + } + } + + return tm - node->cur_anim_offset[which]; +} + + +void anm_set_position(struct anm_node *node, const float *pos, anm_time_t tm) +{ + anm_set_position3f(node, pos[0], pos[1], pos[2], tm); +} + +void anm_set_position3f(struct anm_node *node, float x, float y, float z, anm_time_t tm) +{ + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + anm_set_value(anim->tracks + ANM_TRACK_POS_X, tm, x); + anm_set_value(anim->tracks + ANM_TRACK_POS_Y, tm, y); + anm_set_value(anim->tracks + ANM_TRACK_POS_Z, tm, z); + invalidate_cache(node); +} + +void anm_get_node_position(struct anm_node *node, float *pos, anm_time_t tm) +{ + anm_time_t tm0 = animation_time(node, tm, 0); + struct anm_animation *anim0 = anm_get_active_animation(node, 0); + struct anm_animation *anim1 = anm_get_active_animation(node, 1); + + if(!anim0) { + pos[0] = pos[1] = pos[2] = 0.0f; + return; + } + + pos[0] = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm0); + pos[1] = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm0); + pos[2] = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm0); + + if(anim1) { + anm_time_t tm1 = animation_time(node, tm, 1); + float x1 = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm1); + float y1 = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm1); + float z1 = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm1); + + pos[0] = pos[0] + (x1 - pos[0]) * node->cur_mix; + pos[1] = pos[1] + (y1 - pos[1]) * node->cur_mix; + pos[2] = pos[2] + (z1 - pos[2]) * node->cur_mix; + } +} + +void anm_set_rotation(struct anm_node *node, const float *qrot, anm_time_t tm) +{ + anm_set_rotation4f(node, qrot[0], qrot[1], qrot[2], qrot[3], tm); +} + +void anm_set_rotation4f(struct anm_node *node, float x, float y, float z, float w, anm_time_t tm) +{ + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + anm_set_value(anim->tracks + ANM_TRACK_ROT_X, tm, x); + anm_set_value(anim->tracks + ANM_TRACK_ROT_Y, tm, y); + anm_set_value(anim->tracks + ANM_TRACK_ROT_Z, tm, z); + anm_set_value(anim->tracks + ANM_TRACK_ROT_W, tm, w); + invalidate_cache(node); +} + +void anm_set_rotation_axis(struct anm_node *node, float angle, float x, float y, float z, anm_time_t tm) +{ + cgm_quat q; + cgm_qrotation(&q, angle, x, y, z); + + anm_set_rotation(node, (float*)&q, tm); +} + +static void get_node_rotation(cgm_quat *qres, struct anm_node *node, anm_time_t tm, struct anm_animation *anim) +{ +#ifndef ROT_USE_SLERP + qres->x = anm_get_value(anim->tracks + ANM_TRACK_ROT_X, tm); + qres->y = anm_get_value(anim->tracks + ANM_TRACK_ROT_Y, tm); + qres->z = anm_get_value(anim->tracks + ANM_TRACK_ROT_Z, tm); + qres->w = anm_get_value(anim->tracks + ANM_TRACK_ROT_W, tm); +#else + int idx0, idx1, last_idx; + anm_time_t tstart, tend; + float t, dt; + struct anm_track *track_x, *track_y, *track_z, *track_w; + cgm_quat q1, q2; + + track_x = anim->tracks + ANM_TRACK_ROT_X; + track_y = anim->tracks + ANM_TRACK_ROT_Y; + track_z = anim->tracks + ANM_TRACK_ROT_Z; + track_w = anim->tracks + ANM_TRACK_ROT_W; + + if(!track_x->count) { + qres->x = track_x->def_val; + qres->y = track_y->def_val; + qres->z = track_z->def_val; + qres->w = track_w->def_val; + return; + } + + last_idx = track_x->count - 1; + + tstart = track_x->keys[0].time; + tend = track_x->keys[last_idx].time; + + if(tstart == tend) { + qres->x = track_x->keys[0].val; + qres->y = track_y->keys[0].val; + qres->z = track_z->keys[0].val; + qres->w = track_w->keys[0].val; + return; + } + + tm = anm_remap_time(track_x, tm, tstart, tend); + + idx0 = anm_get_key_interval(track_x, tm); + assert(idx0 >= 0 && idx0 < track_x->count); + idx1 = idx0 + 1; + + if(idx0 == last_idx) { + qres->x = track_x->keys[idx0].val; + qres->y = track_y->keys[idx0].val; + qres->z = track_z->keys[idx0].val; + qres->w = track_w->keys[idx0].val; + return; + } + + dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time); + t = (float)(tm - track_x->keys[idx0].time) / dt; + + q1.x = track_x->keys[idx0].val; + q1.y = track_y->keys[idx0].val; + q1.z = track_z->keys[idx0].val; + q1.w = track_w->keys[idx0].val; + + q2.x = track_x->keys[idx1].val; + q2.y = track_y->keys[idx1].val; + q2.z = track_z->keys[idx1].val; + q2.w = track_w->keys[idx1].val; + + cgm_qslerp(qres, &q1, &q2, t); +#endif +} + +//get_node_rotation(cgm_quat *qres, struct anm_node *node, anm_time_t tm, struct anm_animation *anim) +void anm_get_node_rotation(struct anm_node *node, float *qrot, anm_time_t tm) +{ + anm_time_t tm0 = animation_time(node, tm, 0); + struct anm_animation *anim0 = anm_get_active_animation(node, 0); + struct anm_animation *anim1 = anm_get_active_animation(node, 1); + + if(!anim0) { + qrot[0] = qrot[1] = qrot[2] = 0.0f; + qrot[3] = 1.0f; + return; + } + + + if(anim1) { + cgm_quat q0, q1; + anm_time_t tm1 = animation_time(node, tm, 1); + + get_node_rotation(&q0, node, tm0, anim0); + get_node_rotation(&q1, node, tm1, anim1); + + cgm_qslerp((cgm_quat*)qrot, &q0, &q1, node->cur_mix); + } else { + get_node_rotation((cgm_quat*)qrot, node, tm0, anim0); + } +} + +void anm_set_scaling(struct anm_node *node, const float *scale, anm_time_t tm) +{ + anm_set_scaling3f(node, scale[0], scale[1], scale[2], tm); +} + +void anm_set_scaling3f(struct anm_node *node, float x, float y, float z, anm_time_t tm) +{ + struct anm_animation *anim = anm_get_active_animation(node, 0); + if(!anim) return; + + anm_set_value(anim->tracks + ANM_TRACK_SCL_X, tm, x); + anm_set_value(anim->tracks + ANM_TRACK_SCL_Y, tm, y); + anm_set_value(anim->tracks + ANM_TRACK_SCL_Z, tm, z); + invalidate_cache(node); +} + +void anm_get_node_scaling(struct anm_node *node, float *scale, anm_time_t tm) +{ + anm_time_t tm0 = animation_time(node, tm, 0); + struct anm_animation *anim0 = anm_get_active_animation(node, 0); + struct anm_animation *anim1 = anm_get_active_animation(node, 1); + + if(!anim0) { + scale[0] = scale[1] = scale[2] = 1.0f; + return; + } + + scale[0] = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm0); + scale[1] = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm0); + scale[2] = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm0); + + if(anim1) { + anm_time_t tm1 = animation_time(node, tm, 1); + float x1 = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm1); + float y1 = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm1); + float z1 = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm1); + + scale[0] = scale[0] + (x1 - scale[0]) * node->cur_mix; + scale[1] = scale[1] + (y1 - scale[1]) * node->cur_mix; + scale[2] = scale[2] + (z1 - scale[2]) * node->cur_mix; + } +} + +void anm_get_position(struct anm_node *node, float *pos, anm_time_t tm) +{ + if(!node->parent) { + anm_get_node_position(node, pos, tm); + } else { + float xform[16]; + anm_get_matrix(node, xform, tm); + cgm_mget_translation(xform, (cgm_vec3*)pos); + } +} + +void anm_get_rotation(struct anm_node *node, float *qrot, anm_time_t tm) +{ + if(!node->parent) { + anm_get_node_rotation(node, qrot, tm); + } else { + cgm_quat rot; + anm_get_node_rotation(node, &rot.x, tm); + anm_get_rotation(node->parent, qrot, tm); + cgm_qmul((cgm_quat*)qrot, &rot); + } +} + +void anm_get_scaling(struct anm_node *node, float *scale, anm_time_t tm) +{ + anm_get_node_scaling(node, scale, tm); + if(node->parent) { + cgm_vec3 ps; + anm_get_scaling(node->parent, &ps.x, tm); + cgm_vmul((cgm_vec3*)scale, &ps); + } +} + +void anm_get_node_matrix(struct anm_node *node, float *mat, anm_time_t tm) +{ + int i; + float rmat[16]; + cgm_vec3 pos, scale; + cgm_quat rot; + + anm_get_node_position(node, &pos.x, tm); + anm_get_node_rotation(node, &rot.x, tm); + anm_get_node_scaling(node, &scale.x, tm); + + cgm_mtranslation(mat, node->pivot[0], node->pivot[1], node->pivot[2]); + cgm_mrotation_quat(rmat, &rot); + + /* + for(i=0; i<3; i++) { + mat[i * 4] = rmat[i * 4]; + mat[i * 4 + 1] = rmat[i * 4 + 1]; + mat[i * 4 + 2] = rmat[i * 4 + 2]; + } + */ + for(i=0; i<3; i++) { + mat[i] = rmat[i]; + mat[4 + i] = rmat[4 + i]; + mat[8 + i] = rmat[8 + i]; + } + + mat[0] *= scale.x; mat[4] *= scale.y; mat[8] *= scale.z; mat[12] += pos.x; + mat[1] *= scale.x; mat[5] *= scale.y; mat[9] *= scale.z; mat[13] += pos.y; + mat[2] *= scale.x; mat[6] *= scale.y; mat[10] *= scale.z; mat[14] += pos.z; + + cgm_mpretranslate(mat, -node->pivot[0], -node->pivot[1], -node->pivot[2]); + + /* that's basically: pivot * rotation * translation * scaling * -pivot */ +} + +void anm_get_node_inv_matrix(struct anm_node *node, float *mat, anm_time_t tm) +{ + anm_get_node_matrix(node, mat, tm); + cgm_minverse(mat); +} + +void anm_eval_node(struct anm_node *node, anm_time_t tm) +{ + anm_get_node_matrix(node, node->matrix, tm); +} + +void anm_eval(struct anm_node *node, anm_time_t tm) +{ + struct anm_node *c; + + anm_eval_node(node, tm); + + if(node->parent) { + /* due to post-order traversal, the parent matrix is already evaluated */ + cgm_mmul(node->matrix, node->parent->matrix); + } + + /* recersively evaluate all children */ + c = node->child; + while(c) { + anm_eval(c, tm); + c = c->next; + } +} + +float *anm_get_matrix(struct anm_node *node, float *mat, anm_time_t tm) +{ +#ifdef ANIM_THREAD_SAFE + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(!cache) { + cache = malloc(sizeof *cache); + assert(cache); + + pthread_mutex_lock(&node->cache_list_lock); + cache->next = node->cache_list; + node->cache_list = cache; + pthread_mutex_unlock(&node->cache_list_lock); + + cache->time = ANM_TIME_INVAL; + cache->inv_time = ANM_TIME_INVAL; + pthread_setspecific(node->cache_key, cache); + } +#else + struct mat_cache *cache = &node->cache; +#endif + + if(cache->time != tm) { + anm_get_node_matrix(node, cache->matrix, tm); + + if(node->parent) { + float parent_mat[16]; + + anm_get_matrix(node->parent, parent_mat, tm); + cgm_mmul(cache->matrix, parent_mat); + } + cache->time = tm; + } + + if(mat) { + cgm_mcopy(mat, cache->matrix); + } + return cache->matrix; +} + +float *anm_get_inv_matrix(struct anm_node *node, float *mat, anm_time_t tm) +{ +#ifdef ANIM_THREAD_SAFE + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(!cache) { + cache = malloc(sizeof *cache); + assert(cache); + + pthread_mutex_lock(&node->cache_list_lock); + cache->next = node->cache_list; + node->cache_list = cache; + pthread_mutex_unlock(&node->cache_list_lock); + + cache->inv_time = ANM_TIME_INVAL; + cache->inv_time = ANM_TIME_INVAL; + pthread_setspecific(node->cache_key, cache); + } +#else + struct mat_cache *cache = &node->cache; +#endif + + if(cache->inv_time != tm) { + anm_get_matrix(node, cache->inv_matrix, tm); + cgm_minverse(cache->inv_matrix); + cache->inv_time = tm; + } + + if(mat) { + cgm_mcopy(mat, cache->inv_matrix); + } + return cache->inv_matrix; +} + +anm_time_t anm_get_start_time(struct anm_node *node) +{ + int i, j; + struct anm_node *c; + anm_time_t res = LONG_MAX; + + for(j=0; j<2; j++) { + struct anm_animation *anim = anm_get_active_animation(node, j); + if(!anim) break; + + for(i=0; itracks[i].count) { + anm_time_t tm = anim->tracks[i].keys[0].time; + if(tm < res) { + res = tm; + } + } + } + } + + c = node->child; + while(c) { + anm_time_t tm = anm_get_start_time(c); + if(tm < res) { + res = tm; + } + c = c->next; + } + return res; +} + +anm_time_t anm_get_end_time(struct anm_node *node) +{ + int i, j; + struct anm_node *c; + anm_time_t res = LONG_MIN; + + for(j=0; j<2; j++) { + struct anm_animation *anim = anm_get_active_animation(node, j); + if(!anim) break; + + for(i=0; itracks[i].count) { + anm_time_t tm = anim->tracks[i].keys[anim->tracks[i].count - 1].time; + if(tm > res) { + res = tm; + } + } + } + } + + c = node->child; + while(c) { + anm_time_t tm = anm_get_end_time(c); + if(tm > res) { + res = tm; + } + c = c->next; + } + return res; +} + +static void invalidate_cache(struct anm_node *node) +{ + struct anm_node *c; + +#ifdef ANIM_THREAD_SAFE + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(cache) { + cache->time = cache->inv_time = ANM_TIME_INVAL; + } +#else + node->cache.time = node->cache.inv_time = ANM_TIME_INVAL; +#endif + + c = node->child; + while(c) { + invalidate_cache(c); + c = c->next; + } +} diff --git a/libs/anim/anim.h b/libs/anim/anim.h new file mode 100644 index 0000000..13d0348 --- /dev/null +++ b/libs/anim/anim.h @@ -0,0 +1,252 @@ +/* +libanim - hierarchical keyframe animation library +Copyright (C) 2012-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef LIBANIM_H_ +#define LIBANIM_H_ + +#include "config.h" + +#if _MSC_VER >= 1900 +#define _TIMESPEC_DEFINED +#endif + +#ifdef ANIM_THREAD_SAFE +#include +#endif + +#include "track.h" + +enum { + ANM_TRACK_POS_X, + ANM_TRACK_POS_Y, + ANM_TRACK_POS_Z, + + ANM_TRACK_ROT_X, + ANM_TRACK_ROT_Y, + ANM_TRACK_ROT_Z, + ANM_TRACK_ROT_W, + + ANM_TRACK_SCL_X, + ANM_TRACK_SCL_Y, + ANM_TRACK_SCL_Z, + + ANM_NUM_TRACKS +}; + +struct anm_animation { + char *name; + struct anm_track tracks[ANM_NUM_TRACKS]; +}; + +struct anm_node { + char *name; + + int cur_anim[2]; + anm_time_t cur_anim_offset[2]; + float cur_mix; + + /* high-level animation blending transition duration */ + anm_time_t blend_dur; + + struct anm_animation *animations; + float pivot[3]; + + /* matrix cache */ + struct mat_cache { + float matrix[16], inv_matrix[16]; + anm_time_t time, inv_time; + struct mat_cache *next; +#ifdef ANIM_THREAD_SAFE + } *cache_list; + pthread_key_t cache_key; + pthread_mutex_t cache_list_lock; +#else + } cache; +#endif + + /* matrix calculated by anm_eval functions (no locking, meant as a pre-pass) */ + float matrix[16]; + + struct anm_node *parent; + struct anm_node *child; + struct anm_node *next; + + void *data; /* user data pointer */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int anm_init_animation(struct anm_animation *anim); +void anm_destroy_animation(struct anm_animation *anim); + +void anm_set_animation_name(struct anm_animation *anim, const char *name); + + +/* ---- node/hierarchy management ---- */ + +/* node constructor and destructor */ +int anm_init_node(struct anm_node *node); +void anm_destroy_node(struct anm_node *node); + +/* recursively destroy an animation node tree */ +void anm_destroy_node_tree(struct anm_node *tree); + +/* helper functions to allocate/construct and destroy/free with + * a single call. They call anm_init_node and anm_destroy_node + * internally. + */ +struct anm_node *anm_create_node(void); +void anm_free_node(struct anm_node *node); + +/* recursively destroy and free the nodes of a node tree */ +void anm_free_node_tree(struct anm_node *tree); + +int anm_set_node_name(struct anm_node *node, const char *name); +const char *anm_get_node_name(struct anm_node *node); + +/* link and unlink nodes with parent/child relations */ +void anm_link_node(struct anm_node *parent, struct anm_node *child); +int anm_unlink_node(struct anm_node *parent, struct anm_node *child); + +void anm_set_pivot(struct anm_node *node, float x, float y, float z); +void anm_get_pivot(struct anm_node *node, float *x, float *y, float *z); + +/* ---- multiple animations and animation blending ---- */ + +/* set active animation(s) */ +int anm_use_node_animation(struct anm_node *node, int aidx); +int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t); +/* recursive variants */ +int anm_use_animation(struct anm_node *node, int aidx); +int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t); + +/* set/get current animation offset(s) */ +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which); +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which); +/* recursive variant */ +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which); + +/* returns the requested current animation index, which can be 0 or 1 */ +int anm_get_active_animation_index(const struct anm_node *node, int which); +/* returns the requested current animation, which can be 0 or 1 */ +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which); +float anm_get_active_animation_mix(const struct anm_node *node); + +int anm_get_animation_count(const struct anm_node *node); + +/* add/remove an animation to the specified node */ +int anm_add_node_animation(struct anm_node *node); +int anm_remove_node_animation(struct anm_node *node, int idx); + +/* add/remove an animation to the specified node and all it's descendants */ +int anm_add_animation(struct anm_node *node); +int anm_remove_animation(struct anm_node *node, int idx); + +struct anm_animation *anm_get_animation(struct anm_node *node, int idx); +struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name); + +int anm_find_animation(struct anm_node *node, const char *name); + +/* set the interpolator for the (first) currently active animation */ +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in); +/* set the extrapolator for the (first) currently active animation */ +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex); + +/* set the name of the currently active animation of this node only */ +void anm_set_node_active_animation_name(struct anm_node *node, const char *name); +/* recursively set the name of the currently active animation for this node + * and all it's descendants */ +void anm_set_active_animation_name(struct anm_node *node, const char *name); +/* get the name of the currently active animation of this node */ +const char *anm_get_active_animation_name(struct anm_node *node); + + +/* ---- high level animation blending interface ---- */ +/* XXX this convenience interface assumes monotonically increasing time values + * in all subsequent calls to anm_get_* and anm_eval_* functions. + * + * anmidx: index of the animation to transition to + * start: when to start the transition + * dur: transition duration + * + * sets up a transition from the current animation (cur_anim[0]) to another animation. + * at time start + dur, the transition will be completed, cur_anim[0] will be the new + * animation and cur_anim_offset[0] will be equal to start. + */ +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur); +/* non-recursive variant, acts on a single node (you probably DON'T want to use this) */ +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur); + + +/* ---- keyframes / PRS interpolation ---- */ + +void anm_set_position(struct anm_node *node, const float *pos, anm_time_t tm); +void anm_set_position3f(struct anm_node *node, float x, float y, float z, anm_time_t tm); +void anm_get_node_position(struct anm_node *node, float *pos, anm_time_t tm); + +void anm_set_rotation(struct anm_node *node, const float *qrot, anm_time_t tm); +void anm_set_rotation4f(struct anm_node *node, float x, float y, float z, float w, anm_time_t tm); +void anm_set_rotation_axis(struct anm_node *node, float angle, float x, float y, float z, anm_time_t tm); +void anm_get_node_rotation(struct anm_node *node, float *qrot, anm_time_t tm); + +void anm_set_scaling(struct anm_node *node, const float *scale, anm_time_t tm); +void anm_set_scaling3f(struct anm_node *node, float x, float y, float z, anm_time_t tm); +void anm_get_node_scaling(struct anm_node *node, float *scale, anm_time_t tm); + +/* these three return the full p/r/s taking hierarchy into account */ +void anm_get_position(struct anm_node *node, float *pos, anm_time_t tm); +void anm_get_rotation(struct anm_node *node, float *qrot, anm_time_t tm); +void anm_get_scaling(struct anm_node *node, float *scale, anm_time_t tm); + +/* those return the start and end times of the whole tree */ +anm_time_t anm_get_start_time(struct anm_node *node); +anm_time_t anm_get_end_time(struct anm_node *node); + + +/* ---- transformation matrices ---- */ + +/* these calculate the matrix and inverse matrix of this node alone */ +void anm_get_node_matrix(struct anm_node *node, float *mat, anm_time_t tm); +void anm_get_node_inv_matrix(struct anm_node *node, float *mat, anm_time_t tm); + +/* ---- top-down matrix calculation interface ---- */ + +/* calculate and set the matrix of this node */ +void anm_eval_node(struct anm_node *node, anm_time_t tm); +/* calculate and set the matrix of this node and all its children recursively */ +void anm_eval(struct anm_node *node, anm_time_t tm); + + +/* ---- bottom-up lazy matrix calculation interface ---- */ + +/* These calculate the matrix and inverse matrix of this node taking hierarchy + * into account. The results are cached in thread-specific storage and returned + * if there's no change in time or tracks from the last query... + * + * A pointer to the internal cached matrix is returned, and also if mat is not + * null, the matrix is copied there. + */ +float *anm_get_matrix(struct anm_node *node, float *mat, anm_time_t tm); +float *anm_get_inv_matrix(struct anm_node *node, float *mat, anm_time_t tm); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBANIM_H_ */ diff --git a/libs/anim/config.h b/libs/anim/config.h new file mode 100644 index 0000000..f00310c --- /dev/null +++ b/libs/anim/config.h @@ -0,0 +1,6 @@ +#ifndef ANIM_CONFIG_H_ +#define ANIM_CONFIG_H_ + +#undef ANIM_THREAD_SAFE + +#endif /* ANIM_CONFIG_H_ */ diff --git a/libs/anim/dynarr.c b/libs/anim/dynarr.c new file mode 100644 index 0000000..9a51f4c --- /dev/null +++ b/libs/anim/dynarr.c @@ -0,0 +1,140 @@ +/* +libanim - hierarchical keyframe animation library +Copyright (C) 2012-2014 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include "dynarr.h" + +/* The array descriptor keeps auxilliary information needed to manipulate + * the dynamic array. It's allocated adjacent to the array buffer. + */ +struct arrdesc { + int nelem, szelem; + int max_elem; + int bufsz; /* not including the descriptor */ +}; + +#define DESC(x) ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc))) + +void *anm_dynarr_alloc(int elem, int szelem) +{ + struct arrdesc *desc; + + if(!(desc = malloc(elem * szelem + sizeof *desc))) { + return 0; + } + desc->nelem = desc->max_elem = elem; + desc->szelem = szelem; + desc->bufsz = elem * szelem; + return (char*)desc + sizeof *desc; +} + +void anm_dynarr_free(void *da) +{ + if(da) { + free(DESC(da)); + } +} + +void *anm_dynarr_resize(void *da, int elem) +{ + int newsz; + void *tmp; + struct arrdesc *desc; + + if(!da) return 0; + desc = DESC(da); + + newsz = desc->szelem * elem; + + if(!(tmp = realloc(desc, newsz + sizeof *desc))) { + return 0; + } + desc = tmp; + + desc->nelem = desc->max_elem = elem; + desc->bufsz = newsz; + return (char*)desc + sizeof *desc; +} + +int anm_dynarr_empty(void *da) +{ + return DESC(da)->nelem ? 0 : 1; +} + +int anm_dynarr_size(void *da) +{ + return DESC(da)->nelem; +} + + +/* stack semantics */ +void *anm_dynarr_push(void *da, void *item) +{ + struct arrdesc *desc; + int nelem; + + desc = DESC(da); + nelem = desc->nelem; + + if(nelem >= desc->max_elem) { + /* need to resize */ + struct arrdesc *tmp; + int newsz = desc->max_elem ? desc->max_elem * 2 : 1; + + if(!(tmp = anm_dynarr_resize(da, newsz))) { + fprintf(stderr, "failed to resize\n"); + return da; + } + da = tmp; + desc = DESC(da); + desc->nelem = nelem; + } + + memcpy((char*)da + desc->nelem++ * desc->szelem, item, desc->szelem); + return da; +} + +void *anm_dynarr_pop(void *da) +{ + struct arrdesc *desc; + int nelem; + + desc = DESC(da); + nelem = desc->nelem; + + if(!nelem) return da; + + if(nelem <= desc->max_elem / 3) { + /* reclaim space */ + struct arrdesc *tmp; + int newsz = desc->max_elem / 2; + + if(!(tmp = anm_dynarr_resize(da, newsz))) { + fprintf(stderr, "failed to resize\n"); + return da; + } + da = tmp; + desc = DESC(da); + desc->nelem = nelem; + } + desc->nelem--; + + return da; +} diff --git a/libs/anim/dynarr.h b/libs/anim/dynarr.h new file mode 100644 index 0000000..3d8459f --- /dev/null +++ b/libs/anim/dynarr.h @@ -0,0 +1,34 @@ +/* +libanim - hierarchical keyframe animation library +Copyright (C) 2012-2014 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +#ifndef DYNARR_H_ +#define DYNARR_H_ + +void *anm_dynarr_alloc(int elem, int szelem); +void anm_dynarr_free(void *da); +void *anm_dynarr_resize(void *da, int elem); + +int anm_dynarr_empty(void *da); +int anm_dynarr_size(void *da); + +/* stack semantics */ +void *anm_dynarr_push(void *da, void *item); +void *anm_dynarr_pop(void *da); + + +#endif /* DYNARR_H_ */ diff --git a/libs/anim/track.c b/libs/anim/track.c new file mode 100644 index 0000000..1ffb7a8 --- /dev/null +++ b/libs/anim/track.c @@ -0,0 +1,334 @@ +/* +libanim - hierarchical keyframe animation library +Copyright (C) 2012-2015 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include "track.h" +#include "dynarr.h" + +static int keycmp(const void *a, const void *b); +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm); + +static float interp_step(float v0, float v1, float v2, float v3, float t); +static float interp_linear(float v0, float v1, float v2, float v3, float t); +static float interp_cubic(float v0, float v1, float v2, float v3, float t); + +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end); +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end); +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end); +static anm_time_t remap_pingpong(anm_time_t tm, anm_time_t start, anm_time_t end); + +/* XXX keep this in sync with enum anm_interpolator at track.h */ +static float (*interp[])(float, float, float, float, float) = { + interp_step, + interp_linear, + interp_cubic, + 0 +}; + +/* XXX keep this in sync with enum anm_extrapolator at track.h */ +static anm_time_t (*remap_time[])(anm_time_t, anm_time_t, anm_time_t) = { + remap_extend, + remap_clamp, + remap_repeat, + remap_pingpong, + 0 +}; + +int anm_init_track(struct anm_track *track) +{ + memset(track, 0, sizeof *track); + + if(!(track->keys = anm_dynarr_alloc(0, sizeof *track->keys))) { + return -1; + } + track->interp = ANM_INTERP_LINEAR; + track->extrap = ANM_EXTRAP_CLAMP; + return 0; +} + +void anm_destroy_track(struct anm_track *track) +{ + anm_dynarr_free(track->keys); +} + +struct anm_track *anm_create_track(void) +{ + struct anm_track *track; + + if((track = malloc(sizeof *track))) { + if(anm_init_track(track) == -1) { + free(track); + return 0; + } + } + return track; +} + +void anm_free_track(struct anm_track *track) +{ + anm_destroy_track(track); + free(track); +} + +void anm_copy_track(struct anm_track *dest, const struct anm_track *src) +{ + free(dest->name); + if(dest->keys) { + anm_dynarr_free(dest->keys); + } + + if(src->name) { + dest->name = malloc(strlen(src->name) + 1); + strcpy(dest->name, src->name); + } + + dest->count = src->count; + dest->keys = anm_dynarr_alloc(src->count, sizeof *dest->keys); + memcpy(dest->keys, src->keys, src->count * sizeof *dest->keys); + + dest->def_val = src->def_val; + dest->interp = src->interp; + dest->extrap = src->extrap; +} + +int anm_set_track_name(struct anm_track *track, const char *name) +{ + char *tmp; + + if(!(tmp = malloc(strlen(name) + 1))) { + return -1; + } + free(track->name); + track->name = tmp; + return 0; +} + +const char *anm_get_track_name(struct anm_track *track) +{ + return track->name; +} + +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in) +{ + track->interp = in; +} + +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex) +{ + track->extrap = ex; +} + +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end) +{ + return remap_time[track->extrap](tm, start, end); +} + +void anm_set_track_default(struct anm_track *track, float def) +{ + track->def_val = def; +} + +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key) +{ + int idx = anm_get_key_interval(track, key->time); + + /* if we got a valid keyframe index, compare them... */ + if(idx >= 0 && idx < track->count && keycmp(key, track->keys + idx) == 0) { + /* ... it's the same key, just update the value */ + track->keys[idx].val = key->val; + } else { + /* ... it's a new key, add it and re-sort them */ + void *tmp; + if(!(tmp = anm_dynarr_push(track->keys, key))) { + return -1; + } + track->keys = tmp; + /* TODO lazy qsort */ + qsort(track->keys, ++track->count, sizeof *track->keys, keycmp); + } + return 0; +} + +static int keycmp(const void *a, const void *b) +{ + return ((struct anm_keyframe*)a)->time - ((struct anm_keyframe*)b)->time; +} + +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx) +{ + if(idx < 0 || idx >= track->count) { + return 0; + } + return track->keys + idx; +} + +int anm_get_key_interval(struct anm_track *track, anm_time_t tm) +{ + int last; + + if(!track->count || tm < track->keys[0].time) { + return -1; + } + + last = track->count - 1; + if(tm > track->keys[last].time) { + return last; + } + + return find_prev_key(track->keys, 0, last, tm); +} + +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm) +{ + int mid; + + if(end - start <= 1) { + return start; + } + + mid = (start + end) / 2; + if(tm < arr[mid].time) { + return find_prev_key(arr, start, mid, tm); + } + if(tm > arr[mid].time) { + return find_prev_key(arr, mid, end, tm); + } + return mid; +} + +int anm_set_value(struct anm_track *track, anm_time_t tm, float val) +{ + struct anm_keyframe key; + key.time = tm; + key.val = val; + + return anm_set_keyframe(track, &key); +} + +float anm_get_value(struct anm_track *track, anm_time_t tm) +{ + int idx0, idx1, last_idx; + anm_time_t tstart, tend; + float t, dt; + float v0, v1, v2, v3; + + if(!track->count) { + return track->def_val; + } + + last_idx = track->count - 1; + + tstart = track->keys[0].time; + tend = track->keys[last_idx].time; + + if(tstart == tend) { + return track->keys[0].val; + } + + tm = remap_time[track->extrap](tm, tstart, tend); + + idx0 = anm_get_key_interval(track, tm); + assert(idx0 >= 0 && idx0 < track->count); + idx1 = idx0 + 1; + + if(idx0 == last_idx) { + return track->keys[idx0].val; + } + + dt = (float)(track->keys[idx1].time - track->keys[idx0].time); + t = (float)(tm - track->keys[idx0].time) / dt; + + v1 = track->keys[idx0].val; + v2 = track->keys[idx1].val; + + /* get the neigboring values to allow for cubic interpolation */ + v0 = idx0 > 0 ? track->keys[idx0 - 1].val : v1; + v3 = idx1 < last_idx ? track->keys[idx1 + 1].val : v2; + + return interp[track->interp](v0, v1, v2, v3, t); +} + + +static float interp_step(float v0, float v1, float v2, float v3, float t) +{ + return v1; +} + +static float interp_linear(float v0, float v1, float v2, float v3, float t) +{ + return v1 + (v2 - v1) * t; +} + +static float interp_cubic(float a, float b, float c, float d, float t) +{ + float x, y, z, w; + float tsq = t * t; + + x = -a + 3.0 * b - 3.0 * c + d; + y = 2.0 * a - 5.0 * b + 4.0 * c - d; + z = c - a; + w = 2.0 * b; + + return 0.5 * (x * tsq * t + y * tsq + z * t + w); +} + +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + return remap_repeat(tm, start, end); +} + +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + if(start == end) { + return start; + } + return tm < start ? start : (tm >= end ? end : tm); +} + +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + anm_time_t x, interv = end - start; + + if(interv == 0) { + return start; + } + + x = (tm - start) % interv; + if(x < 0) { + x += interv; + } + return x + start; + + /*if(tm < start) { + while(tm < start) { + tm += interv; + } + return tm; + } + return (tm - start) % interv + start;*/ +} + +static anm_time_t remap_pingpong(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + anm_time_t interv = end - start; + anm_time_t x = remap_repeat(tm, start, end + interv); + + return x > end ? end + interv - x : x; +} diff --git a/libs/anim/track.h b/libs/anim/track.h new file mode 100644 index 0000000..89749dd --- /dev/null +++ b/libs/anim/track.h @@ -0,0 +1,119 @@ +/* +libanim - hierarchical keyframe animation library +Copyright (C) 2012-2014 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +/* An animation track defines the values of a single scalar over time + * and supports various interpolation and extrapolation modes. + */ +#ifndef LIBANIM_TRACK_H_ +#define LIBANIM_TRACK_H_ + +#include +#include "config.h" + +enum anm_interpolator { + ANM_INTERP_STEP, + ANM_INTERP_LINEAR, + ANM_INTERP_CUBIC +}; + +enum anm_extrapolator { + ANM_EXTRAP_EXTEND, /* extend to infinity */ + ANM_EXTRAP_CLAMP, /* clamp to last value */ + ANM_EXTRAP_REPEAT, /* repeat motion */ + ANM_EXTRAP_PINGPONG /* repeat with mirroring */ +}; + +typedef long anm_time_t; +#define ANM_TIME_INVAL LONG_MIN + +#define ANM_SEC2TM(x) ((anm_time_t)((x) * 1000)) +#define ANM_MSEC2TM(x) ((anm_time_t)(x)) +#define ANM_TM2SEC(x) ((x) / 1000.0) +#define ANM_TM2MSEC(x) (x) + +struct anm_keyframe { + anm_time_t time; + float val; +}; + +struct anm_track { + char *name; + int count; + struct anm_keyframe *keys; + + float def_val; + + enum anm_interpolator interp; + enum anm_extrapolator extrap; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* track constructor and destructor */ +int anm_init_track(struct anm_track *track); +void anm_destroy_track(struct anm_track *track); + +/* helper functions that use anm_init_track and anm_destroy_track internally */ +struct anm_track *anm_create_track(void); +void anm_free_track(struct anm_track *track); + +/* copies track src to dest + * XXX: dest must have been initialized first + */ +void anm_copy_track(struct anm_track *dest, const struct anm_track *src); + +int anm_set_track_name(struct anm_track *track, const char *name); +const char *anm_get_track_name(struct anm_track *track); + +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in); +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex); + +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end); + +void anm_set_track_default(struct anm_track *track, float def); + +/* set or update a keyframe */ +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key); + +/* get the idx-th keyframe, returns null if it doesn't exist */ +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx); + +/* Finds the 0-based index of the intra-keyframe interval which corresponds + * to the specified time. If the time falls exactly onto the N-th keyframe + * the function returns N. + * + * Special cases: + * - if the time is before the first keyframe -1 is returned. + * - if the time is after the last keyframe, the index of the last keyframe + * is returned. + */ +int anm_get_key_interval(struct anm_track *track, anm_time_t tm); + +int anm_set_value(struct anm_track *track, anm_time_t tm, float val); + +/* evaluates and returns the value of the track for a particular time */ +float anm_get_value(struct anm_track *track, anm_time_t tm); + +#ifdef __cplusplus +} +#endif + + +#endif /* LIBANIM_TRACK_H_ */ diff --git a/libs/cgmath/LICENSE b/libs/cgmath/LICENSE new file mode 100644 index 0000000..536e666 --- /dev/null +++ b/libs/cgmath/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2016 John Tsiombikas + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/libs/cgmath/README.md b/libs/cgmath/README.md new file mode 100644 index 0000000..119aa4a --- /dev/null +++ b/libs/cgmath/README.md @@ -0,0 +1,29 @@ +gph-cmath: C math library for graphics +====================================== + +About +----- +gph-cmath is a C math library for graphics programs. It provides a plethora of +operations on vectors, matrices and quaternions, among other things. + +It's conceptually a companion to my C++ math library +[gph-math](http://github.com/jtsiomb/gph-math), but where gph-math is designed +with intuitiveness and ease of use as the main priority, this C version is +designed to be as low-overhead as possible, making it more suitable for more +resource-constrained target systems. +For instance most functions modify their first argument, instead of doing an +extra copy to provide a more natural 3 operand interface. Leaving the copy to +the user for the cases where it's necessary. + +License +------- +Copyright (C) 2016 John Tsiombikas + +This program is free software. Feel free to use, modify, and/or redistribute it +under the terms of the MIT/X11 license. See LICENSE for details. + +How to use +---------- +There's nothing to build. All functions are static inline, defined in the header +files. Either type `make install` to install them system-wide, or just copy all +files under `src/` to your project source tree. diff --git a/libs/cgmath/cgmath.h b/libs/cgmath/cgmath.h new file mode 100644 index 0000000..36c3d54 --- /dev/null +++ b/libs/cgmath/cgmath.h @@ -0,0 +1,263 @@ +/* gph-cmath - C graphics math library + * Copyright (C) 2018 John Tsiombikas + * + * This program is free software. Feel free to use, modify, and/or redistribute + * it under the terms of the MIT/X11 license. See LICENSE for details. + * If you intend to redistribute parts of the code without the LICENSE file + * replace this paragraph with the full contents of the LICENSE file. + * + * Function prefixes signify the data type of their operand(s): + * - cgm_v... functions are operations on cgm_vec3 vectors + * - cgm_w... functions are operations on cgm_vec4 vectors + * - cgm_q... functions are operations on cgm_quat quaternions (w + xi + yj + zk) + * - cgm_m... functions are operations on 4x4 matrices (stored as linear 16 float arrays) + * - cgm_r... functions are operations on cgm_ray rays + * + * NOTE: *ALL* matrix arguments are pointers to 16 floats. Even the functions + * which operate on 3x3 matrices, actually use the upper 3x3 of a 4x4 matrix, + * and still expect an array of 16 floats. + * + * NOTE: matrices are treated by all operations as column-major, to match OpenGL + * conventions, so everything is pretty much transposed. +*/ +#ifndef CGMATH_H_ +#define CGMATH_H_ + +#include +#include + +typedef struct { + float x, y; +} cgm_vec2; + +typedef struct { + float x, y, z; +} cgm_vec3; + +typedef struct { + float x, y, z, w; +} cgm_vec4, cgm_quat; + +typedef struct { + cgm_vec3 origin, dir; +} cgm_ray; + +typedef enum cgm_euler_mode { + CGM_EULER_XYZ, + CGM_EULER_XZY, + CGM_EULER_YXZ, + CGM_EULER_YZX, + CGM_EULER_ZXY, + CGM_EULER_ZYX, + CGM_EULER_ZXZ, + CGM_EULER_ZYZ, + CGM_EULER_YXY, + CGM_EULER_YZY, + CGM_EULER_XYX, + CGM_EULER_XZX +} cgm_euler_mode; + +#ifdef __cplusplus +extern "C" { +#endif + +/* --- operations on cgm_vec3 --- */ +static inline void cgm_vcons(cgm_vec3 *v, float x, float y, float z); + +static inline void cgm_vadd(cgm_vec3 *a, const cgm_vec3 *b); +static inline void cgm_vadd_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s); /* a+b*s */ +static inline void cgm_vsub(cgm_vec3 *a, const cgm_vec3 *b); +static inline void cgm_vsub_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s); /* a-b*s */ +static inline void cgm_vmul(cgm_vec3 *a, const cgm_vec3 *b); +static inline void cgm_vscale(cgm_vec3 *v, float s); +static inline void cgm_vmul_m4v3(cgm_vec3 *v, const float *m); /* m4x4 * v */ +static inline void cgm_vmul_v3m4(cgm_vec3 *v, const float *m); /* v * m4x4 */ +static inline void cgm_vmul_m3v3(cgm_vec3 *v, const float *m); /* m3x3 * v (m still 16 floats) */ +static inline void cgm_vmul_v3m3(cgm_vec3 *v, const float *m); /* v * m3x3 (m still 16 floats) */ + +static inline float cgm_vdot(const cgm_vec3 *a, const cgm_vec3 *b); +static inline void cgm_vcross(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b); +static inline float cgm_vlength(const cgm_vec3 *v); +static inline float cgm_vlength_sq(const cgm_vec3 *v); +static inline float cgm_vdist(const cgm_vec3 *a, const cgm_vec3 *b); +static inline float cgm_vdist_sq(const cgm_vec3 *a, const cgm_vec3 *b); +static inline void cgm_vnormalize(cgm_vec3 *v); + +static inline void cgm_vreflect(cgm_vec3 *v, const cgm_vec3 *n); +static inline int cgm_vrefract(cgm_vec3 *v, const cgm_vec3 *n, float ior); + +static inline void cgm_vrotate_quat(cgm_vec3 *v, const cgm_quat *q); +static inline void cgm_vrotate_axis(cgm_vec3 *v, int axis, float angle); +static inline void cgm_vrotate(cgm_vec3 *v, float angle, float x, float y, float z); +static inline void cgm_vrotate_euler(cgm_vec3 *v, float a, float b, float c, enum cgm_euler_mode mode); + +static inline void cgm_vlerp(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b, float t); + +#define cgm_velem(vptr, idx) ((&(vptr)->x)[idx]) + +/* --- operations on cgm_vec4 --- */ +static inline void cgm_wcons(cgm_vec4 *v, float x, float y, float z, float w); + +static inline void cgm_wadd(cgm_vec4 *a, const cgm_vec4 *b); +static inline void cgm_wsub(cgm_vec4 *a, const cgm_vec4 *b); +static inline void cgm_wmul(cgm_vec4 *a, const cgm_vec4 *b); +static inline void cgm_wscale(cgm_vec4 *v, float s); + +static inline void cgm_wmul_m4v4(cgm_vec4 *v, const float *m); +static inline void cgm_wmul_v4m4(cgm_vec4 *v, const float *m); +static inline void cgm_wmul_m34v4(cgm_vec4 *v, const float *m); /* doesn't affect w */ +static inline void cgm_wmul_v4m43(cgm_vec4 *v, const float *m); /* doesn't affect w */ +static inline void cgm_wmul_m3v4(cgm_vec4 *v, const float *m); /* (m still 16 floats) */ +static inline void cgm_wmul_v4m3(cgm_vec4 *v, const float *m); /* (m still 16 floats) */ + +static inline float cgm_wdot(const cgm_vec4 *a, const cgm_vec4 *b); + +static inline float cgm_wlength(const cgm_vec4 *v); +static inline float cgm_wlength_sq(const cgm_vec4 *v); +static inline float cgm_wdist(const cgm_vec4 *a, const cgm_vec4 *b); +static inline float cgm_wdist_sq(const cgm_vec4 *a, const cgm_vec4 *b); +static inline void cgm_wnormalize(cgm_vec4 *v); + +static inline void cgm_wlerp(cgm_vec4 *res, const cgm_vec4 *a, const cgm_vec4 *b, float t); + +#define cgm_welem(vptr, idx) ((&(vptr)->x)[idx]) + +/* --- operations on quaternions --- */ +static inline void cgm_qcons(cgm_quat *q, float x, float y, float z, float w); + +static inline void cgm_qneg(cgm_quat *q); +static inline void cgm_qadd(cgm_quat *a, const cgm_quat *b); +static inline void cgm_qsub(cgm_quat *a, const cgm_quat *b); +static inline void cgm_qmul(cgm_quat *a, const cgm_quat *b); + +static inline float cgm_qlength(const cgm_quat *q); +static inline float cgm_qlength_sq(const cgm_quat *q); +static inline void cgm_qnormalize(cgm_quat *q); +static inline void cgm_qconjugate(cgm_quat *q); +static inline void cgm_qinvert(cgm_quat *q); + +static inline void cgm_qrotation(cgm_quat *q, float angle, float x, float y, float z); +static inline void cgm_qrotate(cgm_quat *q, float angle, float x, float y, float z); + +static inline void cgm_qslerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t); +static inline void cgm_qlerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t); + +#define cgm_qelem(qptr, idx) ((&(qptr)->x)[idx]) + +/* --- operations on matrices --- */ +static inline void cgm_mcopy(float *dest, const float *src); +static inline void cgm_mzero(float *m); +static inline void cgm_midentity(float *m); + +static inline void cgm_mmul(float *a, const float *b); +static inline void cgm_mpremul(float *a, const float *b); + +static inline void cgm_msubmatrix(float *m, int row, int col); +static inline void cgm_mupper3(float *m); +static inline float cgm_msubdet(const float *m, int row, int col); +static inline float cgm_mcofactor(const float *m, int row, int col); +static inline float cgm_mdet(const float *m); +static inline void cgm_mtranspose(float *m); +static inline void cgm_mcofmatrix(float *m); +static inline int cgm_minverse(float *m); /* returns 0 on success, -1 for singular */ + +static inline void cgm_mtranslation(float *m, float x, float y, float z); +static inline void cgm_mscaling(float *m, float sx, float sy, float sz); +static inline void cgm_mrotation_x(float *m, float angle); +static inline void cgm_mrotation_y(float *m, float angle); +static inline void cgm_mrotation_z(float *m, float angle); +static inline void cgm_mrotation_axis(float *m, int idx, float angle); +static inline void cgm_mrotation(float *m, float angle, float x, float y, float z); +static inline void cgm_mrotation_euler(float *m, float a, float b, float c, int mode); +static inline void cgm_mrotation_quat(float *m, const cgm_quat *q); + +static inline void cgm_mtranslate(float *m, float x, float y, float z); +static inline void cgm_mscale(float *m, float sx, float sy, float sz); +static inline void cgm_mrotate_x(float *m, float angle); +static inline void cgm_mrotate_y(float *m, float angle); +static inline void cgm_mrotate_z(float *m, float angle); +static inline void cgm_mrotate_axis(float *m, int idx, float angle); +static inline void cgm_mrotate(float *m, float angle, float x, float y, float z); +static inline void cgm_mrotate_euler(float *m, float a, float b, float c, int mode); +static inline void cgm_mrotate_quat(float *m, const cgm_quat *q); + +static inline void cgm_mpretranslate(float *m, float x, float y, float z); +static inline void cgm_mprescale(float *m, float sx, float sy, float sz); +static inline void cgm_mprerotate_x(float *m, float angle); +static inline void cgm_mprerotate_y(float *m, float angle); +static inline void cgm_mprerotate_z(float *m, float angle); +static inline void cgm_mprerotate_axis(float *m, int idx, float angle); +static inline void cgm_mprerotate(float *m, float angle, float x, float y, float z); +static inline void cgm_mprerotate_euler(float *m, float a, float b, float c, int mode); +static inline void cgm_mprerotate_quat(float *m, const cgm_quat *q); + +static inline void cgm_mget_translation(const float *m, cgm_vec3 *res); +static inline void cgm_mget_rotation(const float *m, cgm_quat *res); +static inline void cgm_mget_scaling(const float *m, cgm_vec3 *res); +static inline void cgm_mget_frustum_plane(const float *m, int p, cgm_vec4 *res); + +static inline void cgm_mlookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, + const cgm_vec3 *up); +static inline void cgm_minv_lookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, + const cgm_vec3 *up); +static inline void cgm_mortho(float *m, float left, float right, float bot, float top, + float znear, float zfar); +static inline void cgm_mfrustum(float *m, float left, float right, float bot, float top, + float znear, float zfar); +static inline void cgm_mperspective(float *m, float vfov, float aspect, float znear, float zfar); + +static inline void cgm_mmirror(float *m, float a, float b, float c, float d); + +/* --- operations on rays --- */ +static inline void cgm_rcons(cgm_ray *r, float x, float y, float z, float dx, float dy, float dz); + +static inline void cgm_rmul_mr(cgm_ray *ray, const float *m); /* m4x4 * ray */ +static inline void cgm_rmul_rm(cgm_ray *ray, const float *m); /* ray * m4x4 */ + +static inline void cgm_rreflect(cgm_ray *ray, const cgm_vec3 *n); +static inline void cgm_rrefract(cgm_ray *ray, const cgm_vec3 *n, float ior); + +/* --- miscellaneous utility functions --- */ +static inline float cgm_deg_to_rad(float deg); +static inline float cgm_rad_to_deg(float rad); + +static inline float cgm_smoothstep(float a, float b, float x); +static inline float cgm_lerp(float a, float b, float t); +static inline float cgm_bezier(float a, float b, float c, float d, float t); +static inline float cgm_bspline(float a, float b, float c, float d, float t); +static inline float cgm_spline(float a, float b, float c, float d, float t); + +static inline void cgm_discrand(cgm_vec3 *v, float rad); +static inline void cgm_sphrand(cgm_vec3 *v, float rad); + +static inline void cgm_unproject(cgm_vec3 *res, const cgm_vec3 *norm_scrpos, + const float *inv_viewproj); +static inline void cgm_glu_unproject(float winx, float winy, float winz, + const float *view, const float *proj, const int *vp, + float *objx, float *objy, float *objz); + +static inline void cgm_pick_ray(cgm_ray *ray, float nx, float ny, + const float *viewmat, const float *projmat); + +static inline void cgm_raypos(cgm_vec3 *p, const cgm_ray *ray, float t); + +/* calculate barycentric coordinates of point pt in triangle (a, b, c) */ +static inline void cgm_bary(cgm_vec3 *bary, const cgm_vec3 *a, + const cgm_vec3 *b, const cgm_vec3 *c, const cgm_vec3 *pt); + +/* convert between unit vectors and spherical coordinates */ +static inline void cgm_uvec_to_sph(float *theta, float *phi, const cgm_vec3 *v); +static inline void cgm_sph_to_uvec(cgm_vec3 *v, float theta, float phi); + +#include "cgmvec3.inl" +#include "cgmvec4.inl" +#include "cgmquat.inl" +#include "cgmmat.inl" +#include "cgmray.inl" +#include "cgmmisc.inl" + +#ifdef __cplusplus +} +#endif + +#endif /* CGMATH_H_ */ diff --git a/libs/cgmath/cgmmat.inl b/libs/cgmath/cgmmat.inl new file mode 100644 index 0000000..2eb4519 --- /dev/null +++ b/libs/cgmath/cgmmat.inl @@ -0,0 +1,623 @@ +/* gph-cmath - C graphics math library + * Copyright (C) 2018 John Tsiombikas + * + * This program is free software. Feel free to use, modify, and/or redistribute + * it under the terms of the MIT/X11 license. See LICENSE for details. + * If you intend to redistribute parts of the code without the LICENSE file + * replace this paragraph with the full contents of the LICENSE file. + */ +static inline void cgm_mcopy(float *dest, const float *src) +{ + memcpy(dest, src, 16 * sizeof(float)); +} + +static inline void cgm_mzero(float *m) +{ + static float z[16]; + cgm_mcopy(m, z); +} + +static inline void cgm_midentity(float *m) +{ + static float id[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; + cgm_mcopy(m, id); +} + +static inline void cgm_mmul(float *a, const float *b) +{ + int i, j; + float res[16]; + float *resptr = res; + float *arow = a; + + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + *resptr++ = arow[0] * b[j] + arow[1] * b[4 + j] + + arow[2] * b[8 + j] + arow[3] * b[12 + j]; + } + arow += 4; + } + cgm_mcopy(a, res); +} + +static inline void cgm_mpremul(float *a, const float *b) +{ + int i, j; + float res[16]; + float *resptr = res; + const float *brow = b; + + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + *resptr++ = brow[0] * a[j] + brow[1] * a[4 + j] + + brow[2] * a[8 + j] + brow[3] * a[12 + j]; + } + brow += 4; + } + cgm_mcopy(a, res); +} + +static inline void cgm_msubmatrix(float *m, int row, int col) +{ + float orig[16]; + int i, j, subi, subj; + + cgm_mcopy(orig, m); + + subi = 0; + for(i=0; i<4; i++) { + if(i == row) continue; + + subj = 0; + for(j=0; j<4; j++) { + if(j == col) continue; + + m[subi * 4 + subj++] = orig[i * 4 + j]; + } + subi++; + } + + cgm_mupper3(m); +} + +static inline void cgm_mupper3(float *m) +{ + m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0.0f; + m[15] = 1.0f; +} + +static inline float cgm_msubdet(const float *m, int row, int col) +{ + float tmp[16]; + float subdet00, subdet01, subdet02; + + cgm_mcopy(tmp, m); + cgm_msubmatrix(tmp, row, col); + + subdet00 = tmp[5] * tmp[10] - tmp[6] * tmp[9]; + subdet01 = tmp[4] * tmp[10] - tmp[6] * tmp[8]; + subdet02 = tmp[4] * tmp[9] - tmp[5] * tmp[8]; + + return tmp[0] * subdet00 - tmp[1] * subdet01 + tmp[2] * subdet02; +} + +static inline float cgm_mcofactor(const float *m, int row, int col) +{ + float min = cgm_msubdet(m, row, col); + return (row + col) & 1 ? -min : min; +} + +static inline float cgm_mdet(const float *m) +{ + return m[0] * cgm_msubdet(m, 0, 0) - m[1] * cgm_msubdet(m, 0, 1) + + m[2] * cgm_msubdet(m, 0, 2) - m[3] * cgm_msubdet(m, 0, 3); +} + +static inline void cgm_mtranspose(float *m) +{ + int i, j; + for(i=0; i<4; i++) { + for(j=0; jx * q->x; + float ysq2 = 2.0f * q->y * q->y; + float zsq2 = 2.0f * q->z * q->z; + float sx = 1.0f - ysq2 - zsq2; + float sy = 1.0f - xsq2 - zsq2; + float sz = 1.0f - xsq2 - ysq2; + + m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0.0f; + m[15] = 1.0f; + + m[0] = sx; + m[1] = 2.0f * q->x * q->y + 2.0f * q->w * q->z; + m[2] = 2.0f * q->z * q->x - 2.0f * q->w * q->y; + m[4] = 2.0f * q->x * q->y - 2.0f * q->w * q->z; + m[5] = sy; + m[6] = 2.0f * q->y * q->z + 2.0f * q->w * q->x; + m[8] = 2.0f * q->z * q->x + 2.0f * q->w * q->y; + m[9] = 2.0f * q->y * q->z - 2.0f * q->w * q->x; + m[10] = sz; +} + +static inline void cgm_mtranslate(float *m, float x, float y, float z) +{ + float tm[16]; + cgm_mtranslation(tm, x, y, z); + cgm_mmul(m, tm); +} + +static inline void cgm_mscale(float *m, float sx, float sy, float sz) +{ + float sm[16]; + cgm_mscaling(sm, sx, sy, sz); + cgm_mmul(m, sm); +} + +static inline void cgm_mrotate_x(float *m, float angle) +{ + float rm[16]; + cgm_mrotation_x(rm, angle); + cgm_mmul(m, rm); +} + +static inline void cgm_mrotate_y(float *m, float angle) +{ + float rm[16]; + cgm_mrotation_y(rm, angle); + cgm_mmul(m, rm); +} + +static inline void cgm_mrotate_z(float *m, float angle) +{ + float rm[16]; + cgm_mrotation_z(rm, angle); + cgm_mmul(m, rm); +} + +static inline void cgm_mrotate_axis(float *m, int idx, float angle) +{ + float rm[16]; + cgm_mrotation_axis(rm, idx, angle); + cgm_mmul(m, rm); +} + +static inline void cgm_mrotate(float *m, float angle, float x, float y, float z) +{ + float rm[16]; + cgm_mrotation(rm, angle, x, y, z); + cgm_mmul(m, rm); +} + +static inline void cgm_mrotate_euler(float *m, float a, float b, float c, int mode) +{ + float rm[16]; + cgm_mrotation_euler(rm, a, b, c, mode); + cgm_mmul(m, rm); +} + +static inline void cgm_mrotate_quat(float *m, const cgm_quat *q) +{ + float rm[16]; + cgm_mrotation_quat(rm, q); + cgm_mmul(m, rm); +} + + +static inline void cgm_mpretranslate(float *m, float x, float y, float z) +{ + float tm[16]; + cgm_mtranslation(tm, x, y, z); + cgm_mpremul(m, tm); +} + +static inline void cgm_mprescale(float *m, float sx, float sy, float sz) +{ + float sm[16]; + cgm_mscaling(sm, sx, sy, sz); + cgm_mpremul(m, sm); +} + +static inline void cgm_mprerotate_x(float *m, float angle) +{ + float rm[16]; + cgm_mrotation_x(rm, angle); + cgm_mpremul(m, rm); +} + +static inline void cgm_mprerotate_y(float *m, float angle) +{ + float rm[16]; + cgm_mrotation_y(rm, angle); + cgm_mpremul(m, rm); +} + +static inline void cgm_mprerotate_z(float *m, float angle) +{ + float rm[16]; + cgm_mrotation_z(rm, angle); + cgm_mpremul(m, rm); +} + +static inline void cgm_mprerotate_axis(float *m, int idx, float angle) +{ + float rm[16]; + cgm_mrotation_axis(rm, idx, angle); + cgm_mpremul(m, rm); +} + +static inline void cgm_mprerotate(float *m, float angle, float x, float y, float z) +{ + float rm[16]; + cgm_mrotation(rm, angle, x, y, z); + cgm_mpremul(m, rm); +} + +static inline void cgm_mprerotate_euler(float *m, float a, float b, float c, int mode) +{ + float rm[16]; + cgm_mrotation_euler(rm, a, b, c, mode); + cgm_mpremul(m, rm); +} + +static inline void cgm_mprerotate_quat(float *m, const cgm_quat *q) +{ + float rm[16]; + cgm_mrotation_quat(rm, q); + cgm_mpremul(m, rm); +} + + +static inline void cgm_mget_translation(const float *m, cgm_vec3 *res) +{ + res->x = m[12]; + res->y = m[13]; + res->z = m[14]; +} + +/* Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + * article "Quaternion Calculus and Fast Animation". + * adapted from: http://www.geometrictools.com/LibMathematics/Algebra/Wm5Quaternion.inl + */ +static inline void cgm_mget_rotation(const float *m, cgm_quat *res) +{ + static const int next[3] = {1, 2, 0}; + float quat[4]; + int i, j, k; + + float trace = m[0] + m[5] + m[10]; + float root; + + if(trace > 0.0f) { + /* |w| > 1/2 */ + root = sqrt(trace + 1.0f); /* 2w */ + res->w = 0.5f * root; + root = 0.5f / root; /* 1 / 4w */ + res->x = (m[6] - m[9]) * root; + res->y = (m[8] - m[2]) * root; + res->z = (m[1] - m[4]) * root; + } else { + /* |w| <= 1/2 */ + i = 0; + if(m[5] > m[0]) { + i = 1; + } + if(m[10] > m[i * 4 + i]) { + i = 2; + } + j = next[i]; + k = next[j]; + + root = sqrt(m[i * 4 + i] - m[j * 4 + j] - m[k * 4 + k] + 1.0f); + quat[i + 1] = 0.5f * root; + root = 0.5f / root; + quat[0] = (m[j + 4 + k] - m[k * 4 + j]) * root; + quat[j + 1] = (m[i * 4 + j] - m[j * 4 + i]) * root; + quat[k + 1] = (m[i * 4 + k] - m[k * 4 + i]) * root; + res->w = quat[0]; + res->x = quat[1]; + res->y = quat[2]; + res->z = quat[3]; + } +} + +static inline void cgm_mget_scaling(const float *m, cgm_vec3 *res) +{ + res->x = sqrt(m[0] * m[0] + m[4] * m[4] + m[8] * m[8]); + res->y = sqrt(m[1] * m[1] + m[5] * m[5] + m[9] * m[9]); + res->z = sqrt(m[2] * m[2] + m[6] * m[6] + m[10] * m[10]); +} + +static inline void cgm_mget_frustum_plane(const float *m, int p, cgm_vec4 *res) +{ + int row = p >> 1; + const float *rowptr = m + row * 4; + + if((p & 1) == 0) { + res->x = m[12] + rowptr[0]; + res->y = m[13] + rowptr[1]; + res->z = m[14] + rowptr[2]; + res->w = m[15] + rowptr[3]; + } else { + res->x = m[12] - rowptr[0]; + res->y = m[13] - rowptr[1]; + res->z = m[14] - rowptr[2]; + res->w = m[15] - rowptr[3]; + } +} + +static inline void cgm_mlookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, + const cgm_vec3 *up) +{ + float trans[16]; + cgm_vec3 dir = *targ, right, vup; + + cgm_vsub(&dir, pos); + cgm_vnormalize(&dir); + cgm_vcross(&right, &dir, up); + cgm_vnormalize(&right); + cgm_vcross(&vup, &right, &dir); + cgm_vnormalize(&vup); + + cgm_midentity(m); + m[0] = right.x; + m[1] = right.y; + m[2] = right.z; + m[4] = vup.x; + m[5] = vup.y; + m[6] = vup.z; + m[8] = -dir.x; + m[9] = -dir.y; + m[10] = -dir.z; + + cgm_mtranslation(trans, pos->x, pos->y, pos->z); + cgm_mmul(m, trans); +} + +static inline void cgm_minv_lookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, + const cgm_vec3 *up) +{ + float rot[16]; + cgm_vec3 dir = *targ, right, vup; + + cgm_vsub(&dir, pos); + cgm_vnormalize(&dir); + cgm_vcross(&right, &dir, up); + cgm_vnormalize(&right); + cgm_vcross(&vup, &right, &dir); + cgm_vnormalize(&vup); + + cgm_midentity(rot); + rot[0] = right.x; + rot[4] = right.y; + rot[8] = right.z; + rot[1] = vup.x; + rot[5] = vup.y; + rot[9] = vup.z; + rot[2] = -dir.x; + rot[6] = -dir.y; + rot[10] = -dir.z; + + cgm_mtranslation(m, -pos->x, -pos->y, -pos->z); + cgm_mmul(m, rot); +} + +static inline void cgm_mortho(float *m, float left, float right, float bot, float top, + float znear, float zfar) +{ + float dx = right - left; + float dy = top - bot; + float dz = zfar - znear; + + cgm_midentity(m); + m[0] = 2.0f / dx; + m[5] = 2.0f / dy; + m[10] = -2.0f / dz; + m[12] = -(right + left) / dx; + m[13] = -(top + bot) / dy; + m[14] = -(zfar + znear) / dz; +} + +static inline void cgm_mfrustum(float *m, float left, float right, float bot, float top, + float znear, float zfar) +{ + float dx = right - left; + float dy = top - bot; + float dz = zfar - znear; + + cgm_mzero(m); + m[0] = 2.0f * znear / dx; + m[5] = 2.0f * znear / dy; + m[8] = (right + left) / dx; + m[9] = (top + bot) / dy; + m[10] = -(zfar + znear) / dz; + m[14] = -2.0f * zfar * znear / dz; + m[11] = -1.0f; +} + +static inline void cgm_mperspective(float *m, float vfov, float aspect, float znear, float zfar) +{ + float s = 1.0f / (float)tan(vfov / 2.0f); + float range = znear - zfar; + + cgm_mzero(m); + m[0] = s / aspect; + m[5] = s; + m[10] = (znear + zfar) / range; + m[14] = 2.0f * znear * zfar / range; + m[11] = -1.0f; +} + +static inline void cgm_mmirror(float *m, float a, float b, float c, float d) +{ + m[0] = 1.0f - 2.0f * a * a; + m[5] = 1.0f - 2.0f * b * b; + m[10] = 1.0f - 2.0f * c * c; + m[15] = 1.0f; + + m[1] = m[4] = -2.0f * a * b; + m[2] = m[8] = -2.0f * a * c; + m[6] = m[9] = -2.0f * b * c; + + m[12] = -2.0f * a * d; + m[13] = -2.0f * b * d; + m[14] = -2.0f * c * d; + + m[3] = m[7] = m[11] = 0.0f; +} diff --git a/libs/cgmath/cgmmisc.inl b/libs/cgmath/cgmmisc.inl new file mode 100644 index 0000000..bf9e958 --- /dev/null +++ b/libs/cgmath/cgmmisc.inl @@ -0,0 +1,203 @@ +/* gph-cmath - C graphics math library + * Copyright (C) 2018 John Tsiombikas + * + * This program is free software. Feel free to use, modify, and/or redistribute + * it under the terms of the MIT/X11 license. See LICENSE for details. + * If you intend to redistribute parts of the code without the LICENSE file + * replace this paragraph with the full contents of the LICENSE file. + */ +#include + +static inline float cgm_deg_to_rad(float deg) +{ + return M_PI * deg / 180.0f; +} + +static inline float cgm_rad_to_deg(float rad) +{ + return 180.0f * rad / M_PI; +} + +static inline float cgm_smoothstep(float a, float b, float x) +{ + if(x < a) return 0.0f; + if(x >= b) return 1.0f; + + x = (x - a) / (b - a); + return x * x * (3.0f - 2.0f * x); +} + +static inline float cgm_lerp(float a, float b, float t) +{ + return a + (b - a) * t; +} + +static inline float cgm_bezier(float a, float b, float c, float d, float t) +{ + float omt, omt3, t3, f; + t3 = t * t * t; + omt = 1.0f - t; + omt3 = omt * omt * omt; + f = 3.0f * t * omt; + + return (a * omt3) + (b * f * omt) + (c * f * t) + (d * t3); +} + +static inline float cgm_bspline(float a, float b, float c, float d, float t) +{ + static const float mat[] = { + -1, 3, -3, 1, + 3, -6, 0, 4, + -3, 3, 3, 1, + 1, 0, 0, 0 + }; + cgm_vec4 tmp, qfact; + float tsq = t * t; + + cgm_wcons(&qfact, tsq * t, tsq, t, 1.0f); + cgm_wcons(&tmp, a, b, c, d); + cgm_wmul_m4v4(&tmp, mat); + cgm_wscale(&tmp, 1.0f / 6.0f); + return cgm_wdot(&tmp, &qfact); +} + +static inline float cgm_spline(float a, float b, float c, float d, float t) +{ + static const float mat[] = { + -1, 2, -1, 0, + 3, -5, 0, 2, + -3, 4, 1, 0, + 1, -1, 0, 0 + }; + cgm_vec4 tmp, qfact; + float tsq = t * t; + + cgm_wcons(&qfact, tsq * t, tsq, t, 1.0f); + cgm_wcons(&tmp, a, b, c, d); + cgm_wmul_m4v4(&tmp, mat); + cgm_wscale(&tmp, 1.0f / 6.0f); + return cgm_wdot(&tmp, &qfact); +} + +static inline void cgm_discrand(cgm_vec3 *pt, float rad) +{ + float theta = 2.0f * M_PI * (float)rand() / RAND_MAX; + float r = sqrt((float)rand() / RAND_MAX) * rad; + pt->x = cos(theta) * r; + pt->y = sin(theta) * r; + pt->z = 0.0f; +} + +static inline void cgm_sphrand(cgm_vec3 *pt, float rad) +{ + float u, v, theta, phi; + + u = (float)rand() / RAND_MAX; + v = (float)rand() / RAND_MAX; + + theta = 2.0f * M_PI * u; + phi = acos(2.0f * v - 1.0f); + + pt->x = cos(theta) * sin(phi) * rad; + pt->y = sin(theta) * sin(phi) * rad; + pt->z = cos(phi) * rad; +} + +static inline void cgm_unproject(cgm_vec3 *res, const cgm_vec3 *norm_scrpos, + const float *inv_viewproj) +{ + cgm_vec4 pos; + + pos.x = 2.0f * norm_scrpos->x - 1.0f; + pos.y = 2.0f * norm_scrpos->y - 1.0f; + pos.z = 2.0f * norm_scrpos->z - 1.0f; + pos.w = 1.0f; + + cgm_wmul_m4v4(&pos, inv_viewproj); + + res->x = pos.x / pos.w; + res->y = pos.y / pos.w; + res->z = pos.z / pos.w; +} + +static inline void cgm_glu_unproject(float winx, float winy, float winz, + const float *view, const float *proj, const int *vp, + float *objx, float *objy, float *objz) +{ + cgm_vec3 npos, res; + float inv_pv[16]; + + cgm_mcopy(inv_pv, proj); + cgm_mmul(inv_pv, view); + + npos.x = (winx - vp[0]) / vp[2]; + npos.y = (winy - vp[1]) / vp[4]; + npos.z = winz; + + cgm_unproject(&res, &npos, inv_pv); + + *objx = res.x; + *objy = res.y; + *objz = res.z; +} + +static inline void cgm_pick_ray(cgm_ray *ray, float nx, float ny, + const float *viewmat, const float *projmat) +{ + cgm_vec3 npos, farpt; + float inv_pv[16]; + + cgm_mcopy(inv_pv, projmat); + cgm_mmul(inv_pv, viewmat); + + cgm_vcons(&npos, nx, ny, 0.0f); + cgm_unproject(&ray->origin, &npos, inv_pv); + npos.z = 1.0f; + cgm_unproject(&farpt, &npos, inv_pv); + + ray->dir.x = farpt.x - ray->origin.x; + ray->dir.y = farpt.y - ray->origin.y; + ray->dir.z = farpt.z - ray->origin.z; +} + +static inline void cgm_raypos(cgm_vec3 *p, const cgm_ray *ray, float t) +{ + p->x = ray->origin.x + ray->dir.x * t; + p->y = ray->origin.y + ray->dir.y * t; + p->z = ray->origin.z + ray->dir.z * t; +} + +static inline void cgm_bary(cgm_vec3 *bary, const cgm_vec3 *a, + const cgm_vec3 *b, const cgm_vec3 *c, const cgm_vec3 *pt) +{ + float d00, d01, d11, d20, d21, denom; + cgm_vec3 v0 = *b, v1 = *c, v2 = *pt; + + cgm_vsub(&v0, a); + cgm_vsub(&v1, a); + cgm_vsub(&v2, a); + + d00 = cgm_vdot(&v0, &v0); + d01 = cgm_vdot(&v0, &v1); + d11 = cgm_vdot(&v1, &v1); + d20 = cgm_vdot(&v2, &v0); + d21 = cgm_vdot(&v2, &v1); + denom = d00 * d11 - d01 * d01; + + bary->y = (d11 * d20 - d01 * d21) / denom; + bary->z = (d00 * d21 - d01 * d20) / denom; + bary->x = 1.0f - bary->y - bary->z; +} + +static inline void cgm_uvec_to_sph(float *theta, float *phi, const cgm_vec3 *v) +{ + *theta = atan2(v->z, v->x); + *phi = acos(v->y); +} + +static inline void cgm_sph_to_uvec(cgm_vec3 *v, float theta, float phi) +{ + v->x = sin(theta) * cos(phi); + v->y = sin(phi); + v->z = cos(theta) * cos(phi); +} diff --git a/libs/cgmath/cgmquat.inl b/libs/cgmath/cgmquat.inl new file mode 100644 index 0000000..743d818 --- /dev/null +++ b/libs/cgmath/cgmquat.inl @@ -0,0 +1,159 @@ +/* gph-cmath - C graphics math library + * Copyright (C) 2018 John Tsiombikas + * + * This program is free software. Feel free to use, modify, and/or redistribute + * it under the terms of the MIT/X11 license. See LICENSE for details. + * If you intend to redistribute parts of the code without the LICENSE file + * replace this paragraph with the full contents of the LICENSE file. + */ +static inline void cgm_qcons(cgm_quat *q, float x, float y, float z, float w) +{ + q->x = x; + q->y = y; + q->z = z; + q->w = w; +} + + +static inline void cgm_qneg(cgm_quat *q) +{ + q->x = -q->x; + q->y = -q->y; + q->z = -q->z; + q->w = -q->w; +} + +static inline void cgm_qadd(cgm_quat *a, const cgm_quat *b) +{ + a->x += b->x; + a->y += b->y; + a->z += b->z; + a->w += b->w; +} + +static inline void cgm_qsub(cgm_quat *a, const cgm_quat *b) +{ + a->x -= b->x; + a->y -= b->y; + a->z -= b->z; + a->w -= b->w; +} + +static inline void cgm_qmul(cgm_quat *a, const cgm_quat *b) +{ + float x, y, z, dot; + cgm_vec3 cross; + + dot = a->x * b->x + a->y * b->y + a->z * b->z; + cgm_vcross(&cross, (cgm_vec3*)a, (cgm_vec3*)b); + + x = a->w * b->x + b->w * a->x + cross.x; + y = a->w * b->y + b->w * a->y + cross.y; + z = a->w * b->z + b->w * a->z + cross.z; + a->w = a->w * b->w - dot; + a->x = x; + a->y = y; + a->z = z; +} + +static inline float cgm_qlength(const cgm_quat *q) +{ + return sqrt(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w); +} + +static inline float cgm_qlength_sq(const cgm_quat *q) +{ + return q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w; +} + +static inline void cgm_qnormalize(cgm_quat *q) +{ + float len = cgm_qlength(q); + if(len != 0.0f) { + float s = 1.0f / len; + q->x *= s; + q->y *= s; + q->z *= s; + q->w *= s; + } +} + +static inline void cgm_qconjugate(cgm_quat *q) +{ + q->x = -q->x; + q->y = -q->y; + q->z = -q->z; +} + +static inline void cgm_qinvert(cgm_quat *q) +{ + float len_sq = cgm_qlength_sq(q); + cgm_qconjugate(q); + if(len_sq != 0.0f) { + float s = 1.0f / len_sq; + q->x *= s; + q->y *= s; + q->z *= s; + q->w *= s; + } +} + +static inline void cgm_qrotation(cgm_quat *q, float angle, float x, float y, float z) +{ + float hangle = angle * 0.5f; + float sin_ha = sin(hangle); + q->w = cos(hangle); + q->x = x * sin_ha; + q->y = y * sin_ha; + q->z = z * sin_ha; +} + +static inline void cgm_qrotate(cgm_quat *q, float angle, float x, float y, float z) +{ + cgm_quat qrot; + cgm_qrotation(&qrot, angle, x, y, z); + cgm_qmul(q, &qrot); +} + +static inline void cgm_qslerp(cgm_quat *res, const cgm_quat *quat1, const cgm_quat *q2, float t) +{ + float angle, dot, a, b, sin_angle; + cgm_quat q1 = *quat1; + + dot = quat1->x * q2->x + quat1->y * q2->y + quat1->z * q2->z + quat1->w * q2->w; + if(dot < 0.0f) { + /* make sure we inteprolate across the shortest arc */ + cgm_qneg(&q1); + dot = -dot; + } + + /* clamp dot to [-1, 1] in order to avoid domain errors in acos due to + * floating point imprecisions + */ + if(dot < -1.0f) dot = -1.0f; + if(dot > 1.0f) dot = 1.0f; + angle = acos(dot); + + sin_angle = sin(angle); + if(sin_angle == 0.0f) { + /* use linear interpolation to avoid div/zero */ + a = 1.0f; + b = t; + } else { + a = sin((1.0f - t) * angle) / sin_angle; + b = sin(t * angle) / sin_angle; + } + + res->x = q1.x * a + q2->x * b; + res->y = q1.y * a + q2->y * b; + res->z = q1.z * a + q2->z * b; + res->w = q1.w * a + q2->w * b; +} + +static inline void cgm_qlerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t) +{ + res->x = a->x + (b->x - a->x) * t; + res->y = a->y + (b->y - a->y) * t; + res->z = a->z + (b->z - a->z) * t; + res->w = a->w + (b->w - a->w) * t; +} diff --git a/libs/cgmath/cgmray.inl b/libs/cgmath/cgmray.inl new file mode 100644 index 0000000..063a7e0 --- /dev/null +++ b/libs/cgmath/cgmray.inl @@ -0,0 +1,39 @@ +/* gph-cmath - C graphics math library + * Copyright (C) 2018 John Tsiombikas + * + * This program is free software. Feel free to use, modify, and/or redistribute + * it under the terms of the MIT/X11 license. See LICENSE for details. + * If you intend to redistribute parts of the code without the LICENSE file + * replace this paragraph with the full contents of the LICENSE file. + */ +static inline void cgm_rcons(cgm_ray *r, float x, float y, float z, float dx, float dy, float dz) +{ + r->origin.x = x; + r->origin.y = y; + r->origin.z = z; + r->dir.x = dx; + r->dir.y = dy; + r->dir.z = dz; +} + +static inline void cgm_rmul_mr(cgm_ray *ray, const float *m) +{ + cgm_vmul_m4v3(&ray->origin, m); + cgm_vmul_m3v3(&ray->dir, m); +} + +static inline void cgm_rmul_rm(cgm_ray *ray, const float *m) +{ + cgm_vmul_v3m4(&ray->origin, m); + cgm_vmul_v3m3(&ray->dir, m); +} + +static inline void cgm_rreflect(cgm_ray *ray, const cgm_vec3 *n) +{ + cgm_vreflect(&ray->dir, n); +} + +static inline void cgm_rrefract(cgm_ray *ray, const cgm_vec3 *n, float ior) +{ + cgm_vrefract(&ray->dir, n, ior); +} diff --git a/libs/cgmath/cgmvec3.inl b/libs/cgmath/cgmvec3.inl new file mode 100644 index 0000000..0a2ec4b --- /dev/null +++ b/libs/cgmath/cgmvec3.inl @@ -0,0 +1,205 @@ +/* gph-cmath - C graphics math library + * Copyright (C) 2018 John Tsiombikas + * + * This program is free software. Feel free to use, modify, and/or redistribute + * it under the terms of the MIT/X11 license. See LICENSE for details. + * If you intend to redistribute parts of the code without the LICENSE file + * replace this paragraph with the full contents of the LICENSE file. + */ +static inline void cgm_vcons(cgm_vec3 *v, float x, float y, float z) +{ + v->x = x; + v->y = y; + v->z = z; +} + +static inline void cgm_vadd(cgm_vec3 *a, const cgm_vec3 *b) +{ + a->x += b->x; + a->y += b->y; + a->z += b->z; +} + +static inline void cgm_vadd_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s) +{ + a->x += b->x * s; + a->y += b->y * s; + a->z += b->z * s; +} + +static inline void cgm_vsub(cgm_vec3 *a, const cgm_vec3 *b) +{ + a->x -= b->x; + a->y -= b->y; + a->z -= b->z; +} + +static inline void cgm_vsub_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s) +{ + a->x -= b->x * s; + a->y -= b->y * s; + a->z -= b->z * s; +} + +static inline void cgm_vmul(cgm_vec3 *a, const cgm_vec3 *b) +{ + a->x *= b->x; + a->y *= b->y; + a->z *= b->z; +} + +static inline void cgm_vscale(cgm_vec3 *v, float s) +{ + v->x *= s; + v->y *= s; + v->z *= s; +} + +static inline void cgm_vmul_m4v3(cgm_vec3 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[4] + v->z * m[8] + m[12]; + float y = v->x * m[1] + v->y * m[5] + v->z * m[9] + m[13]; + v->z = v->x * m[2] + v->y * m[6] + v->z * m[10] + m[14]; + v->x = x; + v->y = y; +} + +static inline void cgm_vmul_v3m4(cgm_vec3 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[1] + v->z * m[2] + m[3]; + float y = v->x * m[4] + v->y * m[5] + v->z * m[6] + m[7]; + v->z = v->x * m[8] + v->y * m[9] + v->z * m[10] + m[11]; + v->x = x; + v->y = y; +} + +static inline void cgm_vmul_m3v3(cgm_vec3 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[4] + v->z * m[8]; + float y = v->x * m[1] + v->y * m[5] + v->z * m[9]; + v->z = v->x * m[2] + v->y * m[6] + v->z * m[10]; + v->x = x; + v->y = y; +} + +static inline void cgm_vmul_v3m3(cgm_vec3 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[1] + v->z * m[2]; + float y = v->x * m[4] + v->y * m[5] + v->z * m[6]; + v->z = v->x * m[8] + v->y * m[9] + v->z * m[10]; + v->x = x; + v->y = y; +} + +static inline float cgm_vdot(const cgm_vec3 *a, const cgm_vec3 *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->z; +} + +static inline void cgm_vcross(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b) +{ + res->x = a->y * b->z - a->z * b->y; + res->y = a->z * b->x - a->x * b->z; + res->z = a->x * b->y - a->y * b->x; +} + +static inline float cgm_vlength(const cgm_vec3 *v) +{ + return sqrt(v->x * v->x + v->y * v->y + v->z * v->z); +} + +static inline float cgm_vlength_sq(const cgm_vec3 *v) +{ + return v->x * v->x + v->y * v->y + v->z * v->z; +} + +static inline float cgm_vdist(const cgm_vec3 *a, const cgm_vec3 *b) +{ + float dx = a->x - b->x; + float dy = a->y - b->y; + float dz = a->z - b->z; + return sqrt(dx * dx + dy * dy + dz * dz); +} + +static inline float cgm_vdist_sq(const cgm_vec3 *a, const cgm_vec3 *b) +{ + float dx = a->x - b->x; + float dy = a->y - b->y; + float dz = a->z - b->z; + return dx * dx + dy * dy + dz * dz; +} + +static inline void cgm_vnormalize(cgm_vec3 *v) +{ + float len = cgm_vlength(v); + if(len != 0.0f) { + float s = 1.0f / len; + v->x *= s; + v->y *= s; + v->z *= s; + } +} + +static inline void cgm_vreflect(cgm_vec3 *v, const cgm_vec3 *n) +{ + float ndotv2 = cgm_vdot(v, n) * 2.0f; + v->x -= n->x * ndotv2; + v->y -= n->y * ndotv2; + v->z -= n->z * ndotv2; +} + +static inline int cgm_vrefract(cgm_vec3 *v, const cgm_vec3 *n, float ior) +{ + float sqrt_k; + float ndotv = cgm_vdot(v, n); + float k = 1.0f - ior * ior * (1.0f - ndotv * ndotv); + + if(k < 0.0f) { + cgm_vreflect(v, n); /* TIR */ + return -1; + } + sqrt_k = sqrt(k); + v->x = ior * v->x - (ior * ndotv + sqrt_k) * n->x; + v->y = ior * v->y - (ior * ndotv + sqrt_k) * n->y; + v->z = ior * v->z - (ior * ndotv + sqrt_k) * n->z; + return 0; +} + +static inline void cgm_vrotate_quat(cgm_vec3 *v, const cgm_quat *q) +{ + cgm_quat vq, inv_q = *q, tmp_q = *q; + + cgm_qcons(&vq, v->x, v->y, v->z, 0.0f); + cgm_qinvert(&inv_q); + cgm_qmul(&tmp_q, &vq); + cgm_qmul(&tmp_q, &inv_q); + cgm_vcons(v, tmp_q.x, tmp_q.y, tmp_q.z); +} + +static inline void cgm_vrotate_axis(cgm_vec3 *v, int axis, float angle) +{ + float m[16]; + cgm_mrotation_axis(m, axis, angle); + cgm_vmul_m3v3(v, m); +} + +static inline void cgm_vrotate(cgm_vec3 *v, float angle, float x, float y, float z) +{ + float m[16]; + cgm_mrotation(m, angle, x, y, z); + cgm_vmul_m3v3(v, m); +} + +static inline void cgm_vrotate_euler(cgm_vec3 *v, float a, float b, float c, enum cgm_euler_mode mode) +{ + float m[16]; + cgm_mrotation_euler(m, a, b, c, mode); + cgm_vmul_m3v3(v, m); +} + +static inline void cgm_vlerp(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b, float t) +{ + res->x = a->x + (b->x - a->x) * t; + res->y = a->y + (b->y - a->y) * t; + res->z = a->z + (b->z - a->z) * t; +} diff --git a/libs/cgmath/cgmvec4.inl b/libs/cgmath/cgmvec4.inl new file mode 100644 index 0000000..b68856c --- /dev/null +++ b/libs/cgmath/cgmvec4.inl @@ -0,0 +1,158 @@ +/* gph-cmath - C graphics math library + * Copyright (C) 2018 John Tsiombikas + * + * This program is free software. Feel free to use, modify, and/or redistribute + * it under the terms of the MIT/X11 license. See LICENSE for details. + * If you intend to redistribute parts of the code without the LICENSE file + * replace this paragraph with the full contents of the LICENSE file. + */ +static inline void cgm_wcons(cgm_vec4 *v, float x, float y, float z, float w) +{ + v->x = x; + v->y = y; + v->z = z; + v->w = w; +} + +static inline void cgm_wadd(cgm_vec4 *a, const cgm_vec4 *b) +{ + a->x += b->x; + a->y += b->y; + a->z += b->z; + a->w += b->w; +} + +static inline void cgm_wsub(cgm_vec4 *a, const cgm_vec4 *b) +{ + a->x -= b->x; + a->y -= b->y; + a->z -= b->z; + a->w -= b->w; +} + +static inline void cgm_wmul(cgm_vec4 *a, const cgm_vec4 *b) +{ + a->x *= b->x; + a->y *= b->y; + a->z *= b->z; + a->w *= b->w; +} + +static inline void cgm_wscale(cgm_vec4 *v, float s) +{ + v->x *= s; + v->y *= s; + v->z *= s; + v->w *= s; +} + +static inline void cgm_wmul_m4v4(cgm_vec4 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[4] + v->z * m[8] + v->w * m[12]; + float y = v->x * m[1] + v->y * m[5] + v->z * m[9] + v->w * m[13]; + float z = v->x * m[2] + v->y * m[6] + v->z * m[10] + v->w * m[14]; + v->w = v->x * m[3] + v->y * m[7] + v->z * m[11] + v->w * m[15]; + v->x = x; + v->y = y; + v->z = z; +} + +static inline void cgm_wmul_v4m4(cgm_vec4 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[1] + v->z * m[2] + v->w * m[3]; + float y = v->x * m[4] + v->y * m[5] + v->z * m[6] + v->w * m[7]; + float z = v->x * m[8] + v->y * m[9] + v->z * m[10] + v->w * m[11]; + v->w = v->x * m[12] + v->y * m[13] + v->z * m[14] + v->w * m[15]; + v->x = x; + v->y = y; + v->z = z; +} + +static inline void cgm_wmul_m34v4(cgm_vec4 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[4] + v->z * m[8] + v->w * m[12]; + float y = v->x * m[1] + v->y * m[5] + v->z * m[9] + v->w * m[13]; + v->z = v->x * m[2] + v->y * m[6] + v->z * m[10] + v->w * m[14]; + v->x = x; + v->y = y; +} + +static inline void cgm_wmul_v4m43(cgm_vec4 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[1] + v->z * m[2] + v->w * m[3]; + float y = v->x * m[4] + v->y * m[5] + v->z * m[6] + v->w * m[7]; + v->z = v->x * m[8] + v->y * m[9] + v->z * m[10] + v->w * m[11]; + v->x = x; + v->y = y; +} + +static inline void cgm_wmul_m3v4(cgm_vec4 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[4] + v->z * m[8]; + float y = v->x * m[1] + v->y * m[5] + v->z * m[9]; + v->z = v->x * m[2] + v->y * m[6] + v->z * m[10]; + v->x = x; + v->y = y; +} + +static inline void cgm_wmul_v4m3(cgm_vec4 *v, const float *m) +{ + float x = v->x * m[0] + v->y * m[1] + v->z * m[2]; + float y = v->x * m[4] + v->y * m[5] + v->z * m[6]; + v->z = v->x * m[8] + v->y * m[9] + v->z * m[10]; + v->x = x; + v->y = y; +} + +static inline float cgm_wdot(const cgm_vec4 *a, const cgm_vec4 *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w; +} + +static inline float cgm_wlength(const cgm_vec4 *v) +{ + return sqrt(v->x * v->x + v->y * v->y + v->z * v->z + v->w * v->w); +} + +static inline float cgm_wlength_sq(const cgm_vec4 *v) +{ + return v->x * v->x + v->y * v->y + v->z * v->z + v->w * v->w; +} + +static inline float cgm_wdist(const cgm_vec4 *a, const cgm_vec4 *b) +{ + float dx = a->x - b->x; + float dy = a->y - b->y; + float dz = a->z - b->z; + float dw = a->w - b->w; + return sqrt(dx * dx + dy * dy + dz * dz + dw * dw); +} + +static inline float cgm_wdist_sq(const cgm_vec4 *a, const cgm_vec4 *b) +{ + float dx = a->x - b->x; + float dy = a->y - b->y; + float dz = a->z - b->z; + float dw = a->w - b->w; + return dx * dx + dy * dy + dz * dz + dw * dw; +} + +static inline void cgm_wnormalize(cgm_vec4 *v) +{ + float len = cgm_wlength(v); + if(len != 0.0f) { + float s = 1.0f / len; + v->x *= s; + v->y *= s; + v->z *= s; + v->w *= s; + } +} + +static inline void cgm_wlerp(cgm_vec4 *res, const cgm_vec4 *a, const cgm_vec4 *b, float t) +{ + res->x = a->x + (b->x - a->x) * t; + res->y = a->y + (b->y - a->y) * t; + res->z = a->z + (b->z - a->z) * t; + res->w = a->w + (b->w - a->w) * t; +} diff --git a/libs/psys/Makefile b/libs/psys/Makefile new file mode 100644 index 0000000..9d434ac --- /dev/null +++ b/libs/psys/Makefile @@ -0,0 +1,28 @@ +src = $(wildcard *.c) +obj = $(src:.c=.o) +alib = ../unix/libpsys.a + +sys ?= $(shell uname -s | sed 's/MINGW.*/mingw/') +ifeq ($(sys), mingw) + obj = $(src:.c=.w32.o) + alib = ../w32/libpsys.a +endif +ifeq ($(sys), android-arm64) + obj = $(src:.c=.arm64.o) + alib = ../android/libpsys.a +endif + +CFLAGS = -O3 -ffast-math -fno-strict-aliasing -I.. + +$(alib): $(obj) + $(AR) rcs $@ $(obj) + +%.arm64.o: %.c + $(CC) -o $@ $(CFLAGS) -c $< + +%.w32.o: %.c + $(CC) -o $@ $(CFLAGS) -c $< + +.PHONY: clean +clean: + rm -f $(obj) $(alib) diff --git a/libs/psys/pattr.c b/libs/psys/pattr.c new file mode 100644 index 0000000..9f0f9ad --- /dev/null +++ b/libs/psys/pattr.c @@ -0,0 +1,447 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#ifdef __MSVCRT__ +#include +#else +#include +#endif + +#include "pattr.h" +#include "psys_gl.h" + +enum { + OPT_STR, + OPT_NUM, + OPT_NUM_RANGE, + OPT_VEC, + OPT_VEC_RANGE +}; + +struct cfgopt { + char *name; + int type; + long tm; + char *valstr; + float val[3], valrng[3]; +}; + +static int init_particle_attr(struct psys_particle_attributes *pattr); +static void destroy_particle_attr(struct psys_particle_attributes *pattr); +static struct cfgopt *get_cfg_opt(const char *line); +static void release_cfg_opt(struct cfgopt *opt); +static char *stripspace(char *str); + +static void *tex_cls; +static unsigned int (*load_texture)(const char*, void*) = psys_gl_load_texture; +static void (*unload_texture)(unsigned int, void*) = psys_gl_unload_texture; + + +void psys_texture_loader(unsigned int (*load)(const char*, void*), void (*unload)(unsigned int, void*), void *cls) +{ + load_texture = load; + unload_texture = unload; + tex_cls = cls; +} + +struct psys_attributes *psys_create_attr(void) +{ + struct psys_attributes *attr = malloc(sizeof *attr); + if(attr) { + if(psys_init_attr(attr) == -1) { + free(attr); + attr = 0; + } + } + return attr; +} + +void psys_free_attr(struct psys_attributes *attr) +{ + psys_destroy_attr(attr); + free(attr); +} + +int psys_init_attr(struct psys_attributes *attr) +{ + memset(attr, 0, sizeof *attr); + + if(psys_init_track3(&attr->spawn_range) == -1) + goto err; + if(psys_init_track(&attr->rate) == -1) + goto err; + if(psys_init_anm_rnd(&attr->life) == -1) + goto err; + if(psys_init_anm_rnd(&attr->size) == -1) + goto err; + if(psys_init_anm_rnd3(&attr->dir) == -1) + goto err; + if(psys_init_track3(&attr->grav) == -1) + goto err; + + if(init_particle_attr(&attr->part_attr) == -1) + goto err; + + attr->max_particles = -1; + + anm_set_track_default(&attr->size.value.trk, 1.0); + anm_set_track_default(&attr->life.value.trk, 1.0); + + attr->blending = PSYS_ADD; + return 0; + +err: + psys_destroy_attr(attr); + return -1; +} + + +static int init_particle_attr(struct psys_particle_attributes *pattr) +{ + if(psys_init_track3(&pattr->color) == -1) { + return -1; + } + if(psys_init_track(&pattr->alpha) == -1) { + psys_destroy_track3(&pattr->color); + return -1; + } + if(psys_init_track(&pattr->size) == -1) { + psys_destroy_track3(&pattr->color); + psys_destroy_track(&pattr->alpha); + return -1; + } + + anm_set_track_default(&pattr->color.x, 1.0); + anm_set_track_default(&pattr->color.y, 1.0); + anm_set_track_default(&pattr->color.z, 1.0); + anm_set_track_default(&pattr->alpha.trk, 1.0); + anm_set_track_default(&pattr->size.trk, 1.0); + return 0; +} + + +void psys_destroy_attr(struct psys_attributes *attr) +{ + psys_destroy_track3(&attr->spawn_range); + psys_destroy_track(&attr->rate); + psys_destroy_anm_rnd(&attr->life); + psys_destroy_anm_rnd(&attr->size); + psys_destroy_anm_rnd3(&attr->dir); + psys_destroy_track3(&attr->grav); + + destroy_particle_attr(&attr->part_attr); + + if(attr->tex && unload_texture) { + unload_texture(attr->tex, tex_cls); + } +} + +static void destroy_particle_attr(struct psys_particle_attributes *pattr) +{ + psys_destroy_track3(&pattr->color); + psys_destroy_track(&pattr->alpha); + psys_destroy_track(&pattr->size); +} + +void psys_copy_attr(struct psys_attributes *dest, const struct psys_attributes *src) +{ + dest->tex = src->tex; + + psys_copy_track3(&dest->spawn_range, &src->spawn_range); + psys_copy_track(&dest->rate, &src->rate); + + psys_copy_anm_rnd(&dest->life, &src->life); + psys_copy_anm_rnd(&dest->size, &src->size); + psys_copy_anm_rnd3(&dest->dir, &src->dir); + + psys_copy_track3(&dest->grav, &src->grav); + + dest->drag = src->drag; + dest->max_particles = src->max_particles; + + dest->blending = src->blending; + + /* also copy the particle attributes */ + psys_copy_track3(&dest->part_attr.color, &src->part_attr.color); + psys_copy_track(&dest->part_attr.alpha, &src->part_attr.alpha); + psys_copy_track(&dest->part_attr.size, &src->part_attr.size); +} + +void psys_eval_attr(struct psys_attributes *attr, anm_time_t tm) +{ + float tmp[3]; + + psys_eval_track3(&attr->spawn_range, tm); + psys_eval_track(&attr->rate, tm); + psys_eval_anm_rnd(&attr->life, tm); + psys_eval_anm_rnd(&attr->size, tm); + psys_eval_anm_rnd3(&attr->dir, tm, tmp); + psys_eval_track3(&attr->grav, tm); +} + +int psys_load_attr(struct psys_attributes *attr, const char *fname) +{ + FILE *fp; + int res; + + if(!fname) { + return -1; + } + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "psys_load_attr: failed to read file: %s: %s\n", fname, strerror(errno)); + return -1; + } + res = psys_load_attr_stream(attr, fp); + fclose(fp); + return res; +} + +int psys_load_attr_stream(struct psys_attributes *attr, FILE *fp) +{ + int lineno = 0; + char buf[512]; + struct cfgopt *opt = 0; + + psys_init_attr(attr); + + while(fgets(buf, sizeof buf, fp)) { + + lineno++; + + if(!(opt = get_cfg_opt(buf))) { + continue; + } + + if(strcmp(opt->name, "texture") == 0) { + if(opt->type != OPT_STR) { + goto err; + } + if(!load_texture) { + fprintf(stderr, "particle system requests a texture, but no texture loader available!\n"); + goto err; + } + if(!(attr->tex = load_texture(opt->valstr, tex_cls))) { + fprintf(stderr, "failed to load texture: %s\n", opt->valstr); + goto err; + } + + release_cfg_opt(opt); + continue; + + } else if(strcmp(opt->name, "blending") == 0) { + if(opt->type != OPT_STR) { + goto err; + } + + /* parse blending mode */ + if(strcmp(opt->valstr, "add") == 0 || strcmp(opt->valstr, "additive") == 0) { + attr->blending = PSYS_ADD; + } else if(strcmp(opt->valstr, "alpha") == 0) { + attr->blending = PSYS_ALPHA; + } else { + fprintf(stderr, "invalid blending mode: %s\n", opt->valstr); + goto err; + } + + release_cfg_opt(opt); + continue; + + } else if(opt->type == OPT_STR) { + fprintf(stderr, "invalid particle config: '%s'\n", opt->name); + goto err; + } + + if(strcmp(opt->name, "spawn_range") == 0) { + psys_set_value3(&attr->spawn_range, opt->tm, opt->val[0], opt->val[1], opt->val[2]); + } else if(strcmp(opt->name, "rate") == 0) { + psys_set_value(&attr->rate, opt->tm, opt->val[0]); + } else if(strcmp(opt->name, "life") == 0) { + psys_set_anm_rnd(&attr->life, opt->tm, opt->val[0], opt->valrng[0]); + } else if(strcmp(opt->name, "size") == 0) { + psys_set_anm_rnd(&attr->size, opt->tm, opt->val[0], opt->valrng[0]); + } else if(strcmp(opt->name, "dir") == 0) { + psys_set_anm_rnd3(&attr->dir, opt->tm, opt->val, opt->valrng); + } else if(strcmp(opt->name, "grav") == 0) { + psys_set_value3(&attr->grav, opt->tm, opt->val[0], opt->val[1], opt->val[2]); + } else if(strcmp(opt->name, "drag") == 0) { + attr->drag = opt->val[0]; + } else if(strcmp(opt->name, "pcolor") == 0) { + psys_set_value3(&attr->part_attr.color, opt->tm, opt->val[0], opt->val[1], opt->val[2]); + } else if(strcmp(opt->name, "palpha") == 0) { + psys_set_value(&attr->part_attr.alpha, opt->tm, opt->val[0]); + } else if(strcmp(opt->name, "psize") == 0) { + psys_set_value(&attr->part_attr.size, opt->tm, opt->val[0]); + } else { + fprintf(stderr, "unrecognized particle config option: %s\n", opt->name); + goto err; + } + + release_cfg_opt(opt); + } + + return 0; + +err: + fprintf(stderr, "Line %d: error parsing particle definition\n", lineno); + release_cfg_opt(opt); + return -1; +} + +static struct cfgopt *get_cfg_opt(const char *line) +{ + char *buf, *tmp; + struct cfgopt *opt; + + /* allocate a working buffer on the stack that could fit the current line */ + buf = alloca(strlen(line) + 1); + + line = stripspace((char*)line); + if(line[0] == '#' || !line[0]) { + return 0; /* skip empty lines and comments */ + } + + if(!(opt = malloc(sizeof *opt))) { + return 0; + } + memset(opt, 0, sizeof *opt); + + if(!(opt->valstr = strchr(line, '='))) { + release_cfg_opt(opt); + return 0; + } + *opt->valstr++ = 0; + opt->valstr = stripspace(opt->valstr); + + strcpy(buf, line); + buf = stripspace(buf); + + /* parse the keyframe time specifier if it exists */ + if((tmp = strchr(buf, '('))) { + char *endp; + float tval; + + *tmp++ = 0; + opt->name = malloc(strlen(buf) + 1); + strcpy(opt->name, buf); + + tval = strtod(tmp, &endp); + if(endp == tmp) { /* nada ... */ + opt->tm = 0; + } else if(*endp == 's') { /* seconds suffix */ + opt->tm = (long)(tval * 1000.0f); + } else { + opt->tm = (long)tval; + } + } else { + opt->name = malloc(strlen(buf) + 1); + strcpy(opt->name, buf); + opt->tm = 0; + } + + if(sscanf(opt->valstr, "[%f %f %f] ~ [%f %f %f]", opt->val, opt->val + 1, opt->val + 2, + opt->valrng, opt->valrng + 1, opt->valrng + 2) == 6) { + /* value is a vector range */ + opt->type = OPT_VEC_RANGE; + + } else if(sscanf(opt->valstr, "%f ~ %f", opt->val, opt->valrng) == 2) { + /* value is a number range */ + opt->type = OPT_NUM_RANGE; + opt->val[1] = opt->val[2] = opt->val[0]; + opt->valrng[1] = opt->valrng[2] = opt->valrng[0]; + + } else if(sscanf(opt->valstr, "[%f %f %f]", opt->val, opt->val + 1, opt->val + 2) == 3) { + /* value is a vector */ + opt->type = OPT_VEC; + opt->valrng[0] = opt->valrng[1] = opt->valrng[2] = 0.0f; + + } else if(sscanf(opt->valstr, "%f", opt->val) == 1) { + /* value is a number */ + opt->type = OPT_NUM; + opt->val[1] = opt->val[2] = opt->val[0]; + opt->valrng[0] = opt->valrng[1] = opt->valrng[2] = 0.0f; + + } else if(sscanf(opt->valstr, "\"%s\"", buf) == 1) { + /* just a string... strip the quotes */ + if(buf[strlen(buf) - 1] == '\"') { + buf[strlen(buf) - 1] = 0; + } + opt->type = OPT_STR; + opt->valstr = malloc(strlen(buf) + 1); + assert(opt->valstr); + strcpy(opt->valstr, buf); + } else { + /* fuck it ... */ + release_cfg_opt(opt); + return 0; + } + + return opt; +} + +static void release_cfg_opt(struct cfgopt *opt) +{ + if(opt) { + free(opt->name); + opt->name = 0; + } + opt = 0; +} + + +int psys_save_attr(const struct psys_attributes *attr, const char *fname) +{ + FILE *fp; + int res; + + if(!(fp = fopen(fname, "w"))) { + fprintf(stderr, "psys_save_attr: failed to write file: %s: %s\n", fname, strerror(errno)); + return -1; + } + res = psys_save_attr_stream(attr, fp); + fclose(fp); + return res; +} + +int psys_save_attr_stream(const struct psys_attributes *attr, FILE *fp) +{ + return -1; /* TODO */ +} + + +static char *stripspace(char *str) +{ + char *end; + + while(*str && isspace(*str)) { + str++; + } + + end = str + strlen(str) - 1; + while(end >= str && isspace(*end)) { + *end-- = 0; + } + return str; +} diff --git a/libs/psys/pattr.h b/libs/psys/pattr.h new file mode 100644 index 0000000..da18c1f --- /dev/null +++ b/libs/psys/pattr.h @@ -0,0 +1,84 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef PATTR_H_ +#define PATTR_H_ + +#include +#include "pstrack.h" +#include "rndval.h" + +/* the particle attributes vary from 0 to 1 during its lifetime */ +struct psys_particle_attributes { + struct psys_track3 color; + struct psys_track alpha; + struct psys_track size; +}; + +enum psys_blending { PSYS_ADD, PSYS_ALPHA }; + +struct psys_attributes { + unsigned int tex; /* OpenGL texture to use for the billboard */ + + struct psys_track3 spawn_range; /* radius of emmiter */ + struct psys_track rate; /* spawn rate particles per sec */ + struct psys_anm_rnd life; /* particle life in seconds */ + struct psys_anm_rnd size; /* base particle size */ + struct psys_anm_rnd3 dir; /* particle shoot direction */ + + struct psys_track3 grav; /* external force (usually gravity) */ + float drag; /* I don't think this needs to animate */ + + enum psys_blending blending; + + /* particle attributes */ + struct psys_particle_attributes part_attr; + + /* limits */ + int max_particles; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void psys_texture_loader(unsigned int (*load)(const char*, void*), void (*unload)(unsigned int, void*), void *cls); + +struct psys_attributes *psys_create_attr(void); +void psys_free_attr(struct psys_attributes *attr); + +int psys_init_attr(struct psys_attributes *attr); +void psys_destroy_attr(struct psys_attributes *attr); + +/* copies particle system attributes src to dest + * XXX: dest must have been initialized first + */ +void psys_copy_attr(struct psys_attributes *dest, const struct psys_attributes *src); + +void psys_eval_attr(struct psys_attributes *attr, anm_time_t tm); + +int psys_load_attr(struct psys_attributes *attr, const char *fname); +int psys_load_attr_stream(struct psys_attributes *attr, FILE *fp); + +int psys_save_attr(const struct psys_attributes *attr, const char *fname); +int psys_save_attr_stream(const struct psys_attributes *attr, FILE *fp); + +#ifdef __cplusplus +} +#endif + +#endif /* PATTR_H_ */ diff --git a/libs/psys/pstrack.c b/libs/psys/pstrack.c new file mode 100644 index 0000000..5df8fb4 --- /dev/null +++ b/libs/psys/pstrack.c @@ -0,0 +1,139 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "pstrack.h" + +int psys_init_track(struct psys_track *track) +{ + track->cache_tm = ANM_TIME_INVAL; + + if(anm_init_track(&track->trk) == -1) { + return -1; + } + return 0; +} + +void psys_destroy_track(struct psys_track *track) +{ + anm_destroy_track(&track->trk); +} + +int psys_init_track3(struct psys_track3 *track) +{ + track->cache_tm = ANM_TIME_INVAL; + + if(anm_init_track(&track->x) == -1) { + return -1; + } + if(anm_init_track(&track->y) == -1) { + anm_destroy_track(&track->x); + return -1; + } + if(anm_init_track(&track->z) == -1) { + anm_destroy_track(&track->x); + anm_destroy_track(&track->z); + return -1; + } + return 0; +} + +void psys_destroy_track3(struct psys_track3 *track) +{ + anm_destroy_track(&track->x); + anm_destroy_track(&track->y); + anm_destroy_track(&track->z); +} + +void psys_copy_track(struct psys_track *dest, const struct psys_track *src) +{ + anm_copy_track(&dest->trk, &src->trk); + dest->cache_tm = ANM_TIME_INVAL; +} + +void psys_copy_track3(struct psys_track3 *dest, const struct psys_track3 *src) +{ + anm_copy_track(&dest->x, &src->x); + anm_copy_track(&dest->y, &src->y); + anm_copy_track(&dest->z, &src->z); + + dest->cache_tm = ANM_TIME_INVAL; +} + +void psys_eval_track(struct psys_track *track, anm_time_t tm) +{ + if(track->cache_tm != tm) { + track->cache_tm = tm; + track->cache_val = anm_get_value(&track->trk, tm); + } +} + +void psys_set_value(struct psys_track *track, anm_time_t tm, float v) +{ + anm_set_value(&track->trk, tm, v); + track->cache_tm = ANM_TIME_INVAL; +} + +float psys_get_value(struct psys_track *track, anm_time_t tm) +{ + psys_eval_track(track, tm); + return track->cache_val; +} + +float psys_get_cur_value(struct psys_track *track) +{ + return track->cache_val; +} + + +void psys_eval_track3(struct psys_track3 *track, anm_time_t tm) +{ + if(track->cache_tm != tm) { + track->cache_tm = tm; + track->cache_vec[0] = anm_get_value(&track->x, tm); + track->cache_vec[1] = anm_get_value(&track->y, tm); + track->cache_vec[2] = anm_get_value(&track->z, tm); + } +} + +void psys_set_value3(struct psys_track3 *track, anm_time_t tm, float x, float y, float z) +{ + anm_set_value(&track->x, tm, x); + anm_set_value(&track->y, tm, y); + anm_set_value(&track->z, tm, z); + track->cache_tm = ANM_TIME_INVAL; +} + +float *psys_get_value3(struct psys_track3 *track, anm_time_t tm, float *vec) +{ + psys_eval_track3(track, tm); + if(vec) { + vec[0] = track->cache_vec[0]; + vec[1] = track->cache_vec[1]; + vec[2] = track->cache_vec[2]; + } + return track->cache_vec; +} + +float *psys_get_cur_value3(struct psys_track3 *track, float *vec) +{ + if(vec) { + vec[0] = track->cache_vec[0]; + vec[1] = track->cache_vec[1]; + vec[2] = track->cache_vec[2]; + } + return track->cache_vec; +} diff --git a/libs/psys/pstrack.h b/libs/psys/pstrack.h new file mode 100644 index 0000000..e71f042 --- /dev/null +++ b/libs/psys/pstrack.h @@ -0,0 +1,66 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef PSTRACK_H_ +#define PSTRACK_H_ + +#include + +struct psys_track { + struct anm_track trk; + + anm_time_t cache_tm; + float cache_val; +}; + +struct psys_track3 { + struct anm_track x, y, z; + + anm_time_t cache_tm; + float cache_vec[3]; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int psys_init_track(struct psys_track *track); +void psys_destroy_track(struct psys_track *track); + +int psys_init_track3(struct psys_track3 *track); +void psys_destroy_track3(struct psys_track3 *track); + +/* XXX dest must have been initialized first */ +void psys_copy_track(struct psys_track *dest, const struct psys_track *src); +void psys_copy_track3(struct psys_track3 *dest, const struct psys_track3 *src); + +void psys_eval_track(struct psys_track *track, anm_time_t tm); +void psys_set_value(struct psys_track *track, anm_time_t tm, float v); +float psys_get_value(struct psys_track *track, anm_time_t tm); +float psys_get_cur_value(struct psys_track *track); + +void psys_eval_track3(struct psys_track3 *track, anm_time_t tm); +void psys_set_value3(struct psys_track3 *track, anm_time_t tm, float x, float y, float z); +/* returns pointer to the internal cached value, and if vec is not null, also copies it there */ +float *psys_get_value3(struct psys_track3 *track, anm_time_t tm, float *vec); +float *psys_get_cur_value3(struct psys_track3 *track, float *vec); + +#ifdef __cplusplus +} +#endif + +#endif /* PSTRACK_H_ */ diff --git a/libs/psys/psys.c b/libs/psys/psys.c new file mode 100644 index 0000000..6825ae7 --- /dev/null +++ b/libs/psys/psys.c @@ -0,0 +1,370 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#include "psys.h" +#include + +static int spawn_particle(struct psys_emitter *em, struct psys_particle *p); +static void update_particle(struct psys_emitter *em, struct psys_particle *p, long tm, float dt, void *cls); + +/* particle pool */ +static struct psys_particle *ppool; +static int ppool_size; +static pthread_mutex_t pool_lock = PTHREAD_MUTEX_INITIALIZER; + +static struct psys_particle *palloc(void); +static void pfree(struct psys_particle *p); + +/* --- constructors and shit --- */ + +struct psys_emitter *psys_create(void) +{ + struct psys_emitter *em; + + if(!(em = malloc(sizeof *em))) { + return 0; + } + if(psys_init(em) == -1) { + free(em); + return 0; + } + return em; +} + +void psys_free(struct psys_emitter *em) +{ + psys_destroy(em); + free(em); +} + +int psys_init(struct psys_emitter *em) +{ + memset(em, 0, sizeof *em); + + if(anm_init_node(&em->prs) == -1) { + return -1; + } + if(psys_init_attr(&em->attr) == -1) { + anm_destroy_node(&em->prs); + return -1; + } + + em->spawn = 0; /* no custom spawning, just the defaults */ + em->update = update_particle; + return 0; +} + +void psys_destroy(struct psys_emitter *em) +{ + struct psys_particle *part; + + part = em->plist; + while(part) { + struct psys_particle *tmp = part; + part = part->next; + pfree(tmp); + } + + psys_destroy_attr(&em->attr); +} + +void psys_set_pos(struct psys_emitter *em, const float *pos, long tm) +{ + anm_set_position(&em->prs, pos, ANM_MSEC2TM(tm)); +} + +void psys_set_pos3f(struct psys_emitter *em, float x, float y, float z, long tm) +{ + anm_set_position3f(&em->prs, x, y, z, ANM_MSEC2TM(tm)); +} + +void psys_set_rot(struct psys_emitter *em, const float *qrot, long tm) +{ + anm_set_rotation(&em->prs, qrot, ANM_MSEC2TM(tm)); +} + +void psys_set_pivot(struct psys_emitter *em, const float *pivot) +{ + anm_set_pivot(&em->prs, pivot[0], pivot[1], pivot[2]); +} + +void psys_set_pivot3f(struct psys_emitter *em, float x, float y, float z) +{ + anm_set_pivot(&em->prs, x, y, z); +} + +void psys_get_pos(struct psys_emitter *em, float *pos, long tm) +{ + anm_get_node_position(&em->prs, pos, ANM_MSEC2TM(tm)); +} + +void psys_get_rot(struct psys_emitter *em, float *qrot, long tm) +{ + anm_get_node_rotation(&em->prs, qrot, ANM_MSEC2TM(tm)); +} + +void psys_get_pivot(struct psys_emitter *em, float *pivot) +{ + anm_get_pivot(&em->prs, pivot, pivot + 1, pivot + 2); +} + +void psys_clear_collision_planes(struct psys_emitter *em) +{ + struct psys_plane *plane; + + plane = em->planes; + while(plane) { + struct psys_plane *tmp = plane; + plane = plane->next; + free(tmp); + } +} + +int psys_add_collision_plane(struct psys_emitter *em, const float *plane, float elast) +{ + struct psys_plane *node; + + if(!(node = malloc(sizeof *node))) { + return -1; + } + node->nx = plane[0]; + node->ny = plane[1]; + node->nz = plane[2]; + node->d = plane[3]; + node->elasticity = elast; + node->next = em->planes; + em->planes = node; + return 0; +} + +void psys_add_particle(struct psys_emitter *em, struct psys_particle *p) +{ + p->next = em->plist; + em->plist = p; + + em->pcount++; +} + +void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls) +{ + em->spawn = func; + em->spawn_cls = cls; +} + +void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls) +{ + em->update = func; + em->upd_cls = cls; +} + +void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw, + psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls) +{ + em->draw = draw; + em->draw_start = start; + em->draw_end = end; + em->draw_cls = cls; +} + +/* --- update and render --- */ + +void psys_update(struct psys_emitter *em, long tm) +{ + long delta_ms, spawn_tm, spawn_dt; + float dt; + int i, spawn_count; + struct psys_particle *p, pdummy; + anm_time_t atm = ANM_MSEC2TM(tm); + + if(!em->update) { + static int once; + if(!once) { + once = 1; + fprintf(stderr, "psys_update called without an update callback\n"); + } + } + + delta_ms = tm - em->last_update; + if(delta_ms <= 0) { + return; + } + dt = (float)delta_ms / 1000.0f; + + psys_eval_attr(&em->attr, atm); + + /* how many particles to spawn for this interval ? */ + em->spawn_acc += psys_get_cur_value(&em->attr.rate) * delta_ms; + if(em->spawn_acc >= 1000) { + spawn_count = em->spawn_acc / 1000; + em->spawn_acc %= 1000; + } else { + spawn_count = 0; + } + + if(spawn_count) { + spawn_dt = delta_ms / spawn_count; + } + spawn_tm = em->last_update; + for(i=0; iattr.max_particles >= 0 && em->pcount >= em->attr.max_particles) { + break; + } + + /* update emitter position for this spawning */ + anm_get_position(&em->prs, em->cur_pos, ANM_MSEC2TM(spawn_tm)); + + if(!(p = palloc())) { + return; + } + if(spawn_particle(em, p) == -1) { + pfree(p); + } + spawn_tm += spawn_dt; + } + + /* update all particles */ + p = em->plist; + while(p) { + em->update(em, p, tm, dt, em->upd_cls); + p = p->next; + } + + /* cleanup dead particles */ + pdummy.next = em->plist; + p = &pdummy; + while(p->next) { + if(p->next->life <= 0) { + struct psys_particle *tmp = p->next; + p->next = p->next->next; + pfree(tmp); + em->pcount--; + } else { + p = p->next; + } + } + em->plist = pdummy.next; + + em->last_update = tm; +} + +void psys_draw(const struct psys_emitter *em) +{ + struct psys_particle *p; + + assert(em->draw); + + if(em->draw_start) { + em->draw_start(em, em->draw_cls); + } + + p = em->plist; + while(p) { + em->draw(em, p, em->draw_cls); + p = p->next; + } + + if(em->draw_end) { + em->draw_end(em, em->draw_cls); + } +} + +static int spawn_particle(struct psys_emitter *em, struct psys_particle *p) +{ + int i; + struct psys_rnd3 rpos; + + for(i=0; i<3; i++) { + rpos.value[i] = em->cur_pos[i]; + } + psys_get_cur_value3(&em->attr.spawn_range, rpos.range); + + psys_eval_rnd3(&rpos, &p->x); + psys_eval_anm_rnd3(&em->attr.dir, PSYS_EVAL_CUR, &p->vx); + p->base_size = psys_eval_anm_rnd(&em->attr.size, PSYS_EVAL_CUR); + p->max_life = p->life = psys_eval_anm_rnd(&em->attr.life, PSYS_EVAL_CUR); + + p->pattr = &em->attr.part_attr; + + if(em->spawn && em->spawn(em, p, em->spawn_cls) == -1) { + return -1; + } + + psys_add_particle(em, p); + return 0; +} + +static void update_particle(struct psys_emitter *em, struct psys_particle *p, long tm, float dt, void *cls) +{ + float accel[3], grav[3]; + anm_time_t t; + + psys_get_cur_value3(&em->attr.grav, grav); + + accel[0] = grav[0] - p->vx * em->attr.drag; + accel[1] = grav[1] - p->vy * em->attr.drag; + accel[2] = grav[2] - p->vz * em->attr.drag; + + p->vx += accel[0] * dt; + p->vy += accel[1] * dt; + p->vz += accel[2] * dt; + + p->x += p->vx * dt; + p->y += p->vy * dt; + p->z += p->vz * dt; + + /* update particle attributes */ + t = (anm_time_t)(1000.0f * (p->max_life - p->life) / p->max_life); + + psys_get_value3(&p->pattr->color, t, &p->r); + p->alpha = psys_get_value(&p->pattr->alpha, t); + p->size = p->base_size * psys_get_value(&p->pattr->size, t); + + p->life -= dt; +} + +/* --- particle allocation pool --- */ + +static struct psys_particle *palloc(void) +{ + struct psys_particle *p; + + pthread_mutex_lock(&pool_lock); + if(ppool) { + p = ppool; + ppool = ppool->next; + ppool_size--; + } else { + p = malloc(sizeof *p); + } + pthread_mutex_unlock(&pool_lock); + + return p; +} + +static void pfree(struct psys_particle *p) +{ + pthread_mutex_lock(&pool_lock); + p->next = ppool; + ppool = p; + ppool_size++; + pthread_mutex_unlock(&pool_lock); +} diff --git a/libs/psys/psys.h b/libs/psys/psys.h new file mode 100644 index 0000000..bc32d1f --- /dev/null +++ b/libs/psys/psys.h @@ -0,0 +1,140 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef LIBPSYS_H_ +#define LIBPSYS_H_ + +#include +#include "rndval.h" +#include "pattr.h" + +struct psys_particle; +struct psys_emitter; + +typedef int (*psys_spawn_func_t)(struct psys_emitter*, struct psys_particle*, void*); +typedef void (*psys_update_func_t)(struct psys_emitter*, struct psys_particle*, long, float, void*); + +typedef void (*psys_draw_func_t)(const struct psys_emitter*, const struct psys_particle*, void*); +typedef void (*psys_draw_start_func_t)(const struct psys_emitter*, void*); +typedef void (*psys_draw_end_func_t)(const struct psys_emitter*, void*); + + +struct psys_plane { + float nx, ny, nz, d; + float elasticity; + struct psys_plane *next; +}; + + +struct psys_emitter { + struct anm_node prs; + float cur_pos[3]; + + struct psys_attributes attr; + + /* list of active particles */ + struct psys_particle *plist; + int pcount; /* number of active particles */ + + /* list of collision planes */ + struct psys_plane *planes; + + /* custom spawn closure */ + void *spawn_cls; + psys_spawn_func_t spawn; + + /* custom particle update closure */ + void *upd_cls; + psys_update_func_t update; + + /* custom draw closure */ + void *draw_cls; + psys_draw_func_t draw; + psys_draw_start_func_t draw_start; + psys_draw_end_func_t draw_end; + + long spawn_acc; /* partial spawn accumulator */ + long last_update; /* last update time (to calc dt) */ +}; + + +struct psys_particle { + float x, y, z; + float vx, vy, vz; + float life, max_life; + float base_size; + + struct psys_particle_attributes *pattr; + + /* current particle attr values calculated during update */ + float r, g, b; + float alpha, size; + + struct psys_particle *next; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +struct psys_emitter *psys_create(void); +void psys_free(struct psys_emitter *em); + +int psys_init(struct psys_emitter *em); +void psys_destroy(struct psys_emitter *em); + +/* set properties */ + +/* set emitter position. pos should point to 3 floats (xyz) */ +void psys_set_pos(struct psys_emitter *em, const float *pos, long tm); +void psys_set_pos3f(struct psys_emitter *em, float x, float y, float z, long tm); +/* set emitter rotation quaternion. qrot should point to 4 floats (xyzw) */ +void psys_set_rot(struct psys_emitter *em, const float *qrot, long tm); +void psys_set_rot4f(struct psys_emitter *em, float x, float y, float z, float w, long tm); +/* set emitter rotation by defining a rotation axis, and an angle */ +void psys_set_rot_axis(struct psys_emitter *em, float angle, float x, float y, float z, long tm); +/* set rotation pivot point. pos should point to 3 floats (xyz) */ +void psys_set_pivot(struct psys_emitter *em, const float *pivot); +void psys_set_pivot3f(struct psys_emitter *em, float x, float y, float z); + +/* pos should be a pointer to 3 floats (xyz) */ +void psys_get_pos(struct psys_emitter *em, float *pos, long tm); +/* qrot should be a pointer to 4 floats (xyzw) */ +void psys_get_rot(struct psys_emitter *em, float *qrot, long tm); +/* pivot should be a pointer to 3 floats (xyz) */ +void psys_get_pivot(struct psys_emitter *em, float *pivot); + +void psys_clear_collision_planes(struct psys_emitter *em); +int psys_add_collision_plane(struct psys_emitter *em, const float *plane, float elast); + +void psys_add_particle(struct psys_emitter *em, struct psys_particle *p); + +void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls); +void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls); +void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw, + psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls); + +/* update and render */ + +void psys_update(struct psys_emitter *em, long tm); +void psys_draw(const struct psys_emitter *em); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBPSYS_H_ */ diff --git a/libs/psys/rndval.c b/libs/psys/rndval.c new file mode 100644 index 0000000..97a952d --- /dev/null +++ b/libs/psys/rndval.c @@ -0,0 +1,134 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include "rndval.h" + +int psys_init_anm_rnd(struct psys_anm_rnd *r) +{ + if(psys_init_track(&r->value) == -1) { + return -1; + } + if(psys_init_track(&r->range) == -1) { + psys_destroy_track(&r->value); + return -1; + } + return 0; +} + +void psys_destroy_anm_rnd(struct psys_anm_rnd *r) +{ + psys_destroy_track(&r->value); + psys_destroy_track(&r->range); +} + +int psys_init_anm_rnd3(struct psys_anm_rnd3 *r) +{ + if(psys_init_track3(&r->value) == -1) { + return -1; + } + if(psys_init_track3(&r->range) == -1) { + psys_destroy_track3(&r->value); + return -1; + } + return 0; +} + +void psys_destroy_anm_rnd3(struct psys_anm_rnd3 *r) +{ + psys_destroy_track3(&r->value); + psys_destroy_track3(&r->range); +} + +void psys_copy_anm_rnd(struct psys_anm_rnd *dest, const struct psys_anm_rnd *src) +{ + psys_copy_track(&dest->value, &src->value); + psys_copy_track(&dest->range, &src->range); +} + +void psys_copy_anm_rnd3(struct psys_anm_rnd3 *dest, const struct psys_anm_rnd3 *src) +{ + psys_copy_track3(&dest->value, &src->value); + psys_copy_track3(&dest->range, &src->range); +} + +void psys_set_rnd(struct psys_rnd *r, float val, float range) +{ + r->value = val; + r->range = range; +} + +void psys_set_rnd3(struct psys_rnd3 *r, const float *val, const float *range) +{ + int i; + for(i=0; i<3; i++) { + r->value[i] = val[i]; + r->range[i] = range[i]; + } +} + +void psys_set_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm, float val, float range) +{ + psys_set_value(&r->value, tm, val); + psys_set_value(&r->range, tm, range); +} + +void psys_set_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm, const float *val, const float *range) +{ + psys_set_value3(&r->value, tm, val[0], val[1], val[2]); + psys_set_value3(&r->range, tm, range[0], range[1], range[2]); +} + + +float psys_eval_rnd(struct psys_rnd *r) +{ + return r->value + r->range * (float)rand() / (float)RAND_MAX - 0.5 * r->range; +} + +void psys_eval_rnd3(struct psys_rnd3 *r, float *val) +{ + val[0] = r->value[0] + r->range[0] * (float)rand() / (float)RAND_MAX - 0.5 * r->range[0]; + val[1] = r->value[1] + r->range[1] * (float)rand() / (float)RAND_MAX - 0.5 * r->range[1]; + val[2] = r->value[2] + r->range[2] * (float)rand() / (float)RAND_MAX - 0.5 * r->range[2]; +} + + +float psys_eval_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm) +{ + struct psys_rnd tmp; + if(tm == ANM_TIME_INVAL) { + tmp.value = psys_get_cur_value(&r->value); + tmp.range = psys_get_cur_value(&r->range); + } else { + tmp.value = psys_get_value(&r->value, tm); + tmp.range = psys_get_value(&r->range, tm); + } + return psys_eval_rnd(&tmp); +} + +void psys_eval_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm, float *val) +{ + struct psys_rnd3 tmp; + if(tm == ANM_TIME_INVAL) { + psys_get_cur_value3(&r->value, tmp.value); + psys_get_cur_value3(&r->range, tmp.range); + } else { + psys_get_value3(&r->value, tm, tmp.value); + psys_get_value3(&r->range, tm, tmp.range); + } + psys_eval_rnd3(&tmp, val); +} diff --git a/libs/psys/rndval.h b/libs/psys/rndval.h new file mode 100644 index 0000000..c28a5a0 --- /dev/null +++ b/libs/psys/rndval.h @@ -0,0 +1,69 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef RNDVAL_H_ +#define RNDVAL_H_ + +#include "pstrack.h" + +struct psys_rnd { + float value, range; +}; + +struct psys_rnd3 { + float value[3], range[3]; +}; + +struct psys_anm_rnd { + struct psys_track value, range; +}; + +struct psys_anm_rnd3 { + struct psys_track3 value, range; +}; + +#define PSYS_EVAL_CUR ANM_TIME_INVAL + +#ifdef __cplusplus +extern "C" { +#endif + +int psys_init_anm_rnd(struct psys_anm_rnd *v); +void psys_destroy_anm_rnd(struct psys_anm_rnd *v); +int psys_init_anm_rnd3(struct psys_anm_rnd3 *v); +void psys_destroy_anm_rnd3(struct psys_anm_rnd3 *v); + +void psys_copy_anm_rnd(struct psys_anm_rnd *dest, const struct psys_anm_rnd *src); +void psys_copy_anm_rnd3(struct psys_anm_rnd3 *dest, const struct psys_anm_rnd3 *src); + +void psys_set_rnd(struct psys_rnd *r, float val, float range); +void psys_set_rnd3(struct psys_rnd3 *r, const float *val, const float *range); + +void psys_set_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm, float val, float range); +void psys_set_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm, const float *val, const float *range); + +float psys_eval_rnd(struct psys_rnd *r); +void psys_eval_rnd3(struct psys_rnd3 *r, float *val); + +float psys_eval_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm); +void psys_eval_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm, float *val); + +#ifdef __cplusplus +} +#endif + +#endif /* RNDVAL_H_ */ -- 1.7.10.4