commit e96a30c275917de1e12a89a2ca8727c38aa2ca57 Author: OLEGSHA Date: Wed Jul 29 12:30:34 2020 +0300 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..61793db --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfbf4ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build + +# Ignore Eclipse IDE files +bin +.settings +.classpath +.project + +# Ignore Windows Thumb.db files +**/Thumbs.db + +# Ignore MacOS +**/.DS_Store \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3877ae0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..4a47ffe --- /dev/null +++ b/build.gradle @@ -0,0 +1,73 @@ +plugins { + // Apply the java-library plugin to add support for Java Library + id 'java-library' +} + +repositories { + mavenCentral() + jcenter() + + maven { url 'http://windcorp.ru/./maven' } +} + +dependencies { + implementation 'org.apache.commons:commons-math3:3.6.1' + implementation 'com.google.guava:guava:28.0-jre' + implementation 'net.sf.trove4j:trove4j:3.0.3' + + implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1' + + testImplementation 'junit:junit:4.12' +} + +/* + * LWJGL + * (auto-generated script) + * ((here be dragons)) + */ + +import org.gradle.internal.os.OperatingSystem + +project.ext.lwjglVersion = "3.2.3" + +switch (OperatingSystem.current()) { + case OperatingSystem.LINUX: + def osArch = System.getProperty("os.arch") + project.ext.lwjglNatives = osArch.startsWith("arm") || osArch.startsWith("aarch64") + ? "natives-linux-${osArch.contains("64") || osArch.startsWith("armv8") ? "arm64" : "arm32"}" + : "natives-linux" + break + case OperatingSystem.MAC_OS: + project.ext.lwjglNatives = "natives-macos" + break + case OperatingSystem.WINDOWS: + project.ext.lwjglNatives = System.getProperty("os.arch").contains("64") ? "natives-windows" : "natives-windows-x86" + break +} + +dependencies { + implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion") + + implementation "org.lwjgl:lwjgl" + implementation "org.lwjgl:lwjgl-assimp" + implementation "org.lwjgl:lwjgl-bgfx" + implementation "org.lwjgl:lwjgl-glfw" + implementation "org.lwjgl:lwjgl-nanovg" + implementation "org.lwjgl:lwjgl-nuklear" + implementation "org.lwjgl:lwjgl-openal" + implementation "org.lwjgl:lwjgl-opengl" + implementation "org.lwjgl:lwjgl-par" + implementation "org.lwjgl:lwjgl-stb" + implementation "org.lwjgl:lwjgl-vulkan" + runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-bgfx::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-nanovg::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-nuklear::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-par::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" + if (lwjglNatives == "natives-macos") runtimeOnly "org.lwjgl:lwjgl-vulkan::$lwjglNatives" +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..cc4fdc2 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..6ce793f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9618d8d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f3e0061 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'Optica' diff --git a/src/main/java/ru/windcorp/optica/Optica.java b/src/main/java/ru/windcorp/optica/Optica.java new file mode 100644 index 0000000..9ebf2b4 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/Optica.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica; + +public class Optica { + +} diff --git a/src/main/java/ru/windcorp/optica/OpticaLauncher.java b/src/main/java/ru/windcorp/optica/OpticaLauncher.java new file mode 100644 index 0000000..4f89519 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/OpticaLauncher.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica; + +public class OpticaLauncher { + + public static void launch(String[] args, Proxy proxy) { + proxy.initialize(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/Proxy.java b/src/main/java/ru/windcorp/optica/Proxy.java new file mode 100644 index 0000000..4720695 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/Proxy.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica; + +public interface Proxy { + + void initialize(); + +} diff --git a/src/main/java/ru/windcorp/optica/client/ClientProxy.java b/src/main/java/ru/windcorp/optica/client/ClientProxy.java new file mode 100644 index 0000000..c0f1bcf --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/ClientProxy.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client; + +import ru.windcorp.optica.Proxy; +import ru.windcorp.optica.client.graphics.GUI; +import ru.windcorp.optica.client.graphics.backend.GraphicsBackend; +import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue; +import ru.windcorp.optica.client.graphics.model.ShapeRenderProgram; +import ru.windcorp.optica.client.graphics.world.LayerWorld; + +public class ClientProxy implements Proxy { + + @Override + public void initialize() { + GraphicsBackend.initialize(); + try { + RenderTaskQueue.waitAndInvoke(ShapeRenderProgram::init); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + GUI.addBottomLayer(new LayerWorld()); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/OpticaClientMain.java b/src/main/java/ru/windcorp/optica/client/OpticaClientMain.java new file mode 100644 index 0000000..c086634 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/OpticaClientMain.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client; + +import ru.windcorp.optica.OpticaLauncher; + +public class OpticaClientMain { + + public static void main(String[] args) { + OpticaLauncher.launch(args, new ClientProxy()); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/TestLayer.java b/src/main/java/ru/windcorp/optica/client/TestLayer.java new file mode 100644 index 0000000..69e3a57 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/TestLayer.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client; + +import org.lwjgl.glfw.GLFW; + +import com.google.common.eventbus.Subscribe; + +import glm.mat._3.Mat3; +import glm.mat._4.Mat4; +import glm.vec._3.Vec3; +import ru.windcorp.optica.client.graphics.Layer; +import ru.windcorp.optica.client.graphics.backend.GraphicsBackend; +import ru.windcorp.optica.client.graphics.backend.GraphicsInterface; +import ru.windcorp.optica.client.graphics.input.KeyEvent; +import ru.windcorp.optica.client.graphics.input.CursorMoveEvent; +import ru.windcorp.optica.client.graphics.model.Model; +import ru.windcorp.optica.client.graphics.model.DynamicModel; +import ru.windcorp.optica.client.graphics.model.Shapes; +import ru.windcorp.optica.client.graphics.texture.SimpleTexture; +import ru.windcorp.optica.client.graphics.texture.Sprite; +import ru.windcorp.optica.client.graphics.texture.Texture; +import ru.windcorp.optica.client.graphics.texture.TextureManager; +import ru.windcorp.optica.client.graphics.world.Camera; +import ru.windcorp.optica.client.graphics.world.WorldRenderer; + +public class TestLayer extends Layer { + + private final Model model; + + private final Camera camera = new Camera( + new Vec3(), + 0, (float) Math.PI, + (float) Math.toRadians(70) + ); + + private final Vec3 velocity = new Vec3(); + private final Vec3 tmp = new Vec3(); + + private final Mat3 angMat = new Mat3(); + + private int movementX = 0; + private int movementY = 0; + private int movementZ = 0; + + public TestLayer() { + super("Test"); + + Texture top = qtex("grass_top"); + Texture side = qtex("grass_side"); + Texture bottom = qtex("grass_bottom"); + + model = new DynamicModel(DynamicModel.builder() + .addDynamicPart( + new Shapes.PppBuilder(top, bottom, side, side, side, side) + .create() + ) + ) { + @Override + protected void getDynamicTransform(int shapeIndex, Mat4 result) { + result.translate(0, 0, +5); + } + }; + } + + private Texture qtex(String name) { + return new SimpleTexture(new Sprite(TextureManager.load(name, false))); + } + + @Override + protected void initialize() { + GraphicsInterface.subscribeToInputEvents(this); + } + + private final WorldRenderer renderer = new WorldRenderer(); + + @Override + protected void doRender() { + camera.apply(renderer); + + angMat.set().rotateY(-camera.getYaw()); + + tmp.set(movementX, 0, movementZ); + angMat.mul_(tmp); // bug in jglm + tmp.y = movementY; + tmp.mul(0.1f); + tmp.sub(velocity); + tmp.mul(0.1f); + velocity.add(tmp); + camera.move(velocity); + + model.render(renderer); + + renderer.reset(); + } + + private boolean flag = true; + + @Subscribe + public void onKeyEvent(KeyEvent event) { + if (event.isRepeat()) return; + + int multiplier = event.isPress() ? 1 : -1; + + switch (event.getKey()) { + case GLFW.GLFW_KEY_W: + movementZ += -1 * multiplier; + break; + case GLFW.GLFW_KEY_S: + movementZ += +1 * multiplier; + break; + case GLFW.GLFW_KEY_A: + movementX += -1 * multiplier; + break; + case GLFW.GLFW_KEY_D: + movementX += +1 * multiplier; + break; + case GLFW.GLFW_KEY_SPACE: + movementY += +1 * multiplier; + break; + case GLFW.GLFW_KEY_LEFT_SHIFT: + movementY += -1 * multiplier; + break; + + case GLFW.GLFW_KEY_ESCAPE: + if (!event.isPress()) return; + + if (flag) { + GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); + } else { + GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); + } + + flag = !flag; + break; + } + } + + @Subscribe + public void onMouseMoved(CursorMoveEvent event) { + if (!flag) return; + + final float yawScale = 0.002f; + final float pitchScale = yawScale; + + camera.turn( + (float) (event.getChangeY() * pitchScale), + (float) (event.getChangeX() * yawScale) + ); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/GUI.java b/src/main/java/ru/windcorp/optica/client/graphics/GUI.java new file mode 100644 index 0000000..a476c08 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/GUI.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics; + +import java.util.ArrayList; +import java.util.List; + +public class GUI { + + private static final List LAYERS = new ArrayList<>(); + + private GUI() {} + + public synchronized static void addBottomLayer(Layer layer) { + LAYERS.add(layer); + } + + public synchronized static void addTopLayer(Layer layer) { + LAYERS.add(0, layer); + } + + public synchronized static void removeLayer(Layer layer) { + LAYERS.remove(layer); + } + + public synchronized static void render() { + for (int i = LAYERS.size() - 1; i >= 0; --i) { + LAYERS.get(i).render(); + } + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/Layer.java b/src/main/java/ru/windcorp/optica/client/graphics/Layer.java new file mode 100644 index 0000000..fcdfaf6 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/Layer.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics; + +public abstract class Layer { + + private final String name; + private boolean hasInitialized = false; + + public Layer(String name) { + this.name = name; + } + + @Override + public String toString() { + return "Layer " + name; + } + + public void render() { + if (!hasInitialized) { + initialize(); + hasInitialized = true; + } + + doRender(); + } + + protected abstract void initialize(); + + protected abstract void doRender(); + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/GraphicsBackend.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/GraphicsBackend.java new file mode 100644 index 0000000..bd52e70 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/GraphicsBackend.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.glfw.GLFW.*; + +public class GraphicsBackend { + + private static RenderThread renderThread; + + private static long windowHandle; + + private static int framebufferWidth; + private static int framebufferHeight; + + private static double frameLength = 1.0 / 60; // TODO do something about it + private static int framesRendered = 0; + private static double frameStart = Double.NaN; + + private GraphicsBackend() {} + + public static void initialize() { + startRenderThread(); + } + + private static void startRenderThread() { + renderThread = new RenderThread(); + renderThread.start(); + } + + public static Thread getRenderThread() { + return renderThread; + } + + static void setWindowHandle(long windowHandle) { + GraphicsBackend.windowHandle = windowHandle; + } + + public static long getWindowHandle() { + return windowHandle; + } + + public static int getFramebufferWidth() { + return framebufferWidth; + } + + public static int getFramebufferHeight() { + return framebufferHeight; + } + + static void onFramebufferResized(long window, int newWidth, int newHeight) { + if (window != windowHandle) return; + + framebufferWidth = newWidth; + framebufferHeight = newHeight; + + glViewport(0, 0, framebufferWidth, framebufferHeight); + } + + static void startFrame() { + double now = glfwGetTime(); + + if (Double.isNaN(frameStart)) { + frameStart = now; + } else { + frameLength = now - frameStart; + frameStart = now; + } + } + + static void endFrame() { + framesRendered++; + } + + public static double getFrameStart() { + return frameStart; + } + + public static double getFrameLength() { + return frameLength; + } + + public static int getFramesRendered() { + return framesRendered; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/GraphicsInterface.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/GraphicsInterface.java new file mode 100644 index 0000000..42f6937 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/GraphicsInterface.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import glm.vec._2.d.Vec2d; + +public class GraphicsInterface { + + private GraphicsInterface() {} + + public static Thread getRenderThread() { + return GraphicsBackend.getRenderThread(); + } + + public static boolean isRenderThread() { + return Thread.currentThread() == getRenderThread(); + } + + public static int getFramebufferWidth() { + return GraphicsBackend.getFramebufferWidth(); + } + + public static int getFramebufferHeight() { + return GraphicsBackend.getFramebufferHeight(); + } + + public static float getAspectRatio() { + return ((float) getFramebufferWidth()) / getFramebufferHeight(); + } + + public static double getTime() { + return GraphicsBackend.getFrameStart(); + } + + public static double getFrameLength() { + return GraphicsBackend.getFrameLength(); + } + + public static double getFPS() { + return 1 / GraphicsBackend.getFrameLength(); + } + + public static void subscribeToInputEvents(Object listener) { + InputHandler.register(listener); + } + + public static double getCursorX() { + return InputHandler.getCursorX(); + } + + public static double getCursorY() { + return InputHandler.getCursorY(); + } + + public static Vec2d getCursorPosition() { + return InputHandler.getCursorPosition(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/InputHandler.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/InputHandler.java new file mode 100644 index 0000000..f990463 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/InputHandler.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import com.google.common.eventbus.EventBus; + +import glm.vec._2.d.Vec2d; +import ru.windcorp.optica.client.graphics.input.*; + +public class InputHandler { + + private static final EventBus INPUT_EVENT_BUS = new EventBus("Input"); + + private static class ModifiableKeyEvent extends KeyEvent { + + public void initialize(int key, int scancode, int action, int mods) { + this.key = key; + this.scancode = scancode; + this.action = action; + this.mods = mods; + } + + } + + private static final ModifiableKeyEvent THE_KEY_EVENT = new ModifiableKeyEvent(); + + static void handleKeyInput( + long window, + int key, + int scancode, + int action, + int mods + ) { + if (GraphicsBackend.getWindowHandle() != window) return; + THE_KEY_EVENT.initialize(key, scancode, action, mods); + dispatch(THE_KEY_EVENT); + } + + private static class ModifiableCursorMoveEvent extends CursorMoveEvent { + + public void initialize(double x, double y) { + Vec2d newPos = getNewPosition(); + newPos.x = x; + newPos.y = y; + } + + } + + private static final Vec2d CURSOR_POSITION = new Vec2d().set( + Double.NaN, Double.NaN + ); + + private static final ModifiableCursorMoveEvent THE_CURSOR_MOVE_EVENT = + new ModifiableCursorMoveEvent(); + + static void handleMouseMoveInput( + long window, + double x, double y + ) { + if (GraphicsBackend.getWindowHandle() != window) return; + + if (Double.isNaN(CURSOR_POSITION.x)) { + CURSOR_POSITION.set(x, y); + } + + THE_CURSOR_MOVE_EVENT.initialize(x, y); + dispatch(THE_CURSOR_MOVE_EVENT); + + CURSOR_POSITION.set(x, y); + } + + public static double getCursorX() { + return CURSOR_POSITION.x; + } + + public static double getCursorY() { + return CURSOR_POSITION.y; + } + + public static Vec2d getCursorPosition() { + return CURSOR_POSITION; + } + + private static void dispatch(InputEvent event) { + INPUT_EVENT_BUS.post(event); + } + + public static void register(Object listener) { + INPUT_EVENT_BUS.register(listener); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/LWJGLInitializer.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/LWJGLInitializer.java new file mode 100644 index 0000000..dd69d0f --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/LWJGLInitializer.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.system.MemoryUtil.*; + +import org.lwjgl.opengl.GL; + +class LWJGLInitializer { + + private LWJGLInitializer() {} + + public static void initialize() { + checkEnvironment(); + initializeGLFW(); + createWindow(); + positionWindow(); + createWindowIcons(); + initializeOpenGL(); + setupWindowCallbacks(); + + glfwShowWindow(GraphicsBackend.getWindowHandle()); + } + + private static void checkEnvironment() { + // TODO Auto-generated method stub + } + + private static void initializeGLFW() { + // TODO Do GLFW error handling: check glfwInit, setup error callback + glfwInit(); + } + + private static void createWindow() { + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); + glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); + + long handle = glfwCreateWindow(900, 900, "OpticaTest", NULL, NULL); + + // TODO Check that handle != NULL + + GraphicsBackend.setWindowHandle(handle); + + glfwSetInputMode(handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + glfwMakeContextCurrent(handle); + glfwSwapInterval(0); + } + + private static void positionWindow() { + // TODO Auto-generated method stub + + } + + private static void createWindowIcons() { + // TODO Auto-generated method stub + + } + + private static void initializeOpenGL() { + GL.createCapabilities(); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + private static void setupWindowCallbacks() { + long handle = GraphicsBackend.getWindowHandle(); + + glfwSetFramebufferSizeCallback(handle, + GraphicsBackend::onFramebufferResized); + + glfwSetKeyCallback(handle, InputHandler::handleKeyInput); + glfwSetCursorPosCallback(handle, InputHandler::handleMouseMoveInput); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/OpenGLObjectTracker.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/OpenGLObjectTracker.java new file mode 100644 index 0000000..b2fa223 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/OpenGLObjectTracker.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import java.util.ArrayList; +import java.util.Collection; + +/* + * FIXME deal with client invocations of .delete() when properly disposing of + * objects mid-execution + */ + +public class OpenGLObjectTracker { + + public static interface OpenGLDeletable { + void delete(); + } + + private static final Collection TO_DELETE = new ArrayList<>(); + + public synchronized static void register(OpenGLDeletable object) { + TO_DELETE.add(object); + } + + public synchronized static void deleteAllObjects() { + TO_DELETE.forEach(OpenGLDeletable::delete); + TO_DELETE.clear(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/RenderTaskQueue.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/RenderTaskQueue.java new file mode 100644 index 0000000..8a8d927 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/RenderTaskQueue.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import ru.windcorp.optica.common.util.ThrowingRunnable; + +public class RenderTaskQueue { + + private static final Queue QUEUE = new ConcurrentLinkedQueue<>(); + + private RenderTaskQueue() {} + + public static void invokeLater(Runnable task) { + QUEUE.add(task); + } + + public static void invokeNow(Runnable task) { + if (GraphicsInterface.isRenderThread()) { + task.run(); + } else { + invokeLater(task); + } + } + + private static final Object WAIT_AND_INVOKE_MONITOR = new Object(); + + @SuppressWarnings("unchecked") + public static void waitAndInvoke( + ThrowingRunnable task + ) throws InterruptedException, T { + + if (GraphicsInterface.isRenderThread()) { + task.run(); + return; + } + + final AtomicBoolean flag = + new AtomicBoolean(false); + final AtomicReference thrownContainer = + new AtomicReference<>(null); + + invokeLater(() -> { + + try { + task.run(); + } catch (Throwable t) { + thrownContainer.set(t); + } + + flag.set(true); + + synchronized (WAIT_AND_INVOKE_MONITOR) { + WAIT_AND_INVOKE_MONITOR.notifyAll(); + } + }); + + while (!flag.get()) { + synchronized (WAIT_AND_INVOKE_MONITOR) { + WAIT_AND_INVOKE_MONITOR.wait(); + } + } + + Throwable thrown = thrownContainer.get(); + if (thrown != null) { + if (thrown instanceof RuntimeException) { + throw (RuntimeException) thrown; + } + + if (thrown instanceof Error) { + throw (Error) thrown; + } + + throw (T) thrown; // Guaranteed + } + } + + public static void runTasks() { + Iterator tasks = QUEUE.iterator(); + + while (tasks.hasNext()) { + tasks.next().run(); + tasks.remove(); + } + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/RenderThread.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/RenderThread.java new file mode 100644 index 0000000..239f71c --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/RenderThread.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; + +import ru.windcorp.optica.client.graphics.GUI; + +class RenderThread extends Thread { + + public RenderThread() { + super("Render"); + } + + @Override + public void run() { + LWJGLInitializer.initialize(); + mainLoop(); + freeResources(); + } + + private void mainLoop() { + while (shouldRun()) { + GraphicsBackend.startFrame(); + RenderTaskQueue.runTasks(); + render(); + waitForFrame(); + GraphicsBackend.endFrame(); + } + } + + private void render() { + clear(); + doRender(); + glfwPollEvents(); + } + + private void clear() { + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + private void doRender() { + GUI.render(); + } + + private void waitForFrame() { + glfwSwapBuffers(GraphicsBackend.getWindowHandle()); + } + + private void freeResources() { + OpenGLObjectTracker.deleteAllObjects(); + } + + private boolean shouldRun() { + return !glfwWindowShouldClose(GraphicsBackend.getWindowHandle()); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/Usage.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/Usage.java new file mode 100644 index 0000000..1a356c1 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/Usage.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import static org.lwjgl.opengl.GL15.GL_DYNAMIC_DRAW; +import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW; +import static org.lwjgl.opengl.GL15.GL_STREAM_DRAW; + +public enum Usage { // TODO add _COPY and _READ, pref. as another enum + STATIC(GL_STATIC_DRAW), + DYNAMIC(GL_DYNAMIC_DRAW), + STREAM(GL_STREAM_DRAW); + + private final int glCode; + + private Usage(int glCode) { + this.glCode = glCode; + } + + public int getGlCode() { + return glCode; + } +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/VertexBufferObject.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/VertexBufferObject.java new file mode 100644 index 0000000..abf4cb8 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/VertexBufferObject.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend; + +import static org.lwjgl.opengl.GL20.*; + +import java.nio.*; + +import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; + +public class VertexBufferObject implements OpenGLDeletable { + + public static enum BindTarget { + ARRAY(GL_ARRAY_BUFFER), + ELEMENT_ARRAY(GL_ELEMENT_ARRAY_BUFFER); + + private final int glCode; + + private BindTarget(int glCode) { + this.glCode = glCode; + } + + public int getGlCode() { + return glCode; + } + } + + private final int handle; + + private long length = 0; + private final Usage usage; + + public VertexBufferObject(Usage usage) { + handle = glGenBuffers(); + OpenGLObjectTracker.register(this); + + this.usage = usage; + } + + public void setData(ByteBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.remaining(); + } + + public void setData(double[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.length * Double.BYTES; + } + + public void setData(DoubleBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.remaining() * Double.BYTES; + } + + public void setData(float[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.length * Float.BYTES; + } + + public void setData(FloatBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.remaining() * Float.BYTES; + } + + public void setData(int[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.length * Integer.BYTES; + } + + public void setData(IntBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.remaining() * Integer.BYTES; + } + + public void setData(long[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.length * Long.BYTES; + } + + public void setData(LongBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.remaining() * Long.BYTES; + } + + public void setData(short[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.length * Short.BYTES; + } + + public void setData(ShortBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode()); + length = data.remaining() * Short.BYTES; + } + + public void initData(long length) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferData(GL_ARRAY_BUFFER, length, usage.getGlCode()); + this.length = length; + } + + public void setData(int offset, ByteBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, double[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, DoubleBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, float[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, FloatBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, int[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, IntBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, long[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, LongBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, short[] data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void setData(int offset, ShortBuffer data) { + glBindBuffer(GL_ARRAY_BUFFER, handle); + glBufferSubData(GL_ARRAY_BUFFER, offset, data); + } + + public void bind(BindTarget target) { + glBindBuffer(target.getGlCode(), handle); + } + + public long getLength() { + return length; + } + + public Usage getUsage() { + return usage; + } + + public int getHandle() { + return handle; + } + + @Override + public void delete() { + glDeleteBuffers(handle); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/CombinedShader.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/CombinedShader.java new file mode 100644 index 0000000..dcd9571 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/CombinedShader.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders; + +import ru.windcorp.optica.common.resource.Resource; + +public class CombinedShader extends Shader { + + public CombinedShader(String... resources) { + super(getTypeOf(resources), combine(resources)); + } + + private static ShaderType getTypeOf(String[] resources) { + ShaderType first = ShaderType.guessByResourceName(resources[0]); + + for (int i = 1; i < resources.length; ++i) { + if (ShaderType.guessByResourceName(resources[i]) != first) { + throw new IllegalArgumentException( + "Deduced shader types of " + + resources[0] + + " and " + + resources[i] + + " differ" + ); + } + } + + return first; + } + + private static String combine(String[] resources) { + StringBuilder accumulator = new StringBuilder("#version 120\n"); + + for (String resourceName : resources) { + Resource resource = getShaderResource(resourceName); + + accumulator.append("\n// START " + resourceName); + accumulator.append(stripVersionAnnotations(resource)); + accumulator.append('\n'); + } + + return accumulator.toString(); + } + + private static String stripVersionAnnotations(Resource resource) { + String contents = resource.readAsString(); + + int versionIndex; + for (versionIndex = 0; versionIndex < contents.length(); ++versionIndex) + { + if (!Character.isWhitespace(contents.codePointAt(versionIndex))) + break; + } + + if (versionIndex < contents.length()) { + if (contents.codePointAt(versionIndex) == '#') { + final String versionAnnotation = "#version "; + + if (contents.regionMatches( + versionIndex, + versionAnnotation, + 0, + versionAnnotation.length() + )) { + contents = contents.substring( + versionIndex + + versionAnnotation.length() + + "120".length() + ); + } + + } + } + + return contents; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/Program.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/Program.java new file mode 100644 index 0000000..e2ed2ac --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/Program.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders; + +import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker; +import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; +import ru.windcorp.optica.client.graphics.backend.shaders.attributes.Attribute; +import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +public class Program implements OpenGLDeletable { + + private int handle; + + public Program(Shader vertexShader, Shader fragmentShader) { + handle = glCreateProgram(); + OpenGLObjectTracker.register(this); + + glAttachShader(handle, vertexShader.getHandle()); + glAttachShader(handle, fragmentShader.getHandle()); + + glLinkProgram(handle); + + if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) { + throw new RuntimeException("Bad program:\n" + glGetProgramInfoLog(handle)); + } + } + + public Attribute getAttribute(String name) { + return new Attribute(glGetAttribLocation(handle, name), this); + } + + public Uniform getUniform(String name) { + return new Uniform(glGetUniformLocation(handle, name), this); + } + + public void use() { + glUseProgram(handle); + } + + @Override + public void delete() { + glDeleteProgram(handle); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/Shader.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/Shader.java new file mode 100644 index 0000000..a2c4081 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/Shader.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders; + +import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker; +import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; +import ru.windcorp.optica.common.resource.Resource; +import ru.windcorp.optica.common.resource.ResourceManager; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +import java.util.Locale; + +public class Shader implements OpenGLDeletable { + + public static enum ShaderType { + VERTEX(GL_VERTEX_SHADER), + FRAGMENT(GL_FRAGMENT_SHADER); + + private final int glCode; + + private ShaderType(int glCode) { + this.glCode = glCode; + } + + public int getGlCode() { + return glCode; + } + + public static ShaderType guessByResourceName(String resource) { + resource = resource.toLowerCase(Locale.ENGLISH); + + if (resource.contains("vertex")) return VERTEX; + if (resource.contains("fragment")) return FRAGMENT; + if (resource.contains("vsh")) return VERTEX; + if (resource.contains("fsh")) return FRAGMENT; + + throw new IllegalArgumentException( + "Cannot deduce shader type from resource name \"" + + resource + "\"" + ); + } + } + + private static final String SHADER_ASSETS_PREFIX = "assets/shaders/"; + + protected static Resource getShaderResource(String name) { + return ResourceManager.getResource(SHADER_ASSETS_PREFIX + name); + } + + private final int handle; + private final ShaderType type; + + public Shader(ShaderType type, String source) { + handle = glCreateShader(type.getGlCode()); + OpenGLObjectTracker.register(this); + + this.type = type; + + glShaderSource(handle, source); + glCompileShader(handle); + + if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) { + System.out.println("***************** ERROR ******************"); + System.out.println(source); + throw new RuntimeException("Bad shader:\n" + glGetShaderInfoLog(handle)); + } + } + + public Shader(String resource) { + this( + ShaderType.guessByResourceName(resource), + getShaderResource(resource).readAsString() + ); + } + + @Override + public void delete() { + glDeleteShader(handle); + } + + public int getHandle() { + return handle; + } + + public ShaderType getType() { + return type; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/attributes/Attribute.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/attributes/Attribute.java new file mode 100644 index 0000000..fcbbe9b --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/attributes/Attribute.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.attributes; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +public class Attribute { + + protected final int handle; + private final Program program; + + public Attribute(int handle, Program program) { + if (handle < 0) { + throw new RuntimeException("Bad handle: " + handle); + } + + this.handle = handle; + this.program = program; + } + + public int getHandle() { + return handle; + } + + public Program getProgram() { + return program; + } + + public AttributeVertexArray asVertexArray() { + return new AttributeVertexArray(handle, program); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/attributes/AttributeVertexArray.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/attributes/AttributeVertexArray.java new file mode 100644 index 0000000..f382f9d --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/attributes/AttributeVertexArray.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.attributes; + +import ru.windcorp.optica.client.graphics.backend.VertexBufferObject; +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +public class AttributeVertexArray extends Attribute { + + private boolean isEnabled = false; + + public AttributeVertexArray(int handle, Program program) { + super(handle, program); + } + + public void enable() { + if (!isEnabled) { + glEnableVertexAttribArray(handle); + isEnabled = true; + } + } + + public void disable() { + if (isEnabled) { + glDisableVertexAttribArray(handle); + isEnabled = false; + } + } + + public void set( + int size, boolean normalized, int stride, + ByteBuffer pointer + ) { + glVertexAttribPointer( + handle, + size, GL_BYTE, normalized, stride, pointer + ); + } + + public void set( + int size, boolean normalized, int stride, + FloatBuffer pointer + ) { + glVertexAttribPointer( + handle, + size, GL_FLOAT, normalized, stride, pointer + ); + } + + public void set( + int size, boolean normalized, int stride, + IntBuffer pointer + ) { + glVertexAttribPointer( + handle, + size, GL_INT, normalized, stride, pointer + ); + } + + public void set( + int size, boolean normalized, int stride, + ShortBuffer pointer + ) { + glVertexAttribPointer( + handle, + size, GL_SHORT, normalized, stride, pointer + ); + } + + public void set( + int size, int type, boolean normalized, int stride, + long pointer + ) { + glVertexAttribPointer( + handle, + size, type, normalized, stride, pointer + ); + } + + public void set( + int size, int type, boolean normalized, int stride, + VertexBufferObject vbo, long offset + ) { + glBindBuffer(GL_ARRAY_BUFFER, vbo.getHandle()); + glVertexAttribPointer( + handle, + size, type, normalized, stride, offset + ); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform.java new file mode 100644 index 0000000..fadcbc7 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +public class Uniform { + + protected final int handle; + private final Program program; + + public Uniform(int handle, Program program) { + if (handle < 0) { + throw new RuntimeException("Bad handle: " + handle); + } + + this.handle = handle; + this.program = program; + } + + public int getHandle() { + return handle; + } + + public Program getProgram() { + return program; + } + + public Uniform1Float as1Float() { + return new Uniform1Float(handle, program); + } + + public Uniform1Int as1Int() { + return new Uniform1Int(handle, program); + } + + public Uniform2Float as2Float() { + return new Uniform2Float(handle, program); + } + + public Uniform2Int as2Int() { + return new Uniform2Int(handle, program); + } + + public Uniform3Float as3Float() { + return new Uniform3Float(handle, program); + } + + public Uniform3Int as3Int() { + return new Uniform3Int(handle, program); + } + + public Uniform4Float as4Float() { + return new Uniform4Float(handle, program); + } + + public Uniform4Int as4Int() { + return new Uniform4Int(handle, program); + } + + public Uniform2Matrix as2Matrix() { + return new Uniform2Matrix(handle, program); + } + + public Uniform3Matrix as3Matrix() { + return new Uniform3Matrix(handle, program); + } + + public Uniform4Matrix as4Matrix() { + return new Uniform4Matrix(handle, program); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform1Float.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform1Float.java new file mode 100644 index 0000000..7a64168 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform1Float.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.FloatBuffer; + +public class Uniform1Float extends Uniform { + + public Uniform1Float(int handle, Program program) { + super(handle, program); + } + + public void set(float value) { + glUniform1f(handle, value); + } + + public void set(float[] value) { + glUniform1fv(handle, value); + } + + public void set(FloatBuffer value) { + glUniform1fv(handle, value); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform1Int.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform1Int.java new file mode 100644 index 0000000..2fa81a2 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform1Int.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.IntBuffer; + +public class Uniform1Int extends Uniform { + + public Uniform1Int(int handle, Program program) { + super(handle, program); + } + + public void set(int value) { + glUniform1i(handle, value); + } + + public void set(int[] value) { + glUniform1iv(handle, value); + } + + public void set(IntBuffer value) { + glUniform1iv(handle, value); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Float.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Float.java new file mode 100644 index 0000000..9a4130a --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Float.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.FloatBuffer; + +import glm.vec._2.Vec2; + +public class Uniform2Float extends Uniform { + + public Uniform2Float(int handle, Program program) { + super(handle, program); + } + + public void set(float x, float y) { + glUniform2f(handle, x, y); + } + + public void set(float[] value) { + glUniform2fv(handle, value); + } + + public void set(FloatBuffer value) { + glUniform2fv(handle, value); + } + + public void set(Vec2 value) { + glUniform2f(handle, value.x, value.y); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Int.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Int.java new file mode 100644 index 0000000..4df68f7 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Int.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.IntBuffer; + +import glm.vec._2.i.Vec2i; + +public class Uniform2Int extends Uniform { + + public Uniform2Int(int handle, Program program) { + super(handle, program); + } + + public void set(int x, int y) { + glUniform2i(handle, x, y); + } + + public void set(int[] value) { + glUniform2iv(handle, value); + } + + public void set(IntBuffer value) { + glUniform2iv(handle, value); + } + + public void set(Vec2i value) { + glUniform2i(handle, value.x, value.y); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Matrix.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Matrix.java new file mode 100644 index 0000000..348cb40 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform2Matrix.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.FloatBuffer; + +public class Uniform2Matrix extends Uniform { + + public Uniform2Matrix(int handle, Program program) { + super(handle, program); + } + + public void set(float[] value) { + glUniformMatrix2fv(handle, false, value); + } + + public void set(FloatBuffer value) { + glUniformMatrix2fv(handle, false, value); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Float.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Float.java new file mode 100644 index 0000000..1f54360 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Float.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.FloatBuffer; + +import glm.vec._3.Vec3; + +public class Uniform3Float extends Uniform { + + public Uniform3Float(int handle, Program program) { + super(handle, program); + } + + public void set(float x, float y, float z) { + glUniform3f(handle, x, y, z); + } + + public void set(float[] value) { + glUniform3fv(handle, value); + } + + public void set(FloatBuffer value) { + glUniform3fv(handle, value); + } + + public void set(Vec3 value) { + glUniform3f(handle, value.x, value.y, value.z); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Int.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Int.java new file mode 100644 index 0000000..8936406 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Int.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.IntBuffer; + +import glm.vec._3.i.Vec3i; + +public class Uniform3Int extends Uniform { + + public Uniform3Int(int handle, Program program) { + super(handle, program); + } + + public void set(int x, int y, int z) { + glUniform3i(handle, x, y, z); + } + + public void set(int[] value) { + glUniform3iv(handle, value); + } + + public void set(IntBuffer value) { + glUniform3iv(handle, value); + } + + public void set(Vec3i value) { + glUniform3i(handle, value.x, value.y, value.z); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Matrix.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Matrix.java new file mode 100644 index 0000000..62ddb77 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform3Matrix.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.FloatBuffer; + +import glm.mat._3.Mat3; + +public class Uniform3Matrix extends Uniform { + + public Uniform3Matrix(int handle, Program program) { + super(handle, program); + } + + public void set(float[] value) { + glUniformMatrix3fv(handle, false, value); + } + + public void set(FloatBuffer value) { + glUniformMatrix3fv(handle, false, value); + } + + private static final float[] BUFFER = new float[3 * 3]; + + public void set(Mat3 value) { + glUniformMatrix3fv(handle, false, value.toFa(BUFFER)); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Float.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Float.java new file mode 100644 index 0000000..6b22f76 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Float.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.FloatBuffer; + +import glm.vec._4.Vec4; + +public class Uniform4Float extends Uniform { + + public Uniform4Float(int handle, Program program) { + super(handle, program); + } + + public void set(float x, float y, float z, float w) { + glUniform4f(handle, x, y, z, w); + } + + public void set(float[] value) { + glUniform4fv(handle, value); + } + + public void set(FloatBuffer value) { + glUniform4fv(handle, value); + } + + public void set(Vec4 value) { + glUniform4f(handle, value.x, value.y, value.z, value.w); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Int.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Int.java new file mode 100644 index 0000000..e21bf27 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Int.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.IntBuffer; + +import glm.vec._4.i.Vec4i; + +public class Uniform4Int extends Uniform { + + public Uniform4Int(int handle, Program program) { + super(handle, program); + } + + public void set(int x, int y, int z, int w) { + glUniform4i(handle, x, y, z, w); + } + + public void set(int[] value) { + glUniform4iv(handle, value); + } + + public void set(IntBuffer value) { + glUniform4iv(handle, value); + } + + public void set(Vec4i value) { + glUniform4i(handle, value.x, value.y, value.z, value.w); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Matrix.java b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Matrix.java new file mode 100644 index 0000000..03e9d62 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/backend/shaders/uniforms/Uniform4Matrix.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.backend.shaders.uniforms; + +import ru.windcorp.optica.client.graphics.backend.shaders.Program; + +import static org.lwjgl.opengl.GL20.*; +import java.nio.FloatBuffer; + +import glm.mat._4.Mat4; + +public class Uniform4Matrix extends Uniform { + + public Uniform4Matrix(int handle, Program program) { + super(handle, program); + } + + public void set(float[] value) { + glUniformMatrix4fv(handle, false, value); + } + + public void set(FloatBuffer value) { + glUniformMatrix4fv(handle, false, value); + } + + private static final float[] BUFFER = new float[4 * 4]; + + public void set(Mat4 value) { + glUniformMatrix4fv(handle, false, value.toFa(BUFFER)); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/input/CursorEvent.java b/src/main/java/ru/windcorp/optica/client/graphics/input/CursorEvent.java new file mode 100644 index 0000000..9483e28 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/input/CursorEvent.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.input; + +import glm.vec._2.d.Vec2d; +import ru.windcorp.optica.client.graphics.backend.GraphicsInterface; + +public abstract class CursorEvent extends InputEvent { + + public double getCursorX() { + return GraphicsInterface.getCursorX(); + } + + public double getCursorY() { + return GraphicsInterface.getCursorY(); + } + + public Vec2d getCursorPosition() { + return GraphicsInterface.getCursorPosition(); + } + + @Override + public abstract CursorEvent snapshot(); + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/input/CursorMoveEvent.java b/src/main/java/ru/windcorp/optica/client/graphics/input/CursorMoveEvent.java new file mode 100644 index 0000000..6c04d2b --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/input/CursorMoveEvent.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.input; + +import glm.vec._2.Vec2; +import glm.vec._2.d.Vec2d; + +public class CursorMoveEvent extends CursorEvent { + + private final Vec2d newPosition = new Vec2d(); + + protected CursorMoveEvent(double newX, double newY) { + newPosition.set(newX, newY); + } + + protected CursorMoveEvent(Vec2d newPos) { + newPosition.set(newPos.x, newPos.y); + } + + @Override + public double getCursorX() { + return getCursorPosition().x; + } + + @Override + public double getCursorY() { + return getCursorPosition().y; + } + + @Override + public Vec2d getCursorPosition() { + return getNewPosition(); + } + + public double getNewX() { + return getNewPosition().x; + } + + public double getNewY() { + return getNewPosition().y; + } + + public Vec2d getNewPosition() { + return newPosition; + } + + public double getPreviousX() { + return getPreviousPosition().x; + } + + public double getPreviousY() { + return getPreviousPosition().y; + } + + public Vec2d getPreviousPosition() { + return super.getCursorPosition(); + } + + public double getChangeX() { + return getNewX() - getPreviousX(); + } + + public double getChangeY() { + return getNewY() - getPreviousY(); + } + + public Vec2 getChange(Vec2 result) { + return result.set(getChangeX(), getChangeY()); + } + + protected CursorMoveEvent() {} + + @Override + public CursorMoveEvent snapshot() { + return new StaticMouseMoveEvent( + getPreviousPosition(), + getNewPosition(), + getTime() + ); + } + + private class StaticMouseMoveEvent extends CursorMoveEvent { + + private final double time; + private final Vec2d previousPosition = new Vec2d(); + + public StaticMouseMoveEvent( + Vec2d previousPosition, + Vec2d newPosition, + double time + ) { + super(newPosition); + this.previousPosition.set(previousPosition.x, previousPosition.y); + this.time = time; + } + + @Override + public Vec2d getPreviousPosition() { + return previousPosition; + } + + @Override + public double getTime() { + return time; + } + + @Override + public CursorMoveEvent snapshot() { + return this; + } + + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/input/InputEvent.java b/src/main/java/ru/windcorp/optica/client/graphics/input/InputEvent.java new file mode 100644 index 0000000..8910bcf --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/input/InputEvent.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.input; + +import ru.windcorp.optica.client.graphics.backend.GraphicsInterface; + +public abstract class InputEvent { + + public double getTime() { + return GraphicsInterface.getTime(); + } + + public abstract InputEvent snapshot(); + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/input/KeyEvent.java b/src/main/java/ru/windcorp/optica/client/graphics/input/KeyEvent.java new file mode 100644 index 0000000..8c9fe89 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/input/KeyEvent.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.input; + +import org.lwjgl.glfw.GLFW; + +public class KeyEvent extends InputEvent { + + protected int key; + protected int scancode; + protected int action; + protected int mods; + + protected KeyEvent(int key, int scancode, int action, int mods) { + this(); + this.key = key; + this.scancode = scancode; + this.action = action; + this.mods = mods; + } + + protected KeyEvent() {} + + public int getKey() { + return key; + } + + public int getScancode() { + return scancode; + } + + public int getAction() { + return action; + } + + public boolean isPress() { + return action == GLFW.GLFW_PRESS; + } + + public boolean isRelease() { + return action == GLFW.GLFW_RELEASE; + } + + public boolean isRepeat() { + return action == GLFW.GLFW_REPEAT; + } + + public int getMods() { + return mods; + } + + @Override + public InputEvent snapshot() { + return new StaticKeyEvent(key, scancode, action, mods, getTime()); + } + + private class StaticKeyEvent extends KeyEvent { + + private final double time; + + public StaticKeyEvent( + int key, int scancode, int action, int mods, + double time + ) { + super(key, scancode, action, mods); + this.time = time; + } + + @Override + public double getTime() { + return time; + } + + @Override + public InputEvent snapshot() { + return this; + } + + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/DynamicModel.java b/src/main/java/ru/windcorp/optica/client/graphics/model/DynamicModel.java new file mode 100644 index 0000000..a0f7575 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/DynamicModel.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.google.common.primitives.Booleans; + +import glm.mat._4.Mat4; + +public abstract class DynamicModel extends Model { + + private static final Mat4 IDENTITY = new Mat4(); + + private final Mat4[] transforms; + private final boolean[] dynamics; + + public DynamicModel( + WorldRenderable[] parts, + Mat4[] transforms, + boolean[] dynamic + ) { + super(parts); + this.transforms = transforms; + this.dynamics = dynamic; + } + + public DynamicModel(Builder builder) { + this( + builder.getParts(), + builder.getTransforms(), + builder.getDynamics() + ); + } + + @Override + protected Mat4 getTransform(int shapeIndex) { + Mat4 transform = transforms[shapeIndex]; + + if (dynamics[shapeIndex]) { + transform.identity(); + getDynamicTransform(shapeIndex, transform); + } + + return transform; + } + + protected abstract void getDynamicTransform(int shapeIndex, Mat4 result); + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final List parts = new ArrayList<>(); + private final List transforms = new ArrayList<>(); + private final List dynamics = new ArrayList<>(); + + protected Builder() {} + + private Builder addPart( + WorldRenderable part, + Mat4 transform, + boolean isDynamic + ) { + parts.add(Objects.requireNonNull(part, "part")); + transforms.add(Objects.requireNonNull(transform, "transform")); + dynamics.add(isDynamic); + + return this; + } + + public Builder addStaticPart( + WorldRenderable part, + Mat4 transform + ) { + return addPart(part, new Mat4(transform), false); + } + + public Builder addDynamicPart( + WorldRenderable part + ) { + return addPart(part, new Mat4(), true); + } + + public Builder addStaticPart( + WorldRenderable part + ) { + return addStaticPart(part, IDENTITY); + } + + private WorldRenderable[] getParts() { + return parts.toArray(new WorldRenderable[parts.size()]); + } + + private Mat4[] getTransforms() { + return transforms.toArray(new Mat4[transforms.size()]); + } + + private boolean[] getDynamics() { + return Booleans.toArray(dynamics); + } + + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/EmptyModel.java b/src/main/java/ru/windcorp/optica/client/graphics/model/EmptyModel.java new file mode 100644 index 0000000..0fda90c --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/EmptyModel.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import glm.mat._4.Mat4; +import ru.windcorp.optica.client.graphics.world.WorldRenderer; + +public class EmptyModel extends Model { + + private static final EmptyModel INSTANCE = new EmptyModel(); + + private EmptyModel() { + super(new WorldRenderable[0]); + } + + public static EmptyModel getInstance() { + return INSTANCE; + } + + @Override + public void render(WorldRenderer renderer) { + // Do nothing + } + + @Override + protected Mat4 getTransform(int shapeIndex) { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/Face.java b/src/main/java/ru/windcorp/optica/client/graphics/model/Face.java new file mode 100644 index 0000000..ec90680 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/Face.java @@ -0,0 +1,299 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; +import java.util.Objects; + +import glm.vec._3.Vec3; +import ru.windcorp.optica.client.graphics.texture.Texture; + +public class Face { + + private static final ShortBuffer GENERATE_SUCCESSIVE_LATER = null; + + private Shape shape = null; + int locationOfIndices; + int locationOfVertices; + + private Texture texture; + + ByteBuffer vertices; + private boolean verticesUpdated = true; + + private ShortBuffer userIndices; + private boolean userIndicesUpdated = true; + + public Face( + Texture texture, + ByteBuffer vertices, + ShortBuffer indices + ) { + setTexture(texture); + setVertices(vertices); + setIndices(indices); + } + + public Face( + Texture texture, + ByteBuffer vertices + ) { + this(texture, vertices, null); + } + + void setShape(Shape shape) { + this.shape = shape; + + checkVertices(); + checkIndices(); + } + + void computeNormals() { + Vec3 a = new Vec3(); + Vec3 b = new Vec3(); + Vec3 c = new Vec3(); + Vec3 normal = new Vec3(); + + for (int i = 0; i < getIndexCount(); i += 3) { + int indexA = getIndex(i + 0); + int indexB = getIndex(i + 1); + int indexC = getIndex(i + 2); + + loadVertexPosition(indexA, a); + loadVertexPosition(indexB, b); + loadVertexPosition(indexC, c); + + computeOneNormal(a, b, c, normal); + + saveVertexNormal(indexA, normal); + saveVertexNormal(indexB, normal); + saveVertexNormal(indexC, normal); + } + } + + private void computeOneNormal( + Vec3 a, Vec3 b, Vec3 c, + Vec3 normal + ) { + b.sub(a); + c.sub(a); + b.cross(c, normal); + normal.normalize(); + } + + private void checkVertices() { + if (vertices.remaining() % getBytesPerVertex() != 0) { + throw new IllegalArgumentException( + "Invalid vertex buffer: " + + (vertices.remaining() % getBytesPerVertex()) + + " extra bytes after last vertex" + ); + } + } + + private void checkIndices() { + if (userIndices != GENERATE_SUCCESSIVE_LATER) { + if (userIndices.remaining() % 3 != 0) { + throw new IllegalArgumentException( + "Invalid vertex indices: " + + (userIndices.remaining() % 3) + + " extra indices after last triangle" + ); + } + + userIndices.mark(); + int vertexCount = getVertexCount(); + + while (userIndices.hasRemaining()) { + short index = userIndices.get(); + if (index < 0 || index >= vertexCount) { + throw new IllegalArgumentException( + "Invalid vertex index " + index + + " (" + vertexCount + " vertices available)" + ); + } + } + + userIndices.reset(); + } else { + if (getVertexCount() % 3 != 0) { + throw new IllegalArgumentException( + "Invalid vertices: " + + (getVertexCount() % 3) + + " extra indices after last triangle " + + "(indices are automatic)" + ); + } + } + } + + boolean needsVerticesUpdate() { + return verticesUpdated; + } + + public void markForIndexUpdate() { + if (shape != null) checkIndices(); + markShapeForReassembly(); + userIndicesUpdated = true; + } + + boolean needsIndicesUpdate() { + return userIndicesUpdated; + } + + void resetUpdateFlags() { + verticesUpdated = false; + userIndicesUpdated = false; + } + + private void markShapeForReassembly() { + if (shape != null) { + shape.markForReassembly(); + } + } + + public int getVertexCount() { + return vertices.remaining() / getBytesPerVertex(); + } + + private int getBytesPerVertex() { + return shape.getProgram().getBytesPerVertex(); + } + + public ByteBuffer getVertices() { + return vertices; + } + + private void loadVertexPosition(int index, Vec3 result) { + int offset = vertices.position() + index * getBytesPerVertex(); + + result.set( + vertices.getFloat(offset + 0 * Float.BYTES), + vertices.getFloat(offset + 1 * Float.BYTES), + vertices.getFloat(offset + 2 * Float.BYTES) + ); + } + + private void saveVertexNormal(int index, Vec3 normal) { + int offset = vertices.position() + index * getBytesPerVertex() + ( + 3 * Float.BYTES + + 3 * Float.BYTES + + 2 * Float.BYTES + ); + + vertices.putFloat(offset + 0 * Float.BYTES, normal.x); + vertices.putFloat(offset + 1 * Float.BYTES, normal.y); + vertices.putFloat(offset + 2 * Float.BYTES, normal.z); + + verticesUpdated = true; + } + + public Face setVertices(ByteBuffer vertices) { + this.vertices = Objects.requireNonNull(vertices, "vertices"); + markShapeForReassembly(); + this.verticesUpdated = true; + + if (shape != null) checkVertices(); + + return this; + } + + int getLocationOfVertices() { + return locationOfVertices; + } + + int getByteOffsetOfVertices() { + return locationOfVertices; + } + + public ShortBuffer getIndices() { + if (userIndices == GENERATE_SUCCESSIVE_LATER) { + userIndices = generateSuccessiveIndices(0); + } + + return userIndices; + } + + public int getIndex(int i) { + if (userIndices == GENERATE_SUCCESSIVE_LATER) { + return i; + } else { + ShortBuffer indices = getIndicesOrNull(); + return indices.get(indices.position() + i); + } + } + + ShortBuffer getIndicesOrNull() { + if (userIndices == GENERATE_SUCCESSIVE_LATER) { + return null; + } + + return userIndices; + } + + public int getIndexCount() { + if (userIndices == GENERATE_SUCCESSIVE_LATER) { + return getVertexCount(); + } + + return userIndices.remaining(); + } + + public Face setIndices(ShortBuffer indices) { + if (indices == null) { + indices = GENERATE_SUCCESSIVE_LATER; + } + + this.userIndices = indices; + markForIndexUpdate(); + + if (shape != null) checkIndices(); + + return this; + } + + private ShortBuffer generateSuccessiveIndices(int offset) { + int vertexCount = getVertexCount(); + ShortBuffer result = ShortBuffer.allocate(vertexCount); + + for (short vertex = 0; vertex < vertexCount; ++vertex) { + result.put((short) (vertex + offset)); + } + + result.flip(); + return result; + } + + int getLocationOfIndices() { + return locationOfIndices; + } + + int getByteOffsetOfIndices() { + return locationOfIndices * Short.BYTES; + } + + public Texture getTexture() { + return texture; + } + + public void setTexture(Texture texture) { + this.texture = Objects.requireNonNull(texture, "texture"); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/Faces.java b/src/main/java/ru/windcorp/optica/client/graphics/model/Faces.java new file mode 100644 index 0000000..6c5d6c9 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/Faces.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import java.nio.ShortBuffer; + +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; +import ru.windcorp.optica.client.graphics.model.ShapeRenderProgram.VertexBuilder; +import ru.windcorp.optica.client.graphics.texture.Texture; +import ru.windcorp.optica.common.block.BlockFace; + +public class Faces { + + private Faces() {} + + public static Face createRectangle( + Texture texture, + Vec3 colorMultiplier, + Vec3 origin, + Vec3 width, + Vec3 height + ) { + VertexBuilder builder = new VertexBuilder(); + + Vec3 pos = new Vec3(); + Vec2 texCoords = new Vec2(); + + builder.addVertex( + origin, + colorMultiplier, + texCoords.set(0, 0) + ).addVertex( + pos.set(origin).add(height), + colorMultiplier, + texCoords.set(0, 1) + ).addVertex( + pos.set(origin).add(width), + colorMultiplier, + texCoords.set(1, 0) + ).addVertex( + pos.add(height), + colorMultiplier, + texCoords.set(1, 1) + ); + + return new Face( + texture, + builder.assemble(), + ShortBuffer.wrap(new short[] { + 3, 1, 0, + 2, 3, 0 + }) + ); + } + + public static Face createBlockFace( + Texture texture, + Vec3 colorMultiplier, + Vec3 blockCenter, + BlockFace face + ) { + switch (face) { + case TOP: + return createRectangle( + texture, colorMultiplier, + blockCenter.add(-0.5f, +0.5f, +0.5f), + new Vec3( 0, -1, 0), + new Vec3(+1, 0, 0) + ); + case BOTTOM: + return createRectangle( + texture, colorMultiplier, + blockCenter.add(-0.5f, -0.5f, -0.5f), + new Vec3( 0, +1, 0), + new Vec3(+1, 0, 0) + ); + case NORTH: + return createRectangle( + texture, colorMultiplier, + blockCenter.add(+0.5f, -0.5f, -0.5f), + new Vec3( 0, +1, 0), + new Vec3( 0, 0, +1) + ); + case SOUTH: + return createRectangle( + texture, colorMultiplier, + blockCenter.add(-0.5f, +0.5f, -0.5f), + new Vec3( 0, -1, 0), + new Vec3( 0, 0, +1) + ); + case EAST: + return createRectangle( + texture, colorMultiplier, + blockCenter.add(-0.5f, -0.5f, -0.5f), + new Vec3(+1, 0, 0), + new Vec3( 0, 0, +1) + ); + case WEST: + return createRectangle( + texture, colorMultiplier, + blockCenter.add(+0.5f, +0.5f, -0.5f), + new Vec3(-1, 0, 0), + new Vec3( 0, 0, +1) + ); + default: + throw new NullPointerException("face"); + } + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/Model.java b/src/main/java/ru/windcorp/optica/client/graphics/model/Model.java new file mode 100644 index 0000000..b65ed39 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/Model.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import glm.mat._4.Mat4; +import ru.windcorp.optica.client.graphics.world.WorldRenderer; + +public abstract class Model implements WorldRenderable { + + private final WorldRenderable[] parts; + + public Model(WorldRenderable[] parts) { + this.parts = parts; + } + + protected abstract Mat4 getTransform(int partIndex); + + @Override + public void render(WorldRenderer renderer) { + for (int i = 0; i < parts.length; ++i) { + WorldRenderable part = parts[i]; + Mat4 transform = getTransform(i); + + try { + renderer.pushWorldTransform().mul(transform); + part.render(renderer); + } finally { + renderer.popWorldTransform(); + } + } + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/Shape.java b/src/main/java/ru/windcorp/optica/client/graphics/model/Shape.java new file mode 100644 index 0000000..05dba75 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/Shape.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +import org.lwjgl.BufferUtils; + +import ru.windcorp.optica.client.graphics.backend.Usage; +import ru.windcorp.optica.client.graphics.backend.VertexBufferObject; +import ru.windcorp.optica.client.graphics.world.WorldRenderer; + +public class Shape implements WorldRenderable { + + private final ShapeRenderProgram program; + private final Face[] faces; + private final Usage usage; + + private ByteBuffer vertices; + private ShortBuffer indices; + + private boolean initialized = false; + private boolean needsAssembly = true; + private boolean needsVBOUpdate = true; + + private VertexBufferObject verticesVbo; + private VertexBufferObject indicesVbo; + + public Shape(Usage usage, ShapeRenderProgram program, Face... faces) { + this.program = program; + this.faces = faces; + this.usage = usage; + + configureFaces(); + + assembleBuffers(); + } + + public Shape(Usage usage, Face... faces) { + this(usage, ShapeRenderProgram.getDefault(), faces); + } + + private void configureFaces() { + for (Face face : faces) { + face.setShape(this); + face.computeNormals(); + } + } + + private void assembleBuffers() { + // TODO optimize: only update faces that requested it + + resizeBuffers(); + + for (Face face : faces) { + assembleVertices(face); + assembleIndices(face); + face.resetUpdateFlags(); + } + + this.vertices.flip(); + this.indices.flip(); + + needsAssembly = false; + needsVBOUpdate = true; + } + + private void resizeBuffers() { + int verticesRequired = 0, indicesRequired = 0; + for (Face face : faces) { + verticesRequired += face.getVertices().remaining(); + indicesRequired += face.getIndices().remaining(); + } + + if (this.vertices == null || vertices.capacity() < verticesRequired) { + this.vertices = BufferUtils.createByteBuffer(verticesRequired); + } else { + this.vertices.position(0).limit(verticesRequired); + } + + if (this.indices == null || this.indices.capacity() < indicesRequired) { + this.indices = BufferUtils.createShortBuffer(indicesRequired); + } else { + this.indices.position(0).limit(indicesRequired); + } + } + + private void assembleVertices(Face face) { + face.locationOfVertices = this.vertices.position(); + + insertVertices(face); + linkVerticesWith(face); + } + + private void insertVertices(Face face) { + ByteBuffer faceVertices = face.getVertices(); + + faceVertices.mark(); + this.vertices.put(faceVertices); + faceVertices.reset(); + } + + private void linkVerticesWith(Face face) { + int limit = vertices.limit(); + int position = vertices.position(); + + vertices.limit(position).position(face.getLocationOfVertices()); + face.vertices = vertices.slice(); + + vertices.position(position).limit(limit); + } + + private void assembleIndices(Face face) { + short vertexOffset = (short) ( + face.getLocationOfVertices() / program.getBytesPerVertex() + ); + + face.locationOfIndices = indices.position(); + + ShortBuffer faceIndices = face.getIndices(); + + if (faceIndices == null) { + for (int i = 0; i < face.getVertexCount(); ++i) { + this.indices.put((short) (vertexOffset + i)); + } + } else { + for (int i = faceIndices.position(); i < faceIndices.limit(); ++i) { + short faceIndex = faceIndices.get(i); + faceIndex += vertexOffset; + this.indices.put(faceIndex); + } + } + } + + void markForReassembly() { + needsAssembly = true; + } + + @Override + public void render(WorldRenderer renderer) { + if (!initialized) initialize(); + if (needsAssembly) assembleBuffers(); + if (needsVBOUpdate) updateVBO(); + + program.render(renderer, this); + } + + private void initialize() { + verticesVbo = new VertexBufferObject(usage); + indicesVbo = new VertexBufferObject(usage); + needsVBOUpdate = true; + + initialized = true; + } + + private void updateVBO() { + verticesVbo.setData(vertices); + indicesVbo.setData(indices); + + needsVBOUpdate = false; + } + + VertexBufferObject getVerticesVbo() { + return verticesVbo; + } + + VertexBufferObject getIndicesVbo() { + return indicesVbo; + } + + public ShapeRenderProgram getProgram() { + return program; + } + + public Face[] getFaces() { + return faces; + } + + public Usage getUsage() { + return usage; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/ShapeRenderProgram.java b/src/main/java/ru/windcorp/optica/client/graphics/model/ShapeRenderProgram.java new file mode 100644 index 0000000..e0560ca --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/ShapeRenderProgram.java @@ -0,0 +1,298 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; + +import com.google.common.collect.ObjectArrays; + +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; +import ru.windcorp.optica.client.graphics.backend.VertexBufferObject; +import ru.windcorp.optica.client.graphics.backend.VertexBufferObject.BindTarget; +import ru.windcorp.optica.client.graphics.backend.shaders.CombinedShader; +import ru.windcorp.optica.client.graphics.backend.shaders.Program; +import ru.windcorp.optica.client.graphics.backend.shaders.attributes.AttributeVertexArray; +import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform1Int; +import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform2Float; +import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform4Matrix; +import ru.windcorp.optica.client.graphics.texture.Sprite; +import ru.windcorp.optica.client.graphics.world.WorldRenderer; + +public class ShapeRenderProgram extends Program { + + private static ShapeRenderProgram def = null; + + public static void init() { + def = new ShapeRenderProgram( + new String[] {"WorldDefault.vertex.glsl"}, + new String[] {"WorldDefault.fragment.glsl"} + ); + } + + public static ShapeRenderProgram getDefault() { + return def; + } + + private static final int DEFAULT_BYTES_PER_VERTEX = + 3 * Float.BYTES + // Position + 3 * Float.BYTES + // Color multiplier + 2 * Float.BYTES + // Texture coordinates + 3 * Float.BYTES; // Normals + + private static final String SHAPE_VERTEX_SHADER_RESOURCE = + "Shape.vertex.glsl"; + private static final String SHAPE_FRAGMENT_SHADER_RESOURCE = + "Shape.fragment.glsl"; + + private static final String + FINAL_TRANSFORM_UNIFORM_NAME = "finalTransform", + WORLD_TRANSFORM_UNIFORM_NAME = "worldTransform", + POSITIONS_ATTRIBUTE_NAME = "inputPositions", + COLOR_MULTIPLER_ATTRIBUTE_NAME = "inputColorMultiplier", + TEXTURE_COORDS_ATTRIBUTE_NAME = "inputTextureCoords", + TEXTURE_SLOT_UNIFORM_NAME = "textureSlot", + TEXTURE_START_UNIFORM_NAME = "textureStart", + TEXTURE_SIZE_UNIFORM_NAME = "textureSize", + NORMALS_ATTRIBUTE_NAME = "inputNormals"; + + private final Uniform4Matrix finalTransformUniform; + private final Uniform4Matrix worldTransformUniform; + private final AttributeVertexArray positionsAttribute; + private final AttributeVertexArray colorsAttribute; + private final AttributeVertexArray textureCoordsAttribute; + private final Uniform1Int textureSlotUniform; + private final Uniform2Float textureStartUniform; + private final Uniform2Float textureSizeUniform; + private final AttributeVertexArray normalsAttribute; + + public ShapeRenderProgram( + String[] vertexShaderResources, + String[] fragmentShaderResources + ) { + super( + new CombinedShader( + attachVertexShader(vertexShaderResources) + ), + new CombinedShader( + attachFragmentShader(fragmentShaderResources) + ) + ); + + this.finalTransformUniform = getUniform(FINAL_TRANSFORM_UNIFORM_NAME) + .as4Matrix(); + + this.worldTransformUniform = getUniform(WORLD_TRANSFORM_UNIFORM_NAME) + .as4Matrix(); + + this.positionsAttribute = + getAttribute(POSITIONS_ATTRIBUTE_NAME).asVertexArray(); + + this.colorsAttribute = + getAttribute(COLOR_MULTIPLER_ATTRIBUTE_NAME).asVertexArray(); + + this.textureCoordsAttribute = + getAttribute(TEXTURE_COORDS_ATTRIBUTE_NAME).asVertexArray(); + + this.textureSlotUniform = getUniform(TEXTURE_SLOT_UNIFORM_NAME) + .as1Int(); + + this.textureStartUniform = getUniform(TEXTURE_START_UNIFORM_NAME) + .as2Float(); + + this.textureSizeUniform = getUniform(TEXTURE_SIZE_UNIFORM_NAME) + .as2Float(); + + this.normalsAttribute = getAttribute(NORMALS_ATTRIBUTE_NAME) + .asVertexArray(); + } + + private static String[] attachVertexShader(String[] others) { + return ObjectArrays.concat(SHAPE_VERTEX_SHADER_RESOURCE, others); + } + + private static String[] attachFragmentShader(String[] others) { + return ObjectArrays.concat(SHAPE_FRAGMENT_SHADER_RESOURCE, others); + } + + public void render( + WorldRenderer renderer, + Shape shape + ) { + use(); + configure(renderer); + + bindVertices(shape.getVerticesVbo()); + bindIndices(shape.getIndicesVbo()); + + try { + positionsAttribute.enable(); + colorsAttribute.enable(); + textureCoordsAttribute.enable(); + normalsAttribute.enable(); + + for (Face face : shape.getFaces()) { + renderFace(face); + } + + } finally { + positionsAttribute.disable(); + colorsAttribute.disable(); + textureCoordsAttribute.disable(); + normalsAttribute.disable(); + } + } + + protected void configure(WorldRenderer renderer) { + finalTransformUniform.set(renderer.getFinalTransform()); + worldTransformUniform.set(renderer.getWorldTransform()); + } + + protected int bindVertices(VertexBufferObject vertices) { + int vertexStride = getBytesPerVertex(); + int offset = 0; + + positionsAttribute.set( + 3, GL11.GL_FLOAT, false, vertexStride, vertices, + offset + ); + offset += 3 * Float.BYTES; + + colorsAttribute.set( + 3, GL11.GL_FLOAT, false, vertexStride, vertices, + offset + ); + offset += 3 * Float.BYTES; + + textureCoordsAttribute.set( + 2, GL11.GL_FLOAT, false, vertexStride, vertices, + offset + ); + offset += 2 * Float.BYTES; + + normalsAttribute.set( + 3, GL11.GL_FLOAT, false, vertexStride, vertices, + offset + ); + offset += 3 * Float.BYTES; + + return offset; + } + + protected void bindIndices(VertexBufferObject indices) { + indices.bind(BindTarget.ELEMENT_ARRAY); + } + + protected void renderFace(Face face) { + Sprite sprite = face.getTexture().getSprite(); + + sprite.getPrimitive().bind(0); + textureSlotUniform.set(0); + + textureStartUniform.set(sprite.getStart()); + textureSizeUniform.set(sprite.getSize()); + + GL11.glDrawElements( + GL11.GL_TRIANGLES, + face.getIndexCount(), + GL11.GL_UNSIGNED_SHORT, + face.getByteOffsetOfIndices() + ); + } + + public int getBytesPerVertex() { + return DEFAULT_BYTES_PER_VERTEX; + } + + public static class VertexBuilder { + + private static class Vertex { + final Vec3 position; + final Vec3 colorMultiplier; + final Vec2 textureCoords; + + Vertex(Vec3 position, Vec3 colorMultiplier, Vec2 textureCoords) { + this.position = position; + this.colorMultiplier = colorMultiplier; + this.textureCoords = textureCoords; + } + } + + private final List vertices = new ArrayList<>(); + + public VertexBuilder addVertex( + float x, float y, float z, + float r, float g, float b, + float tx, float ty + ) { + vertices.add(new Vertex( + new Vec3(x, y, z), + new Vec3(r, g, b), + new Vec2(tx, ty) + )); + + return this; + } + + public VertexBuilder addVertex( + Vec3 position, + Vec3 colorMultiplier, + Vec2 textureCoords + ) { + vertices.add(new Vertex( + new Vec3(position), + new Vec3(colorMultiplier), + new Vec2(textureCoords) + )); + + return this; + } + + public ByteBuffer assemble() { + ByteBuffer result = BufferUtils.createByteBuffer( + DEFAULT_BYTES_PER_VERTEX * vertices.size() + ); + + for (Vertex v : vertices) { + result + .putFloat(v.position.x) + .putFloat(v.position.y) + .putFloat(v.position.z) + .putFloat(v.colorMultiplier.x) + .putFloat(v.colorMultiplier.y) + .putFloat(v.colorMultiplier.z) + .putFloat(v.textureCoords.x) + .putFloat(v.textureCoords.y) + .putFloat(Float.NaN) + .putFloat(Float.NaN) + .putFloat(Float.NaN); + } + + result.flip(); + + return result; + } + + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/Shapes.java b/src/main/java/ru/windcorp/optica/client/graphics/model/Shapes.java new file mode 100644 index 0000000..54dac98 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/Shapes.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import glm.vec._3.Vec3; +import ru.windcorp.optica.client.graphics.backend.Usage; +import ru.windcorp.optica.client.graphics.texture.Texture; + +public class Shapes { + + public static Shape createParallelepiped( // Try saying that 10 times fast + Vec3 origin, + + Vec3 width, + Vec3 height, + Vec3 depth, + + Vec3 colorMultiplier, + + Texture topTexture, + Texture bottomTexture, + Texture northTexture, + Texture southTexture, + Texture eastTexture, + Texture westTexture + ) { + + Vec3 faceOrigin = new Vec3(); + Vec3 faceWidth = new Vec3(); + + Face top = Faces.createRectangle( + topTexture, colorMultiplier, + faceOrigin.set(origin).add(height).add(width), + faceWidth.set(width).negate(), + depth + ); + + Face bottom = Faces.createRectangle( + bottomTexture, colorMultiplier, + origin, + width, + depth + ); + + Face north = Faces.createRectangle( + northTexture, colorMultiplier, + faceOrigin.set(origin).add(depth), + width, + height + ); + + Face south = Faces.createRectangle( + southTexture, colorMultiplier, + faceOrigin.set(origin).add(width), + faceWidth.set(width).negate(), + height + ); + + Face east = Faces.createRectangle( + eastTexture, colorMultiplier, + origin, + depth, + height + ); + + Face west = Faces.createRectangle( + westTexture, colorMultiplier, + faceOrigin.set(origin).add(width).add(depth), + faceWidth.set(depth).negate(), + height + ); + + Shape result = new Shape( + Usage.STATIC, + top, bottom, north, south, east, west + ); + + return result; + } + + public static class PppBuilder { + + private final Vec3 origin = new Vec3(-0.5f, -0.5f, -0.5f); + + private final Vec3 depth = new Vec3(1, 0, 0); + private final Vec3 width = new Vec3(0, 1, 0); + private final Vec3 height = new Vec3(0, 0, 1); + + private final Vec3 colorMultiplier = new Vec3(1, 1, 1); + + private final Texture topTexture; + private final Texture bottomTexture; + private final Texture northTexture; + private final Texture southTexture; + private final Texture eastTexture; + private final Texture westTexture; + + public PppBuilder( + Texture top, + Texture bottom, + Texture north, + Texture south, + Texture east, + Texture west + ) { + this.topTexture = top; + this.bottomTexture = bottom; + this.northTexture = north; + this.southTexture = south; + this.eastTexture = east; + this.westTexture = west; + } + + public PppBuilder(Texture texture) { + this(texture, texture, texture, texture, texture, texture); + } + + public PppBuilder setOrigin(Vec3 origin) { + this.origin.set(origin); + return this; + } + + public PppBuilder setOrigin(float x, float y, float z) { + this.origin.set(x, y, z); + return this; + } + + public PppBuilder setColorMultiplier(Vec3 colorMultiplier) { + this.colorMultiplier.set(colorMultiplier); + return this; + } + + public PppBuilder setColorMultiplier(float r, float g, float b) { + this.colorMultiplier.set(r, g, b); + return this; + } + + public PppBuilder setDepth(Vec3 vector) { + this.depth.set(vector); + return this; + } + + public PppBuilder setDepth(float x, float y, float z) { + this.depth.set(x, y, z); + return this; + } + + public PppBuilder setDepth(float x) { + this.depth.set(x, 0, 0); + return this; + } + + public PppBuilder setWidth(Vec3 vector) { + this.width.set(vector); + return this; + } + + public PppBuilder setWidth(float x, float y, float z) { + this.width.set(x, y, z); + return this; + } + + public PppBuilder setWidth(float y) { + this.width.set(0, y, 0); + return this; + } + + public PppBuilder setHeight(Vec3 vector) { + this.height.set(vector); + return this; + } + + public PppBuilder setHeight(float x, float y, float z) { + this.height.set(x, y, z); + return this; + } + + public PppBuilder setHeight(float z) { + this.height.set(0, 0, z); + return this; + } + + public PppBuilder setSize(float x, float y, float z) { + return this.setWidth(x).setDepth(y).setHeight(z); + } + + public Shape create() { + return createParallelepiped( + origin, + width, height, depth, + colorMultiplier, + topTexture, + bottomTexture, + northTexture, + southTexture, + eastTexture, + westTexture + ); + } + + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/StaticModel.java b/src/main/java/ru/windcorp/optica/client/graphics/model/StaticModel.java new file mode 100644 index 0000000..84c755c --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/StaticModel.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import glm.mat._4.Mat4; + +public class StaticModel extends Model { + + private static final Mat4 IDENTITY = new Mat4(); + + private final Mat4[] transforms; + + public StaticModel( + WorldRenderable[] parts, + Mat4[] transforms + ) { + super(parts); + this.transforms = transforms; + } + + public StaticModel(Builder builder) { + this(builder.getParts(), builder.getTransforms()); + } + + @Override + protected Mat4 getTransform(int partIndex) { + return transforms[partIndex]; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final List parts = new ArrayList<>(); + private final List transforms = new ArrayList<>(); + + protected Builder() {} + + public Builder addPart( + WorldRenderable part, + Mat4 transform + ) { + parts.add(Objects.requireNonNull(part, "part")); + transforms.add(Objects.requireNonNull(transform, "transform")); + + return this; + } + + public Builder addPart( + WorldRenderable part + ) { + return addPart(part, IDENTITY); + } + + private WorldRenderable[] getParts() { + return parts.toArray(new WorldRenderable[parts.size()]); + } + + private Mat4[] getTransforms() { + return transforms.toArray(new Mat4[transforms.size()]); + } + + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/model/WorldRenderable.java b/src/main/java/ru/windcorp/optica/client/graphics/model/WorldRenderable.java new file mode 100644 index 0000000..e8fbe90 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/model/WorldRenderable.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.model; + +import ru.windcorp.optica.client.graphics.world.WorldRenderer; + +public interface WorldRenderable { + + void render(WorldRenderer renderer); + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/texture/Pixels.java b/src/main/java/ru/windcorp/optica/client/graphics/texture/Pixels.java new file mode 100644 index 0000000..2971afc --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/texture/Pixels.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.texture; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL12.*; + +import java.nio.ByteBuffer; + +class Pixels { + + private final ByteBuffer data; + + private final int bufferWidth; + private final int bufferHeight; + + private final boolean filtered; + + public Pixels( + ByteBuffer data, + int bufferWidth, int bufferHeight, + boolean filtered + ) { + this.data = data; + this.bufferWidth = bufferWidth; + this.bufferHeight = bufferHeight; + this.filtered = filtered; + } + + public int load() { + int handle = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, handle); + + if (filtered) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D( + GL_TEXTURE_2D, // Load 2D image + 0, // Not mipmapped + GL_RGBA, // Use RGBA + bufferWidth, // Width + bufferHeight, // Height + 0, // No border + GL_RGBA, // Use RGBA (required) + GL_UNSIGNED_BYTE, // Use unsigned bytes + data // Data buffer + ); + + glBindTexture(GL_TEXTURE_2D, 0); + + return handle; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/texture/SimpleTexture.java b/src/main/java/ru/windcorp/optica/client/graphics/texture/SimpleTexture.java new file mode 100644 index 0000000..0af8360 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/texture/SimpleTexture.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.texture; + +public class SimpleTexture extends Texture { + + private final Sprite sprite; + + public SimpleTexture(Sprite sprite) { + this.sprite = sprite; + } + + @Override + public Sprite getSprite() { + return sprite; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/texture/Sprite.java b/src/main/java/ru/windcorp/optica/client/graphics/texture/Sprite.java new file mode 100644 index 0000000..24f53df --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/texture/Sprite.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.texture; + +import java.util.Objects; + +import glm.vec._2.Vec2; + +public class Sprite { + + private static final Vec2 ORIGIN = new Vec2(0, 0); + private static final Vec2 FULL_PRIMITIVE = new Vec2(1, 1); + + private final TexturePrimitive primitive; + + private final Vec2 start; + private final Vec2 size; + + public Sprite(TexturePrimitive primitive, Vec2 start, Vec2 size) { + this.primitive = Objects.requireNonNull(primitive, "primitive"); + this.start = Objects.requireNonNull(start, "start"); + this.size = Objects.requireNonNull(size, "size"); + } + + public Sprite(TexturePrimitive primitive) { + this(primitive, ORIGIN, FULL_PRIMITIVE); + } + + public TexturePrimitive getPrimitive() { + return primitive; + } + + public Vec2 getStart() { + return start; + } + + public Vec2 getSize() { + return size; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/texture/Texture.java b/src/main/java/ru/windcorp/optica/client/graphics/texture/Texture.java new file mode 100644 index 0000000..be36348 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/texture/Texture.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.texture; + +public abstract class Texture { + + public abstract Sprite getSprite(); + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/texture/TextureManager.java b/src/main/java/ru/windcorp/optica/client/graphics/texture/TextureManager.java new file mode 100644 index 0000000..419b8e8 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/texture/TextureManager.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.texture; + +import java.awt.Graphics; +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Hashtable; +import javax.imageio.ImageIO; + +import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue; +import ru.windcorp.optica.common.resource.Resource; +import ru.windcorp.optica.common.resource.ResourceManager; + +public class TextureManager { + + private static final ColorModel COLOR_MODEL = new ComponentColorModel( + ColorSpace.getInstance(ColorSpace.CS_sRGB), // Use RGB + new int[] {8, 8, 8, 8}, // Use every bit + true, // Has alpha + false, // Not premultiplied + ComponentColorModel.TRANSLUCENT, // Can have any alpha + DataBuffer.TYPE_BYTE // Alpha is one byte + ); + + private static final Hashtable CANVAS_PROPERTIES = new Hashtable<>(); + private static final java.awt.Color CANVAS_BACKGROUND = + new java.awt.Color(0, 0, 0, 0); + + private static final String TEXTURE_ASSETS_PREFIX = "assets/textures/"; + + private static Resource getResource(String textureName) { + return ResourceManager.getResource( + TEXTURE_ASSETS_PREFIX + textureName + ".png" + ); + } + + public static TexturePrimitive load(String textureName, boolean filtered) { + TexturePrimitive result = loadToByteBuffer(textureName, filtered); + RenderTaskQueue.invokeLater(result::load); + return result; + } + + public static TexturePrimitive loadToByteBuffer( + String textureName, boolean filter + ) { + Resource resource = getResource(textureName); + + BufferedImage source = readImage(resource); + + int bufferWidth = toPowerOf2(source.getWidth()), + bufferHeight = toPowerOf2(source.getHeight()); + + WritableRaster raster = Raster.createInterleavedRaster( + DataBuffer.TYPE_BYTE, // Storage model + bufferWidth, // Buffer width + bufferHeight, // Buffer height + 4, // RGBA + null // Location (here (0; 0)) + ); + + BufferedImage canvas = new BufferedImage( + COLOR_MODEL, // Color model + raster, // Backing raster + false, // Raster is not premultipied + CANVAS_PROPERTIES // Properties + ); + + Graphics g = canvas.createGraphics(); + g.setColor(CANVAS_BACKGROUND); + g.fillRect(0, 0, source.getWidth(), source.getHeight()); + g.drawImage( + source, + 0, 0, source.getWidth(), source.getHeight(), + 0, source.getHeight(), source.getWidth(), 0, // Flip the image + null + ); + g.dispose(); + + byte[] data = ( + (DataBufferByte) canvas.getRaster().getDataBuffer() + ).getData(); + + ByteBuffer buffer = ByteBuffer.allocateDirect(data.length); + buffer.order(ByteOrder.nativeOrder()); + buffer.put(data); + buffer.flip(); + + Pixels pixels = new Pixels(buffer, bufferWidth, bufferHeight, filter); + + TexturePrimitive result = new TexturePrimitive( + pixels, + source.getWidth(), + source.getHeight(), + bufferWidth, + bufferHeight + ); + + return result; + } + + private static BufferedImage readImage(Resource resource) { + try { + return ImageIO.read(resource.getInputStream()); + } catch (Exception e) { + throw new RuntimeException("too bad. refresh project u stupid. must be " + resource.getName(), e); + } + } + + private static int toPowerOf2(int i) { + + // TODO optimize + + int result = 1; + do { + result *= 2; + } while (result < i); + return result; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/optica/client/graphics/texture/TexturePrimitive.java b/src/main/java/ru/windcorp/optica/client/graphics/texture/TexturePrimitive.java new file mode 100644 index 0000000..a2833a4 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/texture/TexturePrimitive.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.texture; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker; +import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; + +public class TexturePrimitive implements OpenGLDeletable { + + private static final int NOT_LOADED = -1; + + private int handle = NOT_LOADED; + private Pixels pixelsToLoad; + + private final int width; + private final int height; + private final int bufferWidth; + private final int bufferHeight; + + protected TexturePrimitive( + Pixels pixels, + int width, int height, + int bufferWidth, int bufferHeight + ) { + this.pixelsToLoad = pixels; + this.width = width; + this.height = height; + this.bufferWidth = bufferWidth; + this.bufferHeight = bufferHeight; + + OpenGLObjectTracker.register(this); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getBufferWidth() { + return bufferWidth; + } + + public int getBufferHeight() { + return bufferHeight; + } + + public boolean isLoaded() { + return handle != NOT_LOADED; + } + + public void bind(int slot) { + if (!isLoaded()) { + load(); + } + + int code = GL_TEXTURE0 + slot; + + glActiveTexture(code); + glBindTexture(GL_TEXTURE_2D, handle); + } + + protected void load() { + if (isLoaded()) return; + + handle = pixelsToLoad.load(); + + if (handle < 0) { + throw new RuntimeException("oops"); + } + + pixelsToLoad = null; + } + + @Override + public void delete() { + if (isLoaded()) + glDeleteTextures(handle); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/world/Camera.java b/src/main/java/ru/windcorp/optica/client/graphics/world/Camera.java new file mode 100644 index 0000000..071575e --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/world/Camera.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.world; + +import static java.lang.Math.*; + +import glm.Glm; +import glm.mat._4.Mat4; +import glm.vec._3.Vec3; +import ru.windcorp.optica.client.graphics.backend.GraphicsInterface; + +public class Camera { + + private final Vec3 position = new Vec3(); + + private float pitch; + private float yaw; + + private float fieldOfView; + + public Camera(Vec3 position, float pitch, float yaw, float fieldOfView) { + teleport(position); + setPitch(pitch); + setYaw(yaw); + setFieldOfView(fieldOfView); + } + + public Camera() {} + + public void apply(WorldRenderer renderer) { + Mat4 previous = renderer.getViewTransform(); + Glm.perspective( + computeFovY(), + GraphicsInterface.getAspectRatio(), + 0.01f, 10000.0f, + renderer.pushViewTransform() + ).mul(previous); + + renderer.pushViewTransform().rotateX(pitch).rotateY(yaw); + + renderer.pushViewTransform().translate(position.negate()); + position.negate(); + } + + private float computeFovY() { + float widthOverHeight = GraphicsInterface.getAspectRatio(); + + if (widthOverHeight >= 1) { + return fieldOfView; + } else { + return (float) (2 * atan( + 1 / widthOverHeight + * + tan(fieldOfView / 2) + )); + } + } + + public Vec3 getPosition() { + return position; + } + + public void teleport(Vec3 pos) { + position.set(pos); + } + + public void move(Vec3 pos) { + position.add(pos); + } + + public float getPitch() { + return pitch; + } + + public void setPitch(float pitch) { + final float maxPitch = (float) (Math.PI / 2); + this.pitch = Glm.clamp(pitch, -maxPitch, +maxPitch); + } + + public float getYaw() { + return yaw; + } + + public void setYaw(float yaw) { + this.yaw = Glm.mod(yaw, 2 * (float) PI); + } + + public void setDirection(float pitch, float yaw) { + setPitch(pitch); + setYaw(yaw); + } + + public void turn(float pitchChange, float yawChange) { + setPitch(getPitch() + pitchChange); + setYaw(getYaw() + yawChange); + } + + public float getFieldOfView() { + return fieldOfView; + } + + public void setFieldOfView(float fieldOfView) { + this.fieldOfView = fieldOfView; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/optica/client/graphics/world/LayerWorld.java new file mode 100644 index 0000000..ec6a41a --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/world/LayerWorld.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.world; + +import org.lwjgl.glfw.GLFW; + +import com.google.common.eventbus.Subscribe; + +import glm.mat._3.Mat3; +import glm.vec._3.Vec3; +import ru.windcorp.optica.client.graphics.Layer; +import ru.windcorp.optica.client.graphics.backend.GraphicsBackend; +import ru.windcorp.optica.client.graphics.backend.GraphicsInterface; +import ru.windcorp.optica.client.graphics.input.CursorMoveEvent; +import ru.windcorp.optica.client.graphics.input.KeyEvent; +import ru.windcorp.optica.client.world.WorldRender; +import ru.windcorp.optica.common.world.WorldData; + +public class LayerWorld extends Layer { + + private final Camera camera = new Camera( + new Vec3(8, 8, 8), + 0, 0, + (float) Math.toRadians(70) + ); + + private final Vec3 velocity = new Vec3(); + private final Vec3 tmp = new Vec3(); + + private final Mat3 angMat = new Mat3(); + + private int movementX = 0; + private int movementY = 0; + private int movementZ = 0; + + private final WorldRenderer renderer = new WorldRenderer(); + + private final WorldRender world = new WorldRender(new WorldData()); + + public LayerWorld() { + super("World"); + } + + @Override + protected void initialize() { + // TODO Auto-generated method stub + GraphicsInterface.subscribeToInputEvents(this); + } + + @Override + protected void doRender() { + camera.apply(renderer); + renderWorld(); + renderer.reset(); + + angMat.set().rotateY(-camera.getYaw()); + + tmp.set(movementX, 0, movementZ); + angMat.mul_(tmp); // bug in jglm + tmp.y = movementY; + tmp.mul(0.1f); + tmp.sub(velocity); + tmp.mul(0.1f); + velocity.add(tmp); + tmp.set(velocity); + tmp.mul((float) (GraphicsInterface.getFrameLength() * 60)); + camera.move(tmp); + } + + private void renderWorld() { + world.render(renderer); + } + + public Camera getCamera() { + return camera; + } + + private boolean flag = true; + + @Subscribe + public void onKeyEvent(KeyEvent event) { + if (event.isRepeat()) return; + + int multiplier = event.isPress() ? 1 : -1; + + switch (event.getKey()) { + case GLFW.GLFW_KEY_W: + movementZ += -1 * multiplier; + break; + case GLFW.GLFW_KEY_S: + movementZ += +1 * multiplier; + break; + case GLFW.GLFW_KEY_A: + movementX += -1 * multiplier; + break; + case GLFW.GLFW_KEY_D: + movementX += +1 * multiplier; + break; + case GLFW.GLFW_KEY_SPACE: + movementY += +1 * multiplier; + break; + case GLFW.GLFW_KEY_LEFT_SHIFT: + movementY += -1 * multiplier; + break; + + case GLFW.GLFW_KEY_ESCAPE: + if (!event.isPress()) return; + + if (flag) { + GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); + } else { + GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); + } + + flag = !flag; + break; + } + } + + @Subscribe + public void onMouseMoved(CursorMoveEvent event) { + if (!flag) return; + + final float yawScale = 0.002f; + final float pitchScale = yawScale; + + camera.turn( + (float) (event.getChangeY() * pitchScale), + (float) (event.getChangeX() * yawScale) + ); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/graphics/world/WorldRenderer.java b/src/main/java/ru/windcorp/optica/client/graphics/world/WorldRenderer.java new file mode 100644 index 0000000..1733b4f --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/graphics/world/WorldRenderer.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.graphics.world; + +import glm.mat._4.Mat4; +import ru.windcorp.optica.common.util.StashingStack; + +public class WorldRenderer { + + private static final int TRANSFORM_STACK_SIZE = 64; + + private final StashingStack worldTransformStack = new StashingStack<>( + TRANSFORM_STACK_SIZE, Mat4::new + ); + + private final StashingStack viewTransformStack = new StashingStack<>( + TRANSFORM_STACK_SIZE, Mat4::new + ); + + private final Mat4 finalTransform = new Mat4(); + + { + reset(); + } + + public Mat4 pushWorldTransform() { + Mat4 previous = worldTransformStack.getHead(); + return worldTransformStack.push().set(previous); + } + + public void popWorldTransform() { + worldTransformStack.removeHead(); + } + + public Mat4 getWorldTransform() { + return worldTransformStack.getHead(); + } + + public Mat4 pushViewTransform() { + Mat4 previous = viewTransformStack.getHead(); + return viewTransformStack.push().set(previous); + } + + public void popViewTransform() { + viewTransformStack.removeHead(); + } + + public Mat4 getViewTransform() { + return viewTransformStack.getHead(); + } + + public Mat4 getFinalTransform() { + return finalTransform.set(getViewTransform()).mul(getWorldTransform()); + } + + public void reset() { + worldTransformStack.removeAll(); + worldTransformStack.push().identity(); + viewTransformStack.removeAll(); + viewTransformStack.push().identity(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/ChunkRender.java b/src/main/java/ru/windcorp/optica/client/world/ChunkRender.java new file mode 100644 index 0000000..7282de5 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/ChunkRender.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world; + +import java.util.HashMap; +import java.util.Map; + +import glm.mat._4.Mat4; +import ru.windcorp.optica.client.graphics.model.Model; +import ru.windcorp.optica.client.graphics.model.Shape; +import ru.windcorp.optica.client.graphics.model.StaticModel; +import ru.windcorp.optica.client.graphics.model.StaticModel.Builder; +import ru.windcorp.optica.client.graphics.model.WorldRenderable; +import ru.windcorp.optica.client.graphics.world.WorldRenderer; +import ru.windcorp.optica.client.world.renders.BlockRender; +import ru.windcorp.optica.client.world.renders.BlockRenderNone; +import ru.windcorp.optica.client.world.renders.BlockRenders; +import ru.windcorp.optica.client.world.renders.bro.BlockRenderOptimizer; +import ru.windcorp.optica.client.world.renders.bro.BlockRenderOptimizerGenerator; +import ru.windcorp.optica.client.world.renders.bro.BlockRenderOptimizerGenerators; +import ru.windcorp.optica.common.world.ChunkData; + +public class ChunkRender { + + private final WorldRender world; + private final ChunkData data; + + private Model model = null; + + public ChunkRender(WorldRender world, ChunkData data) { + this.world = world; + this.data = data; + } + + public WorldRender getWorld() { + return world; + } + + public ChunkData getData() { + return data; + } + + public BlockRender getBlock(int xInChunk, int yInChunk, int zInChunk) { + return BlockRenders.get( + getData().getBlock(xInChunk, yInChunk, zInChunk).getId() + ); + } + + public void render(WorldRenderer renderer) { + if (model == null) { + buildModel(); + } + + renderer.pushWorldTransform().translate( + data.getX() * ChunkData.BLOCKS_PER_CHUNK, + data.getY() * ChunkData.BLOCKS_PER_CHUNK, + data.getZ() * ChunkData.BLOCKS_PER_CHUNK + ); + + model.render(renderer); + + renderer.popWorldTransform(); + } + + private void buildModel() { + Map optimizers = new HashMap<>(); + + for ( + BlockRenderOptimizerGenerator generator : + BlockRenderOptimizerGenerators.getAll() + ) { + BlockRenderOptimizer optimizer = generator.createOptimizer(); + optimizers.put(generator.getId(), optimizer); + optimizer.startRender(this); + } + + StaticModel.Builder builder = StaticModel.builder(); + + for (int x = 0; x < ChunkData.BLOCKS_PER_CHUNK; ++x) { + for (int y = 0; y < ChunkData.BLOCKS_PER_CHUNK; ++y) { + for (int z = 0; z < ChunkData.BLOCKS_PER_CHUNK; ++z) { + + BlockRender block = getBlock(x, y, z); + + if (block instanceof BlockRenderNone) { + continue; + } + + if (tryToForwardToOptimizers(block, x, y, z, optimizers)) { + continue; + } + + if (tryToCreateRenderable(block, x, y, z, builder)) { + continue; + } + + addRenderAsRenderable(block, x, y, z, builder); + } + } + } + + for (BlockRenderOptimizer optimizer : optimizers.values()) { + Shape result = optimizer.endRender(); + if (result != null) { + builder.addPart(result); + } + } + + model = new StaticModel(builder); + } + + private boolean tryToForwardToOptimizers( + BlockRender block, int x, int y, int z, + Map optimizers + ) { + if (!block.isOptimized()) { + return false; + } + BlockRenderOptimizer optimizer = optimizers.get(block.getOptimizer()); + + if (optimizer == null) { + return false; + } + + optimizer.processBlock(block, x, y, z); + + return true; + } + + private boolean tryToCreateRenderable( + BlockRender block, int x, int y, int z, + Builder builder + ) { + WorldRenderable renderable = block.createRenderable(); + + if (renderable == null) { + return false; + } + + builder.addPart(renderable, new Mat4().identity().translate(x, y, z)); + return true; + } + + private void addRenderAsRenderable( + BlockRender block, int x, int y, int z, + Builder builder + ) { + builder.addPart( + block::render, + new Mat4().identity().translate(x, y, z) + ); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/WorldRender.java b/src/main/java/ru/windcorp/optica/client/world/WorldRender.java new file mode 100644 index 0000000..505c057 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/WorldRender.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world; + +import java.util.ArrayList; +import java.util.Collection; + +import ru.windcorp.optica.client.graphics.world.WorldRenderer; +import ru.windcorp.optica.common.world.ChunkData; +import ru.windcorp.optica.common.world.WorldData; + +public class WorldRender { + + private final WorldData data; + + private final Collection chunks = new ArrayList<>(); + + public WorldRender(WorldData data) { + this.data = data; + + for (ChunkData chunkData : data.getChunks()) { + chunks.add(new ChunkRender(this, chunkData)); + } + } + + public WorldData getData() { + return data; + } + + public void render(WorldRenderer renderer) { + renderer.pushWorldTransform().rotateX(-Math.PI / 2); + + for (ChunkRender chunk : chunks) { + chunk.render(renderer); + } + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/BlockRender.java b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRender.java new file mode 100644 index 0000000..ca5731e --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRender.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders; + +import ru.windcorp.optica.client.graphics.model.WorldRenderable; +import ru.windcorp.optica.client.graphics.world.WorldRenderer; +import ru.windcorp.optica.common.util.Namespaced; + +public abstract class BlockRender extends Namespaced { + + private String optimizer = null; + + public BlockRender(String namespace, String name) { + super(namespace, name); + } + + public String getOptimizer() { + return optimizer; + } + + public boolean isOptimized() { + return getOptimizer() != null; + } + + public void setOptimizer(String optimizer) { + this.optimizer = optimizer; + } + + public void render(WorldRenderer renderer) { + throw new UnsupportedOperationException( + "BlockRender.render() not implemented" + ); + } + + public WorldRenderable createRenderable() { + return null; + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderNone.java b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderNone.java new file mode 100644 index 0000000..2111d4c --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderNone.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders; + +import ru.windcorp.optica.client.graphics.model.EmptyModel; +import ru.windcorp.optica.client.graphics.model.WorldRenderable; + +public class BlockRenderNone extends BlockRender { + + public BlockRenderNone(String namespace, String name) { + super(namespace, name); + } + + @Override + public WorldRenderable createRenderable() { + return EmptyModel.getInstance(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderTexturedCube.java b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderTexturedCube.java new file mode 100644 index 0000000..f555c88 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderTexturedCube.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders; + +import static ru.windcorp.optica.common.block.BlockFace.*; + +import java.util.EnumMap; + +import ru.windcorp.optica.client.graphics.model.Shapes; +import ru.windcorp.optica.client.graphics.model.WorldRenderable; +import ru.windcorp.optica.client.graphics.texture.Texture; +import ru.windcorp.optica.common.block.BlockFace; + +public abstract class BlockRenderTexturedCube extends BlockRender { + + private final EnumMap textures = + new EnumMap<>(BlockFace.class); + + public BlockRenderTexturedCube( + String namespace, String name, + Texture topTexture, Texture bottomTexture, + Texture northTexture, Texture southTexture, + Texture eastTexture, Texture westTexture + ) { + super(namespace, name); + + textures.put(TOP, topTexture); + textures.put(BOTTOM, bottomTexture); + textures.put(NORTH, northTexture); + textures.put(SOUTH, southTexture); + textures.put(EAST, eastTexture); + textures.put(WEST, westTexture); + } + + public Texture getTexture(BlockFace face) { + return textures.get(face); + } + + @Override + public WorldRenderable createRenderable() { + return new Shapes.PppBuilder( + getTexture(TOP), getTexture(BOTTOM), + getTexture(NORTH), getTexture(SOUTH), + getTexture(EAST), getTexture(WEST) + ).create(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderTransparentCube.java b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderTransparentCube.java new file mode 100644 index 0000000..efca0ec --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenderTransparentCube.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders; + +import ru.windcorp.optica.client.graphics.texture.Texture; + +public class BlockRenderTransparentCube extends BlockRenderTexturedCube { + + public BlockRenderTransparentCube( + String namespace, String name, + Texture topTexture, Texture bottomTexture, + Texture northTexture, Texture southTexture, + Texture eastTexture, Texture westTexture + ) { + super( + namespace, name, + topTexture, bottomTexture, + northTexture, southTexture, + eastTexture, westTexture + ); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenders.java b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenders.java new file mode 100644 index 0000000..75be839 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/BlockRenders.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders; + +import java.util.HashMap; +import java.util.Map; + +import ru.windcorp.optica.client.graphics.texture.SimpleTexture; +import ru.windcorp.optica.client.graphics.texture.Sprite; +import ru.windcorp.optica.client.graphics.texture.Texture; +import ru.windcorp.optica.client.graphics.texture.TextureManager; +import ru.windcorp.optica.client.world.renders.bro.BlockRenderOpaqueCube; + +public class BlockRenders { + + private static Texture grassTop = qtex("grass_top"); + private static Texture grassSide = qtex("grass_side"); + private static Texture dirtT = qtex("grass_bottom"); + private static Texture stoneT = qtex("stone"); + private static Texture glassT = qtex("glass_clear"); + + private static final Map BLOCK_RENDERS = + new HashMap<>(); + + private BlockRenders() {} + + static { + register(new BlockRenderOpaqueCube("Grass", "Test", grassTop, dirtT, grassSide, grassSide, grassSide, grassSide)); + register(new BlockRenderOpaqueCube("Dirt", "Test", dirtT, dirtT, dirtT, dirtT, dirtT, dirtT)); + register(new BlockRenderOpaqueCube("Stone", "Test", stoneT, stoneT, stoneT, stoneT, stoneT, stoneT)); + + register(new BlockRenderOpaqueCube("Compass", "Test", qtex("compass"), qtex("compass"), qtex("side_north"), qtex("side_south"), qtex("side_east"), qtex("side_west"))); + + register(new BlockRenderNone("Air", "Test")); + register(new BlockRenderTransparentCube("Glass", "Test", glassT, glassT, glassT, glassT, glassT, glassT)); + } + + public static BlockRender get(String name) { + return BLOCK_RENDERS.get(name); + } + + public static void register(BlockRender blockRender) { + BLOCK_RENDERS.put(blockRender.getId(), blockRender); + } + + private static Texture qtex(String name) { + return new SimpleTexture(new Sprite(TextureManager.load(name, false))); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOpaqueCube.java b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOpaqueCube.java new file mode 100644 index 0000000..2342a9c --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOpaqueCube.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders.bro; + +import ru.windcorp.optica.client.graphics.texture.Texture; +import ru.windcorp.optica.client.world.renders.BlockRenderTexturedCube; + +public class BlockRenderOpaqueCube extends BlockRenderTexturedCube { + + public BlockRenderOpaqueCube( + String namespace, String name, + Texture topTexture, Texture bottomTexture, + Texture northTexture, Texture southTexture, + Texture eastTexture, Texture westTexture + ) { + super( + namespace, name, + topTexture, bottomTexture, + northTexture, southTexture, + eastTexture, westTexture + ); + setOptimizer("Default:OpaqueCube"); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOpaqueCubeOptimizer.java b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOpaqueCubeOptimizer.java new file mode 100644 index 0000000..5450456 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOpaqueCubeOptimizer.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders.bro; + +import static ru.windcorp.optica.common.world.ChunkData.BLOCKS_PER_CHUNK; + +import java.util.ArrayList; +import java.util.Collection; + +import glm.vec._3.Vec3; +import ru.windcorp.optica.client.graphics.backend.Usage; +import ru.windcorp.optica.client.graphics.model.Face; +import ru.windcorp.optica.client.graphics.model.Faces; +import ru.windcorp.optica.client.graphics.model.Shape; +import ru.windcorp.optica.client.graphics.texture.Texture; +import ru.windcorp.optica.client.world.ChunkRender; +import ru.windcorp.optica.client.world.renders.BlockRender; +import ru.windcorp.optica.common.block.BlockFace; +import ru.windcorp.optica.common.world.ChunkData; + +public class BlockRenderOpaqueCubeOptimizer extends BlockRenderOptimizer { + + private static final int BLOCK_MASK = 1 << 7; + + private static final BlockFace[] GOOD_FACES = new BlockFace[] { + BlockFace.TOP, BlockFace.NORTH, BlockFace.WEST + }; + + private static final Vec3 COLOR_MULTIPLIER = new Vec3(1, 1, 1); + + private final byte[][][] data = new byte[BLOCKS_PER_CHUNK + 1] + [BLOCKS_PER_CHUNK + 1] + [BLOCKS_PER_CHUNK + 1]; + + private ChunkRender chunk; + + private final Vec3 blockCenter = new Vec3(); + + @Override + public void startRender(ChunkRender chunk) { + this.chunk = chunk; + } + + @Override + public void processBlock(BlockRender block, int x, int y, int z) { + addFace(x, y, z, BlockFace.TOP); + addFace(x, y, z, BlockFace.BOTTOM); + addFace(x, y, z, BlockFace.NORTH); + addFace(x, y, z, BlockFace.SOUTH); + addFace(x, y, z, BlockFace.EAST); + addFace(x, y, z, BlockFace.WEST); + addBlock(x, y, z); + } + + protected void addFace(int x, int y, int z, BlockFace face) { + switch (face) { + case BOTTOM: + z -= 1; + face = BlockFace.TOP; + break; + case SOUTH: + x -= 1; + face = BlockFace.NORTH; + break; + case EAST: + y -= 1; + face = BlockFace.WEST; + break; + default: + } + + data[x + 1][y + 1][z + 1] ^= 1 << face.ordinal(); + } + + protected void addBlock(int x, int y, int z) { + data[x + 1][y + 1][z + 1] |= BLOCK_MASK; + } + + protected boolean hasFace(int x, int y, int z, BlockFace face) { + switch (face) { + case BOTTOM: + z -= 1; + face = BlockFace.TOP; + break; + case SOUTH: + x -= 1; + face = BlockFace.NORTH; + break; + case EAST: + y -= 1; + face = BlockFace.WEST; + break; + default: + } + + return (data[x + 1][y + 1][z + 1] & 1 << face.ordinal()) != 0; + } + + protected boolean hasBlock(int x, int y, int z) { + return (data[x + 1][y + 1][z + 1] & BLOCK_MASK) != 0; + } + + @Override + public Shape endRender() { + + Collection shapeFaces = new ArrayList<>( + BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3 + + BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3 + ); + + for (int x = -1; x < ChunkData.BLOCKS_PER_CHUNK; ++x) { + for (int y = -1; y < ChunkData.BLOCKS_PER_CHUNK; ++y) { + for (int z = -1; z < ChunkData.BLOCKS_PER_CHUNK; ++z) { + for (BlockFace face : GOOD_FACES) { + + if (!hasFace(x, y, z, face)) continue; + + Face shapeFace = null; + + if (!hasBlock(x, y, z)) { + switch (face) { + case TOP: + shapeFace = createFace( + x, y, z + 1, + BlockFace.BOTTOM + ); + break; + case NORTH: + shapeFace = createFace( + x + 1, y, z, + BlockFace.SOUTH + ); + break; + case WEST: + shapeFace = createFace( + x, y + 1, z, + BlockFace.EAST + ); + break; + default: + } + } else { + shapeFace = createFace(x, y, z, face); + } + + shapeFaces.add(shapeFace); + + } + } + } + } + + return new Shape( + Usage.STATIC, + shapeFaces.toArray(new Face[shapeFaces.size()]) + ); + } + + private Face createFace(int x, int y, int z, BlockFace face) { + BlockRenderOpaqueCube blockRender = + (BlockRenderOpaqueCube) chunk.getBlock(x, y, z); + Texture texture = blockRender.getTexture(face); + + return Faces.createBlockFace( + texture, + COLOR_MULTIPLIER, + blockCenter.set(x, y, z), + face + ); + } + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizer.java b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizer.java new file mode 100644 index 0000000..33a6f8f --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizer.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders.bro; + +import ru.windcorp.optica.client.graphics.model.Shape; +import ru.windcorp.optica.client.world.ChunkRender; +import ru.windcorp.optica.client.world.renders.BlockRender; + +public abstract class BlockRenderOptimizer { + + public abstract void startRender(ChunkRender chunk); + + public abstract void processBlock(BlockRender block, int x, int y, int z); + + public abstract Shape endRender(); + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizerGenerator.java b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizerGenerator.java new file mode 100644 index 0000000..04cd326 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizerGenerator.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders.bro; + +import ru.windcorp.optica.common.util.Namespaced; + +public abstract class BlockRenderOptimizerGenerator extends Namespaced { + + public BlockRenderOptimizerGenerator(String namespace, String name) { + super(namespace, name); + } + + public abstract BlockRenderOptimizer createOptimizer(); + +} diff --git a/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizerGenerators.java b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizerGenerators.java new file mode 100644 index 0000000..a72db7f --- /dev/null +++ b/src/main/java/ru/windcorp/optica/client/world/renders/bro/BlockRenderOptimizerGenerators.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.client.world.renders.bro; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class BlockRenderOptimizerGenerators { + + private BlockRenderOptimizerGenerators() {} + + private static final Map GENERATORS = + new HashMap<>(); + + static { + register(new BlockRenderOptimizerGenerator("Default", "OpaqueCube") { + @Override + public BlockRenderOptimizer createOptimizer() { + return new BlockRenderOpaqueCubeOptimizer(); + } + }); + } + + public static BlockRenderOptimizerGenerator get(String id) { + return GENERATORS.get(id); + } + + public static void register(BlockRenderOptimizerGenerator generator) { + GENERATORS.put(generator.getId(), generator); + } + + public static Collection getAll() { + return GENERATORS.values(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/block/BlockData.java b/src/main/java/ru/windcorp/optica/common/block/BlockData.java new file mode 100644 index 0000000..84b56a0 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/block/BlockData.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.block; + +import ru.windcorp.optica.common.util.Namespaced; + +public class BlockData extends Namespaced { + + public BlockData(String namespace, String name) { + super(namespace, name); + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/block/BlockDataRegistry.java b/src/main/java/ru/windcorp/optica/common/block/BlockDataRegistry.java new file mode 100644 index 0000000..0363fc7 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/block/BlockDataRegistry.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.block; + +import java.util.HashMap; +import java.util.Map; + +public class BlockDataRegistry { + + private static final Map REGISTRY = new HashMap<>(); + + public static BlockData get(String name) { + return REGISTRY.get(name); + } + + public static void register(BlockData blockData) { + REGISTRY.put(blockData.getId(), blockData); + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/block/BlockFace.java b/src/main/java/ru/windcorp/optica/common/block/BlockFace.java new file mode 100644 index 0000000..93873f5 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/block/BlockFace.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.block; + +public enum BlockFace { + + TOP, BOTTOM, NORTH, SOUTH, EAST, WEST; + +} diff --git a/src/main/java/ru/windcorp/optica/common/resource/Resource.java b/src/main/java/ru/windcorp/optica/common/resource/Resource.java new file mode 100644 index 0000000..83168d3 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/resource/Resource.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.resource; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import com.google.common.io.CharStreams; + +import ru.windcorp.optica.Optica; + +public class Resource { + + private final String name; + + public Resource(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public InputStream getInputStream() { + // TODO Do proper resource lookup + return Optica.class.getClassLoader().getResourceAsStream(name); + } + + public Reader getReader() { + return new InputStreamReader(getInputStream()); + } + + public String readAsString() { + try (Reader reader = getReader()) { + return CharStreams.toString(reader); + } catch (IOException e) { + throw new RuntimeException(e); // TODO handle gracefully + } + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/resource/ResourceManager.java b/src/main/java/ru/windcorp/optica/common/resource/ResourceManager.java new file mode 100644 index 0000000..1751549 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/resource/ResourceManager.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.resource; + +public class ResourceManager { + + public static Resource getResource(String name) { + return new Resource(name); + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/util/CoordinatePacker.java b/src/main/java/ru/windcorp/optica/common/util/CoordinatePacker.java new file mode 100644 index 0000000..5d2f297 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/util/CoordinatePacker.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.util; + +public class CoordinatePacker { + + private static final int BITS_3_INTS_INTO_LONG; + private static final long MASK_3_INTS_INTO_LONG; + + static { + BITS_3_INTS_INTO_LONG = 64 / 3; + + /* + * What happens below: + * + * 1. 1 << BITS_3_INTS_INTO_LONG: + * 0000 ... 00100 ... 0000 + * \_________/ - BITS_3_INTS_INTO_LONG zeros + * + * 2. (1 << BITS_3_INTS_INTO_LONG) - 1: + * 0000 ... 00011 ... 1111 + * \_________/ - BITS_3_INTS_INTO_LONG ones - WIN + */ + + MASK_3_INTS_INTO_LONG = (1 << BITS_3_INTS_INTO_LONG) - 1; + } + + public static long pack3IntsIntoLong(int a, int b, int c) { + return + ((a & MASK_3_INTS_INTO_LONG) << (2 * BITS_3_INTS_INTO_LONG)) | + ((b & MASK_3_INTS_INTO_LONG) << (1 * BITS_3_INTS_INTO_LONG)) | + ((c & MASK_3_INTS_INTO_LONG) << (0 * BITS_3_INTS_INTO_LONG)); + } + + public static int unpack3IntsFromLong(long packed, int index) { + if (index < 0 || index >= 3) { + throw new IllegalArgumentException("Invalid index " + index); + } + + int result = (int) ( + (packed >>> ((2 - index) * BITS_3_INTS_INTO_LONG)) + & MASK_3_INTS_INTO_LONG + ); + + final long signMask = ((MASK_3_INTS_INTO_LONG + 1) >> 1); + + if ((result & signMask) != 0) { + result |= ~MASK_3_INTS_INTO_LONG; + } + + return result; + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/util/Named.java b/src/main/java/ru/windcorp/optica/common/util/Named.java new file mode 100644 index 0000000..c008838 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/util/Named.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.util; + +import java.util.Objects; + +public abstract class Named { + + private final String name; + + public Named(String name) { + this.name = Objects.requireNonNull(name, "name"); + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " " + getName(); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Named other = (Named) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/util/Namespaced.java b/src/main/java/ru/windcorp/optica/common/util/Namespaced.java new file mode 100644 index 0000000..c54a2a6 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/util/Namespaced.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.util; + +import java.util.Objects; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +public abstract class Namespaced extends Named { + + private static final char SEPARATOR = ':'; + + private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$"; + + private static final Predicate PART_CHECKER = + Pattern.compile(PART_REGEX).asPredicate(); + + private final String namespace; + private final String id; + + public Namespaced(String namespace, String name) { + super(name); + this.namespace = Objects.requireNonNull(namespace, "namespace"); + this.id = namespace + SEPARATOR + name; + + if (!PART_CHECKER.test(name)) { + throw new IllegalArgumentException( + "Name \"" + name + "\" is invalid. " + + "Allowed is: " + PART_REGEX + ); + } + + if (!PART_CHECKER.test(namespace)) { + throw new IllegalArgumentException( + "Namespace \"" + namespace + "\" is invalid. " + + "Allowed is: " + PART_REGEX + ); + } + } + + public String getId() { + return id; + } + + public String getNamespace() { + return namespace; + } + + @Override + public String toString() { + return getId(); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Namespaced other = (Namespaced) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/util/StashingStack.java b/src/main/java/ru/windcorp/optica/common/util/StashingStack.java new file mode 100644 index 0000000..27acbd6 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/util/StashingStack.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.util; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.function.Supplier; + +import com.google.common.collect.Iterables; + +/** + * A low-overhead, fixed-capacity stack that does not dispose of popped elements + * but rather stashes them for later pushing. This allows the stack to + * operate without creating new objects. + *

