# HG changeset patch # User John Tsiombikas # Date 1415531016 -7200 # Node ID f234630e38ff9d8cda8d139c3d33d6e6a87e07e0 initial commit diff -r 000000000000 -r f234630e38ff .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,6 @@ +\.o$ +\.d$ +\.swp$ +\.jpg$ +\.png$ +\.cubemap$ diff -r 000000000000 -r f234630e38ff COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Sun Nov 09 13:03:36 2014 +0200 @@ -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 -r 000000000000 -r f234630e38ff Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,50 @@ +src = $(wildcard src/*.cc) $(wildcard vmath/*.cc) +csrc = $(wildcard src/*.c) $(wildcard vmath/*.c) $(wildcard anim/*.c) +obj = $(src:.cc=.o) $(csrc:.c=.o) +dep = $(obj:.o=.d) +bin = ray1 + +opt = -O3 -march=native +dbg = -g +#prof = -pg +CFLAGS = -pedantic -Wall $(dbg) $(opt) $(prof) -I. +CXXFLAGS = -std=c++11 $(CFLAGS) +LDFLAGS = $(prof) $(libgl) -limago + +ifeq ($(shell uname -s), Darwin) + # the gcc shipping with Darwin is ancient and doesn't support C++11 + # use clang instead. + CXX = clang++ + CPP = clang -E + CXXFLAGS += -stdlib=libc++ + LDFLAGS += -stdlib=libc++ + + libgl = -framework OpenGL -framework GLUT -lGLEW +else + libgl = -lGL -lGLU -lglut -lGLEW +endif + +ifeq ($(CC), icc) + libomp = -liomp5 +else + libomp = -lgomp +endif + +$(bin): $(obj) + $(CXX) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(bin) + +.PHONY: cleandep +cleandep: clean + rm -f $(dep) diff -r 000000000000 -r f234630e38ff README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,11 @@ +To compile just type make. Install a recent version of the g++ compiler, +preferable 4.7, as the code is written according to the recent C++11 standard. + +Copyright 2012 John Tsiombikas +You may use, modify, and distribute the program under the terms of the GNU +General Public License version 3, or at your option, any later version published +by the Free Software Foundation. See COPYING for details. + +Dependencies: + - GLUT: http://freeglut.sourceforge.net + - libimago: http://code.google.com/p/libimago diff -r 000000000000 -r f234630e38ff anim/anim.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anim/anim.c Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,498 @@ +#include +#include +#include +#include "anim.h" +#include "dynarr.h" + +#define ROT_USE_SLERP + +static void invalidate_cache(struct anm_node *node); + +int anm_init_node(struct anm_node *node) +{ + 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 */ + }; + + memset(node, 0, sizeof *node); + + /* initialize thread-local matrix cache */ + pthread_key_create(&node->cache_key, 0); + pthread_mutex_init(&node->cache_list_lock, 0); + + for(i=0; itracks + i) == -1) { + for(j=0; jtracks + i); + } + } + anm_set_track_default(node->tracks + i, defaults[i]); + } + return 0; +} + +void anm_destroy_node(struct anm_node *node) +{ + int i; + free(node->name); + + for(i=0; itracks + i); + } + + /* 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); + } +} + +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_set_interpolator(struct anm_node *node, enum anm_interpolator in) +{ + int i; + + for(i=0; itracks + i, in); + } + invalidate_cache(node); +} + +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) +{ + int i; + + for(i=0; itracks + i, ex); + } + invalidate_cache(node); +} + +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_position(struct anm_node *node, vec3_t pos, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x); + anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y); + anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z); + invalidate_cache(node); +} + +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) +{ + vec3_t v; + v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm); + v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm); + v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm); + return v; +} + +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x); + anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y); + anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z); + anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w); + invalidate_cache(node); +} + +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) +{ +#ifndef ROT_USE_SLERP + quat_t q; + q.x = anm_get_value(node->tracks + ANM_TRACK_ROT_X, tm); + q.y = anm_get_value(node->tracks + ANM_TRACK_ROT_Y, tm); + q.z = anm_get_value(node->tracks + ANM_TRACK_ROT_Z, tm); + q.w = anm_get_value(node->tracks + ANM_TRACK_ROT_W, tm); + return q; +#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; + quat_t q, q1, q2; + + track_x = node->tracks + ANM_TRACK_ROT_X; + track_y = node->tracks + ANM_TRACK_ROT_Y; + track_z = node->tracks + ANM_TRACK_ROT_Z; + track_w = node->tracks + ANM_TRACK_ROT_W; + + if(!track_x->count) { + q.x = track_x->def_val; + q.y = track_y->def_val; + q.z = track_z->def_val; + q.w = track_w->def_val; + return q; + } + + last_idx = track_x->count - 1; + + tstart = track_x->keys[0].time; + tend = track_x->keys[last_idx].time; + + if(tstart == tend) { + q.x = track_x->keys[0].val; + q.y = track_y->keys[0].val; + q.z = track_z->keys[0].val; + q.w = track_w->keys[0].val; + return q; + } + + 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) { + q.x = track_x->keys[idx0].val; + q.y = track_y->keys[idx0].val; + q.z = track_z->keys[idx0].val; + q.w = track_w->keys[idx0].val; + return q; + } + + 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; + + /*q1 = quat_normalize(q1); + q2 = quat_normalize(q2);*/ + + return quat_slerp(q1, q2, t); +#endif +} + +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x); + anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y); + anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z); + invalidate_cache(node); +} + +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) +{ + vec3_t v; + v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm); + v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm); + v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm); + return v; +} + + +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm) +{ + mat4_t xform; + vec3_t pos = {0.0, 0.0, 0.0}; + + if(!node->parent) { + return anm_get_node_position(node, tm); + } + + anm_get_matrix(node, xform, tm); + return v3_transform(pos, xform); +} + +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm) +{ + quat_t rot, prot; + rot = anm_get_node_rotation(node, tm); + + if(!node->parent) { + return rot; + } + + prot = anm_get_rotation(node->parent, tm); + return quat_mul(prot, rot); +} + +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm) +{ + vec3_t s, ps; + s = anm_get_node_scaling(node, tm); + + if(!node->parent) { + return s; + } + + ps = anm_get_scaling(node->parent, tm); + return v3_mul(s, ps); +} + +void anm_set_pivot(struct anm_node *node, vec3_t piv) +{ + node->pivot = piv; +} + +vec3_t anm_get_pivot(struct anm_node *node) +{ + return node->pivot; +} + +void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ + int i; + mat4_t rmat; + vec3_t pos, scale; + quat_t rot; + + pos = anm_get_node_position(node, tm); + rot = anm_get_node_rotation(node, tm); + scale = anm_get_node_scaling(node, tm); + + m4_set_translation(mat, node->pivot.x, node->pivot.y, node->pivot.z); + + quat_to_mat4(rmat, rot); + for(i=0; i<3; i++) { + mat[i][0] = rmat[i][0]; + mat[i][1] = rmat[i][1]; + mat[i][2] = rmat[i][2]; + } + /* this loop is equivalent to: m4_mult(mat, mat, rmat); */ + + mat[0][0] *= scale.x; mat[0][1] *= scale.y; mat[0][2] *= scale.z; mat[0][3] += pos.x; + mat[1][0] *= scale.x; mat[1][1] *= scale.y; mat[1][2] *= scale.z; mat[1][3] += pos.y; + mat[2][0] *= scale.x; mat[2][1] *= scale.y; mat[2][2] *= scale.z; mat[2][3] += pos.z; + + m4_translate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z); + + /* that's basically: pivot * rotation * translation * scaling * -pivot */ +} + +void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ + mat4_t tmp; + anm_get_node_matrix(node, tmp, tm); + m4_inverse(mat, tmp); +} + +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ + 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); + } + + if(cache->time != tm) { + anm_get_node_matrix(node, cache->matrix, tm); + + if(node->parent) { + mat4_t parent_mat; + + anm_get_matrix(node->parent, parent_mat, tm); + m4_mult(cache->matrix, parent_mat, cache->matrix); + } + cache->time = tm; + } + m4_copy(mat, cache->matrix); +} + +void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ + 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); + } + + if(cache->inv_time != tm) { + anm_get_matrix(node, mat, tm); + m4_inverse(cache->inv_matrix, mat); + cache->inv_time = tm; + } + m4_copy(mat, cache->inv_matrix); +} + +anm_time_t anm_get_start_time(struct anm_node *node) +{ + int i; + struct anm_node *c; + anm_time_t res = LONG_MAX; + + for(i=0; itracks[i].count) { + anm_time_t tm = node->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; + struct anm_node *c; + anm_time_t res = LONG_MIN; + + for(i=0; itracks[i].count) { + anm_time_t tm = node->tracks[i].keys[node->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 mat_cache *cache = pthread_getspecific(node->cache_key); + if(cache) { + cache->time = cache->inv_time = ANM_TIME_INVAL; + } +} diff -r 000000000000 -r f234630e38ff anim/anim.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anim/anim.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,117 @@ +#ifndef LIBANIM_H_ +#define LIBANIM_H_ + +#include "config.h" + +#include + +#include +#include +#include +#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_node { + char *name; + + struct anm_track tracks[ANM_NUM_TRACKS]; + vec3_t pivot; + + /* matrix cache */ + struct mat_cache { + mat4_t matrix, inv_matrix; + anm_time_t time, inv_time; + struct mat_cache *next; + } *cache_list; + pthread_key_t cache_key; + pthread_mutex_t cache_list_lock; + + struct anm_node *parent; + struct anm_node *child; + struct anm_node *next; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* 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); + +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in); +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex); + +/* 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_position(struct anm_node *node, vec3_t pos, anm_time_t tm); +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm); + +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm); +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm); + +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm); +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm); + +/* these three return the full p/r/s taking hierarchy into account */ +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm); +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm); +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm); + +void anm_set_pivot(struct anm_node *node, vec3_t pivot); +vec3_t anm_get_pivot(struct anm_node *node); + +/* these calculate the matrix and inverse matrix of this node alone */ +void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); +void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); + +/* 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... + */ +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); +void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, 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); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBANIM_H_ */ diff -r 000000000000 -r f234630e38ff anim/config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anim/config.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,6 @@ +#ifndef ANIM_CONFIG_H_ +#define ANIM_CONFIG_H_ + +#undef ANIM_THREAD_SAFE + +#endif /* ANIM_CONFIG_H_ */ diff -r 000000000000 -r f234630e38ff anim/dynarr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anim/dynarr.c Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,122 @@ +#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 *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 dynarr_free(void *da) +{ + if(da) { + free(DESC(da)); + } +} + +void *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 dynarr_empty(void *da) +{ + return DESC(da)->nelem ? 0 : 1; +} + +int dynarr_size(void *da) +{ + return DESC(da)->nelem; +} + + +/* stack semantics */ +void *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 = 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 *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 = 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 -r 000000000000 -r f234630e38ff anim/dynarr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anim/dynarr.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,16 @@ +#ifndef DYNARR_H_ +#define DYNARR_H_ + +void *dynarr_alloc(int elem, int szelem); +void dynarr_free(void *da); +void *dynarr_resize(void *da, int elem); + +int dynarr_empty(void *da); +int dynarr_size(void *da); + +/* stack semantics */ +void *dynarr_push(void *da, void *item); +void *dynarr_pop(void *da); + + +#endif /* DYNARR_H_ */ diff -r 000000000000 -r f234630e38ff anim/track.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anim/track.c Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,316 @@ +#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 = 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) +{ + 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, struct anm_track *src) +{ + free(dest->name); + if(dest->keys) { + 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 = 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 = 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 - 1 : 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 -r 000000000000 -r f234630e38ff anim/track.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anim/track.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,101 @@ +/* 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, 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 -r 000000000000 -r f234630e38ff sdr/postsdr.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/postsdr.glsl Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,9 @@ +uniform sampler2D fb; + +void main() +{ + vec4 pixel = texture2D(fb, gl_TexCoord[0].st); + + gl_FragColor.rgb = pow(pixel.rgb, vec3(1.0 / 2.2)); + gl_FragColor.a = pixel.a; +} diff -r 000000000000 -r f234630e38ff sdr/rt.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/rt.glsl Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,523 @@ +/* vi:set filetype=glsl ts=4 sw=4: */ +#version 120 +#extension GL_ARB_gpu_shader5 : enable + +#define M_PI 3.1415926 + +#define INIT_EVERYTHING +#define USE_XFORM +#define OBJ_LINE_WIDTH 16.0 + +struct Ray { + vec3 origin, dir; +}; + +struct Material { + vec3 diffuse, specular; + float shininess; + vec4 megatex_rect; + float reflectivity; +}; + +struct HitPoint { + float dist; + vec3 pos, normal; + vec2 texcoord; + struct Material mat; +}; + +struct Sphere { + float index; + vec3 pos; + float radius; + struct Material mat; +}; + +struct Plane { + float index; + vec3 normal; + float dist; + struct Material mat; +}; + +struct Box { + float index; + vec3 min, max; + struct Material mat; +}; + +struct Light { + vec3 pos, color; +}; + +vec3 shade(in Ray ray, in HitPoint hit); +bool find_intersection(in Ray ray, out HitPoint hit); +bool sphere_intersect(in Sphere sph, in Ray ray, out HitPoint pt); +bool plane_intersect(in Plane plane, in Ray ray, out HitPoint pt); +bool box_intersect(in Box box, in Ray ray, out HitPoint pt); +vec3 transform(in vec3 v, in mat4 inv_xform); +Ray transform(in Ray ray, in mat4 xform, in mat4 inv_xform); +Ray get_primary_ray(); + +Sphere read_sphere(in float idx); +Plane read_plane(in float idx); +Box read_box(in float idx); +Material read_material(in sampler2D tex, in float ty); +void read_xform(in float idx, out mat4 xform, out mat4 inv_xform); + +uniform sampler2D tex_raydir; +uniform sampler2D tex_spheres, tex_planes, tex_boxes; +uniform sampler2D tex_megatex; +uniform sampler2D tex_xforms; +uniform samplerCube tex_env; +uniform vec2 fog; + +uniform Light lights[8]; +uniform int num_lights; + +int num_spheres, num_planes, num_boxes; +float sph_tex_sz, plane_tex_sz, box_tex_sz, xform_tex_sz; + +#ifdef INIT_EVERYTHING +Material default_material; +#endif + +void main() +{ +#ifdef INIT_EVERYTHING + default_material.diffuse = default_material.specular = vec3(0.0, 0.0, 0.0); + default_material.shininess = 1.0; + default_material.reflectivity = 0.0; + default_material.megatex_rect = vec4(0.0, 0.0, 0.0, 0.0); +#endif + + Ray ray = get_primary_ray(); + + /* read the various descriptors specifying dimensions and counts for + * all the relevant data textures + */ + vec4 desc = texture2D(tex_spheres, vec2(0.0, 0.0)); + num_spheres = int(desc.x); + sph_tex_sz = desc.y; + + desc = texture2D(tex_planes, vec2(0.0, 0.0)); + num_planes = int(desc.x); + plane_tex_sz = desc.y; + + desc = texture2D(tex_boxes, vec2(0.0, 0.0)); + num_boxes = int(desc.x); + box_tex_sz = desc.y; + + xform_tex_sz = texture2D(tex_xforms, vec2(0.0, 0.0)).x; + + + HitPoint hit; +#ifdef INIT_EVERYTHING + hit.dist = 0.0; + hit.pos = hit.normal = vec3(0.0, 0.0, 0.0); +#endif + + vec3 color = vec3(0.0, 0.0, 0.0); + float energy = 1.0; + + int iter = 0; + while(energy > 0.01 && iter++ < 4) { + vec3 envcol = textureCube(tex_env, ray.dir).xyz; + + if(find_intersection(ray, hit)) { + float fog_t = clamp((hit.dist - fog.x) / (fog.y - fog.x), 0.0, 1.0); + color += mix(shade(ray, hit), envcol, fog_t) * energy; + energy *= hit.mat.reflectivity * (1.0 - fog_t); + ray.origin = hit.pos; + ray.dir = reflect(ray.dir, hit.normal); + } else { + color += envcol * energy; + energy = 0.0; + iter = 100; + } + } + + gl_FragColor.xyz = color; + gl_FragColor.w = 1.0; +} + +vec3 shade(in Ray ray, in HitPoint hit) +{ + vec3 normal = faceforward(hit.normal, ray.dir, hit.normal); + + vec3 vdir = normalize(ray.dir); + vec3 vref = reflect(vdir, normal); + + /* if there's no texture rect.zw will be (0, 0, 0, 0) so this will map onto + * the top-left 1x1 null texture which is all white (having no effect) + */ + vec2 tc = mod(hit.texcoord, vec2(1.0, 1.0)) * hit.mat.megatex_rect.zw + hit.mat.megatex_rect.xy; + + vec3 diffuse_color = hit.mat.diffuse * texture2D(tex_megatex, tc).xyz; + + vec3 color = vec3(0.0, 0.0, 0.0); + for(int i=0; i 1.0) { + vec3 ldir = normalize(shadow_ray.dir); + + float diffuse = max(dot(ldir, normal), 0.0); + float specular = pow(max(dot(ldir, vref), 0.0), hit.mat.shininess); + + color += (diffuse_color * diffuse + hit.mat.specular * specular) * lights[i].color; + } + } + + return color; +} + +bool find_intersection(in Ray ray, out HitPoint hit) +{ + hit.dist = 100000.0; +#ifdef INIT_EVERYTHING + hit.pos = hit.normal = vec3(0.0, 0.0, 0.0); + hit.mat = default_material; + hit.texcoord = vec2(0.0, 0.0); +#endif + bool found = false; + + for(int i=0; i 0.0 ? 1.0 : -1.0, 0.0, 0.0); + + if(tmin > tymax || tymin > tmax) { + return false; + } + if(tymin > tmin) { + pt.normal = vec3(0.0, ray.origin.y > 0.0 ? 1.0 : -1.0, 0.0); + tmin = tymin; + } + if(tymax < tmax) { + tmax = tymax; + } + + float tzmin = (param[sgn[2]].z - ray.origin.z) * inv_dir.z; + float tzmax = (param[1 - sgn[2]].z - ray.origin.z) * inv_dir.z; + + if(tmin > tzmax || tzmin > tmax) { + return false; + } + if(tzmin > tmin) { + pt.normal = vec3(0.0, 0.0, ray.origin.z > 0.0 ? 1.0 : -1.0); + tmin = tzmin; + } + if(tzmax < tmax) { + tmax = tzmax; + } + + float t = tmin < EPSILON ? tmax : tmin; + if(t >= 1e-4) { + pt.dist = t; + pt.pos = ray.origin + ray.dir * t; + pt.mat = box.mat; + + float min_dist = 10000.0; + + vec3 offs = box.min + (box.max - box.min) / 2.0; + vec3 local_pt = pt.pos - offs; + + vec3 dist = abs((box.max - offs) - abs(local_pt)); + if(dist.x < min_dist) { + min_dist = dist.x; + pt.normal = sign(local_pt.x) * vec3(1.0, 0.0, 0.0); + pt.texcoord = pt.pos.zy; + } + if(dist.y < min_dist) { + min_dist = dist.y; + pt.normal = sign(local_pt.y) * vec3(0.0, 1.0, 0.0); + pt.texcoord = pt.pos.xz; + } + if(dist.z < min_dist) { + pt.normal = sign(local_pt.y) * vec3(0.0, 0.0, 1.0); + pt.texcoord = pt.pos.xy; + } + + +#ifdef USE_XFORM + pt.pos = (xform * vec4(pt.pos, 1.0)).xyz; + pt.normal = normalize(transform(pt.normal, xform)); +#endif + return true; + } + return false; +} + +vec3 transform(in vec3 v, in mat4 xform) +{ + return mat3(xform) * v; +} + +Ray transform(in Ray ray, in mat4 xform, in mat4 inv_xform) +{ + Ray res; + res.origin = (xform * vec4(ray.origin, 1.0)).xyz; + res.dir = transform(ray.dir, xform); + return res; +} + +Ray get_primary_ray() +{ + Ray ray; + ray.origin = (gl_ModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + vec3 dir = texture2D(tex_raydir, gl_TexCoord[0].st).xyz; + ray.dir = normalize(gl_NormalMatrix * dir); + return ray; +} + +#define ITEM(x) ((float(x) + 0.5) / OBJ_LINE_WIDTH) + +Sphere read_sphere(in float idx) +{ + Sphere sph; + // +1 because the first scanline is the descriptor + float ty = (idx + 1.0) / sph_tex_sz; + + sph.index = texture2D(tex_spheres, vec2(ITEM(0), ty)).x; + + vec4 texel = texture2D(tex_spheres, vec2(ITEM(1), ty)); + sph.pos = texel.xyz; + sph.radius = texel.w; + + sph.mat = read_material(tex_spheres, ty); + return sph; +} + +Plane read_plane(in float idx) +{ + Plane plane; + // +1 (see above) + float ty = (idx + 1.0) / plane_tex_sz; + + plane.index = texture2D(tex_planes, vec2(ITEM(0), ty)).x; + + vec4 texel = texture2D(tex_planes, vec2(ITEM(1), ty)); + plane.normal = texel.xyz; + plane.dist = texel.w; + + plane.mat = read_material(tex_planes, ty); + return plane; +} + +Box read_box(in float idx) +{ + Box box; + float ty = (idx + 1.0) / box_tex_sz; + + box.index = texture2D(tex_boxes, vec2(ITEM(0), ty)).x; + + box.min = texture2D(tex_boxes, vec2(ITEM(1), ty)).xyz; + box.max = texture2D(tex_boxes, vec2(ITEM(2), ty)).xyz; + + box.mat = read_material(tex_boxes, ty); + return box; +} + +void read_xform(in float idx, out mat4 xform, out mat4 inv_xform) +{ + float ty = (idx + 1.0) / xform_tex_sz; + + for(int i=0; i<4; i++) { + xform[i] = texture2D(tex_xforms, vec2(ITEM(i), ty)); + } + inv_xform = inverse(xform); + /*for(int i=0; i<4; i++) { + inv_xform[i] = texture2D(tex_xforms, vec2(ITEM(float(i) + 4.0), ty)); + }*/ +} + +#define MAT_START 4 +Material read_material(in sampler2D tex, in float ty) +{ + Material mat; + + vec4 texel = texture2D(tex, vec2(ITEM(MAT_START), ty)); + mat.diffuse = texel.xyz; + + texel = texture2D(tex, vec2(ITEM(MAT_START + 1), ty)); + mat.specular = texel.xyz; + mat.shininess = texel.w; + + texel = texture2D(tex, vec2(ITEM(MAT_START + 2), ty)); + mat.reflectivity = texel.x; + + mat.megatex_rect = texture2D(tex, vec2(ITEM(MAT_START + 3), ty)); + + return mat; +} diff -r 000000000000 -r f234630e38ff sdr/vertex.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/vertex.glsl Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,6 @@ +void main() +{ + gl_Position = gl_Vertex; + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord0; +} diff -r 000000000000 -r f234630e38ff src/box.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/box.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,63 @@ +#include "box.h" + +Box::Box() + : min(-0.5, -0.5, -0.5), max(0.5, 0.5, 0.5) +{ +} + +Box::Box(const Vector3 &min_arg, const Vector3 &max_arg) + : min(min_arg), max(max_arg) +{ +} + +bool Box::intersect(const Ray &inray, HitPoint *pt) const +{ + Ray ray = inray.transformed(inv_xform); + + Vector3 param[2] = {min, max}; + Vector3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z); + int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0}; + + float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x; + float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x; + float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y; + float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y; + + pt->normal = Vector3(ray.origin.x > 0.0 ? 1 : -1, 0, 0); + + if(tmin > tymax || tymin > tmax) { + return false; + } + if(tymin > tmin) { + pt->normal = Vector3(0, ray.origin.y > 0.0 ? 1 : -1, 0); + tmin = tymin; + } + if(tymax < tmax) { + tmax = tymax; + } + + float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z; + float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z; + + if(tmin > tzmax || tzmin > tmax) { + return false; + } + if(tzmin > tmin) { + pt->normal = Vector3(0, 0, ray.origin.z > 0.0 ? 1 : -1); + tmin = tzmin; + } + if(tzmax < tmax) { + tmax = tzmax; + } + + float t = tmin < 1e-4 ? tmax : tmin; + if(t >= 1e-4) { + pt->obj = this; + pt->dist = t; + pt->pos = ray.origin + ray.dir * t; + pt->pos.transform(xform); + pt->normal.transform(dir_xform); + return true; + } + return false; +} diff -r 000000000000 -r f234630e38ff src/box.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/box.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,17 @@ +#ifndef BOX_H_ +#define BOX_H_ + +#include "vmath/vmath.h" +#include "object.h" + +class Box : public Object { +public: + Vector3 min, max; + + Box(); + Box(const Vector3 &min, const Vector3 &max); + + bool intersect(const Ray &ray, HitPoint *pt) const; +}; + +#endif // BOX_H_ diff -r 000000000000 -r f234630e38ff src/camera.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/camera.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,199 @@ +#include +#include +#include "camera.h" + +static void calc_sample_pos_rec(int sidx, float xsz, float ysz, float *pos); + +Camera::Camera() +{ + vfov = M_PI / 4.0; + cached_matrix_valid = false; + + rdir_cache_width = rdir_cache_height = 0; + rdir_cache = 0; +} + +Camera::Camera(const Vector3 &p) + : pos(p) +{ + vfov = M_PI / 4.0; + cached_matrix_valid = false; + + rdir_cache_width = rdir_cache_height = 0; + rdir_cache = 0; +} + +Camera::~Camera() +{ + delete [] rdir_cache; +} + +void Camera::set_fov(float vfov) +{ + this->vfov = vfov; + + // invalidate the dir cache + delete [] rdir_cache; +} + +float Camera::get_fov() const +{ + return vfov; +} + +void Camera::set_position(const Vector3 &pos) +{ + this->pos = pos; + cached_matrix_valid = false; // invalidate the cached matrix +} + +const Vector3 &Camera::get_position() const +{ + return pos; +} + +const Matrix4x4 &Camera::get_matrix() const +{ + if(!cached_matrix_valid) { + calc_matrix(&cached_matrix); + cached_matrix_valid = true; + } + return cached_matrix; +} + +Vector2 Camera::calc_sample_pos(int x, int y, int xsz, int ysz, int sample) const +{ + float ppos[2]; + float aspect = (float)xsz / (float)ysz; + + float pwidth = 2.0 * aspect / (float)xsz; + float pheight = 2.0 / (float)ysz; + + ppos[0] = (float)x * pwidth - aspect; + ppos[1] = 1.0 - (float)y * pheight; + + calc_sample_pos_rec(sample, pwidth, pheight, ppos); + return Vector2(ppos[0], ppos[1]); +} + +Ray Camera::get_primary_ray(int x, int y, int xsz, int ysz, int sample) const +{ + if(!rdir_cache || rdir_cache_width != xsz || rdir_cache_height != ysz) { + printf("calculating primary ray direction cache\n"); + + delete [] rdir_cache; + rdir_cache = new Vector3[xsz * ysz]; + + for(int i=0; ix = ppos.x; + rdir->y = ppos.y; + rdir->z = 1.0 / tan(vfov / 2.0); + rdir->normalize(); + + rdir++; + } + } + rdir_cache_width = xsz; + rdir_cache_height = ysz; + } + + Ray ray; + ray.origin = pos; + ray.dir = rdir_cache[y * xsz + x]; + + // transform the ray direction with the camera matrix + Matrix4x4 mat = get_matrix(); + mat.m[0][3] = mat.m[1][3] = mat.m[2][3] = mat.m[3][0] = mat.m[3][1] = mat.m[3][2] = 0.0; + mat.m[3][3] = 1.0; + + ray.dir = ray.dir.transformed(mat); + return ray; +} + +TargetCamera::TargetCamera() {} + +TargetCamera::TargetCamera(const Vector3 &pos, const Vector3 &targ) + : Camera(pos), target(targ) +{ +} + +void TargetCamera::set_target(const Vector3 &targ) +{ + target = targ; + cached_matrix_valid = false; // invalidate the cached matrix +} + +const Vector3 &TargetCamera::get_target() const +{ + return target; +} + +void TargetCamera::calc_matrix(Matrix4x4 *mat) const +{ + Vector3 up(0, 1, 0); + Vector3 dir = (target - pos).normalized(); + Vector3 right = cross_product(up, dir); + up = cross_product(dir, right); + + *mat = Matrix4x4( + right.x, up.x, dir.x, pos.x, + right.y, up.y, dir.y, pos.y, + right.z, up.z, dir.z, pos.z, + 0.0, 0.0, 0.0, 1.0); +} + +void FlyCamera::input_move(float x, float y, float z) +{ + static const Vector3 vfwd(0, 0, 1), vright(1, 0, 0); + + Vector3 k = vfwd.transformed(rot); + Vector3 i = vright.transformed(rot); + Vector3 j = cross_product(k, i); + + pos += i * x + j * y + k * z; + cached_matrix_valid = false; +} + +void FlyCamera::input_rotate(float x, float y, float z) +{ + Vector3 axis(x, y, z); + float axis_len = axis.length(); + if(fabs(axis_len) < 1e-5) { + return; + } + rot.rotate(axis / axis_len, -axis_len); + rot.normalize(); + + cached_matrix_valid = false; +} + +void FlyCamera::calc_matrix(Matrix4x4 *mat) const +{ + Matrix3x3 rmat = rot.get_rotation_matrix(); + *mat = rmat; +} + +/* generates a sample position for sample number sidx, in the unit square + * by recursive subdivision and jittering + */ +static void calc_sample_pos_rec(int sidx, float xsz, float ysz, float *pos) +{ + static const float subpt[4][2] = { + {-0.25, -0.25}, {0.25, -0.25}, {-0.25, 0.25}, {0.25, 0.25} + }; + + if(!sidx) { + return; + } + + /* determine which quadrant to recurse into */ + int quadrant = ((sidx - 1) % 4); + pos[0] += subpt[quadrant][0] * xsz; + pos[1] += subpt[quadrant][1] * ysz; + + calc_sample_pos_rec((sidx - 1) / 4, xsz / 2, ysz / 2, pos); +} diff -r 000000000000 -r f234630e38ff src/camera.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/camera.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,61 @@ +#ifndef CAMERA_H_ +#define CAMERA_H_ + +#include "vmath/vmath.h" + +class Camera { +protected: + Vector3 pos; + float vfov; // vertical field of view in radians + + mutable Matrix4x4 cached_matrix; + mutable bool cached_matrix_valid; + + mutable Vector3 *rdir_cache; + mutable int rdir_cache_width, rdir_cache_height; + + virtual void calc_matrix(Matrix4x4 *mat) const = 0; + + Vector2 calc_sample_pos(int x, int y, int xsz, int ysz, int sample) const; + +public: + Camera(); + Camera(const Vector3 &pos); + virtual ~Camera(); + + virtual void set_fov(float vfov); + virtual float get_fov() const; + + virtual void set_position(const Vector3 &pos); + virtual const Vector3 &get_position() const; + virtual const Matrix4x4 &get_matrix() const; + + virtual Ray get_primary_ray(int x, int y, int xsz, int ysz, int sample = 0) const; +}; + +class TargetCamera : public Camera { +protected: + Vector3 target; + + void calc_matrix(Matrix4x4 *mat) const; + +public: + TargetCamera(); + TargetCamera(const Vector3 &pos, const Vector3 &targ); + + virtual void set_target(const Vector3 &targ); + virtual const Vector3 &get_target() const; +}; + +class FlyCamera : public Camera { +protected: + Quaternion rot; + + void calc_matrix(Matrix4x4 *mat) const; + +public: + void input_move(float x, float y, float z); + void input_rotate(float x, float y, float z); +}; + +#endif // CAMERA_H_ diff -r 000000000000 -r f234630e38ff src/glsdr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glsdr.c Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,411 @@ +#include +#include +#include +#include +#include +#include +#include + +#if defined(unix) || defined(__unix__) +#include +#include +#endif /* unix */ + +#include "glsdr.h" + +static const char *sdrtypestr(unsigned int sdrtype); + +unsigned int create_vertex_shader(const char *src) +{ + return create_shader(src, GL_VERTEX_SHADER); +} + +unsigned int create_pixel_shader(const char *src) +{ + return create_shader(src, GL_FRAGMENT_SHADER); +} + +unsigned int create_tessctl_shader(const char *src) +{ + return create_shader(src, GL_TESS_CONTROL_SHADER); +} + +unsigned int create_tesseval_shader(const char *src) +{ + return create_shader(src, GL_TESS_EVALUATION_SHADER); +} + +unsigned int create_geometry_shader(const char *src) +{ + return create_shader(src, GL_GEOMETRY_SHADER); +} + +unsigned int create_shader(const char *src, unsigned int sdr_type) +{ + unsigned int sdr; + int success, info_len; + char *info_str = 0; + GLenum err; + + sdr = glCreateShader(sdr_type); + assert(glGetError() == GL_NO_ERROR); + glShaderSource(sdr, 1, &src, 0); + err = glGetError(); + assert(err == GL_NO_ERROR); + glCompileShader(sdr); + assert(glGetError() == GL_NO_ERROR); + + glGetShaderiv(sdr, GL_COMPILE_STATUS, &success); + assert(glGetError() == GL_NO_ERROR); + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); + assert(glGetError() == GL_NO_ERROR); + + if(info_len) { + if((info_str = malloc(info_len + 1))) { + glGetShaderInfoLog(sdr, info_len, 0, info_str); + assert(glGetError() == GL_NO_ERROR); + } + } + + if(success) { + fprintf(stderr, info_str ? "done: %s\n" : "done\n", info_str); + } else { + fprintf(stderr, info_str ? "failed: %s\n" : "failed\n", info_str); + glDeleteShader(sdr); + sdr = 0; + } + + free(info_str); + return sdr; +} + +void free_shader(unsigned int sdr) +{ + glDeleteShader(sdr); +} + +unsigned int load_vertex_shader(const char *fname) +{ + return load_shader(fname, GL_VERTEX_SHADER); +} + +unsigned int load_pixel_shader(const char *fname) +{ + return load_shader(fname, GL_FRAGMENT_SHADER); +} + +unsigned int load_tessctl_shader(const char *fname) +{ + return load_shader(fname, GL_TESS_CONTROL_SHADER); +} + +unsigned int load_tesseval_shader(const char *fname) +{ + return load_shader(fname, GL_TESS_EVALUATION_SHADER); +} + +unsigned int load_geometry_shader(const char *fname) +{ + return load_shader(fname, GL_GEOMETRY_SHADER); +} + +unsigned int load_shader(const char *fname, unsigned int sdr_type) +{ +#if defined(unix) || defined(__unix__) + struct stat st; +#endif + unsigned int sdr; + size_t filesize; + FILE *fp; + char *src; + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to open shader %s: %s\n", fname, strerror(errno)); + return 0; + } + +#if defined(unix) || defined(__unix__) + fstat(fileno(fp), &st); + filesize = st.st_size; +#else + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); +#endif /* unix */ + + if(!(src = malloc(filesize + 1))) { + fclose(fp); + return 0; + } + fread(src, 1, filesize, fp); + src[filesize] = 0; + fclose(fp); + + fprintf(stderr, "compiling %s shader: %s... ", sdrtypestr(sdr_type), fname); + sdr = create_shader(src, sdr_type); + + free(src); + return sdr; +} + + +unsigned int get_vertex_shader(const char *fname) +{ + return get_shader(fname, GL_VERTEX_SHADER); +} + +unsigned int get_pixel_shader(const char *fname) +{ + return get_shader(fname, GL_FRAGMENT_SHADER); +} + +unsigned int get_tessctl_shader(const char *fname) +{ + return get_shader(fname, GL_TESS_CONTROL_SHADER); +} + +unsigned int get_tesseval_shader(const char *fname) +{ + return get_shader(fname, GL_TESS_EVALUATION_SHADER); +} + +unsigned int get_geometry_shader(const char *fname) +{ + return get_shader(fname, GL_GEOMETRY_SHADER); +} + +unsigned int get_shader(const char *fname, unsigned int sdr_type) +{ + unsigned int sdr; + if(!(sdr = load_shader(fname, sdr_type))) { + return 0; + } + return sdr; +} + + +/* ---- gpu programs ---- */ + +unsigned int create_program(void) +{ + unsigned int prog = glCreateProgram(); + assert(glGetError() == GL_NO_ERROR); + return prog; +} + +unsigned int create_program_link(unsigned int sdr0, ...) +{ + unsigned int prog, sdr; + va_list ap; + + if(!(prog = create_program())) { + return 0; + } + + attach_shader(prog, sdr0); + if(glGetError()) { + return 0; + } + + va_start(ap, sdr0); + while((sdr = va_arg(ap, unsigned int))) { + attach_shader(prog, sdr); + if(glGetError()) { + return 0; + } + } + va_end(ap); + + if(link_program(prog) == -1) { + free_program(prog); + return 0; + } + return prog; +} + +unsigned int create_program_load(const char *vfile, const char *pfile) +{ + unsigned int sdr[2]; + int i = 0; + + if(vfile && *vfile && !(sdr[i++] = get_vertex_shader(vfile))) { + return 0; + } + if(pfile && *pfile && !(sdr[i++] = get_pixel_shader(pfile))) { + return 0; + } + return create_program_link(sdr[0], sdr[1], 0); +} + +void free_program(unsigned int sdr) +{ + glDeleteProgram(sdr); +} + +void attach_shader(unsigned int prog, unsigned int sdr) +{ + glAttachShader(prog, sdr); + assert(glGetError() == GL_NO_ERROR); +} + +int link_program(unsigned int prog) +{ + int linked, info_len, retval = 0; + char *info_str = 0; + + glLinkProgram(prog); + assert(glGetError() == GL_NO_ERROR); + glGetProgramiv(prog, GL_LINK_STATUS, &linked); + assert(glGetError() == GL_NO_ERROR); + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); + assert(glGetError() == GL_NO_ERROR); + + if(info_len) { + if((info_str = malloc(info_len + 1))) { + glGetProgramInfoLog(prog, info_len, 0, info_str); + assert(glGetError() == GL_NO_ERROR); + } + } + + if(linked) { + fprintf(stderr, info_str ? "linking done: %s\n" : "linking done\n", info_str); + } else { + fprintf(stderr, info_str ? "linking failed: %s\n" : "linking failed\n", info_str); + retval = -1; + } + + free(info_str); + return retval; +} + +int bind_program(unsigned int prog) +{ + GLenum err; + + glUseProgram(prog); + if(prog && (err = glGetError()) != GL_NO_ERROR) { + /* maybe the program is not linked, try linking first */ + if(err == GL_INVALID_OPERATION) { + if(link_program(prog) == -1) { + return -1; + } + glUseProgram(prog); + return glGetError() == GL_NO_ERROR ? 0 : -1; + } + return -1; + } + return 0; +} + +/* ugly but I'm not going to write the same bloody code over and over */ +#define BEGIN_UNIFORM_CODE \ + int loc, curr_prog; \ + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); \ + if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { \ + return -1; \ + } \ + if((loc = glGetUniformLocation(prog, name)) != -1) + +#define END_UNIFORM_CODE \ + if((unsigned int)curr_prog != prog) { \ + bind_program(curr_prog); \ + } \ + return loc == -1 ? -1 : 0 + +int set_uniform_int(unsigned int prog, const char *name, int val) +{ + BEGIN_UNIFORM_CODE { + glUniform1i(loc, val); + } + END_UNIFORM_CODE; +} + +int set_uniform_float(unsigned int prog, const char *name, float val) +{ + BEGIN_UNIFORM_CODE { + glUniform1f(loc, val); + } + END_UNIFORM_CODE; +} + +int set_uniform_float2(unsigned int prog, const char *name, float x, float y) +{ + BEGIN_UNIFORM_CODE { + glUniform2f(loc, x, y); + } + END_UNIFORM_CODE; +} + +int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z) +{ + BEGIN_UNIFORM_CODE { + glUniform3f(loc, x, y, z); + } + END_UNIFORM_CODE; +} + +int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w) +{ + BEGIN_UNIFORM_CODE { + glUniform4f(loc, x, y, z, w); + } + END_UNIFORM_CODE; +} + +int set_uniform_matrix4(unsigned int prog, const char *name, float *mat) +{ + BEGIN_UNIFORM_CODE { + glUniformMatrix4fv(loc, 1, GL_FALSE, mat); + } + END_UNIFORM_CODE; +} + +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat) +{ + BEGIN_UNIFORM_CODE { + glUniformMatrix4fv(loc, 1, GL_TRUE, mat); + } + END_UNIFORM_CODE; +} + +int get_attrib_loc(unsigned int prog, const char *name) +{ + int loc, curr_prog; + + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); + if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { + return -1; + } + + loc = glGetAttribLocation(prog, (char*)name); + + if((unsigned int)curr_prog != prog) { + bind_program(curr_prog); + } + return loc; +} + +void set_attrib_float3(int attr_loc, float x, float y, float z) +{ + glVertexAttrib3f(attr_loc, x, y, z); +} + +static const char *sdrtypestr(unsigned int sdrtype) +{ + switch(sdrtype) { + case GL_VERTEX_SHADER: + return "vertex"; + case GL_FRAGMENT_SHADER: + return "pixel"; + case GL_TESS_CONTROL_SHADER: + return "tessellation control"; + case GL_TESS_EVALUATION_SHADER: + return "tessellation evaluation"; + case GL_GEOMETRY_SHADER: + return "geometry"; + + default: + break; + } + return ""; +} diff -r 000000000000 -r f234630e38ff src/glsdr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glsdr.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,59 @@ +#ifndef SDR_H_ +#define SDR_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* ---- shaders ---- */ +unsigned int create_vertex_shader(const char *src); +unsigned int create_pixel_shader(const char *src); +unsigned int create_tessctl_shader(const char *src); +unsigned int create_tesseval_shader(const char *src); +unsigned int create_geometry_shader(const char *src); +unsigned int create_shader(const char *src, unsigned int sdr_type); +void free_shader(unsigned int sdr); + +unsigned int load_vertex_shader(const char *fname); +unsigned int load_pixel_shader(const char *fname); +unsigned int load_tessctl_shader(const char *fname); +unsigned int load_tesseval_shader(const char *fname); +unsigned int load_geometry_shader(const char *fname); +unsigned int load_shader(const char *src, unsigned int sdr_type); + +unsigned int get_vertex_shader(const char *fname); +unsigned int get_pixel_shader(const char *fname); +unsigned int get_tessctl_shader(const char *fname); +unsigned int get_tesseval_shader(const char *fname); +unsigned int get_geometry_shader(const char *fname); +unsigned int get_shader(const char *fname, unsigned int sdr_type); + +int add_shader(const char *fname, unsigned int sdr); +int remove_shader(const char *fname); + +/* ---- gpu programs ---- */ +unsigned int create_program(void); +unsigned int create_program_link(unsigned int sdr0, ...); +unsigned int create_program_load(const char *vfile, const char *pfile); +void free_program(unsigned int sdr); + +void attach_shader(unsigned int prog, unsigned int sdr); +int link_program(unsigned int prog); +int bind_program(unsigned int prog); + +int set_uniform_int(unsigned int prog, const char *name, int val); +int set_uniform_float(unsigned int prog, const char *name, float val); +int set_uniform_float2(unsigned int prog, const char *name, float x, float y); +int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z); +int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w); +int set_uniform_matrix4(unsigned int prog, const char *name, float *mat); +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat); + +int get_attrib_loc(unsigned int prog, const char *name); +void set_attrib_float3(int attr_loc, float x, float y, float z); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SDR_H_ */ diff -r 000000000000 -r f234630e38ff src/gpuscene.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gpuscene.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,317 @@ +#include +#include +#include "gpuscene.h" +#include "sphere.h" +#include "plane.h" +#include "box.h" +#include "opengl.h" + +GPUScene::GPUScene() +{ + glGenTextures(NUM_TEXTURES, textures); + + for(int i=0; i spheres; + std::vector planes; + std::vector boxes; + + // collect all objects into the different type-specific arrays + for(auto obj : objects) { + if((sph = dynamic_cast(obj))) { + spheres.push_back(sph); + + } else if((plane = dynamic_cast(obj))) { + planes.push_back(plane); + + } else if((box = dynamic_cast(obj))) { + boxes.push_back(box); + + } else { + fprintf(stderr, "skipping object of unknown type: %s\n", obj->get_name()); + } + } + + create_sphere_texture(spheres); + create_plane_texture(planes); + create_box_texture(boxes); + + create_env_texture(); + create_xform_texture(); + + return true; +} + +unsigned int GPUScene::get_texture(int which) const +{ + return textures[which]; +} + +#define MAT_START 4 +static void copy_material(Vector4 *ptr, const Material *mtl) +{ + ptr[MAT_START].x = mtl->diffuse.x; + ptr[MAT_START].y = mtl->diffuse.y; + ptr[MAT_START].z = mtl->diffuse.z; + ptr[MAT_START].w = mtl->transparency; + + ptr[MAT_START + 1].x = mtl->specular.x; + ptr[MAT_START + 1].y = mtl->specular.y; + ptr[MAT_START + 1].z = mtl->specular.z; + ptr[MAT_START + 1].w = mtl->shininess; + + ptr[MAT_START + 2].x = mtl->reflectivity; + ptr[MAT_START + 2].y = mtl->ior; + + ptr[MAT_START + 3] = mtl->mega_rect; +} + +int GPUScene::object_index(const Object *obj) const +{ + for(int i=0; i<(int)objects.size(); i++) { + if(objects[i] == obj) { + return i; + } + } + abort(); // can't happen + return -1; +} + +#define OBJ_LINE_WIDTH 16 +void GPUScene::create_sphere_texture(const std::vector &spheres) +{ + int xsz = OBJ_LINE_WIDTH; + int ysz = (int)spheres.size() + 1; + int tex_ysz = next_pow2(ysz); + + Vector4 *pixels = new Vector4[xsz * tex_ysz]; + + pixels[0].x = (float)ysz; + pixels[0].y = (float)tex_ysz; + pixels[0].z = 0.5; + pixels[0].w = 1.0; + + Vector4 *pixptr = pixels + xsz; + + for(size_t i=0; ipos.x; + pixptr[1].y = spheres[i]->pos.y; + pixptr[1].z = spheres[i]->pos.z; + pixptr[1].w = spheres[i]->radius; + + copy_material(pixptr, &spheres[i]->material); + pixptr += OBJ_LINE_WIDTH; + } + + glBindTexture(GL_TEXTURE_2D, textures[TEX_SPHERE]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, pixels); + + delete [] pixels; +} + +void GPUScene::create_plane_texture(const std::vector &planes) +{ + int xsz = OBJ_LINE_WIDTH; + int ysz = (int)planes.size() + 1; + int tex_ysz = next_pow2(ysz); + + Vector4 *pixels = new Vector4[xsz * tex_ysz]; + + pixels[0].x = (float)ysz; + pixels[0].y = (float)tex_ysz; + + Vector4 *pixptr = pixels + xsz; + + for(size_t i=0; inormal.x; + pixptr[1].y = planes[i]->normal.y; + pixptr[1].z = planes[i]->normal.z; + pixptr[1].w = planes[i]->dist; + + copy_material(pixptr, &planes[i]->material); + pixptr += OBJ_LINE_WIDTH; + } + + glBindTexture(GL_TEXTURE_2D, textures[TEX_PLANE]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, pixels); + + delete [] pixels; +} + +void GPUScene::create_box_texture(const std::vector &boxes) +{ + int xsz = OBJ_LINE_WIDTH; + int ysz = (int)boxes.size() + 1; + int tex_ysz = next_pow2(ysz); + + Vector4 *pixels = new Vector4[xsz * tex_ysz]; + + pixels[0].x = (float)ysz; + pixels[0].y = (float)tex_ysz; + + Vector4 *pixptr = pixels + xsz; + + for(size_t i=0; imin; + pixptr[2] = boxes[i]->max; + + copy_material(pixptr, &boxes[i]->material); + pixptr += OBJ_LINE_WIDTH; + } + + glBindTexture(GL_TEXTURE_2D, textures[TEX_BOX]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, pixels); + + delete [] pixels; +} + +void GPUScene::create_megatexture() +{ + // at least a 1x1 dummy white texture + int xsz = 1; + int ysz = 1; + int num_textures = 0; + + for(auto obj : objects) { + // only need 2D textures at this point + Texture *tex = dynamic_cast(obj->material.tex); + if(tex) { + const Image *img = tex->get_image(); + + xsz = std::max(xsz, img->xsz); + ysz += img->ysz; + num_textures++; + } + } + + int tex_xsz = next_pow2(xsz); + int tex_ysz = next_pow2(ysz); + + Color *pixels = new Color[tex_xsz * tex_ysz]; + + // null texture + pixels[0] = Color(1, 1, 1); + + Color *pixptr = pixels + tex_xsz; + + float offs_y = 0.0; + for(auto obj : objects) { + Texture *tex = dynamic_cast(obj->material.tex); + if(tex) { + const Image *img = tex->get_image(); + + Vector4 rect{0.0, offs_y, (float)img->xsz / (float)tex_xsz, + (float)img->ysz / (float)tex_ysz}; + + offs_y += rect.w; + + obj->material.mega_rect = rect; + + for(int i=0; iysz; i++) { + memcpy(pixptr, img->pixels + i * img->xsz, img->xsz * sizeof *pixels); + pixptr += tex_xsz; + } + } else { + obj->material.mega_rect = Vector4(0, 0, 0, 0); + } + } + + glBindTexture(GL_TEXTURE_2D, textures[TEX_TEXTURE]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, tex_xsz, tex_ysz, 0, GL_RGB, GL_FLOAT, pixels); + + delete [] pixels; +} + +void GPUScene::create_env_texture() +{ + // create the scene cubemap, or a null cubemap if we don't have one + glDeleteTextures(1, textures + TEX_ENV); // cause it's not a 2D texture :) + + glGenTextures(1, textures + TEX_ENV); + glBindTexture(GL_TEXTURE_CUBE_MAP, textures[TEX_ENV]); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + if(envmap) { + // we have an environment cubemap, just pass the data to OpenGL + for(int i=0; i<6; i++) { + const Image *img = envmap->get_image(i); + + int face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; + glTexImage2D(face, 0, GL_RGB16F, img->xsz, img->ysz, 0, GL_RGB, GL_FLOAT, img->pixels); + } + } else { + // we don't have an env cubemap, make a dummy 1x1 cubemap with the background color + for(int i=0; i<6; i++) { + int face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; + glTexImage2D(face, 0, GL_RGB16F, 1, 1, 0, GL_RGB, GL_FLOAT, &bgcolor); + } + } +} + +#define XFORM_LINE_WIDTH OBJ_LINE_WIDTH +void GPUScene::create_xform_texture() +{ + int tex_xsz = XFORM_LINE_WIDTH; + int tex_ysz = next_pow2((int)objects.size() + 1); // leave space for the descriptor + + glBindTexture(GL_TEXTURE_2D, textures[TEX_XFORM]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, tex_xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, 0); + + update_xform_texture(); +} + +void GPUScene::update_xform_texture() +{ + int tex_xsz = XFORM_LINE_WIDTH; + int tex_ysz = next_pow2((int)objects.size() + 1); // leave space for the descriptor + + if(!xform_buf) { + xform_buf = new Vector4[tex_xsz * tex_ysz]; + xform_buf[0].x = tex_ysz; // descriptor + } + + Vector4 *pixptr = xform_buf + tex_xsz; + for(auto obj : objects) { + for(int i=0; i<4; i++) { + pixptr[i] = obj->xform.get_column_vector(i); + pixptr[i + 4] = obj->inv_xform.get_column_vector(i); + } + pixptr += tex_xsz; + } + + glBindTexture(GL_TEXTURE_2D, textures[TEX_XFORM]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_xsz, tex_ysz, GL_RGBA, GL_FLOAT, xform_buf); +} diff -r 000000000000 -r f234630e38ff src/gpuscene.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gpuscene.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,38 @@ +#ifndef GPUSCENE_H_ +#define GPUSCENE_H_ + +#include +#include "scene.h" +#include "sphere.h" +#include "plane.h" +#include "box.h" + +class GPUScene : public Scene { +public: + enum { TEX_SPHERE, TEX_PLANE, TEX_BOX, TEX_TEXTURE, TEX_ENV, TEX_XFORM, NUM_TEXTURES }; + +private: + unsigned int textures[NUM_TEXTURES]; + Vector4 *xform_buf; + + int object_index(const Object *obj) const; + + void create_sphere_texture(const std::vector &spheres); + void create_plane_texture(const std::vector &planes); + void create_box_texture(const std::vector &box); + void create_megatexture(); + void create_env_texture(); + void create_xform_texture(); + +public: + GPUScene(); + ~GPUScene(); + + bool create_textures(); + + void update_xform_texture(); + + unsigned int get_texture(int which) const; +}; + +#endif // GPUSCENE_H_ diff -r 000000000000 -r f234630e38ff src/image.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,69 @@ +#include +#include +#include +#include "image.h" +#include "material.h" +#include + +Image::Image() +{ + pixels = 0; + xsz = ysz = 0; +} + +Image::Image(int xsz, int ysz) +{ + pixels = 0; + set_pixels(xsz, ysz, 0); +} + +Image::~Image() +{ + delete [] pixels; +} + +void Image::set_pixels(int xsz, int ysz, const Color *pix) +{ + delete [] pixels; + + pixels = new Color[xsz * ysz]; + this->xsz = xsz; + this->ysz = ysz; + + if(pix) { + memcpy(pixels, pix, xsz * ysz * sizeof *pixels); + } +} + +bool Image::load(const char *fname) +{ + int xsz, ysz; + Color *img = (Color*)img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGBF); + if(!img) { + return false; + } + + /* convert the pixels to linear color space */ + for(int i=0; i + +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 . +*/ +#include "light.h" + +Light::Light() + : color(1, 1, 1) +{ +} + +Light::Light(const Vector3 &p, const Color &col) + : pos(p), color(col) +{ +} diff -r 000000000000 -r f234630e38ff src/light.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/light.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,33 @@ +/* +Simple introductory ray tracer +Copyright (C) 2012 John Tsiombikas + +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 . +*/ +#ifndef LIGHT_H_ +#define LIGHT_H_ + +#include "vmath/vmath.h" +#include "material.h" + +class Light { +public: + Vector3 pos; + Color color; + + Light(); + Light(const Vector3 &pos, const Color &col = Color(1, 1, 1)); +}; + +#endif // LIGHT_H_ diff -r 000000000000 -r f234630e38ff src/main.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,396 @@ +#include +#include +#include +#include +#include "opengl.h" + +#include "gpuscene.h" +#include "sphere.h" +#include "plane.h" +#include "image.h" +#include "rend.h" +#include "glsdr.h" + +static bool init(const char *scene_fname); +static void cleanup(); +static void disp(); +static void draw_text(float r, float g, float b, const char *fmt, ...); +//static void update_texture(const float *fb); +static void idle(); +static void reshape(int x, int y); +static void handle_keys(float dt); +static void keyb(unsigned char key, int x, int y); +static void keyb_up(unsigned char key, int x, int y); +static void skeyb(int key, int x, int y); +static void mouse(int bn, int st, int x, int y); +static void motion(int x, int y); +static void sball_motion(int x, int y, int z); +static void sball_rotate(int x, int y, int z); +static void sball_button(int bn, int state); + +static unsigned int tex; +static long last_fps_upd, first_time = -1; +static long frames, total_frames; +static float fps; +static int xsz = 800; +static int ysz = 450; +//static int tex_xsz, tex_ysz; + +static GPUScene *scn; +static FlyCamera *cam; + +static float img_scale = 1.0f; +static bool keystate[256]; + +static char *scene_fname; +static bool sdr_valid = true; + +int main(int argc, char **argv) +{ + glutInit(&argc, argv); + + int num_samples = 1; + + for(int i=1; i= 1\n"); + return 1; + } + } else { + fprintf(stderr, "invalid option: %s\n", argv[i]); + return 1; + } + } else { + if(scene_fname) { + fprintf(stderr, "unexpected argument: %s\n", argv[i]); + return 1; + } + scene_fname = argv[i]; + } + } + + glutInitWindowSize(xsz * img_scale, ysz * img_scale); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + glutCreateWindow("single threaded"); + + glutDisplayFunc(disp); + glutIdleFunc(idle); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyb); + glutKeyboardUpFunc(keyb_up); + glutSpecialFunc(skeyb); + glutMouseFunc(mouse); + glutMotionFunc(motion); + glutSpaceballMotionFunc(sball_motion); + glutSpaceballRotateFunc(sball_rotate); + glutSpaceballButtonFunc(sball_button); + + glewInit(); + + if(!init(scene_fname)) { + return 1; + } + atexit(cleanup); + + glutMainLoop(); + return 0; +} + +static bool init(const char *scene_fname) +{ + scn = new GPUScene; + if(!scn->load(scene_fname ? scene_fname : "scene")) { + return false; + } + + cam = new FlyCamera; + cam->input_move(0, 1.5, -10); + cam->input_rotate(25, 0, 0); + scn->set_camera(cam); + + if(!init_renderer(scn, xsz, ysz)) { + return false; + } + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + int srgb_capable; + if(GLEW_EXT_framebuffer_sRGB && (glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb_capable), srgb_capable)) { + printf("enabling sRGB framebuffer\n"); + glEnable(GL_FRAMEBUFFER_SRGB); + } else { + printf("using post shader for gamma correction\n"); + unsigned int post_sdr = create_program_load(0, "sdr/postsdr.glsl"); + if(post_sdr) { + glUseProgram(post_sdr); + } + } + + last_fps_upd = glutGet(GLUT_ELAPSED_TIME); + first_time = last_fps_upd; + total_frames = 0; + return true; +} + +static void cleanup() +{ + long interval = glutGet(GLUT_ELAPSED_TIME) - first_time; + printf("average fps: %.2f\n", (float)total_frames / ((float)interval / 1000.0f)); + + glDeleteTextures(1, &tex); + + destroy_renderer(); + delete scn; +} + +static void disp() +{ + static long prev_msec; + long interval, msec = glutGet(GLUT_ELAPSED_TIME); + + handle_keys((msec - prev_msec) / 1000.0f); + prev_msec = msec; + + //update_texture(); + if(sdr_valid) { + render_frame(msec); + } else { + glUseProgram(0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glLineWidth(8.0); + glBegin(GL_LINES); + glColor3f(1, 0, 0); + glVertex2f(-1, -1); + glVertex2f(1, 1); + glVertex2f(-1, 1); + glVertex2f(1, -1); + glEnd(); + glLineWidth(1.0); + } + + draw_text(0.8, 0.75, 0, "fps: %.2f", fps); + + glutSwapBuffers(); + frames++; + total_frames++; + + interval = (msec = glutGet(GLUT_ELAPSED_TIME)) - last_fps_upd; + if(interval >= 2000) { + float tm = (float)interval / 1000.0f; + fps = (float)frames / tm; + /*printf("%.2f fps \r", (float)frames / tm); + fflush(stdout);*/ + last_fps_upd = msec; + frames = 0; + } +} + +void draw_text(float r, float g, float b, const char *fmt, ...) +{ + char buf[256], *text = buf; + va_list ap; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + glUseProgram(0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, xsz, 0, ysz, -1, 1); + + glColor3f(r, g, b); + glRasterPos2f(2, 4); + while(*text) { + glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *text++); + } + glColor3f(1, 1, 1); + + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + + +/* +static void update_texture() +{ + int ntx = next_pow2(xsz); + int nty = next_pow2(ysz); + + if(ntx != tex_xsz || nty != tex_ysz) { + tex_xsz = ntx; + tex_ysz = nty; + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, tex_xsz, tex_ysz, 0, GL_RGB, GL_FLOAT, 0); + } +} +*/ + +static void idle() +{ + glutPostRedisplay(); +} + +static void reshape(int x, int y) +{ + int tx, ty; + + xsz = x / img_scale; + ysz = y / img_scale; + + glViewport(0, 0, x, y); + + /* setup the texture matrix that maps just the visible area + * of the texture to [0, 1] + */ + tx = next_pow2(xsz); + ty = next_pow2(ysz); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef((float)xsz / tx, (float)ysz / ty, 1.0f); + + resize_renderer(xsz, ysz); +} + +static void handle_keys(float dt) +{ + Vector3 move; + float tilt = 0.0f; + float offs = dt * 8.0; + + if(keystate['w']) { + move.z += offs; + } + if(keystate['s']) { + move.z -= offs; + } + if(keystate['d']) { + move.x += offs; + } + if(keystate['a']) { + move.x -= offs; + } + + if(keystate['q']) { + tilt -= dt; + } + if(keystate['e']) { + tilt += dt; + } + + cam->input_move(move.x, move.y, move.z); + cam->input_rotate(0, 0, tilt); +} + +static void keyb(unsigned char key, int x, int y) +{ + keystate[key] = true; + + switch(key) { + case 27: + exit(0); + + case '`': + sdr_valid = reload_shader(); + break; + } +} + +static void keyb_up(unsigned char key, int x, int y) +{ + keystate[key] = false; +} + +static void skeyb(int key, int x, int y) +{ + switch(key) { + case GLUT_KEY_F1: + printf("reinitializing\n"); + cleanup(); + sdr_valid = init(scene_fname); + break; + + default: + break; + } +} + +static int prev_x, prev_y; + +static void mouse(int bn, int st, int x, int y) +{ + prev_x = x; + prev_y = y; +} + +static void motion(int x, int y) +{ + int dx = x - prev_x; + int dy = y - prev_y; + prev_x = x; + prev_y = y; + cam->input_rotate(-dy * 0.01, -dx * 0.01, 0); +} + +static void sball_motion(int x, int y, int z) +{ + float fx = x * 0.01; + float fy = y * 0.01; + float fz = z * 0.01; + cam->input_move(fx, fy, fz); +} + +static void sball_rotate(int x, int y, int z) +{ + float fx = x * 0.00025; + float fy = y * 0.00025; + float fz = z * 0.00025; + cam->input_rotate(fx, fy, fz); +} + +static void sball_button(int bn, int state) +{ +} + + +int next_pow2(int x) +{ + x--; + x = (x >> 1) | x; + x = (x >> 2) | x; + x = (x >> 4) | x; + x = (x >> 8) | x; + x = (x >> 16) | x; + return x + 1; +} diff -r 000000000000 -r f234630e38ff src/material.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/material.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,41 @@ +/* +Simple introductory ray tracer +Copyright (C) 2012 John Tsiombikas + +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 . +*/ +#include "material.h" + +Material::Material() + : diffuse(1.0, 1.0, 1.0), specular(0.0, 0.0, 0.0) +{ + shininess = 60.0; + reflectivity = 0.0; + transparency = 0.0; + ior = 1.0; + + tex = 0; +} + +Material::Material(const Color &dcol, const Color &scol, float spow, + float refl, float refr, float ior) + : diffuse(dcol), specular(scol) +{ + shininess = spow; + reflectivity = refl; + transparency = refr; + this->ior = ior; + + tex = 0; +} diff -r 000000000000 -r f234630e38ff src/material.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/material.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,50 @@ +/* +Simple introductory ray tracer +Copyright (C) 2012 John Tsiombikas + +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 . +*/ +#ifndef MATERIAL_H_ +#define MATERIAL_H_ + +#include "vmath/vmath.h" + +// colors are simply RGB vectors +typedef Vector3 Color; + +class Texture; + +class Material { +public: + // phong model parameters + Color diffuse; // amount of diffuse light scattering (per color channel) + Color specular; // amount of specular light reflection (per color channel) + float shininess; // higher shininess values -> more focused specular refl. + + Color emission; // emissive light + + // additional raytracing parameters + float reflectivity; // range [0, 1] + float transparency; // range [0, 1] + float ior; // index of refraction + + Texture *tex; + Vector4 mega_rect; // tex coords for the megatexture + + Material(); + Material(const Color &dcol, const Color &scol = Color(1.0, 1.0, 1.0), float spow = 60.0, + float refl = 0.0, float refr = 0.0, float ior = 1.0); +}; + +#endif // MATERIAL_H_ diff -r 000000000000 -r f234630e38ff src/object.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,8 @@ +#include "object.h" + +void Object::prepare_xform(long msec) +{ + get_xform(msec, &xform, &inv_xform); + dir_xform = xform; + dir_xform.keep_upper_left(); +} diff -r 000000000000 -r f234630e38ff src/object.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,48 @@ +/* +Simple introductory ray tracer +Copyright (C) 2012 John Tsiombikas + +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 . +*/ +#ifndef OBJECT_H_ +#define OBJECT_H_ + +#include "vmath/vmath.h" +#include "material.h" +#include "xform_node.h" + +struct HitPoint; + +class Object : public XFormNode { +public: + Matrix4x4 xform, inv_xform, dir_xform; + + Material material; // surface material properties + + virtual ~Object() {} + + virtual void prepare_xform(long msec); + + virtual bool intersect(const Ray &ray, HitPoint *pt) const = 0; +}; + +struct HitPoint { + float dist; // parametric distance of intersection along the ray + Vector3 pos; // world position of the intersection point + Vector3 normal; // surface normal vector at the intersection point + Vector2 texcoord; // texture coordinates at the intersection point + const Object *obj; // pointer to the intersected object +}; + +#endif // OBJECT_H_ diff -r 000000000000 -r f234630e38ff src/opengl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/opengl.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,14 @@ +#ifndef OPENGL_H_ +#define OPENGL_H_ + +#include + +#ifndef __APPLE__ +#include +#else +#include +#endif + +int next_pow2(int x); + +#endif // OPENGL_H_ diff -r 000000000000 -r f234630e38ff src/plane.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plane.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,45 @@ +#include "plane.h" + +Plane::Plane() + : normal(0.0, 1.0, 0.0) +{ + dist = 0.0; +} + +Plane::Plane(const Vector3 &norm, float dist) + : normal(norm) +{ + normal.normalize(); + this->dist = dist; +} + +bool Plane::intersect(const Ray &inray, HitPoint *pt) const +{ + Ray ray = inray.transformed(inv_xform); + + float ndotdir = dot_product(normal, ray.dir); + if(fabs(ndotdir) < 1e-5) { + return false; // ray parallel to the plane + } + + Vector3 planept = normal * dist; + Vector3 pptdir = planept - ray.origin; + + float t = dot_product(normal, pptdir) / ndotdir; + if(t < 1e-4) { + return false; // discard intersections behind the origin + } + + // fill the HitPoint structure + pt->obj = this; + pt->dist = t; + + pt->pos = ray.origin + ray.dir * t; + + pt->texcoord.x = pt->pos.x; + pt->texcoord.y = pt->pos.z; + + pt->pos.transform(xform); + pt->normal = normal.transformed(dir_xform);//.normalized(); + return true; +} diff -r 000000000000 -r f234630e38ff src/plane.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plane.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,35 @@ +/* +Simple introductory ray tracer +Copyright (C) 2012 John Tsiombikas + +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 . +*/ +#ifndef PLANE_H_ +#define PLANE_H_ + +#include "vmath/vmath.h" +#include "object.h" + +class Plane : public Object { +public: + Vector3 normal; + float dist; + + Plane(); + Plane(const Vector3 &norm, float dist); + + bool intersect(const Ray &ray, HitPoint *pt) const; +}; + +#endif // PLANE_H_ diff -r 000000000000 -r f234630e38ff src/rend.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rend.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,372 @@ +#include +#include "scene.h" +#include "image.h" +#include "rend.h" +#include "opengl.h" +#include "glsdr.h" + +enum { + TEX_RAYDIR, + TEX_SPHERES, + TEX_PLANES, + TEX_BOXES, + TEX_TEXTURES, + TEX_ENV, + TEX_XFORM, + + NUM_SDR_TEXTURES +}; + +bool reload_shader(); +void gen_ray_texture(unsigned int tex, int xsz, int ysz, float vfov); +static Vector3 get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg); +static int round_pow2(int x); + +static GPUScene *scn; + +static unsigned int sdr; +static unsigned int textures[NUM_SDR_TEXTURES]; + +bool init_renderer(GPUScene *s, int xsz, int ysz) +{ + scn = s; + + glGenTextures(1, textures + TEX_RAYDIR); + glBindTexture(GL_TEXTURE_2D, textures[TEX_RAYDIR]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if(!(s->create_textures())) { + fprintf(stderr, "failed to create scene textures\n"); + return false; + } + + textures[TEX_SPHERES] = s->get_texture(GPUScene::TEX_SPHERE); + textures[TEX_PLANES] = s->get_texture(GPUScene::TEX_PLANE); + textures[TEX_BOXES] = s->get_texture(GPUScene::TEX_BOX); + textures[TEX_TEXTURES] = s->get_texture(GPUScene::TEX_TEXTURE); + textures[TEX_ENV] = s->get_texture(GPUScene::TEX_ENV); + textures[TEX_XFORM] = s->get_texture(GPUScene::TEX_XFORM); + + if(!reload_shader()) { + return false; + } + + resize_renderer(xsz, ysz); + + return true; +} + +bool reload_shader() +{ + if(sdr) { + free_program(sdr); + } + + printf("loading shader...\n"); + + if(!(sdr = create_program_load("sdr/vertex.glsl", "sdr/rt.glsl"))) { + return false; + } + set_uniform_int(sdr, "tex_raydir", TEX_RAYDIR); + set_uniform_int(sdr, "tex_spheres", TEX_SPHERES); + set_uniform_int(sdr, "tex_planes", TEX_PLANES); + set_uniform_int(sdr, "tex_boxes", TEX_BOXES); + set_uniform_int(sdr, "tex_megatex", TEX_TEXTURES); + set_uniform_int(sdr, "tex_env", TEX_ENV); + set_uniform_int(sdr, "tex_xforms", TEX_XFORM); + + set_uniform_int(sdr, "num_lights", scn->get_light_count()); + + for(int i=0; iget_light_count(); i++) { + const Light *lt = scn->get_light(i); + + char name[64]; + sprintf(name, "lights[%d].pos", i); + set_uniform_float3(sdr, name, lt->pos.x, lt->pos.y, lt->pos.z); + + sprintf(name, "lights[%d].color", i); + set_uniform_float3(sdr, name, lt->color.x, lt->color.y, lt->color.z); + } + + Vector2 fog; + scn->get_fog(&fog.x, &fog.y); + if(fog.x < 0.0 && fog.y < 0.0) { + fog.x = 0.0; + fog.y = 100000.0; + } + set_uniform_float2(sdr, "fog", fog.x, fog.y); + return true; +} + +void destroy_renderer() +{ + free_program(sdr); + sdr = 0; +} + +void resize_renderer(int xsz, int ysz) +{ + gen_ray_texture(textures[TEX_RAYDIR], xsz, ysz, 45.0f); +} + +float *render_frame(long msec) +{ + scn->prepare_xform(msec); + scn->update_xform_texture(); + + Camera *cam = scn->get_camera(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + Vector3 cpos = cam->get_position(); + glTranslatef(cpos.x, cpos.y, cpos.z); + Matrix4x4 cmat = cam->get_matrix(); + glMultTransposeMatrixf(cmat[0]); + + for(int i=0; i> 1) | x; + x = (x >> 2) | x; + x = (x >> 4) | x; + x = (x >> 8) | x; + x = (x >> 16) | x; + return x + 1; +} + + +#if 0 +Color trace_ray(const Scene *scn, const Ray &ray, int rdepth) +{ + HitPoint hit; + + if(scn->intersect(ray, &hit)) { + float t; + if(scn->fog_start >= 0.0 && (t = (hit.dist - scn->fog_start) / (scn->fog_end - scn->fog_start)) > 0.0) { + return lerp(shade(scn, ray, hit, rdepth), scn->env_color(ray), t > 1.0 ? 1.0 : t); + } + return shade(scn, ray, hit, rdepth); + } + + return scn->env_color(ray); +} + +Color shade(const Scene *scn, const Ray &ray, const HitPoint &hit, int rdepth) +{ + const Material *mat = &hit.obj->material; + + // if we're leaving the object, we need to invert the normal (and ior) + Vector3 normal; + bool entering; + if(dot_product(hit.normal, ray.dir) <= 0.0) { + normal = hit.normal; + entering = true; + } else { + normal = -hit.normal; + entering = false; + } + + Vector3 vdir = -ray.dir; + + Color diffuse_color = mat->diffuse; + Color tex_color{1, 1, 1}; + if(mat->tex) { + tex_color *= mat->tex->sample(hit); + diffuse_color *= tex_color; + } + + Color color = mat->emission * tex_color; + + // image-based lighting + if(scn->envmap_conv) { + // pick a random direction and create a sampling ray + Ray envray; + envray.origin = hit.pos; + rand_dir(&envray.dir.x, &envray.dir.y, &envray.dir.z, (unsigned int*)ray.user); + if(dot_product(envray.dir, normal) < 0.0) { + envray.dir = -envray.dir; + } + + HitPoint env_hit; + if(!scn->intersect(envray, &env_hit)) { + Vector3 dir = envray.dir; + color += scn->envmap_conv->sample(dir.x, dir.y, dir.z) * diffuse_color; + } + } + + for(Light *lt: scn->lights) { + + /* construct a shadow ray to determine if there is an uninterrupted + * path between the intersection point and the light source + */ + Ray shadow_ray = ray; + shadow_ray.origin = hit.pos; + shadow_ray.dir = lt->pos - hit.pos; + + /* the interval [0, 1] represents the part of the ray from the origin + * to the light. We don't care about intersections behind the origin + * of the shadow ray (behind the surface of the object), or after the + * light source. We only care if there's something in between hiding the + * light. + */ + HitPoint shadow_hit; + if(scn->intersect(shadow_ray, &shadow_hit) && shadow_hit.dist < 1.0f) { + continue; // skip this light, it's hidden from view + } + + // calculate the light direction + Vector3 ldir = shadow_ray.dir.normalized(); + // calculate the reflected light direction + Vector3 lref = ldir.reflection(normal); + + float diffuse = std::max(dot_product(ldir, normal), 0.0f); + float specular = pow(std::max(dot_product(lref, vdir), 0.0f), mat->shininess); + + color += (diffuse_color * diffuse + mat->specular * specular) * lt->color; + } + + Color spec_col; + + if(mat->reflectivity > 0.001f && rdepth < MAX_RAY_DEPTH) { + Ray refl_ray{ray}; + refl_ray.origin = hit.pos; + refl_ray.dir = -ray.dir.reflection(normal); + + spec_col += trace_ray(scn, refl_ray, rdepth + 1) * mat->reflectivity; + } + + if(mat->transparency > 0.001f && rdepth < MAX_RAY_DEPTH) { + float from_ior = entering ? 1.0 : mat->ior; + float to_ior = entering ? mat->ior : 1.0; + + Ray refr_ray{ray}; + refr_ray.origin = hit.pos; + refr_ray.dir = ray.dir.refraction(normal, from_ior / to_ior); + + Color tcol = trace_ray(scn, refr_ray, rdepth + 1) * mat->transparency; + + float fres = fresnel(ray.dir, refr_ray.dir, normal, from_ior, to_ior); + spec_col = spec_col * fres + tcol * (1.0 - fres); + } + + return color + spec_col; +} + + +static void rand_dir(float *x, float *y, float *z, unsigned int *seedp) +{ + float u = (float)rand_r(seedp) / RAND_MAX; + float v = (float)rand_r(seedp) / RAND_MAX; + + float theta = 2.0 * M_PI * u; + float phi = acos(2.0 * v - 1.0); + + *x = cos(theta) * sin(phi); + *y = sin(theta) * sin(phi); + *z = cos(phi); +} + +static float fresnel(const Vector3 &inc, const Vector3 &trans, const Vector3 &norm, float ior_inc, float ior_trans) +{ + float cos_inc = dot_product(-inc, norm); + float cos_trans = dot_product(-trans, norm); + + return fresnel(cos_inc, cos_trans, ior_inc, ior_trans); +} + +static float fresnel(float cos_inc, float cos_trans, float ior_inc, float ior_trans) +{ + float r0 = ((ior_trans * cos_inc) - (ior_inc * cos_trans)) / + ((ior_trans * cos_inc) + (ior_inc * cos_trans)); + float r1 = ((ior_inc * cos_inc) - (ior_trans * cos_trans)) / + ((ior_inc * cos_inc) + (ior_trans * cos_trans)); + return (r0 * r0 + r1 * r1) * 0.5f; +} +#endif diff -r 000000000000 -r f234630e38ff src/rend.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rend.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,14 @@ +#ifndef REND_H_ +#define REND_H_ + +#include "gpuscene.h" + +bool init_renderer(GPUScene *scn, int xsz, int ysz); +bool reload_shader(); +void destroy_renderer(); + +void resize_renderer(int xsz, int ysz); + +float *render_frame(long msec); + +#endif // REND_H_ diff -r 000000000000 -r f234630e38ff src/scene.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include "scene.h" +#include "texture.h" + +bool load_scene_file(Scene *scn, const char *fname); +bool save_scene_file(const Scene *scn, const char *fname); + + +// default camera +TargetCamera Scene::def_cam(Vector3(0, 0, -10), Vector3(0, 0, 0)); + +Scene::Scene() +{ + camera = &def_cam; + bgcolor = Color(0, 0, 0); + envmap = envmap_conv = 0; + fog_start = fog_end = -1; +} + +Scene::~Scene() +{ + for(auto obj: objects) { + delete obj; + } + for(auto lt: lights) { + delete lt; + } + if(camera != &def_cam) { + delete camera; + } + + delete envmap; + if(envmap_conv != envmap) { + delete envmap_conv; + } +} + +bool Scene::load(const char *fname) +{ + return load_scene_file(this, fname); +} + +bool Scene::save(const char *fname) const +{ + return save_scene_file(this, fname); +} + +void Scene::set_background_color(const Color &color) +{ + bgcolor = color; +} + +void Scene::set_fog(float fog_start, float fog_end) +{ + this->fog_start = fog_start; + this->fog_end = fog_end; +} + +void Scene::get_fog(float *fog_start, float *fog_end) const +{ + *fog_start = this->fog_start; + *fog_end = this->fog_end; +} + +void Scene::set_environment_map(TextureCube *map, TextureCube *map_conv) +{ + envmap = map; + envmap_conv = map_conv;//map_conv ? map_conv : map; +} + +void Scene::add_object(Object *obj) +{ + objects.push_back(obj); +} + +Object *Scene::get_object(int i) const +{ + if(i < 0 || i >= (int)objects.size()) { + return 0; + } + return objects[i]; +} + +int Scene::get_object_count() const +{ + return (int)objects.size(); +} + +void Scene::add_light(Light *lt) +{ + lights.push_back(lt); +} + +Light *Scene::get_light(int i) const +{ + if(i < 0 || i >= (int)lights.size()) { + return 0; + } + return lights[i]; +} + +int Scene::get_light_count() const +{ + return (int)lights.size(); +} + +void Scene::set_camera(Camera *cam) +{ + if(camera != &def_cam) { + delete camera; + } + camera = cam; +} + +Camera *Scene::get_camera() const +{ + return camera; +} + +bool Scene::intersect(const Ray &ray, HitPoint *nearest_hit) const +{ + nearest_hit->obj = 0; + nearest_hit->dist = FLT_MAX; + + // find the nearest hit (if any) + for(Object *obj: objects) { + HitPoint hit; + if(obj->intersect(ray, &hit) && hit.dist < nearest_hit->dist) { + *nearest_hit = hit; + } + } + return nearest_hit->obj != 0; +} + +Color Scene::env_color(const Ray &ray) const +{ + if(envmap) { + Vector3 dir = ray.dir.normalized(); + return envmap->sample(dir.x, dir.y, dir.z); + } + return bgcolor; +} + +void Scene::prepare_xform(long msec) +{ + int nobj = get_object_count(); + for(int i=0; iprepare_xform(msec); + } +} diff -r 000000000000 -r f234630e38ff src/scene.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,56 @@ +#ifndef SCENE_H_ +#define SCENE_H_ + +#include +#include "object.h" +#include "light.h" +#include "camera.h" +#include "texture.h" + +class Scene { +protected: + std::vector objects; + std::vector lights; + Camera *camera; + + static TargetCamera def_cam; // default camera + + Color bgcolor; + TextureCube *envmap, *envmap_conv; + float fog_start, fog_end; + +public: + Scene(); + virtual ~Scene(); + + virtual bool load(const char *fname); + virtual bool save(const char *fname) const; + + virtual void set_background_color(const Color &color); + virtual void set_fog(float fog_start, float fog_end); + virtual void get_fog(float *fog_start, float *fog_end) const; + + virtual void set_environment_map(TextureCube *map, TextureCube *map_conv = 0); + + virtual void add_object(Object *obj); + virtual Object *get_object(int i) const; + virtual int get_object_count() const; + + virtual void add_light(Light *lt); + virtual Light *get_light(int i) const; + virtual int get_light_count() const; + + virtual void set_camera(Camera *cam); + virtual Camera *get_camera() const; + + virtual bool intersect(const Ray &ray, HitPoint *hit) const; + + virtual Color env_color(const Ray &ray) const; + + virtual void prepare_xform(long msec); + + friend Color trace_ray(const Scene *scn, const Ray &ray, int rdepth); + friend Color shade(const Scene *scn, const Ray &ray, const HitPoint &hit, int rdepth); +}; + +#endif // SCENE_H_ diff -r 000000000000 -r f234630e38ff src/scene_load.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene_load.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,681 @@ +#include +#include +#include +#include "scene.h" +#include "sphere.h" +#include "plane.h" +#include "box.h" +#include "light.h" +#include "camera.h" +#include "texture.h" + +static bool parse_material(char **argv, Material *mtl, std::string *name); +static Object *parse_sphere(char **argv, const std::map &materials); +static Object *parse_plane(char **argv, const std::map &materials); +static Object *parse_box(char **argv, const std::map &materials); +static Light *parse_light(char **argv); +static Camera *parse_camera(char **argv); +static bool parse_env(char **argv, Scene *scn); +static bool parse_xform(char **argv, Scene *scn); +static bool parse_vec(char **argv, Vector3 *vptr); + +static char *strip_space(char *s); +static char **split_args(char *args); + +bool load_scene_file(Scene *scn, const char *fname) +{ + FILE *fp; + if(!(fp = fopen(fname, "rb"))) { + fprintf(stderr, "%s: failed to open scene file: %s: %s\n", __FUNCTION__, fname, strerror(errno)); + return false; + } + + char buf[512]; + std::map materials; + + int lineno = 0; + while(fgets(buf, sizeof buf, fp)) { + char *line = strip_space(buf); + + lineno++; + + if(!*line || *line == '\n' || *line == '#') { + continue; + } + + if(strstr(line, "material") == line) { + Material mtl; + std::string name; + + char *args = strip_space(line + strlen("material")); + if(!parse_material(split_args(args), &mtl, &name)) { + goto err; + } + materials[name] = mtl; + + } else if(strstr(line, "sphere") == line) { + char *args = strip_space(line + strlen("sphere")); + Object *obj = parse_sphere(split_args(args), materials); + if(!obj) { + goto err; + } + scn->add_object(obj); + + } else if(strstr(line, "plane") == line) { + char *args = strip_space(line + strlen("plane")); + Object *obj = parse_plane(split_args(args), materials); + if(!obj) { + goto err; + } + scn->add_object(obj); + + } else if(strstr(line, "box") == line) { + char *args = strip_space(line + strlen("box")); + Object *obj = parse_box(split_args(args), materials); + if(!obj) { + goto err; + } + scn->add_object(obj); + + } else if(strstr(line, "light") == line) { + char *args = strip_space(line + strlen("light")); + Light *lt = parse_light(split_args(args)); + if(!lt) { + goto err; + } + scn->add_light(lt); + + } else if(strstr(line, "camera") == line) { + char *args = strip_space(line + strlen("camera")); + Camera *cam = parse_camera(split_args(args)); + if(!cam) { + goto err; + } + scn->set_camera(cam); + + } else if(strstr(line, "environment") == line) { + char *args = strip_space(line + strlen("environment")); + if(!parse_env(split_args(args), scn)) { + goto err; + } + + } else if(strstr(line, "xform") == line) { + char *args = strip_space(line + strlen("xform")); + if(!parse_xform(split_args(args), scn)) { + goto err; + } + + } else { + fprintf(stderr, "%s error %s:%d: unknown command: %s\n", __FUNCTION__, fname, lineno, line); + goto err; + } + + } + + fclose(fp); + return true; + +err: + fclose(fp); + return false; +} + +bool save_scene_file(const Scene *scn, const char *fname) +{ + FILE *fp; + if(!(fp = fopen(fname, "wb"))) { + fprintf(stderr, "%s: failed to save scene file: %s: %s\n", __FUNCTION__, fname, strerror(errno)); + return false; + } + + for(int i=0; iget_object_count(); i++) { + // first write the material + const Object *obj = scn->get_object(i); + const Material *mat = &obj->material; + + char name[128]; + sprintf(name, "mat%d", i); + + fprintf(fp, "material -name %s", name); + fprintf(fp, " -diffuse %.3f %.3f %.3f", mat->diffuse.x, mat->diffuse.y, mat->diffuse.z); + fprintf(fp, " -specular %.3f %.3f %.3f", mat->specular.x, mat->specular.y, mat->specular.z); + fprintf(fp, " -shininess %.3f", mat->shininess); + fprintf(fp, " -emission %.3f %.3f %.3f", mat->emission.x, mat->emission.y, mat->emission.z); + fprintf(fp, " -reflect %.3f", mat->reflectivity); + fprintf(fp, " -trans %.3f", mat->transparency); + fprintf(fp, " -ior %.3f", mat->ior); + if(mat->tex) { + fprintf(fp, " -texture %s", mat->tex->get_name()); + } + fputc('\n', fp); + + // then write the object + const Sphere *sph; + const Plane *plane; + const Box *box; + if((sph = dynamic_cast(obj))) { + fprintf(fp, "sphere -material %s", name); + fprintf(fp, " -center %.3f %.3f %.3f", sph->pos.x, sph->pos.y, sph->pos.z); + fprintf(fp, " -radius %.3f\n", sph->radius); + } else if((plane = dynamic_cast(obj))) { + fprintf(fp, "plane -material %s", name); + fprintf(fp, " -normal %.3f %.3f %.3f", plane->normal.x, plane->normal.y, plane->normal.z); + fprintf(fp, " -distance %.3f\n", plane->dist); + } else if((box = dynamic_cast(obj))) { + fprintf(fp, "box -material %s", name); + fprintf(fp, " -min %.3f %.3f %.3f", box->min.x, box->min.y, box->min.z); + fprintf(fp, " -max %.3f %.3f %.3f\n", box->max.x, box->max.y, box->max.z); + } + } + + for(int i=0; iget_light_count(); i++) { + const Light *lt = scn->get_light(i); + + fprintf(fp, "light -position %.3f %.3f %.3f", lt->pos.x, lt->pos.y, lt->pos.z); + fprintf(fp, " -color %.3f %.3f %.3f\n", lt->color.x, lt->color.y, lt->color.z); + } + + const TargetCamera *tcam = dynamic_cast(scn->get_camera()); + if(tcam) { + Vector3 pos = tcam->get_position(); + fprintf(fp, "camera -position %.3f %.3f %.3f", pos.x, pos.y, pos.z); + Vector3 targ = tcam->get_target(); + fprintf(fp, " -target %.3f %.3f %.3f", targ.x, targ.y, targ.z); + fprintf(fp, " -fov %.3f\n", tcam->get_fov()); + } + + + fclose(fp); + return true; +} + +#define ARGERR(n) \ + do { fprintf(stderr, "failed to parse %s argument\n", (n)); return false; } while(0) + +static bool parse_material(char **argv, Material *mtl, std::string *name) +{ + char *endp; + + for(int i=0; argv[i]; i++) { + if(strcmp(argv[i], "-name") == 0) { + *name = argv[++i]; + } else if(strcmp(argv[i], "-diffuse") == 0) { + if(!parse_vec(argv + i + 1, &mtl->diffuse)) { + ARGERR("diffuse"); + } + argv += 3; + + } else if(strcmp(argv[i], "-specular") == 0) { + if(!parse_vec(argv + i + 1, &mtl->specular)) { + ARGERR("specular"); + } + argv += 3; + + } else if(strcmp(argv[i], "-emission") == 0) { + if(!parse_vec(argv + i + 1, &mtl->emission)) { + ARGERR("emission"); + } + argv += 3; + + } else if(strcmp(argv[i], "-shininess") == 0) { + mtl->shininess = strtod(argv[++i], &endp); + if(endp == argv[i]) { + ARGERR("shininess"); + } + + } else if(strcmp(argv[i], "-reflect") == 0) { + mtl->reflectivity = strtod(argv[++i], &endp); + if(endp == argv[i]) { + ARGERR("reflect"); + } + + } else if(strcmp(argv[i], "-trans") == 0) { + mtl->transparency = strtod(argv[++i], &endp); + if(endp == argv[i]) { + ARGERR("trans"); + } + + } else if(strcmp(argv[i], "-ior") == 0) { + mtl->ior = strtod(argv[++i], &endp); + if(endp == argv[i]) { + ARGERR("ior"); + } + + } else if(strcmp(argv[i], "-texture") == 0) { + if(!(mtl->tex = load_texture(argv[++i]))) { + return false; + } + + } else { + fprintf(stderr, "invalid material option: %s\n", argv[i]); + return false; + } + } + return true; +} + +static Object *parse_sphere(char **argv, const std::map &materials) +{ + char *endp; + Sphere *sph = new Sphere; + + for(int i=0; argv[i]; i++) { + if(strcmp(argv[i], "-name") == 0) { + sph->set_name(argv[++i]); + + } else if(strcmp(argv[i], "-center") == 0) { + if(!parse_vec(argv + i + 1, &sph->pos)) { + fprintf(stderr, "failed to parse sphere center vector\n"); + return 0; + } + argv += 3; + + } else if(strcmp(argv[i], "-radius") == 0) { + sph->radius = strtod(argv[++i], &endp); + if(endp == argv[i]) { + fprintf(stderr, "failed to parse sphere radius\n"); + return 0; + } + + } else if(strcmp(argv[i], "-material") == 0) { + auto it = materials.find(argv[++i]); + if(it == materials.end()) { + fprintf(stderr, "material %s not found\n", argv[i]); + return 0; + } + + sph->material = it->second; + } else { + fprintf(stderr, "invalid sphere option: %s\n", argv[i]); + return 0; + } + } + + return sph; +} + +static Object *parse_plane(char **argv, const std::map &materials) +{ + char *endp; + Plane *plane = new Plane; + + for(int i=0; argv[i]; i++) { + if(strcmp(argv[i], "-name") == 0) { + plane->set_name(argv[++i]); + + } else if(strcmp(argv[i], "-normal") == 0) { + if(!parse_vec(argv + i + 1, &plane->normal)) { + fprintf(stderr, "failed to parse plane normal\n"); + return 0; + } + plane->normal.normalize(); + argv += 3; + + } else if(strcmp(argv[i], "-distance") == 0) { + plane->dist = strtod(argv[++i], &endp); + if(endp == argv[i]) { + fprintf(stderr, "failed to parse plane distance\n"); + return 0; + } + + } else if(strcmp(argv[i], "-material") == 0) { + auto it = materials.find(argv[++i]); + if(it == materials.end()) { + fprintf(stderr, "material %s not found\n", argv[i]); + return 0; + } + + plane->material = it->second; + } else { + fprintf(stderr, "invalid plane option: %s\n", argv[i]); + return 0; + } + } + + return plane; +} + +static Object *parse_box(char **argv, const std::map &materials) +{ + Box *box = new Box; + + for(int i=0; argv[i]; i++) { + if(strcmp(argv[i], "-name") == 0) { + box->set_name(argv[++i]); + + } else if(strcmp(argv[i], "-min") == 0) { + if(!parse_vec(argv + i + 1, &box->min)) { + fprintf(stderr, "failed to parse box min\n"); + return 0; + } + argv += 3; + + } else if(strcmp(argv[i], "-max") == 0) { + if(!parse_vec(argv + i + 1, &box->max)) { + fprintf(stderr, "failed to parse box max\n"); + return 0; + } + argv += 3; + + } else if(strcmp(argv[i], "-material") == 0) { + auto it = materials.find(argv[++i]); + if(it == materials.end()) { + fprintf(stderr, "material %s not found\n", argv[i]); + return 0; + } + + box->material = it->second; + } else { + fprintf(stderr, "invalid box option: %s\n", argv[i]); + return 0; + } + } + + return box; +} + +static Light *parse_light(char **argv) +{ + Light *lt = new Light; + + for(int i=0; argv[i]; i++) { + if(strcmp(argv[i], "-position") == 0) { + if(!parse_vec(argv + i + 1, <->pos)) { + fprintf(stderr, "failed to parse light position\n"); + return 0; + } + argv += 3; + + } else if(strcmp(argv[i], "-color") == 0) { + if(!parse_vec(argv + i + 1, <->color)) { + fprintf(stderr, "failed to parse light color\n"); + return 0; + } + argv += 3; + + } else { + fprintf(stderr, "invalid light option: %s\n", argv[i]); + return 0; + } + } + + return lt; +} + +static Camera *parse_camera(char **argv) +{ + char *endp; + TargetCamera *cam = new TargetCamera; + + for(int i=0; argv[i]; i++) { + if(strcmp(argv[i], "-position") == 0) { + Vector3 pos; + if(!parse_vec(argv + i + 1, &pos)) { + fprintf(stderr, "failed to parse camera position\n"); + return 0; + } + argv += 3; + cam->set_position(pos); + + } else if(strcmp(argv[i], "-target") == 0) { + Vector3 targ; + if(!parse_vec(argv + i + 1, &targ)) { + fprintf(stderr, "failed to parse camera target\n"); + return 0; + } + argv += 3; + cam->set_target(targ); + + } else if(strcmp(argv[i], "-fov") == 0) { + float fov = strtod(argv[++i], &endp); + if(endp == argv[i]) { + fprintf(stderr, "failed to parse camera fov\n"); + return 0; + } + cam->set_fov(M_PI * fov / 180.0); + + } else { + fprintf(stderr, "invalid camera option: %s\n", argv[i]); + return 0; + } + } + + return cam; +} + +static bool parse_env(char **argv, Scene *scn) +{ + char *endp; + + TextureCube *env_tex = 0; + TextureCube *conv_tex = 0; + + float fog_start = -1; + float fog_end = -1; + + for(int i=0; argv[i]; i++) { + if(strcmp(argv[i], "-color") == 0) { + Color bgcolor; + if(!parse_vec(argv + i + 1, &bgcolor)) { + fprintf(stderr, "failed to parse environment color\n"); + return false; + } + i += 3; + scn->set_background_color(bgcolor); + + } else if(strcmp(argv[i], "-fog-start") == 0) { + fog_start = strtod(argv[++i], &endp); + if(endp == argv[i]) { + fprintf(stderr, "failed to parse environment fog start\n"); + return false; + } + + } else if(strcmp(argv[i], "-fog-end") == 0) { + fog_end = strtod(argv[++i], &endp); + if(endp == argv[i]) { + fprintf(stderr, "failed to parse environment fog end\n"); + return false; + } + + } else if(strcmp(argv[i], "-texture") == 0 || strcmp(argv[i], "-texture-conv") == 0) { + Texture *tex = load_texture(argv[++i]); + if(!tex || !dynamic_cast(tex)) { + fprintf(stderr, "failed to load environment cubemap: %s\n", argv[i]); + delete tex; + return false; + } + + if(strstr(argv[i - 1], "-conv")) { + conv_tex = (TextureCube*)tex; + } else { + env_tex = (TextureCube*)tex; + } + + } else { + fprintf(stderr, "invalid environment option: %s\n", argv[i]); + return false; + } + } + + if(env_tex || conv_tex) { + scn->set_environment_map(env_tex, conv_tex); + } + + if(fog_start > 0.0 && fog_end > 0.0) { + scn->set_fog(fog_start, fog_end); + } + + return true; +} + +static bool parse_xform(char **argv, Scene *scn) +{ + char *endp, *name = 0; + Vector3 pos, rot_axis, scale; + float rot_angle = 0; + long tm = 0; + Extrap extrap = EXTRAP_REPEAT; + + bool have_pos = false; + bool have_rot_axis = false; + bool have_rot_angle = false; + bool have_scale = false; + bool have_extrap = false; + + for(int i=0; argv[i]; i++) { + if(strcmp(argv[i], "-name") == 0) { + name = argv[++i]; + + } else if(strcmp(argv[i], "-pos") == 0) { + if(!parse_vec(argv + i + 1, &pos)) { + fprintf(stderr, "failed to parse xform position\n"); + return false; + } + have_pos = true; + i += 3; + + } else if(strcmp(argv[i], "-rot-axis") == 0) { + if(!parse_vec(argv + i + 1, &rot_axis)) { + fprintf(stderr, "failed to parse xform rotation axis\n"); + return false; + } + have_rot_axis = true; + i += 3; + + } else if(strcmp(argv[i], "-rot-angle") == 0) { + rot_angle = strtod(argv[++i], &endp); + if(endp == argv[i]) { + fprintf(stderr, "failed to parse xform rotation angle\n"); + return false; + } + rot_angle = M_PI * rot_angle / 180.0; + have_rot_angle = true; + + } else if(strcmp(argv[i], "-scale") == 0) { + if(!parse_vec(argv + i + 1, &scale)) { + fprintf(stderr, "failed to parse xform scale\n"); + return false; + } + have_scale = true; + i += 3; + + } else if(strcmp(argv[i], "-time") == 0) { + float tm_sec = strtod(argv[++i], &endp); + if(endp == argv[i]) { + fprintf(stderr, "failed to parse xform time\n"); + return false; + } + tm = (long)(tm_sec * 1000.0f); + + } else if(strcmp(argv[i], "-extrapolation") == 0) { + if(strcmp(argv[++i], "extend") == 0) { + extrap = EXTRAP_EXTEND; + } else if(strcmp(argv[i], "clamp") == 0) { + extrap = EXTRAP_CLAMP; + } else if(strcmp(argv[i], "repeat") == 0) { + extrap = EXTRAP_REPEAT; + } else if(strcmp(argv[i], "pingpong") == 0) { + extrap = EXTRAP_PINGPONG; + } else { + fprintf(stderr, "failed to parse xform extrapolation, invalid mode: %s\n", argv[i]); + return false; + } + have_extrap = true; + + } else { + fprintf(stderr, "invalid xform option: %s\n", argv[i]); + return false; + } + } + + if(!name) { + fprintf(stderr, "invalid xform command, missing -name option\n"); + return false; + } + if(have_rot_angle != have_rot_axis) { + fprintf(stderr, "invalid xform command, must have both -rot-angle and -rot-axis or neither\n"); + return false; + } + + Object *obj = 0; + + int nobj = scn->get_object_count(); + for(int i=0; iget_object(i); + if(strcmp(tmp->get_name(), name) == 0) { + obj = tmp; + break; + } + } + + if(!obj) { + fprintf(stderr, "invalid xform, refers to nonexistent object: %s\n", name); + return false; + } + + if(have_pos) { + obj->set_position(pos, tm); + } + if(have_rot_angle) { + obj->set_rotation(Quaternion(rot_axis, rot_angle), tm); + } + if(have_scale) { + obj->set_scaling(scale, tm); + } + + if(have_extrap) { + obj->set_extrapolator(extrap); + } + return true; +} + +static bool parse_vec(char **argv, Vector3 *vptr) +{ + char *endp; + + vptr->x = strtod(argv[0], &endp); + if(endp == argv[0]) + return false; + vptr->y = strtod(argv[1], &endp); + if(endp == argv[1]) + return false; + vptr->z = strtod(argv[2], &endp); + if(endp == argv[2]) + return false; + + return true; +} + + +static char *strip_space(char *s) +{ + while(*s && isspace(*s)) s++; + + if(!*s) + return s; + + char *endp = s + strlen(s) - 1; + while(isspace(*endp)) endp--; + endp[1] = 0; + + if((endp = strrchr(s, '#'))) { + *endp = 0; + } + + return s; +} + +static char **split_args(char *args) +{ + static std::vector argv; + char *tok = 0; + + argv.clear(); + + while((tok = strtok(tok ? 0 : args, " \t\v\n\r"))) { + argv.push_back(tok); + } + argv.push_back(0); + + return &argv[0]; +} diff -r 000000000000 -r f234630e38ff src/sphere.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sphere.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,52 @@ +#include +#include "sphere.h" + +Sphere::Sphere() +{ + radius = 1.0; +} + +Sphere::Sphere(const Vector3 &pos, float rad) +{ + radius = rad; + this->pos = pos; +} + +bool Sphere::intersect(const Ray &inray, HitPoint *pt) const +{ + Ray ray = inray.transformed(inv_xform); + + float a = dot_product(ray.dir, ray.dir); + float b = 2.0 * ray.dir.x * (ray.origin.x - pos.x) + + 2.0 * ray.dir.y * (ray.origin.y - pos.y) + + 2.0 * ray.dir.z * (ray.origin.z - pos.z); + float c = dot_product(ray.origin, ray.origin) + dot_product(pos, pos) - + 2.0 * dot_product(ray.origin, pos) - radius * radius; + + float discr = b * b - 4.0 * a * c; + if(discr < 1e-4) + return false; + + float sqrt_discr = sqrt(discr); + float t0 = (-b + sqrt_discr) / (2.0 * a); + float t1 = (-b - sqrt_discr) / (2.0 * a); + + if(t0 < 1e-4) + t0 = t1; + if(t1 < 1e-4) + t1 = t0; + + float t = t0 < t1 ? t0 : t1; + if(t < 1e-4) + return false; + + // fill the HitPoint structure + pt->obj = this; + pt->dist = t; + pt->pos = ray.origin + ray.dir * t; + pt->normal = (pt->pos - pos) / radius; + + pt->pos.transform(xform); + pt->normal.transform(dir_xform); + return true; +} diff -r 000000000000 -r f234630e38ff src/sphere.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sphere.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,35 @@ +/* +Simple introductory ray tracer +Copyright (C) 2012 John Tsiombikas + +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 . +*/ +#ifndef SPHERE_H_ +#define SPHERE_H_ + +#include "vmath/vmath.h" +#include "object.h" + +class Sphere : public Object { +public: + Vector3 pos; + float radius; + + Sphere(); + Sphere(const Vector3 &pos, float rad); + + bool intersect(const Ray &ray, HitPoint *pt) const; +}; + +#endif // SPHERE_H_ diff -r 000000000000 -r f234630e38ff src/texture.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texture.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include "texture.h" +#include "object.h" + +static inline Color sample_image(const Image &img, float u, float v, Texture::WrapMode wrapping); + +Texture::Texture() + : sampling(SampleMode::nearest), wrapping(WrapMode::repeat) +{ +} + +Texture::~Texture() {} + +void Texture::set_name(const char *name) +{ + this->name = name; +} + +const char *Texture::get_name() const +{ + return name.c_str(); +} + +void Texture::set_sampling_mode(SampleMode mode) +{ + sampling = mode; +} + +Texture::SampleMode Texture::get_sampling_mode() const +{ + return sampling; +} + +void Texture::set_wrapping_mode(WrapMode mode) +{ + wrapping = mode; +} + +Texture::WrapMode Texture::get_wrapping_mode() const +{ + return wrapping; +} + + +Color Texture::sample(const HitPoint &hit) const +{ + return sample(hit.texcoord.x, hit.texcoord.y, 0.0f); +} + +Image *Texture2D::get_image(int idx) +{ + return &img; +} + +const Image *Texture2D::get_image(int idx) const +{ + return &img; +} + + +bool Texture2D::load(const char *fname) +{ + name = fname; + return img.load(fname); +} + +Color Texture2D::sample(float u, float v, float w) const +{ + return sample_image(img, u, v, wrapping); +} + +TextureCube::TextureCube() +{ + wrapping = WrapMode::clamp; +} + +Image *TextureCube::get_image(int idx) +{ + return &img[idx]; +} + +const Image *TextureCube::get_image(int idx) const +{ + return &img[idx]; +} + + +bool TextureCube::load(const char *fname) +{ + // assume it's a cubemap descriptor file (face path per line) + FILE *fp; + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to open the cubemap descriptor %s: %s\n", fname, strerror(errno)); + return false; + } + + name = fname; + + char *prefix = (char*)alloca(strlen(fname) + 1); + strcpy(prefix, fname); + char *ptr = strrchr(prefix, '/'); + if(!ptr) { + ptr = prefix; + } + *ptr = 0; + + int xsz = 0, ysz = 0; + + // load the faces + char buf[512]; + for(int i=0; i<6; i++) { + if(!fgets(buf, sizeof buf, fp)) { + fprintf(stderr, "invalid cubemap descriptor file: %s\n", fname); + return false; + } + if(buf[strlen(buf) - 1] == '\n') { + buf[strlen(buf) - 1] = 0; + } + + std::string path = std::string(prefix) + "/" + std::string(buf); + if(!img[i].load(path.c_str())) { + fprintf(stderr, "failed to load image: %s\n", path.c_str()); + fclose(fp); + return false; + } + + if(i == 0) { + xsz = img[i].xsz; + ysz = img[i].ysz; + } else { + if(img[i].xsz != xsz || img[i].ysz != ysz) { + fprintf(stderr, "cubemap %s face image %s size (%dx%d) doesn't match the previous faces (%dx%d)\n", + fname, path.c_str(), img[i].xsz, img[i].ysz, xsz, ysz); + fclose(fp); + return false; + } + } + } + return true; +} + +Color TextureCube::sample(float u, float v, float w) const +{ + int face; + Vector2 uv; + + float abs_u = fabs(u); + float abs_v = fabs(v); + float abs_w = fabs(w); + + if(abs_u > abs_v && abs_u > abs_w) { + if(u >= 0.0) { + face = 0; + uv.x = w / abs_u; + uv.y = v / abs_u; + } else { + face = 1; + uv.x = -w / abs_u; + uv.y = v / abs_u; + } + } else if(abs_v > abs_w) { + if(v >= 0.0) { + face = 2; + uv.x = u / abs_v; + uv.y = w / abs_v; + } else { + face = 3; + uv.x = u / abs_v; + uv.y = -w / abs_v; + } + } else { + if(w >= 0.0) { + face = 5; + uv.x = -u / abs_w; + uv.y = v / abs_w; + } else { + face = 4; + uv.x = u / abs_w; + uv.y = v / abs_w; + } + } + + return sample_image(img[face], uv.x * 0.5 + 0.5, uv.y * 0.5 + 0.5, wrapping); +} + +Color TextureCube::sample(const HitPoint &hit) const +{ + return sample(hit.normal.x, hit.normal.y, hit.normal.z); +} + +Texture *load_texture(const char *fname) +{ + if(access(fname, R_OK) == -1) { + fprintf(stderr, "failed to load texture %s: %s\n", fname, strerror(errno)); + return 0; + } + + Texture2D *tex2d = new Texture2D; + if(tex2d->load(fname)) { + return tex2d; + } + delete tex2d; + + TextureCube *texcube = new TextureCube; + if(texcube->load(fname)) { + return texcube; + } + delete texcube; + + return 0; +} + +#define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x))) + +static inline Color sample_image(const Image &img, float u, float v, Texture::WrapMode wrapping) +{ + int x = (int)round(u * img.xsz); + int y = (int)round((1.0 - v) * img.ysz); + + if(wrapping == Texture::WrapMode::clamp) { + x = CLAMP(x, 0, img.xsz - 1); + y = CLAMP(y, 0, img.ysz - 1); + } else { + x %= img.xsz; + y %= img.ysz; + + if(x < 0) + x += img.xsz; + if(y < 0) + y += img.ysz; + } + + return img.pixels[y * img.xsz + x]; +} diff -r 000000000000 -r f234630e38ff src/texture.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texture.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,76 @@ +#ifndef TEXTURE_H_ +#define TEXTURE_H_ + +#include +#include "image.h" + +struct HitPoint; + +class Texture { +public: + enum class SampleMode { nearest, linear }; + enum class WrapMode { clamp, repeat }; + +protected: + std::string name; + + SampleMode sampling; + WrapMode wrapping; + +public: + Texture(); + virtual ~Texture(); + + virtual void set_name(const char *name); + virtual const char *get_name() const; + + virtual Image *get_image(int idx = 0) = 0; + virtual const Image *get_image(int idx = 0) const = 0; + + virtual void set_sampling_mode(SampleMode mode); + virtual SampleMode get_sampling_mode() const; + virtual void set_wrapping_mode(WrapMode mode); + virtual WrapMode get_wrapping_mode() const; + + virtual bool load(const char *fname) = 0; + + virtual Color sample(float u, float v, float w) const = 0; + virtual Color sample(const HitPoint &hit) const; +}; + +class Texture2D : public Texture { +private: + Image img; + +public: + virtual Image *get_image(int idx = 0); + virtual const Image *get_image(int idx = 0) const; + + virtual bool load(const char *fname); + + virtual Color sample(float u, float v, float w) const; + + friend Texture *load_texture(const char *fname); +}; + +class TextureCube : public Texture { +private: + Image img[6]; + +public: + TextureCube(); + + virtual Image *get_image(int idx = 0); + virtual const Image *get_image(int idx = 0) const; + + virtual bool load(const char *fname); + + virtual Color sample(float u, float v, float w) const; + virtual Color sample(const HitPoint &hit) const; + + friend Texture *load_texture(const char *fname); +}; + +Texture *load_texture(const char *fname); + +#endif // TEXTURE_H_ diff -r 000000000000 -r f234630e38ff src/xform_node.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xform_node.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,372 @@ +#include +#include +#include "xform_node.h" +#include "anim/anim.h" +#include "anim/track.h" + +static inline anm_interpolator track_interpolator(Interp in); +static inline anm_extrapolator track_extrapolator(Extrap ex); + +XFormNode::XFormNode() +{ + anm = new anm_node; + anm_init_node(anm); +} + +XFormNode::~XFormNode() +{ + anm_destroy_node(anm); + delete anm; +} + +void XFormNode::set_name(const char *name) +{ + anm_set_node_name(anm, name); +} + +const char *XFormNode::get_name() const +{ + return anm_get_node_name(anm); +} + +void XFormNode::set_interpolator(Interp in) +{ + anm_set_interpolator(anm, track_interpolator(in)); + interp = in; +} + +Interp XFormNode::get_interpolator() const +{ + return interp; +} + +void XFormNode::set_extrapolator(Extrap ex) +{ + anm_set_extrapolator(anm, track_extrapolator(ex)); + extrap = ex; +} + +Extrap XFormNode::get_extrapolator() const +{ + return extrap; +} + +void XFormNode::add_child(XFormNode *child) +{ + children.push_back(child); + anm_link_node(anm, child->anm); +} + +void XFormNode::remove_child(XFormNode *child) +{ + std::vector::iterator it; + it = std::find(children.begin(), children.end(), child); + if(it != children.end()) { + children.erase(it); + anm_unlink_node(anm, child->anm); + } +} + +int XFormNode::get_children_count() const +{ + return (int)children.size(); +} + +XFormNode *XFormNode::get_child(int idx) +{ + if(idx >= 0 && idx < get_children_count()) { + return children[idx]; + } + return 0; +} + +const XFormNode *XFormNode::get_child(int idx) const +{ + if(idx >= 0 && idx < get_children_count()) { + return children[idx]; + } + return 0; +} + +void XFormNode::set_position(const Vector3 &pos, long tmsec) +{ + anm_set_position(anm, v3_cons(pos.x, pos.y, pos.z), ANM_MSEC2TM(tmsec)); +} + +Vector3 XFormNode::get_node_position(long tmsec) const +{ + vec3_t p = anm_get_node_position(anm, ANM_MSEC2TM(tmsec)); + return Vector3(p.x, p.y, p.z); +} + +void XFormNode::set_rotation(const Quaternion &quat, long tmsec) +{ + anm_set_rotation(anm, quat_cons(quat.s, quat.v.x, quat.v.y, quat.v.z), ANM_MSEC2TM(tmsec)); +} + +Quaternion XFormNode::get_node_rotation(long tmsec) const +{ + quat_t q = anm_get_node_rotation(anm, ANM_MSEC2TM(tmsec)); + return Quaternion(q.w, q.x, q.y, q.z); +} + +void XFormNode::set_scaling(const Vector3 &pos, long tmsec) +{ + anm_set_scaling(anm, v3_cons(pos.x, pos.y, pos.z), ANM_MSEC2TM(tmsec)); +} + +Vector3 XFormNode::get_node_scaling(long tmsec) const +{ + vec3_t s = anm_get_node_scaling(anm, ANM_MSEC2TM(tmsec)); + return Vector3(s.x, s.y, s.z); +} + +// these take hierarchy into account +Vector3 XFormNode::get_position(long tmsec) const +{ + vec3_t v = anm_get_position(anm, ANM_MSEC2TM(tmsec)); + return Vector3(v.x, v.y, v.z); +} + +Quaternion XFormNode::get_rotation(long tmsec) const +{ + quat_t q = anm_get_rotation(anm, tmsec); + return Quaternion(q.w, q.x, q.y, q.z); +} + +Vector3 XFormNode::get_scaling(long tmsec) const +{ + vec3_t v = anm_get_scaling(anm, ANM_MSEC2TM(tmsec)); + return Vector3(v.x, v.y, v.z); +} + +void XFormNode::set_pivot(const Vector3 &pivot) +{ + anm_set_pivot(anm, v3_cons(pivot.x, pivot.y, pivot.z)); +} + +Vector3 XFormNode::get_pivot() const +{ + vec3_t p = anm_get_pivot(anm); + return Vector3(p.x, p.y, p.z); +} + +void XFormNode::set_local_matrix(const Matrix4x4 &mat) +{ + local_matrix = mat; +} + +const Matrix4x4 &XFormNode::get_local_matrix() const +{ + return local_matrix; +} + +void XFormNode::set_bone_matrix(const Matrix4x4 &bmat) +{ + bone_matrix = bmat; +} + +const Matrix4x4 &XFormNode::get_bone_matrix() const +{ + return bone_matrix; +} + +#define FOO + +void XFormNode::get_node_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat) const +{ + anm_time_t tm = ANM_MSEC2TM(tmsec); + + if(mat) { + anm_get_node_matrix(anm, (scalar_t(*)[4])mat, tm); +#ifdef FOO + *mat = local_matrix * *mat; +#else + *mat = *mat * local_matrix; +#endif + } + if(inv_mat) { + anm_get_inv_matrix(anm, (scalar_t(*)[4])inv_mat, tm); + } +} + +void XFormNode::get_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat) const +{ + anm_time_t tm = ANM_MSEC2TM(tmsec); + + if(mat) { + anm_get_matrix(anm, (scalar_t(*)[4])mat, tm); +#ifdef FOO + *mat = local_matrix * *mat; +#else + *mat = *mat * local_matrix; +#endif + } + if(inv_mat) { + anm_get_inv_matrix(anm, (scalar_t(*)[4])inv_mat, tm); + } +} + + +// ---- Track ---- + +Track::Track() +{ + trk = new anm_track; + anm_init_track(trk); +} + +Track::~Track() +{ + anm_destroy_track(trk); + delete trk; +} + +Track::Track(const Track &rhs) +{ + trk = new anm_track; + anm_init_track(trk); + anm_copy_track(trk, rhs.trk); + interp = rhs.interp; + extrap = rhs.extrap; +} + +Track &Track::operator =(const Track &rhs) +{ + if(&rhs == this) { + return *this; + } + + anm_copy_track(trk, rhs.trk); + interp = rhs.interp; + extrap = rhs.extrap; + return *this; +} + + +void Track::set_interpolator(Interp in) +{ + anm_set_track_interpolator(trk, track_interpolator(in)); + interp = in; +} + +Interp Track::get_interpolator() const +{ + return interp; +} + +void Track::set_extrapolator(Extrap ex) +{ + anm_set_track_extrapolator(trk, track_extrapolator(ex)); + extrap = ex; +} + +Extrap Track::get_extrapolator() const +{ + return extrap; +} + +void Track::set_default(double def) +{ + anm_set_track_default(trk, def); +} + +void Track::set_value(float val, long tmsec) +{ + anm_set_value(trk, ANM_MSEC2TM(tmsec), val); +} + +float Track::get_value(long tmsec) const +{ + return anm_get_value(trk, ANM_MSEC2TM(tmsec)); +} + +float Track::operator ()(long tmsec) const +{ + return anm_get_value(trk, ANM_MSEC2TM(tmsec)); +} + + +// ---- Track3 ---- + +void Track3::set_interpolator(Interp in) +{ + for(int i=0; i<3; i++) { + track[i].set_interpolator(in); + } +} + +Interp Track3::get_interpolator() const +{ + return track[0].get_interpolator(); +} + +void Track3::set_extrapolator(Extrap ex) +{ + for(int i=0; i<3; i++) { + track[i].set_extrapolator(ex); + } +} + +Extrap Track3::get_extrapolator() const +{ + return track[0].get_extrapolator(); +} + +void Track3::set_default(const Vector3 &def) +{ + for(int i=0; i<3; i++) { + track[i].set_default(def[i]); + } +} + +void Track3::set_value(const Vector3 &val, long tmsec) +{ + for(int i=0; i<3; i++) { + track[i].set_value(val[i], tmsec); + } +} + +Vector3 Track3::get_value(long tmsec) const +{ + return Vector3(track[0](tmsec), track[1](tmsec), track[2](tmsec)); +} + +Vector3 Track3::operator ()(long tmsec) const +{ + return Vector3(track[0](tmsec), track[1](tmsec), track[2](tmsec)); +} + + +static inline anm_interpolator track_interpolator(Interp in) +{ + switch(in) { + case INTERP_STEP: + return ANM_INTERP_STEP; + case INTERP_LINEAR: + return ANM_INTERP_LINEAR; + case INTERP_CUBIC: + return ANM_INTERP_CUBIC; + } + + assert(0); + return ANM_INTERP_STEP; +} + +static inline anm_extrapolator track_extrapolator(Extrap ex) +{ + switch(ex) { + case EXTRAP_EXTEND: + return ANM_EXTRAP_EXTEND; + case EXTRAP_CLAMP: + return ANM_EXTRAP_CLAMP; + case EXTRAP_REPEAT: + return ANM_EXTRAP_REPEAT; + case EXTRAP_PINGPONG: + return ANM_EXTRAP_PINGPONG; + } + + assert(0); + return ANM_EXTRAP_EXTEND; +} + diff -r 000000000000 -r f234630e38ff src/xform_node.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xform_node.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,134 @@ +/* +TODO: add multiple animations per node in libanim (i.e. multiple sets of tracks) +*/ +#ifndef XFORM_NODE_H_ +#define XFORM_NODE_H_ + +#include +#include "vmath/vector.h" +#include "vmath/quat.h" +#include "vmath/matrix.h" + +enum Interp { INTERP_STEP, INTERP_LINEAR, INTERP_CUBIC }; +enum Extrap { EXTRAP_EXTEND, EXTRAP_CLAMP, EXTRAP_REPEAT, EXTRAP_PINGPONG }; + +struct anm_node; +struct anm_track; + +// XXX all time arguments are milliseconds + +class XFormNode { +private: + struct anm_node *anm; + std::vector children; + + Interp interp; + Extrap extrap; + + Matrix4x4 local_matrix; + Matrix4x4 bone_matrix; + + XFormNode(const XFormNode &node) {} + XFormNode &operator =(const XFormNode &node) { return *this; } + +public: + XFormNode(); + virtual ~XFormNode(); + + void set_name(const char *name); + const char *get_name() const; + + void set_interpolator(Interp in); + Interp get_interpolator() const; + void set_extrapolator(Extrap ex); + Extrap get_extrapolator() const; + + // children management + void add_child(XFormNode *child); + void remove_child(XFormNode *child); + + int get_children_count() const; + XFormNode *get_child(int idx); + const XFormNode *get_child(int idx) const; + + + void set_position(const Vector3 &pos, long tmsec = 0); + Vector3 get_node_position(long tmsec = 0) const; + + void set_rotation(const Quaternion &quat, long tmsec = 0); + Quaternion get_node_rotation(long tmsec = 0) const; + + void set_scaling(const Vector3 &pos, long tmsec = 0); + Vector3 get_node_scaling(long tmsec = 0) const; + + // these take hierarchy into account + Vector3 get_position(long tmsec = 0) const; + Quaternion get_rotation(long tmsec = 0) const; + Vector3 get_scaling(long tmsec = 0) const; + + void set_pivot(const Vector3 &pivot); + Vector3 get_pivot() const; + + // the local matrix is concatenated with the regular node/anim matrix + void set_local_matrix(const Matrix4x4 &mat); + const Matrix4x4 &get_local_matrix() const; + + // for bone nodes, the transformation of the bone in bind position + void set_bone_matrix(const Matrix4x4 &bmat); + const Matrix4x4 &get_bone_matrix() const; + + // node transformation alone + void get_node_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat = 0) const; + + // node transformation taking hierarchy into account + void get_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat = 0) const; +}; + + +class Track { +private: + struct anm_track *trk; + Interp interp; + Extrap extrap; + +public: + Track(); + ~Track(); + + Track(const Track &trk); + Track &operator =(const Track &trk); + + void set_interpolator(Interp in); + Interp get_interpolator() const; + void set_extrapolator(Extrap ex); + Extrap get_extrapolator() const; + + void set_default(double def); + + void set_value(float val, long tmsec = 0); + float get_value(long tmsec = 0) const; + + // the same as get_value + float operator ()(long tmsec = 0) const; +}; + +class Track3 { +private: + Track track[3]; + +public: + void set_interpolator(Interp in); + Interp get_interpolator() const; + void set_extrapolator(Extrap ex); + Extrap get_extrapolator() const; + + void set_default(const Vector3 &def); + + void set_value(const Vector3 &val, long tmsec = 0); + Vector3 get_value(long tmsec = 0) const; + + // the same as get_value + Vector3 operator ()(long tmsec = 0) const; +}; + +#endif /* XFORM_NODE_H_ */ diff -r 000000000000 -r f234630e38ff util/anim.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/anim.c Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,26 @@ +#include +#include + +#define NUM_SEG 96 +#define DIST 12 +#define NAME "green_sphere" +#define FULL_TIME 10.0 +#define BOUNCES 6.0 +#define HEIGHT 3.5 + +int main(void) +{ + int i; + for(i=0; i +#include +#include "matrix.h" +#include "vector.h" +#include "quat.h" + +using namespace std; + +// ----------- Matrix3x3 -------------- + +Matrix3x3 Matrix3x3::identity = Matrix3x3(1, 0, 0, 0, 1, 0, 0, 0, 1); + +Matrix3x3::Matrix3x3() +{ + *this = identity; +} + +Matrix3x3::Matrix3x3( scalar_t m11, scalar_t m12, scalar_t m13, + scalar_t m21, scalar_t m22, scalar_t m23, + scalar_t m31, scalar_t m32, scalar_t m33) +{ + m[0][0] = m11; m[0][1] = m12; m[0][2] = m13; + m[1][0] = m21; m[1][1] = m22; m[1][2] = m23; + m[2][0] = m31; m[2][1] = m32; m[2][2] = m33; +} + +Matrix3x3::Matrix3x3(const Vector3 &ivec, const Vector3 &jvec, const Vector3 &kvec) +{ + set_row_vector(ivec, 0); + set_row_vector(jvec, 1); + set_row_vector(kvec, 2); +} + +Matrix3x3::Matrix3x3(const mat3_t cmat) +{ + memcpy(m, cmat, sizeof(mat3_t)); +} + +Matrix3x3::Matrix3x3(const Matrix4x4 &mat4x4) +{ + for(int i=0; i<3; i++) { + for(int j=0; j<3; j++) { + m[i][j] = mat4x4[i][j]; + } + } +} + +Matrix3x3 operator +(const Matrix3x3 &m1, const Matrix3x3 &m2) +{ + Matrix3x3 res; + const scalar_t *op1 = m1.m[0], *op2 = m2.m[0]; + scalar_t *dest = res.m[0]; + + for(int i=0; i<9; i++) { + *dest++ = *op1++ + *op2++; + } + return res; +} + +Matrix3x3 operator -(const Matrix3x3 &m1, const Matrix3x3 &m2) +{ + Matrix3x3 res; + const scalar_t *op1 = m1.m[0], *op2 = m2.m[0]; + scalar_t *dest = res.m[0]; + + for(int i=0; i<9; i++) { + *dest++ = *op1++ - *op2++; + } + return res; +} + +Matrix3x3 operator *(const Matrix3x3 &m1, const Matrix3x3 &m2) +{ + Matrix3x3 res; + for(int i=0; i<3; i++) { + for(int j=0; j<3; j++) { + res.m[i][j] = m1.m[i][0] * m2.m[0][j] + m1.m[i][1] * m2.m[1][j] + m1.m[i][2] * m2.m[2][j]; + } + } + return res; +} + +void operator +=(Matrix3x3 &m1, const Matrix3x3 &m2) +{ + scalar_t *op1 = m1.m[0]; + const scalar_t *op2 = m2.m[0]; + + for(int i=0; i<9; i++) { + *op1++ += *op2++; + } +} + +void operator -=(Matrix3x3 &m1, const Matrix3x3 &m2) +{ + scalar_t *op1 = m1.m[0]; + const scalar_t *op2 = m2.m[0]; + + for(int i=0; i<9; i++) { + *op1++ -= *op2++; + } +} + +void operator *=(Matrix3x3 &m1, const Matrix3x3 &m2) +{ + Matrix3x3 res; + for(int i=0; i<3; i++) { + for(int j=0; j<3; j++) { + res.m[i][j] = m1.m[i][0] * m2.m[0][j] + m1.m[i][1] * m2.m[1][j] + m1.m[i][2] * m2.m[2][j]; + } + } + memcpy(m1.m, res.m, 9 * sizeof(scalar_t)); +} + +Matrix3x3 operator *(const Matrix3x3 &mat, scalar_t scalar) +{ + Matrix3x3 res; + const scalar_t *mptr = mat.m[0]; + scalar_t *dptr = res.m[0]; + + for(int i=0; i<9; i++) { + *dptr++ = *mptr++ * scalar; + } + return res; +} + +Matrix3x3 operator *(scalar_t scalar, const Matrix3x3 &mat) +{ + Matrix3x3 res; + const scalar_t *mptr = mat.m[0]; + scalar_t *dptr = res.m[0]; + + for(int i=0; i<9; i++) { + *dptr++ = *mptr++ * scalar; + } + return res; +} + +void operator *=(Matrix3x3 &mat, scalar_t scalar) +{ + scalar_t *mptr = mat.m[0]; + + for(int i=0; i<9; i++) { + *mptr++ *= scalar; + } +} + +void Matrix3x3::translate(const Vector2 &trans) +{ + Matrix3x3 tmat(1, 0, trans.x, 0, 1, trans.y, 0, 0, 1); + *this *= tmat; +} + +void Matrix3x3::set_translation(const Vector2 &trans) +{ + *this = Matrix3x3(1, 0, trans.x, 0, 1, trans.y, 0, 0, 1); +} + +void Matrix3x3::rotate(scalar_t angle) +{ + scalar_t cos_a = cos(angle); + scalar_t sin_a = sin(angle); + Matrix3x3 rmat( cos_a, -sin_a, 0, + sin_a, cos_a, 0, + 0, 0, 1); + *this *= rmat; +} + +void Matrix3x3::set_rotation(scalar_t angle) +{ + scalar_t cos_a = cos(angle); + scalar_t sin_a = sin(angle); + *this = Matrix3x3(cos_a, -sin_a, 0, sin_a, cos_a, 0, 0, 0, 1); +} + +void Matrix3x3::rotate(const Vector3 &euler_angles) +{ + Matrix3x3 xrot, yrot, zrot; + + xrot = Matrix3x3( 1, 0, 0, + 0, cos(euler_angles.x), -sin(euler_angles.x), + 0, sin(euler_angles.x), cos(euler_angles.x)); + + yrot = Matrix3x3( cos(euler_angles.y), 0, sin(euler_angles.y), + 0, 1, 0, + -sin(euler_angles.y), 0, cos(euler_angles.y)); + + zrot = Matrix3x3( cos(euler_angles.z), -sin(euler_angles.z), 0, + sin(euler_angles.z), cos(euler_angles.z), 0, + 0, 0, 1); + + *this *= xrot * yrot * zrot; +} + +void Matrix3x3::set_rotation(const Vector3 &euler_angles) +{ + Matrix3x3 xrot, yrot, zrot; + + xrot = Matrix3x3( 1, 0, 0, + 0, cos(euler_angles.x), -sin(euler_angles.x), + 0, sin(euler_angles.x), cos(euler_angles.x)); + + yrot = Matrix3x3( cos(euler_angles.y), 0, sin(euler_angles.y), + 0, 1, 0, + -sin(euler_angles.y), 0, cos(euler_angles.y)); + + zrot = Matrix3x3( cos(euler_angles.z), -sin(euler_angles.z), 0, + sin(euler_angles.z), cos(euler_angles.z), 0, + 0, 0, 1); + + *this = xrot * yrot * zrot; +} + +void Matrix3x3::rotate(const Vector3 &axis, scalar_t angle) +{ + scalar_t sina = (scalar_t)sin(angle); + scalar_t cosa = (scalar_t)cos(angle); + scalar_t invcosa = 1-cosa; + scalar_t nxsq = axis.x * axis.x; + scalar_t nysq = axis.y * axis.y; + scalar_t nzsq = axis.z * axis.z; + + Matrix3x3 xform; + xform.m[0][0] = nxsq + (1-nxsq) * cosa; + xform.m[0][1] = axis.x * axis.y * invcosa - axis.z * sina; + xform.m[0][2] = axis.x * axis.z * invcosa + axis.y * sina; + + xform.m[1][0] = axis.x * axis.y * invcosa + axis.z * sina; + xform.m[1][1] = nysq + (1-nysq) * cosa; + xform.m[1][2] = axis.y * axis.z * invcosa - axis.x * sina; + + xform.m[2][0] = axis.x * axis.z * invcosa - axis.y * sina; + xform.m[2][1] = axis.y * axis.z * invcosa + axis.x * sina; + xform.m[2][2] = nzsq + (1-nzsq) * cosa; + + *this *= xform; +} + +void Matrix3x3::set_rotation(const Vector3 &axis, scalar_t angle) +{ + scalar_t sina = (scalar_t)sin(angle); + scalar_t cosa = (scalar_t)cos(angle); + scalar_t invcosa = 1-cosa; + scalar_t nxsq = axis.x * axis.x; + scalar_t nysq = axis.y * axis.y; + scalar_t nzsq = axis.z * axis.z; + + reset_identity(); + m[0][0] = nxsq + (1-nxsq) * cosa; + m[0][1] = axis.x * axis.y * invcosa - axis.z * sina; + m[0][2] = axis.x * axis.z * invcosa + axis.y * sina; + m[1][0] = axis.x * axis.y * invcosa + axis.z * sina; + m[1][1] = nysq + (1-nysq) * cosa; + m[1][2] = axis.y * axis.z * invcosa - axis.x * sina; + m[2][0] = axis.x * axis.z * invcosa - axis.y * sina; + m[2][1] = axis.y * axis.z * invcosa + axis.x * sina; + m[2][2] = nzsq + (1-nzsq) * cosa; +} + +// 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 +Quaternion Matrix3x3::get_rotation_quat() const +{ + static const int next[3] = {1, 2, 0}; + + float quat[4]; + + scalar_t trace = m[0][0] + m[1][1] + m[2][2]; + scalar_t root; + + if(trace > 0.0f) { + // |w| > 1/2 + root = sqrt(trace + 1.0f); // 2w + quat[0] = 0.5f * root; + root = 0.5f / root; // 1 / 4w + quat[1] = (m[2][1] - m[1][2]) * root; + quat[2] = (m[0][2] - m[2][0]) * root; + quat[3] = (m[1][0] - m[0][1]) * root; + } else { + // |w| <= 1/2 + int i = 0; + if(m[1][1] > m[0][0]) { + i = 1; + } + if(m[2][2] > m[i][i]) { + i = 2; + } + int j = next[i]; + int k = next[j]; + + root = sqrt(m[i][i] - m[j][j] - m[k][k] + 1.0f); + quat[i + 1] = 0.5f * root; + root = 0.5f / root; + quat[0] = (m[k][j] - m[j][k]) * root; + quat[j + 1] = (m[j][i] - m[i][j]) * root; + quat[k + 1] = (m[k][i] - m[i][k]) * root; + } + return Quaternion(quat[0], quat[1], quat[2], quat[3]); +} + +void Matrix3x3::scale(const Vector3 &scale_vec) +{ + Matrix3x3 smat( scale_vec.x, 0, 0, + 0, scale_vec.y, 0, + 0, 0, scale_vec.z); + *this *= smat; +} + +void Matrix3x3::set_scaling(const Vector3 &scale_vec) +{ + *this = Matrix3x3( scale_vec.x, 0, 0, + 0, scale_vec.y, 0, + 0, 0, scale_vec.z); +} + +void Matrix3x3::set_column_vector(const Vector3 &vec, unsigned int col_index) +{ + m[0][col_index] = vec.x; + m[1][col_index] = vec.y; + m[2][col_index] = vec.z; +} + +void Matrix3x3::set_row_vector(const Vector3 &vec, unsigned int row_index) +{ + m[row_index][0] = vec.x; + m[row_index][1] = vec.y; + m[row_index][2] = vec.z; +} + +Vector3 Matrix3x3::get_column_vector(unsigned int col_index) const +{ + return Vector3(m[0][col_index], m[1][col_index], m[2][col_index]); +} + +Vector3 Matrix3x3::get_row_vector(unsigned int row_index) const +{ + return Vector3(m[row_index][0], m[row_index][1], m[row_index][2]); +} + +void Matrix3x3::transpose() +{ + Matrix3x3 tmp = *this; + for(int i=0; i<3; i++) { + for(int j=0; j<3; j++) { + m[i][j] = tmp[j][i]; + } + } +} + +Matrix3x3 Matrix3x3::transposed() const +{ + Matrix3x3 res; + for(int i=0; i<3; i++) { + for(int j=0; j<3; j++) { + res[i][j] = m[j][i]; + } + } + return res; +} + +scalar_t Matrix3x3::determinant() const +{ + return m[0][0] * (m[1][1]*m[2][2] - m[1][2]*m[2][1]) - + m[0][1] * (m[1][0]*m[2][2] - m[1][2]*m[2][0]) + + m[0][2] * (m[1][0]*m[2][1] - m[1][1]*m[2][0]); +} + +Matrix3x3 Matrix3x3::inverse() const +{ + // TODO: implement 3x3 inverse + return *this; +} + +ostream &operator <<(ostream &out, const Matrix3x3 &mat) +{ + for(int i=0; i<3; i++) { + char str[100]; + sprintf(str, "[ %12.5f %12.5f %12.5f ]\n", (float)mat.m[i][0], (float)mat.m[i][1], (float)mat.m[i][2]); + out << str; + } + return out; +} + + + +/* ----------------- Matrix4x4 implementation --------------- */ + +Matrix4x4 Matrix4x4::identity = Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + +Matrix4x4::Matrix4x4() +{ + *this = identity; +} + +Matrix4x4::Matrix4x4( scalar_t m11, scalar_t m12, scalar_t m13, scalar_t m14, + scalar_t m21, scalar_t m22, scalar_t m23, scalar_t m24, + scalar_t m31, scalar_t m32, scalar_t m33, scalar_t m34, + scalar_t m41, scalar_t m42, scalar_t m43, scalar_t m44) +{ + m[0][0] = m11; m[0][1] = m12; m[0][2] = m13; m[0][3] = m14; + m[1][0] = m21; m[1][1] = m22; m[1][2] = m23; m[1][3] = m24; + m[2][0] = m31; m[2][1] = m32; m[2][2] = m33; m[2][3] = m34; + m[3][0] = m41; m[3][1] = m42; m[3][2] = m43; m[3][3] = m44; +} + +Matrix4x4::Matrix4x4(const mat4_t cmat) +{ + memcpy(m, cmat, sizeof(mat4_t)); +} + +Matrix4x4::Matrix4x4(const Matrix3x3 &mat3x3) +{ + reset_identity(); + for(int i=0; i<3; i++) { + for(int j=0; j<3; j++) { + m[i][j] = mat3x3[i][j]; + } + } +} + +Matrix4x4 operator +(const Matrix4x4 &m1, const Matrix4x4 &m2) +{ + Matrix4x4 res; + const scalar_t *op1 = m1.m[0], *op2 = m2.m[0]; + scalar_t *dest = res.m[0]; + + for(int i=0; i<16; i++) { + *dest++ = *op1++ + *op2++; + } + return res; +} + +Matrix4x4 operator -(const Matrix4x4 &m1, const Matrix4x4 &m2) +{ + Matrix4x4 res; + const scalar_t *op1 = m1.m[0], *op2 = m2.m[0]; + scalar_t *dest = res.m[0]; + + for(int i=0; i<16; i++) { + *dest++ = *op1++ - *op2++; + } + return res; +} + +void operator +=(Matrix4x4 &m1, const Matrix4x4 &m2) +{ + scalar_t *op1 = m1.m[0]; + const scalar_t *op2 = m2.m[0]; + + for(int i=0; i<16; i++) { + *op1++ += *op2++; + } +} + +void operator -=(Matrix4x4 &m1, const Matrix4x4 &m2) +{ + scalar_t *op1 = m1.m[0]; + const scalar_t *op2 = m2.m[0]; + + for(int i=0; i<16; i++) { + *op1++ -= *op2++; + } +} + +Matrix4x4 operator *(const Matrix4x4 &mat, scalar_t scalar) +{ + Matrix4x4 res; + const scalar_t *mptr = mat.m[0]; + scalar_t *dptr = res.m[0]; + + for(int i=0; i<16; i++) { + *dptr++ = *mptr++ * scalar; + } + return res; +} + +Matrix4x4 operator *(scalar_t scalar, const Matrix4x4 &mat) +{ + Matrix4x4 res; + const scalar_t *mptr = mat.m[0]; + scalar_t *dptr = res.m[0]; + + for(int i=0; i<16; i++) { + *dptr++ = *mptr++ * scalar; + } + return res; +} + +void operator *=(Matrix4x4 &mat, scalar_t scalar) +{ + scalar_t *mptr = mat.m[0]; + + for(int i=0; i<16; i++) { + *mptr++ *= scalar; + } +} + +void Matrix4x4::translate(const Vector3 &trans) +{ + Matrix4x4 tmat(1, 0, 0, trans.x, 0, 1, 0, trans.y, 0, 0, 1, trans.z, 0, 0, 0, 1); + *this *= tmat; +} + +void Matrix4x4::set_translation(const Vector3 &trans) +{ + *this = Matrix4x4(1, 0, 0, trans.x, 0, 1, 0, trans.y, 0, 0, 1, trans.z, 0, 0, 0, 1); +} + +Vector3 Matrix4x4::get_translation() const +{ + return Vector3(m[0][3], m[1][3], m[2][3]); +} + +void Matrix4x4::rotate(const Vector3 &euler_angles) +{ + Matrix3x3 xrot, yrot, zrot; + + xrot = Matrix3x3( 1, 0, 0, + 0, cos(euler_angles.x), -sin(euler_angles.x), + 0, sin(euler_angles.x), cos(euler_angles.x)); + + yrot = Matrix3x3( cos(euler_angles.y), 0, sin(euler_angles.y), + 0, 1, 0, + -sin(euler_angles.y), 0, cos(euler_angles.y)); + + zrot = Matrix3x3( cos(euler_angles.z), -sin(euler_angles.z), 0, + sin(euler_angles.z), cos(euler_angles.z), 0, + 0, 0, 1); + + *this *= Matrix4x4(xrot * yrot * zrot); +} + +void Matrix4x4::set_rotation(const Vector3 &euler_angles) +{ + Matrix3x3 xrot, yrot, zrot; + + xrot = Matrix3x3( 1, 0, 0, + 0, cos(euler_angles.x), -sin(euler_angles.x), + 0, sin(euler_angles.x), cos(euler_angles.x)); + + yrot = Matrix3x3( cos(euler_angles.y), 0, sin(euler_angles.y), + 0, 1, 0, + -sin(euler_angles.y), 0, cos(euler_angles.y)); + + zrot = Matrix3x3( cos(euler_angles.z), -sin(euler_angles.z), 0, + sin(euler_angles.z), cos(euler_angles.z), 0, + 0, 0, 1); + + *this = Matrix4x4(xrot * yrot * zrot); +} + +void Matrix4x4::rotate(const Vector3 &axis, scalar_t angle) +{ + scalar_t sina = (scalar_t)sin(angle); + scalar_t cosa = (scalar_t)cos(angle); + scalar_t invcosa = 1-cosa; + scalar_t nxsq = axis.x * axis.x; + scalar_t nysq = axis.y * axis.y; + scalar_t nzsq = axis.z * axis.z; + + Matrix4x4 xform; + xform[0][0] = nxsq + (1-nxsq) * cosa; + xform[0][1] = axis.x * axis.y * invcosa - axis.z * sina; + xform[0][2] = axis.x * axis.z * invcosa + axis.y * sina; + xform[1][0] = axis.x * axis.y * invcosa + axis.z * sina; + xform[1][1] = nysq + (1-nysq) * cosa; + xform[1][2] = axis.y * axis.z * invcosa - axis.x * sina; + xform[2][0] = axis.x * axis.z * invcosa - axis.y * sina; + xform[2][1] = axis.y * axis.z * invcosa + axis.x * sina; + xform[2][2] = nzsq + (1-nzsq) * cosa; + + *this *= xform; +} + +void Matrix4x4::set_rotation(const Vector3 &axis, scalar_t angle) +{ + scalar_t sina = (scalar_t)sin(angle); + scalar_t cosa = (scalar_t)cos(angle); + scalar_t invcosa = 1-cosa; + scalar_t nxsq = axis.x * axis.x; + scalar_t nysq = axis.y * axis.y; + scalar_t nzsq = axis.z * axis.z; + + reset_identity(); + m[0][0] = nxsq + (1-nxsq) * cosa; + m[0][1] = axis.x * axis.y * invcosa - axis.z * sina; + m[0][2] = axis.x * axis.z * invcosa + axis.y * sina; + m[1][0] = axis.x * axis.y * invcosa + axis.z * sina; + m[1][1] = nysq + (1-nysq) * cosa; + m[1][2] = axis.y * axis.z * invcosa - axis.x * sina; + m[2][0] = axis.x * axis.z * invcosa - axis.y * sina; + m[2][1] = axis.y * axis.z * invcosa + axis.x * sina; + m[2][2] = nzsq + (1-nzsq) * cosa; +} + +void Matrix4x4::rotate(const Quaternion &quat) +{ + *this *= quat.get_rotation_matrix(); +} + +void Matrix4x4::set_rotation(const Quaternion &quat) +{ + *this = quat.get_rotation_matrix(); +} + +Quaternion Matrix4x4::get_rotation_quat() const +{ + Matrix3x3 mat3 = *this; + return mat3.get_rotation_quat(); +} + +void Matrix4x4::scale(const Vector4 &scale_vec) +{ + Matrix4x4 smat( scale_vec.x, 0, 0, 0, + 0, scale_vec.y, 0, 0, + 0, 0, scale_vec.z, 0, + 0, 0, 0, scale_vec.w); + *this *= smat; +} + +void Matrix4x4::set_scaling(const Vector4 &scale_vec) +{ + *this = Matrix4x4( scale_vec.x, 0, 0, 0, + 0, scale_vec.y, 0, 0, + 0, 0, scale_vec.z, 0, + 0, 0, 0, scale_vec.w); +} + +Vector3 Matrix4x4::get_scaling() const +{ + Vector3 vi = get_row_vector(0); + Vector3 vj = get_row_vector(1); + Vector3 vk = get_row_vector(2); + + return Vector3(vi.length(), vj.length(), vk.length()); +} + +void Matrix4x4::set_column_vector(const Vector4 &vec, unsigned int col_index) +{ + m[0][col_index] = vec.x; + m[1][col_index] = vec.y; + m[2][col_index] = vec.z; + m[3][col_index] = vec.w; +} + +void Matrix4x4::set_row_vector(const Vector4 &vec, unsigned int row_index) +{ + m[row_index][0] = vec.x; + m[row_index][1] = vec.y; + m[row_index][2] = vec.z; + m[row_index][3] = vec.w; +} + +Vector4 Matrix4x4::get_column_vector(unsigned int col_index) const +{ + return Vector4(m[0][col_index], m[1][col_index], m[2][col_index], m[3][col_index]); +} + +Vector4 Matrix4x4::get_row_vector(unsigned int row_index) const +{ + return Vector4(m[row_index][0], m[row_index][1], m[row_index][2], m[row_index][3]); +} + +void Matrix4x4::transpose() +{ + Matrix4x4 tmp = *this; + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + m[i][j] = tmp[j][i]; + } + } +} + +Matrix4x4 Matrix4x4::transposed() const +{ + Matrix4x4 res; + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + res[i][j] = m[j][i]; + } + } + return res; +} + +scalar_t Matrix4x4::determinant() const +{ + scalar_t det11 = (m[1][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[1][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) + + (m[1][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])); + + scalar_t det12 = (m[1][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[1][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[1][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])); + + scalar_t det13 = (m[1][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) - + (m[1][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[1][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + + scalar_t det14 = (m[1][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) - + (m[1][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) + + (m[1][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + + return m[0][0] * det11 - m[0][1] * det12 + m[0][2] * det13 - m[0][3] * det14; +} + + +Matrix4x4 Matrix4x4::adjoint() const +{ + Matrix4x4 coef; + + coef.m[0][0] = (m[1][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[1][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) + + (m[1][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])); + coef.m[0][1] = (m[1][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[1][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[1][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])); + coef.m[0][2] = (m[1][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) - + (m[1][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[1][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + coef.m[0][3] = (m[1][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) - + (m[1][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) + + (m[1][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + + coef.m[1][0] = (m[0][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[0][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) + + (m[0][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])); + coef.m[1][1] = (m[0][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[0][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[0][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])); + coef.m[1][2] = (m[0][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) - + (m[0][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[0][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + coef.m[1][3] = (m[0][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) - + (m[0][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) + + (m[0][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + + coef.m[2][0] = (m[0][1] * (m[1][2] * m[3][3] - m[3][2] * m[1][3])) - + (m[0][2] * (m[1][1] * m[3][3] - m[3][1] * m[1][3])) + + (m[0][3] * (m[1][1] * m[3][2] - m[3][1] * m[1][2])); + coef.m[2][1] = (m[0][0] * (m[1][2] * m[3][3] - m[3][2] * m[1][3])) - + (m[0][2] * (m[1][0] * m[3][3] - m[3][0] * m[1][3])) + + (m[0][3] * (m[1][0] * m[3][2] - m[3][0] * m[1][2])); + coef.m[2][2] = (m[0][0] * (m[1][1] * m[3][3] - m[3][1] * m[1][3])) - + (m[0][1] * (m[1][0] * m[3][3] - m[3][0] * m[1][3])) + + (m[0][3] * (m[1][0] * m[3][1] - m[3][0] * m[1][1])); + coef.m[2][3] = (m[0][0] * (m[1][1] * m[3][2] - m[3][1] * m[1][2])) - + (m[0][1] * (m[1][0] * m[3][2] - m[3][0] * m[1][2])) + + (m[0][2] * (m[1][0] * m[3][1] - m[3][0] * m[1][1])); + + coef.m[3][0] = (m[0][1] * (m[1][2] * m[2][3] - m[2][2] * m[1][3])) - + (m[0][2] * (m[1][1] * m[2][3] - m[2][1] * m[1][3])) + + (m[0][3] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])); + coef.m[3][1] = (m[0][0] * (m[1][2] * m[2][3] - m[2][2] * m[1][3])) - + (m[0][2] * (m[1][0] * m[2][3] - m[2][0] * m[1][3])) + + (m[0][3] * (m[1][0] * m[2][2] - m[2][0] * m[1][2])); + coef.m[3][2] = (m[0][0] * (m[1][1] * m[2][3] - m[2][1] * m[1][3])) - + (m[0][1] * (m[1][0] * m[2][3] - m[2][0] * m[1][3])) + + (m[0][3] * (m[1][0] * m[2][1] - m[2][0] * m[1][1])); + coef.m[3][3] = (m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])) - + (m[0][1] * (m[1][0] * m[2][2] - m[2][0] * m[1][2])) + + (m[0][2] * (m[1][0] * m[2][1] - m[2][0] * m[1][1])); + + coef.transpose(); + + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + coef.m[i][j] = j%2 ? -coef.m[i][j] : coef.m[i][j]; + if(i%2) coef.m[i][j] = -coef.m[i][j]; + } + } + + return coef; +} + +Matrix4x4 Matrix4x4::inverse() const +{ + Matrix4x4 adj = adjoint(); + + return adj * (1.0f / determinant()); +} + +void Matrix4x4::keep_upper_left() +{ + m[3][0] = m[3][1] = m[3][2] = m[0][3] = m[1][3] = m[2][3] = 0.0f; + m[3][3] = 1.0f; +} + +void Matrix4x4::clear_upper_left() +{ + m[0][0] = m[1][1] = m[2][2] = 1.0f; + m[0][1] = m[0][2] = m[1][2] = m[1][0] = m[2][0] = m[2][1] = 0.0f; +} + +ostream &operator <<(ostream &out, const Matrix4x4 &mat) +{ + for(int i=0; i<4; i++) { + char str[100]; + sprintf(str, "[ %12.5f %12.5f %12.5f %12.5f ]\n", (float)mat.m[i][0], (float)mat.m[i][1], (float)mat.m[i][2], (float)mat.m[i][3]); + out << str; + } + return out; +} diff -r 000000000000 -r f234630e38ff vmath/matrix.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/matrix.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,260 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 VMATH_MATRIX_H_ +#define VMATH_MATRIX_H_ + +#include +#include "vmath_types.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* C matrix 3x3 functions */ +static inline void m3_identity(mat3_t m); +static inline void m3_cons(mat3_t m, + scalar_t m11, scalar_t m12, scalar_t m13, + scalar_t m21, scalar_t m22, scalar_t m23, + scalar_t m31, scalar_t m32, scalar_t m33); +static inline void m3_copy(mat3_t dest, mat3_t src); +void m3_to_m4(mat4_t dest, mat3_t src); + +void m3_print(FILE *fp, mat3_t m); + +/* C matrix 4x4 functions */ +static inline void m4_identity(mat4_t m); +static inline void m4_cons(mat4_t m, + scalar_t m11, scalar_t m12, scalar_t m13, scalar_t m14, + scalar_t m21, scalar_t m22, scalar_t m23, scalar_t m24, + scalar_t m31, scalar_t m32, scalar_t m33, scalar_t m34, + scalar_t m41, scalar_t m42, scalar_t m43, scalar_t m44); +static inline void m4_copy(mat4_t dest, mat4_t src); +void m4_to_m3(mat3_t dest, mat4_t src); + +static inline void m4_mult(mat4_t res, mat4_t m1, mat4_t m2); + +void m4_set_translation(mat4_t m, scalar_t x, scalar_t y, scalar_t z); +void m4_translate(mat4_t m, scalar_t x, scalar_t y, scalar_t z); + +void m4_rotate(mat4_t m, scalar_t x, scalar_t y, scalar_t z); + +void m4_set_rotation_x(mat4_t m, scalar_t angle); +void m4_rotate_x(mat4_t m, scalar_t angle); +void m4_set_rotation_y(mat4_t m, scalar_t angle); +void m4_rotate_y(mat4_t m, scalar_t angle); +void m4_set_rotation_z(mat4_t m, scalar_t angle); +void m4_rotate_z(mat4_t m, scalar_t angle); +/* axis-angle rotation */ +void m4_set_rotation_axis(mat4_t m, scalar_t angle, scalar_t x, scalar_t y, scalar_t z); +void m4_rotate_axis(mat4_t m, scalar_t angle, scalar_t x, scalar_t y, scalar_t z); +/* concatentate a rotation quaternion */ +void m4_rotate_quat(mat4_t m, quat_t q); + +void m4_set_scaling(mat4_t m, scalar_t x, scalar_t y, scalar_t z); +void m4_scale(mat4_t m, scalar_t x, scalar_t y, scalar_t z); + +static inline void m4_set_column(mat4_t m, vec4_t v, int idx); +static inline void m4_set_row(mat4_t m, vec4_t v, int idx); + +void m4_transpose(mat4_t res, mat4_t m); +scalar_t m4_determinant(mat4_t m); +void m4_adjoint(mat4_t res, mat4_t m); +void m4_inverse(mat4_t res, mat4_t m); + +void m4_print(FILE *fp, mat4_t m); + +#ifdef __cplusplus +} + +/* when included from C++ source files, also define the matrix classes */ +#include + +/** 3x3 matrix */ +class Matrix3x3 { +public: + scalar_t m[3][3]; + + static Matrix3x3 identity; + + Matrix3x3(); + Matrix3x3( scalar_t m11, scalar_t m12, scalar_t m13, + scalar_t m21, scalar_t m22, scalar_t m23, + scalar_t m31, scalar_t m32, scalar_t m33); + Matrix3x3(const Vector3 &ivec, const Vector3 &jvec, const Vector3 &kvec); + Matrix3x3(const mat3_t cmat); + + Matrix3x3(const Matrix4x4 &mat4x4); + + /* binary operations matrix (op) matrix */ + friend Matrix3x3 operator +(const Matrix3x3 &m1, const Matrix3x3 &m2); + friend Matrix3x3 operator -(const Matrix3x3 &m1, const Matrix3x3 &m2); + friend Matrix3x3 operator *(const Matrix3x3 &m1, const Matrix3x3 &m2); + + friend void operator +=(Matrix3x3 &m1, const Matrix3x3 &m2); + friend void operator -=(Matrix3x3 &m1, const Matrix3x3 &m2); + friend void operator *=(Matrix3x3 &m1, const Matrix3x3 &m2); + + /* binary operations matrix (op) scalar and scalar (op) matrix */ + friend Matrix3x3 operator *(const Matrix3x3 &mat, scalar_t scalar); + friend Matrix3x3 operator *(scalar_t scalar, const Matrix3x3 &mat); + + friend void operator *=(Matrix3x3 &mat, scalar_t scalar); + + inline scalar_t *operator [](int index); + inline const scalar_t *operator [](int index) const; + + inline void reset_identity(); + + void translate(const Vector2 &trans); + void set_translation(const Vector2 &trans); + + void rotate(scalar_t angle); /* 2d rotation */ + void rotate(const Vector3 &euler_angles); /* 3d rotation with euler angles */ + void rotate(const Vector3 &axis, scalar_t angle); /* 3d axis/angle rotation */ + void set_rotation(scalar_t angle); + void set_rotation(const Vector3 &euler_angles); + void set_rotation(const Vector3 &axis, scalar_t angle); + Quaternion get_rotation_quat() const; + + void scale(const Vector3 &scale_vec); + void set_scaling(const Vector3 &scale_vec); + + void set_column_vector(const Vector3 &vec, unsigned int col_index); + void set_row_vector(const Vector3 &vec, unsigned int row_index); + Vector3 get_column_vector(unsigned int col_index) const; + Vector3 get_row_vector(unsigned int row_index) const; + + void transpose(); + Matrix3x3 transposed() const; + scalar_t determinant() const; + Matrix3x3 inverse() const; + + friend std::ostream &operator <<(std::ostream &out, const Matrix3x3 &mat); +}; + +/* binary operations matrix (op) matrix */ +Matrix3x3 operator +(const Matrix3x3 &m1, const Matrix3x3 &m2); +Matrix3x3 operator -(const Matrix3x3 &m1, const Matrix3x3 &m2); +Matrix3x3 operator *(const Matrix3x3 &m1, const Matrix3x3 &m2); + +void operator +=(Matrix3x3 &m1, const Matrix3x3 &m2); +void operator -=(Matrix3x3 &m1, const Matrix3x3 &m2); +void operator *=(Matrix3x3 &m1, const Matrix3x3 &m2); + +/* binary operations matrix (op) scalar and scalar (op) matrix */ +Matrix3x3 operator *(const Matrix3x3 &mat, scalar_t scalar); +Matrix3x3 operator *(scalar_t scalar, const Matrix3x3 &mat); + +void operator *=(Matrix3x3 &mat, scalar_t scalar); + +std::ostream &operator <<(std::ostream &out, const Matrix3x3 &mat); + + + +/** 4x4 matrix */ +class Matrix4x4 { +public: + scalar_t m[4][4]; + + static Matrix4x4 identity; + + Matrix4x4(); + Matrix4x4( scalar_t m11, scalar_t m12, scalar_t m13, scalar_t m14, + scalar_t m21, scalar_t m22, scalar_t m23, scalar_t m24, + scalar_t m31, scalar_t m32, scalar_t m33, scalar_t m34, + scalar_t m41, scalar_t m42, scalar_t m43, scalar_t m44); + Matrix4x4(const mat4_t cmat); + + Matrix4x4(const Matrix3x3 &mat3x3); + + /* binary operations matrix (op) matrix */ + friend Matrix4x4 operator +(const Matrix4x4 &m1, const Matrix4x4 &m2); + friend Matrix4x4 operator -(const Matrix4x4 &m1, const Matrix4x4 &m2); + friend Matrix4x4 operator *(const Matrix4x4 &m1, const Matrix4x4 &m2); + + friend void operator +=(Matrix4x4 &m1, const Matrix4x4 &m2); + friend void operator -=(Matrix4x4 &m1, const Matrix4x4 &m2); + friend inline void operator *=(Matrix4x4 &m1, const Matrix4x4 &m2); + + /* binary operations matrix (op) scalar and scalar (op) matrix */ + friend Matrix4x4 operator *(const Matrix4x4 &mat, scalar_t scalar); + friend Matrix4x4 operator *(scalar_t scalar, const Matrix4x4 &mat); + + friend void operator *=(Matrix4x4 &mat, scalar_t scalar); + + inline scalar_t *operator [](int index); + inline const scalar_t *operator [](int index) const; + + inline void reset_identity(); + + void translate(const Vector3 &trans); + void set_translation(const Vector3 &trans); + Vector3 get_translation() const; /* extract translation */ + + void rotate(const Vector3 &euler_angles); /* 3d rotation with euler angles */ + void rotate(const Vector3 &axis, scalar_t angle); /* 3d axis/angle rotation */ + void rotate(const Quaternion &quat); + void set_rotation(const Vector3 &euler_angles); + void set_rotation(const Vector3 &axis, scalar_t angle); + void set_rotation(const Quaternion &quat); + Quaternion get_rotation_quat() const; /* extract rotation */ + + void scale(const Vector4 &scale_vec); + void set_scaling(const Vector4 &scale_vec); + Vector3 get_scaling() const; /* extract scaling */ + + void set_column_vector(const Vector4 &vec, unsigned int col_index); + void set_row_vector(const Vector4 &vec, unsigned int row_index); + Vector4 get_column_vector(unsigned int col_index) const; + Vector4 get_row_vector(unsigned int row_index) const; + + void transpose(); + Matrix4x4 transposed() const; + scalar_t determinant() const; + Matrix4x4 adjoint() const; + Matrix4x4 inverse() const; + + void keep_upper_left(); + void clear_upper_left(); + + friend std::ostream &operator <<(std::ostream &out, const Matrix4x4 &mat); +}; + +/* binary operations matrix (op) matrix */ +Matrix4x4 operator +(const Matrix4x4 &m1, const Matrix4x4 &m2); +Matrix4x4 operator -(const Matrix4x4 &m1, const Matrix4x4 &m2); +inline Matrix4x4 operator *(const Matrix4x4 &m1, const Matrix4x4 &m2); + +void operator +=(Matrix4x4 &m1, const Matrix4x4 &m2); +void operator -=(Matrix4x4 &m1, const Matrix4x4 &m2); +inline void operator *=(Matrix4x4 &m1, const Matrix4x4 &m2); + +/* binary operations matrix (op) scalar and scalar (op) matrix */ +Matrix4x4 operator *(const Matrix4x4 &mat, scalar_t scalar); +Matrix4x4 operator *(scalar_t scalar, const Matrix4x4 &mat); + +void operator *=(Matrix4x4 &mat, scalar_t scalar); + +std::ostream &operator <<(std::ostream &out, const Matrix4x4 &mat); + +#endif /* __cplusplus */ + +#include "matrix.inl" + +#endif /* VMATH_MATRIX_H_ */ diff -r 000000000000 -r f234630e38ff vmath/matrix.inl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/matrix.inl Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,200 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* C matrix 3x3 functions */ +static inline void m3_identity(mat3_t m) +{ + static const mat3_t id = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; + memcpy(m, id, sizeof id); +} + +static inline void m3_cons(mat3_t m, + scalar_t m11, scalar_t m12, scalar_t m13, + scalar_t m21, scalar_t m22, scalar_t m23, + scalar_t m31, scalar_t m32, scalar_t m33) +{ + m[0][0] = m11; m[0][1] = m12; m[0][2] = m13; + m[1][0] = m21; m[1][1] = m22; m[1][2] = m23; + m[2][0] = m31; m[2][1] = m32; m[2][2] = m33; +} + +static inline void m3_copy(mat3_t dest, mat3_t src) +{ + memcpy(dest, src, sizeof(mat3_t)); +} + + +/* C matrix 4x4 functions */ +static inline void m4_identity(mat4_t m) +{ + static const mat4_t id = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}; + memcpy(m, id, sizeof id); +} + +static inline void m4_cons(mat4_t m, + scalar_t m11, scalar_t m12, scalar_t m13, scalar_t m14, + scalar_t m21, scalar_t m22, scalar_t m23, scalar_t m24, + scalar_t m31, scalar_t m32, scalar_t m33, scalar_t m34, + scalar_t m41, scalar_t m42, scalar_t m43, scalar_t m44) +{ + m[0][0] = m11; m[0][1] = m12; m[0][2] = m13; m[0][3] = m14; + m[1][0] = m21; m[1][1] = m22; m[1][2] = m23; m[1][3] = m24; + m[2][0] = m31; m[2][1] = m32; m[2][2] = m33; m[2][3] = m34; + m[3][0] = m41; m[3][1] = m42; m[3][2] = m43; m[3][3] = m44; +} + +static inline void m4_copy(mat4_t dest, mat4_t src) +{ + memcpy(dest, src, sizeof(mat4_t)); +} + +static inline void m4_mult(mat4_t res, mat4_t m1, mat4_t m2) +{ + mat4_t tmp; + + /* + int i, j; + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + tmp[i][j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j] + m1[i][2] * m2[2][j] + m1[i][3] * m2[3][j]; + } + } + */ + + tmp[0][0] = m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0] + m1[0][2] * m2[2][0] + m1[0][3] * m2[3][0]; + tmp[0][1] = m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1] + m1[0][2] * m2[2][1] + m1[0][3] * m2[3][1]; + tmp[0][2] = m1[0][0] * m2[0][2] + m1[0][1] * m2[1][2] + m1[0][2] * m2[2][2] + m1[0][3] * m2[3][2]; + tmp[0][3] = m1[0][0] * m2[0][3] + m1[0][1] * m2[1][3] + m1[0][2] * m2[2][3] + m1[0][3] * m2[3][3]; + + tmp[1][0] = m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0] + m1[1][2] * m2[2][0] + m1[1][3] * m2[3][0]; + tmp[1][1] = m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1] + m1[1][2] * m2[2][1] + m1[1][3] * m2[3][1]; + tmp[1][2] = m1[1][0] * m2[0][2] + m1[1][1] * m2[1][2] + m1[1][2] * m2[2][2] + m1[1][3] * m2[3][2]; + tmp[1][3] = m1[1][0] * m2[0][3] + m1[1][1] * m2[1][3] + m1[1][2] * m2[2][3] + m1[1][3] * m2[3][3]; + + tmp[2][0] = m1[2][0] * m2[0][0] + m1[2][1] * m2[1][0] + m1[2][2] * m2[2][0] + m1[2][3] * m2[3][0]; + tmp[2][1] = m1[2][0] * m2[0][1] + m1[2][1] * m2[1][1] + m1[2][2] * m2[2][1] + m1[2][3] * m2[3][1]; + tmp[2][2] = m1[2][0] * m2[0][2] + m1[2][1] * m2[1][2] + m1[2][2] * m2[2][2] + m1[2][3] * m2[3][2]; + tmp[2][3] = m1[2][0] * m2[0][3] + m1[2][1] * m2[1][3] + m1[2][2] * m2[2][3] + m1[2][3] * m2[3][3]; + + tmp[3][0] = m1[3][0] * m2[0][0] + m1[3][1] * m2[1][0] + m1[3][2] * m2[2][0] + m1[3][3] * m2[3][0]; + tmp[3][1] = m1[3][0] * m2[0][1] + m1[3][1] * m2[1][1] + m1[3][2] * m2[2][1] + m1[3][3] * m2[3][1]; + tmp[3][2] = m1[3][0] * m2[0][2] + m1[3][1] * m2[1][2] + m1[3][2] * m2[2][2] + m1[3][3] * m2[3][2]; + tmp[3][3] = m1[3][0] * m2[0][3] + m1[3][1] * m2[1][3] + m1[3][2] * m2[2][3] + m1[3][3] * m2[3][3]; + + m4_copy(res, tmp); +} + +static inline void m4_set_column(mat4_t m, vec4_t v, int idx) +{ + m[0][idx] = v.x; + m[1][idx] = v.y; + m[2][idx] = v.z; + m[3][idx] = v.w; +} + +static inline void m4_set_row(mat4_t m, vec4_t v, int idx) +{ + m[idx][0] = v.x; + m[idx][1] = v.y; + m[idx][2] = v.z; + m[idx][3] = v.w; +} + +#ifdef __cplusplus +} /* extern "C" */ + + +/* unrolled to hell and inline */ +inline Matrix4x4 operator *(const Matrix4x4 &m1, const Matrix4x4 &m2) +{ + Matrix4x4 res; + + /* + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + res.m[i][j] = m1.m[i][0] * m2.m[0][j] + m1.m[i][1] * m2.m[1][j] + m1.m[i][2] * m2.m[2][j] + m1.m[i][3] * m2.m[3][j]; + } + } + */ + + res.m[0][0] = m1.m[0][0] * m2.m[0][0] + m1.m[0][1] * m2.m[1][0] + m1.m[0][2] * m2.m[2][0] + m1.m[0][3] * m2.m[3][0]; + res.m[0][1] = m1.m[0][0] * m2.m[0][1] + m1.m[0][1] * m2.m[1][1] + m1.m[0][2] * m2.m[2][1] + m1.m[0][3] * m2.m[3][1]; + res.m[0][2] = m1.m[0][0] * m2.m[0][2] + m1.m[0][1] * m2.m[1][2] + m1.m[0][2] * m2.m[2][2] + m1.m[0][3] * m2.m[3][2]; + res.m[0][3] = m1.m[0][0] * m2.m[0][3] + m1.m[0][1] * m2.m[1][3] + m1.m[0][2] * m2.m[2][3] + m1.m[0][3] * m2.m[3][3]; + + res.m[1][0] = m1.m[1][0] * m2.m[0][0] + m1.m[1][1] * m2.m[1][0] + m1.m[1][2] * m2.m[2][0] + m1.m[1][3] * m2.m[3][0]; + res.m[1][1] = m1.m[1][0] * m2.m[0][1] + m1.m[1][1] * m2.m[1][1] + m1.m[1][2] * m2.m[2][1] + m1.m[1][3] * m2.m[3][1]; + res.m[1][2] = m1.m[1][0] * m2.m[0][2] + m1.m[1][1] * m2.m[1][2] + m1.m[1][2] * m2.m[2][2] + m1.m[1][3] * m2.m[3][2]; + res.m[1][3] = m1.m[1][0] * m2.m[0][3] + m1.m[1][1] * m2.m[1][3] + m1.m[1][2] * m2.m[2][3] + m1.m[1][3] * m2.m[3][3]; + + res.m[2][0] = m1.m[2][0] * m2.m[0][0] + m1.m[2][1] * m2.m[1][0] + m1.m[2][2] * m2.m[2][0] + m1.m[2][3] * m2.m[3][0]; + res.m[2][1] = m1.m[2][0] * m2.m[0][1] + m1.m[2][1] * m2.m[1][1] + m1.m[2][2] * m2.m[2][1] + m1.m[2][3] * m2.m[3][1]; + res.m[2][2] = m1.m[2][0] * m2.m[0][2] + m1.m[2][1] * m2.m[1][2] + m1.m[2][2] * m2.m[2][2] + m1.m[2][3] * m2.m[3][2]; + res.m[2][3] = m1.m[2][0] * m2.m[0][3] + m1.m[2][1] * m2.m[1][3] + m1.m[2][2] * m2.m[2][3] + m1.m[2][3] * m2.m[3][3]; + + res.m[3][0] = m1.m[3][0] * m2.m[0][0] + m1.m[3][1] * m2.m[1][0] + m1.m[3][2] * m2.m[2][0] + m1.m[3][3] * m2.m[3][0]; + res.m[3][1] = m1.m[3][0] * m2.m[0][1] + m1.m[3][1] * m2.m[1][1] + m1.m[3][2] * m2.m[2][1] + m1.m[3][3] * m2.m[3][1]; + res.m[3][2] = m1.m[3][0] * m2.m[0][2] + m1.m[3][1] * m2.m[1][2] + m1.m[3][2] * m2.m[2][2] + m1.m[3][3] * m2.m[3][2]; + res.m[3][3] = m1.m[3][0] * m2.m[0][3] + m1.m[3][1] * m2.m[1][3] + m1.m[3][2] * m2.m[2][3] + m1.m[3][3] * m2.m[3][3]; + + return res; +} + +inline void operator *=(Matrix4x4 &m1, const Matrix4x4 &m2) +{ + Matrix4x4 res = m1 * m2; + m1 = res; +} + + +inline scalar_t *Matrix3x3::operator [](int index) +{ + return m[index]; +} + +inline const scalar_t *Matrix3x3::operator [](int index) const +{ + return m[index]; +} + +inline void Matrix3x3::reset_identity() +{ + *this = identity; +} + +inline scalar_t *Matrix4x4::operator [](int index) +{ + return m[index]; +} + +inline const scalar_t *Matrix4x4::operator [](int index) const +{ + return m[index]; +} + +inline void Matrix4x4::reset_identity() +{ + *this = identity; +} +#endif /* __cplusplus */ diff -r 000000000000 -r f234630e38ff vmath/matrix_c.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/matrix_c.c Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,292 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 "matrix.h" +#include "vector.h" +#include "quat.h" + +void m3_to_m4(mat4_t dest, mat3_t src) +{ + int i, j; + + memset(dest, 0, sizeof(mat4_t)); + for(i=0; i<3; i++) { + for(j=0; j<3; j++) { + dest[i][j] = src[i][j]; + } + } + dest[3][3] = 1.0; +} + +void m3_print(FILE *fp, mat3_t m) +{ + int i; + for(i=0; i<3; i++) { + fprintf(fp, "[ %12.5f %12.5f %12.5f ]\n", (float)m[i][0], (float)m[i][1], (float)m[i][2]); + } +} + +/* C matrix 4x4 functions */ +void m4_to_m3(mat3_t dest, mat4_t src) +{ + int i, j; + for(i=0; i<3; i++) { + for(j=0; j<3; j++) { + dest[i][j] = src[i][j]; + } + } +} + +void m4_set_translation(mat4_t m, scalar_t x, scalar_t y, scalar_t z) +{ + m4_identity(m); + m[0][3] = x; + m[1][3] = y; + m[2][3] = z; +} + +void m4_translate(mat4_t m, scalar_t x, scalar_t y, scalar_t z) +{ + mat4_t tm; + m4_set_translation(tm, x, y, z); + m4_mult(m, m, tm); +} + +void m4_rotate(mat4_t m, scalar_t x, scalar_t y, scalar_t z) +{ + m4_rotate_x(m, x); + m4_rotate_y(m, y); + m4_rotate_z(m, z); +} + +void m4_set_rotation_x(mat4_t m, scalar_t angle) +{ + m4_identity(m); + m[1][1] = cos(angle); m[1][2] = -sin(angle); + m[2][1] = sin(angle); m[2][2] = cos(angle); +} + +void m4_rotate_x(mat4_t m, scalar_t angle) +{ + mat4_t rm; + m4_set_rotation_x(m, angle); + m4_mult(m, m, rm); +} + +void m4_set_rotation_y(mat4_t m, scalar_t angle) +{ + m4_identity(m); + m[0][0] = cos(angle); m[0][2] = sin(angle); + m[2][0] = -sin(angle); m[2][2] = cos(angle); +} + +void m4_rotate_y(mat4_t m, scalar_t angle) +{ + mat4_t rm; + m4_set_rotation_y(rm, angle); + m4_mult(m, m, rm); +} + +void m4_set_rotation_z(mat4_t m, scalar_t angle) +{ + m4_identity(m); + m[0][0] = cos(angle); m[0][1] = -sin(angle); + m[1][0] = sin(angle); m[1][1] = cos(angle); +} + +void m4_rotate_z(mat4_t m, scalar_t angle) +{ + mat4_t rm; + m4_set_rotation_z(rm, angle); + m4_mult(m, m, rm); +} + +void m4_set_rotation_axis(mat4_t m, scalar_t angle, scalar_t x, scalar_t y, scalar_t z) +{ + scalar_t sina = sin(angle); + scalar_t cosa = cos(angle); + scalar_t one_minus_cosa = 1.0 - cosa; + scalar_t nxsq = x * x; + scalar_t nysq = y * y; + scalar_t nzsq = z * z; + + m[0][0] = nxsq + (1.0 - nxsq) * cosa; + m[0][1] = x * y * one_minus_cosa - z * sina; + m[0][2] = x * z * one_minus_cosa + y * sina; + m[1][0] = x * y * one_minus_cosa + z * sina; + m[1][1] = nysq + (1.0 - nysq) * cosa; + m[1][2] = y * z * one_minus_cosa - x * sina; + m[2][0] = x * z * one_minus_cosa - y * sina; + m[2][1] = y * z * one_minus_cosa + x * sina; + m[2][2] = nzsq + (1.0 - nzsq) * cosa; + + /* the rest are identity */ + m[3][0] = m[3][1] = m[3][2] = m[0][3] = m[1][3] = m[2][3] = 0.0; + m[3][3] = 1.0; +} + +void m4_rotate_axis(mat4_t m, scalar_t angle, scalar_t x, scalar_t y, scalar_t z) +{ + mat4_t xform; + m4_set_rotation_axis(xform, angle, x, y, z); + m4_mult(m, m, xform); +} + +void m4_rotate_quat(mat4_t m, quat_t q) +{ + mat4_t rm; + quat_to_mat4(rm, q); + m4_mult(m, m, rm); +} + +void m4_scale(mat4_t m, scalar_t x, scalar_t y, scalar_t z) +{ + mat4_t sm; + m4_identity(sm); + sm[0][0] = x; + sm[1][1] = y; + sm[2][2] = z; + m4_mult(m, m, sm); +} + +void m4_transpose(mat4_t res, mat4_t m) +{ + int i, j; + mat4_t tmp; + m4_copy(tmp, m); + + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + res[i][j] = tmp[j][i]; + } + } +} + +scalar_t m4_determinant(mat4_t m) +{ + scalar_t det11 = (m[1][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[1][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) + + (m[1][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])); + + scalar_t det12 = (m[1][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[1][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[1][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])); + + scalar_t det13 = (m[1][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) - + (m[1][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[1][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + + scalar_t det14 = (m[1][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) - + (m[1][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) + + (m[1][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + + return m[0][0] * det11 - m[0][1] * det12 + m[0][2] * det13 - m[0][3] * det14; +} + +void m4_adjoint(mat4_t res, mat4_t m) +{ + int i, j; + mat4_t coef; + + coef[0][0] = (m[1][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[1][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) + + (m[1][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])); + coef[0][1] = (m[1][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[1][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[1][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])); + coef[0][2] = (m[1][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) - + (m[1][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[1][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + coef[0][3] = (m[1][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) - + (m[1][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) + + (m[1][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + + coef[1][0] = (m[0][1] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[0][2] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) + + (m[0][3] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])); + coef[1][1] = (m[0][0] * (m[2][2] * m[3][3] - m[3][2] * m[2][3])) - + (m[0][2] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[0][3] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])); + coef[1][2] = (m[0][0] * (m[2][1] * m[3][3] - m[3][1] * m[2][3])) - + (m[0][1] * (m[2][0] * m[3][3] - m[3][0] * m[2][3])) + + (m[0][3] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + coef[1][3] = (m[0][0] * (m[2][1] * m[3][2] - m[3][1] * m[2][2])) - + (m[0][1] * (m[2][0] * m[3][2] - m[3][0] * m[2][2])) + + (m[0][2] * (m[2][0] * m[3][1] - m[3][0] * m[2][1])); + + coef[2][0] = (m[0][1] * (m[1][2] * m[3][3] - m[3][2] * m[1][3])) - + (m[0][2] * (m[1][1] * m[3][3] - m[3][1] * m[1][3])) + + (m[0][3] * (m[1][1] * m[3][2] - m[3][1] * m[1][2])); + coef[2][1] = (m[0][0] * (m[1][2] * m[3][3] - m[3][2] * m[1][3])) - + (m[0][2] * (m[1][0] * m[3][3] - m[3][0] * m[1][3])) + + (m[0][3] * (m[1][0] * m[3][2] - m[3][0] * m[1][2])); + coef[2][2] = (m[0][0] * (m[1][1] * m[3][3] - m[3][1] * m[1][3])) - + (m[0][1] * (m[1][0] * m[3][3] - m[3][0] * m[1][3])) + + (m[0][3] * (m[1][0] * m[3][1] - m[3][0] * m[1][1])); + coef[2][3] = (m[0][0] * (m[1][1] * m[3][2] - m[3][1] * m[1][2])) - + (m[0][1] * (m[1][0] * m[3][2] - m[3][0] * m[1][2])) + + (m[0][2] * (m[1][0] * m[3][1] - m[3][0] * m[1][1])); + + coef[3][0] = (m[0][1] * (m[1][2] * m[2][3] - m[2][2] * m[1][3])) - + (m[0][2] * (m[1][1] * m[2][3] - m[2][1] * m[1][3])) + + (m[0][3] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])); + coef[3][1] = (m[0][0] * (m[1][2] * m[2][3] - m[2][2] * m[1][3])) - + (m[0][2] * (m[1][0] * m[2][3] - m[2][0] * m[1][3])) + + (m[0][3] * (m[1][0] * m[2][2] - m[2][0] * m[1][2])); + coef[3][2] = (m[0][0] * (m[1][1] * m[2][3] - m[2][1] * m[1][3])) - + (m[0][1] * (m[1][0] * m[2][3] - m[2][0] * m[1][3])) + + (m[0][3] * (m[1][0] * m[2][1] - m[2][0] * m[1][1])); + coef[3][3] = (m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])) - + (m[0][1] * (m[1][0] * m[2][2] - m[2][0] * m[1][2])) + + (m[0][2] * (m[1][0] * m[2][1] - m[2][0] * m[1][1])); + + m4_transpose(res, coef); + + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + res[i][j] = j % 2 ? -res[i][j] : res[i][j]; + if(i % 2) res[i][j] = -res[i][j]; + } + } +} + +void m4_inverse(mat4_t res, mat4_t m) +{ + int i, j; + mat4_t adj; + scalar_t det; + + m4_adjoint(adj, m); + det = m4_determinant(m); + + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + res[i][j] = adj[i][j] / det; + } + } +} + +void m4_print(FILE *fp, mat4_t m) +{ + int i; + for(i=0; i<4; i++) { + fprintf(fp, "[ %12.5f %12.5f %12.5f %12.5f ]\n", (float)m[i][0], (float)m[i][1], (float)m[i][2], (float)m[i][3]); + } +} diff -r 000000000000 -r f234630e38ff vmath/quat.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/quat.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,191 @@ +#include "quat.h" +#include "vmath.h" + +Quaternion::Quaternion() { + s = 1.0; + v.x = v.y = v.z = 0.0; +} + +Quaternion::Quaternion(scalar_t s, const Vector3 &v) { + this->s = s; + this->v = v; +} + +Quaternion::Quaternion(scalar_t s, scalar_t x, scalar_t y, scalar_t z) { + v.x = x; + v.y = y; + v.z = z; + this->s = s; +} + +Quaternion::Quaternion(const Vector3 &axis, scalar_t angle) { + set_rotation(axis, angle); +} + +Quaternion::Quaternion(const quat_t &quat) +{ + v.x = quat.x; + v.y = quat.y; + v.z = quat.z; + s = quat.w; +} + +Quaternion Quaternion::operator +(const Quaternion &quat) const +{ + return Quaternion(s + quat.s, v + quat.v); +} + +Quaternion Quaternion::operator -(const Quaternion &quat) const +{ + return Quaternion(s - quat.s, v - quat.v); +} + +Quaternion Quaternion::operator -() const +{ + return Quaternion(-s, -v); +} + +/** Quaternion Multiplication: + * Q1*Q2 = [s1*s2 - v1.v2, s1*v2 + s2*v1 + v1(x)v2] + */ +Quaternion Quaternion::operator *(const Quaternion &quat) const { + Quaternion newq; + newq.s = s * quat.s - dot_product(v, quat.v); + newq.v = quat.v * s + v * quat.s + cross_product(v, quat.v); + return newq; +} + +void Quaternion::operator +=(const Quaternion &quat) { + *this = Quaternion(s + quat.s, v + quat.v); +} + +void Quaternion::operator -=(const Quaternion &quat) { + *this = Quaternion(s - quat.s, v - quat.v); +} + +void Quaternion::operator *=(const Quaternion &quat) { + *this = *this * quat; +} + +void Quaternion::reset_identity() { + s = 1.0; + v.x = v.y = v.z = 0.0; +} + +Quaternion Quaternion::conjugate() const { + return Quaternion(s, -v); +} + +scalar_t Quaternion::length() const { + return (scalar_t)sqrt(v.x*v.x + v.y*v.y + v.z*v.z + s*s); +} + +/** Q * ~Q = ||Q||^2 */ +scalar_t Quaternion::length_sq() const { + return v.x*v.x + v.y*v.y + v.z*v.z + s*s; +} + +void Quaternion::normalize() { + scalar_t len = (scalar_t)sqrt(v.x*v.x + v.y*v.y + v.z*v.z + s*s); + v.x /= len; + v.y /= len; + v.z /= len; + s /= len; +} + +Quaternion Quaternion::normalized() const { + Quaternion nq = *this; + scalar_t len = (scalar_t)sqrt(v.x*v.x + v.y*v.y + v.z*v.z + s*s); + nq.v.x /= len; + nq.v.y /= len; + nq.v.z /= len; + nq.s /= len; + return nq; +} + +/** Quaternion Inversion: Q^-1 = ~Q / ||Q||^2 */ +Quaternion Quaternion::inverse() const { + Quaternion inv = conjugate(); + scalar_t lensq = length_sq(); + inv.v /= lensq; + inv.s /= lensq; + + return inv; +} + + +void Quaternion::set_rotation(const Vector3 &axis, scalar_t angle) { + scalar_t half_angle = angle / 2.0; + s = cos(half_angle); + v = axis * sin(half_angle); +} + +void Quaternion::rotate(const Vector3 &axis, scalar_t angle) { + Quaternion q; + scalar_t half_angle = angle / 2.0; + q.s = cos(half_angle); + q.v = axis * sin(half_angle); + + *this *= q; +} + +void Quaternion::rotate(const Quaternion &q) { + *this = q * *this * q.conjugate(); +} + +Matrix3x3 Quaternion::get_rotation_matrix() const { + return Matrix3x3( + 1.0 - 2.0 * v.y*v.y - 2.0 * v.z*v.z, 2.0 * v.x * v.y - 2.0 * s * v.z, 2.0 * v.z * v.x + 2.0 * s * v.y, + 2.0 * v.x * v.y + 2.0 * s * v.z, 1.0 - 2.0 * v.x*v.x - 2.0 * v.z*v.z, 2.0 * v.y * v.z - 2.0 * s * v.x, + 2.0 * v.z * v.x - 2.0 * s * v.y, 2.0 * v.y * v.z + 2.0 * s * v.x, 1.0 - 2.0 * v.x*v.x - 2.0 * v.y*v.y); +} + + +/** Spherical linear interpolation (slerp) */ +Quaternion slerp(const Quaternion &quat1, const Quaternion &q2, scalar_t t) { + Quaternion q1; + scalar_t dot = q1.s * q2.s + q1.v.x * q2.v.x + q1.v.y * q2.v.y + q1.v.z * q2.v.z; + + if(dot < 0.0) { + /* make sure we interpolate across the shortest arc */ + q1 = -quat1; + dot = -dot; + } else { + q1 = quat1; + } + + /* clamp dot to [-1, 1] in order to avoid domain errors in acos due to + * floating point imprecisions + */ + if(dot < -1.0) dot = -1.0; + if(dot > 1.0) dot = 1.0; + + scalar_t angle = acos(dot); + scalar_t a, b; + + scalar_t sin_angle = sin(angle); + if(fabs(sin_angle) < SMALL_NUMBER) { + /* for very small angles or completely opposite orientations + * use linear interpolation to avoid div/zero (in the first case it makes sense, + * the second case is pretty much undefined anyway I guess ... + */ + a = 1.0f - t; + b = t; + } else { + a = sin((1.0f - t) * angle) / sin_angle; + b = sin(t * angle) / sin_angle; + } + + scalar_t x = q1.v.x * a + q2.v.x * b; + scalar_t y = q1.v.y * a + q2.v.y * b; + scalar_t z = q1.v.z * a + q2.v.z * b; + scalar_t s = q1.s * a + q2.s * b; + + return Quaternion(s, Vector3(x, y, z)); +} + + +std::ostream &operator <<(std::ostream &out, const Quaternion &q) { + out << "(" << q.s << ", " << q.v << ")"; + return out; +} diff -r 000000000000 -r f234630e38ff vmath/quat.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/quat.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,118 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 VMATH_QUATERNION_H_ +#define VMATH_QUATERNION_H_ + +#include +#include "vmath_types.h" +#include "vector.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define quat_cons(s, x, y, z) v4_cons(x, y, z, s) +#define quat_vec(q) v3_cons((q).x, (q).y, (q).z) +#define quat_s(q) ((q).w) +#define quat_identity() quat_cons(1.0, 0.0, 0.0, 0.0) +void quat_print(FILE *fp, quat_t q); + +#define quat_add v4_add +#define quat_sub v4_sub +#define quat_neg v4_neg + +static inline quat_t quat_mul(quat_t q1, quat_t q2); + +static inline quat_t quat_conjugate(quat_t q); + +#define quat_length v4_length +#define quat_length_sq v4_length_sq + +#define quat_normalize v4_normalize +static inline quat_t quat_inverse(quat_t q); + +quat_t quat_rotate(quat_t q, scalar_t angle, scalar_t x, scalar_t y, scalar_t z); +quat_t quat_rotate_quat(quat_t q, quat_t rotq); + +static inline void quat_to_mat3(mat3_t res, quat_t q); +static inline void quat_to_mat4(mat4_t res, quat_t q); + +#define quat_lerp quat_slerp +quat_t quat_slerp(quat_t q1, quat_t q2, scalar_t t); + + +#ifdef __cplusplus +} /* extern "C" */ + +#include + +/* Quaternion */ +class Quaternion { +public: + scalar_t s; + Vector3 v; + + Quaternion(); + Quaternion(scalar_t s, const Vector3 &v); + Quaternion(scalar_t s, scalar_t x, scalar_t y, scalar_t z); + Quaternion(const Vector3 &axis, scalar_t angle); + Quaternion(const quat_t &quat); + + Quaternion operator +(const Quaternion &quat) const; + Quaternion operator -(const Quaternion &quat) const; + Quaternion operator -() const; + Quaternion operator *(const Quaternion &quat) const; + + void operator +=(const Quaternion &quat); + void operator -=(const Quaternion &quat); + void operator *=(const Quaternion &quat); + + void reset_identity(); + + Quaternion conjugate() const; + + scalar_t length() const; + scalar_t length_sq() const; + + void normalize(); + Quaternion normalized() const; + + Quaternion inverse() const; + + void set_rotation(const Vector3 &axis, scalar_t angle); + void rotate(const Vector3 &axis, scalar_t angle); + /* note: this is a totally different operation from the above + * this treats the quaternion as signifying direction and rotates + * it by a rotation quaternion by rot * q * rot' + */ + void rotate(const Quaternion &q); + + Matrix3x3 get_rotation_matrix() const; +}; + +Quaternion slerp(const Quaternion &q1, const Quaternion &q2, scalar_t t); +inline Quaternion lerp(const Quaternion &q1, const Quaternion &q2, scalar_t t); + +std::ostream &operator <<(std::ostream &out, const Quaternion &q); + +#endif /* __cplusplus */ + +#include "quat.inl" + +#endif /* VMATH_QUATERNION_H_ */ diff -r 000000000000 -r f234630e38ff vmath/quat.inl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/quat.inl Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,81 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 "vector.h" +#include "matrix.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +static inline quat_t quat_mul(quat_t q1, quat_t q2) +{ + quat_t res; + vec3_t v1 = quat_vec(q1); + vec3_t v2 = quat_vec(q2); + + res.w = q1.w * q2.w - v3_dot(v1, v2); + /* resvec = v2 * q1 + v1 * q2 + cross(v1, v2) */ + res.x = v2.x * q1.w + v1.x * q2.w + (v1.y * v2.z - v1.z * v2.y); + res.y = v2.y * q1.w + v1.y * q2.w + (v1.z * v2.x - v1.x * v2.z); + res.z = v2.z * q1.w + v1.z * q2.w + (v1.x * v2.y - v1.y * v2.x); + return res; +} + +static inline quat_t quat_conjugate(quat_t q) +{ + q.x = -q.x; + q.y = -q.y; + q.z = -q.z; + return q; +} + +static inline quat_t quat_inverse(quat_t q) +{ + scalar_t lensq = quat_length_sq(q); + q = quat_conjugate(q); + q.x /= lensq; + q.y /= lensq; + q.z /= lensq; + q.w /= lensq; + return q; +} + +static inline void quat_to_mat3(mat3_t res, quat_t q) +{ + m3_cons(res, 1.0 - 2.0 * q.y*q.y - 2.0 * q.z*q.z, 2.0 * q.x * q.y - 2.0 * q.w * q.z, 2.0 * q.z * q.x + 2.0 * q.w * q.y, + 2.0 * q.x * q.y + 2.0 * q.w * q.z, 1.0 - 2.0 * q.x*q.x - 2.0 * q.z*q.z, 2.0 * q.y * q.z - 2.0 * q.w * q.x, + 2.0 * q.z * q.x - 2.0 * q.w * q.y, 2.0 * q.y * q.z + 2.0 * q.w * q.x, 1.0 - 2.0 * q.x*q.x - 2.0 * q.y*q.y); +} + +static inline void quat_to_mat4(mat4_t res, quat_t q) +{ + m4_cons(res, 1.0 - 2.0 * q.y*q.y - 2.0 * q.z*q.z, 2.0 * q.x * q.y - 2.0 * q.w * q.z, 2.0 * q.z * q.x + 2.0 * q.w * q.y, 0, + 2.0 * q.x * q.y + 2.0 * q.w * q.z, 1.0 - 2.0 * q.x*q.x - 2.0 * q.z*q.z, 2.0 * q.y * q.z - 2.0 * q.w * q.x, 0, + 2.0 * q.z * q.x - 2.0 * q.w * q.y, 2.0 * q.y * q.z + 2.0 * q.w * q.x, 1.0 - 2.0 * q.x*q.x - 2.0 * q.y*q.y, 0, + 0, 0, 0, 1); +} + +#ifdef __cplusplus +} /* extern "C" */ + +inline Quaternion lerp(const Quaternion &a, const Quaternion &b, scalar_t t) +{ + return slerp(a, b, t); +} +#endif /* __cplusplus */ diff -r 000000000000 -r f234630e38ff vmath/quat_c.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/quat_c.c Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,89 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 "quat.h" + +void quat_print(FILE *fp, quat_t q) +{ + fprintf(fp, "([ %.4f %.4f %.4f ] %.4f)", q.x, q.y, q.z, q.w); +} + +quat_t quat_rotate(quat_t q, scalar_t angle, scalar_t x, scalar_t y, scalar_t z) +{ + quat_t rq; + scalar_t half_angle = angle * 0.5; + scalar_t sin_half = sin(half_angle); + + rq.w = cos(half_angle); + rq.x = x * sin_half; + rq.y = y * sin_half; + rq.z = z * sin_half; + + return quat_mul(q, rq); +} + +quat_t quat_rotate_quat(quat_t q, quat_t rotq) +{ + return quat_mul(quat_mul(rotq, q), quat_conjugate(rotq)); +} + +quat_t quat_slerp(quat_t q1, quat_t q2, scalar_t t) +{ + quat_t res; + scalar_t a, b, angle, sin_angle, dot; + + dot = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z; + if(dot < 0.0) { + /* make sure we interpolate across the shortest arc */ + q1.x = -q1.x; + q1.y = -q1.y; + q1.z = -q1.z; + q1.w = -q1.w; + dot = -dot; + } + + /* clamp dot to [-1, 1] in order to avoid domain errors in acos due to + * floating point imprecisions + */ + if(dot < -1.0) dot = -1.0; + if(dot > 1.0) dot = 1.0; + + angle = acos(dot); + sin_angle = sin(angle); + + if(fabs(sin_angle) < SMALL_NUMBER) { + /* for very small angles or completely opposite orientations + * use linear interpolation to avoid div/zero (in the first case it makes sense, + * the second case is pretty much undefined anyway I guess ... + */ + a = 1.0f - t; + 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; + return res; +} diff -r 000000000000 -r f234630e38ff vmath/ray.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/ray.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,39 @@ +#include "ray.h" +#include "vector.h" + +scalar_t Ray::env_ior = 1.0; + +Ray::Ray() +{ + ior = env_ior; + energy = 1.0; + time = 0; + iter = 0; +} + +Ray::Ray(const Vector3 &origin, const Vector3 &dir) +{ + this->origin = origin; + this->dir = dir; + ior = env_ior; + energy = 1.0; + time = 0; + iter = 0; +} + +void Ray::transform(const Matrix4x4 &xform) +{ + Matrix4x4 upper = xform; + upper[0][3] = upper[1][3] = upper[2][3] = upper[3][0] = upper[3][1] = upper[3][2] = 0.0; + upper[3][3] = 1.0; + + dir.transform(upper); + origin.transform(xform); +} + +Ray Ray::transformed(const Matrix4x4 &xform) const +{ + Ray foo = *this; + foo.transform(xform); + return foo; +} diff -r 000000000000 -r f234630e38ff vmath/ray.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/ray.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,66 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 VMATH_RAY_H_ +#define VMATH_RAY_H_ + +#include "matrix.h" +#include "vector.h" + +typedef struct { + vec3_t origin, dir; +} ray_t; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +static inline ray_t ray_cons(vec3_t origin, vec3_t dir); +ray_t ray_transform(ray_t r, mat4_t m); + +#ifdef __cplusplus +} /* __cplusplus */ + +#include + +class Ray { +public: + static scalar_t env_ior; + + Vector3 origin, dir; + scalar_t energy; + int iter; + scalar_t ior; + long time; + + void *user; /* store any random crap with the ray */ + + Ray(); + Ray(const Vector3 &origin, const Vector3 &dir); + + void transform(const Matrix4x4 &xform); + Ray transformed(const Matrix4x4 &xform) const; +}; + +inline Ray reflect_ray(const Ray &inray, const Vector3 &norm); +inline Ray refract_ray(const Ray &inray, const Vector3 &norm, scalar_t from_ior, scalar_t to_ior); +#endif /* __cplusplus */ + +#include "ray.inl" + +#endif /* VMATH_RAY_H_ */ diff -r 000000000000 -r f234630e38ff vmath/ray.inl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/ray.inl Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,52 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 . +*/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +static inline ray_t ray_cons(vec3_t origin, vec3_t dir) +{ + ray_t r; + r.origin = origin; + r.dir = dir; + return r; +} + +#ifdef __cplusplus +} + +inline Ray reflect_ray(const Ray &inray, const Vector3 &norm) +{ + Ray ray = inray; + ray.dir = ray.dir.reflection(norm); + return ray; +} + +inline Ray refract_ray(const Ray &inray, const Vector3 &norm, scalar_t from_ior, scalar_t to_ior) +{ + Ray ray = inray; + ray.dir = ray.dir.refraction(norm, from_ior, to_ior); + + /* check TIR */ + if(dot_product(ray.dir, norm) > 0.0) { + return reflect_ray(inray, norm); + } + return ray; +} +#endif /* __cplusplus */ diff -r 000000000000 -r f234630e38ff vmath/ray_c.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/ray_c.c Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,36 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 "ray.h" +#include "vector.h" + +ray_t ray_transform(ray_t r, mat4_t xform) +{ + mat4_t upper; + vec3_t dir; + + m4_copy(upper, xform); + upper[0][3] = upper[1][3] = upper[2][3] = upper[3][0] = upper[3][1] = upper[3][2] = 0.0; + upper[3][3] = 1.0; + + dir = v3_sub(r.dir, r.origin); + dir = v3_transform(dir, upper); + r.origin = v3_transform(r.origin, xform); + r.dir = v3_add(dir, r.origin); + return r; +} diff -r 000000000000 -r f234630e38ff vmath/vector.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/vector.cc Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,313 @@ +#include "vector.h" +#include "vmath.h" + +// ---------- Vector2 ----------- + +Vector2::Vector2(scalar_t x, scalar_t y) +{ + this->x = x; + this->y = y; +} + +Vector2::Vector2(const vec2_t &vec) +{ + x = vec.x; + y = vec.y; +} + +Vector2::Vector2(const Vector3 &vec) +{ + x = vec.x; + y = vec.y; +} + +Vector2::Vector2(const Vector4 &vec) +{ + x = vec.x; + y = vec.y; +} + +void Vector2::normalize() +{ + scalar_t len = length(); + x /= len; + y /= len; +} + +Vector2 Vector2::normalized() const +{ + scalar_t len = length(); + return Vector2(x / len, y / len); +} + +void Vector2::transform(const Matrix3x3 &mat) +{ + scalar_t nx = mat[0][0] * x + mat[0][1] * y + mat[0][2]; + y = mat[1][0] * x + mat[1][1] * y + mat[1][2]; + x = nx; +} + +Vector2 Vector2::transformed(const Matrix3x3 &mat) const +{ + Vector2 vec; + vec.x = mat[0][0] * x + mat[0][1] * y + mat[0][2]; + vec.y = mat[1][0] * x + mat[1][1] * y + mat[1][2]; + return vec; +} + +void Vector2::rotate(scalar_t angle) +{ + *this = Vector2(cos(angle) * x - sin(angle) * y, sin(angle) * x + cos(angle) * y); +} + +Vector2 Vector2::rotated(scalar_t angle) const +{ + return Vector2(cos(angle) * x - sin(angle) * y, sin(angle) * x + cos(angle) * y); +} + +Vector2 Vector2::reflection(const Vector2 &normal) const +{ + return 2.0 * dot_product(*this, normal) * normal - *this; +} + +Vector2 Vector2::refraction(const Vector2 &normal, scalar_t src_ior, scalar_t dst_ior) const +{ + // quick and dirty implementation :) + Vector3 v3refr = Vector3(this->x, this->y, 1.0).refraction(Vector3(this->x, this->y, 1), src_ior, dst_ior); + return Vector2(v3refr.x, v3refr.y); +} + +std::ostream &operator <<(std::ostream &out, const Vector2 &vec) +{ + out << "[" << vec.x << " " << vec.y << "]"; + return out; +} + + + +// --------- Vector3 ---------- + +Vector3::Vector3(scalar_t x, scalar_t y, scalar_t z) +{ + this->x = x; + this->y = y; + this->z = z; +} + +Vector3::Vector3(const vec3_t &vec) +{ + x = vec.x; + y = vec.y; + z = vec.z; +} + +Vector3::Vector3(const Vector2 &vec) +{ + x = vec.x; + y = vec.y; + z = 1; +} + +Vector3::Vector3(const Vector4 &vec) +{ + x = vec.x; + y = vec.y; + z = vec.z; +} + +void Vector3::normalize() +{ + scalar_t len = length(); + x /= len; + y /= len; + z /= len; +} + +Vector3 Vector3::normalized() const +{ + scalar_t len = length(); + return Vector3(x / len, y / len, z / len); +} + +Vector3 Vector3::reflection(const Vector3 &normal) const +{ + return 2.0 * dot_product(*this, normal) * normal - *this; +} + +Vector3 Vector3::refraction(const Vector3 &normal, scalar_t src_ior, scalar_t dst_ior) const +{ + return refraction(normal, src_ior / dst_ior); +} + +Vector3 Vector3::refraction(const Vector3 &normal, scalar_t ior) const +{ + scalar_t cos_inc = dot_product(*this, -normal); + + scalar_t radical = 1.0 + SQ(ior) * (SQ(cos_inc) - 1.0); + + if(radical < 0.0) { // total internal reflection + return -reflection(normal); + } + + scalar_t beta = ior * cos_inc - sqrt(radical); + + return *this * ior + normal * beta; +} + +void Vector3::transform(const Matrix3x3 &mat) +{ + scalar_t nx = mat[0][0] * x + mat[0][1] * y + mat[0][2] * z; + scalar_t ny = mat[1][0] * x + mat[1][1] * y + mat[1][2] * z; + z = mat[2][0] * x + mat[2][1] * y + mat[2][2] * z; + x = nx; + y = ny; +} + +Vector3 Vector3::transformed(const Matrix3x3 &mat) const +{ + Vector3 vec; + vec.x = mat[0][0] * x + mat[0][1] * y + mat[0][2] * z; + vec.y = mat[1][0] * x + mat[1][1] * y + mat[1][2] * z; + vec.z = mat[2][0] * x + mat[2][1] * y + mat[2][2] * z; + return vec; +} + +void Vector3::transform(const Matrix4x4 &mat) +{ + scalar_t nx = mat[0][0] * x + mat[0][1] * y + mat[0][2] * z + mat[0][3]; + scalar_t ny = mat[1][0] * x + mat[1][1] * y + mat[1][2] * z + mat[1][3]; + z = mat[2][0] * x + mat[2][1] * y + mat[2][2] * z + mat[2][3]; + x = nx; + y = ny; +} + +Vector3 Vector3::transformed(const Matrix4x4 &mat) const +{ + Vector3 vec; + vec.x = mat[0][0] * x + mat[0][1] * y + mat[0][2] * z + mat[0][3]; + vec.y = mat[1][0] * x + mat[1][1] * y + mat[1][2] * z + mat[1][3]; + vec.z = mat[2][0] * x + mat[2][1] * y + mat[2][2] * z + mat[2][3]; + return vec; +} + +void Vector3::transform(const Quaternion &quat) +{ + Quaternion vq(0.0f, *this); + vq = quat * vq * quat.inverse(); + *this = vq.v; +} + +Vector3 Vector3::transformed(const Quaternion &quat) const +{ + Quaternion vq(0.0f, *this); + vq = quat * vq * quat.inverse(); + return vq.v; +} + +void Vector3::rotate(const Vector3 &euler) +{ + Matrix4x4 rot; + rot.set_rotation(euler); + transform(rot); +} + +Vector3 Vector3::rotated(const Vector3 &euler) const +{ + Matrix4x4 rot; + rot.set_rotation(euler); + return transformed(rot); +} + +std::ostream &operator <<(std::ostream &out, const Vector3 &vec) +{ + out << "[" << vec.x << " " << vec.y << " " << vec.z << "]"; + return out; +} + + +// -------------- Vector4 -------------- +Vector4::Vector4(scalar_t x, scalar_t y, scalar_t z, scalar_t w) +{ + this->x = x; + this->y = y; + this->z = z; + this->w = w; +} + +Vector4::Vector4(const vec4_t &vec) +{ + x = vec.x; + y = vec.y; + z = vec.z; + w = vec.w; +} + +Vector4::Vector4(const Vector2 &vec) +{ + x = vec.x; + y = vec.y; + z = 1; + w = 1; +} + +Vector4::Vector4(const Vector3 &vec) +{ + x = vec.x; + y = vec.y; + z = vec.z; + w = 1; +} + +void Vector4::normalize() +{ + scalar_t len = (scalar_t)sqrt(x*x + y*y + z*z + w*w); + x /= len; + y /= len; + z /= len; + w /= len; +} + +Vector4 Vector4::normalized() const +{ + scalar_t len = (scalar_t)sqrt(x*x + y*y + z*z + w*w); + return Vector4(x / len, y / len, z / len, w / len); +} + +void Vector4::transform(const Matrix4x4 &mat) +{ + scalar_t nx = mat[0][0] * x + mat[0][1] * y + mat[0][2] * z + mat[0][3] * w; + scalar_t ny = mat[1][0] * x + mat[1][1] * y + mat[1][2] * z + mat[1][3] * w; + scalar_t nz = mat[2][0] * x + mat[2][1] * y + mat[2][2] * z + mat[2][3] * w; + w = mat[3][0] * x + mat[3][1] * y + mat[3][2] * z + mat[3][3] * w; + x = nx; + y = ny; + z = nz; +} + +Vector4 Vector4::transformed(const Matrix4x4 &mat) const +{ + Vector4 vec; + vec.x = mat[0][0] * x + mat[0][1] * y + mat[0][2] * z + mat[0][3] * w; + vec.y = mat[1][0] * x + mat[1][1] * y + mat[1][2] * z + mat[1][3] * w; + vec.z = mat[2][0] * x + mat[2][1] * y + mat[2][2] * z + mat[2][3] * w; + vec.w = mat[3][0] * x + mat[3][1] * y + mat[3][2] * z + mat[3][3] * w; + return vec; +} + +// TODO: implement 4D vector reflection +Vector4 Vector4::reflection(const Vector4 &normal) const +{ + return *this; +} + +// TODO: implement 4D vector refraction +Vector4 Vector4::refraction(const Vector4 &normal, scalar_t src_ior, scalar_t dst_ior) const +{ + return *this; +} + +std::ostream &operator <<(std::ostream &out, const Vector4 &vec) +{ + out << "[" << vec.x << " " << vec.y << " " << vec.z << " " << vec.w << "]"; + return out; +} diff -r 000000000000 -r f234630e38ff vmath/vector.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/vector.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,291 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 VMATH_VECTOR_H_ +#define VMATH_VECTOR_H_ + +#include +#include "vmath_types.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* C 2D vector functions */ +static inline vec2_t v2_cons(scalar_t x, scalar_t y); +static inline void v2_print(FILE *fp, vec2_t v); + +static inline vec2_t v2_add(vec2_t v1, vec2_t v2); +static inline vec2_t v2_sub(vec2_t v1, vec2_t v2); +static inline vec2_t v2_scale(vec2_t v, scalar_t s); +static inline scalar_t v2_dot(vec2_t v1, vec2_t v2); +static inline scalar_t v2_length(vec2_t v); +static inline scalar_t v2_length_sq(vec2_t v); +static inline vec2_t v2_normalize(vec2_t v); + +static inline vec2_t v2_lerp(vec2_t v1, vec2_t v2, scalar_t t); + +/* C 3D vector functions */ +static inline vec3_t v3_cons(scalar_t x, scalar_t y, scalar_t z); +static inline void v3_print(FILE *fp, vec3_t v); + +static inline vec3_t v3_add(vec3_t v1, vec3_t v2); +static inline vec3_t v3_sub(vec3_t v1, vec3_t v2); +static inline vec3_t v3_neg(vec3_t v); +static inline vec3_t v3_mul(vec3_t v1, vec3_t v2); +static inline vec3_t v3_scale(vec3_t v1, scalar_t s); +static inline scalar_t v3_dot(vec3_t v1, vec3_t v2); +static inline vec3_t v3_cross(vec3_t v1, vec3_t v2); +static inline scalar_t v3_length(vec3_t v); +static inline scalar_t v3_length_sq(vec3_t v); +static inline vec3_t v3_normalize(vec3_t v); +static inline vec3_t v3_transform(vec3_t v, mat4_t m); + +static inline vec3_t v3_rotate(vec3_t v, scalar_t x, scalar_t y, scalar_t z); +static inline vec3_t v3_rotate_axis(vec3_t v, scalar_t angle, scalar_t x, scalar_t y, scalar_t z); +static inline vec3_t v3_rotate_quat(vec3_t v, quat_t q); + +static inline vec3_t v3_reflect(vec3_t v, vec3_t n); + +static inline vec3_t v3_lerp(vec3_t v1, vec3_t v2, scalar_t t); + +/* C 4D vector functions */ +static inline vec4_t v4_cons(scalar_t x, scalar_t y, scalar_t z, scalar_t w); +static inline void v4_print(FILE *fp, vec4_t v); + +static inline vec4_t v4_add(vec4_t v1, vec4_t v2); +static inline vec4_t v4_sub(vec4_t v1, vec4_t v2); +static inline vec4_t v4_neg(vec4_t v); +static inline vec4_t v4_mul(vec4_t v1, vec4_t v2); +static inline vec4_t v4_scale(vec4_t v, scalar_t s); +static inline scalar_t v4_dot(vec4_t v1, vec4_t v2); +static inline scalar_t v4_length(vec4_t v); +static inline scalar_t v4_length_sq(vec4_t v); +static inline vec4_t v4_normalize(vec4_t v); +static inline vec4_t v4_transform(vec4_t v, mat4_t m); + +#ifdef __cplusplus +} /* extern "C" */ + +/* when included from C++ source files, also define the vector classes */ +#include + +/** 2D Vector */ +class Vector2 { +public: + scalar_t x, y; + + Vector2(scalar_t x = 0.0, scalar_t y = 0.0); + Vector2(const vec2_t &vec); + Vector2(const Vector3 &vec); + Vector2(const Vector4 &vec); + + inline scalar_t &operator [](int elem); + inline const scalar_t &operator [](int elem) const; + + inline scalar_t length() const; + inline scalar_t length_sq() const; + void normalize(); + Vector2 normalized() const; + + void transform(const Matrix3x3 &mat); + Vector2 transformed(const Matrix3x3 &mat) const; + + void rotate(scalar_t angle); + Vector2 rotated(scalar_t angle) const; + + Vector2 reflection(const Vector2 &normal) const; + Vector2 refraction(const Vector2 &normal, scalar_t src_ior, scalar_t dst_ior) const; +}; + +/* unary operations */ +inline Vector2 operator -(const Vector2 &vec); + +/* binary vector (op) vector operations */ +inline scalar_t dot_product(const Vector2 &v1, const Vector2 &v2); + +inline Vector2 operator +(const Vector2 &v1, const Vector2 &v2); +inline Vector2 operator -(const Vector2 &v1, const Vector2 &v2); +inline Vector2 operator *(const Vector2 &v1, const Vector2 &v2); +inline Vector2 operator /(const Vector2 &v1, const Vector2 &v2); +inline bool operator ==(const Vector2 &v1, const Vector2 &v2); + +inline void operator +=(Vector2 &v1, const Vector2 &v2); +inline void operator -=(Vector2 &v1, const Vector2 &v2); +inline void operator *=(Vector2 &v1, const Vector2 &v2); +inline void operator /=(Vector2 &v1, const Vector2 &v2); + +/* binary vector (op) scalar and scalar (op) vector operations */ +inline Vector2 operator +(const Vector2 &vec, scalar_t scalar); +inline Vector2 operator +(scalar_t scalar, const Vector2 &vec); +inline Vector2 operator -(const Vector2 &vec, scalar_t scalar); +inline Vector2 operator *(const Vector2 &vec, scalar_t scalar); +inline Vector2 operator *(scalar_t scalar, const Vector2 &vec); +inline Vector2 operator /(const Vector2 &vec, scalar_t scalar); + +inline void operator +=(Vector2 &vec, scalar_t scalar); +inline void operator -=(Vector2 &vec, scalar_t scalar); +inline void operator *=(Vector2 &vec, scalar_t scalar); +inline void operator /=(Vector2 &vec, scalar_t scalar); + +std::ostream &operator <<(std::ostream &out, const Vector2 &vec); + +inline Vector2 lerp(const Vector2 &a, const Vector2 &b, scalar_t t); +inline Vector2 catmull_rom_spline(const Vector2 &v0, const Vector2 &v1, + const Vector2 &v2, const Vector2 &v3, scalar_t t); + +/* 3D Vector */ +class Vector3 { +public: + scalar_t x, y, z; + + Vector3(scalar_t x = 0.0, scalar_t y = 0.0, scalar_t z = 0.0); + Vector3(const vec3_t &vec); + Vector3(const Vector2 &vec); + Vector3(const Vector4 &vec); + + inline scalar_t &operator [](int elem); + inline const scalar_t &operator [](int elem) const; + + inline scalar_t length() const; + inline scalar_t length_sq() const; + void normalize(); + Vector3 normalized() const; + + void transform(const Matrix3x3 &mat); + Vector3 transformed(const Matrix3x3 &mat) const; + void transform(const Matrix4x4 &mat); + Vector3 transformed(const Matrix4x4 &mat) const; + void transform(const Quaternion &quat); + Vector3 transformed(const Quaternion &quat) const; + + void rotate(const Vector3 &euler); + Vector3 rotated(const Vector3 &euler) const; + + Vector3 reflection(const Vector3 &normal) const; + Vector3 refraction(const Vector3 &normal, scalar_t src_ior, scalar_t dst_ior) const; + Vector3 refraction(const Vector3 &normal, scalar_t ior) const; +}; + +/* unary operations */ +inline Vector3 operator -(const Vector3 &vec); + +/* binary vector (op) vector operations */ +inline scalar_t dot_product(const Vector3 &v1, const Vector3 &v2); +inline Vector3 cross_product(const Vector3 &v1, const Vector3 &v2); + +inline Vector3 operator +(const Vector3 &v1, const Vector3 &v2); +inline Vector3 operator -(const Vector3 &v1, const Vector3 &v2); +inline Vector3 operator *(const Vector3 &v1, const Vector3 &v2); +inline Vector3 operator /(const Vector3 &v1, const Vector3 &v2); +inline bool operator ==(const Vector3 &v1, const Vector3 &v2); + +inline void operator +=(Vector3 &v1, const Vector3 &v2); +inline void operator -=(Vector3 &v1, const Vector3 &v2); +inline void operator *=(Vector3 &v1, const Vector3 &v2); +inline void operator /=(Vector3 &v1, const Vector3 &v2); + +/* binary vector (op) scalar and scalar (op) vector operations */ +inline Vector3 operator +(const Vector3 &vec, scalar_t scalar); +inline Vector3 operator +(scalar_t scalar, const Vector3 &vec); +inline Vector3 operator -(const Vector3 &vec, scalar_t scalar); +inline Vector3 operator *(const Vector3 &vec, scalar_t scalar); +inline Vector3 operator *(scalar_t scalar, const Vector3 &vec); +inline Vector3 operator /(const Vector3 &vec, scalar_t scalar); + +inline void operator +=(Vector3 &vec, scalar_t scalar); +inline void operator -=(Vector3 &vec, scalar_t scalar); +inline void operator *=(Vector3 &vec, scalar_t scalar); +inline void operator /=(Vector3 &vec, scalar_t scalar); + +std::ostream &operator <<(std::ostream &out, const Vector3 &vec); + +inline Vector3 lerp(const Vector3 &a, const Vector3 &b, scalar_t t); +inline Vector3 catmull_rom_spline(const Vector3 &v0, const Vector3 &v1, + const Vector3 &v2, const Vector3 &v3, scalar_t t); + +inline Vector3 faceforward(const Vector3 &v, const Vector3 &ref); + +/* 4D Vector */ +class Vector4 { +public: + scalar_t x, y, z, w; + + Vector4(scalar_t x = 0.0, scalar_t y = 0.0, scalar_t z = 0.0, scalar_t w = 0.0); + Vector4(const vec4_t &vec); + Vector4(const Vector2 &vec); + Vector4(const Vector3 &vec); + + inline scalar_t &operator [](int elem); + inline const scalar_t &operator [](int elem) const; + + inline scalar_t length() const; + inline scalar_t length_sq() const; + void normalize(); + Vector4 normalized() const; + + void transform(const Matrix4x4 &mat); + Vector4 transformed(const Matrix4x4 &mat) const; + + Vector4 reflection(const Vector4 &normal) const; + Vector4 refraction(const Vector4 &normal, scalar_t src_ior, scalar_t dst_ior) const; +}; + + +/* unary operations */ +inline Vector4 operator -(const Vector4 &vec); + +/* binary vector (op) vector operations */ +inline scalar_t dot_product(const Vector4 &v1, const Vector4 &v2); +inline Vector4 cross_product(const Vector4 &v1, const Vector4 &v2, const Vector4 &v3); + +inline Vector4 operator +(const Vector4 &v1, const Vector4 &v2); +inline Vector4 operator -(const Vector4 &v1, const Vector4 &v2); +inline Vector4 operator *(const Vector4 &v1, const Vector4 &v2); +inline Vector4 operator /(const Vector4 &v1, const Vector4 &v2); +inline bool operator ==(const Vector4 &v1, const Vector4 &v2); + +inline void operator +=(Vector4 &v1, const Vector4 &v2); +inline void operator -=(Vector4 &v1, const Vector4 &v2); +inline void operator *=(Vector4 &v1, const Vector4 &v2); +inline void operator /=(Vector4 &v1, const Vector4 &v2); + +/* binary vector (op) scalar and scalar (op) vector operations */ +inline Vector4 operator +(const Vector4 &vec, scalar_t scalar); +inline Vector4 operator +(scalar_t scalar, const Vector4 &vec); +inline Vector4 operator -(const Vector4 &vec, scalar_t scalar); +inline Vector4 operator *(const Vector4 &vec, scalar_t scalar); +inline Vector4 operator *(scalar_t scalar, const Vector4 &vec); +inline Vector4 operator /(const Vector4 &vec, scalar_t scalar); + +inline void operator +=(Vector4 &vec, scalar_t scalar); +inline void operator -=(Vector4 &vec, scalar_t scalar); +inline void operator *=(Vector4 &vec, scalar_t scalar); +inline void operator /=(Vector4 &vec, scalar_t scalar); + +std::ostream &operator <<(std::ostream &out, const Vector4 &vec); + +inline Vector4 lerp(const Vector4 &v0, const Vector4 &v1, scalar_t t); +inline Vector4 catmull_rom_spline(const Vector4 &v0, const Vector4 &v1, + const Vector4 &v2, const Vector4 &v3, scalar_t t); + +#endif /* __cplusplus */ + +#include "vector.inl" + +#endif /* VMATH_VECTOR_H_ */ diff -r 000000000000 -r f234630e38ff vmath/vector.inl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/vector.inl Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,766 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* C 2D vector functions */ +static inline vec2_t v2_cons(scalar_t x, scalar_t y) +{ + vec2_t v; + v.x = x; + v.y = y; + return v; +} + +static inline void v2_print(FILE *fp, vec2_t v) +{ + fprintf(fp, "[ %.4f %.4f ]", v.x, v.y); +} + +static inline vec2_t v2_add(vec2_t v1, vec2_t v2) +{ + vec2_t res; + res.x = v1.x + v2.x; + res.y = v1.y + v2.y; + return res; +} + +static inline vec2_t v2_sub(vec2_t v1, vec2_t v2) +{ + vec2_t res; + res.x = v1.x - v2.x; + res.y = v1.y - v2.y; + return res; +} + +static inline vec2_t v2_scale(vec2_t v, scalar_t s) +{ + vec2_t res; + res.x = v.x * s; + res.y = v.y * s; + return res; +} + +static inline scalar_t v2_dot(vec2_t v1, vec2_t v2) +{ + return v1.x * v2.x + v1.y * v2.y; +} + +static inline scalar_t v2_length(vec2_t v) +{ + return sqrt(v.x * v.x + v.y * v.y); +} + +static inline scalar_t v2_length_sq(vec2_t v) +{ + return v.x * v.x + v.y * v.y; +} + +static inline vec2_t v2_normalize(vec2_t v) +{ + scalar_t len = (scalar_t)sqrt(v.x * v.x + v.y * v.y); + v.x /= len; + v.y /= len; + return v; +} + +static inline vec2_t v2_lerp(vec2_t v1, vec2_t v2, scalar_t t) +{ + vec2_t res; + res.x = v1.x + (v2.x - v1.x) * t; + res.y = v1.y + (v2.y - v1.y) * t; + return res; +} + + +/* C 3D vector functions */ +static inline vec3_t v3_cons(scalar_t x, scalar_t y, scalar_t z) +{ + vec3_t v; + v.x = x; + v.y = y; + v.z = z; + return v; +} + +static inline void v3_print(FILE *fp, vec3_t v) +{ + fprintf(fp, "[ %.4f %.4f %.4f ]", v.x, v.y, v.z); +} + +static inline vec3_t v3_add(vec3_t v1, vec3_t v2) +{ + v1.x += v2.x; + v1.y += v2.y; + v1.z += v2.z; + return v1; +} + +static inline vec3_t v3_sub(vec3_t v1, vec3_t v2) +{ + v1.x -= v2.x; + v1.y -= v2.y; + v1.z -= v2.z; + return v1; +} + +static inline vec3_t v3_neg(vec3_t v) +{ + v.x = -v.x; + v.y = -v.y; + v.z = -v.z; + return v; +} + +static inline vec3_t v3_mul(vec3_t v1, vec3_t v2) +{ + v1.x *= v2.x; + v1.y *= v2.y; + v1.z *= v2.z; + return v1; +} + +static inline vec3_t v3_scale(vec3_t v1, scalar_t s) +{ + v1.x *= s; + v1.y *= s; + v1.z *= s; + return v1; +} + +static inline scalar_t v3_dot(vec3_t v1, vec3_t v2) +{ + return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; +} + +static inline vec3_t v3_cross(vec3_t v1, vec3_t v2) +{ + vec3_t v; + v.x = v1.y * v2.z - v1.z * v2.y; + v.y = v1.z * v2.x - v1.x * v2.z; + v.z = v1.x * v2.y - v1.y * v2.x; + return v; +} + +static inline scalar_t v3_length(vec3_t v) +{ + return sqrt(v.x * v.x + v.y * v.y + v.z * v.z); +} + +static inline scalar_t v3_length_sq(vec3_t v) +{ + return v.x * v.x + v.y * v.y + v.z * v.z; +} + +static inline vec3_t v3_normalize(vec3_t v) +{ + scalar_t len = sqrt(v.x * v.x + v.y * v.y + v.z * v.z); + v.x /= len; + v.y /= len; + v.z /= len; + return v; +} + +static inline vec3_t v3_transform(vec3_t v, mat4_t m) +{ + vec3_t res; + res.x = m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3]; + res.y = m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3]; + res.z = m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3]; + return res; +} + +static inline vec3_t v3_rotate(vec3_t v, scalar_t x, scalar_t y, scalar_t z) +{ + void m4_rotate(mat4_t, scalar_t, scalar_t, scalar_t); + + mat4_t m = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}; + m4_rotate(m, x, y, z); + return v3_transform(v, m); +} + +static inline vec3_t v3_rotate_axis(vec3_t v, scalar_t angle, scalar_t x, scalar_t y, scalar_t z) +{ + void m4_rotate_axis(mat4_t, scalar_t, scalar_t, scalar_t, scalar_t); + + mat4_t m = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}; + m4_rotate_axis(m, angle, x, y, z); + return v3_transform(v, m); +} + +static inline vec3_t v3_rotate_quat(vec3_t v, quat_t q) +{ + quat_t quat_rotate_quat(quat_t, quat_t); + + quat_t vq = v4_cons(v.x, v.y, v.z, 0.0); + quat_t res = quat_rotate_quat(vq, q); + return v3_cons(res.x, res.y, res.z); +} + +static inline vec3_t v3_reflect(vec3_t v, vec3_t n) +{ + scalar_t dot = v3_dot(v, n); + return v3_sub(v3_scale(n, dot * 2.0), v); +} + +static inline vec3_t v3_lerp(vec3_t v1, vec3_t v2, scalar_t t) +{ + v1.x += (v2.x - v1.x) * t; + v1.y += (v2.y - v1.y) * t; + v1.z += (v2.z - v1.z) * t; + return v1; +} + +/* C 4D vector functions */ +static inline vec4_t v4_cons(scalar_t x, scalar_t y, scalar_t z, scalar_t w) +{ + vec4_t v; + v.x = x; + v.y = y; + v.z = z; + v.w = w; + return v; +} + +static inline void v4_print(FILE *fp, vec4_t v) +{ + fprintf(fp, "[ %.4f %.4f %.4f %.4f ]", v.x, v.y, v.z, v.w); +} + +static inline vec4_t v4_add(vec4_t v1, vec4_t v2) +{ + v1.x += v2.x; + v1.y += v2.y; + v1.z += v2.z; + v1.w += v2.w; + return v1; +} + +static inline vec4_t v4_sub(vec4_t v1, vec4_t v2) +{ + v1.x -= v2.x; + v1.y -= v2.y; + v1.z -= v2.z; + v1.w -= v2.w; + return v1; +} + +static inline vec4_t v4_neg(vec4_t v) +{ + v.x = -v.x; + v.y = -v.y; + v.z = -v.z; + v.w = -v.w; + return v; +} + +static inline vec4_t v4_mul(vec4_t v1, vec4_t v2) +{ + v1.x *= v2.x; + v1.y *= v2.y; + v1.z *= v2.z; + v1.w *= v2.w; + return v1; +} + +static inline vec4_t v4_scale(vec4_t v, scalar_t s) +{ + v.x *= s; + v.y *= s; + v.z *= s; + v.w *= s; + return v; +} + +static inline scalar_t v4_dot(vec4_t v1, vec4_t v2) +{ + return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w; +} + +static inline scalar_t v4_length(vec4_t v) +{ + return sqrt(v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w); +} + +static inline scalar_t v4_length_sq(vec4_t v) +{ + return v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w; +} + +static inline vec4_t v4_normalize(vec4_t v) +{ + scalar_t len = sqrt(v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w); + v.x /= len; + v.y /= len; + v.z /= len; + v.w /= len; + return v; +} + +static inline vec4_t v4_transform(vec4_t v, mat4_t m) +{ + vec4_t res; + res.x = m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3] * v.w; + res.y = m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3] * v.w; + res.z = m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3] * v.w; + res.w = m[3][0] * v.x + m[3][1] * v.y + m[3][2] * v.z + m[3][3] * v.w; + return res; +} + +#ifdef __cplusplus +} /* extern "C" */ + + +/* --------------- C++ part -------------- */ + +inline scalar_t &Vector2::operator [](int elem) { + return elem ? y : x; +} + +inline const scalar_t &Vector2::operator [](int elem) const { + return elem ? y : x; +} + +inline Vector2 operator -(const Vector2 &vec) { + return Vector2(-vec.x, -vec.y); +} + +inline scalar_t dot_product(const Vector2 &v1, const Vector2 &v2) { + return v1.x * v2.x + v1.y * v2.y; +} + +inline Vector2 operator +(const Vector2 &v1, const Vector2 &v2) { + return Vector2(v1.x + v2.x, v1.y + v2.y); +} + +inline Vector2 operator -(const Vector2 &v1, const Vector2 &v2) { + return Vector2(v1.x - v2.x, v1.y - v2.y); +} + +inline Vector2 operator *(const Vector2 &v1, const Vector2 &v2) { + return Vector2(v1.x * v2.x, v1.y * v2.y); +} + +inline Vector2 operator /(const Vector2 &v1, const Vector2 &v2) { + return Vector2(v1.x / v2.x, v1.y / v2.y); +} + +inline bool operator ==(const Vector2 &v1, const Vector2 &v2) { + return (fabs(v1.x - v2.x) < XSMALL_NUMBER) && (fabs(v1.y - v2.x) < XSMALL_NUMBER); +} + +inline void operator +=(Vector2 &v1, const Vector2 &v2) { + v1.x += v2.x; + v1.y += v2.y; +} + +inline void operator -=(Vector2 &v1, const Vector2 &v2) { + v1.x -= v2.x; + v1.y -= v2.y; +} + +inline void operator *=(Vector2 &v1, const Vector2 &v2) { + v1.x *= v2.x; + v1.y *= v2.y; +} + +inline void operator /=(Vector2 &v1, const Vector2 &v2) { + v1.x /= v2.x; + v1.y /= v2.y; +} + +inline Vector2 operator +(const Vector2 &vec, scalar_t scalar) { + return Vector2(vec.x + scalar, vec.y + scalar); +} + +inline Vector2 operator +(scalar_t scalar, const Vector2 &vec) { + return Vector2(vec.x + scalar, vec.y + scalar); +} + +inline Vector2 operator -(scalar_t scalar, const Vector2 &vec) { + return Vector2(vec.x - scalar, vec.y - scalar); +} + +inline Vector2 operator *(const Vector2 &vec, scalar_t scalar) { + return Vector2(vec.x * scalar, vec.y * scalar); +} + +inline Vector2 operator *(scalar_t scalar, const Vector2 &vec) { + return Vector2(vec.x * scalar, vec.y * scalar); +} + +inline Vector2 operator /(const Vector2 &vec, scalar_t scalar) { + return Vector2(vec.x / scalar, vec.y / scalar); +} + +inline void operator +=(Vector2 &vec, scalar_t scalar) { + vec.x += scalar; + vec.y += scalar; +} + +inline void operator -=(Vector2 &vec, scalar_t scalar) { + vec.x -= scalar; + vec.y -= scalar; +} + +inline void operator *=(Vector2 &vec, scalar_t scalar) { + vec.x *= scalar; + vec.y *= scalar; +} + +inline void operator /=(Vector2 &vec, scalar_t scalar) { + vec.x /= scalar; + vec.y /= scalar; +} + +inline scalar_t Vector2::length() const { + return sqrt(x*x + y*y); +} + +inline scalar_t Vector2::length_sq() const { + return x*x + y*y; +} + +inline Vector2 lerp(const Vector2 &a, const Vector2 &b, scalar_t t) +{ + return a + (b - a) * t; +} + +inline Vector2 catmull_rom_spline(const Vector2 &v0, const Vector2 &v1, + const Vector2 &v2, const Vector2 &v3, scalar_t t) +{ + scalar_t spline(scalar_t, scalar_t, scalar_t, scalar_t, scalar_t); + scalar_t x = spline(v0.x, v1.x, v2.x, v3.x, t); + scalar_t y = spline(v0.y, v1.y, v2.y, v3.y, t); + return Vector2(x, y); +} + + +/* ------------- Vector3 -------------- */ + +inline scalar_t &Vector3::operator [](int elem) { + return elem ? (elem == 1 ? y : z) : x; +} + +inline const scalar_t &Vector3::operator [](int elem) const { + return elem ? (elem == 1 ? y : z) : x; +} + +/* unary operations */ +inline Vector3 operator -(const Vector3 &vec) { + return Vector3(-vec.x, -vec.y, -vec.z); +} + +/* binary vector (op) vector operations */ +inline scalar_t dot_product(const Vector3 &v1, const Vector3 &v2) { + return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; +} + +inline Vector3 cross_product(const Vector3 &v1, const Vector3 &v2) { + return Vector3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); +} + + +inline Vector3 operator +(const Vector3 &v1, const Vector3 &v2) { + return Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); +} + +inline Vector3 operator -(const Vector3 &v1, const Vector3 &v2) { + return Vector3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); +} + +inline Vector3 operator *(const Vector3 &v1, const Vector3 &v2) { + return Vector3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z); +} + +inline Vector3 operator /(const Vector3 &v1, const Vector3 &v2) { + return Vector3(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z); +} + +inline bool operator ==(const Vector3 &v1, const Vector3 &v2) { + return (fabs(v1.x - v2.x) < XSMALL_NUMBER) && (fabs(v1.y - v2.y) < XSMALL_NUMBER) && (fabs(v1.z - v2.z) < XSMALL_NUMBER); +} + +inline void operator +=(Vector3 &v1, const Vector3 &v2) { + v1.x += v2.x; + v1.y += v2.y; + v1.z += v2.z; +} + +inline void operator -=(Vector3 &v1, const Vector3 &v2) { + v1.x -= v2.x; + v1.y -= v2.y; + v1.z -= v2.z; +} + +inline void operator *=(Vector3 &v1, const Vector3 &v2) { + v1.x *= v2.x; + v1.y *= v2.y; + v1.z *= v2.z; +} + +inline void operator /=(Vector3 &v1, const Vector3 &v2) { + v1.x /= v2.x; + v1.y /= v2.y; + v1.z /= v2.z; +} +/* binary vector (op) scalar and scalar (op) vector operations */ +inline Vector3 operator +(const Vector3 &vec, scalar_t scalar) { + return Vector3(vec.x + scalar, vec.y + scalar, vec.z + scalar); +} + +inline Vector3 operator +(scalar_t scalar, const Vector3 &vec) { + return Vector3(vec.x + scalar, vec.y + scalar, vec.z + scalar); +} + +inline Vector3 operator -(const Vector3 &vec, scalar_t scalar) { + return Vector3(vec.x - scalar, vec.y - scalar, vec.z - scalar); +} + +inline Vector3 operator *(const Vector3 &vec, scalar_t scalar) { + return Vector3(vec.x * scalar, vec.y * scalar, vec.z * scalar); +} + +inline Vector3 operator *(scalar_t scalar, const Vector3 &vec) { + return Vector3(vec.x * scalar, vec.y * scalar, vec.z * scalar); +} + +inline Vector3 operator /(const Vector3 &vec, scalar_t scalar) { + return Vector3(vec.x / scalar, vec.y / scalar, vec.z / scalar); +} + +inline void operator +=(Vector3 &vec, scalar_t scalar) { + vec.x += scalar; + vec.y += scalar; + vec.z += scalar; +} + +inline void operator -=(Vector3 &vec, scalar_t scalar) { + vec.x -= scalar; + vec.y -= scalar; + vec.z -= scalar; +} + +inline void operator *=(Vector3 &vec, scalar_t scalar) { + vec.x *= scalar; + vec.y *= scalar; + vec.z *= scalar; +} + +inline void operator /=(Vector3 &vec, scalar_t scalar) { + vec.x /= scalar; + vec.y /= scalar; + vec.z /= scalar; +} + +inline scalar_t Vector3::length() const { + return sqrt(x*x + y*y + z*z); +} +inline scalar_t Vector3::length_sq() const { + return x*x + y*y + z*z; +} + +inline Vector3 lerp(const Vector3 &a, const Vector3 &b, scalar_t t) { + return a + (b - a) * t; +} + +inline Vector3 catmull_rom_spline(const Vector3 &v0, const Vector3 &v1, + const Vector3 &v2, const Vector3 &v3, scalar_t t) +{ + scalar_t spline(scalar_t, scalar_t, scalar_t, scalar_t, scalar_t); + scalar_t x = spline(v0.x, v1.x, v2.x, v3.x, t); + scalar_t y = spline(v0.y, v1.y, v2.y, v3.y, t); + scalar_t z = spline(v0.z, v1.z, v2.z, v3.z, t); + return Vector3(x, y, z); +} + +inline Vector3 faceforward(const Vector3 &v, const Vector3 &ref) +{ + return dot_product(v, ref) >= 0.0 ? -v : v; +} + +/* ----------- Vector4 ----------------- */ + +inline scalar_t &Vector4::operator [](int elem) { + return elem ? (elem == 1 ? y : (elem == 2 ? z : w)) : x; +} + +inline const scalar_t &Vector4::operator [](int elem) const { + return elem ? (elem == 1 ? y : (elem == 2 ? z : w)) : x; +} + +inline Vector4 operator -(const Vector4 &vec) { + return Vector4(-vec.x, -vec.y, -vec.z, -vec.w); +} + +inline scalar_t dot_product(const Vector4 &v1, const Vector4 &v2) { + return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w; +} + +inline Vector4 cross_product(const Vector4 &v1, const Vector4 &v2, const Vector4 &v3) { + scalar_t a, b, c, d, e, f; /* Intermediate Values */ + Vector4 result; + + /* Calculate intermediate values. */ + a = (v2.x * v3.y) - (v2.y * v3.x); + b = (v2.x * v3.z) - (v2.z * v3.x); + c = (v2.x * v3.w) - (v2.w * v3.x); + d = (v2.y * v3.z) - (v2.z * v3.y); + e = (v2.y * v3.w) - (v2.w * v3.y); + f = (v2.z * v3.w) - (v2.w * v3.z); + + /* Calculate the result-vector components. */ + result.x = (v1.y * f) - (v1.z * e) + (v1.w * d); + result.y = - (v1.x * f) + (v1.z * c) - (v1.w * b); + result.z = (v1.x * e) - (v1.y * c) + (v1.w * a); + result.w = - (v1.x * d) + (v1.y * b) - (v1.z * a); + return result; +} + +inline Vector4 operator +(const Vector4 &v1, const Vector4 &v2) { + return Vector4(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z, v1.w + v2.w); +} + +inline Vector4 operator -(const Vector4 &v1, const Vector4 &v2) { + return Vector4(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z, v1.w - v2.w); +} + +inline Vector4 operator *(const Vector4 &v1, const Vector4 &v2) { + return Vector4(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z, v1.w * v2.w); +} + +inline Vector4 operator /(const Vector4 &v1, const Vector4 &v2) { + return Vector4(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z, v1.w / v2.w); +} + +inline bool operator ==(const Vector4 &v1, const Vector4 &v2) { + return (fabs(v1.x - v2.x) < XSMALL_NUMBER) && + (fabs(v1.y - v2.y) < XSMALL_NUMBER) && + (fabs(v1.z - v2.z) < XSMALL_NUMBER) && + (fabs(v1.w - v2.w) < XSMALL_NUMBER); +} + +inline void operator +=(Vector4 &v1, const Vector4 &v2) { + v1.x += v2.x; + v1.y += v2.y; + v1.z += v2.z; + v1.w += v2.w; +} + +inline void operator -=(Vector4 &v1, const Vector4 &v2) { + v1.x -= v2.x; + v1.y -= v2.y; + v1.z -= v2.z; + v1.w -= v2.w; +} + +inline void operator *=(Vector4 &v1, const Vector4 &v2) { + v1.x *= v2.x; + v1.y *= v2.y; + v1.z *= v2.z; + v1.w *= v2.w; +} + +inline void operator /=(Vector4 &v1, const Vector4 &v2) { + v1.x /= v2.x; + v1.y /= v2.y; + v1.z /= v2.z; + v1.w /= v2.w; +} + +/* binary vector (op) scalar and scalar (op) vector operations */ +inline Vector4 operator +(const Vector4 &vec, scalar_t scalar) { + return Vector4(vec.x + scalar, vec.y + scalar, vec.z + scalar, vec.w + scalar); +} + +inline Vector4 operator +(scalar_t scalar, const Vector4 &vec) { + return Vector4(vec.x + scalar, vec.y + scalar, vec.z + scalar, vec.w + scalar); +} + +inline Vector4 operator -(const Vector4 &vec, scalar_t scalar) { + return Vector4(vec.x - scalar, vec.y - scalar, vec.z - scalar, vec.w - scalar); +} + +inline Vector4 operator *(const Vector4 &vec, scalar_t scalar) { + return Vector4(vec.x * scalar, vec.y * scalar, vec.z * scalar, vec.w * scalar); +} + +inline Vector4 operator *(scalar_t scalar, const Vector4 &vec) { + return Vector4(vec.x * scalar, vec.y * scalar, vec.z * scalar, vec.w * scalar); +} + +inline Vector4 operator /(const Vector4 &vec, scalar_t scalar) { + return Vector4(vec.x / scalar, vec.y / scalar, vec.z / scalar, vec.w / scalar); +} + +inline void operator +=(Vector4 &vec, scalar_t scalar) { + vec.x += scalar; + vec.y += scalar; + vec.z += scalar; + vec.w += scalar; +} + +inline void operator -=(Vector4 &vec, scalar_t scalar) { + vec.x -= scalar; + vec.y -= scalar; + vec.z -= scalar; + vec.w -= scalar; +} + +inline void operator *=(Vector4 &vec, scalar_t scalar) { + vec.x *= scalar; + vec.y *= scalar; + vec.z *= scalar; + vec.w *= scalar; +} + +inline void operator /=(Vector4 &vec, scalar_t scalar) { + vec.x /= scalar; + vec.y /= scalar; + vec.z /= scalar; + vec.w /= scalar; +} + +inline scalar_t Vector4::length() const { + return sqrt(x*x + y*y + z*z + w*w); +} +inline scalar_t Vector4::length_sq() const { + return x*x + y*y + z*z + w*w; +} + +inline Vector4 lerp(const Vector4 &v0, const Vector4 &v1, scalar_t t) +{ + return v0 + (v1 - v0) * t; +} + +inline Vector4 catmull_rom_spline(const Vector4 &v0, const Vector4 &v1, + const Vector4 &v2, const Vector4 &v3, scalar_t t) +{ + scalar_t spline(scalar_t, scalar_t, scalar_t, scalar_t, scalar_t); + scalar_t x = spline(v0.x, v1.x, v2.x, v3.x, t); + scalar_t y = spline(v0.y, v1.y, v2.y, v3.y, t); + scalar_t z = spline(v0.z, v1.z, v2.z, v3.z, t); + scalar_t w = spline(v0.w, v1.w, v2.w, v3.w, t); + return Vector4(x, y, z, w); +} + +#endif /* __cplusplus */ diff -r 000000000000 -r f234630e38ff vmath/vmath.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/vmath.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,92 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2013 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 VMATH_H_ +#define VMATH_H_ + +#include +#include "vmath_types.h" + +#ifndef M_PI +#define M_PI PI +#endif + +#ifndef M_E +#define M_E 2.718281828459045 +#endif + +#define PI 3.141592653589793 +#define HALF_PI 1.570796326794897 +#define QUARTER_PI 0.785398163397448 +#define TWO_PI 6.283185307179586 + + +#define RAD_TO_DEG(a) ((((scalar_t)a) * 360.0) / TWO_PI) +#define DEG_TO_RAD(a) (((scalar_t)a) * (PI / 180.0)) + +#define SQ(x) ((x) * (x)) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef __GNUC__ +#define round(x) ((x) >= 0 ? (x) + 0.5 : (x) - 0.5) +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +static inline scalar_t smoothstep(float a, float b, float x); + +static inline scalar_t frand(scalar_t range); +static inline vec3_t sphrand(scalar_t rad); + +scalar_t integral(scalar_t (*f)(scalar_t), scalar_t low, scalar_t high, int samples); +scalar_t gaussian(scalar_t x, scalar_t mean, scalar_t sdev); + +static inline scalar_t lerp(scalar_t a, scalar_t b, scalar_t t); + +scalar_t bspline(scalar_t a, scalar_t b, scalar_t c, scalar_t d, scalar_t t); +scalar_t spline(scalar_t a, scalar_t b, scalar_t c, scalar_t d, scalar_t t); +scalar_t bezier(scalar_t a, scalar_t b, scalar_t c, scalar_t d, scalar_t t); + +scalar_t noise1(scalar_t x); +scalar_t noise2(scalar_t x, scalar_t y); +scalar_t noise3(scalar_t x, scalar_t y, scalar_t z); + +scalar_t fbm1(scalar_t x, int octaves); +scalar_t fbm2(scalar_t x, scalar_t y, int octaves); +scalar_t fbm3(scalar_t x, scalar_t y, scalar_t z, int octaves); + +scalar_t turbulence1(scalar_t x, int octaves); +scalar_t turbulence2(scalar_t x, scalar_t y, int octaves); +scalar_t turbulence3(scalar_t x, scalar_t y, scalar_t z, int octaves); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#include "vmath.inl" + +#include "vector.h" +#include "matrix.h" +#include "quat.h" +#include "ray.h" + +#endif /* VMATH_H_ */ diff -r 000000000000 -r f234630e38ff vmath/vmath.inl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/vmath.inl Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,56 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 + +static inline scalar_t smoothstep(float a, float b, float x) +{ + if(x < a) return 0.0; + if(x >= b) return 1.0; + + x = (x - a) / (b - a); + return x * x * (3.0 - 2.0 * x); +} + +/** Generates a random number in [0, range) */ +static inline scalar_t frand(scalar_t range) +{ + return range * (scalar_t)rand() / (scalar_t)RAND_MAX; +} + +/** Generates a random vector on the surface of a sphere */ +static inline vec3_t sphrand(scalar_t rad) +{ + scalar_t u = (scalar_t)rand() / RAND_MAX; + scalar_t v = (scalar_t)rand() / RAND_MAX; + + scalar_t theta = 2.0 * M_PI * u; + scalar_t phi = acos(2.0 * v - 1.0); + + vec3_t res; + res.x = rad * cos(theta) * sin(phi); + res.y = rad * sin(theta) * sin(phi); + res.z = rad * cos(phi); + return res; +} + +/** linear interpolation */ +static inline scalar_t lerp(scalar_t a, scalar_t b, scalar_t t) +{ + return a + (b - a) * t; +} diff -r 000000000000 -r f234630e38ff vmath/vmath_config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/vmath_config.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,19 @@ +#ifndef VMATH_CONFIG_H_ +#define VMATH_CONFIG_H_ + +#if (__STDC_VERSION__ < 199999) +#if defined(__GNUC__) || defined(_MSC_VER) +#define inline __inline +#else +#define inline + +#ifdef VECTOR_H_ +#warning "compiling vector operations without inline, performance might suffer" +#endif /* VECTOR_H_ */ + +#endif /* gcc/msvc */ +#endif /* not C99 */ + +#define SINGLE_PRECISION_MATH + +#endif /* VMATH_CONFIG_H_ */ diff -r 000000000000 -r f234630e38ff vmath/vmath_types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmath/vmath_types.h Sun Nov 09 13:03:36 2014 +0200 @@ -0,0 +1,58 @@ +/* +libvmath - a vector math library +Copyright (C) 2004-2011 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 VMATH_TYPES_H_ +#define VMATH_TYPES_H_ + +#include "vmath_config.h" + +#define SMALL_NUMBER 1.e-4 +#define XSMALL_NUMBER 1.e-8 +#define ERROR_MARGIN 1.e-6 + + +#ifdef SINGLE_PRECISION_MATH +typedef float scalar_t; +#else +typedef double scalar_t; +#endif /* floating point precision */ + +/* vectors */ +typedef struct { scalar_t x, y; } vec2_t; +typedef struct { scalar_t x, y, z; } vec3_t; +typedef struct { scalar_t x, y, z, w; } vec4_t; + +/* quaternions */ +typedef vec4_t quat_t; + +/* matrices */ +typedef scalar_t mat3_t[3][3]; +typedef scalar_t mat4_t[4][4]; + + +#ifdef __cplusplus +class Vector2; +class Vector3; +class Vector4; +class Quaternion; +class Matrix3x3; +class Matrix4x4; +class SphVector; +#endif /* __cplusplus */ + +#endif /* VMATH_TYPES_H_ */