star-enclosures-3d

Extract enclosures from 3D geometry
git clone git://git.meso-star.fr/star-enclosures-3d.git
Log | Files | Refs | README | LICENSE

commit ea96ad9c852b427814621b68fc30d811e8210b87
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon, 12 Feb 2018 17:23:24 +0100

Initial commit.

Diffstat:
ACOPYING | 674+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 29+++++++++++++++++++++++++++++
Acmake/CMakeLists.txt | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc.h | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_descriptor.c | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_descriptor_c.h | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_device.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_device_c.h | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_enclosure.c | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_enclosure_c.h | 37+++++++++++++++++++++++++++++++++++++
Asrc/senc_enclosure_data.h | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_internal_types.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_s3d_wrapper.h | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_scene.c | 289++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_scene_analyze.c | 1097+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_scene_analyze_c.h | 319+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/senc_scene_c.h | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_cube_behind_cube.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_cube_in_cube.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_descriptor.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_device.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_enclosure.c | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_sample_enclosure.c | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_scene.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_utils.h | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
25 files changed, 4858 insertions(+), 0 deletions(-)

diff --git a/COPYING b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 <http://www.gnu.org/licenses/>. + +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: + + <program> Copyright (C) <year> <name of author> + 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 +<http://www.gnu.org/licenses/>. + + 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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/README.md b/README.md @@ -0,0 +1,29 @@ +# StarEnclosures + +The purpose of this library is to extract enclosures from raw geometry. An +enclosure is a set of triangles enclosing a given volume. The library manages +vertices and triangles duplicates, easing the scene definition process. It also +checks some coherency properties, most noticeably the enclosed medium unicity. + +## How to build + +StarEnclosures relies on the [CMake](http://www.cmake.org) and the +[RCMake](https://gitlab.com/vaplv/rcmake/) package to build. It also depends on +the [RSys](https://gitlab.com/vaplv/rsys/) and +[Star-3D](https://gitlab.com/meso-star/star-3d/) and +[Star-SP](https://gitlab.com/meso-star/star-sp/) libraries, the later being +necessary only when building tests. + +First ensure that CMake and a C compiler are installed on your system. Then +install the RCMake package as well as all the aforementioned prerequisites. +Finally generate the project from the `cmake/CMakeLists.txt` file by appending +to the `CMAKE_PREFIX_PATH` variable the install directories of its +dependencies. + +## License + +StarEnclosures is Copyright (C) |Meso|Star> 2018 (<contact@meso-star.com>). +It is free software released under the GPLv3+ license. You are welcome to +redistribute it under certain conditions; refer to the COPYING files for +details. + diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -0,0 +1,133 @@ +# Copyright (C) |Meso|Star> 2016-2018 +# +# 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 <http://www.gnu.org/licenses/>. + +cmake_minimum_required(VERSION 3.0) +project(Star-Enclosures C) +enable_testing() + +set(SENC_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) +option(NO_TEST "Do not build tests" OFF) + +################################################################################ +# Check dependencies +################################################################################ +find_package(RCMake 0.4 REQUIRED) +find_package(Star3D 0.5 REQUIRED) +find_package(RSys 0.6.1 REQUIRED) + +if(NOT NO_TEST) +find_package(StarSP 0.7 REQUIRED) +endif() + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) +include(rcmake) +include(rcmake_runtime) + +################################################################################ +# Configure and define targets +################################################################################ +set(VERSION_MAJOR 0) +set(VERSION_MINOR 1) +set(VERSION_PATCH 0) +set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +set(SENC_FILES_SRC + senc_descriptor.c + senc_device.c + senc_enclosure.c + senc_scene.c + senc_scene_analyze.c) + +set(SENC_FILES_INC_API + senc.h + senc_s3d_wrapper.h) + +set(SENC_FILES_INC + senc_descriptor_c.h + senc_device_c.h + senc_enclosure_c.h + senc_enclosure_data.h + senc_internal_types.h + senc_scene_c.h + senc_scene_analyze_c.h) + +set(SENC_FILES_DOC COPYING README.md) + +# Prepend each file by `SENC_SOURCE_DIR' +rcmake_prepend_path(SENC_FILES_SRC ${SENC_SOURCE_DIR}) +rcmake_prepend_path(SENC_FILES_INC ${SENC_SOURCE_DIR}) +rcmake_prepend_path(SENC_FILES_INC_API ${SENC_SOURCE_DIR}) +rcmake_prepend_path(SENC_FILES_DOC ${PROJECT_SOURCE_DIR}/../) + +add_library(senc SHARED + ${SENC_FILES_SRC} + ${SENC_FILES_INC} + ${SENC_FILES_INC_API}) +target_link_libraries(senc RSys Star3D) + +set_target_properties(senc PROPERTIES + DEFINE_SYMBOL SENC_SHARED_BUILD + VERSION ${VERSION} + SOVERSION ${VERSION_MAJOR}) +rcmake_copy_runtime_libraries(senc) + +if(CMAKE_COMPILER_IS_GNUCC) + set_target_properties(senc PROPERTIES LINK_FLAGS "-lm") +endif() + +rcmake_setup_devel(senc StarEnc ${VERSION} senc_version.h) + +################################################################################ +# Add tests +################################################################################ +if(NOT NO_TEST) + function(build_test _name) + add_executable(${_name} + ${SENC_SOURCE_DIR}/test_senc_utils.h + ${SENC_SOURCE_DIR}/${_name}.c) + target_link_libraries(${_name} RSys senc) + endfunction() + + function(register_test _name) + add_test(${_name} ${ARGN}) + rcmake_set_test_runtime_dirs(${_name} _runtime_dirs) + endfunction() + + function(new_test _name) + build_test(${_name}) + register_test(${_name} ${_name}) + endfunction() + + new_test(test_senc_cube_behind_cube) + new_test(test_senc_cube_in_cube) + new_test(test_senc_descriptor) + new_test(test_senc_device) + new_test(test_senc_enclosure) + new_test(test_senc_sample_enclosure) + new_test(test_senc_scene) + + target_link_libraries(test_senc_sample_enclosure StarSP) + rcmake_copy_runtime_libraries(test_senc_sample_enclosure) +endif() + +################################################################################ +# Define output & install directories +################################################################################ +install(TARGETS senc + ARCHIVE DESTINATION bin + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) +install(FILES ${SENC_FILES_INC_API} DESTINATION include/) +install(FILES ${SENC_FILES_DOC} DESTINATION share/doc/star-enc) diff --git a/src/senc.h b/src/senc.h @@ -0,0 +1,222 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_H +#define SENC_H + +#include <rsys/rsys.h> + +/* Library symbol management */ +#if defined(SENC_SHARED_BUILD) + #define SENC_API extern EXPORT_SYM +#elif defined(SENC_STATIC_BUILD) + #define SENC_API extern LOCAL_SYM +#else /* Use shared library */ + #define SENC_API extern IMPORT_SYM +#endif + +/* Helper macro that asserts if the invocation of the StarEnc function `Func' + * returns an error. One should use this macro on StarEnc function calls for + * which no explicit error checking is performed. */ +#ifndef NDEBUG + #define SENC(Func) ASSERT(senc_ ## Func == RES_OK) +#else + #define SENC(Func) senc_ ## Func +#endif + +/* Syntactic sugar used to inform the library that it can use as many threads + * as CPU cores */ +#define SENC_NTHREADS_DEFAULT (~0u) + +/* Forward declaration of external opaque data types */ +struct logger; +struct mem_allocator; + +/* Forward declaration of StarEnclosures opaque data types. These data types + * are ref counted. Once created with the appropriated `senc_<TYPE>_create' + * function, the caller implicitly owns the created data, i.e. its reference + * counter is set to 1. The senc_<TYPE>_ref_<get|put> functions get or release + * a reference on the data, i.e. they increment or decrement the reference + * counter, respectively. When this counter reaches 0, the object is silently + * destroyed and cannot be used anymore. */ +struct senc_descriptor; +struct senc_device; +struct senc_scene; +struct senc_enclosure; + +/* Enclosure header type */ +struct enclosure_header { + /* The ID of the enclosure; guaranteed to be compact */ + unsigned enclosure_id; + /* Number of triangles; a triangle can be accounted for twice, once by side */ + unsigned triangle_count; + /* Number of triangles; a triangle cannot be accounted for twice */ + unsigned unique_triangle_count; + /* Number of vertices */ + unsigned vertices_count; + /* The medium in the enclosure */ + unsigned enclosed_medium; + /* Is the enclosure infinite? */ + char is_infinite; +}; + +BEGIN_DECLS + +/******************************************************************************* + * StarEnclosures device. It is an handle toward the StarEnc library. + * It manages the lib resources. + ******************************************************************************/ +SENC_API res_T +senc_device_create + (struct logger* logger, /* May be NULL <=> use default logger */ + struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ + const unsigned nthreads_hint, /* Hint on the number of threads to use (UNUSED) */ + const int verbose, + struct senc_device** device); + +SENC_API res_T +senc_device_ref_get + (struct senc_device* device); + +SENC_API res_T +senc_device_ref_put + (struct senc_device* device); + +/******************************************************************************* + * StarEnclosures scene. A scene is a collection of triangles. Each triangle is + * defined with a medium on each side. + ******************************************************************************/ +SENC_API res_T +senc_scene_create + (struct senc_device* device, + const unsigned mediums_count, + struct senc_scene** scene); + +SENC_API res_T +senc_scene_add_geometry + (struct senc_scene* scene, + const unsigned triangles_count, + void(*indices)(const unsigned itri, unsigned ids[3], void* context), + void(*mediums)(const unsigned itri, unsigned med[2], void* context), + const unsigned vertices_count, + void(*position)(const unsigned ivert, double pos[3], void* context), + void* context); + +SENC_API res_T +senc_scene_analyze + (struct senc_scene* scene, + struct senc_descriptor** descriptor); + +SENC_API res_T +senc_scene_ref_get + (struct senc_scene* scene); + +SENC_API res_T +senc_scene_ref_put + (struct senc_scene* scene); + +/******************************************************************************* + * StarEnclosures descriptor. It is an handle toward an analyze result. + ******************************************************************************/ +SENC_API res_T +senc_descriptor_get_enclosure_count + (const struct senc_descriptor* descriptor, + unsigned* count); + +SENC_API res_T +senc_descriptor_get_enclosure + (struct senc_descriptor* descriptor, + const unsigned idx, + struct senc_enclosure** enclosure); + +SENC_API res_T +senc_descriptor_get_global_triangle_count + (const struct senc_descriptor* descriptor, + unsigned* count); + +SENC_API res_T +senc_descriptor_get_global_vertices_count + (const struct senc_descriptor* descriptor, + unsigned* count); + +SENC_API res_T +senc_descriptor_get_global_indices + (const struct senc_descriptor* descriptor, + const unsigned itri, + unsigned indices[3]); + +SENC_API res_T +senc_descriptor_get_global_vertices + (const struct senc_descriptor* descriptor, + const unsigned ivert, + double coord[3]); + +SENC_API res_T +senc_descriptor_get_global_mediums + (const struct senc_descriptor* descriptor, + const unsigned itri, + unsigned mediums[2]); + +SENC_API res_T +senc_descriptor_get_global_enclosures + (const struct senc_descriptor* descriptor, + const unsigned itri, + unsigned enclosures[2]); + +SENC_API res_T +senc_descriptor_ref_get + (struct senc_descriptor* descriptor); + +SENC_API res_T +senc_descriptor_ref_put + (struct senc_descriptor* descriptor); + +/******************************************************************************* +* StarEnclosures enclosure. It is an handle toward an enclosure. +******************************************************************************/ +SENC_API res_T +senc_enclosure_get_header + (const struct senc_enclosure* enclosure, + const struct enclosure_header** header); + +SENC_API res_T +senc_enclosure_get_indices + (const struct senc_enclosure* enclosure, + const unsigned itri, + unsigned indices[3]); + +SENC_API res_T +senc_enclosure_get_vertices + (const struct senc_enclosure* enclosure, + const unsigned ivert, + double coord[3]); + +SENC_API res_T +senc_enclosure_get_mediums + (const struct senc_enclosure* enclosure, + const unsigned itri, + unsigned medium[2]); + +SENC_API res_T +senc_enclosure_ref_get + (struct senc_enclosure* enclosure); + +SENC_API res_T +senc_enclosure_ref_put + (struct senc_enclosure* enclosure); + +END_DECLS + +#endif /* SENC_H */ diff --git a/src/senc_descriptor.c b/src/senc_descriptor.c @@ -0,0 +1,224 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#include "senc_descriptor_c.h" +#include "senc_device_c.h" +#include "senc_enclosure_c.h" +#include "senc_scene_c.h" +#include "senc.h" + +#include <rsys/rsys.h> +#include <rsys/double3.h> +#include <rsys/mem_allocator.h> + + /******************************************************************************* + * Helper function + ******************************************************************************/ +static void +descriptor_release(ref_T * ref) +{ + struct senc_scene* scn = NULL; + struct senc_descriptor* desc = NULL; + ASSERT(ref); + desc = CONTAINER_OF(ref, struct senc_descriptor, ref); + scn = desc->scene; + darray_triangle_comp_release(&desc->triangles_comp); + darray_triangle_enc_release(&desc->triangles_enc); + darray_enclosure_release(&desc->enclosures); + + MEM_RM(scn->dev->allocator, desc); + SENC(scene_ref_put(scn)); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +struct senc_descriptor* +descriptor_create(struct senc_scene* scn) +{ + struct senc_descriptor* desc; + ASSERT(scn); + desc = MEM_CALLOC(scn->dev->allocator, 1, sizeof(struct senc_descriptor)); + if(desc) { + desc->scene = scn; + SENC(scene_ref_get(desc->scene)); + darray_triangle_comp_init(scn->dev->allocator, &desc->triangles_comp); + darray_triangle_enc_init(scn->dev->allocator, &desc->triangles_enc); + /* Enclosure 0 is always defined for infinite */ + darray_enclosure_init(scn->dev->allocator, &desc->enclosures); + darray_enclosure_resize(&desc->enclosures, 1); + desc->enclosures_count = 1; + desc->triangle_count = scn->nutris; + desc->vertices_count = scn->nuverts; + ref_init(&desc->ref); + } + return desc; +} + +struct mem_allocator* + descriptor_get_allocator(struct senc_descriptor* desc) +{ + ASSERT(desc); + return desc->scene->dev->allocator; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +senc_descriptor_get_enclosure_count + (const struct senc_descriptor* desc, unsigned* count) +{ + size_t tmp; + if(!desc || !count) return RES_BAD_ARG; + tmp = darray_enclosure_size_get(&desc->enclosures); + ASSERT(tmp < UINT_MAX); /* API type */ + *count = (unsigned)tmp; + return RES_OK; +} + +res_T +senc_descriptor_get_enclosure + (struct senc_descriptor* desc, + const unsigned idx, + struct senc_enclosure** out_enc) +{ + struct senc_enclosure* enc; + if(!desc || idx >= darray_enclosure_size_get(&desc->enclosures) || !out_enc) + return RES_BAD_ARG; + enc = + enclosure_create(desc, darray_enclosure_data_get(&desc->enclosures) + idx); + if(!enc) return RES_MEM_ERR; + *out_enc = enc; + return RES_OK; +} + +res_T +senc_descriptor_get_global_triangle_count + (const struct senc_descriptor* desc, + unsigned* count) +{ + if(!desc || !count) return RES_BAD_ARG; + ASSERT(desc->triangle_count < UINT_MAX); + *count = (unsigned)desc->triangle_count; /* Back to API type */ + return RES_OK; +} + +res_T +senc_descriptor_get_global_vertices_count + (const struct senc_descriptor* desc, + unsigned* count) +{ + if(!desc || !count) return RES_BAD_ARG; + if(desc->vertices_count >= UINT_MAX) + return RES_BAD_ARG; + *count = (unsigned)desc->vertices_count; /* Back to API type */ + return RES_OK; +} + +res_T +senc_descriptor_get_global_indices + (const struct senc_descriptor* desc, + const unsigned itri, + unsigned indices[3]) +{ + const struct triangle_in* trg; + int i; + if(!indices || ! desc + || itri >= darray_triangle_in_size_get(&desc->scene->triangles_in)) + return RES_BAD_ARG; + trg = darray_triangle_in_cdata_get(&desc->scene->triangles_in) + itri; + + FOR_EACH(i, 0, 3) { + ASSERT(trg->vertice_id[i] < UINT_MAX); + indices[i] = (unsigned)trg->vertice_id[i]; /* Back to API type */ + } + return RES_OK; +} + +res_T +senc_descriptor_get_global_vertices + (const struct senc_descriptor* desc, + const unsigned ivert, + double vrtx[3]) +{ + const union double3* v; + if(!vrtx || !desc + || ivert >= darray_triangle_in_size_get(&desc->scene->triangles_in)) + return RES_BAD_ARG; + + v = darray_position_cdata_get(&desc->scene->vertices) + ivert; + d3_set(vrtx, v->vec); + return RES_OK; +} + +res_T +senc_descriptor_get_global_mediums + (const struct senc_descriptor* desc, + const unsigned itri, + unsigned mediums[2]) +{ + const struct triangle_in* trg; + int i; + if(!mediums || !desc + || itri >= darray_triangle_in_size_get(&desc->scene->triangles_in)) + return RES_BAD_ARG; + trg = darray_triangle_in_cdata_get(&desc->scene->triangles_in) + itri; + FOR_EACH(i, 0, 2) { + ASSERT(trg->medium[i] < UINT_MAX); + mediums[i] = (unsigned)trg->medium[i]; /* Back to API type */ + } + return RES_OK; +} + +res_T +senc_descriptor_get_global_enclosures + (const struct senc_descriptor* desc, + const unsigned itri, + unsigned enclosures[2]) +{ + const struct triangle_comp* trg; + int i; + if(!enclosures || !desc + || itri >= darray_triangle_in_size_get(&desc->scene->triangles_in)) + return RES_BAD_ARG; + trg = darray_triangle_comp_cdata_get(&desc->triangles_comp) + itri; + + /* FIXME: trg->component is not what we need. + * We want an enclosure ID, not a component ID! */ + + + FOR_EACH(i, 0, 2) { + ASSERT(trg->component[i] < UINT_MAX); + enclosures[i] = (unsigned)trg->component[i]; /* Back to API type */ + } + return RES_OK; +} + +res_T +senc_descriptor_ref_get(struct senc_descriptor* desc) +{ + if(!desc) return RES_BAD_ARG; + ref_get(&desc->ref); + return RES_OK; +} + +res_T +senc_descriptor_ref_put(struct senc_descriptor* desc) +{ + if(!desc) return RES_BAD_ARG; + ref_put(&desc->ref, descriptor_release); + return RES_OK; +} diff --git a/src/senc_descriptor_c.h b/src/senc_descriptor_c.h @@ -0,0 +1,95 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_DESCRIPTOR_C_H +#define SENC_DESCRIPTOR_C_H + +#include <rsys/ref_count.h> +#include <rsys/dynamic_array.h> + +#include "senc.h" +#include "senc_enclosure_data.h" +#include "senc_internal_types.h" + +struct senc_scene; +struct mem_allocator; + +struct triangle_comp { + /* The connex component in which each side is. */ + component_id_t component[2]; +}; + +#ifndef NDEBUG +static void +triangle_comp_init(struct mem_allocator* alloc, struct triangle_comp* trg) { + int i; + (void)alloc; + ASSERT(trg); + FOR_EACH(i, 0, 2) trg->component[i] = COMPONENT_NULL__; +} +#define DARRAY_FUNCTOR_INIT triangle_comp_init +#endif + +#define DARRAY_NAME triangle_comp +#define DARRAY_DATA struct triangle_comp +#include <rsys/dynamic_array.h> + +struct triangle_enc { + /* The connex component in which each side is. */ + enclosure_id_t enclosure[2]; +}; + +#ifndef NDEBUG +static void +triangle_enc_init(struct mem_allocator* alloc, struct triangle_enc* trg) { + int i; + (void) alloc; + ASSERT(trg); + FOR_EACH(i, 0, 2) trg->enclosure[i] = ENCLOSURE_NULL__; +} +#define DARRAY_FUNCTOR_INIT triangle_enc_init +#endif + +#define DARRAY_NAME triangle_enc +#define DARRAY_DATA struct triangle_enc +#include <rsys/dynamic_array.h> + +#define DARRAY_NAME enclosure +#define DARRAY_DATA struct enclosure_data +#define DARRAY_FUNCTOR_INIT enclosure_data_init +#define DARRAY_FUNCTOR_COPY enclosure_data_copy +#define DARRAY_FUNCTOR_RELEASE enclosure_data_release +#define DARRAY_FUNCTOR_COPY_AND_RELEASE enclosure_data_copy_and_release +#include <rsys/dynamic_array.h> + +struct senc_descriptor { + struct senc_scene* scene; + enclosure_id_t enclosures_count; + struct darray_triangle_comp triangles_comp; + struct darray_triangle_enc triangles_enc; + struct darray_enclosure enclosures; + trg_id_t triangle_count; + vrtx_id_t vertices_count; + + ref_T ref; +}; + +struct senc_descriptor* +descriptor_create(struct senc_scene* scn); + +struct mem_allocator* +descriptor_get_allocator(struct senc_descriptor* desc); + +#endif /* SENC_DESCRIPTOR_C_H */ diff --git a/src/senc_device.c b/src/senc_device.c @@ -0,0 +1,134 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "senc_device_c.h" + +#include <rsys/logger.h> +#include <rsys/mem_allocator.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +log_msg + (struct senc_device* dev, + const enum log_type stream, + const char* msg, + va_list vargs) +{ + ASSERT(dev && msg); + if(dev->verbose) { + res_T res; (void)res; + res = logger_vprint(dev->logger, stream, msg, vargs); + ASSERT(res == RES_OK); + } +} + +static void +device_release(ref_T* ref) +{ + struct senc_device* dev; + ASSERT(ref); + dev = CONTAINER_OF(ref, struct senc_device, ref); + MEM_RM(dev->allocator, dev); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +void +log_err(struct senc_device* dev, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(dev && msg); + + va_start(vargs_list, msg); + log_msg(dev, LOG_ERROR, msg, vargs_list); + va_end(vargs_list); +} + +void +log_warn(struct senc_device* dev, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(dev && msg); + + va_start(vargs_list, msg); + log_msg(dev, LOG_WARNING, msg, vargs_list); + va_end(vargs_list); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +senc_device_create + (struct logger* logger, + struct mem_allocator* mem_allocator, + const unsigned nthreads_hint, + const int verbose, + struct senc_device** out_dev) +{ + struct logger* log = NULL; + struct senc_device* dev = NULL; + struct mem_allocator* allocator = NULL; + res_T res = RES_OK; + (void)nthreads_hint; /* Unused */ + if(nthreads_hint == 0 || !out_dev) return RES_BAD_ARG; + + log = logger ? logger : LOGGER_DEFAULT; + allocator = mem_allocator ? mem_allocator : &mem_default_allocator; + dev = MEM_CALLOC(allocator, 1, sizeof(struct senc_device)); + if(!dev) { + if(verbose) { + /* Do not use helper log functions since dev is not initialised */ + CHK(logger_print(log, LOG_ERROR, + "%s: could not allocate the StarEnclosures device.\n", FUNC_NAME) == RES_OK); + } + res = RES_MEM_ERR; + goto error; + } + dev->logger = log; + dev->allocator = allocator; + dev->verbose = verbose; + ref_init(&dev->ref); + +exit: + if(dev) *out_dev = dev; + return res; +error: + if(dev) { + SENC(device_ref_put(dev)); + dev = NULL; + } + goto exit; +} + +res_T +senc_device_ref_get(struct senc_device* dev) +{ + if(!dev) return RES_BAD_ARG; + ref_get(&dev->ref); + return RES_OK; +} + +res_T +senc_device_ref_put(struct senc_device* dev) +{ + if(!dev) return RES_BAD_ARG; + ref_put(&dev->ref, device_release); + return RES_OK; +} diff --git a/src/senc_device_c.h b/src/senc_device_c.h @@ -0,0 +1,58 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_DEVICE_C_H +#define SENC_DEVICE_C_H + +#include <rsys/free_list.h> +#include <rsys/ref_count.h> + +struct name { FITEM; }; +#define FITEM_TYPE name +#include <rsys/free_list.h> + +struct senc_device { + struct logger* logger; + struct mem_allocator* allocator; + int verbose; + + ref_T ref; +}; + +/* Conditionally log a message on the LOG_ERROR stream of the device logger, + * with respect to the device verbose flag */ +extern LOCAL_SYM void +log_err + (struct senc_device* dev, + const char* msg, + ...) +#ifdef COMPILER_GCC + __attribute((format(printf, 2, 3))) +#endif +; + +/* Conditionally log a message on the LOG_WARNING stream of the device logger, + * with respect to the device verbose flag */ +extern LOCAL_SYM void +log_warn + (struct senc_device* dev, + const char* msg, + ...) +#ifdef COMPILER_GCC + __attribute((format(printf, 2, 3))) +#endif +; + +#endif /* SENC_DEVICE_C_H */ diff --git a/src/senc_enclosure.c b/src/senc_enclosure.c @@ -0,0 +1,145 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#include "senc_enclosure_c.h" +#include "senc_descriptor_c.h" +#include "senc_scene_c.h" +#include "senc.h" + +#include <rsys/rsys.h> +#include <rsys/double3.h> +#include <rsys/mem_allocator.h> + + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static void +enclosure_release(ref_T * ref) +{ + struct senc_enclosure* enclosure = NULL; + struct senc_descriptor* desc = NULL; + ASSERT(ref); + enclosure = CONTAINER_OF(ref, struct senc_enclosure, ref); + desc = enclosure->desc; + + MEM_RM(descriptor_get_allocator(desc), enclosure); + SENC(descriptor_ref_put(desc)); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +struct senc_enclosure* +enclosure_create + (struct senc_descriptor* desc, + const struct enclosure_data* data) +{ + struct senc_enclosure* enc; + ASSERT(desc); + enc = MEM_CALLOC(descriptor_get_allocator(desc), + 1, sizeof(struct senc_enclosure)); + if(enc) { + SENC(descriptor_ref_get(desc)); + enc->desc = desc; + enc->data = data; + ref_init(&enc->ref); + } + return enc; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +senc_enclosure_get_header + (const struct senc_enclosure* enclosure, + const struct enclosure_header** header) +{ + if(!enclosure || !header) return RES_BAD_ARG; + *header = &enclosure->data->header; + return RES_OK; +} + +res_T +senc_enclosure_get_indices + (const struct senc_enclosure* enclosure, + const unsigned itri, + unsigned indices[3]) +{ + const struct triangle_in* triangle; + int i; + if(!enclosure || !indices + || itri >= enclosure->data->header.triangle_count) + return RES_BAD_ARG; + triangle = darray_triangle_in_cdata_get(&enclosure->data->sides) + itri; + FOR_EACH(i, 0, 3) { + ASSERT(triangle->vertice_id[i] < UINT_MAX); + indices[i] = (unsigned)triangle->vertice_id[i]; /* Back to API type */ + } + return RES_OK; +} + +res_T +senc_enclosure_get_vertices + (const struct senc_enclosure* enclosure, + const unsigned ivert, + double coord[3]) +{ + const struct senc_scene* scene; + if(!enclosure || !coord + || ivert >= enclosure->data->header.vertices_count) + return RES_BAD_ARG; + scene = enclosure->desc->scene; + ASSERT(darray_position_size_get(&scene->vertices) + == enclosure->data->header.vertices_count); + d3_set(coord, darray_position_cdata_get(&scene->vertices)[ivert].vec); + return RES_OK; +} + +res_T +senc_enclosure_get_mediums + (const struct senc_enclosure* enclosure, + const unsigned itri, + unsigned medium[2]) +{ + const struct triangle_in* triangle; + int i; + if(!enclosure || !medium + || itri >= enclosure->data->header.triangle_count) + return RES_BAD_ARG; + triangle = darray_triangle_in_cdata_get(&enclosure->data->sides) + itri; + FOR_EACH(i, 0, 2) { + ASSERT(triangle->medium[i] < UINT_MAX); + medium[i] = (unsigned)triangle->medium[i]; /* Back to API type */ + } + return RES_OK; +} + +res_T +senc_enclosure_ref_get(struct senc_enclosure* enc) +{ + if(!enc) return RES_BAD_ARG; + ref_get(&enc->ref); + return RES_OK; +} + +res_T +senc_enclosure_ref_put(struct senc_enclosure* enc) +{ + if(!enc) return RES_BAD_ARG; + ref_put(&enc->ref, enclosure_release); + return RES_OK; +} diff --git a/src/senc_enclosure_c.h b/src/senc_enclosure_c.h @@ -0,0 +1,37 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_ENCLOSURE_C_H +#define SENC_ENCLOSURE_C_H + +#include <rsys/ref_count.h> + +#include "senc.h" + +struct enclosure_data; +struct senc_descriptor; + +struct senc_enclosure { + const struct enclosure_data* data; + struct senc_descriptor* desc; + ref_T ref; +}; + +struct senc_enclosure* +enclosure_create + (struct senc_descriptor* desc, + const struct enclosure_data* data); + +#endif /* SENC_ENCLOSURE_C_H */ diff --git a/src/senc_enclosure_data.h b/src/senc_enclosure_data.h @@ -0,0 +1,85 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_ENCLOSURE_DATA_H +#define SENC_ENCLOSURE_DATA_H + +#include <rsys/rsys.h> +#include <rsys/ref_count.h> + +#include "senc.h" +#include "senc_scene_c.h" + +static void +init_header(struct enclosure_header* header) +{ + ASSERT(header); + header->enclosure_id = ENCLOSURE_NULL__; + header->triangle_count = 0; + header->unique_triangle_count = 0; + header->vertices_count = 0; + header->enclosed_medium = MEDIUM_NULL__; + header->is_infinite = CHAR_MAX; +} + +struct enclosure_data { + struct enclosure_header header; + /* Same triangle can appear twice if both sides */ + struct darray_triangle_in sides; + struct darray_position vertices; +}; + +static FINLINE void +enclosure_data_init(struct mem_allocator* alloc, struct enclosure_data* enc) { + ASSERT(enc); + init_header(&enc->header); + darray_triangle_in_init(alloc, &enc->sides); + darray_position_init(alloc, &enc->vertices); +} + +static FINLINE res_T +enclosure_data_copy +(struct enclosure_data* dst, + const struct enclosure_data* src) +{ + res_T res = RES_OK; + ASSERT(src && dst); + dst->header = src->header; + res = darray_triangle_in_copy(&dst->sides, &src->sides); + if(res != RES_OK) return res; + return darray_position_copy(&dst->vertices, &src->vertices); +} + +static FINLINE void +enclosure_data_release(struct enclosure_data* n) { + ASSERT(n); + darray_triangle_in_release(&n->sides); + darray_position_release(&n->vertices); +} + +static FINLINE res_T +enclosure_data_copy_and_release +(struct enclosure_data* dst, + struct enclosure_data* src) +{ + res_T res = RES_OK; + ASSERT(src && dst); + dst->header = src->header; + res = darray_triangle_in_copy_and_release(&dst->sides, &src->sides); + if(res != RES_OK) return res; + return darray_position_copy_and_release(&dst->vertices, &src->vertices); +} + +#endif /* SENC_ENCLOSURE_DATA_H */ diff --git a/src/senc_internal_types.h b/src/senc_internal_types.h @@ -0,0 +1,64 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_INTERNAL_TYPES_H +#define SENC_INTERNAL_TYPES_H + +#include <rsys/math.h> + +#include <stdint.h> + +/* Side IDs are uint32_t */ +typedef uint32_t side_id_t; +#define SIDE_MAX__ (UINT32_MAX-1) +#define SIDE_NULL__ UINT32_MAX + +/* Trg IDs use internally side_id_t */ +/* Cannot be larger than unsigned, as the API uses it. */ +typedef side_id_t trg_id_t; +/* TRG_MAX__ is limited to allow to count sides */ +#define TRG_MAX__ (SIDE_MAX__ / 2) +#define TRG_NULL__ UINT32_MAX + +/* Vertex IDs are internally uint32_t */ +/* Cannot be larger than unsigned, as the API uses it. */ +typedef uint32_t vrtx_id_t; +#define VRTX_MAX__ (UINT32_MAX-1) +#define VRTX_NULL__ UINT32_MAX + +/* Edge IDs use the same type than vertex IDs */ +/* Cannot be larger than unsigned, as the API uses it. */ +typedef vrtx_id_t edge_id_t; +#define EDGE_MAX__ VRTX_MAX__ +#define EDGE_NULL__ VRTX_NULL__ + +/* Medium IDs are internally uint16_t */ +/* Should nnot be larger than unsigned, as the API uses it. */ +typedef uint16_t medium_id_t; +#define MEDIUM_MAX__ (UINT16_MAX-1) +#define MEDIUM_NULL__ UINT16_MAX + +/* Enclosure IDs are internally uint16_t */ +/* Cannot be larger than unsigned, as the API uses it. */ +typedef uint16_t enclosure_id_t; +#define ENCLOSURE_MAX__ (UINT16_MAX-1) +#define ENCLOSURE_NULL__ UINT16_MAX + +/* Component IDs use the same type than enclosure IDs */ +typedef enclosure_id_t component_id_t; +#define COMPONENT_MAX__ ENCLOSURE_MAX__ +#define COMPONENT_NULL__ ENCLOSURE_NULL__ + +#endif /* SENC_INTERNAL_TYPES_H */ diff --git a/src/senc_s3d_wrapper.h b/src/senc_s3d_wrapper.h @@ -0,0 +1,80 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_S3D_WRAPPER_H +#define SENC_S3D_WRAPPER_H + +#include "senc.h" + +#include <rsys/rsys.h> +#include <rsys/float3.h> + +void FINLINE +senc_descriptor_get_global_indices__ + (const unsigned itri, + unsigned indices[3], + void* ctx) +{ + const struct senc_descriptor* descriptor = ctx; + res_T r; + ASSERT(indices && ctx); + r = senc_descriptor_get_global_indices(descriptor, itri, indices); + ASSERT(r == RES_OK); +} + +void FINLINE +senc_descriptor_get_global_vertices__ + (const unsigned ivert, + float coord[3], + void* ctx) +{ + const struct senc_descriptor* descriptor = ctx; + double tmp[3]; + res_T r; + ASSERT(coord && ctx); + r = senc_descriptor_get_global_vertices(descriptor, ivert, tmp); + ASSERT(r == RES_OK); + f3_set_d3(coord, tmp); +} + +void FINLINE +senc_enclosure_get_indices__ + (const unsigned itri, + unsigned indices[3], + void* ctx) +{ + const struct senc_enclosure* enclosure = ctx; + res_T r; + ASSERT(indices && ctx); + r = senc_enclosure_get_indices(enclosure, itri, indices); + ASSERT(r == RES_OK); +} + +void FINLINE +senc_enclosure_get_vertices__ + (const unsigned ivert, + float coord[3], + void* ctx) +{ + const struct senc_enclosure* enclosure = ctx; + double tmp[3]; + res_T r; + ASSERT(coord && ctx); + r = senc_enclosure_get_vertices(enclosure, ivert, tmp); + ASSERT(r == RES_OK); + f3_set_d3(coord, tmp); +} + +#endif /* SENC_S3D_WRAPPER_H */ diff --git a/src/senc_scene.c b/src/senc_scene.c @@ -0,0 +1,289 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "senc_device_c.h" +#include "senc_scene_c.h" + +#include <rsys/rsys.h> +#include <rsys/mem_allocator.h> + +#include <limits.h> + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static void +scene_release(ref_T * ref) +{ + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + ASSERT(ref); + scn = CONTAINER_OF(ref, struct senc_scene, ref); + dev = scn->dev; + darray_triangle_in_release(&scn->triangles_in); + darray_position_release(&scn->vertices); + htable_vrtx_release(&scn->unique_vertices); + darray_vrtx_id_release(&scn->unique_vertice_ids); + htable_trg_release(&scn->unique_triangles); + darray_trg_id_release(&scn->unique_triangle_ids); + darray_char_release(&scn->triangle_reversed); + MEM_RM(dev->allocator, scn); + SENC(device_ref_put(dev)); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +senc_scene_create + (struct senc_device* dev, + const unsigned nmeds, + struct senc_scene** out_scn) +{ + struct senc_scene* scn = NULL; + res_T res = RES_OK; + + if(!dev || !out_scn || !nmeds || nmeds > MEDIUM_MAX__) + return RES_BAD_ARG; + + scn = MEM_CALLOC(dev->allocator, 1, sizeof(struct senc_scene)); + if(!scn) { + log_err(dev, "%s: could not allocate the StarEnclosures scene.\n", FUNC_NAME); + res = RES_MEM_ERR; + goto error; + } + ref_init(&scn->ref); + SENC(device_ref_get(dev)); + scn->dev = dev; + scn->ngeoms = 0; + scn->ntris = 0; + scn->nutris = 0; + scn->nmeds = (medium_id_t)nmeds; + scn->nverts = 0; + scn->nuverts = 0; + darray_triangle_in_init(dev->allocator, &scn->triangles_in); + darray_position_init(dev->allocator, &scn->vertices); + htable_vrtx_init(dev->allocator, &scn->unique_vertices); + darray_vrtx_id_init(dev->allocator, &scn->unique_vertice_ids); + htable_trg_init(dev->allocator, &scn->unique_triangles); + darray_trg_id_init(dev->allocator, &scn->unique_triangle_ids); + darray_char_init(dev->allocator, &scn->triangle_reversed); + +exit: + if(scn) *out_scn = scn; + return res; +error: + if(scn) { + SENC(scene_ref_put(scn)); + scn = NULL; + } + goto exit; +} + +res_T +senc_scene_add_geometry + (struct senc_scene* scn, + const unsigned ntris, + void(*indices)(const unsigned itri, unsigned ids[3], void*), + void(*mediums)(const unsigned itri, unsigned medium[2], void*), + const unsigned nverts, + void(*position)(const unsigned ivert, double pos[3], void* ctx), + void* ctx) +{ + unsigned i; + vrtx_id_t actual_nverts = 0; + vrtx_id_t actual_nuverts = 0; + trg_id_t actual_ntris = 0; + trg_id_t actual_nutris = 0; + const struct triangle_in* trg; + res_T res = RES_OK; + + if(!scn + || !indices || !mediums || !position + || !nverts || ((size_t)scn->nverts + (size_t)nverts) > VRTX_MAX__ + || !ntris || ((size_t)scn->ntris + (size_t)ntris) > TRG_MAX__) + return RES_BAD_ARG; + + /* Make room for new geometry; suppose no more duplicates. */ + res = darray_position_reserve(&scn->vertices, scn->nuverts + nverts); + if(res != RES_OK) goto error; + res = darray_triangle_in_reserve(&scn->triangles_in, scn->nutris + ntris); + if(res != RES_OK) goto error; + res = htable_vrtx_reserve(&scn->unique_vertices, scn->nuverts + nverts); + if(res != RES_OK) goto error; + res = darray_vrtx_id_reserve(&scn->unique_vertice_ids, scn->nverts + nverts); + if(res != RES_OK) goto error; + res = htable_trg_reserve(&scn->unique_triangles, scn->nutris + ntris); + if(res != RES_OK) goto error; + res = darray_trg_id_reserve(&scn->unique_triangle_ids, scn->ntris + ntris); + if (res != RES_OK) goto error; + res = darray_char_reserve(&scn->triangle_reversed, scn->ntris + ntris); + if (res != RES_OK) goto error; + + /* Get geometry */ + trg = darray_triangle_in_cdata_get(&scn->triangles_in); + + FOR_EACH(i, 0, nverts) { + vrtx_id_t* p_vrtx; + union double3 tmp; + vrtx_id_t unique_v; + /* API: position needs an unsigned */ + position(i, tmp.vec, ctx); + p_vrtx = htable_vrtx_find(&scn->unique_vertices, &tmp); + if(p_vrtx) { + /* Duplicate vertex */ + unique_v = darray_vrtx_id_cdata_get(&scn->unique_vertice_ids)[*p_vrtx]; + } else { + /* New vertex */ + unique_v = scn->nuverts + actual_nuverts; + res = darray_position_push_back(&scn->vertices, &tmp); + if (res != RES_OK) goto error; + ASSERT(unique_v == htable_vrtx_size_get(&scn->unique_vertices)); + res = htable_vrtx_set(&scn->unique_vertices, &tmp, &unique_v); + ++actual_nuverts; + if(res != RES_OK) goto error; + } + /* The unique ID for vertex v is u */ + ASSERT(scn->nverts + i + == darray_vrtx_id_size_get(&scn->unique_vertice_ids)); + res = darray_vrtx_id_push_back(&scn->unique_vertice_ids, &unique_v); + if(res != RES_OK) goto error; + ++actual_nverts; + } + + FOR_EACH(i, 0, ntris) { + int j; + unsigned med[2]; + unsigned ind[3]; + vrtx_id_t vrtx_key; + union vrtx_id3 trg_key; + struct triangle_in tmp; + trg_id_t* p_trg; + trg_id_t tr; + char reversed; + indices(i, ind, ctx); /* API: indices needs an unsigned */ + FOR_EACH(j, 0, 3) { + if(ind[j] >= nverts) { + res = RES_BAD_ARG; + goto error; + } + vrtx_key = ind[j] + scn->nverts; + ASSERT(vrtx_key < darray_vrtx_id_size_get(&scn->unique_vertice_ids)); + /* Find the unique ID for this vertex */ + tmp.vertice_id[j] = + darray_vrtx_id_cdata_get(&scn->unique_vertice_ids)[vrtx_key]; + } + if(tmp.vertice_id[0] == tmp.vertice_id[1] + || tmp.vertice_id[0] == tmp.vertice_id[2] + || tmp.vertice_id[1] == tmp.vertice_id[2]) { + log_err(scn->dev, "%s: triangle %lu is degenerate.\n", + FUNC_NAME, (unsigned long)i); + res = RES_BAD_ARG; + goto error; + } + /* Get mediums */ + mediums(i, med, ctx); /* API: mediums needs an unsigned */ + ASSERT(scn->nmeds <= MEDIUM_MAX__); + FOR_EACH(j, 0, 2) { + if(med[j] >= scn->nmeds) { + log_err(scn->dev, "%s: triangle %lu references invalid medium.\n", + FUNC_NAME, (unsigned long)i); + res = RES_BAD_ARG; + goto error; + } + tmp.medium[j] = (medium_id_t)med[j]; + } + /* Find duplicate triangles */ + reversed = trg_make_key(&trg_key, tmp.vertice_id); + p_trg = htable_trg_find(&scn->unique_triangles, &trg_key); + if(p_trg) { + union vrtx_id3 utrg_key; + char ureversed = trg_make_key(&utrg_key, trg[*p_trg].vertice_id); + int same = (reversed == ureversed); + const medium_id_t* umed; + /* Duplicate triangle. Need to check duplicate validity */ + ASSERT(trg_key_eq(&trg_key, &utrg_key)); + umed = trg[*p_trg].medium; + if(umed[0] != (same ? med[0] : med[1]) + || umed[1] != (same ? med[1] : med[0])) { + /* Same triangles with different mediums: invalid! */ + log_err(scn->dev, "%s: triangle %lu is a duplicate with incoherent mediums.\n", + FUNC_NAME, (unsigned long)*p_trg); + res = RES_BAD_ARG; + goto error; + } else { + if(!same) { + FOR_EACH(j, 0, 2) { + tmp.medium[j] = (medium_id_t) med[1-j]; + } + } + } + } + else { + /* New triangle */ + trg_id_t u = scn->nutris + actual_nutris; + ASSERT(u == htable_trg_size_get(&scn->unique_triangles)); + res = htable_trg_set(&scn->unique_triangles, &trg_key, &u); + if(res != RES_OK) goto error; + res = darray_triangle_in_push_back(&scn->triangles_in, &tmp); + if (res != RES_OK) goto error; + ++actual_nutris; + } + ASSERT(darray_trg_id_size_get(&scn->unique_triangle_ids) + == darray_char_size_get(&scn->triangle_reversed)); + ASSERT(scn->ntris + i + == darray_trg_id_size_get(&scn->unique_triangle_ids)); + tr = scn->nutris + i; + res = darray_trg_id_push_back(&scn->unique_triangle_ids, &tr); + if (res != RES_OK) goto error; + res = darray_char_push_back(&scn->triangle_reversed, &reversed); + if (res != RES_OK) goto error; + ++actual_ntris; + } + +exit: + /* Update sizes */ + scn->nuverts += actual_nuverts; + scn->nverts += actual_nverts; + scn->nutris += actual_nutris; + scn->ntris += actual_ntris; + ASSERT(scn->nverts == darray_vrtx_id_size_get(&scn->unique_vertice_ids)); + ASSERT(scn->nuverts == htable_vrtx_size_get(&scn->unique_vertices)); + ASSERT(scn->nutris == htable_trg_size_get(&scn->unique_triangles)); + ASSERT(scn->ntris == darray_trg_id_size_get(&scn->unique_triangle_ids)); + ASSERT(darray_trg_id_size_get(&scn->unique_triangle_ids) + == darray_char_size_get(&scn->triangle_reversed)); + ++scn->ngeoms; + return res; +error: + goto exit; +} + +res_T +senc_scene_ref_get(struct senc_scene* scn) +{ + if(!scn) return RES_BAD_ARG; + ref_get(&scn->ref); + return RES_OK; +} + +res_T +senc_scene_ref_put(struct senc_scene* scn) +{ + if(!scn) return RES_BAD_ARG; + ref_put(&scn->ref, scene_release); + return RES_OK; +} diff --git a/src/senc_scene_analyze.c b/src/senc_scene_analyze.c @@ -0,0 +1,1097 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "senc_descriptor_c.h" +#include "senc_device_c.h" +#include "senc_scene_c.h" +#include "senc_scene_analyze_c.h" +#include "senc_internal_types.h" + +#include <rsys/rsys.h> +#include <rsys/float3.h> +#include <rsys/double33.h> +#include <rsys/mem_allocator.h> +#include <rsys/hash_table.h> +#include <rsys/dynamic_array.h> +#include <rsys/dynamic_array_uint.h> + +#include <star/s3d.h> + +#include <limits.h> +#include <stdlib.h> + +#define CC_DESCRIPTOR_NULL__ {\ + {0,0,-DBL_MAX}, -1, SIDE_NULL__, VRTX_NULL__, 0, MEDIUM_NULL__,\ + CC_ID_NONE, CC_GROUP_ROOT_NONE, CC_GROUP_ID_NONE\ +} +const struct cc_descriptor CC_DESCRIPTOR_NULL = CC_DESCRIPTOR_NULL__; + +#define DARRAY_NAME component_id +#define DARRAY_DATA component_id_t +#include <rsys/dynamic_array.h> + +#define DARRAY_NAME side_id +#define DARRAY_DATA side_id_t +#include <rsys/dynamic_array.h> + +#define HTABLE_NAME vrtx_id +#define HTABLE_KEY vrtx_id_t +#define HTABLE_DATA vrtx_id_t +#include <rsys/hash_table.h> + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static void +dumplist + (const struct trgside* trgsides, + const struct darray_side_id* side_ids, + const enum list_id list_id) +{ + side_id_t i; + size_t tmp; + (void)list_id; + ASSERT(trgsides && side_ids); + printf("\n"); + tmp = darray_side_id_size_get(side_ids); + ASSERT(tmp <= SIDE_MAX__); + FOR_EACH(i, 0, (side_id_t)tmp) { + const side_id_t id = darray_side_id_cdata_get(side_ids)[i]; + const struct trgside* const side = trgsides + id; + printf("Side %lu (%lu %lu %lu)\n", + (unsigned long)id, + (unsigned long)side->facing_side_id[0], + (unsigned long)side->facing_side_id[1], + (unsigned long)side->facing_side_id[2]); + ASSERT(side->list_id & list_id); + } +} + +static int +find_side_in_list + (const struct trgside* trgsides, + const struct darray_side_id* side_ids, + const side_id_t side_id, + const enum list_id list_id) +{ + side_id_t i; + size_t tmp; + (void)list_id; + (void)trgsides; + ASSERT(trgsides && side_ids); + tmp = darray_side_id_size_get(side_ids); + ASSERT(tmp <= SIDE_MAX__); + FOR_EACH(i, 0, (side_id_t)tmp) { + const side_id_t id = darray_side_id_cdata_get(side_ids)[i]; + ASSERT(trgsides[id].list_id & list_id); + if(id == side_id) return 1; + } + return 0; +} + +static INLINE int +neighbour_cmp(const void* w1, const void* w2) +{ + const double a1 = ((struct neighbour_info*)w1)->angle; + const double a2 = ((struct neighbour_info*)w2)->angle; + return (a1 > a2) - (a1 < a2); +} + +static void +find_component_Zmax + (struct senc_scene* scn, + struct darray_triangle_tmp* triangles_tmp_array, + struct cc_descriptor* cc) +{ + trg_id_t trid; + double edge0[3], edge1[3], normal[3], norm, side_nz; + struct triangle_in* triangles_in; + struct triangle_tmp* triangles_tmp; + const union double3* vertices; + const char* side_membership; + ASSERT(scn && triangles_tmp_array && cc); + + vertices = darray_position_cdata_get(&scn->vertices); + triangles_in = darray_triangle_in_data_get(&scn->triangles_in); + triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array); + side_membership = darray_char_cdata_get(&cc->side_membership); + + /* Build the sorted list of side ids */ + FOR_EACH(trid, 0, scn->ntris) { + const char member = side_membership[trid]; + struct triangle_in* const trg_in = triangles_in + trid; + struct triangle_tmp* const trg_tmp = triangles_tmp + trid; + enum side_id side; + int change = 0; + + if(!member) continue; + + /* Keep track of the appropriate vertex/side of the connex component + * in order to cast a ray at the component grouping step of the algorithm. + * The most appropriate vertex is (the) one with the greater Z coordinate. + * If more than one vertex/side has the same Z, we want the side that most + * faces Z (that is the one with the greater nz). + * This is mandatory to select the correct side when both sides of a triangle + * are candidate. */ + if(cc->max_vrtx[2] > trg_tmp->max_z) + return; + + d3_sub(edge0, vertices[trg_in->vertice_id[1]].vec, + vertices[trg_in->vertice_id[0]].vec); + d3_sub(edge1, vertices[trg_in->vertice_id[2]].vec, + vertices[trg_in->vertice_id[0]].vec); + d3_cross(normal, edge0, edge1); + norm = d3_normalize(normal, normal); + ASSERT(norm); + + if((member & FLAG_FRONT) && (member & FLAG_BACK)) { + /* Select the side with nz>0 */ + side_nz = fabs(normal[2]); + side = (normal[2] > 0) ? SIDE_FRONT : SIDE_BACK; + } else if(member & FLAG_FRONT) { + side_nz = normal[2]; + side = SIDE_FRONT; + } else { + ASSERT(member & FLAG_BACK); + side_nz = -normal[2]; + side = SIDE_BACK; + } + + /* TODO: keep the list of triangle sharing the selected vertex + * to improve self-hit filter. */ + if(cc->max_vrtx[2] < trg_tmp->max_z) { + change = 1; /* Try first to improve z */ + } + else if(cc->max_z_nz <= 0 && fabs(cc->max_z_nz) < fabs(side_nz)) { + change = 1; /* If nz <= 0, the more negative the better */ + } + else if(cc->max_z_nz > 0 && cc->max_z_nz < side_nz) { + change = 1; /* If nz > 0, the more positive the better */ + } + if(change) { + cc->max_z_nz = side_nz; + cc->max_z_side_id = TRGIDxSIDE_2_TRGSIDE(trid, side); + ASSERT(trg_tmp->max_z_vrtx_id < 3); + ASSERT(trg_in->vertice_id[trg_tmp->max_z_vrtx_id] < scn->nverts); + cc->max_z_vrtx_id = trg_in->vertice_id[trg_tmp->max_z_vrtx_id]; + d3_set(cc->max_vrtx, vertices[cc->max_z_vrtx_id].vec); + } + } +} + +static FINLINE void +add_side_to_stack + (struct senc_scene* scn, + struct darray_side_id* stack, + struct trgside* trgsides, + const side_id_t side_id) +{ + (void)scn; + ASSERT(scn && trgsides && stack + && side_id < SIDE_MAX__ && side_id < 2 * scn->ntris); + ASSERT(!find_side_in_list(trgsides, stack, side_id, FLAG_WAITING_STACK)); + darray_side_id_push_back(stack, &side_id); + trgsides[side_id].list_id = FLAG_WAITING_STACK; +} + +static FINLINE void +add_side_to_medium_list + (struct senc_scene* scn, + struct darray_side_id* side_ids_by_medium, + struct trgside* trgsides, + const side_id_t side_id) +{ + (void)scn; + ASSERT(scn && side_ids_by_medium && trgsides + && side_id < 2 * scn->ntris); + if(trgsides[side_id].list_id == FLAG_LIST_BY_MEDIUM) { + ASSERT(find_side_in_list(trgsides, side_ids_by_medium, side_id, + FLAG_LIST_BY_MEDIUM)); + return; + } + ASSERT(!find_side_in_list(trgsides, side_ids_by_medium, side_id, + FLAG_LIST_BY_MEDIUM)); + darray_side_id_push_back(side_ids_by_medium, &side_id); + trgsides[side_id].list_id = FLAG_LIST_BY_MEDIUM; +} + +static side_id_t +get_side_from_medium_list_not_in_connex_component + (struct senc_scene* scn, + struct trgside* trgsides, + side_id_t* first_side_by_medium_not_in_component, + const struct darray_side_id* side_ids_by_medium) +{ + side_id_t i, sz; + size_t tmp; + const side_id_t* ids; + (void)scn; + ASSERT(scn && trgsides && first_side_by_medium_not_in_component + && side_ids_by_medium); + tmp = darray_side_id_size_get(side_ids_by_medium); + ids = darray_side_id_cdata_get(side_ids_by_medium); + ASSERT(tmp <= SIDE_MAX__); + sz = (side_id_t)tmp; + i = *first_side_by_medium_not_in_component; + while(i < sz && trgsides[ids[i]].list_id != FLAG_LIST_BY_MEDIUM) ++i; + + *first_side_by_medium_not_in_component = i+1; + if(i == sz) return SIDE_NULL__; + ASSERT(trgsides[ids[i]].list_id == FLAG_LIST_BY_MEDIUM); + return ids[i]; +} + +static FINLINE side_id_t +get_side_from_stack + (struct trgside* trgsides, + struct darray_side_id* stack) +{ + side_id_t id; + size_t sz; + (void)trgsides; + ASSERT(trgsides && stack); + sz = darray_side_id_size_get(stack); + id = darray_side_id_cdata_get(stack)[sz - 1]; + ASSERT(trgsides[id].list_id == FLAG_WAITING_STACK); + darray_side_id_pop_back(stack); + return id; +} + +static res_T +extract_connex_components + (struct senc_descriptor* desc, + struct trgside* trgsides, + struct darray_cc_descriptor* connex_components, + struct darray_triangle_tmp* triangles_tmp_array, + struct darray_side_id* side_ids_by_medium) +{ + res_T res = RES_OK; + medium_id_t m; + size_t tmp; + struct senc_scene* scn; + struct mem_allocator* alloc; + struct darray_side_id stack; + struct triangle_comp* triangles_comp; + /* Arrays of cc_descriptor ids, organized by medium. */ + struct darray_component_id* component_ids_by_medium = NULL; + + ASSERT(trgsides && desc && connex_components && triangles_tmp_array + && side_ids_by_medium); + + alloc = descriptor_get_allocator(desc); + scn = desc->scene; + + res = darray_triangle_comp_resize(&desc->triangles_comp, scn->ntris); + if(res != RES_OK) goto error; + triangles_comp = darray_triangle_comp_data_get(&desc->triangles_comp); + + /* Init data structures */ + darray_side_id_init(alloc, &stack); + component_ids_by_medium + = MEM_ALLOC(alloc, scn->nmeds * sizeof(struct darray_uint)); + if(!component_ids_by_medium) { + res = RES_MEM_ERR; + goto error; + } + FOR_EACH(m, 0, scn->nmeds) { + struct darray_component_id* const cc = component_ids_by_medium + m; + darray_component_id_init(alloc, cc); + } + + /* For each medium extract connex components */ + FOR_EACH(m, 0, scn->nmeds) { + struct darray_component_id* const cc_ids_by_medium + = component_ids_by_medium + m; + struct cc_descriptor current_cc; + /* Id of the first trgside not already in a connex component */ + side_id_t first_side_by_medium_not_in_component = 0; + /* Init current component */ + cc_descriptor_init(alloc, &current_cc); + + for(;;) { + /* Any not-already-used side by medium is used as a starting point */ + const side_id_t start_side_id = + get_side_from_medium_list_not_in_connex_component(scn, trgsides, + &first_side_by_medium_not_in_component, side_ids_by_medium + m); + side_id_t crt_side_id = start_side_id; + char* side_membership; + + ASSERT(start_side_id == SIDE_NULL__ || start_side_id < 2 * scn->ntris); + if(start_side_id == SIDE_NULL__) + break; /* start_side_id=SIDE_NULL__ => medium done! */ + ASSERT(trgsides[start_side_id].list_id == FLAG_LIST_BY_MEDIUM); + + /* Reset CC data for a new CC */ + tmp = darray_cc_descriptor_size_get(connex_components); + ASSERT(tmp <= COMPONENT_MAX__); + cc_descriptor_clear(&current_cc); + /* 1 char per triangle (2 sides) */ + darray_char_resize(&current_cc.side_membership, scn->ntris); + side_membership = darray_char_data_get(&current_cc.side_membership); + memset(side_membership, 0, scn->ntris * sizeof(char)); + current_cc.cc_id = (component_id_t)tmp; + current_cc.medium = m; + + for(;;) { + int i; + /* Pop waiting stack to feed crt_side */ + struct trgside* crt_side = trgsides + crt_side_id; + const trg_id_t crt_trg_id = TRGSIDE_2_TRG(crt_side_id); + struct triangle_comp* trg_comp = triangles_comp + crt_trg_id; + ASSERT(crt_trg_id < scn->ntris); + ASSERT(trgsides[crt_side_id].medium == m); + + /* Record crt_side both as component and triangle level */ + current_cc.triangle_count++; + trgsides[crt_side_id].list_id = FLAG_LIST_COMPONENT; + if(TRGSIDE_IS_FRONT(crt_side_id)) { + ASSERT(trg_comp->component[SIDE_FRONT] == USHRT_MAX); + trg_comp->component[SIDE_FRONT] = current_cc.cc_id; + ASSERT(!(side_membership[crt_trg_id] & FLAG_FRONT)); + side_membership[crt_trg_id] |= FLAG_FRONT; + } else { + ASSERT(trg_comp->component[SIDE_BACK] == USHRT_MAX); + trg_comp->component[SIDE_BACK] = current_cc.cc_id; + ASSERT(!(side_membership[crt_trg_id] & FLAG_BACK)); + side_membership[crt_trg_id] |= FLAG_BACK; + } +#ifndef NDEBUG + crt_side->member_of_cc = current_cc.cc_id; +#endif + /* Store neighbour sides in a waiting stack */ + FOR_EACH(i, 0, 3) { + side_id_t neighbour_id = crt_side->facing_side_id[i]; + struct trgside* neighbour = trgsides + neighbour_id; + if(neighbour->medium != crt_side->medium) { + /* Found medium discontinuity! + * Model topology is broken. */ + /* TODO: clean problem reporting. */ + log_err(scn->dev, + "Medium mismatch found between triangle %u %s" + " side and triangle %u %s side.\n", + crt_trg_id, TRGSIDE_IS_FRONT(crt_side_id) ? "front": "back", + TRGSIDE_2_TRG(neighbour_id), + TRGSIDE_IS_FRONT(neighbour_id) ? "front" : "back"); + res = RES_BAD_ARG; + cc_descriptor_release(&current_cc); + goto error; + } + if(neighbour->list_id == FLAG_LIST_COMPONENT) { + /* Already processed */ +#ifndef NDEBUG + ASSERT(neighbour->member_of_cc == current_cc.cc_id); +#endif + continue; + } + if(neighbour->list_id == FLAG_WAITING_STACK) { + continue; /* Already processed */ + } + add_side_to_stack(scn, &stack, trgsides, neighbour_id); + } + if(darray_side_id_size_get(&stack) == 0) + break; /* Empty stack => connex component is done! */ + crt_side_id = get_side_from_stack(trgsides, &stack); + } + /* Keep track of this new connex component */ + darray_component_id_push_back(cc_ids_by_medium, &current_cc.cc_id); + find_component_Zmax(scn, triangles_tmp_array, &current_cc); + darray_cc_descriptor_push_back(connex_components, &current_cc); + } + cc_descriptor_release(&current_cc); + } + +exit: + if(component_ids_by_medium) { + FOR_EACH(m, 0, scn->nmeds) { + struct darray_component_id* cc = component_ids_by_medium + m; + darray_component_id_release(cc); + } + MEM_RM(scn->dev->allocator, component_ids_by_medium); + } + darray_side_id_release(&stack); + return res; +error: + goto exit; +} + +void +get_scn_indices(const unsigned itri, unsigned ids[3], void* ctx) { + int i; + const struct senc_scene* scene = ctx; + const struct triangle_in* trg = + darray_triangle_in_cdata_get(&scene->triangles_in) + itri; + FOR_EACH(i, 0, 3) { + ASSERT(trg->vertice_id[i] < scene->nverts); + ids[i] = (unsigned)trg->vertice_id[i]; /* Back to API type */ + } +} + +void +get_scn_position(const unsigned ivert, float pos[3], void* ctx) { + const struct senc_scene* scene = ctx; + const union double3* pt = + darray_position_cdata_get(&scene->vertices) + ivert; + f3_set_d3(pos, pt->vec); +} + +static res_T +group_connex_components + (struct senc_descriptor* desc, + struct trgside* trgsides, + struct darray_cc_descriptor* connex_components) +{ + res_T res = RES_OK; + struct mem_allocator* alloc; + struct cc_descriptor* descriptors; + struct s3d_device* s3d = NULL; + struct s3d_scene* s3d_scn = NULL; + struct s3d_shape* s3d_shp = NULL; + struct s3d_scene_view* s3d_view = NULL; + struct s3d_vertex_data attribs; + size_t tmp; + component_id_t cc_count, c; + medium_id_t infinite_medium = MEDIUM_NULL__; + side_id_t infinite_medium_first_side = SIDE_MAX__; + char infinite_medium_is_known = 0; + (void)trgsides; + + alloc = descriptor_get_allocator(desc); + descriptors = darray_cc_descriptor_data_get(connex_components); + tmp = darray_cc_descriptor_size_get(connex_components); + ASSERT(tmp <= COMPONENT_MAX__); + cc_count = (component_id_t)tmp; + + if(!cc_count) return RES_OK; /* No component to group */ + + attribs.type = S3D_FLOAT3; + attribs.usage = S3D_POSITION; + attribs.get = get_scn_position; + + res = s3d_device_create(desc->scene->dev->logger, alloc, 0, &s3d); + if(res != RES_OK) goto error; + + res = s3d_scene_create(s3d, &s3d_scn); + if(res != RES_OK) goto error; + + /* Put geometry in a 3D view */ + res = s3d_shape_create_mesh(s3d, &s3d_shp); + if(res != RES_OK) goto error; + + /* Back to API type for ntris and nverts */ + ASSERT(desc->scene->nutris < UINT_MAX); + ASSERT(desc->scene->nuverts < UINT_MAX); + res = s3d_mesh_setup_indexed_vertices(s3d_shp, (unsigned)desc->scene->nutris, + get_scn_indices, (unsigned)desc->scene->nuverts, &attribs, 1, desc->scene); + if(res != RES_OK) goto error; + + res = s3d_scene_attach_shape(s3d_scn, s3d_shp); + if(res != RES_OK) goto error; + + res = s3d_scene_view_create(s3d_scn, S3D_TRACE, &s3d_view); + if(res != RES_OK) goto error; + + /* Cast rays to find links between connex components */ + FOR_EACH(c, 0, cc_count) { + struct s3d_hit hit = S3D_HIT_NULL; + float origin[3]; + const float direction[3] = { 0, 0, 1 }; + const float range[2] = { 0, FLT_MAX }; + struct cc_descriptor* const cc = descriptors + c; + + ASSERT(cc->cc_group_root == CC_GROUP_ID_NONE); + + if(cc->max_z_nz < 0) { + /* Don't need to cast a ray */ + cc->cc_group_root = c; /* New group with self as root */ + ASSERT(desc->enclosures_count < USHRT_MAX); + cc->enclosure_id = desc->enclosures_count++; + continue; + } + + ASSERT(cc->max_z_nz != 0 + /* The only situation with nz==0 we can think of is this one: */ + || (trgsides[cc->max_z_side_id].medium + == trgsides[TRGSIDE_OPPOSITE(cc->max_z_side_id)].medium + && (trgsides[cc->max_z_side_id].facing_side_id[0] + == TRGSIDE_OPPOSITE(cc->max_z_side_id) + || trgsides[cc->max_z_side_id].facing_side_id[1] + == TRGSIDE_OPPOSITE(cc->max_z_side_id) + || trgsides[cc->max_z_side_id].facing_side_id[2] + == TRGSIDE_OPPOSITE(cc->max_z_side_id)))); + f3_set_d3(origin, cc->max_vrtx); + res = s3d_scene_view_trace_ray( + s3d_view, origin, direction, range, NULL, &hit); + if(res != RES_OK) goto error; + /* If no hit, the component is facing an infinite medium */ + if(S3D_HIT_NONE(&hit)) { + cc->cc_group_root = CC_GROUP_ROOT_INFINITE; + cc->enclosure_id = 0; + if(!infinite_medium_is_known) { + infinite_medium_is_known = 1; + infinite_medium = cc->medium; + infinite_medium_first_side = cc->max_z_side_id; + continue; + } + if(infinite_medium != cc->medium) { + /* Medium mismatch! + * Model topology is broken. */ + /* TODO: clean problem reporting. */ + log_err(desc->scene->dev, + "Medium mismatch found between triangle %u %s side and triangle" + " %u %s side, both facing infinite.\n", + TRGSIDE_2_TRG(infinite_medium_first_side), + TRGSIDE_IS_FRONT(infinite_medium_first_side) ? "front" : "back", + TRGSIDE_2_TRG(cc->max_z_side_id), + TRGSIDE_IS_FRONT(cc->max_z_side_id) ? "front" : "back"); + res = RES_BAD_ARG; + goto error; + } + /* Same medium as previous members of the group: OK */ + continue; + } else { + /* If hit, group this component */ + const trg_id_t hit_trg_id = (trg_id_t) hit.prim.prim_id; + const struct triangle_in* hit_trg_in = + darray_triangle_in_cdata_get(&desc->scene->triangles_in) + hit_trg_id; + const struct triangle_comp* hit_trg_comp = + darray_triangle_comp_cdata_get(&desc->triangles_comp) + hit_trg_id; + enum side_id hit_side = (hit.normal[2] > 0) ? SIDE_FRONT : SIDE_BACK; + const side_id_t hit_side_id = TRGIDxSIDE_2_TRGSIDE(hit_trg_id, hit_side); + + ASSERT(hit.prim.prim_id < desc->scene->ntris); + ASSERT(hit.prim.prim_id <= TRG_MAX__); + + /* Not really the root until following links */ + cc->cc_group_root = hit_trg_comp->component[hit_side]; +#ifndef NDEBUG + { + const struct cc_descriptor* hit_desc; + ASSERT(cc->cc_group_root + < darray_cc_descriptor_size_get(connex_components)); + hit_desc = darray_cc_descriptor_cdata_get(connex_components) + + cc->cc_group_root; + ASSERT(hit_desc->medium == hit_trg_in->medium[hit_side]); + ASSERT(darray_char_cdata_get(&hit_desc->side_membership)[hit_trg_id] + & TRGSIDE_IS_FRONT(hit_side) ? FLAG_FRONT : FLAG_BACK); + } +#endif + if(hit_trg_in->medium[hit_side] != cc->medium) { + /* Medium mismatch! + * Model topology is broken. */ + /* TODO: clean problem reporting. */ + log_err(desc->scene->dev, + "Medium mismatch found between triangle %u %s side and triangle" + "%u %s side facing each other.\n", + TRGSIDE_2_TRG(hit_side_id), + TRGSIDE_IS_FRONT(hit_side) ? "front" : "back", + TRGSIDE_2_TRG(cc->max_z_side_id), + TRGSIDE_IS_FRONT(cc->max_z_side_id) ? "front" : "back"); + res = RES_BAD_ARG; + goto error; + } + } + } + + if(s3d) S3D(device_ref_put(s3d)); + s3d = NULL; + if(s3d_view) S3D(scene_view_ref_put(s3d_view)); + s3d_view = NULL; + + /* Post-process links to group connex components */ + printf("\n"); + FOR_EACH(c, 0, cc_count) { + struct cc_descriptor* const cc = descriptors + c; + const struct cc_descriptor* other_desc = cc; + + while(other_desc->enclosure_id == CC_GROUP_ID_NONE) { + other_desc = darray_cc_descriptor_cdata_get(connex_components) + + other_desc->cc_group_root; + } + ASSERT(other_desc->cc_group_root != CC_GROUP_ROOT_NONE); + ASSERT(other_desc->enclosure_id != CC_GROUP_ID_NONE); + cc->cc_group_root = other_desc->cc_group_root; + cc->enclosure_id = other_desc->enclosure_id; + } + +exit: + /* Local Star3D stuff is no longer useful */ + if(s3d) S3D(device_ref_put(s3d)); + if(s3d_scn) S3D(scene_ref_put(s3d_scn)); + if(s3d_shp) S3D(shape_ref_put(s3d_shp)); + if(s3d_view) S3D(scene_view_ref_put(s3d_view)); + return res; +error: + goto exit; +} + +static res_T +scan_edges + (struct senc_scene* scn, + struct darray_neighbourhood* neighbourhood_by_edge, + struct darray_triangle_tmp* triangles_tmp_array) +{ + trg_id_t t; + struct triangle_in *triangles_in; + struct triangle_tmp *triangles_tmp; + edge_id_t max_nbedges; + /* Htable used to give an id to edges */ + struct htable_edge_id edge_ids; + res_T res = RES_OK; + + ASSERT(scn && neighbourhood_by_edge && triangles_tmp_array); + ASSERT((size_t)scn->nuverts + (size_t)scn->nutris + 2 <= EDGE_MAX__); + + /* Make some room for edges. */ + max_nbedges = (edge_id_t)(scn->nuverts + scn->nutris + 2); + htable_edge_id_init(scn->dev->allocator, &edge_ids); + res = htable_edge_id_reserve(&edge_ids, max_nbedges); + if(res != RES_OK) goto error; + res = darray_neighbourhood_reserve(neighbourhood_by_edge, max_nbedges); + if(res != RES_OK) goto error; + res = darray_triangle_tmp_resize(triangles_tmp_array, scn->nutris); + if(res != RES_OK) goto error; + + /* Loop on triangles to register edges. */ + triangles_in = darray_triangle_in_data_get(&scn->triangles_in); + triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array); + FOR_EACH(t, 0, scn->nutris) { + struct darray_neighbour* neighbour_list; + struct neighbour_info neighbour; + struct edge_neighbourhood neighbourhood; + char e; + FOR_EACH(e, 0, 3) { + edge_id_t* p_id; + edge_id_t id; + + /* Create edge. */ + set_edge(triangles_in[t].vertice_id[e], + triangles_in[t].vertice_id[(e + 1) % 3], + &neighbourhood.edge, &triangles_tmp[t].reversed_edge[e]); + /* Find edge id; create it if not already done. */ + p_id = htable_edge_id_find(&edge_ids, &neighbourhood.edge); + + if(p_id) { + id = *p_id; + } else { + /* Create id */ + size_t tmp; + tmp = htable_edge_id_size_get(&edge_ids); + ASSERT(tmp <= EDGE_MAX__); + id = (edge_id_t)tmp; + res = htable_edge_id_set(&edge_ids, &neighbourhood.edge, &id); + if(res != RES_OK) goto error; + darray_neighbour_init(scn->dev->allocator, &neighbourhood.neighbours); + res = darray_neighbourhood_push_back( + neighbourhood_by_edge, &neighbourhood); + if(res != RES_OK) goto error; + } + /* Add neighbour info to edge's neighbour list */ + neighbour_list + = &darray_neighbourhood_data_get(neighbourhood_by_edge)[id].neighbours; + neighbour.trg_id = t; + neighbour.edge_rank = e; + darray_neighbour_push_back(neighbour_list, &neighbour); + } + } + +exit: + htable_edge_id_release(&edge_ids); + return res; +error: + goto exit; +} + +static res_T +link_neighbours + (struct senc_scene* scn, + struct trgside* trgsides, + struct darray_neighbourhood* neighbourhood_by_edge, + struct darray_triangle_tmp* triangles_tmp_array, + struct darray_side_id* side_ids_by_medium) +{ + edge_id_t e, edge_count; + struct triangle_in *triangles_in; + struct triangle_tmp *triangles_tmp; + const union double3* vertices; + size_t tmp; + res_T res = RES_OK; + + ASSERT(scn && neighbourhood_by_edge && triangles_tmp_array + && side_ids_by_medium); + + /* Loop on edges. + * For each edge sort triangle sides by rotation angle + * and connect neighbours. */ + triangles_in = darray_triangle_in_data_get(&scn->triangles_in); + triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array); + vertices = darray_position_cdata_get(&scn->vertices); + tmp = darray_neighbourhood_size_get(neighbourhood_by_edge); + ASSERT(tmp <= EDGE_MAX__); + edge_count = (edge_id_t)tmp; + + FOR_EACH(e, 0, edge_count) { + double edge[3], common_edge[3], basis[9], norm, mz; + vrtx_id_t v0, v1, v2; + struct edge_neighbourhood* neighbourhood + = darray_neighbourhood_data_get(neighbourhood_by_edge) + e; + struct darray_neighbour* neighbour_list = &neighbourhood->neighbours; + side_id_t i, neighbour_count; + char mz_vid; + tmp = darray_neighbour_size_get(neighbour_list); + ASSERT(tmp <= SIDE_MAX__); + neighbour_count = (side_id_t)tmp; + ASSERT(neighbour_count); + v0 = neighbourhood->edge.vrtx0; + v1 = neighbourhood->edge.vrtx1; + d3_sub(common_edge, vertices[v1].vec, vertices[v0].vec); + if(vertices[v0].pos.z > vertices[v1].pos.z) { + mz = vertices[v0].pos.z; + mz_vid = 0; + } else { + mz = vertices[v1].pos.z; + mz_vid = 1; + } + norm = d3_normalize(common_edge, common_edge); + ASSERT(norm); + d33_basis(basis, common_edge); + d33_inverse(basis, basis); + FOR_EACH(i, 0, neighbour_count) { + struct neighbour_info* neighbour_info + = darray_neighbour_data_get(neighbour_list) + i; + struct triangle_in *trg_in = triangles_in + neighbour_info->trg_id; + struct triangle_tmp *neighbour = triangles_tmp + neighbour_info->trg_id; + v2 = trg_in->vertice_id[(neighbour_info->edge_rank + 2) % 3]; + if(vertices[v2].pos.z > mz) { + mz = vertices[v2].pos.z; + mz_vid = 2; + } + neighbour->max_z = mz; + neighbour->max_z_vrtx_id = mz_vid; + /* Compute rotation angle around common edge */ + d3_sub(edge, vertices[v2].vec, vertices[v0].vec); + d33_muld3(edge, basis, edge); + /* TODO: where are invalid triangles detected? */ + ASSERT(d3_len(edge) && (edge[0] || edge[1])); + neighbour_info->angle = atan2(edge[1], edge[0]); + if(neighbour_info->angle < 0) neighbour_info->angle += 2 * PI; + ASSERT(0 <= neighbour_info->angle && neighbour_info->angle < 2 * PI); + } + /* Sort triangles by rotation angle */ + qsort(darray_neighbour_data_get(neighbour_list), neighbour_count, + sizeof(struct neighbour_info), neighbour_cmp); + /* Link sides. + * Create cycles of sides by neighbourhood around common edge. */ + FOR_EACH(i, 0, neighbour_count) { + /* Neighbourhood info for current pair of triangles */ + const struct neighbour_info* current + = darray_neighbour_cdata_get(neighbour_list) + i; + const struct neighbour_info* ccw_neighbour + = darray_neighbour_cdata_get(neighbour_list) + (i + 1) % neighbour_count; + /* Rank of the edge of interest in triangles */ + const char crt_edge = current->edge_rank; + const char ccw_edge = ccw_neighbour->edge_rank; + /* User id of current triangles */ + const trg_id_t crt_id = current->trg_id; + const trg_id_t ccw_id = ccw_neighbour->trg_id; + /* Facing sides of triangles */ + const enum side_id crt_side + = triangles_tmp[crt_id].reversed_edge[crt_edge] ? SIDE_BACK : SIDE_FRONT; + const enum side_id ccw_side + = triangles_tmp[ccw_id].reversed_edge[ccw_edge] ? SIDE_FRONT : SIDE_BACK; + /* Index of sides in trgsides */ + const side_id_t crt_side_idx = TRGIDxSIDE_2_TRGSIDE(crt_id, crt_side); + const side_id_t ccw_side_idx = TRGIDxSIDE_2_TRGSIDE(ccw_id, ccw_side); + /* Side ptrs */ + struct trgside* const p_crt_side = trgsides + crt_side_idx; + struct trgside* const p_ccw_side = trgsides + ccw_side_idx; + /* Link sides */ + p_crt_side->facing_side_id[crt_edge] = ccw_side_idx; + p_ccw_side->facing_side_id[ccw_edge] = crt_side_idx; + /* Record sides by medium */ + p_crt_side->medium = triangles_in[crt_id].medium[crt_side]; + p_ccw_side->medium = triangles_in[ccw_id].medium[ccw_side]; + ASSERT(p_crt_side->medium < scn->nmeds); + ASSERT(p_ccw_side->medium < scn->nmeds); + add_side_to_medium_list(scn, side_ids_by_medium + p_crt_side->medium, + trgsides, crt_side_idx); + add_side_to_medium_list(scn, side_ids_by_medium + p_ccw_side->medium, + trgsides, ccw_side_idx); + } + } + return res; +} + +static res_T +build_result + (struct senc_descriptor* desc, + struct darray_cc_descriptor* connex_components) +{ + res_T res = RES_OK; + struct mem_allocator* alloc; + const struct cc_descriptor* cc_descriptors; + struct enclosure_data* enclosures; + char* side_membership = NULL; + const struct triangle_in* triangles_in; + const union double3* positions; + struct htable_vrtx_id vtable; + size_t tmp; + component_id_t cc_count, c; + enclosure_id_t e; + trg_id_t* approx_trg_counts; + + ASSERT(desc && connex_components); + + alloc = descriptor_get_allocator(desc); + tmp = darray_cc_descriptor_size_get(connex_components); + ASSERT(tmp < COMPONENT_MAX__); + cc_count = (component_id_t)tmp; + cc_descriptors = darray_cc_descriptor_cdata_get(connex_components); + darray_enclosure_resize(&desc->enclosures, desc->enclosures_count); + enclosures = darray_enclosure_data_get(&desc->enclosures); + triangles_in = darray_triangle_in_cdata_get(&desc->scene->triangles_in); + positions = darray_position_cdata_get(&desc->scene->vertices); + /* Set some enclosure data */ + approx_trg_counts + = MEM_CALLOC(alloc, desc->enclosures_count, sizeof(trg_id_t)); + if(!approx_trg_counts) { + res = RES_MEM_ERR; + goto error; + } + FOR_EACH(e, 0, desc->enclosures_count) { + struct enclosure_data* enc = enclosures + e; + ASSERT(e < UINT_MAX); + enc->header.enclosure_id = (unsigned)e; /* Back to API type */ + enc->header.is_infinite = (e == 0); + } + /* Process components to feed enclosures */ + FOR_EACH(c, 0, cc_count) { + const struct cc_descriptor* cc = cc_descriptors + c; + const enclosure_id_t e_id = cc->enclosure_id; + struct enclosure_data* enc = enclosures + e_id; + ASSERT(enc->header.enclosed_medium == MEDIUM_NULL__ /* Unset */ + || enc->header.enclosed_medium == cc->medium); /* Same medium */ + ASSERT(enc->header.enclosed_medium < UINT_MAX); + enc->header.enclosed_medium = (unsigned)cc->medium; /* Back to API type */ + approx_trg_counts[e_id] += cc->triangle_count; + } + side_membership + = MEM_ALLOC(alloc, desc->scene->ntris * sizeof(*side_membership)); + if(!side_membership) { + res = RES_MEM_ERR; + goto error; + } + htable_vrtx_id_init(alloc, &vtable); + FOR_EACH(e, 0, desc->enclosures_count) { + struct enclosure_data* enc = enclosures + e; + trg_id_t t; + memset(side_membership, 0, desc->scene->ntris * sizeof(*side_membership)); + /* Process all CC enclosures_count times to limit memory footprint. */ + FOR_EACH(c, 0, cc_count) { + const struct cc_descriptor* cc = cc_descriptors + c; + const char* cc_membership + = darray_char_cdata_get(&cc->side_membership); + if(cc->enclosure_id != e) continue; + FOR_EACH(t, 0, desc->scene->ntris) side_membership[t] |= cc_membership[t]; + } + /* Translate membership into a side and vertex lists. */ + res = darray_triangle_in_reserve(&enc->sides, approx_trg_counts[e]); + if(res != RES_OK) goto error; + res = darray_position_reserve(&enc->vertices, approx_trg_counts[e] / 2); + if(res != RES_OK) goto error; + /* New vertex numbering scheme local to the enclosure */ + htable_vrtx_id_clear(&vtable); + if(res != RES_OK) goto error; + FOR_EACH(t, 0, desc->scene->ntris) { + const struct triangle_in* trg_in = triangles_in + t; + struct triangle_in trg; + int i; + if(!side_membership[t]) continue; + ++enc->header.unique_triangle_count; + FOR_EACH(i, 0, 3) { + vrtx_id_t* id = htable_vrtx_id_find(&vtable, trg_in->vertice_id + i); + if(id) { + trg.vertice_id[i] = *id; /* Known vertex */ + } else { + /* Create new association */ + tmp = htable_vrtx_id_size_get(&vtable); + ASSERT(tmp == darray_position_size_get(&enc->vertices)); + ASSERT(tmp < VRTX_MAX__); + trg.vertice_id[i] = (vrtx_id_t)tmp; + res = htable_vrtx_id_set( + &vtable, trg_in->vertice_id + i, trg.vertice_id + i); + if(res != RES_OK) goto error; + res = darray_position_push_back( + &enc->vertices, positions + trg_in->vertice_id[i]); + if(res != RES_OK) goto error; + ++enc->header.vertices_count; + } + } + FOR_EACH(i, 0, 2) trg.medium[i] = trg_in->medium[i]; + if(side_membership[t] & FLAG_FRONT) { + ++enc->header.triangle_count; + res = darray_triangle_in_push_back(&enc->sides, &trg); + if(res != RES_OK) goto error; + } + if(side_membership[t] & FLAG_BACK) { + ++enc->header.triangle_count; + triangle_in_flip(&trg); + res = darray_triangle_in_push_back(&enc->sides, &trg); + if(res != RES_OK) goto error; + } + } + } + +exit: + htable_vrtx_id_release(&vtable); + if(side_membership) MEM_RM(alloc, side_membership); + if(approx_trg_counts) MEM_RM(alloc, approx_trg_counts); + return res; +error: + goto exit; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +senc_scene_analyze(struct senc_scene* scn, struct senc_descriptor** out_desc) +{ + res_T res = RES_OK; + unsigned m; + struct senc_descriptor* desc = NULL; + /* By triangle tmp data */ + struct darray_triangle_tmp triangles_tmp; + char triangles_tmp_initialized = 0; + /* Arrays of trgside ids, organized by medium */ + struct darray_side_id* side_ids_by_medium = NULL; + /* Array of connex components. + * They are refered to by arrays of ids. */ + struct darray_cc_descriptor connex_components; + char connex_components_initialized = 0; + /* Triangle neighbourhood by edge. */ + struct darray_neighbourhood neighbourhood_by_edge; + char neighbourhood_by_edge_initialized = 0; + /* Array of triangle sides. */ + struct trgside* trgsides = NULL; + + if(!scn || !out_desc) return RES_BAD_ARG; + + desc = descriptor_create(scn); + if(!desc) { + res = RES_MEM_ERR; + goto error; + } + + if(!scn->nutris) goto exit; + + darray_triangle_tmp_init(scn->dev->allocator, &triangles_tmp); + triangles_tmp_initialized = 1; + darray_neighbourhood_init(scn->dev->allocator, &neighbourhood_by_edge); + neighbourhood_by_edge_initialized = 1; + + /* Step 1: */ + res = scan_edges(scn, &neighbourhood_by_edge, &triangles_tmp); + if(res != RES_OK) { + log_err(scn->dev, "%s: could not scan edges.\n", FUNC_NAME); + goto error; + } + + side_ids_by_medium + = MEM_ALLOC(scn->dev->allocator, scn->nmeds * sizeof(struct darray_uint)); + if(!side_ids_by_medium) { + res = RES_MEM_ERR; + goto error; + } + FOR_EACH(m, 0, scn->nmeds) { + struct darray_side_id* const s = side_ids_by_medium + m; + darray_side_id_init(scn->dev->allocator, s); + } + trgsides + = MEM_CALLOC(scn->dev->allocator, 2 * scn->nutris, sizeof(struct trgside)); + if(!trgsides) { + res = RES_MEM_ERR; + goto error; + } + + res = link_neighbours(scn, trgsides, &neighbourhood_by_edge, + &triangles_tmp, side_ids_by_medium); + if(res != RES_OK) { + log_err(scn->dev, "%s: could not link neighbours.\n", FUNC_NAME); + goto error; + } + + darray_neighbourhood_release(&neighbourhood_by_edge); + neighbourhood_by_edge_initialized = 0; + darray_cc_descriptor_init(scn->dev->allocator, &connex_components); + connex_components_initialized = 1; + + res = extract_connex_components(desc, trgsides, &connex_components, + &triangles_tmp, side_ids_by_medium); + if(res != RES_OK) { + log_err(scn->dev, + "%s: could not extract connex components from scene.\n", FUNC_NAME); + goto error; + } + + darray_triangle_tmp_release(&triangles_tmp); + triangles_tmp_initialized = 0; + if(side_ids_by_medium) { + FOR_EACH(m, 0, scn->nmeds) { + struct darray_side_id* cc = side_ids_by_medium + m; + darray_side_id_release(cc); + } + MEM_RM(scn->dev->allocator, side_ids_by_medium); + } + side_ids_by_medium = NULL; + + res = group_connex_components(desc, trgsides, &connex_components); + if(res != RES_OK) { + log_err(scn->dev, + "%s: could not group connex components from scene.\n", FUNC_NAME); + goto error; + } + + /* Build result. */ + res = build_result(desc, &connex_components); + if(res != RES_OK) { + log_err(scn->dev, "%s: could not build result.\n", FUNC_NAME); + goto error; + } + + darray_triangle_comp_purge(&desc->triangles_comp); + + +exit: + if(connex_components_initialized) + darray_cc_descriptor_release(&connex_components); + if(neighbourhood_by_edge_initialized) + darray_neighbourhood_release(&neighbourhood_by_edge); + if(triangles_tmp_initialized) darray_triangle_tmp_release(&triangles_tmp); + if(side_ids_by_medium) { + FOR_EACH(m, 0, scn->nmeds) { + struct darray_side_id* cc = side_ids_by_medium + m; + darray_side_id_release(cc); + } + MEM_RM(scn->dev->allocator, side_ids_by_medium); + } + if(trgsides) MEM_RM(scn->dev->allocator, trgsides); + if(desc) *out_desc = desc; + return res; +error: + if(desc) SENC(descriptor_ref_put(desc)); + desc = NULL; + goto exit; +} diff --git a/src/senc_scene_analyze_c.h b/src/senc_scene_analyze_c.h @@ -0,0 +1,319 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_SCNENE_ANALYZE_C_H +#define SENC_SCNENE_ANALYZE_C_H + +#include "senc_scene_c.h" +#include "senc_internal_types.h" + +#include <rsys/mem_allocator.h> +#include <rsys/dynamic_array_char.h> +#include <rsys/hash_table.h> +#include <rsys/double3.h> + + +/* This one is used as an index to arrays */ +enum side_id { + SIDE_FRONT = 0, + SIDE_BACK = 1 +}; + +/* This one is used as flag */ +enum side_flag { + FLAG_FRONT = BIT(0), + FLAG_BACK = BIT(1) +}; + +enum list_id { + FLAG_NO_LIST = 0, + FLAG_LIST_BY_MEDIUM = BIT(1), + FLAG_LIST_COMPONENT = BIT(2), + FLAG_WAITING_STACK = BIT(3), + FLAG_ANY_LIST = 0xFF +}; + +/* Triangle edge struct and basic functions */ +struct trg_edge { + vrtx_id_t vrtx0, vrtx1; +}; + +FINLINE int +edge_ok(const struct trg_edge* edge) { + return(edge + && edge->vrtx0 <= VRTX_MAX__ + && edge->vrtx1 <= VRTX_MAX__ + && edge->vrtx0 < edge->vrtx1); +} + +FINLINE void +set_edge + (const vrtx_id_t vrtx0, + const vrtx_id_t vrtx1, + struct trg_edge* edge, + char* reversed) +{ + ASSERT(edge && reversed && vrtx0 != vrtx1); + if(vrtx0 < vrtx1) { + edge->vrtx0 = vrtx0; + edge->vrtx1 = vrtx1; + *reversed = 0; /* Non reversed edge */ + } else { + edge->vrtx0 = vrtx1; + edge->vrtx1 = vrtx0; + *reversed = 1; /* Reversed edge */ + } + ASSERT(edge_ok(edge)); +} + +FINLINE int +edge_eq(const struct trg_edge* e1, const struct trg_edge* e2) +{ + ASSERT(edge_ok(e1) && edge_ok(e2)); + return e1->vrtx0 == e2->vrtx0 && e1->vrtx1 == e2->vrtx1; +} + +/* Information kept during the building of side groups. */ +struct trgside { + /* Rank of the trgside facing this trgside through its edges */ + side_id_t facing_side_id[3]; + /* Id of this trgside's medium */ + medium_id_t medium; + /* The list containing the trgside; made of enum list_id flags */ + char list_id; + + /* Implicit information that we don't need to store: + * - triangle_id + * - side + * This is due to the memory layout of the elt darray: + * front(trg_0), back(trg_0), front(trg_1), back(trg_1), ... */ + +#ifndef NDEBUG + component_id_t member_of_cc; +#endif +}; + +static FINLINE trg_id_t +TRGSIDE_2_TRG(side_id_t s) { + ASSERT(((size_t)s >> 1) <= TRG_MAX__); + return s >> 1; } + +static FINLINE int +TRGSIDE_IS_FRONT(side_id_t s) { + return (s & 1) == 0; +} + +static FINLINE side_id_t +TRGSIDE_OPPOSITE(side_id_t s) { + return s ^ (~1); +} + +static FINLINE enum side_id +TRGSIDE_2_SIDE(side_id_t s) { + return (s & 1) ? SIDE_BACK : SIDE_FRONT; +} + +static FINLINE side_id_t +TRGIDxSIDE_2_TRGSIDE(trg_id_t t, enum side_id i) { + ASSERT((((size_t)t << 1) | (i == SIDE_BACK)) < SIDE_MAX__); + return (side_id_t)((t << 1) | (i == SIDE_BACK)); +} + +/* Descriptors for connex component. + * Define lists of trg sides starting from a given head. + * Also keeps the maximum z info of the component + * along with the associated triangle id, + * a star3D id */ +#define CC_ID_NONE USHRT_MAX +#define CC_GROUP_ROOT_NONE USHRT_MAX +#define CC_GROUP_ROOT_INFINITE (USHRT_MAX-1) +#define CC_GROUP_ID_NONE USHRT_MAX +struct cc_descriptor { + double max_vrtx[3]; + double max_z_nz; + side_id_t max_z_side_id; + vrtx_id_t max_z_vrtx_id; + trg_id_t triangle_count; + medium_id_t medium; + component_id_t cc_id; + component_id_t cc_group_root; + enclosure_id_t enclosure_id; + /* TODO: use only 1 bit per side (now 1 char per triangle) */ + struct darray_char side_membership; +}; +extern const struct cc_descriptor CC_DESCRIPTOR_NULL; + +static FINLINE void +cc_descriptor_init(struct mem_allocator* alloc, struct cc_descriptor* data) +{ + ASSERT(data); + *data = CC_DESCRIPTOR_NULL; + darray_char_init(alloc, &data->side_membership); +} + +static FINLINE void +cc_descriptor_release(struct cc_descriptor* data) +{ + ASSERT(data); + darray_char_release(&data->side_membership); +} + +static FINLINE void +cc_descriptor_clear(struct cc_descriptor* data) +{ + d3_set(data->max_vrtx, CC_DESCRIPTOR_NULL.max_vrtx); + data->max_z_nz = CC_DESCRIPTOR_NULL.max_z_nz; + data->max_z_side_id = CC_DESCRIPTOR_NULL.max_z_side_id; + data->max_z_vrtx_id = CC_DESCRIPTOR_NULL.max_z_vrtx_id; + data->triangle_count = CC_DESCRIPTOR_NULL.triangle_count; + data->medium = CC_DESCRIPTOR_NULL.medium; + data->cc_id = CC_DESCRIPTOR_NULL.cc_id; + data->cc_group_root = CC_DESCRIPTOR_NULL.cc_group_root; + data->enclosure_id = CC_DESCRIPTOR_NULL.enclosure_id; + darray_char_clear(&data->side_membership); +} + +static FINLINE void +cc_descriptor_purge(struct cc_descriptor* data) +{ + d3_set(data->max_vrtx, CC_DESCRIPTOR_NULL.max_vrtx); + data->max_z_nz = CC_DESCRIPTOR_NULL.max_z_nz; + data->max_z_side_id = CC_DESCRIPTOR_NULL.max_z_side_id; + data->max_z_vrtx_id = CC_DESCRIPTOR_NULL.max_z_vrtx_id; + data->triangle_count = CC_DESCRIPTOR_NULL.triangle_count; + data->medium = CC_DESCRIPTOR_NULL.medium; + data->cc_id = CC_DESCRIPTOR_NULL.cc_id; + data->cc_group_root = CC_DESCRIPTOR_NULL.cc_group_root; + data->enclosure_id = CC_DESCRIPTOR_NULL.enclosure_id; + darray_char_purge(&data->side_membership); +} + +static FINLINE res_T +cc_descriptor_copy(struct cc_descriptor* dst, const struct cc_descriptor* src) +{ + ASSERT(dst && src); + d3_set(dst->max_vrtx, src->max_vrtx); + dst->max_z_nz = src->max_z_nz; + dst->max_z_side_id = src->max_z_side_id; + dst->max_z_vrtx_id = src->max_z_vrtx_id; + dst->triangle_count = src->triangle_count; + dst->medium = src->medium; + dst->cc_id = src->cc_id; + dst->cc_group_root = src->cc_group_root; + dst->enclosure_id = src->enclosure_id; + return darray_char_copy(&dst->side_membership, &src->side_membership); +} + +static FINLINE res_T +cc_descriptor_copy_and_release(struct cc_descriptor* dst, struct cc_descriptor* src) +{ + ASSERT(dst && src); + d3_set(dst->max_vrtx, src->max_vrtx); + dst->max_z_nz = src->max_z_nz; + dst->max_z_side_id = src->max_z_side_id; + dst->max_z_vrtx_id = src->max_z_vrtx_id; + dst->triangle_count = src->triangle_count; + dst->medium = src->medium; + dst->cc_id = src->cc_id; + dst->cc_group_root = src->cc_group_root; + dst->enclosure_id = src->enclosure_id; + return darray_char_copy_and_release(&dst->side_membership, &src->side_membership); +} + +#define DARRAY_NAME cc_descriptor +#define DARRAY_DATA struct cc_descriptor +#define DARRAY_FUNCTOR_INIT cc_descriptor_init +#define DARRAY_FUNCTOR_RELEASE cc_descriptor_release +#define DARRAY_FUNCTOR_COPY cc_descriptor_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE cc_descriptor_copy_and_release +#include <rsys/dynamic_array.h> + +/* Triangle information. + * Depending on lifespan, information is kept in different places: + * - triangle_in for user provided information (kept in scene) + * - triangle_comp for information describing components (kept in senc_descriptor) + * - triangle_tmp for tmp information (kept until triangle_comp is ready) */ +struct triangle_tmp { + /* Are the edges of the triangle defined in the same order than + * the edges they are linked to? */ + char reversed_edge[3]; + /* tmp data used to find the +Z-most vertex of components */ + char max_z_vrtx_id; + double max_z; +}; +#define DARRAY_NAME triangle_tmp +#define DARRAY_DATA struct triangle_tmp +#include <rsys/dynamic_array.h> + +#define HTABLE_NAME edge_id +#define HTABLE_KEY struct trg_edge +#define HTABLE_DATA edge_id_t +#define HTABLE_KEY_FUNCTOR_EQ edge_eq +#include <rsys/hash_table.h> + +struct neighbour_info { + trg_id_t trg_id; + /* Rank of the vrtx in the triangle (in [0 2]) */ + char edge_rank; + double angle; +}; +#define DARRAY_NAME neighbour +#define DARRAY_DATA struct neighbour_info +#include <rsys/dynamic_array.h> + +struct edge_neighbourhood { + struct trg_edge edge; + struct darray_neighbour neighbours; +}; +static void +neighbourhood_init + (struct mem_allocator* alloc, + struct edge_neighbourhood* n) +{ + ASSERT(n); + darray_neighbour_init(alloc, &n->neighbours); +} +static res_T +neighbourhood_copy + (struct edge_neighbourhood* dst, + const struct edge_neighbourhood* src) +{ + ASSERT(src && dst); + dst->edge = src->edge; + return darray_neighbour_copy(&dst->neighbours, &src->neighbours); +} +static void +neighbourhood_release(struct edge_neighbourhood* n) { + ASSERT(n); + darray_neighbour_release(&n->neighbours); +} +static res_T +neighbourhood_copy_and_release + (struct edge_neighbourhood* dst, + struct edge_neighbourhood* src) +{ + ASSERT(src && dst); + dst->edge = src->edge; + return darray_neighbour_copy_and_release(&dst->neighbours, &src->neighbours); +} +#define DARRAY_NAME neighbourhood +#define DARRAY_DATA struct edge_neighbourhood +#define DARRAY_FUNCTOR_INIT neighbourhood_init +#define DARRAY_FUNCTOR_COPY neighbourhood_copy +#define DARRAY_FUNCTOR_RELEASE neighbourhood_release +#define DARRAY_FUNCTOR_COPY_AND_RELEASE neighbourhood_copy_and_release +#include <rsys/dynamic_array.h> + +#endif /* SENC_SCNENE_ANALYZE_C_H */ diff --git a/src/senc_scene_c.h b/src/senc_scene_c.h @@ -0,0 +1,215 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#ifndef SENC_SCNENE_C_H +#define SENC_SCNENE_C_H + +#include "senc_internal_types.h" + +#include <rsys/ref_count.h> +#include <rsys/dynamic_array.h> +#include <rsys/hash_table.h> +#include <rsys/dynamic_array_char.h> + +struct mem_allocator; + +union double3 { + struct { + double x, y, z; + } pos; + double vec[3]; +}; +#define DARRAY_NAME position +#define DARRAY_DATA union double3 +#include <rsys/dynamic_array.h> +/* Triangle information. + * Depending on lifespan, information is kept in different places: + * - triangle_in for user provided information (kept in scene) + * - triangle_comp for information describing components (kept in senc_descriptor) + * - triangle_tmp for tmp information (kept until triangle_comp is ready) */ +struct triangle_in { + /* Ids of the triangle's vertices */ + vrtx_id_t vertice_id[3]; + /* Ids of this triangle's mediums */ + medium_id_t medium[2]; +}; + +#ifndef NDEBUG +static FINLINE void +triangle_in_init(struct mem_allocator* alloc, struct triangle_in* trg) { + int i; + (void) alloc; + ASSERT(trg); + FOR_EACH(i, 0, 3) trg->vertice_id[i] = VRTX_NULL__; + FOR_EACH(i, 0, 2) trg->medium[i] = MEDIUM_NULL__; +} +#define DARRAY_FUNCTOR_INIT triangle_in_init +#endif + +#define DARRAY_NAME triangle_in +#define DARRAY_DATA struct triangle_in +#include <rsys/dynamic_array.h> + +static FINLINE void +triangle_in_flip(struct triangle_in* trg) { + vrtx_id_t v; + medium_id_t m; + ASSERT(trg); + v = trg->vertice_id[1]; + trg->vertice_id[1] = trg->vertice_id[2]; + trg->vertice_id[2] = v; + m = trg->medium[0]; + trg->medium[0] = trg->medium[1]; + trg->medium[1] = m; +} + +FINLINE int +vrtx_eq(const union double3* v1, const union double3* v2) +{ + ASSERT(v1 && v2); + return (v1->pos.x == v2->pos.x + && v1->pos.y == v2->pos.y + && v1->pos.z == v2->pos.z); +} + +#define HTABLE_NAME vrtx +#define HTABLE_KEY union double3 +#define HTABLE_DATA vrtx_id_t +#define HTABLE_KEY_FUNCTOR_EQ vrtx_eq +#include <rsys/hash_table.h> + +#define DARRAY_NAME vrtx_id +#define DARRAY_DATA vrtx_id_t +#include <rsys/dynamic_array.h> + +union vrtx_id3 { + struct { + vrtx_id_t v0, v1, v2; + } pos; + vrtx_id_t vec[3]; +}; + +FINLINE char /* Return 1 if reversed */ +trg_make_key(union vrtx_id3* k, const vrtx_id_t t[3]) +{ + ASSERT(t); + ASSERT(t[0] != t[1] && t[0] != t[2] && t[1] != t[2]); + if(t[0] < t[2]) { + if(t[0] < t[1]) { + k->vec[0] = t[0]; + if(t[1] < t[2]) { + k->vec[1] = t[1]; + k->vec[2] = t[2]; + return 0; + } else { + k->vec[1] = t[2]; + k->vec[2] = t[1]; + return 1; + } + } else { + k->vec[0] = t[1]; + if(t[0] < t[2]) { + k->vec[1] = t[0]; + k->vec[2] = t[2]; + return 1; + } else { + k->vec[1] = t[2]; + k->vec[2] = t[0]; + return 0; + } + } + } else if(t[2] < t[1]) { + k->vec[0] = t[2]; + if(t[0] < t[1]) { + k->vec[1] = t[0]; + k->vec[2] = t[1]; + return 0; + } + else { + k->vec[1] = t[1]; + k->vec[2] = t[0]; + return 1; + } + } + else { + k->vec[0] = t[1]; + if(t[0] < t[2]) { + k->vec[1] = t[0]; + k->vec[2] = t[2]; + return 1; + } + else { + k->vec[1] = t[2]; + k->vec[2] = t[0]; + return 0; + } + } +} + +FINLINE int +trg_key_eq(const union vrtx_id3* k1, const union vrtx_id3* k2) +{ + ASSERT(k1 && k2); + ASSERT(k1->vec[0] < k1->vec[1] && k1->vec[1] < k1->vec[2]); + ASSERT(k2->vec[0] < k2->vec[1] && k2->vec[1] < k2->vec[2]); + return (k1->vec[0] == k2->vec[0]) + && (k1->vec[1] == k2->vec[1]) + && (k1->vec[2] == k2->vec[2]); +} + +#define HTABLE_NAME trg +#define HTABLE_KEY union vrtx_id3 +#define HTABLE_DATA trg_id_t +#define HTABLE_KEY_FUNCTOR_EQ trg_key_eq +#include <rsys/hash_table.h> + +#define DARRAY_NAME trg_id +#define DARRAY_DATA trg_id_t +#include <rsys/dynamic_array.h> + +struct senc_scene { + /* Triangle information as given by user; no duplicates here */ + struct darray_triangle_in triangles_in; + + /* Vertex information as given by user; no duplicates here */ + struct darray_position vertices; + + /* Htables used to detect duplicate vertices. + * As we rely on edges (i.e. vertice IDs) to build + * neighbourhoods, we need vertice unicity. */ + /* Keep each unique vertex; no duplicates here. */ + struct htable_vrtx unique_vertices; + /* Associate each vertex with its rank in unique_vertices. */ + struct darray_vrtx_id unique_vertice_ids; + + /* Htables used to detect duplicate triangles. */ + /* Keep each unique triangle; no duplicates here. */ + struct htable_trg unique_triangles; + /* Associate each triangle with its rank in unique_triangles... */ + struct darray_trg_id unique_triangle_ids; + /* ... and a bool indicating reverse vertices order. */ + struct darray_char triangle_reversed; + + /* Keep sizes */ + unsigned ngeoms; /* Not used yet (just counted). */ + trg_id_t ntris, nutris; /* Trg count, unique trg count */ + vrtx_id_t nverts, nuverts; /* Vrtx count, unique vrtx count */ + medium_id_t nmeds; + + ref_T ref; + struct senc_device* dev; +}; + +#endif /* SENC_SCNENE_C_H */ diff --git a/src/test_senc_cube_behind_cube.c b/src/test_senc_cube_behind_cube.c @@ -0,0 +1,84 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "test_senc_utils.h" + +#include <rsys/double3.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc_descriptor* desc = NULL; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct context ctx; + (void)argc, (void)argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(senc_device_create + (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); + + /* Create the scene */ + CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + + ctx.positions = box_vertices; + ctx.indices = box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d3(ctx.offset, 0, 0, 0); + ctx.front_medium = 0; + ctx.back_medium = 1; + + /* First cube */ + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + /* +Z from the first cube, + * big enough to prevent rays from the first cube to miss this one */ + d3(ctx.offset, -2, -2, 20); + ctx.scale = 5; + + /* Second cube */ + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + /* Even further in +Z, even bigger */ + d3(ctx.offset, -3, -3, 30); + ctx.scale = 7; + ctx.front_medium = 1; + ctx.back_medium = 0; + + /* Third cube */ + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK); + desc = NULL; + CHK(senc_scene_analyze(scn, &desc) == RES_BAD_ARG); + + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_senc_cube_in_cube.c b/src/test_senc_cube_in_cube.c @@ -0,0 +1,90 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "test_senc_utils.h" + +#include <rsys/double3.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc_descriptor* desc = NULL; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct context ctx; + (void)argc, (void)argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(senc_device_create + (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); + + /* Create the scene */ + CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + + ctx.positions = box_vertices; + ctx.indices = box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d3(ctx.offset, 0, 0, 0); + /* Smallest cube exterior is medium 0 */ + ctx.front_medium = 0; + /* Smallest cube interior is medium 1 */ + ctx.back_medium = 1; + + /* First cube */ + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + d3(ctx.offset, -1, -1, -1); + ctx.scale = 3; + /* Bigger cube exterior is medium 1 */ + /* Bigger cube interior is medium 0 */ + ctx.reverse_vrtx = 1; + + /* Second cube */ + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + d3(ctx.offset, -4, -4, -4); + ctx.scale = 10; + ctx.reverse_vrtx = 1; + ctx.reverse_med = 1; + /* Biggest cube exterior is medium 1 */ + ctx.front_medium = 1; + /* Biggest cube interior is medium 0 */ + ctx.back_medium = 0; /* mismatch with cube 2 */ + + /* Third cube */ + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK); + desc = NULL; + CHK(senc_scene_analyze(scn, &desc) == RES_BAD_ARG); + + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_senc_descriptor.c b/src/test_senc_descriptor.c @@ -0,0 +1,121 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "test_senc_utils.h" + +#include <rsys/float3.h> +#include <rsys/double3.h> + +#include <star/s3d.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct senc_descriptor* desc = NULL; + struct senc_enclosure* enc = NULL; + struct context ctx; + unsigned count; + (void) argc, (void) argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) + == RES_OK); + + CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + + /* A 3D cube */ + ctx.positions = box_vertices; + ctx.indices = box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d3(ctx.offset, 0, 0, 0); + ctx.front_medium = 0; + ctx.back_medium = 1; + + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_ref_get(desc) == RES_OK); + CHK(senc_descriptor_ref_get(NULL) == RES_BAD_ARG); + CHK(senc_descriptor_ref_put(NULL) == RES_BAD_ARG); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + + CHK(senc_descriptor_get_enclosure_count(NULL, &count) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count(desc, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count(NULL, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK); + + CHK(count == 2); + + CHK(senc_descriptor_get_enclosure(NULL, 0, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(desc, count, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(desc, 0, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(NULL, count, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(NULL, 0, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(desc, count, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(NULL, count, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(desc, 0, &enc) == RES_OK); + + CHK(senc_descriptor_get_global_vertices_count(NULL, &count) == RES_BAD_ARG); + CHK(senc_descriptor_get_global_vertices_count(desc, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_global_vertices_count(desc, &count) == RES_OK); + CHK(count == box_nvertices); + + CHK(senc_descriptor_get_global_triangle_count(NULL, &count) == RES_BAD_ARG); + CHK(senc_descriptor_get_global_triangle_count(desc, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_global_triangle_count(desc, &count) == RES_OK); + CHK(count == box_ntriangles); + + /* Add valid duplicate geometry */ + CHK(senc_descriptor_ref_put(desc) == RES_OK); + desc = NULL; + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_get_global_vertices_count(desc, &count) == RES_OK); + /* Duplicate vertices have been replaced */ + CHK(count == box_nvertices); + + CHK(senc_descriptor_get_global_triangle_count(desc, &count) == RES_OK); + /* Duplicate triangles have been replaced */ + CHK(count == box_ntriangles); + + /* Add invalid duplicate geometry */ + CHK(senc_descriptor_ref_put(desc) == RES_OK); + desc = NULL; + ctx.front_medium = 1; + ctx.back_medium = 0; + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_BAD_ARG); + + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK); + CHK(senc_enclosure_ref_put(enc) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + + return 0; +} diff --git a/src/test_senc_device.c b/src/test_senc_device.c @@ -0,0 +1,77 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "test_senc_utils.h" + +#include <rsys/logger.h> + +static INLINE void +log_stream(const char* msg, void* ctx) +{ + ASSERT(msg); + (void) msg, (void) ctx; + printf("%s\n", msg); +} + +int +main(int argc, char** argv) +{ + struct logger logger; + struct mem_allocator allocator; + struct senc_device* dev; + (void)argc, (void)argv; + + CHK(senc_device_create(NULL, NULL, 0, 0, NULL) == RES_BAD_ARG); + CHK(senc_device_create(NULL, NULL, 0, 0, &dev) == RES_BAD_ARG); + CHK(senc_device_create(NULL, NULL, 1, 0, &dev) == RES_OK); + CHK(senc_device_ref_get(NULL) == RES_BAD_ARG); + CHK(senc_device_ref_get(dev) == RES_OK); + CHK(senc_device_ref_put(NULL) == RES_BAD_ARG); + CHK(senc_device_ref_put(dev) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) + == RES_OK); + + CHK(MEM_ALLOCATED_SIZE(&allocator) == 0); + CHK(senc_device_create(NULL, &allocator, 1, 0, NULL) == RES_BAD_ARG); + CHK(senc_device_create(NULL, &allocator, 1, 0, &dev) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + CHK(MEM_ALLOCATED_SIZE(&allocator) == 0); + + CHK(logger_init(&allocator, &logger) == RES_OK); + logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL); + logger_set_stream(&logger, LOG_ERROR, log_stream, NULL); + logger_set_stream(&logger, LOG_WARNING, log_stream, NULL); + + CHK(senc_device_create(&logger, NULL, 1, 0, NULL) == RES_BAD_ARG); + CHK(senc_device_create(&logger, NULL, 1, 0, &dev) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + + CHK(senc_device_create(&logger, &allocator, 1, 0, NULL) == RES_BAD_ARG); + CHK(senc_device_create(&logger, &allocator, 1, 0, &dev) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + + CHK(senc_device_create + (&logger, &allocator, SENC_NTHREADS_DEFAULT, 0, &dev) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + + logger_release(&logger); + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_senc_enclosure.c b/src/test_senc_enclosure.c @@ -0,0 +1,181 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "senc_s3d_wrapper.h" +#include "test_senc_utils.h" + +#include <rsys/float3.h> +#include <rsys/double3.h> + +#include <star/s3d.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc_descriptor* desc = NULL; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct senc_enclosure* enclosures[2] = { NULL, NULL }; + struct senc_enclosure* enclosure; + const struct enclosure_header* header; + struct s3d_device* s3d = NULL; + struct s3d_scene* s3d_scn = NULL; + struct s3d_shape* s3d_shp = NULL; + struct s3d_vertex_data s3d_attribs; + struct context ctx; + unsigned i, n, count; + (void) argc, (void) argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) + == RES_OK); + + CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + + s3d_attribs.type = S3D_FLOAT3; + s3d_attribs.usage = S3D_POSITION; + s3d_attribs.get = senc_enclosure_get_vertices__; + + CHK(s3d_device_create(NULL, &allocator, 0, &s3d) == RES_OK); + + CHK(s3d_scene_create(s3d, &s3d_scn) == RES_OK); + + /* A 3D cube. + * 2 enclosures (inside, outside) sharing the same triangles, but opposite sides */ + ctx.positions = box_vertices; + ctx.indices = box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d3(ctx.offset, 0, 0, 0); + ctx.front_medium = 0; + ctx.back_medium = 1; + + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK); + CHK(count == 2); + + FOR_EACH(i, 0, count) { + CHK(senc_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); + + CHK(senc_enclosure_get_header(NULL, &header) == RES_BAD_ARG); + CHK(senc_enclosure_get_header(enclosure, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_header(NULL, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + + CHK(header->enclosure_id == i); + CHK(header->enclosed_medium == (i == 0 ? 0U : 1U)); + CHK(header->triangle_count == box_ntriangles); + CHK(header->unique_triangle_count == box_ntriangles); + CHK(header->vertices_count == box_nvertices); + CHK(header->is_infinite == (i == 0)); + + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + } + + FOR_EACH(i, 0, 2) + CHK(senc_descriptor_get_enclosure(desc, i, enclosures + i) == RES_OK); + FOR_EACH(n, 0, box_ntriangles) { + unsigned indices[2][3]; + /* Read same triangles in both enclosures */ + FOR_EACH(i, 0, 2) + senc_enclosure_get_indices(enclosures[i], n, indices[i]); + /* Same triangles, opposite sides */ + CHK(indices[0][0] == indices[1][0]); + CHK(indices[0][1] == indices[1][2]); + CHK(indices[0][2] == indices[1][1]); + } + FOR_EACH(i, 0, 2) + CHK(senc_enclosure_ref_put(enclosures[i]) == RES_OK); + + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + + /* Same 3D cube, but with a hole (incomplete). + * 1 single enclosure including both sides of triangles */ + + CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + + ctx.positions = box_vertices; + ctx.indices = box_indices; + ctx.scale = 1; + d3(ctx.offset, 0, 0, 0); + ctx.front_medium = 0; + ctx.back_medium = 0; + + CHK(senc_scene_add_geometry(scn, box_ntriangles - 1, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK); + CHK(count == 1); + + CHK(senc_descriptor_get_enclosure(desc, 0, &enclosure) == RES_OK); + + CHK(senc_enclosure_get_header(NULL, &header) == RES_BAD_ARG); + CHK(senc_enclosure_get_header(enclosure, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_header(NULL, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + + CHK(header->enclosure_id == 0); + CHK(header->enclosed_medium == 0); + CHK(header->triangle_count == 2 * header->unique_triangle_count); + CHK(header->unique_triangle_count == box_ntriangles - 1); + CHK(header->vertices_count == box_nvertices); + CHK(header->is_infinite == 1); + + FOR_EACH(n, 0, header->unique_triangle_count) { + unsigned indices[2][3]; + /* Read 2 consecutive triangles in the enclosure */ + FOR_EACH(i, 0, 2) + senc_enclosure_get_indices(enclosure, 2 * n + i, indices[i]); + /* Same triangles, opposite sides */ + CHK(indices[0][0] == indices[1][0]); + CHK(indices[0][1] == indices[1][2]); + CHK(indices[0][2] == indices[1][1]); + + /* Put geometry in a 3D view */ + CHK(s3d_shape_create_mesh(s3d, &s3d_shp) == RES_OK); + + CHK(s3d_mesh_setup_indexed_vertices(s3d_shp, header->triangle_count, + senc_enclosure_get_indices__, header->vertices_count, &s3d_attribs, + 1, enclosure) + == RES_OK); + + CHK(s3d_scene_attach_shape(s3d_scn, s3d_shp) == RES_OK); + CHK(s3d_shape_ref_put(s3d_shp) == RES_OK); + } + + CHK(s3d_device_ref_put(s3d) == RES_OK); + CHK(s3d_scene_ref_put(s3d_scn) == RES_OK); + + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + + return 0; +} diff --git a/src/test_senc_sample_enclosure.c b/src/test_senc_sample_enclosure.c @@ -0,0 +1,139 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) +* +* 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "senc_s3d_wrapper.h" +#include "test_senc_utils.h" + +#include <rsys/float3.h> +#include <rsys/double3.h> + +#include <star/s3d.h> +#include <star/ssp.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc_descriptor* desc = NULL; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct senc_enclosure* enclosure = NULL; + const struct enclosure_header* header = NULL; + struct s3d_device* s3d = NULL; + struct s3d_scene* s3d_scn = NULL; + struct s3d_scene_view* s3d_view = NULL; + struct s3d_shape* s3d_shp = NULL; + struct s3d_primitive prim; + struct s3d_vertex_data vrtx_get; + struct ssp_rng* rng; + struct context ctx; + int i; + float st[2]; + (void) argc, (void) argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) + == RES_OK); + + CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + + vrtx_get.type = S3D_FLOAT3; + vrtx_get.usage = S3D_POSITION; + vrtx_get.get = senc_enclosure_get_vertices__; + + CHK(s3d_device_create(NULL, &allocator, 0, &s3d) == RES_OK); + + CHK(s3d_scene_create(s3d, &s3d_scn) == RES_OK); + + /* A 3D cube, but with a hole (incomplete). + * 1 single enclosure including both sides of triangles */ + + ctx.positions = box_vertices; + ctx.indices = box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d3(ctx.offset, 0, 0, 0); + ctx.front_medium = 0; + ctx.back_medium = 0; + + CHK(senc_scene_add_geometry(scn, box_ntriangles - 1, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_get_enclosure(desc, 0, &enclosure) == RES_OK); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + + /* Put enclosure in a 3D view... */ + CHK(s3d_shape_create_mesh(s3d, &s3d_shp) == RES_OK); + CHK(s3d_mesh_setup_indexed_vertices(s3d_shp, header->triangle_count, + senc_enclosure_get_indices__, header->vertices_count, &vrtx_get, 1, enclosure) + == RES_OK); + + CHK(s3d_scene_attach_shape(s3d_scn, s3d_shp) == RES_OK); + + CHK(s3d_scene_view_create(s3d_scn, S3D_SAMPLE, &s3d_view) == RES_OK); + + /* ... and sample it. */ + ssp_rng_create(&allocator, &ssp_rng_threefry, &rng); + FOR_EACH(i, 0, 10000) { + struct s3d_attrib attrib; + int n, c; + CHK(s3d_scene_view_sample(s3d_view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st) == RES_OK); + s3d_primitive_get_attrib(&prim, S3D_POSITION, st, &attrib); + c = 0; + FOR_EACH(n, 0, 3) + if(eq_eps(attrib.value[n], 0, FLT_EPSILON) + || eq_eps(attrib.value[n], 1, FLT_EPSILON)) + c++; + ASSERT(c == 1); + s3d_primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attrib); + c = 0; + FOR_EACH(n, 0, 3) + if(eq_eps(attrib.value[n], -1, FLT_EPSILON) + || eq_eps(attrib.value[n], 1, FLT_EPSILON)) + c++; + ASSERT(c == 1); + c = 0; + FOR_EACH(n, 0, 3) + if(eq_eps(attrib.value[n], 0, FLT_EPSILON)) + c++; + ASSERT(c == 2); + } + + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + + CHK(ssp_rng_ref_put(rng) == RES_OK); + + CHK(s3d_shape_ref_put(s3d_shp) == RES_OK); + CHK(s3d_scene_view_ref_put(s3d_view) == RES_OK); + CHK(s3d_device_ref_put(s3d) == RES_OK); + CHK(s3d_scene_ref_put(s3d_scn) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + + return 0; +} diff --git a/src/test_senc_scene.c b/src/test_senc_scene.c @@ -0,0 +1,131 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#include "senc.h" +#include "test_senc_utils.h" + +#include <rsys/float3.h> +#include <rsys/double3.h> + +#include <star/s3d.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct senc_descriptor* desc = NULL; + struct context ctx; + (void)argc, (void)argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) + == RES_OK); + + CHK(senc_scene_create(NULL, 2, &scn) == RES_BAD_ARG); + CHK(senc_scene_create(dev, 0, &scn) == RES_BAD_ARG); + CHK(senc_scene_create(dev, 2, NULL) == RES_BAD_ARG); + CHK(senc_scene_create(NULL, 0, &scn) == RES_BAD_ARG); + CHK(senc_scene_create(NULL, 2, NULL) == RES_BAD_ARG); + CHK(senc_scene_create(dev, 0, NULL) == RES_BAD_ARG); + CHK(senc_scene_create(NULL, 0, NULL) == RES_BAD_ARG); + /* It is valid to have unused mediums */ + CHK(senc_scene_create(dev, 4, &scn) == RES_OK); + + /* A 3D cube */ + ctx.positions = box_vertices; + ctx.indices = box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d3(ctx.offset, 0, 0, 0); + ctx.front_medium = 0; + ctx.back_medium = 1; + + CHK(senc_scene_add_geometry(NULL, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, 0, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, box_ntriangles, NULL, get_mediums, + box_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, NULL, + box_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + 0, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, NULL, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_analyze(NULL, NULL) == RES_BAD_ARG); + CHK(senc_scene_analyze(scn, NULL) == RES_BAD_ARG); + CHK(senc_scene_analyze(NULL, &desc) == RES_BAD_ARG); + CHK(senc_scene_analyze(NULL, NULL) == RES_BAD_ARG); + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_scene_ref_get(NULL) == RES_BAD_ARG); + CHK(senc_scene_ref_get(scn) == RES_OK); + CHK(senc_scene_ref_put(NULL) == RES_BAD_ARG); + CHK(senc_scene_ref_put(scn) == RES_OK); + + /* Invalid medium ID */ + ctx.back_medium = 12; + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_BAD_ARG); + ctx.back_medium = 1; + + /* Invalid vertex ID */ + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices - 1, get_position, &ctx) == RES_BAD_ARG); + + /* Incoherent medium on duplicate triangle */ + ctx.back_medium = 3; + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_BAD_ARG); + + /* It is OK dd geometry after a failed add */ + ctx.back_medium = 1; + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + /* Coherent medium on duplicate triangle */ + ctx.back_medium = 1; + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + /* Coherent medium on duplicate triangle V2 */ + ctx.reverse_med = 1; + ctx.front_medium = 1; + ctx.back_medium = 0; + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + /* Coherent medium on duplicate triangle V3 */ + ctx.reverse_med = 0; + ctx.reverse_vrtx = 1; + CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_mediums, + box_nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + + return 0; +} diff --git a/src/test_senc_utils.h b/src/test_senc_utils.h @@ -0,0 +1,135 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * 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 <http://www.gnu.org/licenses/>. */ + +#ifndef TEST_UTILS_H +#define TEST_UTILS_H + +#include <rsys/mem_allocator.h> +#include <stdio.h> + +/******************************************************************************* + * Geometry + ******************************************************************************/ +static const double box_vertices[8/*#vertices*/*3/*#coords per vertex*/] = { + 0.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 0.0, 0.0, 1.0, + 1.0, 0.0, 1.0, + 0.0, 1.0, 1.0, + 1.0, 1.0, 1.0 +}; +static const unsigned box_nvertices = sizeof(box_vertices) / sizeof(double[3]); + +/* The following array lists the indices toward the 3D vertices of each + * triangle. + * ,6---,7 ,6----7 + * ,' | ,'/| ,' | \ | + * 2----3' / | 2', | \ | + * |', | / ,5 | ',4---,5 + * | ',|/,' | ,' | ,' + * 0----1' 0----1' + * Front, right Back, left and + * and Top faces bottom faces */ +static const unsigned box_indices[12/*#triangles*/*3/*#indices per triangle*/] = { + 0, 2, 1, 1, 2, 3, /* Front face */ + 0, 4, 2, 2, 4, 6, /* Left face*/ + 4, 5, 6, 6, 5, 7, /* Back face */ + 3, 7, 1, 1, 7, 5, /* Right face */ + 2, 6, 3, 3, 6, 7, /* Top face */ + 0, 1, 4, 4, 1, 5 /* Bottom face */ +}; +static const unsigned box_ntriangles = sizeof(box_indices) / (3 * sizeof(*box_indices)); + +struct context { + const double* positions; + const unsigned* indices; + struct cube_specifics* specifics; + unsigned front_medium; + unsigned back_medium; + double offset[3]; + double scale; + char reverse_vrtx, reverse_med; +}; + +static void +get_indices(const unsigned itri, unsigned ids[3], void* context) +{ + struct context* ctx = context; + ASSERT(ids && ctx); + ids[0] = ctx->indices[itri * 3 + 0]; + ids[ctx->reverse_vrtx ? 2 : 1] = ctx->indices[itri * 3 + 1]; + ids[ctx->reverse_vrtx ? 1 : 2] = ctx->indices[itri * 3 + 2]; +} + +static void +get_position(const unsigned ivert, double pos[3], void* context) +{ + struct context* ctx = context; + ASSERT(pos && ctx); + pos[0] = ctx->positions[ivert * 3 + 0] * ctx->scale + ctx->offset[0]; + pos[1] = ctx->positions[ivert * 3 + 1] * ctx->scale + ctx->offset[1]; + pos[2] = ctx->positions[ivert * 3 + 2] * ctx->scale + ctx->offset[2]; +} + +static void +get_mediums(const unsigned itri, unsigned medium[2], void* context) +{ + struct context* ctx = context; + ASSERT(medium && ctx); + (void) itri; + medium[ctx->reverse_med ? 1 : 0] = ctx->front_medium; + medium[ctx->reverse_med ? 0 : 1] = ctx->back_medium; +} + +/******************************************************************************* + * Miscellaneous + ******************************************************************************/ +static INLINE void +dump_mesh + (FILE* stream, + const double* pos, + const size_t npos, + const size_t* ids, + const size_t nids) +{ + size_t i; + CHK(pos != NULL && npos != 0); + CHK(ids != NULL && nids != 0); + FOR_EACH(i, 0, npos) { + fprintf(stream, "v %g %g %g\n", SPLIT3(pos+i*3)); + } + FOR_EACH(i, 0, nids) { + fprintf(stream, "f %lu %lu %lu\n", + (unsigned long)(ids[i*3+0] + 1), + (unsigned long)(ids[i*3+1] + 1), + (unsigned long)(ids[i*3+2] + 1)); + } +} + +static INLINE void +check_memory_allocator(struct mem_allocator* allocator) +{ + if(MEM_ALLOCATED_SIZE(allocator)) { + char dump[128]; + MEM_DUMP(allocator, dump, sizeof(dump)); + fprintf(stderr, "%s\n", dump); + FATAL("Memory leaks.\n"); + } +} + +#endif /* TEST_UTILS_H */ +