+ * This object always contains references to {@link #getCapacity()} elements, of + * which first {@link #getSize()} elements are present in the stack proper and + * accessable, and the rest are stashed. When an element is popped, is + * becomes stashed. When an element is pushed, it ceases to be stashed. + *

+ * Stashed elements can be replaced with {@link #push(Object)}. + * + * @author Javapony + */ +@SuppressWarnings("unchecked") +public class StashingStack implements Iterable { + + /** + * Stores all elements. Elements with indices + * [0; {@link #head}] + * are present in the stack, elements with indices + * ({@link #head}; contents.length] are stashed. + */ + private final Object[] contents; + + private transient List contentsAsList; + + /** + * Index of the head of the stack in the {@link #contents} array, or + * -1, if the stack is empty. + */ + private int head = -1; + + protected StashingStack(Object[] stash, int dummy) { + this.contents = stash; + } + + /** + * Creates a new stack. Its stash is filled with {@code null}s. + * @param capacity stack's capacity + */ + public StashingStack(int capacity) { + this((T[]) new Object[capacity], 0); + } + + /** + * Creates a new stack with the supplied stash. + * @param contents elements that are put in the stash initially. + */ + public StashingStack(T[] contents) { + this(contents.clone(), 0); + } + + /** + * Creates a new stack with the supplied stash. + * @param contents elements that are put in the stash initially. + */ + public StashingStack(Iterable contents) { + this(Iterables.toArray(contents, Object.class), 0); + } + + /** + * Creates a new stack. Its stash is filled with objects provided by + * {@code generator}. The generator's {@link Supplier#get() get()} method + * will only be invoked {@code capacity} times from within this constructor. + * @param capacity stack's capacity + * @param generator a supplier of objects for the stash + */ + public StashingStack(int capacity, Supplier generator) { + this(capacity); + + for (int i = 0; i < contents.length; ++i) { + contents[i] = generator.get(); + } + } + /** + * Returns the amount of elements this stack can store. + * @return the capacity + */ + public int getCapacity() { + return contents.length; + } + + /** + * Returns the amount of elements that are currently in the stack. + * @return the size + */ + public int getSize() { + return head + 1; + } + + /** + * Checks whether this stack does not contain any elements. + * @return {@code true} is this stack is empty + */ + public boolean isEmpty() { + return getSize() == 0; + } + + /** + * Checks whether this stack is full. + * @return {@code true} is this stack is full + */ + public boolean isFull() { + return getSize() == getCapacity(); + } + + /** + * Returns, but does not remove, the head of this stack. If the stack is + * empty returns {@code null}. + * @return head of this stack or {@code null} + * @see #getHead() + */ + public T peek() { + if (head < 0) return null; + return (T) contents[head]; + } + + /** + * Returns, but does not remove, the head of this stack. If the stack is + * empty throws a {@link NoSuchElementException}. + * @return head of this stack + * @throws NoSuchElementException is the stack is empty + * @see #peek() + */ + public T getHead() { + if (head < 0) throw new NoSuchElementException(); + return (T) contents[head]; + } + + /** + * Returns and removes the head of this stack. If the stack is + * empty returns {@code null}. + * @return head of this stack or {@code null} + * @see #removeHead() + */ + public T pop() { + if (head < 0) return null; + return (T) contents[head--]; + } + + /** + * Returns and removes the head of this stack. If the stack is + * empty throws a {@link NoSuchElementException}. + * @return head of this stack + * @throws NoSuchElementException is the stack is empty + * @see #pop() + */ + public T removeHead() { + if (head < 0) throw new NoSuchElementException(); + return (T) contents[head--]; + } + + /** + * Pushes a new element from the stash onto the stack. If the stack is + * already full throws an {@link IllegalStateException}. The state of the + * new element is not specified. + * @return the new head + */ + public T push() { + if (head == contents.length - 1) { + throw new IllegalStateException(); + } + + return (T) contents[++head]; + } + + /** + * Pushes the specified element onto the stack. A stashed element is + * removed. If the stack is already full throws an + * {@link IllegalStateException}. + * @param newElement the element to push + * @return the new head + */ + public T push(T newElement) { + if (head == contents.length - 1) { + throw new IllegalStateException(); + } + + contents[++head] = newElement; + return newElement; + } + + /** + * Removes all elements from the stack. + */ + public void removeAll() { + head = -1; + } + + @Override + public Iterator iterator() { + if (contentsAsList == null) { + contentsAsList = Arrays.asList((T[]) contents); + } + + return contentsAsList.subList(0, getSize()).iterator(); + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/util/ThrowingRunnable.java b/src/main/java/ru/windcorp/optica/common/util/ThrowingRunnable.java new file mode 100644 index 0000000..5602d0d --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/util/ThrowingRunnable.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.util; + +import java.util.function.Consumer; + +import com.google.common.base.Throwables; + +@FunctionalInterface +public interface ThrowingRunnable { + + void run() throws T; + + default Runnable withCatcher( + Consumer catcher, + Class throwableClass + ) { + return () -> { + + try { + ThrowingRunnable.this.run(); + } catch (Throwable t) { + if (t.getClass() == throwableClass) { + catcher.accept(throwableClass.cast(t)); + } + + Throwables.throwIfUnchecked(t); + + // This should never happen + throw new AssertionError("This should not have been thrown", t); + } + + }; + } + + default Runnable withCatcher( + Consumer catcher + ) { + return () -> { + try { + ThrowingRunnable.this.run(); + } catch (Throwable t) { + catcher.accept(t); + } + }; + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/world/ChunkData.java b/src/main/java/ru/windcorp/optica/common/world/ChunkData.java new file mode 100644 index 0000000..ca5f363 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/world/ChunkData.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.world; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.optica.common.block.BlockData; + +public class ChunkData { + + public static final int BLOCKS_PER_CHUNK = 16; + + private final int x; + private final int y; + private final int z; + + private final BlockData[][][] blocks = new BlockData[BLOCKS_PER_CHUNK] + [BLOCKS_PER_CHUNK] + [BLOCKS_PER_CHUNK]; + + private final BlockData grass = new BlockData("Grass", "Test"); + private final BlockData dirt = new BlockData("Dirt", "Test"); + private final BlockData stone = new BlockData("Stone", "Test"); + private final BlockData air = new BlockData("Air", "Test"); +// private final BlockData glass = new BlockData("Glass", "Test"); +// private final BlockData compass = new BlockData("Compass", "Test"); + + public ChunkData(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + + tmp_generate(); + } + + private void tmp_generate() { + Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2); + Vec3i pos = new Vec3i(); + + for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { + for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) { + for (int z = 0; z < BLOCKS_PER_CHUNK; ++z) { + + pos.set(x, y, z); + + float f = aPoint.sub(pos, pos).length(); + + if (f > 17) { + blocks[x][y][z] = stone; + } else if (f > 14) { + blocks[x][y][z] = dirt; + } else { + blocks[x][y][z] = air; + } + + } + } + } + + for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { + for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) { + int z; + for (z = BLOCKS_PER_CHUNK - 1; z >= 0 && blocks[x][y][z] == air; --z); + + blocks[x][y][z] = grass; + } + } + } + + public BlockData getBlock(int xInChunk, int yInChunk, int zInChunk) { + if (!isInBounds(xInChunk, yInChunk, zInChunk)) { + throw new IllegalArgumentException( + "Coordinates (" + x + "; " + y + "; " + z + ") " + + "are not legal chunk coordinates" + ); + } + + return blocks[xInChunk][yInChunk][zInChunk]; + } + + private boolean isInBounds(int xInChunk, int yInChunk, int zInChunk) { + return + xInChunk >= 0 && xInChunk < BLOCKS_PER_CHUNK || + yInChunk >= 0 && yInChunk < BLOCKS_PER_CHUNK || + zInChunk >= 0 && zInChunk < BLOCKS_PER_CHUNK; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + +} diff --git a/src/main/java/ru/windcorp/optica/common/world/WorldData.java b/src/main/java/ru/windcorp/optica/common/world/WorldData.java new file mode 100644 index 0000000..3db4455 --- /dev/null +++ b/src/main/java/ru/windcorp/optica/common/world/WorldData.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Optica + * Copyright (C) 2020 Wind Corporation + * + * 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 . + *******************************************************************************/ +package ru.windcorp.optica.common.world; + +import java.util.Collection; +import java.util.Collections; + +import gnu.trove.map.TLongObjectMap; +import gnu.trove.map.hash.TLongObjectHashMap; +import ru.windcorp.optica.common.util.CoordinatePacker; + +public class WorldData { + + private final TLongObjectMap chunks = new TLongObjectHashMap<>(); + + public WorldData() { + final int size = 1; + + for (int x = -(size / 2); x <= (size / 2); ++x) { + for (int y = -(size / 2); y <= (size / 2); ++y) { + chunks.put(CoordinatePacker.pack3IntsIntoLong(x, y, 0), new ChunkData(x, y, 0)); + } + } + } + + public ChunkData getChunk(int x, int y, int z) { + long key = CoordinatePacker.pack3IntsIntoLong(x, y, z); + return chunks.get(key); + } + + public Collection getChunks() { + return Collections.unmodifiableCollection(chunks.valueCollection()); + } + +} diff --git a/src/main/resources/assets/shaders/Shape.fragment.glsl b/src/main/resources/assets/shaders/Shape.fragment.glsl new file mode 100644 index 0000000..360358d --- /dev/null +++ b/src/main/resources/assets/shaders/Shape.fragment.glsl @@ -0,0 +1,53 @@ +#version 120 + +varying vec3 varyingColorMultiplier; +varying vec2 varyingTextureCoords; +varying vec3 varyingNormals; + +uniform sampler2D textureSlot; +uniform vec2 textureStart; +uniform vec2 textureSize; + +void applyTexture() { + gl_FragColor = texture2D( + textureSlot, + vec2( + varyingTextureCoords[0] * textureSize[0] + textureStart[0], + varyingTextureCoords[1] * textureSize[1] + textureStart[1] + ) + ); +} + +void multiply(inout vec4 vector, float scalar) { + vector.x *= scalar; + vector.y *= scalar; + vector.z *= scalar; + vector.w *= scalar; +} + +void linearMultiply(inout vec4 vector, vec4 scalars) { + vector.x *= scalars.x; + vector.y *= scalars.y; + vector.z *= scalars.z; + vector.w *= scalars.w; +} + +void applyColorMultiplier() { + linearMultiply(gl_FragColor, vec4(varyingColorMultiplier, 1.0)); +} + +void applyShading() { + vec3 light = normalize(vec3(0.5, 1.0, 0.2)); + vec3 normal = varyingNormals; + + float angleCos = dot(normal, light); + float lightness = (angleCos + 1.5) / 2; + + linearMultiply(gl_FragColor, vec4(lightness.xxx, 1.0)); +} + +void applyAlpha() { + if (gl_FragColor.w < 0.01) { + discard; + } +} \ No newline at end of file diff --git a/src/main/resources/assets/shaders/Shape.vertex.glsl b/src/main/resources/assets/shaders/Shape.vertex.glsl new file mode 100644 index 0000000..3ce95ff --- /dev/null +++ b/src/main/resources/assets/shaders/Shape.vertex.glsl @@ -0,0 +1,27 @@ +#version 120 + +attribute vec3 inputPositions; + +attribute vec3 inputColorMultiplier; +varying vec3 varyingColorMultiplier; + +attribute vec2 inputTextureCoords; +varying vec2 varyingTextureCoords; + +attribute vec3 inputNormals; +varying vec3 varyingNormals; + +uniform mat4 worldTransform; +uniform mat4 finalTransform; + +vec4 applyFinalTransform(vec4 vector) { + return finalTransform * vector; +} + +void transferToFragment() { + varyingColorMultiplier = inputColorMultiplier; + varyingTextureCoords = inputTextureCoords; + + mat3 worldRotation = mat3(worldTransform); + varyingNormals = normalize(worldRotation * inputNormals); +} \ No newline at end of file diff --git a/src/main/resources/assets/shaders/WorldDefault.fragment.glsl b/src/main/resources/assets/shaders/WorldDefault.fragment.glsl new file mode 100644 index 0000000..94be7fd --- /dev/null +++ b/src/main/resources/assets/shaders/WorldDefault.fragment.glsl @@ -0,0 +1,8 @@ +#version 120 + +void main(void) { + applyTexture(); + applyColorMultiplier(); + applyShading(); + applyAlpha(); +} \ No newline at end of file diff --git a/src/main/resources/assets/shaders/WorldDefault.vertex.glsl b/src/main/resources/assets/shaders/WorldDefault.vertex.glsl new file mode 100644 index 0000000..11faad1 --- /dev/null +++ b/src/main/resources/assets/shaders/WorldDefault.vertex.glsl @@ -0,0 +1,6 @@ +#version 120 + +void main(void) { + gl_Position = applyFinalTransform(vec4(inputPositions, 1.0)); + transferToFragment(); +} \ No newline at end of file diff --git a/src/main/resources/assets/textures/compass.png b/src/main/resources/assets/textures/compass.png new file mode 100644 index 0000000..642b73a Binary files /dev/null and b/src/main/resources/assets/textures/compass.png differ diff --git a/src/main/resources/assets/textures/glass.png b/src/main/resources/assets/textures/glass.png new file mode 100644 index 0000000..b7de889 Binary files /dev/null and b/src/main/resources/assets/textures/glass.png differ diff --git a/src/main/resources/assets/textures/glass_clear.png b/src/main/resources/assets/textures/glass_clear.png new file mode 100644 index 0000000..c48cc37 Binary files /dev/null and b/src/main/resources/assets/textures/glass_clear.png differ diff --git a/src/main/resources/assets/textures/grass_bottom.png b/src/main/resources/assets/textures/grass_bottom.png new file mode 100644 index 0000000..6be30d6 Binary files /dev/null and b/src/main/resources/assets/textures/grass_bottom.png differ diff --git a/src/main/resources/assets/textures/grass_side.png b/src/main/resources/assets/textures/grass_side.png new file mode 100644 index 0000000..04f01b9 Binary files /dev/null and b/src/main/resources/assets/textures/grass_side.png differ diff --git a/src/main/resources/assets/textures/grass_top.png b/src/main/resources/assets/textures/grass_top.png new file mode 100644 index 0000000..e2c1c73 Binary files /dev/null and b/src/main/resources/assets/textures/grass_top.png differ diff --git a/src/main/resources/assets/textures/side_east.png b/src/main/resources/assets/textures/side_east.png new file mode 100644 index 0000000..696e93a Binary files /dev/null and b/src/main/resources/assets/textures/side_east.png differ diff --git a/src/main/resources/assets/textures/side_north.png b/src/main/resources/assets/textures/side_north.png new file mode 100644 index 0000000..441d376 Binary files /dev/null and b/src/main/resources/assets/textures/side_north.png differ diff --git a/src/main/resources/assets/textures/side_south.png b/src/main/resources/assets/textures/side_south.png new file mode 100644 index 0000000..9a1e71b Binary files /dev/null and b/src/main/resources/assets/textures/side_south.png differ diff --git a/src/main/resources/assets/textures/side_west.png b/src/main/resources/assets/textures/side_west.png new file mode 100644 index 0000000..05c2695 Binary files /dev/null and b/src/main/resources/assets/textures/side_west.png differ diff --git a/src/main/resources/assets/textures/stone.png b/src/main/resources/assets/textures/stone.png new file mode 100644 index 0000000..cd8cb59 Binary files /dev/null and b/src/main/resources/assets/textures/stone.png differ diff --git a/src/test/java/ru/windcorp/optica/util/CoordinatePackerTest.java b/src/test/java/ru/windcorp/optica/util/CoordinatePackerTest.java new file mode 100644 index 0000000..0c74ec3 --- /dev/null +++ b/src/test/java/ru/windcorp/optica/util/CoordinatePackerTest.java @@ -0,0 +1,54 @@ +package ru.windcorp.optica.util; + +import static org.junit.Assert.assertEquals; + +import java.util.Random; + +import org.junit.Test; + +import ru.windcorp.optica.common.util.CoordinatePacker; + +public class CoordinatePackerTest { + + @Test + public void cornerCases() { + check(0, 0, 0); + check(0, 0, 42); + check(0, 42, 0); + check(42, 0, 0); + check(1, 1, 1); + check(-1, -1, -1); + check(1 << 19, 1 << 19, 1 << 19); + check((1 << 20) - 1, (1 << 20) - 1, (1 << 20) - 1); + check(-(1 << 19), -(1 << 19), -(1 << 19)); + } + + @Test + public void randomValues() { + Random random = new Random(0); + int bound = 1 << 20; + + for (int i = 0; i < 1000000; ++i) { + check( + random.nextInt(bound) * (random.nextBoolean() ? 1 : -1), + random.nextInt(bound) * (random.nextBoolean() ? 1 : -1), + random.nextInt(bound) * (random.nextBoolean() ? 1 : -1) + ); + } + } + + private void check(int a, int b, int c) { + + long packed = CoordinatePacker.pack3IntsIntoLong(a, b, c); + + int unpackedA = CoordinatePacker.unpack3IntsFromLong(packed, 0); + int unpackedB = CoordinatePacker.unpack3IntsFromLong(packed, 1); + int unpackedC = CoordinatePacker.unpack3IntsFromLong(packed, 2); + + assertEquals(a, unpackedA); + assertEquals(b, unpackedB); + assertEquals(c, unpackedC); + + } + +} diff --git a/src/test/java/ru/windcorp/optica/util/NamespacedTest.java b/src/test/java/ru/windcorp/optica/util/NamespacedTest.java new file mode 100644 index 0000000..10fd1cd --- /dev/null +++ b/src/test/java/ru/windcorp/optica/util/NamespacedTest.java @@ -0,0 +1,153 @@ +package ru.windcorp.optica.util; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Random; + +import org.junit.Test; + +import junit.framework.AssertionFailedError; +import ru.windcorp.optica.common.util.Namespaced; + +public class NamespacedTest { + + class TestNamespaced extends Namespaced { + + public TestNamespaced(String namespace, String name) { + super(namespace, name); + } + + } + + void shouldReject(String a, String b) { + try { + new TestNamespaced(a, b); + } catch (IllegalArgumentException | NullPointerException e) { + try { + new TestNamespaced(b, a); + } catch (IllegalArgumentException | NullPointerException e1) { + return; + } + } + + throw new AssertionFailedError("Expected NPE or IAE for: \"" + a + "\":\"" + b + "\""); + } + + @Test + public void shouldAllow() { + new TestNamespaced("Something", "Usual"); + new TestNamespaced("Vry", "Sml"); + new TestNamespaced("ALL", "CAPS"); + new TestNamespaced("WithDigits12345", "MoreDigits67890"); + } + + @Test + public void shouldRejectNulls() { + shouldReject(null, "Normal"); + shouldReject(null, null); + } + + @Test + public void shouldRejectInvalid() { + shouldReject("Contains-hyphens", "Normal"); + shouldReject("Contains_underscores", "Normal"); + shouldReject("ALL_CAPS_WITH_UNDERSCORES", "Normal"); + shouldReject("Contains whitespace", "Normal"); + shouldReject("0StartsWithDigit", "Normal"); + shouldReject("lowerCamelCase", "Normal"); + shouldReject("XS", "Normal"); + shouldReject("", "Normal"); + shouldReject("Contains:separators", "Normal"); + shouldReject("СодержитРусский", "Normal"); + } + + @Test + public void shouldRejectGarbage() { + Random random = new Random(0); + + byte[] bytes = new byte[1024]; + for (int attempt = 0; attempt < 10000; ++attempt) { + random.nextBytes(bytes); + bytes[0] = 'a'; // Make sure it is invalid + shouldReject(new String(bytes), "ContainsUtterGarbage"); + } + } + + @Test + public void testHashCodeAndEquals() { + HashSet hashSet = new HashSet<>(); + + Collection contains = new ArrayList<>(); + Collection doesNotContain = new ArrayList<>(); + + Random random = new Random(0); + + for (int i = 0; i < 256; ++i) { + String namespace = getRandomValidString(random); + String name = getRandomValidString(random); + + TestNamespaced a = new TestNamespaced(namespace, name); + TestNamespaced b = new TestNamespaced(namespace, name); + + contains.add(a); + hashSet.add(b); + } + + for (int i = 0; i < 256; ++i) { + String namespace = getRandomValidString(random); + String name = getRandomValidString(random); + + TestNamespaced c = new TestNamespaced(namespace, name); + + doesNotContain.add(c); + } + + for (TestNamespaced x : contains) { + Iterator it = doesNotContain.iterator(); + while (it.hasNext()) { + TestNamespaced next = it.next(); + if (next.getName().equals(x.getName()) && next.getNamespace().equals(x.getNamespace())) { + it.remove(); + } + } + } + + for (TestNamespaced test : contains) { + assertTrue(hashSet.contains(test)); + } + + for (TestNamespaced test : doesNotContain) { + assertFalse(hashSet.contains(test)); + } + } + + String getRandomValidString(Random random) { + char[] chars = new char[random.nextInt(100) + 3]; + + for (int i = 0; i < chars.length; ++i) { + switch (random.nextInt(3)) { + case 0: + if (i != 0) { + chars[i] = (char) ('a' + random.nextInt('z' - 'a')); + break; + } + case 1: + if (i != 0) { + chars[i] = (char) ('0' + random.nextInt('9' - '0')); + break; + } + case 2: + chars[i] = (char) ('A' + random.nextInt('Z' - 'A')); + break; + } + } + + return new String(chars); + } + +} diff --git a/src/test/java/ru/windcorp/optica/util/StashingStackTest.java b/src/test/java/ru/windcorp/optica/util/StashingStackTest.java new file mode 100644 index 0000000..d363b1b --- /dev/null +++ b/src/test/java/ru/windcorp/optica/util/StashingStackTest.java @@ -0,0 +1,83 @@ +package ru.windcorp.optica.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.NoSuchElementException; +import java.util.Random; + +import org.junit.Test; + +import com.google.common.collect.Iterators; + +import ru.windcorp.optica.common.util.StashingStack; + +public class StashingStackTest { + + @Test + public void normalOperation() { + final int size = 256; + final int operations = 2 << 16; + final long seed = 0; + + Random random = new Random(seed); + + Deque spares = new LinkedList<>(); + for (int i = 0; i < size; ++i) { + spares.add(Integer.toString(i)); + } + + StashingStack stashing = new StashingStack<>(spares); + Deque reference = new LinkedList<>(); + + for (int i = 0; i < operations; ++i) { + boolean isFull = stashing.isFull(); + boolean isEmpty = stashing.isEmpty(); + + assertTrue("isFull", isFull == (reference.size() == size)); + assertTrue("isEmpty", isEmpty == reference.isEmpty()); + assertEquals("size", reference.size(), stashing.getSize()); + + if (isFull || (!isEmpty && random.nextBoolean())) { + if (random.nextBoolean()) { + String popped = reference.pop(); + assertEquals("pop", popped, stashing.pop()); + spares.push(popped); + } else { + String peeked = reference.peek(); + assertEquals("peek", peeked, stashing.peek()); + } + } else { + reference.push(spares.pop()); + stashing.push(); + } + } + + assertTrue("remaining", Iterators.elementsEqual( + reference.descendingIterator(), + stashing.iterator() + )); + } + + @Test + public void cornerCases() { + StashingStack stack = new StashingStack<>(10); + assertNull(stack.peek()); + assertNull(stack.pop()); + assertNull(stack.push()); + } + + @Test(expected = NoSuchElementException.class) + public void noSuchElementWhenGetHead() { + new StashingStack<>(10).getHead(); + } + + @Test(expected = NoSuchElementException.class) + public void noSuchElementWhenRemoveHead() { + new StashingStack<>(10).removeHead(); + } + +}