diff --git a/.gitignore b/.gitignore
index e94b5b5f6..a001e38de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
/Release
/wadsrc_wad
*.user
+/build
/debug
/release
*/debug
@@ -41,7 +42,11 @@
/jpeg-6b/x64/
/lzma/x64/
/zlib/x64/
+/build_vc2013_64bit
/build_vc2015
/build_vc2015-32
/build_vc2015-64
/build
+/llvm
+/src/r_drawersasm.obj
+/src/r_drawersasm.o
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 607eb0054..b52e680dd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
cmake_minimum_required( VERSION 2.8.7 )
-project(ZDoom)
+project(QZDoom)
if( COMMAND cmake_policy )
if( POLICY CMP0011 )
@@ -122,7 +122,7 @@ IF( NOT CMAKE_BUILD_TYPE )
ENDIF()
set( ZDOOM_OUTPUT_DIR ${CMAKE_BINARY_DIR} CACHE PATH "Directory where zdoom.pk3 and the executable will be created." )
-set( ZDOOM_EXE_NAME "zdoom" CACHE FILEPATH "Name of the executable to create." )
+set( ZDOOM_EXE_NAME "qzdoom" CACHE FILEPATH "Name of the executable to create" )
if( MSVC )
# Allow the user to use ZDOOM_OUTPUT_DIR as a single release point.
# Use zdoom, zdoomd, zdoom64, and zdoomd64 for the binary names
@@ -320,6 +320,8 @@ add_subdirectory( tools )
add_subdirectory( dumb )
add_subdirectory( gdtoa )
add_subdirectory( wadsrc )
+add_subdirectory( wadsrc_bm )
+add_subdirectory( wadsrc_lights )
add_subdirectory( src )
if( NOT WIN32 AND NOT APPLE )
diff --git a/docs/licenses/fxaa.txt b/docs/licenses/fxaa.txt
new file mode 100644
index 000000000..fbd143c1d
--- /dev/null
+++ b/docs/licenses/fxaa.txt
@@ -0,0 +1,33 @@
+//----------------------------------------------------------------------------------
+// File: es3-kepler\FXAA/FXAA3_11.h
+// SDK Version: v3.00
+// Email: gameworks@nvidia.com
+// Site: http://developer.nvidia.com/
+//
+// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of NVIDIA CORPORATION nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//----------------------------------------------------------------------------------
diff --git a/docs/licenses/lgpl.txt b/docs/licenses/lgpl.txt
index 5ab7695ab..be2c3ea8e 100644
--- a/docs/licenses/lgpl.txt
+++ b/docs/licenses/lgpl.txt
@@ -1,504 +1,841 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 2.1, February 1999
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
-[This is the first released version of the Lesser GPL. It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
- Preamble
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
+ 0. Additional Definitions.
- This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it. You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
- When we speak of free software, we are referring to freedom of use,
-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 this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
- To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights. These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
- For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you. You must make sure that they, too, receive or can get the source
-code. If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it. And you must show them these terms so they know their rights.
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
- We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
- To protect each distributor, we want to make it very clear that
-there is no warranty for the free library. Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
- Finally, software patents pose a constant threat to the existence of
-any free program. We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder. Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
- Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License. This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License. We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
+ 1. Exception to Section 3 of the GNU GPL.
- When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library. The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom. The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
- We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License. It also provides other free software developers Less
-of an advantage over competing non-free programs. These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries. However, the Lesser license provides advantages in certain
-special circumstances.
+ 2. Conveying Modified Versions.
- For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard. To achieve this, non-free programs must be
-allowed to use the library. A more frequent case is that a free
-library does the same job as widely used non-free libraries. In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
- In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software. For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
- Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
- The precise terms and conditions for copying, distribution and
-modification follow. Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library". The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
- GNU LESSER GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+ 3. Object Code Incorporating Material from Library Header Files.
- 0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
- A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
- The "Library", below, refers to any such software library or work
-which has been distributed under these terms. A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language. (Hereinafter, translation is
-included without limitation in the term "modification".)
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
- "Source code" for a work means the preferred form of the work for
-making modifications to it. For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
+ 4. Combined Works.
- Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it). Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
- 1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
Library.
- You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
- 2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
- a) The modified work must itself be a software library.
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
- b) You must cause the files modified to carry prominent notices
- stating that you changed the files and the date of any change.
+ 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.
- c) You must cause the whole of the work to be licensed at no
- charge to all third parties under the terms of this License.
+ Preamble
- d) If a facility in the modified Library refers to a function or a
- table of data to be supplied by an application program that uses
- the facility, other than as an argument passed when the facility
- is invoked, then you must make a good faith effort to ensure that,
- in the event an application does not supply such function or
- table, the facility still operates, and performs whatever part of
- its purpose remains meaningful.
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
- (For example, a function in a library to compute square roots has
- a purpose that is entirely well-defined independent of the
- application. Therefore, Subsection 2d requires that any
- application-supplied function or table used by this function must
- be optional: if the application does not supply it, the square
- root function must still compute square roots.)
+ 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.
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
+ 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.
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
+ 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.
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
+ 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.
- 3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library. To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License. (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.) Do not make any other change in
-these notices.
-
- Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
+ 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.
- This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
+ 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.
- 4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
+ 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.
- If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
+ 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.
- 5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library". Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
+ The precise terms and conditions for copying, distribution and
+modification follow.
- However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library". The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
+ TERMS AND CONDITIONS
- When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library. The
-threshold for this to be true is not precisely defined by law.
+ 0. Definitions.
- If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work. (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
+ "This License" refers to version 3 of the GNU General Public License.
- Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
- 6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
- You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License. You must supply a copy of this License. If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License. Also, you must do one
-of these things:
+ "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.
- a) Accompany the work with the complete corresponding
- machine-readable source code for the Library including whatever
- changes were used in the work (which must be distributed under
- Sections 1 and 2 above); and, if the work is an executable linked
- with the Library, with the complete machine-readable "work that
- uses the Library", as object code and/or source code, so that the
- user can modify the Library and then relink to produce a modified
- executable containing the modified Library. (It is understood
- that the user who changes the contents of definitions files in the
- Library will not necessarily be able to recompile the application
- to use the modified definitions.)
+ 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.
- b) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (1) uses at run time a
- copy of the library already present on the user's computer system,
- rather than copying library functions into the executable, and (2)
- will operate properly with a modified version of the library, if
- the user installs one, as long as the modified version is
- interface-compatible with the version that the work was made with.
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
- c) Accompany the work with a written offer, valid for at
- least three years, to give the same user the materials
- specified in Subsection 6a, above, for a charge no more
- than the cost of performing this distribution.
+ 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.
- d) If distribution of the work is made by offering access to copy
- from a designated place, offer equivalent access to copy the above
- specified materials from the same place.
+ 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.
- e) Verify that the user has already received a copy of these
- materials or that you have already sent this user a copy.
+ 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.
- For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it. However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
+ 1. Source Code.
- It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system. Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
- 7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
+ 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) Accompany the combined library with a copy of the same work
- based on the Library, uncombined with any other library
- facilities. This must be distributed under the terms of the
- Sections above.
+ 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.
- b) Give prominent notice with the combined library of the fact
- that part of it is a work based on the Library, and explaining
- where to find the accompanying uncombined form of the same work.
+ 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.
- 8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License. Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License. However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
+ 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.
- 9. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Library or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
- 10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
+ 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.
-
- 11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
+
+ 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
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all. For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
+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.
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
+ 13. Use with the GNU Affero General Public License.
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
+ 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.
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
+ 14. Revised Versions of this License.
- 12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded. In such case, this License incorporates the limitation as if
-written in the body 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.
- 13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
+ Each version is given a distinguishing version number. If the
+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.
-Each version is given a distinguishing version number. If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation. If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
- 14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission. For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this. Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
+ 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.
- NO WARRANTY
+ 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. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "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
-LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+ 15. Disclaimer of Warranty.
- 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY 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
-LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
+ 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.
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Libraries
+ 16. Limitation of Liability.
- If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change. You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
+ 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.
- To apply these terms, attach the following notices to the library. It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
+ 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 library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+ 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 library is distributed in the hope that it will be useful,
+ This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
+ 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 Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ 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.
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary. Here is a sample; alter the names:
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
- Yoyodyne, Inc., hereby disclaims all copyright interest in the
- library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+ 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.
- , 1 April 1990
- Ty Coon, President of Vice
-
-That's all there is to it!
+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/docs/licenses/xBRZ.jpg b/docs/licenses/xBRZ.jpg
new file mode 100644
index 000000000..ab8335391
Binary files /dev/null and b/docs/licenses/xBRZ.jpg differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index aca24181f..2fb046924 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,6 +13,7 @@ include( CheckIncludeFile )
include( CheckIncludeFiles )
include( CheckLibraryExists )
include( FindPkgConfig )
+include( FindOpenGL )
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
option( NO_STRIP "Do not strip Release or MinSizeRel builds" )
@@ -254,6 +255,14 @@ else()
endif()
endif()
+# Check if we have OpenGL
+
+if( NOT OPENGL_FOUND )
+ message( FATAL_ERROR "OpenGL is required for building." )
+endif( NOT OPENGL_FOUND )
+
+set( ZDOOM_LIBS ${ZDOOM_LIBS} ${OPENGL_LIBRARIES} )
+include_directories( ${OPENGL_INCLUDE_DIR} )
if( NOT NO_OPENAL )
find_package( OpenAL )
@@ -408,6 +417,22 @@ else()
set( BACKPATCH 0 )
endif()
+if( X64 )
+ set( HAVE_MMX 1 )
+else( X64 )
+ set( SAFE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} )
+
+ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
+ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmmx")
+ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
+
+ CHECK_CXX_SOURCE_COMPILES("#include
+ int main(void) { __m64 v = _m_from_int(0); }"
+ HAVE_MMX)
+
+ set( CMAKE_CXX_FLAGS ${SAFE_CMAKE_CXX_FLAGS} )
+endif( X64 )
+
# Set up flags for GCC
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
@@ -540,6 +565,20 @@ add_custom_target( revision_check ALL
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
DEPENDS updaterevision )
+# Run drawer codegen tool
+
+if ( WIN32 )
+ add_custom_target( drawergen_target ALL
+ COMMAND drawergen src/r_drawersasm.obj
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ DEPENDS drawergen )
+else()
+ add_custom_target( drawergen_target ALL
+ COMMAND drawergen src/r_drawersasm.o
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ DEPENDS drawergen )
+endif()
+
# Libraries ZDoom needs
message( STATUS "Fluid synth libs: ${FLUIDSYNTH_LIBRARIES}" )
@@ -584,6 +623,7 @@ set( PLAT_WIN32_SOURCES
win32/i_system.cpp
win32/i_specialpaths.cpp
win32/st_start.cpp
+ win32/win32gliface.cpp
win32/win32video.cpp )
set( PLAT_POSIX_SOURCES
posix/i_cd.cpp
@@ -599,6 +639,7 @@ set( PLAT_SDL_SOURCES
posix/sdl/i_system.cpp
posix/sdl/i_timer.cpp
posix/sdl/sdlvideo.cpp
+ posix/sdl/sdlglvideo.cpp
posix/sdl/st_start.cpp )
set( PLAT_UNIX_SOURCES
posix/unix/i_specialpaths.cpp
@@ -724,6 +765,21 @@ file( GLOB HEADER_FILES
scripting/zscript/*.h
scripting/vm/*.h
xlat/*.h
+ gl/*.h
+ gl/api/*.h
+ gl/data/*.h
+ gl/dynlights/*.h
+ gl/hqnx/*.h
+ gl/hqnx_asm/*.h
+ gl/xbr/*.h
+ gl/models/*.h
+ gl/renderer/*.h
+ gl/scene/*.h
+ gl/stereo3d/*.h
+ gl/shaders/*.h
+ gl/system/*.h
+ gl/textures/*.h
+ gl/utility/*.h
*.h
)
@@ -744,17 +800,33 @@ set( NOT_COMPILED_SOURCE_FILES
set( FASTMATH_PCH_SOURCES
r_swrenderer.cpp
+ r_poly.cpp
+ r_poly_scene.cpp
+ r_poly_portal.cpp
+ r_poly_cull.cpp
+ r_poly_decal.cpp
+ r_poly_particle.cpp
+ r_poly_plane.cpp
+ r_poly_playersprite.cpp
+ r_poly_wall.cpp
+ r_poly_wallsprite.cpp
+ r_poly_sprite.cpp
+ r_poly_sky.cpp
+ r_poly_triangle.cpp
+ r_poly_intersection.cpp
r_3dfloors.cpp
r_bsp.cpp
r_draw.cpp
r_draw_pal.cpp
- r_drawt_pal.cpp
+ r_draw_rgba.cpp
+ r_drawers.cpp
r_thread.cpp
r_main.cpp
r_plane.cpp
r_segs.cpp
r_sky.cpp
r_things.cpp
+ r_voxel.cpp
r_walldraw.cpp
s_advsound.cpp
s_environment.cpp
@@ -835,6 +907,92 @@ set( FASTMATH_SOURCES
sound/sndfile_decoder.cpp
sound/music_pseudo_mididevice.cpp
wildmidi/wildmidi_lib.cpp
+ gl/compatibility/gl_20.cpp
+ gl/data/gl_data.cpp
+ gl/data/gl_portaldata.cpp
+ gl/data/gl_setup.cpp
+ gl/data/gl_matrix.cpp
+ gl/data/gl_vertexbuffer.cpp
+ gl/dynlights/a_dynlight.cpp
+ gl/utility/gl_clock.cpp
+ gl/utility/gl_cycler.cpp
+ gl/utility/gl_geometric.cpp
+ gl/renderer/gl_2ddrawer.cpp
+ gl/renderer/gl_quaddrawer.cpp
+ gl/renderer/gl_renderer.cpp
+ gl/renderer/gl_renderstate.cpp
+ gl/renderer/gl_renderbuffers.cpp
+ gl/renderer/gl_lightdata.cpp
+ gl/renderer/gl_postprocess.cpp
+ gl/renderer/gl_postprocessstate.cpp
+ gl/hqnx/init.cpp
+ gl/hqnx/hq2x.cpp
+ gl/hqnx/hq3x.cpp
+ gl/hqnx/hq4x.cpp
+ gl/xbr/xbrz.cpp
+ gl/xbr/xbrz_old.cpp
+ gl/textures/gl_hwtexture.cpp
+ gl/textures/gl_texture.cpp
+ gl/textures/gl_material.cpp
+ gl/textures/gl_hirestex.cpp
+ gl/textures/gl_bitmap.cpp
+ gl/textures/gl_samplers.cpp
+ gl/textures/gl_translate.cpp
+ gl/textures/gl_hqresize.cpp
+ gl/textures/gl_skyboxtexture.cpp
+ gl/scene/gl_bsp.cpp
+ gl/scene/gl_fakeflat.cpp
+ gl/scene/gl_clipper.cpp
+ gl/scene/gl_decal.cpp
+ gl/scene/gl_drawinfo.cpp
+ gl/scene/gl_flats.cpp
+ gl/scene/gl_walls.cpp
+ gl/scene/gl_sprite.cpp
+ gl/scene/gl_skydome.cpp
+ gl/scene/gl_renderhacks.cpp
+ gl/scene/gl_weapon.cpp
+ gl/scene/gl_scene.cpp
+ gl/scene/gl_sky.cpp
+ gl/scene/gl_portal.cpp
+ gl/scene/gl_walls_draw.cpp
+ gl/scene/gl_vertex.cpp
+ gl/scene/gl_spritelight.cpp
+ gl/stereo3d/gl_stereo3d.cpp
+ gl/stereo3d/gl_stereo_cvars.cpp
+ gl/stereo3d/gl_stereo_leftright.cpp
+ gl/stereo3d/scoped_view_shifter.cpp
+ gl/stereo3d/gl_anaglyph.cpp
+ gl/stereo3d/gl_quadstereo.cpp
+ gl/stereo3d/gl_sidebyside3d.cpp
+ gl/stereo3d/gl_interleaved3d.cpp
+ gl/dynlights/gl_dynlight.cpp
+ gl/dynlights/gl_glow.cpp
+ gl/dynlights/gl_dynlight1.cpp
+ gl/dynlights/gl_lightbuffer.cpp
+ gl/shaders/gl_shader.cpp
+ gl/shaders/gl_texshader.cpp
+ gl/shaders/gl_shaderprogram.cpp
+ gl/shaders/gl_presentshader.cpp
+ gl/shaders/gl_present3dRowshader.cpp
+ gl/shaders/gl_bloomshader.cpp
+ gl/shaders/gl_ambientshader.cpp
+ gl/shaders/gl_blurshader.cpp
+ gl/shaders/gl_colormapshader.cpp
+ gl/shaders/gl_tonemapshader.cpp
+ gl/shaders/gl_lensshader.cpp
+ gl/shaders/gl_fxaashader.cpp
+ gl/system/gl_interface.cpp
+ gl/system/gl_framebuffer.cpp
+ gl/system/gl_swframebuffer.cpp
+ gl/system/gl_swwipe.cpp
+ gl/system/gl_debug.cpp
+ gl/system/gl_menu.cpp
+ gl/system/gl_wipe.cpp
+ gl/system/gl_load.c
+ gl/models/gl_models_md3.cpp
+ gl/models/gl_models_md2.cpp
+ gl/models/gl_models.cpp
+ gl/models/gl_voxels.cpp
)
set (PCH_SOURCES
@@ -929,6 +1087,7 @@ set (PCH_SOURCES
p_mobj.cpp
p_pillar.cpp
p_plats.cpp
+ p_portals.cpp
p_pspr.cpp
p_pusher.cpp
p_saveg.cpp
@@ -1069,6 +1228,16 @@ set (PCH_SOURCES
)
enable_precompiled_headers( g_pch.h PCH_SOURCES )
+if ( WIN32 )
+ set (CODEGENOBJ_SOURCES
+ r_drawersasm.obj
+ )
+else()
+ set (CODEGENOBJ_SOURCES
+ r_drawersasm.o
+ )
+endif()
+
add_executable( zdoom WIN32 MACOSX_BUNDLE
${HEADER_FILES}
${NOT_COMPILED_SOURCE_FILES}
@@ -1099,14 +1268,21 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE
math/tanh.c
math/fastsin.cpp
zzautozend.cpp
+ ${CMAKE_BINARY_DIR}/src/${CODEGENOBJ_SOURCES}
)
+set_source_files_properties( ${CODEGENOBJ_SOURCES} PROPERTIES EXTERNAL_OBJECT true GENERATED true)
+
set_source_files_properties( ${FASTMATH_SOURCES} PROPERTIES COMPILE_FLAGS ${ZD_FASTMATH_FLAG} )
set_source_files_properties( xlat/parse_xlat.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.c" )
set_source_files_properties( sc_man.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h" )
set_source_files_properties( ${NOT_COMPILED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE )
if ( WIN32 )
set_source_files_properties( win32/fb_d3d9.cpp win32/fb_d3d9_wipe.cpp PROPERTIES COMPILE_FLAGS ${ZD_FASTMATH_FLAG} )
+
+ # Supress C4244: 'initializing': conversion from '__int64' to 'unsigned int', possible loss of data
+ # For some reason using #pragma warning(disable: 4244) is not working..
+ set_source_files_properties( ${PCH_SOURCES} PROPERTIES COMPILE_FLAGS /wd4244 )
endif()
@@ -1116,6 +1292,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS")
endif()
target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma )
+
include_directories( .
g_strife
g_shared
@@ -1133,7 +1310,7 @@ include_directories( .
${CMAKE_BINARY_DIR}/gdtoa
${SYSTEM_SOURCES_DIR} )
-add_dependencies( zdoom revision_check )
+add_dependencies( zdoom revision_check drawergen_target )
# Due to some quirks, we need to do this in this order
if( NOT ZDOOM_OUTPUT_OLDSTYLE )
@@ -1250,6 +1427,20 @@ source_group("Games\\Strife Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DI
source_group("Intermission" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/intermission/.+")
source_group("Inventory" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_inventory/.+")
source_group("Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/menu/.+")
+source_group("OpenGL Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/.+")
+source_group("OpenGL Renderer\\Data" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/data/.+")
+source_group("OpenGL Renderer\\Dynamic Lights" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/dynlights/.+")
+source_group("OpenGL Renderer\\HQ Resize" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx/.+")
+source_group("OpenGL Renderer\\HQ Resize MMX version" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx_asm/.+")
+source_group("OpenGL Renderer\\XBRZ" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx_asm/.+")
+source_group("OpenGL Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/models/.+")
+source_group("OpenGL Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/renderer/.+")
+source_group("OpenGL Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/scene/.+")
+source_group("OpenGL Renderer\\Stereo3D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/stereo3d/.+")
+source_group("OpenGL Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/shaders/.+")
+source_group("OpenGL Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/system/.+")
+source_group("OpenGL Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/textures/.+")
+source_group("OpenGL Renderer\\Utilities" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/utility/.+")
source_group("Render Core\\Render Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_.+\\.h$")
source_group("Render Core\\Render Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_.+\\.cpp$")
source_group("Render Data\\Resource Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/.+\\.h$")
diff --git a/src/actor.h b/src/actor.h
index 364bb7098..5445c92be 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -1439,6 +1439,14 @@ public:
int ApplyDamageFactor(FName damagetype, int damage) const;
int GetModifiedDamage(FName damagetype, int damage, bool passive);
+
+ // begin of GZDoom specific additions
+ TArray > dynamiclights;
+ void * lightassociations;
+ bool hasmodel;
+ // end of GZDoom specific additions
+
+ size_t PropagateMark();
};
class FActorIterator
diff --git a/src/c_console.cpp b/src/c_console.cpp
index faeec0a68..0e9b94996 100644
--- a/src/c_console.cpp
+++ b/src/c_console.cpp
@@ -159,10 +159,14 @@ int active_con_scale()
int scale = con_scale;
if (scale <= 0)
{
- scale = CleanXfac - 1;
- if (scale <= 0)
+ scale = uiscale;
+ if (scale == 0)
{
- scale = 1;
+ scale = CleanXfac - 1;
+ if (scale <= 0)
+ {
+ scale = 1;
+ }
}
}
return scale;
diff --git a/src/d_main.cpp b/src/d_main.cpp
index 04ba34cbb..1ea2d2707 100644
--- a/src/d_main.cpp
+++ b/src/d_main.cpp
@@ -205,6 +205,11 @@ CUSTOM_CVAR (String, vid_cursor, "None", CVAR_ARCHIVE | CVAR_NOINITCALL)
}
}
+// Controlled by startup dialog
+CVAR (Bool, disableautoload, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
+CVAR (Bool, autoloadbrightmaps, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
+CVAR (Bool, autoloadlights, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
+
bool wantToRestart;
bool DrawFSHUD; // [RH] Draw fullscreen HUD?
TArray allwads;
@@ -2038,7 +2043,24 @@ static void AddAutoloadFiles(const char *autoname)
{
LumpFilterIWAD.Format("%s.", autoname); // The '.' is appened to simplify parsing the string
- if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload"))
+ // [SP] Dialog reaction - load lights.pk3 and brightmaps.pk3 based on user choices
+ if (!(gameinfo.flags & GI_SHAREWARE))
+ {
+ if (autoloadlights)
+ {
+ const char *lightswad = BaseFileSearch ("lights.pk3", NULL);
+ if (lightswad)
+ D_AddFile (allwads, lightswad);
+ }
+ if (autoloadbrightmaps)
+ {
+ const char *bmwad = BaseFileSearch ("brightmaps.pk3", NULL);
+ if (bmwad)
+ D_AddFile (allwads, bmwad);
+ }
+ }
+
+ if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload") && !disableautoload)
{
FString file;
diff --git a/src/f_wipe.cpp b/src/f_wipe.cpp
index a3ceb8d50..1fb7c982b 100644
--- a/src/f_wipe.cpp
+++ b/src/f_wipe.cpp
@@ -28,6 +28,9 @@
#include "f_wipe.h"
#include "c_cvars.h"
#include "templates.h"
+#include "v_palette.h"
+
+EXTERN_CVAR(Bool, r_blendmethod)
//
// SCREEN WIPE PACKAGE
@@ -280,39 +283,80 @@ bool wipe_doBurn (int ticks)
fromold = (BYTE *)wipe_scr_start;
fromnew = (BYTE *)wipe_scr_end;
- for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep)
+ if (!r_blendmethod)
{
- for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep)
+ for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep)
{
- int fglevel;
+ for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep)
+ {
+ int fglevel;
- fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2;
- if (fglevel >= 63)
- {
- to[x] = fromnew[x];
- }
- else if (fglevel == 0)
- {
- to[x] = fromold[x];
- done = false;
- }
- else
- {
- int bglevel = 64-fglevel;
- DWORD *fg2rgb = Col2RGB8[fglevel];
- DWORD *bg2rgb = Col2RGB8[bglevel];
- DWORD fg = fg2rgb[fromnew[x]];
- DWORD bg = bg2rgb[fromold[x]];
- fg = (fg+bg) | 0x1f07c1f;
- to[x] = RGB32k.All[fg & (fg>>15)];
- done = false;
+ fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2;
+ if (fglevel >= 63)
+ {
+ to[x] = fromnew[x];
+ }
+ else if (fglevel == 0)
+ {
+ to[x] = fromold[x];
+ done = false;
+ }
+ else
+ {
+ int bglevel = 64-fglevel;
+ DWORD *fg2rgb = Col2RGB8[fglevel];
+ DWORD *bg2rgb = Col2RGB8[bglevel];
+ DWORD fg = fg2rgb[fromnew[x]];
+ DWORD bg = bg2rgb[fromold[x]];
+ fg = (fg+bg) | 0x1f07c1f;
+ to[x] = RGB32k.All[fg & (fg>>15)];
+ done = false;
+ }
}
+ fromold += SCREENWIDTH;
+ fromnew += SCREENWIDTH;
+ to += SCREENPITCH;
}
- fromold += SCREENWIDTH;
- fromnew += SCREENWIDTH;
- to += SCREENPITCH;
- }
+ }
+ else
+ {
+ for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep)
+ {
+ for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep)
+ {
+ int fglevel;
+
+ fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2;
+ if (fglevel >= 63)
+ {
+ to[x] = fromnew[x];
+ }
+ else if (fglevel == 0)
+ {
+ to[x] = fromold[x];
+ done = false;
+ }
+ else
+ {
+ int bglevel = 64-fglevel;
+
+ const PalEntry* pal = GPalette.BaseColors;
+
+ DWORD fg = fromnew[x];
+ DWORD bg = fromold[x];
+ int r = MIN((pal[fg].r * fglevel + pal[bg].r * bglevel) >> 8, 63);
+ int g = MIN((pal[fg].g * fglevel + pal[bg].g * bglevel) >> 8, 63);
+ int b = MIN((pal[fg].b * fglevel + pal[bg].b * bglevel) >> 8, 63);
+ to[x] = RGB256k.RGB[r][g][b];
+ done = false;
+ }
+ }
+ fromold += SCREENWIDTH;
+ fromnew += SCREENWIDTH;
+ to += SCREENPITCH;
+ }
+ }
return done || (burntime > 40);
}
@@ -347,19 +391,41 @@ bool wipe_doFade (int ticks)
BYTE *fromnew = (BYTE *)wipe_scr_end;
BYTE *fromold = (BYTE *)wipe_scr_start;
BYTE *to = screen->GetBuffer();
+ const PalEntry *pal = GPalette.BaseColors;
- for (y = 0; y < SCREENHEIGHT; y++)
+ if (!r_blendmethod)
{
- for (x = 0; x < SCREENWIDTH; x++)
+ for (y = 0; y < SCREENHEIGHT; y++)
{
- DWORD fg = fg2rgb[fromnew[x]];
- DWORD bg = bg2rgb[fromold[x]];
- fg = (fg+bg) | 0x1f07c1f;
- to[x] = RGB32k.All[fg & (fg>>15)];
+ for (x = 0; x < SCREENWIDTH; x++)
+ {
+ DWORD fg = fg2rgb[fromnew[x]];
+ DWORD bg = bg2rgb[fromold[x]];
+ fg = (fg+bg) | 0x1f07c1f;
+ to[x] = RGB32k.All[fg & (fg>>15)];
+ }
+ fromnew += SCREENWIDTH;
+ fromold += SCREENWIDTH;
+ to += SCREENPITCH;
+ }
+ }
+ else
+ {
+ for (y = 0; y < SCREENHEIGHT; y++)
+ {
+ for (x = 0; x < SCREENWIDTH; x++)
+ {
+ DWORD fg = fromnew[x];
+ DWORD bg = fromold[x];
+ int r = MIN((pal[fg].r * (64-bglevel) + pal[bg].r * bglevel) >> 8, 63);
+ int g = MIN((pal[fg].g * (64-bglevel) + pal[bg].g * bglevel) >> 8, 63);
+ int b = MIN((pal[fg].b * (64-bglevel) + pal[bg].b * bglevel) >> 8, 63);
+ to[x] = RGB256k.RGB[r][g][b];
+ }
+ fromnew += SCREENWIDTH;
+ fromold += SCREENWIDTH;
+ to += SCREENPITCH;
}
- fromnew += SCREENWIDTH;
- fromold += SCREENWIDTH;
- to += SCREENPITCH;
}
}
return false;
@@ -382,6 +448,9 @@ static bool (*wipes[])(int) =
// Returns true if the wipe should be performed.
bool wipe_StartScreen (int type)
{
+ if (screen->IsBgra())
+ return false;
+
CurrentWipeType = clamp(type, 0, wipe_NUMWIPES - 1);
if (CurrentWipeType)
@@ -395,11 +464,15 @@ bool wipe_StartScreen (int type)
void wipe_EndScreen (void)
{
+ if (screen->IsBgra())
+ return;
+
if (CurrentWipeType)
{
wipe_scr_end = new short[SCREENWIDTH * SCREENHEIGHT / 2];
screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end);
screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); // restore start scr.
+
// Initialize the wipe
(*wipes[(CurrentWipeType-1)*3])(0);
}
@@ -410,6 +483,9 @@ bool wipe_ScreenWipe (int ticks)
{
bool rc;
+ if (screen->IsBgra())
+ return true;
+
if (CurrentWipeType == wipe_None)
return true;
@@ -423,6 +499,9 @@ bool wipe_ScreenWipe (int ticks)
// Final things for the wipe
void wipe_Cleanup()
{
+ if (screen->IsBgra())
+ return;
+
if (wipe_scr_start != NULL)
{
delete[] wipe_scr_start;
diff --git a/src/g_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp
index 4afe74f1f..572022f66 100644
--- a/src/g_inventory/a_artifacts.cpp
+++ b/src/g_inventory/a_artifacts.cpp
@@ -735,7 +735,8 @@ int APowerInvisibility::AlterWeaponSprite (visstyle_t *vis)
if ((vis->Alpha < 0.25f && special1 > 0) || (vis->Alpha == 0))
{
vis->Alpha = clamp((1.f - float(Strength/100)), 0.f, 1.f);
- vis->colormap = SpecialColormaps[INVERSECOLORMAP].Colormap;
+ vis->BaseColormap = &SpecialColormaps[INVERSECOLORMAP];
+ vis->ColormapNum = 0;
}
return -1; // This item is valid so another one shouldn't reset the translucency
}
diff --git a/src/g_level.cpp b/src/g_level.cpp
index 321810589..f8ccd13be 100644
--- a/src/g_level.cpp
+++ b/src/g_level.cpp
@@ -1345,7 +1345,7 @@ void G_InitLevelLocals ()
level_info_t *info;
BaseBlendA = 0.0f; // Remove underwater blend effect, if any
- NormalLight.Maps = realcolormaps;
+ NormalLight.Maps = realcolormaps.Maps;
// [BB] Instead of just setting the color, we also have to reset Desaturate and build the lights.
NormalLight.ChangeColor (PalEntry (255, 255, 255), 0);
@@ -1373,6 +1373,7 @@ void G_InitLevelLocals ()
R_SetDefaultColormap (info->FadeTable);
if (strnicmp (info->FadeTable, "COLORMAP", 8) != 0)
{
+ level.fadeto = 0xff939393; //[SP] Hexen True-color compatibility, just use gray.
level.flags |= LEVEL_HASFADETABLE;
}
/*
diff --git a/src/g_strife/strife_sbar.cpp b/src/g_strife/strife_sbar.cpp
index 46dd63ff7..ccf17f8f1 100644
--- a/src/g_strife/strife_sbar.cpp
+++ b/src/g_strife/strife_sbar.cpp
@@ -37,7 +37,6 @@ public:
const BYTE *GetColumn (unsigned int column, const Span **spans_out);
const BYTE *GetPixels ();
bool CheckModified ();
- void Unload ();
void SetVial (int level);
@@ -92,10 +91,6 @@ bool FHealthBar::CheckModified ()
return NeedRefresh;
}
-void FHealthBar::Unload ()
-{
-}
-
const BYTE *FHealthBar::GetColumn (unsigned int column, const Span **spans_out)
{
if (NeedRefresh)
diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp
new file mode 100644
index 000000000..348663929
--- /dev/null
+++ b/src/gl/compatibility/gl_20.cpp
@@ -0,0 +1,844 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_20.cpp
+**
+** Fallback code for ancient hardware
+** This file collects everything larger that is only needed for
+** OpenGL 2.0/no shader compatibility.
+**
+*/
+
+#include "gl/system/gl_system.h"
+#include "menu/menu.h"
+#include "tarray.h"
+#include "doomtype.h"
+#include "m_argv.h"
+#include "zstring.h"
+#include "version.h"
+#include "i_system.h"
+#include "v_text.h"
+#include "r_utility.h"
+#include "gl/dynlights/gl_dynlight.h"
+#include "gl/utility/gl_geometric.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/system/gl_interface.h"
+#include "gl/system/gl_cvars.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/data/gl_vertexbuffer.h"
+
+
+CVAR(Bool, gl_lights_additive, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+
+//==========================================================================
+//
+// Do some tinkering with the menus so that certain options only appear
+// when they are actually valid.
+//
+//==========================================================================
+
+void gl_PatchMenu()
+{
+ // Radial fog and Doom lighting are not available without full shader support.
+
+ FOptionValues **opt = OptionValues.CheckKey("LightingModes");
+ if (opt != NULL)
+ {
+ for(int i = (*opt)->mValues.Size()-1; i>=0; i--)
+ {
+ // Delete 'Doom' lighting mode
+ if ((*opt)->mValues[i].Value == 2.0 || (*opt)->mValues[i].Value == 8.0)
+ {
+ (*opt)->mValues.Delete(i);
+ }
+ }
+ }
+
+ opt = OptionValues.CheckKey("FogMode");
+ if (opt != NULL)
+ {
+ for(int i = (*opt)->mValues.Size()-1; i>=0; i--)
+ {
+ // Delete 'Radial' fog mode
+ if ((*opt)->mValues[i].Value == 2.0)
+ {
+ (*opt)->mValues.Delete(i);
+ }
+ }
+ }
+
+ // disable features that don't work without shaders.
+ if (gl_lightmode == 2 || gl_lightmode == 8) gl_lightmode = 3;
+ if (gl_fogmode == 2) gl_fogmode = 1;
+
+ // todo: remove more unsupported stuff like postprocessing options.
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void gl_SetTextureMode(int type)
+{
+ static float white[] = {1.f,1.f,1.f,1.f};
+
+ if (type == TM_MASK)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE0);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+ }
+ else if (type == TM_OPAQUE)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ }
+ else if (type == TM_INVERSE)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE0);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+ }
+ else if (type == TM_INVERTOPAQUE)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ }
+ else // if (type == TM_MODULATE)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ }
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+static int ffTextureMode;
+static bool ffTextureEnabled;
+static bool ffFogEnabled;
+static PalEntry ffFogColor;
+static int ffSpecialEffect;
+static float ffFogDensity;
+static bool currentTextureMatrixState;
+static bool currentModelMatrixState;
+
+void FRenderState::ApplyFixedFunction()
+{
+ if (mTextureMode != ffTextureMode)
+ {
+ ffTextureMode = mTextureMode;
+ if (ffTextureMode == TM_CLAMPY) ffTextureMode = TM_MODULATE; // this cannot be replicated. Too bad if it creates visual artifacts
+ gl_SetTextureMode(ffTextureMode);
+ }
+ if (mTextureEnabled != ffTextureEnabled)
+ {
+ if ((ffTextureEnabled = mTextureEnabled)) glEnable(GL_TEXTURE_2D);
+ else glDisable(GL_TEXTURE_2D);
+ }
+ if (mFogEnabled != ffFogEnabled)
+ {
+ if ((ffFogEnabled = mFogEnabled))
+ {
+ glEnable(GL_FOG);
+ }
+ else glDisable(GL_FOG);
+ }
+ if (mFogEnabled)
+ {
+ if (ffFogColor != mFogColor)
+ {
+ ffFogColor = mFogColor;
+ GLfloat FogColor[4] = { mFogColor.r / 255.0f,mFogColor.g / 255.0f,mFogColor.b / 255.0f,0.0f };
+ glFogfv(GL_FOG_COLOR, FogColor);
+ }
+ if (ffFogDensity != mLightParms[2])
+ {
+ glFogf(GL_FOG_DENSITY, mLightParms[2] * -0.6931471f); // = 1/log(2)
+ ffFogDensity = mLightParms[2];
+ }
+ }
+ if (mSpecialEffect != ffSpecialEffect)
+ {
+ switch (ffSpecialEffect)
+ {
+ case EFF_SPHEREMAP:
+ glDisable(GL_TEXTURE_GEN_T);
+ glDisable(GL_TEXTURE_GEN_S);
+
+ default:
+ break;
+ }
+ switch (mSpecialEffect)
+ {
+ case EFF_SPHEREMAP:
+ // Use sphere mapping for this
+ glEnable(GL_TEXTURE_GEN_T);
+ glEnable(GL_TEXTURE_GEN_S);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ break;
+
+ default:
+ break;
+ }
+ ffSpecialEffect = mSpecialEffect;
+ }
+
+ FStateVec4 col = mColor;
+
+ col.vec[0] += mDynColor.vec[0];
+ col.vec[1] += mDynColor.vec[1];
+ col.vec[2] += mDynColor.vec[2];
+ col.vec[0] = clamp(col.vec[0], 0.f, 1.f);
+
+ col.vec[0] = clamp(col.vec[0], 0.f, 1.f);
+ col.vec[1] = clamp(col.vec[1], 0.f, 1.f);
+ col.vec[2] = clamp(col.vec[2], 0.f, 1.f);
+ col.vec[3] = clamp(col.vec[3], 0.f, 1.f);
+
+ col.vec[0] *= (mObjectColor.r / 255.f);
+ col.vec[1] *= (mObjectColor.g / 255.f);
+ col.vec[2] *= (mObjectColor.b / 255.f);
+ col.vec[3] *= (mObjectColor.a / 255.f);
+ glColor4fv(col.vec);
+
+ glEnable(GL_BLEND);
+ if (mAlphaThreshold > 0)
+ {
+ glEnable(GL_ALPHA_TEST);
+ glAlphaFunc(GL_GREATER, mAlphaThreshold * col.vec[3]);
+ }
+ else
+ {
+ glDisable(GL_ALPHA_TEST);
+ }
+
+ if (mTextureMatrixEnabled)
+ {
+ glMatrixMode(GL_TEXTURE);
+ glLoadMatrixf(mTextureMatrix.get());
+ currentTextureMatrixState = true;
+ }
+ else if (currentTextureMatrixState)
+ {
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ currentTextureMatrixState = false;
+ }
+
+ if (mModelMatrixEnabled)
+ {
+ VSMatrix mult = mViewMatrix;
+ mult.multMatrix(mModelMatrix);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf(mult.get());
+ currentModelMatrixState = true;
+ }
+ else if (currentModelMatrixState)
+ {
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf(mViewMatrix.get());
+ currentModelMatrixState = false;
+ }
+
+
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void gl_FillScreen();
+
+void FRenderState::DrawColormapOverlay()
+{
+ float r, g, b;
+ if (mColormapState > CM_DEFAULT && mColormapState < CM_MAXCOLORMAP)
+ {
+ FSpecialColormap *scm = &SpecialColormaps[gl_fixedcolormap - CM_FIRSTSPECIALCOLORMAP];
+ float m[] = { scm->ColorizeEnd[0] - scm->ColorizeStart[0],
+ scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f };
+
+ if (m[0] < 0 && m[1] < 0 && m[2] < 0)
+ {
+ gl_RenderState.SetColor(1, 1, 1, 1);
+ gl_RenderState.BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
+ gl_FillScreen();
+
+ r = scm->ColorizeStart[0];
+ g = scm->ColorizeStart[1];
+ b = scm->ColorizeStart[2];
+ }
+ else
+ {
+ r = scm->ColorizeEnd[0];
+ g = scm->ColorizeEnd[1];
+ b = scm->ColorizeEnd[2];
+ }
+ }
+ else if (mColormapState == CM_LITE)
+ {
+ if (gl_enhanced_nightvision)
+ {
+ r = 0.375f, g = 1.0f, b = 0.375f;
+ }
+ else
+ {
+ return;
+ }
+ }
+ else if (mColormapState >= CM_TORCH)
+ {
+ int flicker = mColormapState - CM_TORCH;
+ r = (0.8f + (7 - flicker) / 70.0f);
+ if (r > 1.0f) r = 1.0f;
+ b = g = r;
+ if (gl_enhanced_nightvision) b = g * 0.75f;
+ }
+ else return;
+
+ gl_RenderState.SetColor(r, g, b, 1.f);
+ gl_RenderState.BlendFunc(GL_DST_COLOR, GL_ZERO);
+ gl_FillScreen();
+ gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+//==========================================================================
+//
+// Sets up the parameters to render one dynamic light onto one plane
+//
+//==========================================================================
+
+bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, Vector & up, Vector & right,
+ float & scale, bool checkside, bool additive)
+{
+ Vector fn, pos;
+
+ DVector3 lpos = light->PosRelative(group);
+
+ float dist = fabsf(p.DistToPoint(lpos.X, lpos.Z, lpos.Y));
+ float radius = light->GetRadius();
+
+ if (radius <= 0.f) return false;
+ if (dist > radius) return false;
+ if (checkside && gl_lights_checkside && p.PointOnSide(lpos.X, lpos.Z, lpos.Y))
+ {
+ return false;
+ }
+ if (!light->visibletoplayer)
+ {
+ return false;
+ }
+
+ scale = 1.0f / ((2.f * radius) - dist);
+
+ // project light position onto plane (find closest point on plane)
+
+
+ pos.Set(lpos.X, lpos.Z, lpos.Y);
+ fn = p.Normal();
+ fn.GetRightUp(right, up);
+
+#ifdef _MSC_VER
+ nearPt = pos + fn * dist;
+#else
+ Vector tmpVec = fn * dist;
+ nearPt = pos + tmpVec;
+#endif
+
+ float cs = 1.0f - (dist / radius);
+ if (additive) cs *= 0.2f; // otherwise the light gets too strong.
+ float r = light->GetRed() / 255.0f * cs;
+ float g = light->GetGreen() / 255.0f * cs;
+ float b = light->GetBlue() / 255.0f * cs;
+
+ if (light->IsSubtractive())
+ {
+ Vector v;
+
+ gl_RenderState.BlendEquation(GL_FUNC_REVERSE_SUBTRACT);
+ v.Set(r, g, b);
+ r = v.Length() - r;
+ g = v.Length() - g;
+ b = v.Length() - b;
+ }
+ else
+ {
+ gl_RenderState.BlendEquation(GL_FUNC_ADD);
+ }
+ gl_RenderState.SetColor(r, g, b);
+ return true;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+bool gl_SetupLightTexture()
+{
+ if (GLRenderer->gllight == nullptr) return false;
+ FMaterial * pat = FMaterial::ValidateTexture(GLRenderer->gllight, false);
+ gl_RenderState.SetMaterial(pat, CLAMP_XY_NOMIP, 0, -1, false);
+ return true;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+bool GLWall::PutWallCompat(int passflag)
+{
+ static int list_indices[2][2] =
+ { { GLLDL_WALLS_PLAIN, GLLDL_WALLS_FOG },{ GLLDL_WALLS_MASKED, GLLDL_WALLS_FOGMASKED } };
+
+ // are lights possible?
+ if (gl_fixedcolormap != CM_DEFAULT || !gl_lights || seg->sidedef == nullptr || type == RENDERWALL_M2SNF || !gltexture) return false;
+
+ // multipassing these is problematic.
+ if ((flags&GLWF_SKYHACK && type == RENDERWALL_M2S)) return false;
+
+ // Any lights affecting this wall?
+ if (!(seg->sidedef->Flags & WALLF_POLYOBJ))
+ {
+ if (seg->sidedef->lighthead == nullptr) return false;
+ }
+ else if (sub)
+ {
+ if (sub->lighthead == nullptr) return false;
+ }
+
+ bool foggy = gl_CheckFog(&Colormap, lightlevel) || (level.flags&LEVEL_HASFADETABLE) || gl_lights_additive;
+ bool masked = passflag == 2 && gltexture->isMasked();
+
+ int list = list_indices[masked][foggy];
+ gl_drawinfo->dldrawlists[list].AddWall(this);
+ return true;
+
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+bool GLFlat::PutFlatCompat(bool fog)
+{
+ // are lights possible?
+ if (gl_fixedcolormap != CM_DEFAULT || !gl_lights || !gltexture || renderstyle != STYLE_Translucent || alpha < 1.f - FLT_EPSILON || sector->lighthead == NULL) return false;
+
+ static int list_indices[2][2] =
+ { { GLLDL_FLATS_PLAIN, GLLDL_FLATS_FOG },{ GLLDL_FLATS_MASKED, GLLDL_FLATS_FOGMASKED } };
+
+ bool masked = gltexture->isMasked() && ((renderflags&SSRF_RENDER3DPLANES) || stack);
+ bool foggy = gl_CheckFog(&Colormap, lightlevel) || (level.flags&LEVEL_HASFADETABLE) || gl_lights_additive;
+
+
+ int list = list_indices[masked][foggy];
+ gl_drawinfo->dldrawlists[list].AddFlat(this);
+ return true;
+}
+
+
+//==========================================================================
+//
+// Fog boundary without any shader support
+//
+//==========================================================================
+
+void GLWall::RenderFogBoundaryCompat()
+{
+ // without shaders some approximation is needed. This won't look as good
+ // as the shader version but it's an acceptable compromise.
+ float fogdensity = gl_GetFogDensity(lightlevel, Colormap.FadeColor);
+
+ float dist1 = Dist2(ViewPos.X, ViewPos.Y, glseg.x1, glseg.y1);
+ float dist2 = Dist2(ViewPos.X, ViewPos.Y, glseg.x2, glseg.y2);
+
+ // these values were determined by trial and error and are scale dependent!
+ float fogd1 = (0.95f - exp(-fogdensity*dist1 / 62500.f)) * 1.05f;
+ float fogd2 = (0.95f - exp(-fogdensity*dist2 / 62500.f)) * 1.05f;
+
+ float fc[4] = { Colormap.FadeColor.r / 255.0f,Colormap.FadeColor.g / 255.0f,Colormap.FadeColor.b / 255.0f,fogd2 };
+
+ gl_RenderState.EnableTexture(false);
+ gl_RenderState.EnableFog(false);
+ gl_RenderState.AlphaFunc(GL_GEQUAL, 0);
+ gl_RenderState.Apply();
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(-1.0f, -128.0f);
+ glDepthFunc(GL_LEQUAL);
+ glColor4f(fc[0], fc[1], fc[2], fogd1);
+ glBegin(GL_TRIANGLE_FAN);
+ glTexCoord2f(tcs[LOLFT].u, tcs[LOLFT].v);
+ glVertex3f(glseg.x1, zbottom[0], glseg.y1);
+ glTexCoord2f(tcs[UPLFT].u, tcs[UPLFT].v);
+ glVertex3f(glseg.x1, ztop[0], glseg.y1);
+ glColor4f(fc[0], fc[1], fc[2], fogd2);
+ glTexCoord2f(tcs[UPRGT].u, tcs[UPRGT].v);
+ glVertex3f(glseg.x2, ztop[1], glseg.y2);
+ glTexCoord2f(tcs[LORGT].u, tcs[LORGT].v);
+ glVertex3f(glseg.x2, zbottom[1], glseg.y2);
+ glEnd();
+ glDepthFunc(GL_LESS);
+ glPolygonOffset(0.0f, 0.0f);
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ gl_RenderState.EnableFog(true);
+ gl_RenderState.AlphaFunc(GL_GEQUAL, 0.5f);
+ gl_RenderState.EnableTexture(true);
+}
+
+//==========================================================================
+//
+// Flats
+//
+//==========================================================================
+
+void GLFlat::DrawSubsectorLights(subsector_t * sub, int pass)
+{
+ Plane p;
+ Vector nearPt, up, right, t1;
+ float scale;
+
+ FLightNode * node = sub->lighthead;
+ while (node)
+ {
+ ADynamicLight * light = node->lightsource;
+
+ if (light->flags2&MF2_DORMANT ||
+ (pass == GLPASS_LIGHTTEX && light->IsAdditive()) ||
+ (pass == GLPASS_LIGHTTEX_ADDITIVE && !light->IsAdditive()))
+ {
+ node = node->nextLight;
+ continue;
+ }
+
+ // we must do the side check here because gl_SetupLight needs the correct plane orientation
+ // which we don't have for Legacy-style 3D-floors
+ double planeh = plane.plane.ZatPoint(light);
+ if (gl_lights_checkside && ((planehZ() && ceiling) || (planeh>light->Z() && !ceiling)))
+ {
+ node = node->nextLight;
+ continue;
+ }
+
+ p.Set(plane.plane);
+ if (!gl_SetupLight(sub->sector->PortalGroup, p, light, nearPt, up, right, scale, false, pass != GLPASS_LIGHTTEX))
+ {
+ node = node->nextLight;
+ continue;
+ }
+ gl_RenderState.Apply();
+
+ FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
+ for (unsigned int k = 0; k < sub->numlines; k++)
+ {
+ vertex_t *vt = sub->firstline[k].v1;
+ ptr->x = vt->fX();
+ ptr->z = plane.plane.ZatPoint(vt) + dz;
+ ptr->y = vt->fY();
+ t1.Set(ptr->x, ptr->z, ptr->y);
+ Vector nearToVert = t1 - nearPt;
+
+ ptr->u = (nearToVert.Dot(right) * scale) + 0.5f;
+ ptr->v = (nearToVert.Dot(up) * scale) + 0.5f;
+ ptr++;
+ }
+ GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_FAN);
+ node = node->nextLight;
+ }
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void GLFlat::DrawLightsCompat(int pass)
+{
+ gl_RenderState.Apply();
+ // Draw the subsectors belonging to this sector
+ for (int i = 0; isubsectorcount; i++)
+ {
+ subsector_t * sub = sector->subsectors[i];
+ if (gl_drawinfo->ss_renderflags[sub - subsectors] & renderflags)
+ {
+ DrawSubsectorLights(sub, pass);
+ }
+ }
+
+ // Draw the subsectors assigned to it due to missing textures
+ if (!(renderflags&SSRF_RENDER3DPLANES))
+ {
+ gl_subsectorrendernode * node = (renderflags&SSRF_RENDERFLOOR) ?
+ gl_drawinfo->GetOtherFloorPlanes(sector->sectornum) :
+ gl_drawinfo->GetOtherCeilingPlanes(sector->sectornum);
+
+ while (node)
+ {
+ DrawSubsectorLights(node->sub, pass);
+ node = node->next;
+ }
+ }
+}
+
+
+//==========================================================================
+//
+// Sets up the texture coordinates for one light to be rendered
+//
+//==========================================================================
+bool GLWall::PrepareLight(ADynamicLight * light, int pass)
+{
+ float vtx[] = { glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2 };
+ Plane p;
+ Vector nearPt, up, right;
+ float scale;
+
+ p.Init(vtx, 4);
+
+ if (!p.ValidNormal())
+ {
+ return false;
+ }
+
+ if (!gl_SetupLight(seg->frontsector->PortalGroup, p, light, nearPt, up, right, scale, true, pass != GLPASS_LIGHTTEX))
+ {
+ return false;
+ }
+
+ Vector t1;
+ int outcnt[4] = { 0,0,0,0 };
+
+ for (int i = 0; i<4; i++)
+ {
+ t1.Set(&vtx[i * 3]);
+ Vector nearToVert = t1 - nearPt;
+ tcs[i].u = (nearToVert.Dot(right) * scale) + 0.5f;
+ tcs[i].v = (nearToVert.Dot(up) * scale) + 0.5f;
+
+ // quick check whether the light touches this polygon
+ if (tcs[i].u<0) outcnt[0]++;
+ if (tcs[i].u>1) outcnt[1]++;
+ if (tcs[i].v<0) outcnt[2]++;
+ if (tcs[i].v>1) outcnt[3]++;
+
+ }
+ // The light doesn't touch this polygon
+ if (outcnt[0] == 4 || outcnt[1] == 4 || outcnt[2] == 4 || outcnt[3] == 4) return false;
+
+ draw_dlight++;
+ return true;
+}
+
+
+void GLWall::RenderLightsCompat(int pass)
+{
+ FLightNode * node;
+
+ // black fog is diminishing light and should affect lights less than the rest!
+ if (pass == GLPASS_LIGHTTEX) gl_SetFog((255 + lightlevel) >> 1, 0, NULL, false);
+ else gl_SetFog(lightlevel, 0, &Colormap, true);
+
+ if (seg->sidedef == NULL)
+ {
+ return;
+ }
+ else if (!(seg->sidedef->Flags & WALLF_POLYOBJ))
+ {
+ // Iterate through all dynamic lights which touch this wall and render them
+ node = seg->sidedef->lighthead;
+ }
+ else if (sub)
+ {
+ // To avoid constant rechecking for polyobjects use the subsector's lightlist instead
+ node = sub->lighthead;
+ }
+ else
+ {
+ return;
+ }
+
+ texcoord save[4];
+ memcpy(save, tcs, sizeof(tcs));
+ while (node)
+ {
+ ADynamicLight * light = node->lightsource;
+
+ if (light->flags2&MF2_DORMANT ||
+ (pass == GLPASS_LIGHTTEX && light->IsAdditive()) ||
+ (pass == GLPASS_LIGHTTEX_ADDITIVE && !light->IsAdditive()))
+ {
+ node = node->nextLight;
+ continue;
+ }
+ if (PrepareLight(light, pass))
+ {
+ vertcount = 0;
+ RenderWall(RWF_TEXTURED);
+ }
+ node = node->nextLight;
+ }
+ memcpy(tcs, save, sizeof(tcs));
+ vertcount = 0;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FGLRenderer::RenderMultipassStuff()
+{
+ // First pass: empty background with sector light only
+
+ // Part 1: solid geometry. This is set up so that there are no transparent parts
+
+ // remove any remaining texture bindings and shaders whick may get in the way.
+ gl_RenderState.EnableTexture(false);
+ gl_RenderState.EnableBrightmap(false);
+ gl_RenderState.Apply();
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawWalls(GLPASS_PLAIN);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_PLAIN].DrawFlats(GLPASS_PLAIN);
+
+ // Part 2: masked geometry. This is set up so that only pixels with alpha>0.5 will show
+ // This creates a blank surface that only fills the nontransparent parts of the texture
+ gl_RenderState.EnableTexture(true);
+ gl_RenderState.SetTextureMode(TM_MASK);
+ gl_RenderState.EnableBrightmap(true);
+ gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_MASKED].DrawWalls(GLPASS_PLAIN);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_MASKED].DrawFlats(GLPASS_PLAIN);
+
+ // Part 3: The base of fogged surfaces, including the texture
+ gl_RenderState.EnableBrightmap(false);
+ gl_RenderState.SetTextureMode(TM_MODULATE);
+ gl_RenderState.AlphaFunc(GL_GEQUAL, 0);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_FOG].DrawWalls(GLPASS_PLAIN);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_FOG].DrawFlats(GLPASS_PLAIN);
+ gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_FOGMASKED].DrawWalls(GLPASS_PLAIN);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_FOGMASKED].DrawFlats(GLPASS_PLAIN);
+
+ // second pass: draw lights
+ glDepthMask(false);
+ if (mLightCount && !gl_fixedcolormap)
+ {
+ if (gl_SetupLightTexture())
+ {
+ gl_RenderState.BlendFunc(GL_ONE, GL_ONE);
+ glDepthFunc(GL_EQUAL);
+ if (glset.lightmode == 8) gl_RenderState.SetSoftLightLevel(255);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawWalls(GLPASS_LIGHTTEX);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_MASKED].DrawWalls(GLPASS_LIGHTTEX);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_PLAIN].DrawFlats(GLPASS_LIGHTTEX);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_MASKED].DrawFlats(GLPASS_LIGHTTEX);
+ gl_RenderState.BlendEquation(GL_FUNC_ADD);
+ }
+ else gl_lights = false;
+ }
+
+ // third pass: modulated texture
+ gl_RenderState.SetColor(0xffffffff);
+ gl_RenderState.BlendFunc(GL_DST_COLOR, GL_ZERO);
+ gl_RenderState.EnableFog(false);
+ gl_RenderState.AlphaFunc(GL_GEQUAL, 0);
+ glDepthFunc(GL_LEQUAL);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawWalls(GLPASS_TEXONLY);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_PLAIN].DrawFlats(GLPASS_TEXONLY);
+ gl_RenderState.AlphaFunc(GL_GREATER, gl_mask_threshold);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_MASKED].DrawWalls(GLPASS_TEXONLY);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_MASKED].DrawFlats(GLPASS_TEXONLY);
+
+ // fourth pass: additive lights
+ gl_RenderState.EnableFog(true);
+ gl_RenderState.BlendFunc(GL_ONE, GL_ONE);
+ glDepthFunc(GL_EQUAL);
+ if (gl_SetupLightTexture())
+ {
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawWalls(GLPASS_LIGHTTEX_ADDITIVE);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_MASKED].DrawWalls(GLPASS_LIGHTTEX_ADDITIVE);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_PLAIN].DrawFlats(GLPASS_LIGHTTEX_ADDITIVE);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_MASKED].DrawFlats(GLPASS_LIGHTTEX_ADDITIVE);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_FOG].DrawWalls(GLPASS_LIGHTTEX_FOGGY);
+ gl_drawinfo->dldrawlists[GLLDL_WALLS_FOGMASKED].DrawWalls(GLPASS_LIGHTTEX_FOGGY);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_FOG].DrawFlats(GLPASS_LIGHTTEX_FOGGY);
+ gl_drawinfo->dldrawlists[GLLDL_FLATS_FOGMASKED].DrawFlats(GLPASS_LIGHTTEX_FOGGY);
+ }
+ else gl_lights = false;
+
+ glDepthFunc(GL_LESS);
+ gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
+ gl_RenderState.EnableFog(true);
+ gl_RenderState.BlendFunc(GL_ONE, GL_ZERO);
+ glDepthMask(true);
+
+}
+
diff --git a/src/gl/data/gl_data.cpp b/src/gl/data/gl_data.cpp
new file mode 100644
index 000000000..eb676de75
--- /dev/null
+++ b/src/gl/data/gl_data.cpp
@@ -0,0 +1,574 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_data.cpp
+** Maintenance data for GL renderer (mostly to handle rendering hacks)
+**
+**/
+
+#include "gl/system/gl_system.h"
+
+#include "doomtype.h"
+#include "colormatcher.h"
+#include "i_system.h"
+#include "p_local.h"
+#include "p_tags.h"
+#include "p_lnspec.h"
+#include "c_dispatch.h"
+#include "r_sky.h"
+#include "sc_man.h"
+#include "w_wad.h"
+#include "gi.h"
+#include "g_level.h"
+#include "doomstat.h"
+#include "d_player.h"
+
+#include "gl/system/gl_interface.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/data/gl_data.h"
+#include "gl/dynlights/gl_dynlight.h"
+#include "gl/dynlights/gl_glow.h"
+#include "gl/models/gl_models.h"
+#include "gl/utility/gl_clock.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/gl_functions.h"
+
+GLRenderSettings glset;
+long gl_frameMS;
+long gl_frameCount;
+
+EXTERN_CVAR(Int, gl_lightmode)
+EXTERN_CVAR(Bool, gl_brightfog)
+EXTERN_CVAR(Bool, gl_lightadditivesurfaces)
+EXTERN_CVAR(Bool, gl_attenuate)
+
+
+CUSTOM_CVAR(Float, maxviewpitch, 90.f, CVAR_ARCHIVE|CVAR_SERVERINFO)
+{
+ if (self>90.f) self=90.f;
+ else if (self<-90.f) self=-90.f;
+ if (usergame)
+ {
+ // [SP] Update pitch limits to the netgame/gamesim.
+ players[consoleplayer].SendPitchLimits();
+ }
+}
+
+CUSTOM_CVAR(Bool, gl_notexturefill, false, 0)
+{
+ glset.notexturefill = self;
+}
+
+
+CUSTOM_CVAR(Bool, gl_nocoloredspritelighting, false, 0)
+{
+ glset.nocoloredspritelighting = self;
+}
+
+void gl_CreateSections();
+
+//-----------------------------------------------------------------------------
+//
+// Adjust sprite offsets for GL rendering (IWAD resources only)
+//
+//-----------------------------------------------------------------------------
+
+void AdjustSpriteOffsets()
+{
+ int lump, lastlump = 0;
+ while ((lump = Wads.FindLump("SPROFS", &lastlump, false)) != -1)
+ {
+ FScanner sc;
+ sc.OpenLumpNum(lump);
+ sc.SetCMode(true);
+ GLRenderer->FlushTextures();
+ int ofslumpno = Wads.GetLumpFile(lump);
+ while (sc.GetString())
+ {
+ int x,y;
+ bool iwadonly = false;
+ FTextureID texno = TexMan.CheckForTexture(sc.String, FTexture::TEX_Sprite);
+ sc.MustGetStringName(",");
+ sc.MustGetNumber();
+ x=sc.Number;
+ sc.MustGetStringName(",");
+ sc.MustGetNumber();
+ y=sc.Number;
+ if (sc.CheckString(","))
+ {
+ sc.MustGetString();
+ if (sc.Compare("iwad")) iwadonly = true;
+ }
+ if (texno.isValid())
+ {
+ FTexture * tex = TexMan[texno];
+
+ int lumpnum = tex->GetSourceLump();
+ // We only want to change texture offsets for sprites in the IWAD or the file this lump originated from.
+ if (lumpnum >= 0 && lumpnum < Wads.GetNumLumps())
+ {
+ int wadno = Wads.GetLumpFile(lumpnum);
+ if ((iwadonly && wadno==FWadCollection::IWAD_FILENUM) || (!iwadonly && wadno == ofslumpno))
+ {
+ tex->LeftOffset=x;
+ tex->TopOffset=y;
+ tex->KillNative();
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+// Normally this would be better placed in p_lnspec.cpp.
+// But I have accidentally overwritten that file several times
+// so I'd rather place it here.
+int LS_Sector_SetPlaneReflection (line_t *ln, AActor *it, bool backSide,
+ int arg0, int arg1, int arg2, int arg3, int arg4)
+{
+// Sector_SetPlaneReflection (tag, floor, ceiling)
+ int secnum;
+ FSectorTagIterator itr(arg0);
+
+ while ((secnum = itr.Next()) >= 0)
+ {
+ sector_t * s = §ors[secnum];
+ if (!s->floorplane.isSlope()) s->reflect[sector_t::floor] = arg1/255.f;
+ if (!s->ceilingplane.isSlope()) sectors[secnum].reflect[sector_t::ceiling] = arg2/255.f;
+ }
+
+ return true;
+}
+
+int LS_SetGlobalFogParameter (line_t *ln, AActor *it, bool backSide,
+ int arg0, int arg1, int arg2, int arg3, int arg4)
+{
+// SetGlobalFogParameter (type, value)
+ switch(arg0)
+ {
+ case 0:
+ fogdensity = arg1>>1;
+ return true;
+
+ case 1:
+ outsidefogdensity = arg1>>1;
+ return true;
+
+ case 2:
+ skyfog = arg1;
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+
+//==========================================================================
+//
+// Portal identifier lists
+//
+//==========================================================================
+
+
+//==========================================================================
+//
+// MAPINFO stuff
+//
+//==========================================================================
+
+struct FGLROptions : public FOptionalMapinfoData
+{
+ FGLROptions()
+ {
+ identifier = "gl_renderer";
+ fogdensity = 0;
+ outsidefogdensity = 0;
+ skyfog = 0;
+ brightfog = false;
+ lightmode = -1;
+ attenuate = -1;
+ nocoloredspritelighting = -1;
+ notexturefill = -1;
+ skyrotatevector = FVector3(0,0,1);
+ skyrotatevector2 = FVector3(0,0,1);
+ pixelstretch = 1.2f;
+ lightadditivesurfaces = false;
+ }
+ virtual FOptionalMapinfoData *Clone() const
+ {
+ FGLROptions *newopt = new FGLROptions;
+ newopt->identifier = identifier;
+ newopt->fogdensity = fogdensity;
+ newopt->outsidefogdensity = outsidefogdensity;
+ newopt->skyfog = skyfog;
+ newopt->lightmode = lightmode;
+ newopt->attenuate = attenuate;
+ newopt->nocoloredspritelighting = nocoloredspritelighting;
+ newopt->notexturefill = notexturefill;
+ newopt->skyrotatevector = skyrotatevector;
+ newopt->skyrotatevector2 = skyrotatevector2;
+ newopt->pixelstretch = pixelstretch;
+ newopt->lightadditivesurfaces = lightadditivesurfaces;
+ return newopt;
+ }
+ int fogdensity;
+ int outsidefogdensity;
+ int skyfog;
+ int lightmode;
+ int brightfog;
+ int8_t attenuate;
+ int8_t lightadditivesurfaces;
+ int8_t nocoloredspritelighting;
+ int8_t notexturefill;
+ FVector3 skyrotatevector;
+ FVector3 skyrotatevector2;
+ float pixelstretch;
+};
+
+DEFINE_MAP_OPTION(fogdensity, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ parse.ParseAssign();
+ parse.sc.MustGetNumber();
+ opt->fogdensity = parse.sc.Number;
+}
+
+DEFINE_MAP_OPTION(brightfog, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ parse.ParseAssign();
+ parse.sc.MustGetNumber();
+ opt->brightfog = parse.sc.Number;
+}
+
+DEFINE_MAP_OPTION(outsidefogdensity, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ parse.ParseAssign();
+ parse.sc.MustGetNumber();
+ opt->outsidefogdensity = parse.sc.Number;
+}
+
+DEFINE_MAP_OPTION(skyfog, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ parse.ParseAssign();
+ parse.sc.MustGetNumber();
+ opt->skyfog = parse.sc.Number;
+}
+
+DEFINE_MAP_OPTION(lightmode, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ parse.ParseAssign();
+ parse.sc.MustGetNumber();
+ opt->lightmode = BYTE(parse.sc.Number);
+}
+
+DEFINE_MAP_OPTION(nocoloredspritelighting, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ if (parse.CheckAssign())
+ {
+ parse.sc.MustGetNumber();
+ opt->nocoloredspritelighting = !!parse.sc.Number;
+ }
+ else
+ {
+ opt->nocoloredspritelighting = true;
+ }
+}
+
+DEFINE_MAP_OPTION(notexturefill, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ if (parse.CheckAssign())
+ {
+ parse.sc.MustGetNumber();
+ opt->notexturefill = !!parse.sc.Number;
+ }
+ else
+ {
+ opt->notexturefill = true;
+ }
+}
+
+DEFINE_MAP_OPTION(lightadditivesurfaces, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ if (parse.CheckAssign())
+ {
+ parse.sc.MustGetNumber();
+ opt->lightadditivesurfaces = !!parse.sc.Number;
+ }
+ else
+ {
+ opt->lightadditivesurfaces = true;
+ }
+}
+
+DEFINE_MAP_OPTION(attenuate, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+ if (parse.CheckAssign())
+ {
+ parse.sc.MustGetNumber();
+ opt->attenuate = !!parse.sc.Number;
+ }
+ else
+ {
+ opt->attenuate = true;
+ }
+}
+
+DEFINE_MAP_OPTION(skyrotate, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+
+ parse.ParseAssign();
+ parse.sc.MustGetFloat();
+ opt->skyrotatevector.X = (float)parse.sc.Float;
+ if (parse.format_type == FMapInfoParser::FMT_New) parse.sc.MustGetStringName(",");
+ parse.sc.MustGetFloat();
+ opt->skyrotatevector.Y = (float)parse.sc.Float;
+ if (parse.format_type == FMapInfoParser::FMT_New) parse.sc.MustGetStringName(",");
+ parse.sc.MustGetFloat();
+ opt->skyrotatevector.Z = (float)parse.sc.Float;
+ opt->skyrotatevector.MakeUnit();
+}
+
+DEFINE_MAP_OPTION(skyrotate2, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+
+ parse.ParseAssign();
+ parse.sc.MustGetFloat();
+ opt->skyrotatevector2.X = (float)parse.sc.Float;
+ if (parse.format_type == FMapInfoParser::FMT_New) parse.sc.MustGetStringName(",");
+ parse.sc.MustGetFloat();
+ opt->skyrotatevector2.Y = (float)parse.sc.Float;
+ if (parse.format_type == FMapInfoParser::FMT_New) parse.sc.MustGetStringName(",");
+ parse.sc.MustGetFloat();
+ opt->skyrotatevector2.Z = (float)parse.sc.Float;
+ opt->skyrotatevector2.MakeUnit();
+}
+
+DEFINE_MAP_OPTION(pixelratio, false)
+{
+ FGLROptions *opt = info->GetOptData("gl_renderer");
+
+ parse.ParseAssign();
+ parse.sc.MustGetFloat();
+ opt->pixelstretch = (float)parse.sc.Float;
+}
+
+bool IsLightmodeValid()
+{
+ return (glset.map_lightmode >= 0 && glset.map_lightmode <= 4) || glset.map_lightmode == 8;
+}
+
+static void ResetOpts()
+{
+ if (!IsLightmodeValid()) glset.lightmode = gl_lightmode;
+ else glset.lightmode = glset.map_lightmode;
+ if (glset.map_nocoloredspritelighting == -1) glset.nocoloredspritelighting = gl_nocoloredspritelighting;
+ else glset.nocoloredspritelighting = !!glset.map_nocoloredspritelighting;
+ if (glset.map_notexturefill == -1) glset.notexturefill = gl_notexturefill;
+ else glset.notexturefill = !!glset.map_notexturefill;
+ if (glset.map_brightfog == -1) glset.brightfog = gl_brightfog;
+ else glset.brightfog = !!glset.map_brightfog;
+ if (glset.map_lightadditivesurfaces == -1) glset.lightadditivesurfaces = gl_lightadditivesurfaces;
+ else glset.lightadditivesurfaces = !!glset.map_lightadditivesurfaces;
+ if (glset.map_attenuate == -1) glset.attenuate = gl_attenuate;
+ else glset.attenuate = !!glset.map_attenuate;
+}
+
+void InitGLRMapinfoData()
+{
+ FGLROptions *opt = level.info->GetOptData("gl_renderer", false);
+
+ if (opt != NULL)
+ {
+ gl_SetFogParams(opt->fogdensity, level.info->outsidefog, opt->outsidefogdensity, opt->skyfog);
+ glset.map_lightmode = opt->lightmode;
+ glset.map_lightadditivesurfaces = opt->lightadditivesurfaces;
+ glset.map_attenuate = opt->attenuate;
+ glset.map_brightfog = opt->brightfog;
+ glset.map_nocoloredspritelighting = opt->nocoloredspritelighting;
+ glset.map_notexturefill = opt->notexturefill;
+ glset.skyrotatevector = opt->skyrotatevector;
+ glset.skyrotatevector2 = opt->skyrotatevector2;
+ glset.pixelstretch = opt->pixelstretch;
+ }
+ else
+ {
+ gl_SetFogParams(0, level.info->outsidefog, 0, 0);
+ glset.map_lightmode = -1;
+ glset.map_lightadditivesurfaces = -1;
+ glset.map_brightfog = -1;
+ glset.map_attenuate = -1;
+ glset.map_nocoloredspritelighting = -1;
+ glset.map_notexturefill = -1;
+ glset.skyrotatevector = FVector3(0, 0, 1);
+ glset.skyrotatevector2 = FVector3(0, 0, 1);
+ glset.pixelstretch = 1.2f;
+ }
+ ResetOpts();
+}
+CCMD(gl_resetmap)
+{
+ ResetOpts();
+}
+
+
+//===========================================================================
+//
+// Gets the texture index for a sprite frame
+//
+//===========================================================================
+
+FTextureID gl_GetSpriteFrame(unsigned sprite, int frame, int rot, angle_t ang, bool *mirror)
+{
+ spritedef_t *sprdef = &sprites[sprite];
+ if (frame >= sprdef->numframes)
+ {
+ // If there are no frames at all for this sprite, don't draw it.
+ return FNullTextureID();
+ }
+ else
+ {
+ //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0];
+ // choose a different rotation based on player view
+ spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + frame];
+ if (rot==-1)
+ {
+ if (sprframe->Texture[0] == sprframe->Texture[1])
+ {
+ rot = (ang + (angle_t)(ANGLE_90/4)*9) >> 28;
+ }
+ else
+ {
+ rot = (ang + (angle_t)(ANGLE_90/4)*9-(angle_t)(ANGLE_180/16)) >> 28;
+ }
+ }
+ if (mirror) *mirror = !!(sprframe->Flip&(1<Texture[rot];
+ }
+}
+
+
+//==========================================================================
+//
+// Recalculate all heights affectting this vertex.
+//
+//==========================================================================
+void gl_RecalcVertexHeights(vertex_t * v)
+{
+ int i,j,k;
+ float height;
+
+ v->numheights=0;
+ for(i=0;inumsectors;i++)
+ {
+ for(j=0;j<2;j++)
+ {
+ if (j==0) height=v->sectors[i]->ceilingplane.ZatPoint(v);
+ else height=v->sectors[i]->floorplane.ZatPoint(v);
+
+ for(k=0;knumheights;k++)
+ {
+ if (height == v->heightlist[k]) break;
+ if (height < v->heightlist[k])
+ {
+ memmove(&v->heightlist[k+1], &v->heightlist[k], sizeof(float) * (v->numheights-k));
+ v->heightlist[k]=height;
+ v->numheights++;
+ break;
+ }
+ }
+ if (k==v->numheights) v->heightlist[v->numheights++]=height;
+ }
+ }
+ if (v->numheights<=2) v->numheights=0; // is not in need of any special attention
+ v->dirty = false;
+}
+
+
+
+
+void gl_InitData()
+{
+ AdjustSpriteOffsets();
+}
+
+//==========================================================================
+//
+// dumpgeometry
+//
+//==========================================================================
+
+CCMD(dumpgeometry)
+{
+ for(int i=0;isubsectorcount;j++)
+ {
+ subsector_t * sub = sector->subsectors[j];
+
+ Printf(PRINT_LOG, " Subsector %d - real sector = %d - %s\n", int(sub-subsectors), sub->sector->sectornum, sub->hacked&1? "hacked":"");
+ for(DWORD k=0;knumlines;k++)
+ {
+ seg_t * seg = sub->firstline + k;
+ if (seg->linedef)
+ {
+ Printf(PRINT_LOG, " (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, linedef %d, side %d",
+ seg->v1->fX(), seg->v1->fY(), seg->v2->fX(), seg->v2->fY(),
+ int(seg-segs), int(seg->linedef-lines), seg->sidedef != seg->linedef->sidedef[0]);
+ }
+ else
+ {
+ Printf(PRINT_LOG, " (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, miniseg",
+ seg->v1->fX(), seg->v1->fY(), seg->v2->fX(), seg->v2->fY(),
+ int(seg-segs));
+ }
+ if (seg->PartnerSeg)
+ {
+ subsector_t * sub2 = seg->PartnerSeg->Subsector;
+ Printf(PRINT_LOG, ", back sector = %d, real back sector = %d", sub2->render_sector->sectornum, seg->PartnerSeg->frontsector->sectornum);
+ }
+ else if (seg->backsector)
+ {
+ Printf(PRINT_LOG, ", back sector = %d (no partnerseg)", seg->backsector->sectornum);
+ }
+
+ Printf(PRINT_LOG, "\n");
+ }
+ }
+ }
+}
diff --git a/src/gl/data/gl_data.h b/src/gl/data/gl_data.h
new file mode 100644
index 000000000..bb57c9139
--- /dev/null
+++ b/src/gl/data/gl_data.h
@@ -0,0 +1,80 @@
+
+#ifndef __GLC_DATA_H
+#define __GLC_DATA_H
+
+#include "doomtype.h"
+#include "vectors.h"
+
+struct GLRenderSettings
+{
+
+ SBYTE lightmode;
+ bool nocoloredspritelighting;
+ bool notexturefill;
+ bool brightfog;
+ bool lightadditivesurfaces;
+ bool attenuate;
+
+ int8_t map_lightmode;
+ int8_t map_nocoloredspritelighting;
+ int8_t map_notexturefill;
+ int8_t map_brightfog;
+ int8_t map_lightadditivesurfaces;
+ int8_t map_attenuate;
+
+ FVector3 skyrotatevector;
+ FVector3 skyrotatevector2;
+
+ float pixelstretch;
+
+};
+
+extern GLRenderSettings glset;
+
+#include "r_defs.h"
+#include "a_sharedglobal.h"
+#include "c_cvars.h"
+
+extern int extralight;
+EXTERN_CVAR(Int, gl_weaponlight);
+
+inline int getExtraLight()
+{
+ return extralight * gl_weaponlight;
+}
+
+void gl_RecalcVertexHeights(vertex_t * v);
+FTextureID gl_GetSpriteFrame(unsigned sprite, int frame, int rot, angle_t ang, bool *mirror);
+
+class AStackPoint;
+struct GLSectorStackPortal;
+
+struct FPortal
+{
+ DVector2 mDisplacement;
+ int plane;
+ GLSectorStackPortal *glportal; // for quick access to the render data. This is only valid during BSP traversal!
+
+ GLSectorStackPortal *GetRenderState();
+};
+
+struct FGLLinePortal
+{
+ // defines the complete span of this portal, if this is of type PORTT_LINKED.
+ vertex_t *v1 = nullptr, *v2 = nullptr; // vertices, from v1 to v2
+ TArray lines;
+ int validcount = 0;
+};
+
+extern TArray portals;
+extern TArray linePortalToGL;
+
+extern TArray currentmapsection;
+
+void gl_InitPortals();
+void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, const DVector2 &displacement);
+void gl_InitData();
+
+extern long gl_frameMS;
+
+#endif
diff --git a/src/gl/data/gl_matrix.cpp b/src/gl/data/gl_matrix.cpp
new file mode 100644
index 000000000..115882857
--- /dev/null
+++ b/src/gl/data/gl_matrix.cpp
@@ -0,0 +1,486 @@
+/* --------------------------------------------------
+
+Lighthouse3D
+
+VSMatrix - Very Simple Matrix Library
+
+http://www.lighthouse3d.com/very-simple-libs
+
+This is a simplified version of VSMatrix that has been adjusted for GZDoom's needs.
+
+----------------------------------------------------*/
+
+#include
+#include "gl/system/gl_system.h"
+#include
+#include
+#include
+#include
+#include "doomtype.h"
+#include "gl/data/gl_matrix.h"
+
+static inline FLOATTYPE
+DegToRad(FLOATTYPE degrees)
+{
+ return (FLOATTYPE)(degrees * (M_PI / 180.0f));
+};
+
+// sets the square matrix mat to the identity matrix,
+// size refers to the number of rows (or columns)
+void
+VSMatrix::setIdentityMatrix( FLOATTYPE *mat, int size) {
+
+ // fill matrix with 0s
+ for (int i = 0; i < size * size; ++i)
+ mat[i] = 0.0f;
+
+ // fill diagonal with 1s
+ for (int i = 0; i < size; ++i)
+ mat[i + i * size] = 1.0f;
+}
+
+
+
+// gl LoadIdentity implementation
+void
+VSMatrix::loadIdentity()
+{
+ // fill matrix with 0s
+ for (int i = 0; i < 16; ++i)
+ mMatrix[i] = 0.0f;
+
+ // fill diagonal with 1s
+ for (int i = 0; i < 4; ++i)
+ mMatrix[i + i * 4] = 1.0f;
+}
+
+
+// gl MultMatrix implementation
+void
+VSMatrix::multMatrix(const FLOATTYPE *aMatrix)
+{
+
+ FLOATTYPE res[16];
+
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ res[j*4 + i] = 0.0f;
+ for (int k = 0; k < 4; ++k)
+ {
+ res[j*4 + i] += mMatrix[k*4 + i] * aMatrix[j*4 + k];
+ }
+ }
+ }
+ memcpy(mMatrix, res, 16 * sizeof(FLOATTYPE));
+}
+
+#ifdef USE_DOUBLE
+// gl MultMatrix implementation
+void
+VSMatrix::multMatrix(const float *aMatrix)
+{
+
+ FLOATTYPE res[16];
+
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ res[j * 4 + i] = 0.0f;
+ for (int k = 0; k < 4; ++k)
+ {
+ res[j*4 + i] += mMatrix[k*4 + i] * aMatrix[j*4 + k];
+ }
+ }
+ }
+ memcpy(mMatrix, res, 16 * sizeof(FLOATTYPE));
+}
+#endif
+
+
+
+// gl LoadMatrix implementation
+void
+VSMatrix::loadMatrix(const FLOATTYPE *aMatrix)
+{
+ memcpy(mMatrix, aMatrix, 16 * sizeof(FLOATTYPE));
+}
+
+#ifdef USE_DOUBLE
+// gl LoadMatrix implementation
+void
+VSMatrix::loadMatrix(const float *aMatrix)
+{
+ for (int i = 0; i < 16; ++i)
+ {
+ mMatrix[i] = aMatrix[i];
+ }
+}
+#endif
+
+
+// gl Translate implementation
+void
+VSMatrix::translate(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
+{
+ mMatrix[12] = mMatrix[0] * x + mMatrix[4] * y + mMatrix[8] * z + mMatrix[12];
+ mMatrix[13] = mMatrix[1] * x + mMatrix[5] * y + mMatrix[9] * z + mMatrix[13];
+ mMatrix[14] = mMatrix[2] * x + mMatrix[6] * y + mMatrix[10] * z + mMatrix[14];
+}
+
+
+// gl Scale implementation
+void
+VSMatrix::scale(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
+{
+ mMatrix[0] *= x; mMatrix[1] *= x; mMatrix[2] *= x; mMatrix[3] *= x;
+ mMatrix[4] *= y; mMatrix[5] *= y; mMatrix[6] *= y; mMatrix[7] *= y;
+ mMatrix[8] *= z; mMatrix[9] *= z; mMatrix[10] *= z; mMatrix[11] *= z;
+}
+
+
+// gl Rotate implementation
+void
+VSMatrix::rotate(FLOATTYPE angle, FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
+{
+ FLOATTYPE mat[16];
+ FLOATTYPE v[3];
+
+ v[0] = x;
+ v[1] = y;
+ v[2] = z;
+
+ FLOATTYPE radAngle = DegToRad(angle);
+ FLOATTYPE co = cos(radAngle);
+ FLOATTYPE si = sin(radAngle);
+ normalize(v);
+ FLOATTYPE x2 = v[0]*v[0];
+ FLOATTYPE y2 = v[1]*v[1];
+ FLOATTYPE z2 = v[2]*v[2];
+
+// mat[0] = x2 + (y2 + z2) * co;
+ mat[0] = co + x2 * (1 - co);// + (y2 + z2) * co;
+ mat[4] = v[0] * v[1] * (1 - co) - v[2] * si;
+ mat[8] = v[0] * v[2] * (1 - co) + v[1] * si;
+ mat[12]= 0.0f;
+
+ mat[1] = v[0] * v[1] * (1 - co) + v[2] * si;
+// mat[5] = y2 + (x2 + z2) * co;
+ mat[5] = co + y2 * (1 - co);
+ mat[9] = v[1] * v[2] * (1 - co) - v[0] * si;
+ mat[13]= 0.0f;
+
+ mat[2] = v[0] * v[2] * (1 - co) - v[1] * si;
+ mat[6] = v[1] * v[2] * (1 - co) + v[0] * si;
+// mat[10]= z2 + (x2 + y2) * co;
+ mat[10]= co + z2 * (1 - co);
+ mat[14]= 0.0f;
+
+ mat[3] = 0.0f;
+ mat[7] = 0.0f;
+ mat[11]= 0.0f;
+ mat[15]= 1.0f;
+
+ multMatrix(mat);
+}
+
+
+// gluLookAt implementation
+void
+VSMatrix::lookAt(FLOATTYPE xPos, FLOATTYPE yPos, FLOATTYPE zPos,
+ FLOATTYPE xLook, FLOATTYPE yLook, FLOATTYPE zLook,
+ FLOATTYPE xUp, FLOATTYPE yUp, FLOATTYPE zUp)
+{
+ FLOATTYPE dir[3], right[3], up[3];
+
+ up[0] = xUp; up[1] = yUp; up[2] = zUp;
+
+ dir[0] = (xLook - xPos);
+ dir[1] = (yLook - yPos);
+ dir[2] = (zLook - zPos);
+ normalize(dir);
+
+ crossProduct(dir,up,right);
+ normalize(right);
+
+ crossProduct(right,dir,up);
+ normalize(up);
+
+ FLOATTYPE m1[16],m2[16];
+
+ m1[0] = right[0];
+ m1[4] = right[1];
+ m1[8] = right[2];
+ m1[12] = 0.0f;
+
+ m1[1] = up[0];
+ m1[5] = up[1];
+ m1[9] = up[2];
+ m1[13] = 0.0f;
+
+ m1[2] = -dir[0];
+ m1[6] = -dir[1];
+ m1[10] = -dir[2];
+ m1[14] = 0.0f;
+
+ m1[3] = 0.0f;
+ m1[7] = 0.0f;
+ m1[11] = 0.0f;
+ m1[15] = 1.0f;
+
+ setIdentityMatrix(m2,4);
+ m2[12] = -xPos;
+ m2[13] = -yPos;
+ m2[14] = -zPos;
+
+ multMatrix(m1);
+ multMatrix(m2);
+}
+
+
+// gluPerspective implementation
+void
+VSMatrix::perspective(FLOATTYPE fov, FLOATTYPE ratio, FLOATTYPE nearp, FLOATTYPE farp)
+{
+ FLOATTYPE f = 1.0f / tan (fov * (M_PI / 360.0f));
+
+ loadIdentity();
+ mMatrix[0] = f / ratio;
+ mMatrix[1 * 4 + 1] = f;
+ mMatrix[2 * 4 + 2] = (farp + nearp) / (nearp - farp);
+ mMatrix[3 * 4 + 2] = (2.0f * farp * nearp) / (nearp - farp);
+ mMatrix[2 * 4 + 3] = -1.0f;
+ mMatrix[3 * 4 + 3] = 0.0f;
+}
+
+
+// gl Ortho implementation
+void
+VSMatrix::ortho(FLOATTYPE left, FLOATTYPE right,
+ FLOATTYPE bottom, FLOATTYPE top,
+ FLOATTYPE nearp, FLOATTYPE farp)
+{
+ loadIdentity();
+
+ mMatrix[0 * 4 + 0] = 2 / (right - left);
+ mMatrix[1 * 4 + 1] = 2 / (top - bottom);
+ mMatrix[2 * 4 + 2] = -2 / (farp - nearp);
+ mMatrix[3 * 4 + 0] = -(right + left) / (right - left);
+ mMatrix[3 * 4 + 1] = -(top + bottom) / (top - bottom);
+ mMatrix[3 * 4 + 2] = -(farp + nearp) / (farp - nearp);
+}
+
+
+// gl Frustum implementation
+void
+VSMatrix::frustum(FLOATTYPE left, FLOATTYPE right,
+ FLOATTYPE bottom, FLOATTYPE top,
+ FLOATTYPE nearp, FLOATTYPE farp)
+{
+ FLOATTYPE m[16];
+
+ setIdentityMatrix(m,4);
+
+ m[0 * 4 + 0] = 2 * nearp / (right-left);
+ m[1 * 4 + 1] = 2 * nearp / (top - bottom);
+ m[2 * 4 + 0] = (right + left) / (right - left);
+ m[2 * 4 + 1] = (top + bottom) / (top - bottom);
+ m[2 * 4 + 2] = - (farp + nearp) / (farp - nearp);
+ m[2 * 4 + 3] = -1.0f;
+ m[3 * 4 + 2] = - 2 * farp * nearp / (farp-nearp);
+ m[3 * 4 + 3] = 0.0f;
+
+ multMatrix(m);
+}
+
+
+/*
+// returns a pointer to the requested matrix
+FLOATTYPE *
+VSMatrix::get(MatrixTypes aType)
+{
+ return mMatrix[aType];
+}
+*/
+
+
+/* -----------------------------------------------------
+ SEND MATRICES TO OPENGL
+------------------------------------------------------*/
+
+
+
+
+// universal
+void
+VSMatrix::matrixToGL(int loc)
+{
+#ifdef USE_DOUBLE
+ float copyto[16];
+ copy(copyto);
+ glUniformMatrix4fv(loc, 1, false, copyto);
+#else
+ glUniformMatrix4fv(loc, 1, false, mMatrix);
+#endif
+}
+
+// -----------------------------------------------------
+// AUX functions
+// -----------------------------------------------------
+
+
+// Compute res = M * point
+void
+VSMatrix::multMatrixPoint(const FLOATTYPE *point, FLOATTYPE *res)
+{
+
+ for (int i = 0; i < 4; ++i)
+ {
+
+ res[i] = 0.0f;
+
+ for (int j = 0; j < 4; j++) {
+
+ res[i] += point[j] * mMatrix[j*4 + i];
+ }
+ }
+}
+
+// res = a cross b;
+void
+VSMatrix::crossProduct(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
+
+ res[0] = a[1] * b[2] - b[1] * a[2];
+ res[1] = a[2] * b[0] - b[2] * a[0];
+ res[2] = a[0] * b[1] - b[0] * a[1];
+}
+
+
+// returns a . b
+FLOATTYPE
+VSMatrix::dotProduct(const FLOATTYPE *a, const FLOATTYPE *b) {
+
+ FLOATTYPE res = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+
+ return res;
+}
+
+
+// Normalize a vec3
+void
+VSMatrix::normalize(FLOATTYPE *a) {
+
+ FLOATTYPE mag = sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
+
+ a[0] /= mag;
+ a[1] /= mag;
+ a[2] /= mag;
+}
+
+
+// res = b - a
+void
+VSMatrix::subtract(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
+
+ res[0] = b[0] - a[0];
+ res[1] = b[1] - a[1];
+ res[2] = b[2] - a[2];
+}
+
+
+// res = a + b
+void
+VSMatrix::add(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
+
+ res[0] = b[0] + a[0];
+ res[1] = b[1] + a[1];
+ res[2] = b[2] + a[2];
+}
+
+
+// returns |a|
+FLOATTYPE
+VSMatrix::length(const FLOATTYPE *a) {
+
+ return(sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]));
+
+}
+
+
+static inline int
+M3(int i, int j)
+{
+ return (i*3+j);
+};
+
+
+
+// computes the derived normal matrix for the view matrix
+void
+VSMatrix::computeNormalMatrix(const FLOATTYPE *aMatrix)
+{
+
+ double mMat3x3[9];
+
+ mMat3x3[0] = aMatrix[0];
+ mMat3x3[1] = aMatrix[1];
+ mMat3x3[2] = aMatrix[2];
+
+ mMat3x3[3] = aMatrix[4];
+ mMat3x3[4] = aMatrix[5];
+ mMat3x3[5] = aMatrix[6];
+
+ mMat3x3[6] = aMatrix[8];
+ mMat3x3[7] = aMatrix[9];
+ mMat3x3[8] = aMatrix[10];
+
+ double det, invDet;
+
+ det = mMat3x3[0] * (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) +
+ mMat3x3[1] * (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) +
+ mMat3x3[2] * (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]);
+
+ invDet = 1.0/det;
+
+ mMatrix[0] = (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) * invDet;
+ mMatrix[1] = (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) * invDet;
+ mMatrix[2] = (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]) * invDet;
+ mMatrix[3] = 0.0f;
+ mMatrix[4] = (mMat3x3[2] * mMat3x3[7] - mMat3x3[1] * mMat3x3[8]) * invDet;
+ mMatrix[5] = (mMat3x3[0] * mMat3x3[8] - mMat3x3[2] * mMat3x3[6]) * invDet;
+ mMatrix[6] = (mMat3x3[1] * mMat3x3[6] - mMat3x3[7] * mMat3x3[0]) * invDet;
+ mMatrix[7] = 0.0f;
+ mMatrix[8] = (mMat3x3[1] * mMat3x3[5] - mMat3x3[4] * mMat3x3[2]) * invDet;
+ mMatrix[9] = (mMat3x3[2] * mMat3x3[3] - mMat3x3[0] * mMat3x3[5]) * invDet;
+ mMatrix[10] =(mMat3x3[0] * mMat3x3[4] - mMat3x3[3] * mMat3x3[1]) * invDet;
+ mMatrix[11] = 0.0;
+ mMatrix[12] = 0.0;
+ mMatrix[13] = 0.0;
+ mMatrix[14] = 0.0;
+ mMatrix[15] = 1.0;
+
+}
+
+
+// aux function resMat = resMat * aMatrix
+void
+VSMatrix::multMatrix(FLOATTYPE *resMat, const FLOATTYPE *aMatrix)
+{
+
+ FLOATTYPE res[16];
+
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ res[j*4 + i] = 0.0f;
+ for (int k = 0; k < 4; ++k)
+ {
+ res[j*4 + i] += resMat[k*4 + i] * aMatrix[j*4 + k];
+ }
+ }
+ }
+ memcpy(resMat, res, 16 * sizeof(FLOATTYPE));
+}
diff --git a/src/gl/data/gl_matrix.h b/src/gl/data/gl_matrix.h
new file mode 100644
index 000000000..3ec1f5ff4
--- /dev/null
+++ b/src/gl/data/gl_matrix.h
@@ -0,0 +1,115 @@
+
+// Matrix class based on code from VSML:
+
+/** ----------------------------------------------------------
+ * \class VSMathLib
+ *
+ * Lighthouse3D
+ *
+ * VSMathLib - Very Simple Matrix Library
+ *
+ * Full documentation at
+ * http://www.lighthouse3d.com/very-simple-libs
+ *
+ * This class aims at easing geometric transforms, camera
+ * placement and projection definition for programmers
+ * working with OpenGL core versions.
+ *
+ *
+ ---------------------------------------------------------------*/
+#ifndef __VSMatrix__
+#define __VSMatrix__
+
+#include
+
+#ifdef USE_DOUBLE
+typedef double FLOATTYPE;
+#else
+typedef float FLOATTYPE;
+#endif
+
+class VSMatrix {
+
+ public:
+
+ VSMatrix()
+ {
+ }
+
+ VSMatrix(int)
+ {
+ loadIdentity();
+ }
+
+ void translate(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
+ void scale(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
+ void rotate(FLOATTYPE angle, FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
+ void loadIdentity();
+#ifdef USE_DOUBLE
+ void multMatrix(const float *aMatrix);
+#endif
+ void multVector(FLOATTYPE *aVector);
+ void multMatrix(const FLOATTYPE *aMatrix);
+ void multMatrix(const VSMatrix &aMatrix)
+ {
+ multMatrix(aMatrix.mMatrix);
+ }
+ void loadMatrix(const FLOATTYPE *aMatrix);
+#ifdef USE_DOUBLE
+ void loadMatrix(const float *aMatrix);
+#endif
+ void lookAt(FLOATTYPE xPos, FLOATTYPE yPos, FLOATTYPE zPos, FLOATTYPE xLook, FLOATTYPE yLook, FLOATTYPE zLook, FLOATTYPE xUp, FLOATTYPE yUp, FLOATTYPE zUp);
+ void perspective(FLOATTYPE fov, FLOATTYPE ratio, FLOATTYPE nearp, FLOATTYPE farp);
+ void ortho(FLOATTYPE left, FLOATTYPE right, FLOATTYPE bottom, FLOATTYPE top, FLOATTYPE nearp=-1.0f, FLOATTYPE farp=1.0f);
+ void frustum(FLOATTYPE left, FLOATTYPE right, FLOATTYPE bottom, FLOATTYPE top, FLOATTYPE nearp, FLOATTYPE farp);
+ void copy(FLOATTYPE * pDest)
+ {
+ memcpy(pDest, mMatrix, 16 * sizeof(FLOATTYPE));
+ }
+
+#ifdef USE_DOUBLE
+ void copy(float * pDest)
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ pDest[i] = (float)mMatrix[i];
+ }
+ }
+#endif
+
+ const FLOATTYPE *get() const
+ {
+ return mMatrix;
+ }
+
+ void matrixToGL(int location);
+ void multMatrixPoint(const FLOATTYPE *point, FLOATTYPE *res);
+
+#ifdef USE_DOUBLE
+ void computeNormalMatrix(const float *aMatrix);
+#endif
+ void computeNormalMatrix(const FLOATTYPE *aMatrix);
+ void computeNormalMatrix(const VSMatrix &aMatrix)
+ {
+ computeNormalMatrix(aMatrix.mMatrix);
+ }
+ bool inverseMatrix(VSMatrix &result);
+ void transpose();
+
+ protected:
+ static void crossProduct(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
+ static FLOATTYPE dotProduct(const FLOATTYPE *a, const FLOATTYPE * b);
+ static void normalize(FLOATTYPE *a);
+ static void subtract(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
+ static void add(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
+ static FLOATTYPE length(const FLOATTYPE *a);
+ static void multMatrix(FLOATTYPE *resMatrix, const FLOATTYPE *aMatrix);
+
+ static void setIdentityMatrix(FLOATTYPE *mat, int size = 4);
+
+ /// The storage for matrices
+ FLOATTYPE mMatrix[16];
+
+};
+
+#endif
\ No newline at end of file
diff --git a/src/gl/data/gl_portaldata.cpp b/src/gl/data/gl_portaldata.cpp
new file mode 100644
index 000000000..a7e140449
--- /dev/null
+++ b/src/gl/data/gl_portaldata.cpp
@@ -0,0 +1,522 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_setup.cpp
+** Initializes the data structures required by the GL renderer to handle
+** a level
+**
+**/
+
+#include "doomtype.h"
+#include "colormatcher.h"
+#include "i_system.h"
+#include "p_local.h"
+#include "p_lnspec.h"
+#include "c_dispatch.h"
+#include "r_sky.h"
+#include "sc_man.h"
+#include "w_wad.h"
+#include "gi.h"
+#include "g_level.h"
+#include "a_sharedglobal.h"
+
+#include "gl/renderer/gl_renderer.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/scene/gl_clipper.h"
+#include "gl/scene/gl_portal.h"
+#include "gl/dynlights/gl_dynlight.h"
+#include "gl/dynlights/gl_glow.h"
+#include "gl/utility/gl_clock.h"
+#include "gl/gl_functions.h"
+
+struct FPortalID
+{
+ DVector2 mDisplacement;
+
+ // for the hash code
+ operator intptr_t() const { return (FLOAT2FIXED(mDisplacement.X) >> 8) + (FLOAT2FIXED(mDisplacement.Y) << 8); }
+ bool operator != (const FPortalID &other) const
+ {
+ return mDisplacement != other.mDisplacement;
+ }
+};
+
+struct FPortalSector
+{
+ sector_t *mSub;
+ int mPlane;
+};
+
+typedef TArray FPortalSectors;
+
+typedef TMap FPortalMap;
+
+TArray portals;
+TArray linePortalToGL;
+TArray glLinePortals;
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+GLSectorStackPortal *FPortal::GetRenderState()
+{
+ if (glportal == NULL) glportal = new GLSectorStackPortal(this);
+ return glportal;
+}
+
+//==========================================================================
+//
+// this is left as fixed_t because the nodes also are, it makes no sense
+// to convert this without converting the nodes as well.
+//
+//==========================================================================
+
+struct FCoverageVertex
+{
+ fixed_t x, y;
+
+ bool operator !=(FCoverageVertex &other)
+ {
+ return x != other.x || y != other.y;
+ }
+};
+
+struct FCoverageLine
+{
+ FCoverageVertex v[2];
+};
+
+struct FCoverageBuilder
+{
+ subsector_t *target;
+ TArray collect;
+ FCoverageVertex center;
+
+ //==========================================================================
+ //
+ //
+ //
+ //==========================================================================
+
+ FCoverageBuilder(subsector_t *sub)
+ {
+ target = sub;
+ }
+
+ //==========================================================================
+ //
+ // GetIntersection
+ //
+ // adapted from P_InterceptVector
+ //
+ //==========================================================================
+
+ bool GetIntersection(FCoverageVertex *v1, FCoverageVertex *v2, node_t *bsp, FCoverageVertex *v)
+ {
+ double frac;
+ double num;
+ double den;
+
+ double v2x = (double)v1->x;
+ double v2y = (double)v1->y;
+ double v2dx = (double)(v2->x - v1->x);
+ double v2dy = (double)(v2->y - v1->y);
+ double v1x = (double)bsp->x;
+ double v1y = (double)bsp->y;
+ double v1dx = (double)bsp->dx;
+ double v1dy = (double)bsp->dy;
+
+ den = v1dy*v2dx - v1dx*v2dy;
+
+ if (den == 0)
+ return false; // parallel
+
+ num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx;
+ frac = num / den;
+
+ if (frac < 0. || frac > 1.) return false;
+
+ v->x = xs_RoundToInt(v2x + frac * v2dx);
+ v->y = xs_RoundToInt(v2y + frac * v2dy);
+ return true;
+ }
+
+ //==========================================================================
+ //
+ //
+ //
+ //==========================================================================
+
+ double PartitionDistance(FCoverageVertex *vt, node_t *node)
+ {
+ return fabs(double(-node->dy) * (vt->x - node->x) + double(node->dx) * (vt->y - node->y)) / (node->len * 65536.);
+ }
+
+ //==========================================================================
+ //
+ //
+ //
+ //==========================================================================
+
+ int PointOnSide(FCoverageVertex *vt, node_t *node)
+ {
+ return R_PointOnSide(vt->x, vt->y, node);
+ }
+
+ //==========================================================================
+ //
+ // adapted from polyobject splitter
+ //
+ //==========================================================================
+
+ void CollectNode(void *node, TArray &shape)
+ {
+ static TArray lists[2];
+ const double COVERAGE_EPSILON = 6.; // same epsilon as the node builder
+
+ if (!((size_t)node & 1)) // Keep going until found a subsector
+ {
+ node_t *bsp = (node_t *)node;
+
+ int centerside = R_PointOnSide(center.x, center.y, bsp);
+
+ lists[0].Clear();
+ lists[1].Clear();
+ for(unsigned i=0;ichildren[0], shape);
+ }
+ else if (lists[0].Size() == 0)
+ {
+ CollectNode(bsp->children[1], shape);
+ }
+ else
+ {
+ // copy the static arrays into local ones
+ TArray locallists[2];
+
+ for(int l=0;l<2;l++)
+ {
+ for (unsigned i=0;ichildren[0], locallists[0]);
+ CollectNode(bsp->children[1], locallists[1]);
+ }
+ }
+ else
+ {
+ // we reached a subsector so we can link the node with this subsector
+ subsector_t *sub = (subsector_t *)((BYTE *)node - 1);
+ collect.Push(int(sub-subsectors));
+ }
+ }
+};
+
+//==========================================================================
+//
+// Calculate portal coverage for a single subsector
+//
+//==========================================================================
+
+void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, const DVector2 &displacement)
+{
+ TArray shape;
+ double centerx=0, centery=0;
+
+ shape.Resize(subsector->numlines);
+ for(unsigned i=0; inumlines; i++)
+ {
+ centerx += (shape[i].x = FLOAT2FIXED(subsector->firstline[i].v1->fX() + displacement.X));
+ centery += (shape[i].y = FLOAT2FIXED(subsector->firstline[i].v1->fY() + displacement.Y));
+ }
+
+ FCoverageBuilder build(subsector);
+ build.center.x = xs_CRoundToInt(centerx / subsector->numlines);
+ build.center.y = xs_CRoundToInt(centery / subsector->numlines);
+
+ build.CollectNode(nodes + numnodes - 1, shape);
+ coverage->subsectors = new DWORD[build.collect.Size()];
+ coverage->sscount = build.collect.Size();
+ memcpy(coverage->subsectors, &build.collect[0], build.collect.Size() * sizeof(DWORD));
+}
+
+//==========================================================================
+//
+// portal initialization
+//
+//==========================================================================
+
+static void CollectPortalSectors(FPortalMap &collection)
+{
+ for (int i=0;iGetPortalType(j);
+ if (ptype== PORTS_STACKEDSECTORTHING || ptype == PORTS_PORTAL || ptype == PORTS_LINKEDPORTAL) // only offset-displacing portal types
+ {
+ FPortalID id = { sec->GetPortalDisplacement(j) };
+
+ FPortalSectors &sss = collection[id];
+ FPortalSector ss = { sec, j };
+ sss.Push(ss);
+ }
+ }
+ }
+}
+
+void gl_InitPortals()
+{
+ FPortalMap collection;
+
+ if (numnodes == 0) return;
+
+ for(int i=0;idx);
+ double fdy = FIXED2DBL(no->dy);
+ no->len = (float)sqrt(fdx * fdx + fdy * fdy);
+ }
+
+ CollectPortalSectors(collection);
+ portals.Clear();
+
+ FPortalMap::Iterator it(collection);
+ FPortalMap::Pair *pair;
+ int c = 0;
+ int planeflags = 0;
+ while (it.NextPair(pair))
+ {
+ for(unsigned i=0;iValue.Size(); i++)
+ {
+ if (pair->Value[i].mPlane == sector_t::floor) planeflags |= 1;
+ else if (pair->Value[i].mPlane == sector_t::ceiling) planeflags |= 2;
+ }
+ for (int i=1;i<=2;i<<=1)
+ {
+ // add separate portals for floor and ceiling.
+ if (planeflags & i)
+ {
+ FPortal *portal = new FPortal;
+ portal->mDisplacement = pair->Key.mDisplacement;
+ portal->plane = (i==1? sector_t::floor : sector_t::ceiling); /**/
+ portal->glportal = NULL;
+ portals.Push(portal);
+ for(unsigned j=0;jValue.Size(); j++)
+ {
+ sector_t *sec = pair->Value[j].mSub;
+ int plane = pair->Value[j].mPlane;
+ if (portal->plane == plane)
+ {
+ for(int k=0;ksubsectorcount; k++)
+ {
+ subsector_t *sub = sec->subsectors[k];
+ gl_BuildPortalCoverage(&sub->portalcoverage[plane], sub, pair->Key.mDisplacement);
+ }
+ sec->portals[plane] = portal;
+ }
+ }
+ }
+ }
+ }
+
+ // Now group the line portals (each group must be a continuous set of colinear linedefs with no gaps)
+ glLinePortals.Clear();
+ linePortalToGL.Clear();
+ TArray tempindex;
+
+ tempindex.Reserve(linePortals.Size());
+ memset(&tempindex[0], -1, linePortals.Size() * sizeof(int));
+
+ for (unsigned i = 0; i < linePortals.Size(); i++)
+ {
+ auto port = linePortals[i];
+ bool gotsome;
+
+ if (tempindex[i] == -1)
+ {
+ tempindex[i] = glLinePortals.Size();
+ line_t *pSrcLine = linePortals[i].mOrigin;
+ line_t *pLine = linePortals[i].mDestination;
+ FGLLinePortal &glport = glLinePortals[glLinePortals.Reserve(1)];
+ glport.lines.Push(&linePortals[i]);
+
+ // We cannot do this grouping for non-linked portals because they can be changed at run time.
+ if (linePortals[i].mType == PORTT_LINKED && pLine != nullptr)
+ {
+ glport.v1 = pLine->v1;
+ glport.v2 = pLine->v2;
+ do
+ {
+ // now collect all other colinear lines connected to this one. We run this loop as long as it still finds a match
+ gotsome = false;
+ for (unsigned j = 0; j < linePortals.Size(); j++)
+ {
+ if (tempindex[j] == -1)
+ {
+ line_t *pSrcLine2 = linePortals[j].mOrigin;
+ line_t *pLine2 = linePortals[j].mDestination;
+ // angular precision is intentionally reduced to 32 bit BAM to account for precision problems (otherwise many not perfectly horizontal or vertical portals aren't found here.)
+ unsigned srcang = pSrcLine->Delta().Angle().BAMs();
+ unsigned dstang = pLine->Delta().Angle().BAMs();
+ if ((pSrcLine->v2 == pSrcLine2->v1 && pLine->v1 == pLine2->v2) ||
+ (pSrcLine->v1 == pSrcLine2->v2 && pLine->v2 == pLine2->v1))
+ {
+ // The line connects, now check the translation
+ unsigned srcang2 = pSrcLine2->Delta().Angle().BAMs();
+ unsigned dstang2 = pLine2->Delta().Angle().BAMs();
+ if (srcang == srcang2 && dstang == dstang2)
+ {
+ // The lines connect and both source and destination are colinear, so this is a match
+ gotsome = true;
+ tempindex[j] = tempindex[i];
+ if (pLine->v1 == pLine2->v2) glport.v1 = pLine2->v1;
+ else glport.v2 = pLine2->v2;
+ glport.lines.Push(&linePortals[j]);
+ }
+ }
+ }
+ }
+ } while (gotsome);
+ }
+ }
+ }
+ linePortalToGL.Resize(linePortals.Size());
+ for (unsigned i = 0; i < linePortals.Size(); i++)
+ {
+ linePortalToGL[i] = &glLinePortals[tempindex[i]];
+ /*
+ Printf("portal at line %d translates to GL portal %d, range = %f,%f to %f,%f\n",
+ int(linePortals[i].mOrigin - lines), tempindex[i], linePortalToGL[i]->v1->fixX() / 65536., linePortalToGL[i]->v1->fixY() / 65536., linePortalToGL[i]->v2->fixX() / 65536., linePortalToGL[i]->v2->fixY() / 65536.);
+ */
+ }
+}
+
+CCMD(dumpportals)
+{
+ for(unsigned i=0;imDisplacement.X;
+ double ydisp = portals[i]->mDisplacement.Y;
+ Printf(PRINT_LOG, "Portal #%d, %s, displacement = (%f,%f)\n", i, portals[i]->plane==0? "floor":"ceiling",
+ xdisp, ydisp);
+ Printf(PRINT_LOG, "Coverage:\n");
+ for(int j=0;jrender_sector->GetGLPortal(portals[i]->plane);
+ if (port == portals[i])
+ {
+ Printf(PRINT_LOG, "\tSubsector %d (%d):\n\t\t", j, sub->render_sector->sectornum);
+ for(unsigned k = 0;k< sub->numlines; k++)
+ {
+ Printf(PRINT_LOG, "(%.3f,%.3f), ", sub->firstline[k].v1->fX() + xdisp, sub->firstline[k].v1->fY() + ydisp);
+ }
+ Printf(PRINT_LOG, "\n\t\tCovered by subsectors:\n");
+ FPortalCoverage *cov = &sub->portalcoverage[portals[i]->plane];
+ for(int l = 0;l< cov->sscount; l++)
+ {
+ subsector_t *csub = &subsectors[cov->subsectors[l]];
+ Printf(PRINT_LOG, "\t\t\t%5d (%4d): ", cov->subsectors[l], csub->render_sector->sectornum);
+ for(unsigned m = 0;m< csub->numlines; m++)
+ {
+ Printf(PRINT_LOG, "(%.3f,%.3f), ", csub->firstline[m].v1->fX(), csub->firstline[m].v1->fY());
+ }
+ Printf(PRINT_LOG, "\n");
+ }
+ }
+ }
+ }
+}
diff --git a/src/gl/data/gl_setup.cpp b/src/gl/data/gl_setup.cpp
new file mode 100644
index 000000000..37f5c370e
--- /dev/null
+++ b/src/gl/data/gl_setup.cpp
@@ -0,0 +1,731 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_setup.cpp
+** Initializes the data structures required by the GL renderer to handle
+** a level
+**
+**/
+
+#include "gl/system/gl_system.h"
+#include "doomtype.h"
+#include "colormatcher.h"
+#include "i_system.h"
+#include "p_local.h"
+#include "p_spec.h"
+#include "p_lnspec.h"
+#include "c_dispatch.h"
+#include "r_sky.h"
+#include "sc_man.h"
+#include "w_wad.h"
+#include "gi.h"
+#include "p_setup.h"
+#include "g_level.h"
+
+#include "gl/renderer/gl_renderer.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/dynlights/gl_dynlight.h"
+#include "gl/dynlights/gl_glow.h"
+#include "gl/utility/gl_clock.h"
+#include "gl/gl_functions.h"
+
+void InitGLRMapinfoData();
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+static TArray MapSectionCollector;
+
+static void DoSetMapSection(subsector_t *sub, int num)
+{
+ MapSectionCollector.Resize(1);
+ MapSectionCollector[0] = sub;
+ sub->mapsection = num;
+ for (unsigned a = 0; a < MapSectionCollector.Size(); a++)
+ {
+ sub = MapSectionCollector[a];
+ for (DWORD i = 0; i < sub->numlines; i++)
+ {
+ seg_t * seg = sub->firstline + i;
+
+ if (seg->PartnerSeg)
+ {
+ subsector_t * sub2 = seg->PartnerSeg->Subsector;
+
+ if (sub2->mapsection != num)
+ {
+ assert(sub2->mapsection == 0);
+ sub2->mapsection = num;
+ MapSectionCollector.Push(sub2);
+ }
+ }
+ }
+ }
+ MapSectionCollector.Clear();
+}
+
+//==========================================================================
+//
+// Merge sections. This is needed in case the map contains errors
+// like overlapping lines resulting in abnormal subsectors.
+//
+// This function ensures that any vertex position can only be in one section.
+//
+//==========================================================================
+
+struct cvertex_t
+{
+ double X, Y;
+
+ operator int() const { return xs_FloorToInt(X) + 65536 * xs_FloorToInt(Y); }
+ bool operator!= (const cvertex_t &other) const { return fabs(X - other.X) >= EQUAL_EPSILON || fabs(Y - other.Y) >= EQUAL_EPSILON; }
+ cvertex_t& operator =(const vertex_t *v) { X = v->fX(); Y = v->fY(); return *this; }
+};
+
+typedef TMap FSectionVertexMap;
+
+static int MergeMapSections(int num)
+{
+ FSectionVertexMap vmap;
+ FSectionVertexMap::Pair *pair;
+ TArray sectmap;
+ TArray sectvalid;
+ sectmap.Resize(num);
+ sectvalid.Resize(num);
+ for(int i=0;iSubsector->mapsection;
+ for(int j=0;j<2;j++)
+ {
+ vt = j==0? seg->v1:seg->v2;
+ vmap[vt] = section;
+ }
+ }
+
+ // second step: Check if any seg references more than one mapsection, either by subsector or by vertex
+ for(DWORD i=0;i<(DWORD)numsegs;i++)
+ {
+ seg_t * seg = &segs[i];
+ int section = seg->Subsector->mapsection;
+ for(int j=0;j<2;j++)
+ {
+ vt = j==0? seg->v1:seg->v2;
+ int vsection = vmap[vt];
+
+ if (vsection != section)
+ {
+ // These 2 sections should be merged
+ for(int k=0;kValue == vsection) pair->Value = section;
+ }
+ sectvalid[vsection-1] = false;
+ }
+ }
+ }
+ for(int i=0;inumlines;i++)
+ {
+ seg_t * seg = sub->firstline + i;
+
+ if (seg->PartnerSeg)
+ {
+ subsector_t * sub2 = seg->PartnerSeg->Subsector;
+
+ if (!(sub2->hacked&1) && sub2->render_sector == sub->render_sector)
+ {
+ sub2->hacked|=1;
+ sub->hacked &= ~4;
+ SpreadHackedFlag (sub2);
+ }
+ }
+ }
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+static void PrepareSectorData()
+{
+ int i;
+ TArray undetermined;
+ subsector_t * ss;
+
+ // now group the subsectors by sector
+ subsector_t ** subsectorbuffer = new subsector_t * [numsubsectors];
+
+ for(i=0, ss=subsectors; irender_sector->subsectorcount++;
+ }
+
+ for (i=0; irender_sector->subsectors[ss->render_sector->subsectorcount++]=ss;
+ }
+
+ // marks all malformed subsectors so rendering tricks using them can be handled more easily
+ for (i = 0; i < numsubsectors; i++)
+ {
+ if (subsectors[i].sector == subsectors[i].render_sector)
+ {
+ seg_t * seg = subsectors[i].firstline;
+ for(DWORD j=0;jSubsector->render_sector)
+ {
+ DPrintf(DMSG_NOTIFY, "Found hack: (%f,%f) (%f,%f)\n", seg[j].v1->fX(), seg[j].v1->fY(), seg[j].v2->fX(), seg[j].v2->fY());
+ subsectors[i].hacked|=5;
+ SpreadHackedFlag(&subsectors[i]);
+ }
+ if (seg[j].PartnerSeg==NULL) subsectors[i].hacked|=2; // used for quick termination checks
+ }
+ }
+ }
+ SetMapSections();
+}
+
+//==========================================================================
+//
+// Some processing for transparent door hacks using a floor raised by 1 map unit
+// - This will be used to lower the floor of such sectors by one map unit
+//
+//==========================================================================
+
+static void PrepareTransparentDoors(sector_t * sector)
+{
+ bool solidwall=false;
+ int notextures=0;
+ int nobtextures=0;
+ int selfref=0;
+ int i;
+ sector_t * nextsec=NULL;
+
+#ifdef _DEBUG
+ if (sector-sectors==34)
+ {
+ int a = 0;
+ }
+#endif
+
+ P_Recalculate3DFloors(sector);
+ if (sector->subsectorcount==0) return;
+
+ sector->transdoorheight=sector->GetPlaneTexZ(sector_t::floor);
+ sector->transdoor= !(sector->e->XFloor.ffloors.Size() || sector->heightsec || sector->floorplane.isSlope());
+
+ if (sector->transdoor)
+ {
+ for (i=0; ilinecount; i++)
+ {
+ if (sector->lines[i]->frontsector==sector->lines[i]->backsector)
+ {
+ selfref++;
+ continue;
+ }
+
+ sector_t * sec=getNextSector(sector->lines[i], sector);
+ if (sec==NULL)
+ {
+ solidwall=true;
+ continue;
+ }
+ else
+ {
+ nextsec=sec;
+
+ int side = sector->lines[i]->sidedef[0]->sector == sec;
+
+ if (sector->GetPlaneTexZ(sector_t::floor)!=sec->GetPlaneTexZ(sector_t::floor)+1. || sec->floorplane.isSlope())
+ {
+ sector->transdoor=false;
+ return;
+ }
+ if (!sector->lines[i]->sidedef[1-side]->GetTexture(side_t::top).isValid()) notextures++;
+ if (!sector->lines[i]->sidedef[1-side]->GetTexture(side_t::bottom).isValid()) nobtextures++;
+ }
+ }
+ if (sector->GetTexture(sector_t::ceiling)==skyflatnum)
+ {
+ sector->transdoor=false;
+ return;
+ }
+
+ if (selfref+nobtextures!=sector->linecount)
+ {
+ sector->transdoor=false;
+ }
+
+ if (selfref+notextures!=sector->linecount)
+ {
+ // This is a crude attempt to fix an incorrect transparent door effect I found in some
+ // WolfenDoom maps but considering the amount of code required to handle it I left it in.
+ // Do this only if the sector only contains one-sided walls or ones with no lower texture.
+ if (solidwall)
+ {
+ if (solidwall+nobtextures+selfref==sector->linecount && nextsec)
+ {
+ sector->heightsec=nextsec;
+ sector->heightsec->MoreFlags=0;
+ }
+ sector->transdoor=false;
+ }
+ }
+ }
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+static void AddToVertex(const sector_t * sec, TArray & list)
+{
+ int secno = int(sec-sectors);
+
+ for(unsigned i=0;i * vt_sectorlists;
+
+ int i,j,k;
+
+ vt_sectorlists = new TArray[numvertexes];
+
+
+ for(i=0;iv1 : line->v2;
+
+ for(k=0;k<2;k++)
+ {
+ sector_t * sec = k==0? line->frontsector : line->backsector;
+
+ if (sec)
+ {
+ extsector_t::xfloor &x = sec->e->XFloor;
+
+ AddToVertex(sec, vt_sectorlists[v-vertexes]);
+ if (sec->heightsec) AddToVertex(sec->heightsec, vt_sectorlists[v-vertexes]);
+ }
+ }
+ }
+ }
+
+ for(i=0;i1)
+ {
+ vertexes[i].numsectors= cnt;
+ vertexes[i].sectors=new sector_t*[cnt];
+ vertexes[i].heightlist = new float[cnt*2];
+ for(int j=0;jsidedef[0] == &sides[sdnum])
+ {
+ *v1 = ln->v1->fPos();
+ *v2 = ln->v2->fPos();
+ }
+ else
+ {
+ *v2 = ln->v1->fPos();
+ *v1 = ln->v2->fPos();
+ }
+}
+
+static int segcmp(const void *a, const void *b)
+{
+ seg_t *A = *(seg_t**)a;
+ seg_t *B = *(seg_t**)b;
+ return xs_RoundToInt(FRACUNIT*(A->sidefrac - B->sidefrac));
+}
+
+//==========================================================================
+//
+// Group segs to sidedefs
+//
+//==========================================================================
+
+static void PrepareSegs()
+{
+ int *segcount = new int[numsides];
+ int realsegs = 0;
+
+ // Get floatng point coordinates of vertices
+ for(int i = 0; i < numvertexes; i++)
+ {
+ vertexes[i].dirty = true;
+ }
+
+ // count the segs
+ memset(segcount, 0, numsides * sizeof(int));
+
+ // set up the extra data in case the map was loaded with regular nodes that might pass as GL nodes.
+ if (glsegextras == NULL)
+ {
+ for(int i=0;iPartnerSeg = &segs[partner];
+ else seg->PartnerSeg = NULL;
+ seg->Subsector = glsegextras[i].Subsector;
+ }
+ }
+
+ for(int i=0;isidedef == NULL) continue; // miniseg
+ int sidenum = int(seg->sidedef - sides);
+
+ realsegs++;
+ segcount[sidenum]++;
+ DVector2 sidestart, sideend, segend = seg->v2->fPos();
+ GetSideVertices(sidenum, &sidestart, &sideend);
+
+ sideend -=sidestart;
+ segend -= sidestart;
+
+ seg->sidefrac = float(segend.Length() / sideend.Length());
+ }
+
+ // allocate memory
+ sides[0].segs = new seg_t*[realsegs];
+ sides[0].numsegs = 0;
+
+ for(int i = 1; i < numsides; i++)
+ {
+ sides[i].segs = sides[i-1].segs + segcount[i-1];
+ sides[i].numsegs = 0;
+ }
+ delete [] segcount;
+
+ // assign the segs
+ for(int i=0;isidedef != NULL) seg->sidedef->segs[seg->sidedef->numsegs++] = seg;
+ }
+
+ // sort the segs
+ for(int i = 0; i < numsides; i++)
+ {
+ if (sides[i].numsegs > 1) qsort(sides[i].segs, sides[i].numsegs, sizeof(seg_t*), segcmp);
+ }
+}
+
+//==========================================================================
+//
+// Initialize the level data for the GL renderer
+//
+//==========================================================================
+extern int restart;
+
+void gl_PreprocessLevel()
+{
+ int i;
+
+ PrepareSegs();
+ PrepareSectorData();
+ InitVertexData();
+ int *checkmap = new int[numvertexes];
+ memset(checkmap, -1, sizeof(int)*numvertexes);
+ for(i=0;isidedef[0]->Flags & WALLF_POLYOBJ) continue; // don't bother with polyobjects
+
+ int vtnum1 = int(l->v1 - vertexes);
+ int vtnum2 = int(l->v2 - vertexes);
+
+ if (checkmap[vtnum1] < i)
+ {
+ checkmap[vtnum1] = i;
+ sectors[i].e->vertices.Push(&vertexes[vtnum1]);
+ vertexes[vtnum1].dirty = true;
+ }
+
+ if (checkmap[vtnum2] < i)
+ {
+ checkmap[vtnum2] = i;
+ sectors[i].e->vertices.Push(&vertexes[vtnum2]);
+ vertexes[vtnum2].dirty = true;
+ }
+ }
+ }
+ delete[] checkmap;
+
+ gl_InitPortals();
+
+ if (GLRenderer != NULL)
+ {
+ GLRenderer->SetupLevel();
+ }
+
+#if 0
+ gl_CreateSections();
+#endif
+
+ InitGLRMapinfoData();
+}
+
+
+
+//==========================================================================
+//
+// Cleans up all the GL data for the last level
+//
+//==========================================================================
+
+void gl_CleanLevelData()
+{
+ // Dynamic lights must be destroyed before the sector information here is deleted.
+ TThinkerIterator it(STAT_DLIGHT);
+ AActor * mo=it.Next();
+ while (mo)
+ {
+ AActor * next = it.Next();
+ mo->Destroy();
+ mo=next;
+ }
+
+ if (vertexes != NULL)
+ {
+ for(int i = 0; i < numvertexes; i++) if (vertexes[i].numsectors > 0)
+ {
+ if (vertexes[i].sectors != NULL)
+ {
+ delete [] vertexes[i].sectors;
+ vertexes[i].sectors = NULL;
+ }
+ if (vertexes[i].heightlist != NULL)
+ {
+ delete [] vertexes[i].heightlist;
+ vertexes[i].heightlist = NULL;
+ }
+ }
+ }
+
+ if (sides && sides[0].segs)
+ {
+ delete [] sides[0].segs;
+ sides[0].segs = NULL;
+ }
+ if (sectors && sectors[0].subsectors)
+ {
+ delete [] sectors[0].subsectors;
+ sectors[0].subsectors = NULL;
+ }
+ for (int i=0;isectornum, int(subsectors[j].firstline->linedef-lines));
+ break;
+ }
+ }
+ }
+}
diff --git a/src/gl/data/gl_vertexbuffer.cpp b/src/gl/data/gl_vertexbuffer.cpp
new file mode 100644
index 000000000..d14599eb8
--- /dev/null
+++ b/src/gl/data/gl_vertexbuffer.cpp
@@ -0,0 +1,494 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_vertexbuffer.cpp
+** Vertex buffer handling.
+**
+**/
+
+#include "gl/system/gl_system.h"
+#include "doomtype.h"
+#include "p_local.h"
+#include "r_state.h"
+#include "m_argv.h"
+#include "c_cvars.h"
+#include "gl/system/gl_interface.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+
+
+//==========================================================================
+//
+// Create / destroy the VBO
+//
+//==========================================================================
+
+FVertexBuffer::FVertexBuffer(bool wantbuffer)
+{
+ vbo_id = 0;
+ if (wantbuffer) glGenBuffers(1, &vbo_id);
+}
+
+FVertexBuffer::~FVertexBuffer()
+{
+ if (vbo_id != 0)
+ {
+ glDeleteBuffers(1, &vbo_id);
+ }
+}
+
+
+void FSimpleVertexBuffer::BindVBO()
+{
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ if (!gl.legacyMode)
+ {
+ glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FSimpleVertex), &VSiO->x);
+ glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FSimpleVertex), &VSiO->u);
+ glVertexAttribPointer(VATTR_COLOR, 4, GL_UNSIGNED_BYTE, true, sizeof(FSimpleVertex), &VSiO->color);
+ glEnableVertexAttribArray(VATTR_VERTEX);
+ glEnableVertexAttribArray(VATTR_TEXCOORD);
+ glEnableVertexAttribArray(VATTR_COLOR);
+ glDisableVertexAttribArray(VATTR_VERTEX2);
+ glDisableVertexAttribArray(VATTR_NORMAL);
+ }
+ else
+ {
+ glVertexPointer(3, GL_FLOAT, sizeof(FSimpleVertex), &VSiO->x);
+ glTexCoordPointer(2, GL_FLOAT, sizeof(FSimpleVertex), &VSiO->u);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(FSimpleVertex), &VSiO->color);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+}
+
+void FSimpleVertexBuffer::EnableColorArray(bool on)
+{
+ if (on)
+ {
+ if (!gl.legacyMode)
+ {
+ glEnableVertexAttribArray(VATTR_COLOR);
+ }
+ else
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+ }
+ else
+ {
+ if (!gl.legacyMode)
+ {
+ glDisableVertexAttribArray(VATTR_COLOR);
+ }
+ else
+ {
+ glDisableClientState(GL_COLOR_ARRAY);
+ }
+ }
+}
+
+
+void FSimpleVertexBuffer::set(FSimpleVertex *verts, int count)
+{
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ gl_RenderState.ResetVertexBuffer();
+ gl_RenderState.SetVertexBuffer(this);
+ glBufferData(GL_ARRAY_BUFFER, count * sizeof(*verts), verts, GL_STREAM_DRAW);
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FFlatVertexBuffer::FFlatVertexBuffer(int width, int height)
+: FVertexBuffer(!gl.legacyMode)
+{
+ switch (gl.buffermethod)
+ {
+ case BM_PERSISTENT:
+ {
+ unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ glBufferStorage(GL_ARRAY_BUFFER, bytesize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
+ map = (FFlatVertex*)glMapBufferRange(GL_ARRAY_BUFFER, 0, bytesize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
+ DPrintf(DMSG_NOTIFY, "Using persistent buffer\n");
+ break;
+ }
+
+ case BM_DEFERRED:
+ {
+ unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ glBufferData(GL_ARRAY_BUFFER, bytesize, NULL, GL_STREAM_DRAW);
+ map = nullptr;
+ DPrintf(DMSG_NOTIFY, "Using deferred buffer\n");
+ break;
+ }
+
+ default:
+ {
+ map = new FFlatVertex[BUFFER_SIZE];
+ DPrintf(DMSG_NOTIFY, "Using client array buffer\n");
+ break;
+ }
+ }
+ mIndex = mCurIndex = 0;
+ mNumReserved = NUM_RESERVED;
+ vbo_shadowdata.Resize(mNumReserved);
+
+ // the first quad is reserved for handling coordinates through uniforms.
+ vbo_shadowdata[0].Set(0, 0, 0, 0, 0);
+ vbo_shadowdata[1].Set(1, 0, 0, 0, 0);
+ vbo_shadowdata[2].Set(2, 0, 0, 0, 0);
+ vbo_shadowdata[3].Set(3, 0, 0, 0, 0);
+
+ // and the second one for the fullscreen quad used for blend overlays.
+ vbo_shadowdata[4].Set(0, 0, 0, 0, 0);
+ vbo_shadowdata[5].Set(0, (float)height, 0, 0, 0);
+ vbo_shadowdata[6].Set((float)width, 0, 0, 0, 0);
+ vbo_shadowdata[7].Set((float)width, (float)height, 0, 0, 0);
+
+ // and this is for the postprocessing copy operation
+ vbo_shadowdata[8].Set(-1.0f, -1.0f, 0, 0.0f, 0.0f);
+ vbo_shadowdata[9].Set(-1.0f, 1.0f, 0, 0.0f, 1.f);
+ vbo_shadowdata[10].Set(1.0f, -1.0f, 0, 1.f, 0.0f);
+ vbo_shadowdata[11].Set(1.0f, 1.0f, 0, 1.f, 1.f);
+
+ // The next two are the stencil caps.
+ vbo_shadowdata[12].Set(-32767.0f, 32767.0f, -32767.0f, 0, 0);
+ vbo_shadowdata[13].Set(-32767.0f, 32767.0f, 32767.0f, 0, 0);
+ vbo_shadowdata[14].Set(32767.0f, 32767.0f, 32767.0f, 0, 0);
+ vbo_shadowdata[15].Set(32767.0f, 32767.0f, -32767.0f, 0, 0);
+
+ vbo_shadowdata[16].Set(-32767.0f, -32767.0f, -32767.0f, 0, 0);
+ vbo_shadowdata[17].Set(-32767.0f, -32767.0f, 32767.0f, 0, 0);
+ vbo_shadowdata[18].Set(32767.0f, -32767.0f, 32767.0f, 0, 0);
+ vbo_shadowdata[19].Set(32767.0f, -32767.0f, -32767.0f, 0, 0);
+
+ if (gl.buffermethod == BM_DEFERRED)
+ {
+ Map();
+ memcpy(map, &vbo_shadowdata[0], mNumReserved * sizeof(FFlatVertex));
+ Unmap();
+ }
+
+}
+
+FFlatVertexBuffer::~FFlatVertexBuffer()
+{
+ if (vbo_id != 0)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+ if (gl.legacyMode)
+ {
+ delete[] map;
+ }
+ map = nullptr;
+}
+
+void FFlatVertexBuffer::OutputResized(int width, int height)
+{
+ vbo_shadowdata[4].Set(0, 0, 0, 0, 0);
+ vbo_shadowdata[5].Set(0, (float)height, 0, 0, 0);
+ vbo_shadowdata[6].Set((float)width, 0, 0, 0, 0);
+ vbo_shadowdata[7].Set((float)width, (float)height, 0, 0, 0);
+
+ Map();
+ memcpy(&map[4], &vbo_shadowdata[4], 4 * sizeof(FFlatVertex));
+ Unmap();
+}
+
+void FFlatVertexBuffer::BindVBO()
+{
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ if (!gl.legacyMode)
+ {
+ glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FFlatVertex), &VTO->x);
+ glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FFlatVertex), &VTO->u);
+ glEnableVertexAttribArray(VATTR_VERTEX);
+ glEnableVertexAttribArray(VATTR_TEXCOORD);
+ glDisableVertexAttribArray(VATTR_COLOR);
+ glDisableVertexAttribArray(VATTR_VERTEX2);
+ glDisableVertexAttribArray(VATTR_NORMAL);
+ }
+ else
+ {
+ glVertexPointer(3, GL_FLOAT, sizeof(FFlatVertex), &map->x);
+ glTexCoordPointer(2, GL_FLOAT, sizeof(FFlatVertex), &map->u);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ }
+}
+
+void FFlatVertexBuffer::Map()
+{
+ if (gl.buffermethod == BM_DEFERRED)
+ {
+ unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ gl_RenderState.ResetVertexBuffer();
+ map = (FFlatVertex*)glMapBufferRange(GL_ARRAY_BUFFER, 0, bytesize, GL_MAP_WRITE_BIT|GL_MAP_UNSYNCHRONIZED_BIT);
+ }
+}
+
+void FFlatVertexBuffer::Unmap()
+{
+ if (gl.buffermethod == BM_DEFERRED)
+ {
+ unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ gl_RenderState.ResetVertexBuffer();
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ map = nullptr;
+ }
+}
+
+//==========================================================================
+//
+// Initialize a single vertex
+//
+//==========================================================================
+
+void FFlatVertex::SetFlatVertex(vertex_t *vt, const secplane_t & plane)
+{
+ x = vt->fX();
+ y = vt->fY();
+ z = plane.ZatPoint(vt);
+ u = vt->fX()/64.f;
+ v = -vt->fY()/64.f;
+}
+
+//==========================================================================
+//
+// Find a 3D floor
+//
+//==========================================================================
+
+static F3DFloor *Find3DFloor(sector_t *target, sector_t *model)
+{
+ for(unsigned i=0; ie->XFloor.ffloors.Size(); i++)
+ {
+ F3DFloor *ffloor = target->e->XFloor.ffloors[i];
+ if (ffloor->model == model) return ffloor;
+ }
+ return NULL;
+}
+
+//==========================================================================
+//
+// Creates the vertices for one plane in one subsector
+//
+//==========================================================================
+
+int FFlatVertexBuffer::CreateSubsectorVertices(subsector_t *sub, const secplane_t &plane, int floor)
+{
+ int idx = vbo_shadowdata.Reserve(sub->numlines);
+ for(unsigned int k=0; knumlines; k++, idx++)
+ {
+ vbo_shadowdata[idx].SetFlatVertex(sub->firstline[k].v1, plane);
+ if (sub->sector->transdoor && floor) vbo_shadowdata[idx].z -= 1.f;
+ }
+ return idx;
+}
+
+//==========================================================================
+//
+// Creates the vertices for one plane in one subsector
+//
+//==========================================================================
+
+int FFlatVertexBuffer::CreateSectorVertices(sector_t *sec, const secplane_t &plane, int floor)
+{
+ int rt = vbo_shadowdata.Size();
+ // First calculate the vertices for the sector itself
+ for(int j=0; jsubsectorcount; j++)
+ {
+ subsector_t *sub = sec->subsectors[j];
+ CreateSubsectorVertices(sub, plane, floor);
+ }
+ return rt;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+int FFlatVertexBuffer::CreateVertices(int h, sector_t *sec, const secplane_t &plane, int floor)
+{
+ // First calculate the vertices for the sector itself
+ sec->vboheight[h] = sec->GetPlaneTexZ(h);
+ sec->vboindex[h] = CreateSectorVertices(sec, plane, floor);
+
+ // Next are all sectors using this one as heightsec
+ TArray &fakes = sec->e->FakeFloor.Sectors;
+ for (unsigned g=0; gvboindex[2+h] = CreateSectorVertices(fsec, plane, false);
+ }
+
+ // and finally all attached 3D floors
+ TArray &xf = sec->e->XFloor.attached;
+ for (unsigned g=0; gflags & FF_RENDERPLANES)
+ {
+ bool dotop = (ffloor->top.model == sec) && (ffloor->top.isceiling == h);
+ bool dobottom = (ffloor->bottom.model == sec) && (ffloor->bottom.isceiling == h);
+
+ if (dotop || dobottom)
+ {
+ if (dotop) ffloor->top.vindex = vbo_shadowdata.Size();
+ if (dobottom) ffloor->bottom.vindex = vbo_shadowdata.Size();
+
+ CreateSectorVertices(fsec, plane, false);
+ }
+ }
+ }
+ sec->vbocount[h] = vbo_shadowdata.Size() - sec->vboindex[h];
+ return sec->vboindex[h];
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FFlatVertexBuffer::CreateFlatVBO()
+{
+ for (int h = sector_t::floor; h <= sector_t::ceiling; h++)
+ {
+ for(int i=0; iXFloor.ffloors.Size(); j++)
+ {
+ F3DFloor *ff = sectors[i].e->XFloor.ffloors[j];
+
+ if (ff->top.model == §ors[i])
+ {
+ ff->top.vindex = sectors[i].vboindex[ff->top.isceiling];
+ }
+ if (ff->bottom.model == §ors[i])
+ {
+ ff->bottom.vindex = sectors[i].vboindex[ff->top.isceiling];
+ }
+ }
+ }
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FFlatVertexBuffer::UpdatePlaneVertices(sector_t *sec, int plane)
+{
+ int startvt = sec->vboindex[plane];
+ int countvt = sec->vbocount[plane];
+ secplane_t &splane = sec->GetSecPlane(plane);
+ FFlatVertex *vt = &vbo_shadowdata[startvt];
+ FFlatVertex *mapvt = &map[startvt];
+ for(int i=0; iz = splane.ZatPoint(vt->x, vt->y);
+ if (plane == sector_t::floor && sec->transdoor) vt->z -= 1;
+ mapvt->z = vt->z;
+ }
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FFlatVertexBuffer::CreateVBO()
+{
+ vbo_shadowdata.Resize(mNumReserved);
+ CreateFlatVBO();
+ mCurIndex = mIndex = vbo_shadowdata.Size();
+ Map();
+ memcpy(map, &vbo_shadowdata[0], vbo_shadowdata.Size() * sizeof(FFlatVertex));
+ Unmap();
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FFlatVertexBuffer::CheckPlanes(sector_t *sector)
+{
+ if (sector->GetPlaneTexZ(sector_t::ceiling) != sector->vboheight[sector_t::ceiling])
+ {
+ UpdatePlaneVertices(sector, sector_t::ceiling);
+ sector->vboheight[sector_t::ceiling] = sector->GetPlaneTexZ(sector_t::ceiling);
+ }
+ if (sector->GetPlaneTexZ(sector_t::floor) != sector->vboheight[sector_t::floor])
+ {
+ UpdatePlaneVertices(sector, sector_t::floor);
+ sector->vboheight[sector_t::floor] = sector->GetPlaneTexZ(sector_t::floor);
+ }
+}
+
+//==========================================================================
+//
+// checks the validity of all planes attached to this sector
+// and updates them if possible.
+//
+//==========================================================================
+
+void FFlatVertexBuffer::CheckUpdate(sector_t *sector)
+{
+ CheckPlanes(sector);
+ sector_t *hs = sector->GetHeightSec();
+ if (hs != NULL) CheckPlanes(hs);
+ for (unsigned i = 0; i < sector->e->XFloor.ffloors.Size(); i++)
+ CheckPlanes(sector->e->XFloor.ffloors[i]->model);
+}
\ No newline at end of file
diff --git a/src/gl/data/gl_vertexbuffer.h b/src/gl/data/gl_vertexbuffer.h
new file mode 100644
index 000000000..f777118fd
--- /dev/null
+++ b/src/gl/data/gl_vertexbuffer.h
@@ -0,0 +1,322 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+
+#ifndef __VERTEXBUFFER_H
+#define __VERTEXBUFFER_H
+
+#include "tarray.h"
+#include "gl/utility/gl_clock.h"
+#include "gl/system/gl_interface.h"
+
+struct vertex_t;
+struct secplane_t;
+struct subsector_t;
+struct sector_t;
+
+enum
+{
+ VATTR_VERTEX_BIT,
+ VATTR_TEXCOORD_BIT,
+ VATTR_COLOR_BIT,
+ VATTR_VERTEX2_BIT,
+ VATTR_NORMAL_BIT
+};
+
+
+class FVertexBuffer
+{
+protected:
+ unsigned int vbo_id;
+
+public:
+ FVertexBuffer(bool wantbuffer = true);
+ virtual ~FVertexBuffer();
+ virtual void BindVBO() = 0;
+ void EnableBufferArrays(int enable, int disable);
+};
+
+struct FFlatVertex
+{
+ float x,z,y; // world position
+ float u,v; // texture coordinates
+
+ void SetFlatVertex(vertex_t *vt, const secplane_t &plane);
+ void Set(float xx, float zz, float yy, float uu, float vv)
+ {
+ x = xx;
+ z = zz;
+ y = yy;
+ u = uu;
+ v = vv;
+ }
+};
+
+struct FSimpleVertex
+{
+ float x, z, y; // world position
+ float u, v; // texture coordinates
+ PalEntry color;
+
+ void Set(float xx, float zz, float yy, float uu = 0, float vv = 0, PalEntry col = 0xffffffff)
+ {
+ x = xx;
+ z = zz;
+ y = yy;
+ u = uu;
+ v = vv;
+ color = col;
+ }
+};
+
+#define VTO ((FFlatVertex*)NULL)
+#define VSiO ((FSimpleVertex*)NULL)
+
+class FSimpleVertexBuffer : public FVertexBuffer
+{
+ TArray mBuffer;
+public:
+ FSimpleVertexBuffer()
+ {
+ }
+ void BindVBO();
+ void set(FSimpleVertex *verts, int count);
+ void EnableColorArray(bool on);
+};
+
+class FFlatVertexBuffer : public FVertexBuffer
+{
+ FFlatVertex *map;
+ unsigned int mIndex;
+ unsigned int mCurIndex;
+ unsigned int mNumReserved;
+
+ void CheckPlanes(sector_t *sector);
+
+ static const unsigned int BUFFER_SIZE = 2000000;
+ static const unsigned int BUFFER_SIZE_TO_USE = 1999500;
+
+public:
+ enum
+ {
+ QUAD_INDEX = 0,
+ FULLSCREEN_INDEX = 4,
+ PRESENT_INDEX = 8,
+ STENCILTOP_INDEX = 12,
+ STENCILBOTTOM_INDEX = 16,
+
+ NUM_RESERVED = 20
+ };
+
+ TArray vbo_shadowdata; // this is kept around for updating the actual (non-readable) buffer and as stand-in for pre GL 4.x
+
+ FFlatVertexBuffer(int width, int height);
+ ~FFlatVertexBuffer();
+
+ void OutputResized(int width, int height);
+
+ void BindVBO();
+
+ void CreateVBO();
+ void CheckUpdate(sector_t *sector);
+
+ FFlatVertex *GetBuffer()
+ {
+ return &map[mCurIndex];
+ }
+ FFlatVertex *Alloc(int num, int *poffset)
+ {
+ FFlatVertex *p = GetBuffer();
+ *poffset = mCurIndex;
+ mCurIndex += num;
+ if (mCurIndex >= BUFFER_SIZE_TO_USE) mCurIndex = mIndex;
+ return p;
+ }
+
+ unsigned int GetCount(FFlatVertex *newptr, unsigned int *poffset)
+ {
+ unsigned int newofs = (unsigned int)(newptr - map);
+ unsigned int diff = newofs - mCurIndex;
+ *poffset = mCurIndex;
+ mCurIndex = newofs;
+ if (mCurIndex >= BUFFER_SIZE_TO_USE) mCurIndex = mIndex;
+ return diff;
+ }
+#ifdef __GL_PCH_H // we need the system includes for this but we cannot include them ourselves without creating #define clashes. The affected files wouldn't try to draw anyway.
+ void RenderArray(unsigned int primtype, unsigned int offset, unsigned int count)
+ {
+ drawcalls.Clock();
+ glDrawArrays(primtype, offset, count);
+ drawcalls.Unclock();
+ }
+
+ void RenderCurrent(FFlatVertex *newptr, unsigned int primtype, unsigned int *poffset = NULL, unsigned int *pcount = NULL)
+ {
+ unsigned int offset;
+ unsigned int count = GetCount(newptr, &offset);
+ RenderArray(primtype, offset, count);
+ if (poffset) *poffset = offset;
+ if (pcount) *pcount = count;
+ }
+
+#endif
+ void Reset()
+ {
+ mCurIndex = mIndex;
+ }
+
+ void Map();
+ void Unmap();
+
+private:
+ int CreateSubsectorVertices(subsector_t *sub, const secplane_t &plane, int floor);
+ int CreateSectorVertices(sector_t *sec, const secplane_t &plane, int floor);
+ int CreateVertices(int h, sector_t *sec, const secplane_t &plane, int floor);
+ void CreateFlatVBO();
+ void UpdatePlaneVertices(sector_t *sec, int plane);
+
+};
+
+
+struct FSkyVertex
+{
+ float x, y, z, u, v;
+ PalEntry color;
+
+ void Set(float xx, float zz, float yy, float uu=0, float vv=0, PalEntry col=0xffffffff)
+ {
+ x = xx;
+ z = zz;
+ y = yy;
+ u = uu;
+ v = vv;
+ color = col;
+ }
+
+ void SetXYZ(float xx, float yy, float zz, float uu = 0, float vv = 0, PalEntry col = 0xffffffff)
+ {
+ x = xx;
+ y = yy;
+ z = zz;
+ u = uu;
+ v = vv;
+ color = col;
+ }
+
+};
+
+class FSkyVertexBuffer : public FVertexBuffer
+{
+public:
+ static const int SKYHEMI_UPPER = 1;
+ static const int SKYHEMI_LOWER = 2;
+
+ enum
+ {
+ SKYMODE_MAINLAYER = 0,
+ SKYMODE_SECONDLAYER = 1,
+ SKYMODE_FOGLAYER = 2
+ };
+
+private:
+ TArray mVertices;
+ TArray mPrimStart;
+
+ int mRows, mColumns;
+
+ // indices for sky cubemap faces
+ int mFaceStart[7];
+ int mSideStart;
+
+ void SkyVertex(int r, int c, bool yflip);
+ void CreateSkyHemisphere(int hemi);
+ void CreateDome();
+ void RenderRow(int prim, int row);
+
+public:
+
+ FSkyVertexBuffer();
+ virtual ~FSkyVertexBuffer();
+ void RenderDome(FMaterial *tex, int mode);
+ void BindVBO();
+ int FaceStart(int i)
+ {
+ if (i >= 0 && i < 7) return mFaceStart[i];
+ else return mSideStart;
+ }
+
+};
+
+#define VSO ((FSkyVertex*)NULL)
+
+struct FModelVertex
+{
+ float x, y, z; // world position
+ float u, v; // texture coordinates
+ unsigned packedNormal; // normal vector as GL_INT_2_10_10_10_REV.
+
+ void Set(float xx, float yy, float zz, float uu, float vv)
+ {
+ x = xx;
+ y = yy;
+ z = zz;
+ u = uu;
+ v = vv;
+ }
+
+ void SetNormal(float nx, float ny, float nz)
+ {
+ /*
+ int inx = int(nx * 512);
+ int iny = int(ny * 512);
+ int inz = int(nz * 512);
+ packedNormal = 0x40000000 | ((inx & 1023) << 20) | ((iny & 1023) << 10) | (inz & 1023);
+ */
+ packedNormal = 0; // Per-pixel lighting for models isn't implemented yet so leave this at 0 for now.
+ }
+};
+
+
+class FModelVertexBuffer : public FVertexBuffer
+{
+ int mIndexFrame[2];
+ FModelVertex *vbo_ptr;
+ uint32_t ibo_id;
+
+public:
+
+ FModelVertexBuffer(bool needindex, bool singleframe);
+ ~FModelVertexBuffer();
+
+ FModelVertex *LockVertexBuffer(unsigned int size);
+ void UnlockVertexBuffer();
+
+ unsigned int *LockIndexBuffer(unsigned int size);
+ void UnlockIndexBuffer();
+
+ unsigned int SetupFrame(unsigned int frame1, unsigned int frame2, unsigned int size);
+ void BindVBO();
+};
+
+#define VMO ((FModelVertex*)NULL)
+
+
+#endif
\ No newline at end of file
diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp
new file mode 100644
index 000000000..40420963c
--- /dev/null
+++ b/src/gl/dynlights/a_dynlight.cpp
@@ -0,0 +1,888 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2004-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** a_dynlight.cpp
+** Implements actors representing dynamic lights (hardware independent)
+**
+**
+** all functions marked with [TS] are licensed under
+**---------------------------------------------------------------------------
+** Copyright 2003 Timothy Stump
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/
+
+#include "gl/system/gl_system.h"
+
+#include "templates.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "c_dispatch.h"
+#include "g_level.h"
+#include "scripting/thingdef.h"
+#include "i_system.h"
+#include "templates.h"
+#include "doomdata.h"
+#include "r_utility.h"
+#include "p_local.h"
+#include "portal.h"
+#include "doomstat.h"
+#include "serializer.h"
+
+
+#include "gl/renderer/gl_renderer.h"
+#include "gl/data/gl_data.h"
+#include "gl/dynlights/gl_dynlight.h"
+#include "gl/utility/gl_convert.h"
+#include "gl/utility/gl_templates.h"
+
+
+
+//==========================================================================
+//
+//==========================================================================
+DEFINE_CLASS_PROPERTY(type, S, DynamicLight)
+{
+ PROP_STRING_PARM(str, 0);
+ static const char * ltype_names[]={
+ "Point","Pulse","Flicker","Sector","RandomFlicker", "ColorPulse", "ColorFlicker", "RandomColorFlicker", NULL};
+
+ static const int ltype_values[]={
+ PointLight, PulseLight, FlickerLight, SectorLight, RandomFlickerLight, ColorPulseLight, ColorFlickerLight, RandomColorFlickerLight };
+
+ int style = MatchString(str, ltype_names);
+ if (style < 0) I_Error("Unknown light type '%s'", str);
+ defaults->lighttype = ltype_values[style];
+}
+
+//==========================================================================
+//
+// Actor classes
+//
+// For flexibility all functionality has been packed into a single class
+// which is controlled by flags
+//
+//==========================================================================
+IMPLEMENT_CLASS (ADynamicLight, false, false)
+IMPLEMENT_CLASS (AVavoomLight, false, false)
+IMPLEMENT_CLASS (AVavoomLightWhite, false, false)
+IMPLEMENT_CLASS (AVavoomLightColor, false, false)
+
+void AVavoomLight::BeginPlay ()
+{
+ // This must not call Super::BeginPlay!
+ ChangeStatNum(STAT_DLIGHT);
+ if (Sector) AddZ(-Sector->floorplane.ZatPoint(this), false);
+ lighttype = PointLight;
+}
+
+void AVavoomLightWhite::BeginPlay ()
+{
+ m_Radius[0] = args[0] * 4;
+ args[LIGHT_RED] = 128;
+ args[LIGHT_GREEN] = 128;
+ args[LIGHT_BLUE] = 128;
+
+ Super::BeginPlay();
+}
+
+void AVavoomLightColor::BeginPlay ()
+{
+ int l_args[5];
+ memcpy(l_args, args, sizeof(l_args));
+ memset(args, 0, sizeof(args));
+ m_Radius[0] = l_args[0] * 4;
+ args[LIGHT_RED] = l_args[1] >> 1;
+ args[LIGHT_GREEN] = l_args[2] >> 1;
+ args[LIGHT_BLUE] = l_args[3] >> 1;
+
+ Super::BeginPlay();
+}
+
+static FRandom randLight;
+
+//==========================================================================
+//
+// Base class
+//
+//==========================================================================
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void ADynamicLight::Serialize(FSerializer &arc)
+{
+ Super::Serialize (arc);
+ auto def = static_cast(GetDefault());
+ arc("lightflags", lightflags, def->lightflags)
+ ("lighttype", lighttype, def->lighttype)
+ ("tickcount", m_tickCount, def->m_tickCount)
+ ("currentradius", m_currentRadius, def->m_currentRadius)
+ .Array("lightradius", m_Radius, def->m_Radius, 2);
+
+ if (lighttype == PulseLight)
+ arc("lastupdate", m_lastUpdate, def->m_lastUpdate)
+ ("cycler", m_cycler, def->m_cycler);
+}
+
+
+void ADynamicLight::PostSerialize()
+{
+ Super::PostSerialize();
+ // The default constructor which is used for creating objects before deserialization will not set this variable.
+ // It needs to be true for all placed lights.
+ visibletoplayer = true;
+ LinkLight();
+}
+
+//==========================================================================
+//
+// [TS]
+//
+//==========================================================================
+void ADynamicLight::BeginPlay()
+{
+ //Super::BeginPlay();
+ ChangeStatNum(STAT_DLIGHT);
+
+ m_Radius[0] = args[LIGHT_INTENSITY];
+ m_Radius[1] = args[LIGHT_SECONDARY_INTENSITY];
+ specialf1 = DAngle(double(SpawnAngle)).Normalized360().Degrees;
+ visibletoplayer = true;
+}
+
+//==========================================================================
+//
+// [TS]
+//
+//==========================================================================
+void ADynamicLight::PostBeginPlay()
+{
+ Super::PostBeginPlay();
+
+ if (!(SpawnFlags & MTF_DORMANT))
+ {
+ Activate (NULL);
+ }
+
+ subsector = R_PointInSubsector(Pos());
+}
+
+
+//==========================================================================
+//
+// [TS]
+//
+//==========================================================================
+void ADynamicLight::Activate(AActor *activator)
+{
+ //Super::Activate(activator);
+ flags2&=~MF2_DORMANT;
+
+ m_currentRadius = float(m_Radius[0]);
+ m_tickCount = 0;
+
+ if (lighttype == PulseLight)
+ {
+ float pulseTime = specialf1 / TICRATE;
+
+ m_lastUpdate = level.maptime;
+ m_cycler.SetParams(float(m_Radius[1]), float(m_Radius[0]), pulseTime);
+ m_cycler.ShouldCycle(true);
+ m_cycler.SetCycleType(CYCLE_Sin);
+ m_currentRadius = m_cycler.GetVal();
+ }
+}
+
+
+//==========================================================================
+//
+// [TS]
+//
+//==========================================================================
+void ADynamicLight::Deactivate(AActor *activator)
+{
+ //Super::Deactivate(activator);
+ flags2|=MF2_DORMANT;
+}
+
+
+//==========================================================================
+//
+// [TS]
+//
+//==========================================================================
+void ADynamicLight::Tick()
+{
+ if (IsOwned())
+ {
+ if (!target || !target->state)
+ {
+ this->Destroy();
+ return;
+ }
+ if (target->flags & MF_UNMORPHED) return;
+ visibletoplayer = target->IsVisibleToPlayer(); // cache this value for the renderer to speed up calculations.
+ }
+
+ // Don't bother if the light won't be shown
+ if (!IsActive()) return;
+
+ // I am doing this with a type field so that I can dynamically alter the type of light
+ // without having to create or maintain multiple objects.
+ switch(lighttype)
+ {
+ case PulseLight:
+ {
+ float diff = (level.maptime - m_lastUpdate) / (float)TICRATE;
+
+ m_lastUpdate = level.maptime;
+ m_cycler.Update(diff);
+ m_currentRadius = m_cycler.GetVal();
+ break;
+ }
+
+ case FlickerLight:
+ {
+ BYTE rnd = randLight();
+ float pct = specialf1 / 360.f;
+
+ m_currentRadius = float(m_Radius[rnd >= pct * 255]);
+ break;
+ }
+
+ case RandomFlickerLight:
+ {
+ int flickerRange = m_Radius[1] - m_Radius[0];
+ float amt = randLight() / 255.f;
+
+ if (m_tickCount > specialf1)
+ {
+ m_tickCount = 0;
+ }
+ if (m_tickCount++ == 0 || m_currentRadius > m_Radius[1])
+ {
+ m_currentRadius = float(m_Radius[0] + (amt * flickerRange));
+ }
+ break;
+ }
+
+#if 0
+ // These need some more work elsewhere
+ case ColorFlickerLight:
+ {
+ BYTE rnd = randLight();
+ float pct = specialf1/360.f;
+
+ m_currentRadius = m_Radius[rnd >= pct * 255];
+ break;
+ }
+
+ case RandomColorFlickerLight:
+ {
+ int flickerRange = m_Radius[1] - m_Radius[0];
+ float amt = randLight() / 255.f;
+
+ m_tickCount++;
+
+ if (m_tickCount > specialf1)
+ {
+ m_currentRadius = m_Radius[0] + (amt * flickerRange);
+ m_tickCount = 0;
+ }
+ break;
+ }
+#endif
+
+ case SectorLight:
+ {
+ float intensity;
+ float scale = args[LIGHT_SCALE] / 8.f;
+
+ if (scale == 0.f) scale = 1.f;
+
+ intensity = Sector->lightlevel * scale;
+ intensity = clamp(intensity, 0.f, 255.f);
+
+ m_currentRadius = intensity;
+ break;
+ }
+
+ case PointLight:
+ m_currentRadius = float(m_Radius[0]);
+ break;
+ }
+ UpdateLocation();
+}
+
+
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void ADynamicLight::UpdateLocation()
+{
+ double oldx= X();
+ double oldy= Y();
+ double oldradius= radius;
+ float intensity;
+
+ if (IsActive())
+ {
+ if (target)
+ {
+ DAngle angle = target->Angles.Yaw;
+ double s = angle.Sin();
+ double c = angle.Cos();
+
+ DVector3 pos = target->Vec3Offset(m_off.X * c + m_off.Y * s, m_off.X * s - m_off.Y * c, m_off.Z + target->GetBobOffset());
+ SetXYZ(pos); // attached lights do not need to go into the regular blockmap
+ Prev = target->Pos();
+ subsector = R_PointInSubsector(Prev);
+ Sector = subsector->sector;
+
+ // Some z-coordinate fudging to prevent the light from getting too close to the floor or ceiling planes. With proper attenuation this would render them invisible.
+ // A distance of 5 is needed so that the light's effect doesn't become too small.
+ if (Z() < target->floorz + 5.) SetZ(target->floorz + 5.);
+ else if (Z() > target->ceilingz - 5.) SetZ(target->ceilingz - 5.);
+ }
+ else
+ {
+ if (Z() < floorz + 5.) SetZ(floorz + 5.);
+ else if (Z() > ceilingz - 5.) SetZ(ceilingz - 5.);
+ }
+
+
+ // The radius being used here is always the maximum possible with the
+ // current settings. This avoids constant relinking of flickering lights
+
+ if (lighttype == FlickerLight || lighttype == RandomFlickerLight || lighttype == PulseLight)
+ {
+ intensity = float(MAX(m_Radius[0], m_Radius[1]));
+ }
+ else
+ {
+ intensity = m_currentRadius;
+ }
+ radius = intensity * 2.0f;
+ assert(radius >= m_currentRadius * 2);
+
+ if (X() != oldx || Y() != oldy || radius != oldradius)
+ {
+ //Update the light lists
+ LinkLight();
+ }
+ }
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void ADynamicLight::SetOrigin(double x, double y, double z, bool moving)
+{
+ Super::SetOrigin(x, y, z, moving);
+ LinkLight();
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void ADynamicLight::SetOffset(const DVector3 &pos)
+{
+ m_off = pos;
+ UpdateLocation();
+}
+
+
+//==========================================================================
+//
+// The target pointer in dynamic lights should never be substituted unless
+// notOld is NULL (which indicates that the object was destroyed by force.)
+//
+//==========================================================================
+size_t ADynamicLight::PointerSubstitution (DObject *old, DObject *notOld)
+{
+ AActor *saved_target = target;
+ size_t ret = Super::PointerSubstitution(old, notOld);
+ if (notOld != NULL) target = saved_target;
+ return ret;
+}
+
+//=============================================================================
+//
+// These have been copied from the secnode code and modified for the light links
+//
+// P_AddSecnode() searches the current list to see if this sector is
+// already there. If not, it adds a sector node at the head of the list of
+// sectors this object appears in. This is called when creating a list of
+// nodes that will get linked in later. Returns a pointer to the new node.
+//
+//=============================================================================
+
+FLightNode * AddLightNode(FLightNode ** thread, void * linkto, ADynamicLight * light, FLightNode *& nextnode)
+{
+ FLightNode * node;
+
+ node = nextnode;
+ while (node)
+ {
+ if (node->targ==linkto) // Already have a node for this sector?
+ {
+ node->lightsource = light; // Yes. Setting m_thing says 'keep it'.
+ return(nextnode);
+ }
+ node = node->nextTarget;
+ }
+
+ // Couldn't find an existing node for this sector. Add one at the head
+ // of the list.
+
+ node = new FLightNode;
+
+ node->targ = linkto;
+ node->lightsource = light;
+
+ node->prevTarget = &nextnode;
+ node->nextTarget = nextnode;
+
+ if (nextnode) nextnode->prevTarget = &node->nextTarget;
+
+ // Add new node at head of sector thread starting at s->touching_thinglist
+
+ node->prevLight = thread;
+ node->nextLight = *thread;
+ if (node->nextLight) node->nextLight->prevLight=&node->nextLight;
+ *thread = node;
+ return(node);
+}
+
+
+//=============================================================================
+//
+// P_DelSecnode() deletes a sector node from the list of
+// sectors this object appears in. Returns a pointer to the next node
+// on the linked list, or NULL.
+//
+//=============================================================================
+
+static FLightNode * DeleteLightNode(FLightNode * node)
+{
+ FLightNode * tn; // next node on thing thread
+
+ if (node)
+ {
+
+ *node->prevTarget = node->nextTarget;
+ if (node->nextTarget) node->nextTarget->prevTarget=node->prevTarget;
+
+ *node->prevLight = node->nextLight;
+ if (node->nextLight) node->nextLight->prevLight=node->prevLight;
+
+ // Return this node to the freelist
+ tn=node->nextTarget;
+ delete node;
+ return(tn);
+ }
+ return(NULL);
+} // phares 3/13/98
+
+
+
+//==========================================================================
+//
+// Gets the light's distance to a line
+//
+//==========================================================================
+
+double ADynamicLight::DistToSeg(const DVector3 &pos, seg_t *seg)
+{
+ double u, px, py;
+
+ double seg_dx = seg->v2->fX() - seg->v1->fX();
+ double seg_dy = seg->v2->fY() - seg->v1->fY();
+ double seg_length_sq = seg_dx * seg_dx + seg_dy * seg_dy;
+
+ u = (((pos.X - seg->v1->fX()) * seg_dx) + (pos.Y - seg->v1->fY()) * seg_dy) / seg_length_sq;
+ if (u < 0.) u = 0.; // clamp the test point to the line segment
+ else if (u > 1.) u = 1.;
+
+ px = seg->v1->fX() + (u * seg_dx);
+ py = seg->v1->fY() + (u * seg_dy);
+
+ px -= pos.X;
+ py -= pos.Y;
+
+ return (px*px) + (py*py);
+}
+
+
+//==========================================================================
+//
+// Collect all touched sidedefs and subsectors
+// to sidedefs and sector parts.
+//
+//==========================================================================
+struct LightLinkEntry
+{
+ subsector_t *sub;
+ DVector3 pos;
+};
+static TArray collected_ss;
+
+void ADynamicLight::CollectWithinRadius(const DVector3 &opos, subsector_t *subSec, float radius)
+{
+ if (!subSec) return;
+ collected_ss.Clear();
+ collected_ss.Push({ subSec, opos });
+ subSec->validcount = ::validcount;
+
+ for (unsigned i = 0; i < collected_ss.Size(); i++)
+ {
+ subSec = collected_ss[i].sub;
+
+ touching_subsectors = AddLightNode(&subSec->lighthead, subSec, this, touching_subsectors);
+ if (subSec->sector->validcount != ::validcount)
+ {
+ touching_sector = AddLightNode(&subSec->render_sector->lighthead, subSec->sector, this, touching_sector);
+ subSec->sector->validcount = ::validcount;
+ }
+
+ for (unsigned int j = 0; j < subSec->numlines; ++j)
+ {
+ auto &pos = collected_ss[i].pos;
+ seg_t *seg = subSec->firstline + j;
+
+ // check distance from x/y to seg and if within radius add this seg and, if present the opposing subsector (lather/rinse/repeat)
+ // If out of range we do not need to bother with this seg.
+ if (DistToSeg(pos, seg) <= radius)
+ {
+ if (seg->sidedef && seg->linedef && seg->linedef->validcount != ::validcount)
+ {
+ // light is in front of the seg
+ if ((pos.Y - seg->v1->fY()) * (seg->v2->fX() - seg->v1->fX()) + (seg->v1->fX() - pos.X) * (seg->v2->fY() - seg->v1->fY()) <= 0)
+ {
+ seg->linedef->validcount = validcount;
+ touching_sides = AddLightNode(&seg->sidedef->lighthead, seg->sidedef, this, touching_sides);
+ }
+ }
+ if (seg->linedef)
+ {
+ FLinePortal *port = seg->linedef->getPortal();
+ if (port && port->mType == PORTT_LINKED)
+ {
+ line_t *other = port->mDestination;
+ if (other->validcount != ::validcount)
+ {
+ subsector_t *othersub = R_PointInSubsector(other->v1->fPos() + other->Delta() / 2);
+ if (othersub->validcount != ::validcount)
+ {
+ othersub->validcount = ::validcount;
+ collected_ss.Push({ othersub, PosRelative(other) });
+ }
+ }
+ }
+ }
+
+ seg_t *partner = seg->PartnerSeg;
+ if (partner)
+ {
+ subsector_t *sub = partner->Subsector;
+ if (sub != NULL && sub->validcount != ::validcount)
+ {
+ sub->validcount = ::validcount;
+ collected_ss.Push({ sub, pos });
+ }
+ }
+ }
+ }
+ sector_t *sec = subSec->sector;
+ if (!sec->PortalBlocksSight(sector_t::ceiling))
+ {
+ line_t *other = subSec->firstline->linedef;
+ if (sec->GetPortalPlaneZ(sector_t::ceiling) < Z() + radius)
+ {
+ DVector2 refpos = other->v1->fPos() + other->Delta() / 2 + sec->GetPortalDisplacement(sector_t::ceiling);
+ subsector_t *othersub = R_PointInSubsector(refpos);
+ if (othersub->validcount != ::validcount)
+ {
+ othersub->validcount = ::validcount;
+ collected_ss.Push({ othersub, PosRelative(othersub->sector) });
+ }
+ }
+ }
+ if (!sec->PortalBlocksSight(sector_t::floor))
+ {
+ line_t *other = subSec->firstline->linedef;
+ if (sec->GetPortalPlaneZ(sector_t::floor) > Z() - radius)
+ {
+ DVector2 refpos = other->v1->fPos() + other->Delta() / 2 + sec->GetPortalDisplacement(sector_t::floor);
+ subsector_t *othersub = R_PointInSubsector(refpos);
+ if (othersub->validcount != ::validcount)
+ {
+ othersub->validcount = ::validcount;
+ collected_ss.Push({ othersub, PosRelative(othersub->sector) });
+ }
+ }
+ }
+ }
+}
+
+//==========================================================================
+//
+// Link the light into the world
+//
+//==========================================================================
+
+void ADynamicLight::LinkLight()
+{
+ // mark the old light nodes
+ FLightNode * node;
+
+ node = touching_sides;
+ while (node)
+ {
+ node->lightsource = NULL;
+ node = node->nextTarget;
+ }
+ node = touching_subsectors;
+ while (node)
+ {
+ node->lightsource = NULL;
+ node = node->nextTarget;
+ }
+ node = touching_sector;
+ while (node)
+ {
+ node->lightsource = NULL;
+ node = node->nextTarget;
+ }
+
+ if (radius>0)
+ {
+ // passing in radius*radius allows us to do a distance check without any calls to sqrt
+ subsector_t * subSec = R_PointInSubsector(Pos());
+ ::validcount++;
+ CollectWithinRadius(Pos(), subSec, radius*radius);
+
+ }
+
+ // Now delete any nodes that won't be used. These are the ones where
+ // m_thing is still NULL.
+
+ node = touching_sides;
+ while (node)
+ {
+ if (node->lightsource == NULL)
+ {
+ node = DeleteLightNode(node);
+ }
+ else
+ node = node->nextTarget;
+ }
+
+ node = touching_subsectors;
+ while (node)
+ {
+ if (node->lightsource == NULL)
+ {
+ node = DeleteLightNode(node);
+ }
+ else
+ node = node->nextTarget;
+ }
+
+ node = touching_sector;
+ while (node)
+ {
+ if (node->lightsource == NULL)
+ {
+ node = DeleteLightNode(node);
+ }
+ else
+ node = node->nextTarget;
+ }
+}
+
+
+//==========================================================================
+//
+// Deletes the link lists
+//
+//==========================================================================
+void ADynamicLight::UnlinkLight ()
+{
+ if (owned && target != NULL)
+ {
+ // Delete reference in owning actor
+ for(int c=target->dynamiclights.Size()-1; c>=0; c--)
+ {
+ if (target->dynamiclights[c] == this)
+ {
+ target->dynamiclights.Delete(c);
+ break;
+ }
+ }
+ }
+ while (touching_sides) touching_sides = DeleteLightNode(touching_sides);
+ while (touching_subsectors) touching_subsectors = DeleteLightNode(touching_subsectors);
+ while (touching_sector) touching_sector = DeleteLightNode(touching_sector);
+}
+
+void ADynamicLight::Destroy()
+{
+ UnlinkLight();
+ Super::Destroy();
+}
+
+
+//==========================================================================
+//
+// Needed for garbage collection
+//
+//==========================================================================
+
+size_t AActor::PropagateMark()
+{
+ for (unsigned i=0; i it;
+
+ while ((dl=it.Next()))
+ {
+ walls=0;
+ sectors=0;
+ subsecs = 0;
+ Printf("%s at (%f, %f, %f), color = 0x%02x%02x%02x, radius = %f ",
+ dl->target? dl->target->GetClass()->TypeName.GetChars() : dl->GetClass()->TypeName.GetChars(),
+ dl->X(), dl->Y(), dl->Z(), dl->args[LIGHT_RED],
+ dl->args[LIGHT_GREEN], dl->args[LIGHT_BLUE], dl->radius);
+ i++;
+
+ if (dl->target)
+ {
+ FTextureID spr = gl_GetSpriteFrame(dl->target->sprite, dl->target->frame, 0, 0, NULL);
+ Printf(", frame = %s ", TexMan[spr]->Name.GetChars());
+ }
+
+
+ FLightNode * node;
+
+ node=dl->touching_sides;
+
+ while (node)
+ {
+ walls++;
+ allwalls++;
+ node = node->nextTarget;
+ }
+
+ node=dl->touching_subsectors;
+
+ while (node)
+ {
+ allsubsecs++;
+ subsecs++;
+ node = node->nextTarget;
+ }
+
+ node = dl->touching_sector;
+
+ while (node)
+ {
+ allsectors++;
+ sectors++;
+ node = node->nextTarget;
+ }
+ Printf("- %d walls, %d subsectors, %d sectors\n", walls, subsecs, sectors);
+
+ }
+ Printf("%i dynamic lights, %d walls, %d subsectors, %d sectors\n\n\n", i, allwalls, allsubsecs, allsectors);
+}
+
+CCMD(listsublights)
+{
+ for(int i=0;ilighthead;
+ while (node != NULL)
+ {
+ lights++;
+ node = node->nextLight;
+ }
+
+ Printf(PRINT_LOG, "Subsector %d - %d lights\n", i, lights);
+ }
+}
+
+
diff --git a/src/gl/dynlights/gl_dynlight.cpp b/src/gl/dynlights/gl_dynlight.cpp
new file mode 100644
index 000000000..de05bb19f
--- /dev/null
+++ b/src/gl/dynlights/gl_dynlight.cpp
@@ -0,0 +1,1409 @@
+/*
+** gl_dynlight.cpp
+** Light definitions for actors.
+**
+**---------------------------------------------------------------------------
+** Copyright 2003 Timothy Stump
+** Copyright 2005 Christoph Oelckers
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/
+
+
+#include
+#include "i_system.h"
+#include "doomtype.h"
+#include "c_cvars.h"
+#include "c_dispatch.h"
+#include "m_random.h"
+#include "sc_man.h"
+#include "templates.h"
+#include "w_wad.h"
+#include "gi.h"
+#include "r_state.h"
+#include "stats.h"
+#include "zstring.h"
+#include "d_dehacked.h"
+
+
+#include "gl/dynlights/gl_dynlight.h"
+#include "gl/textures/gl_skyboxtexture.h"
+#include "gl/utility/gl_clock.h"
+#include "gl/utility/gl_convert.h"
+#include "gl/data/gl_data.h"
+
+int ScriptDepth;
+void gl_InitGlow(FScanner &sc);
+void gl_ParseBrightmap(FScanner &sc, int);
+void gl_DestroyUserShaders();
+void gl_ParseHardwareShader(FScanner &sc, int deflump);
+void gl_ParseSkybox(FScanner &sc);
+void gl_ParseDetailTexture(FScanner &sc);
+void gl_ParseVavoomSkybox();
+
+//==========================================================================
+//
+// Dehacked aliasing
+//
+//==========================================================================
+
+inline PClassActor * GetRealType(PClassActor * ti)
+{
+ PClassActor *rep = ti->GetReplacement(false);
+ if (rep != ti && rep != NULL && rep->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup)))
+ {
+ return rep;
+ }
+ return ti;
+}
+
+
+
+//==========================================================================
+//
+// Light associations
+//
+//==========================================================================
+class FLightAssociation
+{
+public:
+ //FLightAssociation();
+ FLightAssociation(FName actorName, const char *frameName, FName lightName)
+ : m_ActorName(actorName), m_AssocLight(lightName)
+ {
+ strncpy(m_FrameName, frameName, 8);
+ }
+
+ FName ActorName() { return m_ActorName; }
+ const char *FrameName() { return m_FrameName; }
+ FName Light() { return m_AssocLight; }
+ void ReplaceLightName(FName newName) { m_AssocLight = newName; }
+protected:
+ char m_FrameName[8];
+ FName m_ActorName, m_AssocLight;
+};
+
+TArray LightAssociations;
+
+
+//==========================================================================
+//
+// Light definitions
+//
+//==========================================================================
+class FLightDefaults
+{
+public:
+ FLightDefaults(FName name, ELightType type);
+
+ void ApplyProperties(ADynamicLight * light) const;
+ FName GetName() const { return m_Name; }
+ void SetParameter(double p) { m_Param = p; }
+ void SetArg(int arg, BYTE val) { m_Args[arg] = val; }
+ BYTE GetArg(int arg) { return m_Args[arg]; }
+ void SetOffset(float* ft) { m_Pos.X = ft[0]; m_Pos.Z = ft[1]; m_Pos.Y = ft[2]; }
+ void SetSubtractive(bool subtract) { m_subtractive = subtract; }
+ void SetAdditive(bool add) { m_additive = add; }
+ void SetDontLightSelf(bool add) { m_dontlightself = add; }
+ void SetAttenuate(bool on) { m_attenuate = on; }
+ void SetHalo(bool halo) { m_halo = halo; }
+protected:
+ FName m_Name;
+ unsigned char m_Args[5];
+ double m_Param;
+ DVector3 m_Pos;
+ ELightType m_type;
+ int8_t m_attenuate;
+ bool m_subtractive, m_additive, m_halo, m_dontlightself;
+};
+
+TArray LightDefaults;
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+FLightDefaults::FLightDefaults(FName name, ELightType type)
+{
+ m_Name = name;
+ m_type = type;
+
+ m_Pos.Zero();
+ memset(m_Args, 0, 5);
+ m_Param = 0;
+
+ m_subtractive = false;
+ m_additive = false;
+ m_halo = false;
+ m_dontlightself = false;
+ m_attenuate = -1;
+}
+
+void FLightDefaults::ApplyProperties(ADynamicLight * light) const
+{
+ auto oldtype = light->lighttype;
+
+ light->lighttype = m_type;
+ light->specialf1 = m_Param;
+ light->SetOffset(m_Pos);
+ light->halo = m_halo;
+ for (int a = 0; a < 3; a++) light->args[a] = clamp((int)(m_Args[a]), 0, 255);
+ light->m_Radius[0] = int(m_Args[LIGHT_INTENSITY]);
+ light->m_Radius[1] = int(m_Args[LIGHT_SECONDARY_INTENSITY]);
+ light->flags4 &= ~(MF4_ADDITIVE | MF4_SUBTRACTIVE | MF4_DONTLIGHTSELF);
+ if (m_subtractive) light->flags4 |= MF4_SUBTRACTIVE;
+ if (m_additive) light->flags4 |= MF4_ADDITIVE;
+ if (m_dontlightself) light->flags4 |= MF4_DONTLIGHTSELF;
+ light->m_tickCount = 0;
+ if (m_type == PulseLight)
+ {
+ float pulseTime = float(m_Param / TICRATE);
+
+ light->m_lastUpdate = level.maptime;
+ light->m_cycler.SetParams(float(light->m_Radius[1]), float(light->m_Radius[0]), pulseTime, oldtype == PulseLight);
+ light->m_cycler.ShouldCycle(true);
+ light->m_cycler.SetCycleType(CYCLE_Sin);
+ light->m_currentRadius = light->m_cycler.GetVal();
+ }
+
+ switch (m_attenuate)
+ {
+ case 0: light->flags4 &= ~MF4_ATTENUATE; break;
+ case 1: light->flags4 |= MF4_ATTENUATE; break;
+ default: if (glset.attenuate) light->flags4 |= MF4_ATTENUATE; else light->flags4 &= ~MF4_ATTENUATE; break;
+ }
+ }
+
+
+//==========================================================================
+//
+// light definition file parser
+//
+//==========================================================================
+
+
+static const char *LightTags[]=
+{
+ "color",
+ "size",
+ "secondarySize",
+ "offset",
+ "chance",
+ "interval",
+ "scale",
+ "frame",
+ "light",
+ "{",
+ "}",
+ "subtractive",
+ "additive",
+ "halo",
+ "dontlightself",
+ "attenuate",
+ NULL
+};
+
+
+enum {
+ LIGHTTAG_COLOR,
+ LIGHTTAG_SIZE,
+ LIGHTTAG_SECSIZE,
+ LIGHTTAG_OFFSET,
+ LIGHTTAG_CHANCE,
+ LIGHTTAG_INTERVAL,
+ LIGHTTAG_SCALE,
+ LIGHTTAG_FRAME,
+ LIGHTTAG_LIGHT,
+ LIGHTTAG_OPENBRACE,
+ LIGHTTAG_CLOSEBRACE,
+ LIGHTTAG_SUBTRACTIVE,
+ LIGHTTAG_ADDITIVE,
+ LIGHTTAG_HALO,
+ LIGHTTAG_DONTLIGHTSELF,
+ LIGHTTAG_ATTENUATE
+};
+
+
+extern int ScriptDepth;
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+inline float gl_ParseFloat(FScanner &sc)
+{
+ sc.GetFloat();
+
+ return float(sc.Float);
+}
+
+
+inline int gl_ParseInt(FScanner &sc)
+{
+ sc.GetNumber();
+
+ return sc.Number;
+}
+
+
+inline char *gl_ParseString(FScanner &sc)
+{
+ sc.GetString();
+
+ return sc.String;
+}
+
+
+void gl_ParseTriple(FScanner &sc, float floatVal[3])
+{
+ for (int i = 0; i < 3; i++)
+ {
+ floatVal[i] = gl_ParseFloat(sc);
+ }
+}
+
+
+void gl_AddLightDefaults(FLightDefaults *defaults)
+{
+ FLightDefaults *temp;
+ unsigned int i;
+
+ // remove duplicates
+ for (i = 0; i < LightDefaults.Size(); i++)
+ {
+ temp = LightDefaults[i];
+ if (temp->GetName() == defaults->GetName())
+ {
+ delete temp;
+ LightDefaults.Delete(i);
+ break;
+ }
+ }
+
+ LightDefaults.Push(defaults);
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_ParsePointLight(FScanner &sc)
+{
+ int type;
+ float floatTriple[3];
+ int intVal;
+ FLightDefaults *defaults;
+
+ // get name
+ sc.GetString();
+ FName name = sc.String;
+
+ // check for opening brace
+ sc.GetString();
+ if (sc.Compare("{"))
+ {
+ defaults = new FLightDefaults(name, PointLight);
+ ScriptDepth++;
+ while (ScriptDepth)
+ {
+ sc.GetString();
+ type = sc.MatchString(LightTags);
+ switch (type)
+ {
+ case LIGHTTAG_OPENBRACE:
+ ScriptDepth++;
+ break;
+ case LIGHTTAG_CLOSEBRACE:
+ ScriptDepth--;
+ break;
+ case LIGHTTAG_COLOR:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255));
+ defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255));
+ defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255));
+ break;
+ case LIGHTTAG_OFFSET:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetOffset(floatTriple);
+ break;
+ case LIGHTTAG_SIZE:
+ intVal = clamp(gl_ParseInt(sc), 0, 255);
+ defaults->SetArg(LIGHT_INTENSITY, intVal);
+ break;
+ case LIGHTTAG_SUBTRACTIVE:
+ defaults->SetSubtractive(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_ADDITIVE:
+ defaults->SetAdditive(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_HALO:
+ defaults->SetHalo(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_DONTLIGHTSELF:
+ defaults->SetDontLightSelf(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_ATTENUATE:
+ defaults->SetAttenuate(gl_ParseInt(sc) != 0);
+ break;
+ default:
+ sc.ScriptError("Unknown tag: %s\n", sc.String);
+ }
+ }
+ gl_AddLightDefaults(defaults);
+ }
+ else
+ {
+ sc.ScriptError("Expected '{'.\n");
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_ParsePulseLight(FScanner &sc)
+{
+ int type;
+ float floatVal, floatTriple[3];
+ int intVal;
+ FLightDefaults *defaults;
+
+ // get name
+ sc.GetString();
+ FName name = sc.String;
+
+ // check for opening brace
+ sc.GetString();
+ if (sc.Compare("{"))
+ {
+ defaults = new FLightDefaults(name, PulseLight);
+ ScriptDepth++;
+ while (ScriptDepth)
+ {
+ sc.GetString();
+ type = sc.MatchString(LightTags);
+ switch (type)
+ {
+ case LIGHTTAG_OPENBRACE:
+ ScriptDepth++;
+ break;
+ case LIGHTTAG_CLOSEBRACE:
+ ScriptDepth--;
+ break;
+ case LIGHTTAG_COLOR:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255));
+ defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255));
+ defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255));
+ break;
+ case LIGHTTAG_OFFSET:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetOffset(floatTriple);
+ break;
+ case LIGHTTAG_SIZE:
+ intVal = clamp(gl_ParseInt(sc), 0, 255);
+ defaults->SetArg(LIGHT_INTENSITY, intVal);
+ break;
+ case LIGHTTAG_SECSIZE:
+ intVal = clamp(gl_ParseInt(sc), 0, 255);
+ defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal);
+ break;
+ case LIGHTTAG_INTERVAL:
+ floatVal = gl_ParseFloat(sc);
+ defaults->SetParameter(floatVal * TICRATE);
+ break;
+ case LIGHTTAG_SUBTRACTIVE:
+ defaults->SetSubtractive(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_HALO:
+ defaults->SetHalo(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_DONTLIGHTSELF:
+ defaults->SetDontLightSelf(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_ATTENUATE:
+ defaults->SetAttenuate(gl_ParseInt(sc) != 0);
+ break;
+ default:
+ sc.ScriptError("Unknown tag: %s\n", sc.String);
+ }
+ }
+ if (defaults->GetArg(LIGHT_INTENSITY) > defaults->GetArg(LIGHT_SECONDARY_INTENSITY))
+ {
+ auto i = defaults->GetArg(LIGHT_INTENSITY);
+ auto j = defaults->GetArg(LIGHT_SECONDARY_INTENSITY);
+ defaults->SetArg(LIGHT_INTENSITY, j);
+ defaults->SetArg(LIGHT_SECONDARY_INTENSITY, i);
+ }
+
+ gl_AddLightDefaults(defaults);
+ }
+ else
+ {
+ sc.ScriptError("Expected '{'.\n");
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_ParseFlickerLight(FScanner &sc)
+{
+ int type;
+ float floatVal, floatTriple[3];
+ int intVal;
+ FLightDefaults *defaults;
+
+ // get name
+ sc.GetString();
+ FName name = sc.String;
+
+ // check for opening brace
+ sc.GetString();
+ if (sc.Compare("{"))
+ {
+ defaults = new FLightDefaults(name, FlickerLight);
+ ScriptDepth++;
+ while (ScriptDepth)
+ {
+ sc.GetString();
+ type = sc.MatchString(LightTags);
+ switch (type)
+ {
+ case LIGHTTAG_OPENBRACE:
+ ScriptDepth++;
+ break;
+ case LIGHTTAG_CLOSEBRACE:
+ ScriptDepth--;
+ break;
+ case LIGHTTAG_COLOR:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255));
+ defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255));
+ defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255));
+ break;
+ case LIGHTTAG_OFFSET:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetOffset(floatTriple);
+ break;
+ case LIGHTTAG_SIZE:
+ intVal = clamp(gl_ParseInt(sc), 0, 255);
+ defaults->SetArg(LIGHT_INTENSITY, intVal);
+ break;
+ case LIGHTTAG_SECSIZE:
+ intVal = clamp(gl_ParseInt(sc), 0, 255);
+ defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal);
+ break;
+ case LIGHTTAG_CHANCE:
+ floatVal = gl_ParseFloat(sc);
+ defaults->SetParameter(floatVal*360.);
+ break;
+ case LIGHTTAG_SUBTRACTIVE:
+ defaults->SetSubtractive(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_HALO:
+ defaults->SetHalo(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_DONTLIGHTSELF:
+ defaults->SetDontLightSelf(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_ATTENUATE:
+ defaults->SetAttenuate(gl_ParseInt(sc) != 0);
+ break;
+ default:
+ sc.ScriptError("Unknown tag: %s\n", sc.String);
+ }
+ }
+ gl_AddLightDefaults(defaults);
+ }
+ else
+ {
+ sc.ScriptError("Expected '{'.\n");
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_ParseFlickerLight2(FScanner &sc)
+{
+ int type;
+ float floatVal, floatTriple[3];
+ int intVal;
+ FLightDefaults *defaults;
+
+ // get name
+ sc.GetString();
+ FName name = sc.String;
+
+ // check for opening brace
+ sc.GetString();
+ if (sc.Compare("{"))
+ {
+ defaults = new FLightDefaults(name, RandomFlickerLight);
+ ScriptDepth++;
+ while (ScriptDepth)
+ {
+ sc.GetString();
+ type = sc.MatchString(LightTags);
+ switch (type)
+ {
+ case LIGHTTAG_OPENBRACE:
+ ScriptDepth++;
+ break;
+ case LIGHTTAG_CLOSEBRACE:
+ ScriptDepth--;
+ break;
+ case LIGHTTAG_COLOR:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255));
+ defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255));
+ defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255));
+ break;
+ case LIGHTTAG_OFFSET:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetOffset(floatTriple);
+ break;
+ case LIGHTTAG_SIZE:
+ intVal = clamp(gl_ParseInt(sc), 0, 255);
+ defaults->SetArg(LIGHT_INTENSITY, intVal);
+ break;
+ case LIGHTTAG_SECSIZE:
+ intVal = clamp(gl_ParseInt(sc), 0, 255);
+ defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal);
+ break;
+ case LIGHTTAG_INTERVAL:
+ floatVal = gl_ParseFloat(sc);
+ defaults->SetParameter(floatVal * 360.);
+ break;
+ case LIGHTTAG_SUBTRACTIVE:
+ defaults->SetSubtractive(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_HALO:
+ defaults->SetHalo(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_DONTLIGHTSELF:
+ defaults->SetDontLightSelf(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_ATTENUATE:
+ defaults->SetAttenuate(gl_ParseInt(sc) != 0);
+ break;
+ default:
+ sc.ScriptError("Unknown tag: %s\n", sc.String);
+ }
+ }
+ if (defaults->GetArg(LIGHT_SECONDARY_INTENSITY) < defaults->GetArg(LIGHT_INTENSITY))
+ {
+ BYTE v = defaults->GetArg(LIGHT_SECONDARY_INTENSITY);
+ defaults->SetArg(LIGHT_SECONDARY_INTENSITY, defaults->GetArg(LIGHT_INTENSITY));
+ defaults->SetArg(LIGHT_INTENSITY, v);
+ }
+ gl_AddLightDefaults(defaults);
+ }
+ else
+ {
+ sc.ScriptError("Expected '{'.\n");
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_ParseSectorLight(FScanner &sc)
+{
+ int type;
+ float floatVal;
+ float floatTriple[3];
+ FLightDefaults *defaults;
+
+ // get name
+ sc.GetString();
+ FName name = sc.String;
+
+ // check for opening brace
+ sc.GetString();
+ if (sc.Compare("{"))
+ {
+ defaults = new FLightDefaults(name, SectorLight);
+ ScriptDepth++;
+ while (ScriptDepth)
+ {
+ sc.GetString();
+ type = sc.MatchString(LightTags);
+ switch (type)
+ {
+ case LIGHTTAG_OPENBRACE:
+ ScriptDepth++;
+ break;
+ case LIGHTTAG_CLOSEBRACE:
+ ScriptDepth--;
+ break;
+ case LIGHTTAG_COLOR:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255));
+ defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255));
+ defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255));
+ break;
+ case LIGHTTAG_OFFSET:
+ gl_ParseTriple(sc, floatTriple);
+ defaults->SetOffset(floatTriple);
+ break;
+ case LIGHTTAG_SCALE:
+ floatVal = gl_ParseFloat(sc);
+ defaults->SetArg(LIGHT_SCALE, (BYTE)(floatVal * 255));
+ break;
+ case LIGHTTAG_SUBTRACTIVE:
+ defaults->SetSubtractive(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_HALO:
+ defaults->SetHalo(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_DONTLIGHTSELF:
+ defaults->SetDontLightSelf(gl_ParseInt(sc) != 0);
+ break;
+ case LIGHTTAG_ATTENUATE:
+ defaults->SetAttenuate(gl_ParseInt(sc) != 0);
+ break;
+ default:
+ sc.ScriptError("Unknown tag: %s\n", sc.String);
+ }
+ }
+ gl_AddLightDefaults(defaults);
+ }
+ else
+ {
+ sc.ScriptError("Expected '{'.\n");
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_AddLightAssociation(const char *actor, const char *frame, const char *light)
+{
+ FLightAssociation *temp;
+ unsigned int i;
+ FLightAssociation assoc(actor, frame, light);
+
+ for (i = 0; i < LightAssociations.Size(); i++)
+ {
+ temp = &LightAssociations[i];
+ if (temp->ActorName() == assoc.ActorName())
+ {
+ if (strcmp(temp->FrameName(), assoc.FrameName()) == 0)
+ {
+ temp->ReplaceLightName(assoc.Light());
+ return;
+ }
+ }
+ }
+
+ LightAssociations.Push(assoc);
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_ParseFrame(FScanner &sc, FString name)
+{
+ int type, startDepth;
+ FString frameName;
+
+ // get name
+ sc.GetString();
+ if (strlen(sc.String) > 8)
+ {
+ sc.ScriptError("Name longer than 8 characters: %s\n", sc.String);
+ }
+ frameName = sc.String;
+
+ startDepth = ScriptDepth;
+
+ // check for opening brace
+ sc.GetString();
+ if (sc.Compare("{"))
+ {
+ ScriptDepth++;
+ while (ScriptDepth > startDepth)
+ {
+ sc.GetString();
+ type = sc.MatchString(LightTags);
+ switch (type)
+ {
+ case LIGHTTAG_OPENBRACE:
+ ScriptDepth++;
+ break;
+ case LIGHTTAG_CLOSEBRACE:
+ ScriptDepth--;
+ break;
+ case LIGHTTAG_LIGHT:
+ gl_ParseString(sc);
+ gl_AddLightAssociation(name, frameName, sc.String);
+ break;
+ default:
+ sc.ScriptError("Unknown tag: %s\n", sc.String);
+ }
+ }
+ }
+ else
+ {
+ sc.ScriptError("Expected '{'.\n");
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_ParseObject(FScanner &sc)
+{
+ int type;
+ FString name;
+
+ // get name
+ sc.GetString();
+ name = sc.String;
+ if (!PClass::FindClass(name))
+ sc.ScriptMessage("Warning: dynamic lights attached to non-existent actor %s\n", name.GetChars());
+
+ // check for opening brace
+ sc.GetString();
+ if (sc.Compare("{"))
+ {
+ ScriptDepth++;
+ while (ScriptDepth)
+ {
+ sc.GetString();
+ type = sc.MatchString(LightTags);
+ switch (type)
+ {
+ case LIGHTTAG_OPENBRACE:
+ ScriptDepth++;
+ break;
+ case LIGHTTAG_CLOSEBRACE:
+ ScriptDepth--;
+ break;
+ case LIGHTTAG_FRAME:
+ gl_ParseFrame(sc, name);
+ break;
+ default:
+ sc.ScriptError("Unknown tag: %s\n", sc.String);
+ }
+ }
+ }
+ else
+ {
+ sc.ScriptError("Expected '{'.\n");
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+void gl_ReleaseLights()
+{
+ unsigned int i;
+
+ for (i = 0; i < LightDefaults.Size(); i++)
+ {
+ delete LightDefaults[i];
+ }
+
+ LightAssociations.Clear();
+ LightDefaults.Clear();
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+// these are the core types available in the *DEFS lump
+static const char *CoreKeywords[]=
+{
+ "pointlight",
+ "pulselight",
+ "flickerlight",
+ "flickerlight2",
+ "sectorlight",
+ "object",
+ "clearlights",
+ "shader",
+ "clearshaders",
+ "skybox",
+ "glow",
+ "brightmap",
+ "disable_fullbright",
+ "hardwareshader",
+ "detail",
+ "#include",
+ NULL
+};
+
+
+enum
+{
+ LIGHT_POINT,
+ LIGHT_PULSE,
+ LIGHT_FLICKER,
+ LIGHT_FLICKER2,
+ LIGHT_SECTOR,
+ LIGHT_OBJECT,
+ LIGHT_CLEAR,
+ TAG_SHADER,
+ TAG_CLEARSHADERS,
+ TAG_SKYBOX,
+ TAG_GLOW,
+ TAG_BRIGHTMAP,
+ TAG_DISABLE_FB,
+ TAG_HARDWARESHADER,
+ TAG_DETAIL,
+ TAG_INCLUDE,
+};
+
+
+//==========================================================================
+//
+// This is only here so any shader definition for ZDoomGL can be skipped
+// There is no functionality for this stuff!
+//
+//==========================================================================
+bool gl_ParseShader(FScanner &sc)
+{
+ int ShaderDepth = 0;
+
+ if (sc.GetString())
+ {
+ char *tmp;
+
+ tmp = strstr(sc.String, "{");
+ while (tmp)
+ {
+ ShaderDepth++;
+ tmp++;
+ tmp = strstr(tmp, "{");
+ }
+
+ tmp = strstr(sc.String, "}");
+ while (tmp)
+ {
+ ShaderDepth--;
+ tmp++;
+ tmp = strstr(tmp, "}");
+ }
+
+ if (ShaderDepth == 0) return true;
+ }
+ return false;
+}
+
+//==========================================================================
+//
+// Light associations per actor class
+//
+// Turn this inefficient mess into something that can be used at run time.
+//
+//==========================================================================
+
+class FInternalLightAssociation
+{
+public:
+ FInternalLightAssociation(FLightAssociation * asso);
+ int Sprite() const { return m_sprite; }
+ int Frame() const { return m_frame; }
+ const FLightDefaults *Light() const { return m_AssocLight; }
+protected:
+ int m_sprite;
+ int m_frame;
+ FLightDefaults * m_AssocLight;
+};
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FInternalLightAssociation::FInternalLightAssociation(FLightAssociation * asso)
+{
+
+ m_AssocLight=NULL;
+ for(unsigned int i=0;iGetName() == asso->Light())
+ {
+ m_AssocLight = LightDefaults[i];
+ break;
+ }
+ }
+
+ m_sprite=-1;
+ m_frame = -1;
+ for (unsigned i = 0; i < sprites.Size (); ++i)
+ {
+ if (strncmp (sprites[i].name, asso->FrameName(), 4) == 0)
+ {
+ m_sprite = (int)i;
+ break;
+ }
+ }
+
+ // Only handle lights for full frames.
+ // I won't bother with special lights for single rotations
+ // because there is no decent use for them!
+ if (strlen(asso->FrameName())==5 || asso->FrameName()[5]=='0')
+ {
+ m_frame = toupper(asso->FrameName()[4])-'A';
+ }
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+inline TDeletingArray * gl_GetActorLights(AActor * actor)
+{
+ return (TDeletingArray*)actor->lightassociations;
+}
+
+TDeletingArray< TDeletingArray * > AssoDeleter;
+
+//==========================================================================
+//
+// State lights
+//
+//==========================================================================
+TArray ParsedStateLights;
+TArray StateLights;
+
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void gl_InitializeActorLights()
+{
+ for(unsigned int i=0;ilightassociations)
+ {
+ TDeletingArray *p =new TDeletingArray;
+ defaults->lightassociations = p;
+ AssoDeleter.Push(p);
+ }
+ TDeletingArray * lights = gl_GetActorLights(defaults);
+ if (iasso->Light()==NULL)
+ {
+ // The definition was not valid.
+ delete iasso;
+ }
+ else
+ {
+ lights->Push(iasso);
+ }
+ }
+ }
+ }
+ // we don't need the parser data for the light associations anymore
+ LightAssociations.Clear();
+ LightAssociations.ShrinkToFit();
+
+ StateLights.Resize(ParsedStateLights.Size()+1);
+ for(unsigned i=0; iGetName() == ParsedStateLights[i])
+ {
+ StateLights[i] = LightDefaults[j];
+ break;
+ }
+ }
+ }
+ else StateLights[i] = NULL;
+ }
+ StateLights[StateLights.Size()-1] = NULL; // terminator
+ ParsedStateLights.Clear();
+ ParsedStateLights.ShrinkToFit();
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void gl_AttachLight(AActor *actor, unsigned int count, const FLightDefaults *lightdef)
+{
+ ADynamicLight *light;
+
+ // I'm skipping the single rotations because that really doesn't make sense!
+ if (count < actor->dynamiclights.Size())
+ {
+ light = barrier_cast(actor->dynamiclights[count]);
+ assert(light != NULL);
+ }
+ else
+ {
+ light = Spawn(actor->Pos(), NO_REPLACE);
+ light->target = actor;
+ light->owned = true;
+ light->ObjectFlags |= OF_Transient;
+ //light->flags4 |= MF4_ATTENUATE;
+ actor->dynamiclights.Push(light);
+ }
+ light->flags2&=~MF2_DORMANT;
+ lightdef->ApplyProperties(light);
+}
+
+//==========================================================================
+//
+// per-state light adjustment
+//
+//==========================================================================
+
+void gl_SetActorLights(AActor *actor)
+{
+ TArray * l = gl_GetActorLights(actor);
+ unsigned int count = 0;
+
+ All.Clock();
+ if (actor->state == NULL) return;
+ if (l)
+ {
+ TArray & LightAssociations=*l;
+ ADynamicLight *lights, *tmpLight;
+ unsigned int i;
+
+ int sprite = actor->sprite;
+ int frame = actor->frame;
+
+ lights = tmpLight = NULL;
+
+
+ for (i = 0; i < LightAssociations.Size(); i++)
+ {
+ if (LightAssociations[i]->Sprite() == sprite &&
+ (LightAssociations[i]->Frame()==frame || LightAssociations[i]->Frame()==-1))
+ {
+ gl_AttachLight(actor, count++, LightAssociations[i]->Light());
+ }
+ }
+ }
+ if (count == 0 && actor->state->Light > 0)
+ {
+ for(int i= actor->state->Light; StateLights[i] != NULL; i++)
+ {
+ if (StateLights[i] != (FLightDefaults*)-1)
+ {
+ gl_AttachLight(actor, count++, StateLights[i]);
+ }
+ }
+ }
+
+ for(;countdynamiclights.Size();count++)
+ {
+ actor->dynamiclights[count]->flags2|=MF2_DORMANT;
+ memset(actor->dynamiclights[count]->args, 0, sizeof(actor->args));
+ }
+ All.Unclock();
+}
+
+//==========================================================================
+//
+// This is called before saving the game
+//
+//==========================================================================
+
+void gl_DeleteAllAttachedLights()
+{
+ TThinkerIterator it;
+ AActor * a;
+ ADynamicLight * l;
+
+ while ((a=it.Next()))
+ {
+ a->dynamiclights.Clear();
+ }
+
+ TThinkerIterator it2;
+
+ l=it2.Next();
+ while (l)
+ {
+ ADynamicLight * ll = it2.Next();
+ if (l->owned) l->Destroy();
+ l=ll;
+ }
+
+
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void gl_RecreateAllAttachedLights()
+{
+ TThinkerIterator it;
+ AActor * a;
+
+ while ((a=it.Next()))
+ {
+ gl_SetActorLights(a);
+ }
+}
+
+
+//==========================================================================
+// The actual light def parsing code is there.
+// DoParseDefs is no longer called directly by ParseDefs, now it's called
+// by LoadDynLightDefs, which wasn't simply integrated into ParseDefs
+// because of the way the code needs to load two out of five lumps.
+//==========================================================================
+void gl_DoParseDefs(FScanner &sc, int workingLump)
+{
+ int recursion=0;
+ int lump, type;
+
+ // Get actor class name.
+ while (true)
+ {
+ sc.SavePos();
+ if (!sc.GetToken ())
+ {
+ return;
+ }
+ type = sc.MatchString(CoreKeywords);
+ switch (type)
+ {
+ case TAG_INCLUDE:
+ {
+ sc.MustGetString();
+ // This is not using sc.Open because it can print a more useful error message when done here
+ lump = Wads.CheckNumForFullName(sc.String, true);
+ if (lump==-1)
+ sc.ScriptError("Lump '%s' not found", sc.String);
+
+ FScanner newscanner(lump);
+ gl_DoParseDefs(newscanner, lump);
+ break;
+ }
+ case LIGHT_POINT:
+ gl_ParsePointLight(sc);
+ break;
+ case LIGHT_PULSE:
+ gl_ParsePulseLight(sc);
+ break;
+ case LIGHT_FLICKER:
+ gl_ParseFlickerLight(sc);
+ break;
+ case LIGHT_FLICKER2:
+ gl_ParseFlickerLight2(sc);
+ break;
+ case LIGHT_SECTOR:
+ gl_ParseSectorLight(sc);
+ break;
+ case LIGHT_OBJECT:
+ gl_ParseObject(sc);
+ break;
+ case LIGHT_CLEAR:
+ gl_ReleaseLights();
+ break;
+ case TAG_SHADER:
+ gl_ParseShader(sc);
+ break;
+ case TAG_CLEARSHADERS:
+ break;
+ case TAG_SKYBOX:
+ gl_ParseSkybox(sc);
+ break;
+ case TAG_GLOW:
+ gl_InitGlow(sc);
+ break;
+ case TAG_BRIGHTMAP:
+ gl_ParseBrightmap(sc, workingLump);
+ break;
+ case TAG_HARDWARESHADER:
+ gl_ParseHardwareShader(sc, workingLump);
+ break;
+ case TAG_DETAIL:
+ gl_ParseDetailTexture(sc);
+ break;
+ case TAG_DISABLE_FB:
+ {
+ /* not implemented.
+ sc.MustGetString();
+ const PClass *cls = PClass::FindClass(sc.String);
+ if (cls) GetDefaultByType(cls)->renderflags |= RF_NEVERFULLBRIGHT;
+ */
+ }
+ break;
+ default:
+ sc.ScriptError("Error parsing defs. Unknown tag: %s.\n", sc.String);
+ break;
+ }
+ }
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void gl_LoadGLDefs(const char * defsLump)
+{
+ int workingLump, lastLump;
+
+ lastLump = 0;
+ while ((workingLump = Wads.FindLump(defsLump, &lastLump)) != -1)
+ {
+ FScanner sc(workingLump);
+ gl_DoParseDefs(sc, workingLump);
+ }
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void gl_ParseDefs()
+{
+ const char *defsLump = NULL;
+
+ atterm( gl_ReleaseLights );
+ gl_ReleaseLights();
+ gl_DestroyUserShaders();
+ switch (gameinfo.gametype)
+ {
+ case GAME_Heretic:
+ defsLump = "HTICDEFS";
+ break;
+ case GAME_Hexen:
+ defsLump = "HEXNDEFS";
+ break;
+ case GAME_Strife:
+ defsLump = "STRFDEFS";
+ break;
+ case GAME_Doom:
+ defsLump = "DOOMDEFS";
+ break;
+ case GAME_Chex:
+ defsLump = "CHEXDEFS";
+ break;
+ default: // silence GCC
+ break;
+ }
+ gl_ParseVavoomSkybox();
+ if (defsLump != NULL) gl_LoadGLDefs(defsLump);
+ gl_LoadGLDefs("GLDEFS");
+ gl_InitializeActorLights();
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void AddStateLight(FState *State, const char *lname)
+{
+ if (State->Light == 0)
+ {
+ ParsedStateLights.Push(NAME_None);
+ State->Light = ParsedStateLights.Push(FName(lname));
+ }
+ else
+ {
+ ParsedStateLights.Push(FName(lname));
+ }
+}
diff --git a/src/gl/dynlights/gl_dynlight.h b/src/gl/dynlights/gl_dynlight.h
new file mode 100644
index 000000000..f71f0dfdf
--- /dev/null
+++ b/src/gl/dynlights/gl_dynlight.h
@@ -0,0 +1,211 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+
+#ifndef __GLC_DYNLIGHT_H
+#define __GLC_DYNLIGHT_H
+
+#include "c_cvars.h"
+#include "gl/utility/gl_geometric.h"
+#include "gl/utility/gl_cycler.h"
+
+
+EXTERN_CVAR(Bool, gl_lights)
+EXTERN_CVAR(Bool, gl_attachedlights)
+
+class ADynamicLight;
+class FSerializer;
+class FLightDefaults;
+
+
+enum
+{
+ LIGHT_RED = 0,
+ LIGHT_GREEN = 1,
+ LIGHT_BLUE = 2,
+ LIGHT_INTENSITY = 3,
+ LIGHT_SECONDARY_INTENSITY = 4,
+ LIGHT_SCALE = 3,
+};
+
+// This is as good as something new - and it can be set directly in the ActorInfo!
+#define MF4_SUBTRACTIVE MF4_MISSILEEVENMORE
+#define MF4_ADDITIVE MF4_MISSILEMORE
+#define MF4_DONTLIGHTSELF MF4_SEESDAGGERS
+#define MF4_ATTENUATE MF4_INCOMBAT
+
+enum ELightType
+{
+ PointLight,
+ PulseLight,
+ FlickerLight,
+ RandomFlickerLight,
+ SectorLight,
+ SpotLight,
+ ColorPulseLight,
+ ColorFlickerLight,
+ RandomColorFlickerLight
+};
+
+
+struct FLightNode
+{
+ FLightNode ** prevTarget;
+ FLightNode * nextTarget;
+ FLightNode ** prevLight;
+ FLightNode * nextLight;
+ ADynamicLight * lightsource;
+ union
+ {
+ side_t * targLine;
+ subsector_t * targSubsector;
+ void * targ;
+ };
+};
+
+
+//
+// Base class
+//
+// [CO] I merged everything together in this one class so that I don't have
+// to create and re-create an excessive amount of objects
+//
+
+class ADynamicLight : public AActor
+{
+ friend class FLightDefaults;
+ DECLARE_CLASS (ADynamicLight, AActor)
+public:
+ virtual void Tick();
+ void Serialize(FSerializer &arc);
+ void PostSerialize();
+ BYTE GetRed() const { return args[LIGHT_RED]; }
+ BYTE GetGreen() const { return args[LIGHT_GREEN]; }
+ BYTE GetBlue() const { return args[LIGHT_BLUE]; }
+ float GetRadius() const { return (IsActive() ? m_currentRadius * 2.f : 0.f); }
+ void LinkLight();
+ void UnlinkLight();
+ size_t PointerSubstitution (DObject *old, DObject *notOld);
+
+ void BeginPlay();
+ void SetOrigin (double x, double y, double z, bool moving = false);
+ void PostBeginPlay();
+ void Destroy();
+ void Activate(AActor *activator);
+ void Deactivate(AActor *activator);
+ void SetOffset(const DVector3 &pos);
+ void UpdateLocation();
+ bool IsOwned() const { return owned; }
+ bool IsActive() const { return !(flags2&MF2_DORMANT); }
+ bool IsSubtractive() { return !!(flags4&MF4_SUBTRACTIVE); }
+ bool IsAdditive() { return !!(flags4&MF4_ADDITIVE); }
+ FState *targetState;
+ FLightNode * touching_sides;
+ FLightNode * touching_subsectors;
+ FLightNode * touching_sector;
+
+private:
+ double DistToSeg(const DVector3 &pos, seg_t *seg);
+ void CollectWithinRadius(const DVector3 &pos, subsector_t *subSec, float radius);
+
+protected:
+ DVector3 m_off;
+ float m_currentRadius;
+ unsigned int m_lastUpdate;
+ FCycler m_cycler;
+ subsector_t * subsector;
+
+public:
+ int m_tickCount;
+ int m_Radius[2];
+ BYTE lightflags;
+ BYTE lighttype;
+ bool owned;
+ bool halo;
+ BYTE color2[3];
+ bool visibletoplayer;
+ int bufferindex;
+
+
+};
+
+class AVavoomLight : public ADynamicLight
+{
+ DECLARE_CLASS (AVavoomLight, ADynamicLight)
+public:
+ virtual void BeginPlay();
+};
+
+class AVavoomLightWhite : public AVavoomLight
+{
+ DECLARE_CLASS (AVavoomLightWhite, AVavoomLight)
+public:
+ virtual void BeginPlay();
+};
+
+class AVavoomLightColor : public AVavoomLight
+{
+ DECLARE_CLASS (AVavoomLightColor, AVavoomLight)
+public:
+ void BeginPlay();
+};
+
+
+enum
+{
+ STAT_DLIGHT=64
+};
+
+struct FDynLightData
+{
+ TArray arrays[3];
+
+ void Clear()
+ {
+ arrays[0].Clear();
+ arrays[1].Clear();
+ arrays[2].Clear();
+ }
+
+ void Combine(int *siz, int max)
+ {
+ siz[0] = arrays[0].Size();
+ siz[1] = siz[0] + arrays[1].Size();
+ siz[2] = siz[1] + arrays[2].Size();
+ arrays[0].Resize(arrays[0].Size() + arrays[1].Size() + arrays[2].Size());
+ memcpy(&arrays[0][siz[0]], &arrays[1][0], arrays[1].Size() * sizeof(float));
+ memcpy(&arrays[0][siz[1]], &arrays[2][0], arrays[2].Size() * sizeof(float));
+ siz[0]>>=2;
+ siz[1]>>=2;
+ siz[2]>>=2;
+ if (siz[0] > max) siz[0] = max;
+ if (siz[1] > max) siz[1] = max;
+ if (siz[2] > max) siz[2] = max;
+ }
+};
+
+
+
+bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FDynLightData &data);
+void gl_UploadLights(FDynLightData &data);
+
+
+#endif
diff --git a/src/gl/dynlights/gl_dynlight1.cpp b/src/gl/dynlights/gl_dynlight1.cpp
new file mode 100644
index 000000000..2f8ef44ed
--- /dev/null
+++ b/src/gl/dynlights/gl_dynlight1.cpp
@@ -0,0 +1,122 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2002-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_dynlight1.cpp
+** dynamic light application
+**
+**/
+
+#include "gl/system/gl_system.h"
+#include "c_dispatch.h"
+#include "p_local.h"
+#include "vectors.h"
+#include "gl/gl_functions.h"
+#include "g_level.h"
+
+#include "gl/system/gl_interface.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/data/gl_data.h"
+#include "gl/dynlights/gl_dynlight.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/scene/gl_portal.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/textures/gl_material.h"
+
+
+//==========================================================================
+//
+// Light related CVARs
+//
+//==========================================================================
+
+CUSTOM_CVAR (Bool, gl_lights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
+{
+ if (self) gl_RecreateAllAttachedLights();
+ else gl_DeleteAllAttachedLights();
+}
+
+CVAR (Bool, gl_attachedlights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
+CVAR (Bool, gl_lights_checkside, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
+CVAR (Bool, gl_light_sprites, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
+CVAR (Bool, gl_light_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
+
+//==========================================================================
+//
+// Sets up the parameters to render one dynamic light onto one plane
+//
+//==========================================================================
+bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FDynLightData &ldata)
+{
+ int i = 0;
+
+ DVector3 pos = light->PosRelative(group);
+
+ float dist = fabsf(p.DistToPoint(pos.X, pos.Z, pos.Y));
+ float radius = (light->GetRadius());
+
+ if (radius <= 0.f) return false;
+ if (dist > radius) return false;
+ if (checkside && gl_lights_checkside && p.PointOnSide(pos.X, pos.Z, pos.Y))
+ {
+ return false;
+ }
+
+
+ float cs;
+ if (light->IsAdditive())
+ {
+ cs = 0.2f;
+ i = 2;
+ }
+ else
+ {
+ cs = 1.0f;
+ }
+
+ float r = light->GetRed() / 255.0f * cs;
+ float g = light->GetGreen() / 255.0f * cs;
+ float b = light->GetBlue() / 255.0f * cs;
+
+ if (light->IsSubtractive())
+ {
+ Vector v;
+
+ v.Set(r, g, b);
+ r = v.Length() - r;
+ g = v.Length() - g;
+ b = v.Length() - b;
+ i = 1;
+ }
+
+ float *data = &ldata.arrays[i][ldata.arrays[i].Reserve(8)];
+ data[0] = pos.X;
+ data[1] = pos.Z;
+ data[2] = pos.Y;
+ data[3] = radius;
+ data[4] = r;
+ data[5] = g;
+ data[6] = b;
+ data[7] = !!(light->flags4 & MF4_ATTENUATE);
+ return true;
+}
+
diff --git a/src/gl/dynlights/gl_glow.cpp b/src/gl/dynlights/gl_glow.cpp
new file mode 100644
index 000000000..eacbfa953
--- /dev/null
+++ b/src/gl/dynlights/gl_glow.cpp
@@ -0,0 +1,126 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_glow.cpp
+** Glowing flats like Doomsday
+**
+**/
+
+#include "w_wad.h"
+#include "sc_man.h"
+#include "v_video.h"
+#include "r_defs.h"
+#include "textures/textures.h"
+
+#include "gl/dynlights/gl_glow.h"
+
+//===========================================================================
+//
+// Reads glow definitions from GLDEFS
+//
+//===========================================================================
+void gl_InitGlow(FScanner &sc)
+{
+ sc.MustGetStringName("{");
+ while (!sc.CheckString("}"))
+ {
+ sc.MustGetString();
+ if (sc.Compare("FLATS"))
+ {
+ sc.MustGetStringName("{");
+ while (!sc.CheckString("}"))
+ {
+ sc.MustGetString();
+ FTextureID flump=TexMan.CheckForTexture(sc.String, FTexture::TEX_Flat,FTextureManager::TEXMAN_TryAny);
+ FTexture *tex = TexMan[flump];
+ if (tex) tex->gl_info.bGlowing = tex->gl_info.bFullbright = true;
+ }
+ }
+ else if (sc.Compare("WALLS"))
+ {
+ sc.MustGetStringName("{");
+ while (!sc.CheckString("}"))
+ {
+ sc.MustGetString();
+ FTextureID flump=TexMan.CheckForTexture(sc.String, FTexture::TEX_Wall,FTextureManager::TEXMAN_TryAny);
+ FTexture *tex = TexMan[flump];
+ if (tex) tex->gl_info.bGlowing = tex->gl_info.bFullbright = true;
+ }
+ }
+ else if (sc.Compare("TEXTURE"))
+ {
+ sc.SetCMode(true);
+ sc.MustGetString();
+ FTextureID flump=TexMan.CheckForTexture(sc.String, FTexture::TEX_Flat,FTextureManager::TEXMAN_TryAny);
+ FTexture *tex = TexMan[flump];
+ sc.MustGetStringName(",");
+ sc.MustGetString();
+ PalEntry color = V_GetColor(NULL, sc.String);
+ //sc.MustGetStringName(",");
+ //sc.MustGetNumber();
+ if (sc.CheckString(","))
+ {
+ if (sc.CheckNumber())
+ {
+ if (tex) tex->gl_info.GlowHeight = sc.Number;
+ if (!sc.CheckString(",")) goto skip_fb;
+ }
+
+ sc.MustGetStringName("fullbright");
+ if (tex) tex->gl_info.bFullbright = true;
+ }
+ skip_fb:
+ sc.SetCMode(false);
+
+ if (tex && color != 0)
+ {
+ tex->gl_info.bGlowing = true;
+ tex->gl_info.GlowColor = color;
+ }
+ }
+ }
+}
+
+
+//==========================================================================
+//
+// Checks whether a sprite should be affected by a glow
+//
+//==========================================================================
+int gl_CheckSpriteGlow(sector_t *sec, int lightlevel, const DVector3 &pos)
+{
+ FTextureID floorpic = sec->GetTexture(sector_t::floor);
+ FTexture *tex = TexMan[floorpic];
+ if (tex != NULL && tex->isGlowing())
+ {
+ double floordiff = pos.Z - sec->floorplane.ZatPoint(pos);
+ if (floordiff < tex->gl_info.GlowHeight && tex->gl_info.GlowHeight != 0)
+ {
+ int maxlight = (255 + lightlevel) >> 1;
+ double lightfrac = floordiff / tex->gl_info.GlowHeight;
+ if (lightfrac < 0) lightfrac = 0;
+ lightlevel = int(lightfrac*lightlevel + maxlight*(1 - lightfrac));
+ }
+ }
+ return lightlevel;
+}
+
diff --git a/src/gl/dynlights/gl_glow.h b/src/gl/dynlights/gl_glow.h
new file mode 100644
index 000000000..7d9ae9f7f
--- /dev/null
+++ b/src/gl/dynlights/gl_glow.h
@@ -0,0 +1,10 @@
+
+#ifndef __GL_GLOW
+#define __GL_GLOW
+
+struct sector_t;
+
+void gl_InitGlow(const char * lumpnm);
+int gl_CheckSpriteGlow(sector_t *sec, int lightlevel, const DVector3 &pos);
+
+#endif
diff --git a/src/gl/dynlights/gl_lightbuffer.cpp b/src/gl/dynlights/gl_lightbuffer.cpp
new file mode 100644
index 000000000..b819a3be6
--- /dev/null
+++ b/src/gl/dynlights/gl_lightbuffer.cpp
@@ -0,0 +1,211 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2014-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_lightbuffer.cpp
+** Buffer data maintenance for dynamic lights
+**
+**/
+
+#include "gl/system/gl_system.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/dynlights/gl_lightbuffer.h"
+#include "gl/dynlights/gl_dynlight.h"
+#include "gl/system/gl_interface.h"
+#include "gl/utility//gl_clock.h"
+
+static const int INITIAL_BUFFER_SIZE = 160000; // This means 80000 lights per frame and 160000*16 bytes == 2.56 MB.
+
+float *mMap;
+
+FLightBuffer::FLightBuffer()
+{
+
+ mBufferSize = INITIAL_BUFFER_SIZE;
+ mByteSize = mBufferSize * sizeof(float);
+ if (gl.flags & RFL_SHADER_STORAGE_BUFFER)
+ {
+ mBufferType = GL_SHADER_STORAGE_BUFFER;
+ mBlockAlign = 0;
+ mBlockSize = mBufferSize;
+ }
+ else
+ {
+ mBufferType = GL_UNIFORM_BUFFER;
+ mBlockSize = gl.maxuniformblock / 16;
+ if (mBlockSize > 2048) mBlockSize = 2048; // we don't really need a larger buffer
+ mBlockAlign = mBlockSize / 2;
+ }
+
+ glGenBuffers(1, &mBufferId);
+ glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, mBufferId);
+ glBindBuffer(mBufferType, mBufferId); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
+ if (gl.lightmethod == LM_DIRECT)
+ {
+ glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
+ mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
+ }
+ else
+ {
+ glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
+ mBufferPointer = NULL;
+ }
+
+ Clear();
+ mLastMappedIndex = UINT_MAX;
+}
+
+FLightBuffer::~FLightBuffer()
+{
+ glBindBuffer(mBufferType, 0);
+ glDeleteBuffers(1, &mBufferId);
+}
+
+void FLightBuffer::Clear()
+{
+ mIndex = 0;
+ mIndices.Clear();
+ mUploadIndex = 0;
+}
+
+int FLightBuffer::UploadLights(FDynLightData &data)
+{
+ int size0 = data.arrays[0].Size()/4;
+ int size1 = data.arrays[1].Size()/4;
+ int size2 = data.arrays[2].Size()/4;
+ int totalsize = size0 + size1 + size2 + 1;
+
+ // pointless type casting because some compilers can't print enough warnings.
+ if (mBlockAlign > 0 && (unsigned int)totalsize + (mIndex % mBlockAlign) > mBlockSize)
+ {
+ mIndex = ((mIndex + mBlockAlign) / mBlockAlign) * mBlockAlign;
+
+ // can't be rendered all at once.
+ if ((unsigned int)totalsize > mBlockSize)
+ {
+ int diff = totalsize - (int)mBlockSize;
+
+ size2 -= diff;
+ if (size2 < 0)
+ {
+ size1 += size2;
+ size2 = 0;
+ }
+ if (size1 < 0)
+ {
+ size0 += size1;
+ size1 = 0;
+ }
+ totalsize = size0 + size1 + size2 + 1;
+ }
+ }
+
+ if (totalsize <= 1) return -1;
+
+ if (mIndex + totalsize > mBufferSize/4)
+ {
+ // reallocate the buffer with twice the size
+ unsigned int newbuffer;
+
+ // first unmap the old buffer
+ glBindBuffer(mBufferType, mBufferId);
+ glUnmapBuffer(mBufferType);
+
+ // create and bind the new buffer, bind the old one to a copy target (too bad that DSA is not yet supported well enough to omit this crap.)
+ glGenBuffers(1, &newbuffer);
+ glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, newbuffer);
+ glBindBuffer(mBufferType, newbuffer); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
+ glBindBuffer(GL_COPY_READ_BUFFER, mBufferId);
+
+ // create the new buffer's storage (twice as large as the old one)
+ mBufferSize *= 2;
+ mByteSize *= 2;
+ if (gl.lightmethod == LM_DIRECT)
+ {
+ glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
+ mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
+ }
+ else
+ {
+ glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
+ mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT);
+ }
+
+ // copy contents and delete the old buffer.
+ glCopyBufferSubData(GL_COPY_READ_BUFFER, mBufferType, 0, 0, mByteSize/2);
+ glBindBuffer(GL_COPY_READ_BUFFER, 0);
+ glDeleteBuffers(1, &mBufferId);
+ mBufferId = newbuffer;
+ }
+
+ float *copyptr;
+
+ assert(mBufferPointer != NULL);
+ if (mBufferPointer == NULL) return -1;
+ copyptr = mBufferPointer + mIndex * 4;
+
+ float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) };
+
+ memcpy(©ptr[0], parmcnt, 4 * sizeof(float));
+ memcpy(©ptr[4], &data.arrays[0][0], 4 * size0*sizeof(float));
+ memcpy(©ptr[4 + 4*size0], &data.arrays[1][0], 4 * size1*sizeof(float));
+ memcpy(©ptr[4 + 4*(size0 + size1)], &data.arrays[2][0], 4 * size2*sizeof(float));
+
+ unsigned int bufferindex = mIndex;
+ mIndex += totalsize;
+ draw_dlight += (totalsize-1) / 2;
+ return bufferindex;
+}
+
+void FLightBuffer::Begin()
+{
+ if (gl.lightmethod == LM_DEFERRED)
+ {
+ glBindBuffer(mBufferType, mBufferId);
+ mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT);
+ }
+}
+
+void FLightBuffer::Finish()
+{
+ if (gl.lightmethod == LM_DEFERRED)
+ {
+ glBindBuffer(mBufferType, mBufferId);
+ glUnmapBuffer(mBufferType);
+ mBufferPointer = NULL;
+ }
+}
+
+int FLightBuffer::BindUBO(unsigned int index)
+{
+ unsigned int offset = (index / mBlockAlign) * mBlockAlign;
+
+ if (offset != mLastMappedIndex)
+ {
+ // this will only get called if a uniform buffer is used. For a shader storage buffer we only need to bind the buffer once at the start to all shader programs
+ mLastMappedIndex = offset;
+ glBindBufferRange(GL_UNIFORM_BUFFER, LIGHTBUF_BINDINGPOINT, mBufferId, offset*16, mBlockSize*16); // we go from counting vec4's to counting bytes here.
+ }
+ return (index - offset);
+}
+
+
+
diff --git a/src/gl/dynlights/gl_lightbuffer.h b/src/gl/dynlights/gl_lightbuffer.h
new file mode 100644
index 000000000..8781ccb57
--- /dev/null
+++ b/src/gl/dynlights/gl_lightbuffer.h
@@ -0,0 +1,39 @@
+#ifndef __GL_LIGHTBUFFER_H
+#define __GL_LIGHTBUFFER_H
+
+#include "tarray.h"
+struct FDynLightData;
+
+class FLightBuffer
+{
+ TArray mIndices;
+ unsigned int mBufferId;
+ float * mBufferPointer;
+
+ unsigned int mBufferType;
+ unsigned int mIndex;
+ unsigned int mUploadIndex;
+ unsigned int mLastMappedIndex;
+ unsigned int mBlockAlign;
+ unsigned int mBlockSize;
+ unsigned int mBufferSize;
+ unsigned int mByteSize;
+
+public:
+
+ FLightBuffer();
+ ~FLightBuffer();
+ void Clear();
+ int UploadLights(FDynLightData &data);
+ void Begin();
+ void Finish();
+ int BindUBO(unsigned int index);
+ unsigned int GetBlockSize() const { return mBlockSize; }
+ unsigned int GetBufferType() const { return mBufferType; }
+ unsigned int GetIndexPtr() const { return mIndices.Size(); }
+ void StoreIndex(int index) { mIndices.Push(index); }
+ int GetIndex(int i) const { return mIndices[i]; }
+};
+
+#endif
+
diff --git a/src/gl/gl_functions.h b/src/gl/gl_functions.h
new file mode 100644
index 000000000..aa08d19be
--- /dev/null
+++ b/src/gl/gl_functions.h
@@ -0,0 +1,13 @@
+#ifndef __GL_FUNCT
+#define __GL_FUNCT
+
+#include "v_palette.h"
+
+class AActor;
+
+void gl_PreprocessLevel();
+void gl_CleanLevelData();
+void gl_LinkLights();
+void gl_SetActorLights(AActor *);
+
+#endif
diff --git a/src/gl/hqnx/common.h b/src/gl/hqnx/common.h
new file mode 100644
index 000000000..c3e700571
--- /dev/null
+++ b/src/gl/hqnx/common.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com )
+ *
+ * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
+ * Copyright (C) 2011 Francois Gannaz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __HQX_COMMON_H_
+#define __HQX_COMMON_H_
+
+#include
+#include
+
+#define MASK_2 0x0000FF00
+#define MASK_13 0x00FF00FF
+#define MASK_RGB 0x00FFFFFF
+#define MASK_ALPHA 0xFF000000
+
+#define Ymask 0x00FF0000
+#define Umask 0x0000FF00
+#define Vmask 0x000000FF
+#define trY 0x00300000
+#define trU 0x00000700
+#define trV 0x00000006
+
+/* RGB to YUV lookup table */
+extern uint32_t *RGBtoYUV;
+
+static inline uint32_t rgb_to_yuv(uint32_t c)
+{
+ // Mask against MASK_RGB to discard the alpha channel
+ return RGBtoYUV[MASK_RGB & c];
+}
+
+/* Test if there is difference in color */
+static inline int yuv_diff(uint32_t yuv1, uint32_t yuv2) {
+ return (( abs((int64_t)(yuv1 & Ymask) - (int64_t)(yuv2 & Ymask)) > trY ) ||
+ ( abs((int64_t)(yuv1 & Umask) - (int64_t)(yuv2 & Umask)) > trU ) ||
+ ( abs((int64_t)(yuv1 & Vmask) - (int64_t)(yuv2 & Vmask)) > trV ) );
+}
+
+static inline int Diff(uint32_t c1, uint32_t c2)
+{
+ return yuv_diff(rgb_to_yuv(c1), rgb_to_yuv(c2));
+}
+
+/* Interpolate functions */
+static inline uint32_t Interpolate_2(uint32_t c1, int w1, uint32_t c2, int w2, int s)
+{
+ if (c1 == c2) {
+ return c1;
+ }
+ return
+ (((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2) << (24-s)) & MASK_ALPHA) +
+ ((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2) >> s) & MASK_2) +
+ ((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2) >> s) & MASK_13);
+}
+
+static inline uint32_t Interpolate_3(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s)
+{
+ return
+ (((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2 + ((c3 & MASK_ALPHA) >> 24) * w3) << (24-s)) & MASK_ALPHA) +
+ ((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2 + (c3 & MASK_2) * w3) >> s) & MASK_2) +
+ ((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2 + (c3 & MASK_13) * w3) >> s) & MASK_13);
+}
+
+static inline uint32_t Interp1(uint32_t c1, uint32_t c2)
+{
+ //(c1*3+c2) >> 2;
+ return Interpolate_2(c1, 3, c2, 1, 2);
+}
+
+static inline uint32_t Interp2(uint32_t c1, uint32_t c2, uint32_t c3)
+{
+ //(c1*2+c2+c3) >> 2;
+ return Interpolate_3(c1, 2, c2, 1, c3, 1, 2);
+}
+
+static inline uint32_t Interp3(uint32_t c1, uint32_t c2)
+{
+ //(c1*7+c2)/8;
+ return Interpolate_2(c1, 7, c2, 1, 3);
+}
+
+static inline uint32_t Interp4(uint32_t c1, uint32_t c2, uint32_t c3)
+{
+ //(c1*2+(c2+c3)*7)/16;
+ return Interpolate_3(c1, 2, c2, 7, c3, 7, 4);
+}
+
+static inline uint32_t Interp5(uint32_t c1, uint32_t c2)
+{
+ //(c1+c2) >> 1;
+ return Interpolate_2(c1, 1, c2, 1, 1);
+}
+
+static inline uint32_t Interp6(uint32_t c1, uint32_t c2, uint32_t c3)
+{
+ //(c1*5+c2*2+c3)/8;
+ return Interpolate_3(c1, 5, c2, 2, c3, 1, 3);
+}
+
+static inline uint32_t Interp7(uint32_t c1, uint32_t c2, uint32_t c3)
+{
+ //(c1*6+c2+c3)/8;
+ return Interpolate_3(c1, 6, c2, 1, c3, 1, 3);
+}
+
+static inline uint32_t Interp8(uint32_t c1, uint32_t c2)
+{
+ //(c1*5+c2*3)/8;
+ return Interpolate_2(c1, 5, c2, 3, 3);
+}
+
+static inline uint32_t Interp9(uint32_t c1, uint32_t c2, uint32_t c3)
+{
+ //(c1*2+(c2+c3)*3)/8;
+ return Interpolate_3(c1, 2, c2, 3, c3, 3, 3);
+}
+
+static inline uint32_t Interp10(uint32_t c1, uint32_t c2, uint32_t c3)
+{
+ //(c1*14+c2+c3)/16;
+ return Interpolate_3(c1, 14, c2, 1, c3, 1, 4);
+}
+
+#endif
diff --git a/src/gl/hqnx/hq2x.cpp b/src/gl/hqnx/hq2x.cpp
new file mode 100644
index 000000000..637d1fb78
--- /dev/null
+++ b/src/gl/hqnx/hq2x.cpp
@@ -0,0 +1,2808 @@
+/*
+ * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com )
+ *
+ * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "common.h"
+#include "hqx.h"
+
+#define PIXEL00_0 *dp = w[5];
+#define PIXEL00_10 *dp = Interp1(w[5], w[1]);
+#define PIXEL00_11 *dp = Interp1(w[5], w[4]);
+#define PIXEL00_12 *dp = Interp1(w[5], w[2]);
+#define PIXEL00_20 *dp = Interp2(w[5], w[4], w[2]);
+#define PIXEL00_21 *dp = Interp2(w[5], w[1], w[2]);
+#define PIXEL00_22 *dp = Interp2(w[5], w[1], w[4]);
+#define PIXEL00_60 *dp = Interp6(w[5], w[2], w[4]);
+#define PIXEL00_61 *dp = Interp6(w[5], w[4], w[2]);
+#define PIXEL00_70 *dp = Interp7(w[5], w[4], w[2]);
+#define PIXEL00_90 *dp = Interp9(w[5], w[4], w[2]);
+#define PIXEL00_100 *dp = Interp10(w[5], w[4], w[2]);
+#define PIXEL01_0 *(dp+1) = w[5];
+#define PIXEL01_10 *(dp+1) = Interp1(w[5], w[3]);
+#define PIXEL01_11 *(dp+1) = Interp1(w[5], w[2]);
+#define PIXEL01_12 *(dp+1) = Interp1(w[5], w[6]);
+#define PIXEL01_20 *(dp+1) = Interp2(w[5], w[2], w[6]);
+#define PIXEL01_21 *(dp+1) = Interp2(w[5], w[3], w[6]);
+#define PIXEL01_22 *(dp+1) = Interp2(w[5], w[3], w[2]);
+#define PIXEL01_60 *(dp+1) = Interp6(w[5], w[6], w[2]);
+#define PIXEL01_61 *(dp+1) = Interp6(w[5], w[2], w[6]);
+#define PIXEL01_70 *(dp+1) = Interp7(w[5], w[2], w[6]);
+#define PIXEL01_90 *(dp+1) = Interp9(w[5], w[2], w[6]);
+#define PIXEL01_100 *(dp+1) = Interp10(w[5], w[2], w[6]);
+#define PIXEL10_0 *(dp+dpL) = w[5];
+#define PIXEL10_10 *(dp+dpL) = Interp1(w[5], w[7]);
+#define PIXEL10_11 *(dp+dpL) = Interp1(w[5], w[8]);
+#define PIXEL10_12 *(dp+dpL) = Interp1(w[5], w[4]);
+#define PIXEL10_20 *(dp+dpL) = Interp2(w[5], w[8], w[4]);
+#define PIXEL10_21 *(dp+dpL) = Interp2(w[5], w[7], w[4]);
+#define PIXEL10_22 *(dp+dpL) = Interp2(w[5], w[7], w[8]);
+#define PIXEL10_60 *(dp+dpL) = Interp6(w[5], w[4], w[8]);
+#define PIXEL10_61 *(dp+dpL) = Interp6(w[5], w[8], w[4]);
+#define PIXEL10_70 *(dp+dpL) = Interp7(w[5], w[8], w[4]);
+#define PIXEL10_90 *(dp+dpL) = Interp9(w[5], w[8], w[4]);
+#define PIXEL10_100 *(dp+dpL) = Interp10(w[5], w[8], w[4]);
+#define PIXEL11_0 *(dp+dpL+1) = w[5];
+#define PIXEL11_10 *(dp+dpL+1) = Interp1(w[5], w[9]);
+#define PIXEL11_11 *(dp+dpL+1) = Interp1(w[5], w[6]);
+#define PIXEL11_12 *(dp+dpL+1) = Interp1(w[5], w[8]);
+#define PIXEL11_20 *(dp+dpL+1) = Interp2(w[5], w[6], w[8]);
+#define PIXEL11_21 *(dp+dpL+1) = Interp2(w[5], w[9], w[8]);
+#define PIXEL11_22 *(dp+dpL+1) = Interp2(w[5], w[9], w[6]);
+#define PIXEL11_60 *(dp+dpL+1) = Interp6(w[5], w[8], w[6]);
+#define PIXEL11_61 *(dp+dpL+1) = Interp6(w[5], w[6], w[8]);
+#define PIXEL11_70 *(dp+dpL+1) = Interp7(w[5], w[6], w[8]);
+#define PIXEL11_90 *(dp+dpL+1) = Interp9(w[5], w[6], w[8]);
+#define PIXEL11_100 *(dp+dpL+1) = Interp10(w[5], w[6], w[8]);
+
+HQX_API void HQX_CALLCONV hq2x_32_rb( uint32_t * sp, uint32_t srb, uint32_t * dp, uint32_t drb, int Xres, int Yres )
+{
+ int i, j, k;
+ int prevline, nextline;
+ uint32_t w[10];
+ int dpL = (drb >> 2);
+ int spL = (srb >> 2);
+ uint8_t *sRowP = (uint8_t *) sp;
+ uint8_t *dRowP = (uint8_t *) dp;
+ uint32_t yuv1, yuv2;
+
+ // +----+----+----+
+ // | | | |
+ // | w1 | w2 | w3 |
+ // +----+----+----+
+ // | | | |
+ // | w4 | w5 | w6 |
+ // +----+----+----+
+ // | | | |
+ // | w7 | w8 | w9 |
+ // +----+----+----+
+
+ for (j=0; j0) prevline = -spL; else prevline = 0;
+ if (j0)
+ {
+ w[1] = *(sp + prevline - 1);
+ w[4] = *(sp - 1);
+ w[7] = *(sp + nextline - 1);
+ }
+ else
+ {
+ w[1] = w[2];
+ w[4] = w[5];
+ w[7] = w[8];
+ }
+
+ if (i> 2);
+ int spL = (srb >> 2);
+ uint8_t *sRowP = (uint8_t *) sp;
+ uint8_t *dRowP = (uint8_t *) dp;
+ uint32_t yuv1, yuv2;
+
+ // +----+----+----+
+ // | | | |
+ // | w1 | w2 | w3 |
+ // +----+----+----+
+ // | | | |
+ // | w4 | w5 | w6 |
+ // +----+----+----+
+ // | | | |
+ // | w7 | w8 | w9 |
+ // +----+----+----+
+
+ for (j=0; j0) prevline = -spL; else prevline = 0;
+ if (j0)
+ {
+ w[1] = *(sp + prevline - 1);
+ w[4] = *(sp - 1);
+ w[7] = *(sp + nextline - 1);
+ }
+ else
+ {
+ w[1] = w[2];
+ w[4] = w[5];
+ w[7] = w[8];
+ }
+
+ if (i> 2);
+ int spL = (srb >> 2);
+ uint8_t *sRowP = (uint8_t *) sp;
+ uint8_t *dRowP = (uint8_t *) dp;
+ uint32_t yuv1, yuv2;
+
+ // +----+----+----+
+ // | | | |
+ // | w1 | w2 | w3 |
+ // +----+----+----+
+ // | | | |
+ // | w4 | w5 | w6 |
+ // +----+----+----+
+ // | | | |
+ // | w7 | w8 | w9 |
+ // +----+----+----+
+
+ for (j=0; j0) prevline = -spL; else prevline = 0;
+ if (j0)
+ {
+ w[1] = *(sp + prevline - 1);
+ w[4] = *(sp - 1);
+ w[7] = *(sp + nextline - 1);
+ }
+ else
+ {
+ w[1] = w[2];
+ w[4] = w[5];
+ w[7] = w[8];
+ }
+
+ if (i
+
+#if defined( __GNUC__ )
+ #ifdef __MINGW32__
+ #define HQX_CALLCONV __stdcall
+ #else
+ #define HQX_CALLCONV
+ #endif
+#else
+ #define HQX_CALLCONV
+#endif
+
+#if 0 //defined(_WIN32)
+ #ifdef DLL_EXPORT
+ #define HQX_API __declspec(dllexport)
+ #else
+ #define HQX_API __declspec(dllimport)
+ #endif
+#else
+ #define HQX_API
+#endif
+
+HQX_API void HQX_CALLCONV hqxInit(void);
+HQX_API void HQX_CALLCONV hq2x_32( uint32_t * src, uint32_t * dest, int width, int height );
+HQX_API void HQX_CALLCONV hq3x_32( uint32_t * src, uint32_t * dest, int width, int height );
+HQX_API void HQX_CALLCONV hq4x_32( uint32_t * src, uint32_t * dest, int width, int height );
+
+HQX_API void HQX_CALLCONV hq2x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
+HQX_API void HQX_CALLCONV hq3x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
+HQX_API void HQX_CALLCONV hq4x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
+
+#endif
diff --git a/src/gl/hqnx/init.cpp b/src/gl/hqnx/init.cpp
new file mode 100644
index 000000000..0e8c2db1c
--- /dev/null
+++ b/src/gl/hqnx/init.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "hqx.h"
+
+uint32_t *RGBtoYUV;
+uint32_t YUV1, YUV2;
+
+HQX_API void HQX_CALLCONV hqxInit(void)
+{
+ /* Initalize RGB to YUV lookup table */
+ uint32_t c, r, g, b, y, u, v;
+ RGBtoYUV = new uint32_t[16777216];
+ for (c = 0; c < 16777215; c++) {
+ r = (c & 0xFF0000) >> 16;
+ g = (c & 0x00FF00) >> 8;
+ b = c & 0x0000FF;
+ y = (uint32_t)(0.299*r + 0.587*g + 0.114*b);
+ u = (uint32_t)(-0.169*r - 0.331*g + 0.5*b) + 128;
+ v = (uint32_t)(0.5*r - 0.419*g - 0.081*b) + 128;
+ RGBtoYUV[c] = (y << 16) + (u << 8) + v;
+ }
+}
diff --git a/src/gl/hqnx_asm/hq2x_asm.cpp b/src/gl/hqnx_asm/hq2x_asm.cpp
new file mode 100644
index 000000000..65e460377
--- /dev/null
+++ b/src/gl/hqnx_asm/hq2x_asm.cpp
@@ -0,0 +1,2943 @@
+//hq2x filter demo program
+//----------------------------------------------------------
+//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )
+//Copyright (C) 2012-2014 Alexey Lysiuk
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU Lesser General Public
+//License as published by the Free Software Foundation; either
+//version 2.1 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+//Lesser General Public License for more details.
+//
+//You should have received a copy of the GNU Lesser General Public
+//License along with this program; if not, write to the Free Software
+//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#include "hqnx_asm.h"
+
+namespace HQnX_asm
+{
+
+extern int LUT16to32[65536*2];
+extern int RGBtoYUV[65536*2];
+
+static const hq_vec const3 = hq_vec::expand(0x0003);
+static const hq_vec const5 = hq_vec::expand(0x0005);
+static const hq_vec const6 = hq_vec::expand(0x0006);
+static const hq_vec const14 = hq_vec::expand(0x000E);
+
+inline void Interp1(unsigned char * pc, int c1, int c2)
+{
+ //*((int*)pc) = (c1*3+c2)/4;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const3;
+ result += hq_vec::load(c2);
+ result >> 2;
+
+ result.store(pc);
+}
+
+inline void Interp2(unsigned char * pc, int c1, int c2, int c3)
+{
+ //*((int*)pc) = (c1*2+c2+c3) >> 2;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result << 1;
+ result += hq_vec::load(c2);
+ result += hq_vec::load(c3);
+ result >> 2;
+
+ result.store(pc);
+}
+
+inline void Interp5(unsigned char * pc, int c1, int c2)
+{
+ //*((int*)pc) = (c1+c2)/2;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result += hq_vec::load(c2);
+ result >> 1;
+
+ result.store(pc);
+}
+
+inline void Interp6(unsigned char * pc, int c1, int c2, int c3)
+{
+ //*((int*)pc) = (c1*5+c2*2+c3)/8;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const5;
+ result += hq_vec::load(c2) << 1;
+ result += hq_vec::load(c3);
+ result >> 3;
+
+ result.store(pc);
+}
+
+inline void Interp7(unsigned char * pc, int c1, int c2, int c3)
+{
+ //*((int*)pc) = (c1*6+c2+c3)/8;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const6;
+ result += hq_vec::load(c2);
+ result += hq_vec::load(c3);
+ result >> 3;
+
+ result.store(pc);
+}
+
+inline void Interp9(unsigned char * pc, int c1, int c2, int c3)
+{
+ //*((int*)pc) = (c1*2+(c2+c3)*3)/8;
+
+ hq_vec result = hq_vec::load(c2);
+
+ result += hq_vec::load(c3);
+ result *= const3;
+ result += hq_vec::load(c1) << 1;
+ result >> 3;
+
+ result.store(pc);
+}
+
+inline void Interp10(unsigned char * pc, int c1, int c2, int c3)
+{
+ //*((int*)pc) = (c1*14+c2+c3)/16;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const14;
+ result += hq_vec::load(c2);
+ result += hq_vec::load(c3);
+ result >> 4;
+
+ result.store(pc);
+}
+
+#define PIXEL00_0 *((int*)(pOut)) = c[5];
+#define PIXEL00_10 Interp1(pOut, c[5], c[1]);
+#define PIXEL00_11 Interp1(pOut, c[5], c[4]);
+#define PIXEL00_12 Interp1(pOut, c[5], c[2]);
+#define PIXEL00_20 Interp2(pOut, c[5], c[4], c[2]);
+#define PIXEL00_21 Interp2(pOut, c[5], c[1], c[2]);
+#define PIXEL00_22 Interp2(pOut, c[5], c[1], c[4]);
+#define PIXEL00_60 Interp6(pOut, c[5], c[2], c[4]);
+#define PIXEL00_61 Interp6(pOut, c[5], c[4], c[2]);
+#define PIXEL00_70 Interp7(pOut, c[5], c[4], c[2]);
+#define PIXEL00_90 Interp9(pOut, c[5], c[4], c[2]);
+#define PIXEL00_100 Interp10(pOut, c[5], c[4], c[2]);
+#define PIXEL01_0 *((int*)(pOut+4)) = c[5];
+#define PIXEL01_10 Interp1(pOut+4, c[5], c[3]);
+#define PIXEL01_11 Interp1(pOut+4, c[5], c[2]);
+#define PIXEL01_12 Interp1(pOut+4, c[5], c[6]);
+#define PIXEL01_20 Interp2(pOut+4, c[5], c[2], c[6]);
+#define PIXEL01_21 Interp2(pOut+4, c[5], c[3], c[6]);
+#define PIXEL01_22 Interp2(pOut+4, c[5], c[3], c[2]);
+#define PIXEL01_60 Interp6(pOut+4, c[5], c[6], c[2]);
+#define PIXEL01_61 Interp6(pOut+4, c[5], c[2], c[6]);
+#define PIXEL01_70 Interp7(pOut+4, c[5], c[2], c[6]);
+#define PIXEL01_90 Interp9(pOut+4, c[5], c[2], c[6]);
+#define PIXEL01_100 Interp10(pOut+4, c[5], c[2], c[6]);
+#define PIXEL10_0 *((int*)(pOut+BpL)) = c[5];
+#define PIXEL10_10 Interp1(pOut+BpL, c[5], c[7]);
+#define PIXEL10_11 Interp1(pOut+BpL, c[5], c[8]);
+#define PIXEL10_12 Interp1(pOut+BpL, c[5], c[4]);
+#define PIXEL10_20 Interp2(pOut+BpL, c[5], c[8], c[4]);
+#define PIXEL10_21 Interp2(pOut+BpL, c[5], c[7], c[4]);
+#define PIXEL10_22 Interp2(pOut+BpL, c[5], c[7], c[8]);
+#define PIXEL10_60 Interp6(pOut+BpL, c[5], c[4], c[8]);
+#define PIXEL10_61 Interp6(pOut+BpL, c[5], c[8], c[4]);
+#define PIXEL10_70 Interp7(pOut+BpL, c[5], c[8], c[4]);
+#define PIXEL10_90 Interp9(pOut+BpL, c[5], c[8], c[4]);
+#define PIXEL10_100 Interp10(pOut+BpL, c[5], c[8], c[4]);
+#define PIXEL11_0 *((int*)(pOut+BpL+4)) = c[5];
+#define PIXEL11_10 Interp1(pOut+BpL+4, c[5], c[9]);
+#define PIXEL11_11 Interp1(pOut+BpL+4, c[5], c[6]);
+#define PIXEL11_12 Interp1(pOut+BpL+4, c[5], c[8]);
+#define PIXEL11_20 Interp2(pOut+BpL+4, c[5], c[6], c[8]);
+#define PIXEL11_21 Interp2(pOut+BpL+4, c[5], c[9], c[8]);
+#define PIXEL11_22 Interp2(pOut+BpL+4, c[5], c[9], c[6]);
+#define PIXEL11_60 Interp6(pOut+BpL+4, c[5], c[8], c[6]);
+#define PIXEL11_61 Interp6(pOut+BpL+4, c[5], c[6], c[8]);
+#define PIXEL11_70 Interp7(pOut+BpL+4, c[5], c[6], c[8]);
+#define PIXEL11_90 Interp9(pOut+BpL+4, c[5], c[6], c[8]);
+#define PIXEL11_100 Interp10(pOut+BpL+4, c[5], c[6], c[8]);
+
+
+bool Diff(const unsigned int, const unsigned int);
+
+void DLL hq2x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL )
+{
+ int i, j, k;
+ int w[10];
+ unsigned int c[10];
+
+ // +----+----+----+
+ // | | | |
+ // | w1 | w2 | w3 |
+ // +----+----+----+
+ // | | | |
+ // | w4 | w5 | w6 |
+ // +----+----+----+
+ // | | | |
+ // | w7 | w8 | w9 |
+ // +----+----+----+
+
+ for (j=0; j0)
+ {
+ w[1] = *(pIn - Xres - 1);
+ }
+ else
+ {
+ w[1] = 0;
+ }
+
+ w[2] = *(pIn - Xres);
+
+ if (i0)
+ {
+ w[4] = *(pIn - 1);
+ }
+ else
+ {
+ w[4] = 0;
+ }
+
+ w[5] = *(pIn);
+ if (i0)
+ {
+ w[7] = *(pIn + Xres - 1);
+ }
+ else
+ {
+ w[7] = 0;
+ }
+
+ w[8] = *(pIn + Xres);
+ if (i> 2;
+
+ result.store(pc);
+}
+
+inline void Interp2(unsigned char * pc, int c1, int c2, int c3)
+{
+// *((int*)pc) = (c1*2+c2+c3)/4;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result << 1;
+ result += hq_vec::load(c2);
+ result += hq_vec::load(c3);
+ result >> 2;
+
+ result.store(pc);
+}
+
+inline void Interp3(unsigned char * pc, int c1, int c2)
+{
+ //*((int*)pc) = (c1*7+c2)/8;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const7;
+ result += hq_vec::load(c2);
+ result >> 3;
+
+ result.store(pc);
+}
+
+inline void Interp4(unsigned char * pc, int c1, int c2, int c3)
+{
+ //*((int*)pc) = (c1*2+(c2+c3)*7)/16;
+
+ hq_vec result = hq_vec::load(c2);
+
+ result += hq_vec::load(c3);
+ result *= const7;
+ result += hq_vec::load(c1) << 1;
+ result >> 4;
+
+ result.store(pc);
+}
+
+inline void Interp5(unsigned char * pc, int c1, int c2)
+{
+ //*((int*)pc) = (c1+c2)/2;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result += hq_vec::load(c2);
+ result >> 1;
+
+ result.store(pc);
+}
+
+#define PIXEL00_1M Interp1(pOut, c[5], c[1]);
+#define PIXEL00_1U Interp1(pOut, c[5], c[2]);
+#define PIXEL00_1L Interp1(pOut, c[5], c[4]);
+#define PIXEL00_2 Interp2(pOut, c[5], c[4], c[2]);
+#define PIXEL00_4 Interp4(pOut, c[5], c[4], c[2]);
+#define PIXEL00_5 Interp5(pOut, c[4], c[2]);
+#define PIXEL00_C *((int*)(pOut)) = c[5];
+
+#define PIXEL01_1 Interp1(pOut+4, c[5], c[2]);
+#define PIXEL01_3 Interp3(pOut+4, c[5], c[2]);
+#define PIXEL01_6 Interp1(pOut+4, c[2], c[5]);
+#define PIXEL01_C *((int*)(pOut+4)) = c[5];
+
+#define PIXEL02_1M Interp1(pOut+8, c[5], c[3]);
+#define PIXEL02_1U Interp1(pOut+8, c[5], c[2]);
+#define PIXEL02_1R Interp1(pOut+8, c[5], c[6]);
+#define PIXEL02_2 Interp2(pOut+8, c[5], c[2], c[6]);
+#define PIXEL02_4 Interp4(pOut+8, c[5], c[2], c[6]);
+#define PIXEL02_5 Interp5(pOut+8, c[2], c[6]);
+#define PIXEL02_C *((int*)(pOut+8)) = c[5];
+
+#define PIXEL10_1 Interp1(pOut+BpL, c[5], c[4]);
+#define PIXEL10_3 Interp3(pOut+BpL, c[5], c[4]);
+#define PIXEL10_6 Interp1(pOut+BpL, c[4], c[5]);
+#define PIXEL10_C *((int*)(pOut+BpL)) = c[5];
+
+#define PIXEL11 *((int*)(pOut+BpL+4)) = c[5];
+
+#define PIXEL12_1 Interp1(pOut+BpL+8, c[5], c[6]);
+#define PIXEL12_3 Interp3(pOut+BpL+8, c[5], c[6]);
+#define PIXEL12_6 Interp1(pOut+BpL+8, c[6], c[5]);
+#define PIXEL12_C *((int*)(pOut+BpL+8)) = c[5];
+
+#define PIXEL20_1M Interp1(pOut+BpL+BpL, c[5], c[7]);
+#define PIXEL20_1D Interp1(pOut+BpL+BpL, c[5], c[8]);
+#define PIXEL20_1L Interp1(pOut+BpL+BpL, c[5], c[4]);
+#define PIXEL20_2 Interp2(pOut+BpL+BpL, c[5], c[8], c[4]);
+#define PIXEL20_4 Interp4(pOut+BpL+BpL, c[5], c[8], c[4]);
+#define PIXEL20_5 Interp5(pOut+BpL+BpL, c[8], c[4]);
+#define PIXEL20_C *((int*)(pOut+BpL+BpL)) = c[5];
+
+#define PIXEL21_1 Interp1(pOut+BpL+BpL+4, c[5], c[8]);
+#define PIXEL21_3 Interp3(pOut+BpL+BpL+4, c[5], c[8]);
+#define PIXEL21_6 Interp1(pOut+BpL+BpL+4, c[8], c[5]);
+#define PIXEL21_C *((int*)(pOut+BpL+BpL+4)) = c[5];
+
+#define PIXEL22_1M Interp1(pOut+BpL+BpL+8, c[5], c[9]);
+#define PIXEL22_1D Interp1(pOut+BpL+BpL+8, c[5], c[8]);
+#define PIXEL22_1R Interp1(pOut+BpL+BpL+8, c[5], c[6]);
+#define PIXEL22_2 Interp2(pOut+BpL+BpL+8, c[5], c[6], c[8]);
+#define PIXEL22_4 Interp4(pOut+BpL+BpL+8, c[5], c[6], c[8]);
+#define PIXEL22_5 Interp5(pOut+BpL+BpL+8, c[6], c[8]);
+#define PIXEL22_C *((int*)(pOut+BpL+BpL+8)) = c[5];
+
+bool Diff(const unsigned int, const unsigned int);
+
+void DLL hq3x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL )
+{
+ int i, j, k;
+ int w[10];
+ unsigned int c[10];
+
+ // +----+----+----+
+ // | | | |
+ // | w1 | w2 | w3 |
+ // +----+----+----+
+ // | | | |
+ // | w4 | w5 | w6 |
+ // +----+----+----+
+ // | | | |
+ // | w7 | w8 | w9 |
+ // +----+----+----+
+
+ for (j=0; j0) w[1] = *(pIn - Xres - 1); else w[1] = 0;
+ w[2] = *(pIn - Xres);
+ if (i0) w[4] = *(pIn - 1); else w[4] = 0;
+ w[5] = *(pIn);
+ if (i0) w[7] = *(pIn + Xres - 1); else w[7] = 0;
+ w[8] = *(pIn + Xres);
+ if (i
+#include
+#include
+#include "hqnx_asm.h"
+
+namespace HQnX_asm
+{
+
+int LUT16to32[65536*2];
+int RGBtoYUV[65536*2];
+
+static const hq_vec const3 = hq_vec::expand(0x0003);
+static const hq_vec const5 = hq_vec::expand(0x0005);
+static const hq_vec const6 = hq_vec::expand(0x0006);
+static const hq_vec const7 = hq_vec::expand(0x0007);
+
+
+inline void Interp1(unsigned char * pc, int c1, int c2)
+{
+ //*((int*)pc) = (c1*3+c2)/4;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const3;
+ result += hq_vec::load(c2);
+ result >> 2;
+
+ result.store(pc);
+}
+
+inline void Interp2(unsigned char * pc, int c1, int c2, int c3)
+{
+// *((int*)pc) = (c1*2+c2+c3)/4;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result << 1;
+ result += hq_vec::load(c2);
+ result += hq_vec::load(c3);
+ result >> 2;
+
+ result.store(pc);
+}
+
+inline void Interp3(unsigned char * pc, int c1, int c2)
+{
+ //*((int*)pc) = (c1*7+c2)/8;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const7;
+ result += hq_vec::load(c2);
+ result >> 3;
+
+ result.store(pc);
+}
+
+inline void Interp5(unsigned char * pc, int c1, int c2)
+{
+ //*((int*)pc) = (c1+c2)/2;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result += hq_vec::load(c2);
+ result >> 1;
+
+ result.store(pc);
+}
+
+inline void Interp6(unsigned char * pc, int c1, int c2, int c3)
+{
+ //*((int*)pc) = (c1*5+c2*2+c3)/8;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const5;
+ result += hq_vec::load(c2) << 1;
+ result += hq_vec::load(c3);
+ result >> 3;
+
+ result.store(pc);
+}
+
+inline void Interp7(unsigned char * pc, int c1, int c2, int c3)
+{
+ //*((int*)pc) = (c1*6+c2+c3)/8;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const6;
+ result += hq_vec::load(c2);
+ result += hq_vec::load(c3);
+ result >> 3;
+
+ result.store(pc);
+}
+
+inline void Interp8(unsigned char * pc, int c1, int c2)
+{
+ //*((int*)pc) = (c1*5+c2*3)/8;
+
+ hq_vec result = hq_vec::load(c1);
+
+ result *= const5;
+ result += hq_vec::load(c2) * const3;
+ result >> 3;
+
+ result.store(pc);
+}
+
+#define PIXEL00_0 *((int*)(pOut)) = c[5];
+#define PIXEL00_11 Interp1(pOut, c[5], c[4]);
+#define PIXEL00_12 Interp1(pOut, c[5], c[2]);
+#define PIXEL00_20 Interp2(pOut, c[5], c[2], c[4]);
+#define PIXEL00_50 Interp5(pOut, c[2], c[4]);
+#define PIXEL00_80 Interp8(pOut, c[5], c[1]);
+#define PIXEL00_81 Interp8(pOut, c[5], c[4]);
+#define PIXEL00_82 Interp8(pOut, c[5], c[2]);
+#define PIXEL01_0 *((int*)(pOut+4)) = c[5];
+#define PIXEL01_10 Interp1(pOut+4, c[5], c[1]);
+#define PIXEL01_12 Interp1(pOut+4, c[5], c[2]);
+#define PIXEL01_14 Interp1(pOut+4, c[2], c[5]);
+#define PIXEL01_21 Interp2(pOut+4, c[2], c[5], c[4]);
+#define PIXEL01_31 Interp3(pOut+4, c[5], c[4]);
+#define PIXEL01_50 Interp5(pOut+4, c[2], c[5]);
+#define PIXEL01_60 Interp6(pOut+4, c[5], c[2], c[4]);
+#define PIXEL01_61 Interp6(pOut+4, c[5], c[2], c[1]);
+#define PIXEL01_82 Interp8(pOut+4, c[5], c[2]);
+#define PIXEL01_83 Interp8(pOut+4, c[2], c[4]);
+#define PIXEL02_0 *((int*)(pOut+8)) = c[5];
+#define PIXEL02_10 Interp1(pOut+8, c[5], c[3]);
+#define PIXEL02_11 Interp1(pOut+8, c[5], c[2]);
+#define PIXEL02_13 Interp1(pOut+8, c[2], c[5]);
+#define PIXEL02_21 Interp2(pOut+8, c[2], c[5], c[6]);
+#define PIXEL02_32 Interp3(pOut+8, c[5], c[6]);
+#define PIXEL02_50 Interp5(pOut+8, c[2], c[5]);
+#define PIXEL02_60 Interp6(pOut+8, c[5], c[2], c[6]);
+#define PIXEL02_61 Interp6(pOut+8, c[5], c[2], c[3]);
+#define PIXEL02_81 Interp8(pOut+8, c[5], c[2]);
+#define PIXEL02_83 Interp8(pOut+8, c[2], c[6]);
+#define PIXEL03_0 *((int*)(pOut+12)) = c[5];
+#define PIXEL03_11 Interp1(pOut+12, c[5], c[2]);
+#define PIXEL03_12 Interp1(pOut+12, c[5], c[6]);
+#define PIXEL03_20 Interp2(pOut+12, c[5], c[2], c[6]);
+#define PIXEL03_50 Interp5(pOut+12, c[2], c[6]);
+#define PIXEL03_80 Interp8(pOut+12, c[5], c[3]);
+#define PIXEL03_81 Interp8(pOut+12, c[5], c[2]);
+#define PIXEL03_82 Interp8(pOut+12, c[5], c[6]);
+#define PIXEL10_0 *((int*)(pOut+BpL)) = c[5];
+#define PIXEL10_10 Interp1(pOut+BpL, c[5], c[1]);
+#define PIXEL10_11 Interp1(pOut+BpL, c[5], c[4]);
+#define PIXEL10_13 Interp1(pOut+BpL, c[4], c[5]);
+#define PIXEL10_21 Interp2(pOut+BpL, c[4], c[5], c[2]);
+#define PIXEL10_32 Interp3(pOut+BpL, c[5], c[2]);
+#define PIXEL10_50 Interp5(pOut+BpL, c[4], c[5]);
+#define PIXEL10_60 Interp6(pOut+BpL, c[5], c[4], c[2]);
+#define PIXEL10_61 Interp6(pOut+BpL, c[5], c[4], c[1]);
+#define PIXEL10_81 Interp8(pOut+BpL, c[5], c[4]);
+#define PIXEL10_83 Interp8(pOut+BpL, c[4], c[2]);
+#define PIXEL11_0 *((int*)(pOut+BpL+4)) = c[5];
+#define PIXEL11_30 Interp3(pOut+BpL+4, c[5], c[1]);
+#define PIXEL11_31 Interp3(pOut+BpL+4, c[5], c[4]);
+#define PIXEL11_32 Interp3(pOut+BpL+4, c[5], c[2]);
+#define PIXEL11_70 Interp7(pOut+BpL+4, c[5], c[4], c[2]);
+#define PIXEL12_0 *((int*)(pOut+BpL+8)) = c[5];
+#define PIXEL12_30 Interp3(pOut+BpL+8, c[5], c[3]);
+#define PIXEL12_31 Interp3(pOut+BpL+8, c[5], c[2]);
+#define PIXEL12_32 Interp3(pOut+BpL+8, c[5], c[6]);
+#define PIXEL12_70 Interp7(pOut+BpL+8, c[5], c[6], c[2]);
+#define PIXEL13_0 *((int*)(pOut+BpL+12)) = c[5];
+#define PIXEL13_10 Interp1(pOut+BpL+12, c[5], c[3]);
+#define PIXEL13_12 Interp1(pOut+BpL+12, c[5], c[6]);
+#define PIXEL13_14 Interp1(pOut+BpL+12, c[6], c[5]);
+#define PIXEL13_21 Interp2(pOut+BpL+12, c[6], c[5], c[2]);
+#define PIXEL13_31 Interp3(pOut+BpL+12, c[5], c[2]);
+#define PIXEL13_50 Interp5(pOut+BpL+12, c[6], c[5]);
+#define PIXEL13_60 Interp6(pOut+BpL+12, c[5], c[6], c[2]);
+#define PIXEL13_61 Interp6(pOut+BpL+12, c[5], c[6], c[3]);
+#define PIXEL13_82 Interp8(pOut+BpL+12, c[5], c[6]);
+#define PIXEL13_83 Interp8(pOut+BpL+12, c[6], c[2]);
+#define PIXEL20_0 *((int*)(pOut+BpL+BpL)) = c[5];
+#define PIXEL20_10 Interp1(pOut+BpL+BpL, c[5], c[7]);
+#define PIXEL20_12 Interp1(pOut+BpL+BpL, c[5], c[4]);
+#define PIXEL20_14 Interp1(pOut+BpL+BpL, c[4], c[5]);
+#define PIXEL20_21 Interp2(pOut+BpL+BpL, c[4], c[5], c[8]);
+#define PIXEL20_31 Interp3(pOut+BpL+BpL, c[5], c[8]);
+#define PIXEL20_50 Interp5(pOut+BpL+BpL, c[4], c[5]);
+#define PIXEL20_60 Interp6(pOut+BpL+BpL, c[5], c[4], c[8]);
+#define PIXEL20_61 Interp6(pOut+BpL+BpL, c[5], c[4], c[7]);
+#define PIXEL20_82 Interp8(pOut+BpL+BpL, c[5], c[4]);
+#define PIXEL20_83 Interp8(pOut+BpL+BpL, c[4], c[8]);
+#define PIXEL21_0 *((int*)(pOut+BpL+BpL+4)) = c[5];
+#define PIXEL21_30 Interp3(pOut+BpL+BpL+4, c[5], c[7]);
+#define PIXEL21_31 Interp3(pOut+BpL+BpL+4, c[5], c[8]);
+#define PIXEL21_32 Interp3(pOut+BpL+BpL+4, c[5], c[4]);
+#define PIXEL21_70 Interp7(pOut+BpL+BpL+4, c[5], c[4], c[8]);
+#define PIXEL22_0 *((int*)(pOut+BpL+BpL+8)) = c[5];
+#define PIXEL22_30 Interp3(pOut+BpL+BpL+8, c[5], c[9]);
+#define PIXEL22_31 Interp3(pOut+BpL+BpL+8, c[5], c[6]);
+#define PIXEL22_32 Interp3(pOut+BpL+BpL+8, c[5], c[8]);
+#define PIXEL22_70 Interp7(pOut+BpL+BpL+8, c[5], c[6], c[8]);
+#define PIXEL23_0 *((int*)(pOut+BpL+BpL+12)) = c[5];
+#define PIXEL23_10 Interp1(pOut+BpL+BpL+12, c[5], c[9]);
+#define PIXEL23_11 Interp1(pOut+BpL+BpL+12, c[5], c[6]);
+#define PIXEL23_13 Interp1(pOut+BpL+BpL+12, c[6], c[5]);
+#define PIXEL23_21 Interp2(pOut+BpL+BpL+12, c[6], c[5], c[8]);
+#define PIXEL23_32 Interp3(pOut+BpL+BpL+12, c[5], c[8]);
+#define PIXEL23_50 Interp5(pOut+BpL+BpL+12, c[6], c[5]);
+#define PIXEL23_60 Interp6(pOut+BpL+BpL+12, c[5], c[6], c[8]);
+#define PIXEL23_61 Interp6(pOut+BpL+BpL+12, c[5], c[6], c[9]);
+#define PIXEL23_81 Interp8(pOut+BpL+BpL+12, c[5], c[6]);
+#define PIXEL23_83 Interp8(pOut+BpL+BpL+12, c[6], c[8]);
+#define PIXEL30_0 *((int*)(pOut+BpL+BpL+BpL)) = c[5];
+#define PIXEL30_11 Interp1(pOut+BpL+BpL+BpL, c[5], c[8]);
+#define PIXEL30_12 Interp1(pOut+BpL+BpL+BpL, c[5], c[4]);
+#define PIXEL30_20 Interp2(pOut+BpL+BpL+BpL, c[5], c[8], c[4]);
+#define PIXEL30_50 Interp5(pOut+BpL+BpL+BpL, c[8], c[4]);
+#define PIXEL30_80 Interp8(pOut+BpL+BpL+BpL, c[5], c[7]);
+#define PIXEL30_81 Interp8(pOut+BpL+BpL+BpL, c[5], c[8]);
+#define PIXEL30_82 Interp8(pOut+BpL+BpL+BpL, c[5], c[4]);
+#define PIXEL31_0 *((int*)(pOut+BpL+BpL+BpL+4)) = c[5];
+#define PIXEL31_10 Interp1(pOut+BpL+BpL+BpL+4, c[5], c[7]);
+#define PIXEL31_11 Interp1(pOut+BpL+BpL+BpL+4, c[5], c[8]);
+#define PIXEL31_13 Interp1(pOut+BpL+BpL+BpL+4, c[8], c[5]);
+#define PIXEL31_21 Interp2(pOut+BpL+BpL+BpL+4, c[8], c[5], c[4]);
+#define PIXEL31_32 Interp3(pOut+BpL+BpL+BpL+4, c[5], c[4]);
+#define PIXEL31_50 Interp5(pOut+BpL+BpL+BpL+4, c[8], c[5]);
+#define PIXEL31_60 Interp6(pOut+BpL+BpL+BpL+4, c[5], c[8], c[4]);
+#define PIXEL31_61 Interp6(pOut+BpL+BpL+BpL+4, c[5], c[8], c[7]);
+#define PIXEL31_81 Interp8(pOut+BpL+BpL+BpL+4, c[5], c[8]);
+#define PIXEL31_83 Interp8(pOut+BpL+BpL+BpL+4, c[8], c[4]);
+#define PIXEL32_0 *((int*)(pOut+BpL+BpL+BpL+8)) = c[5];
+#define PIXEL32_10 Interp1(pOut+BpL+BpL+BpL+8, c[5], c[9]);
+#define PIXEL32_12 Interp1(pOut+BpL+BpL+BpL+8, c[5], c[8]);
+#define PIXEL32_14 Interp1(pOut+BpL+BpL+BpL+8, c[8], c[5]);
+#define PIXEL32_21 Interp2(pOut+BpL+BpL+BpL+8, c[8], c[5], c[6]);
+#define PIXEL32_31 Interp3(pOut+BpL+BpL+BpL+8, c[5], c[6]);
+#define PIXEL32_50 Interp5(pOut+BpL+BpL+BpL+8, c[8], c[5]);
+#define PIXEL32_60 Interp6(pOut+BpL+BpL+BpL+8, c[5], c[8], c[6]);
+#define PIXEL32_61 Interp6(pOut+BpL+BpL+BpL+8, c[5], c[8], c[9]);
+#define PIXEL32_82 Interp8(pOut+BpL+BpL+BpL+8, c[5], c[8]);
+#define PIXEL32_83 Interp8(pOut+BpL+BpL+BpL+8, c[8], c[6]);
+#define PIXEL33_0 *((int*)(pOut+BpL+BpL+BpL+12)) = c[5];
+#define PIXEL33_11 Interp1(pOut+BpL+BpL+BpL+12, c[5], c[6]);
+#define PIXEL33_12 Interp1(pOut+BpL+BpL+BpL+12, c[5], c[8]);
+#define PIXEL33_20 Interp2(pOut+BpL+BpL+BpL+12, c[5], c[8], c[6]);
+#define PIXEL33_50 Interp5(pOut+BpL+BpL+BpL+12, c[8], c[6]);
+#define PIXEL33_80 Interp8(pOut+BpL+BpL+BpL+12, c[5], c[9]);
+#define PIXEL33_81 Interp8(pOut+BpL+BpL+BpL+12, c[5], c[6]);
+#define PIXEL33_82 Interp8(pOut+BpL+BpL+BpL+12, c[5], c[8]);
+
+bool Diff(const unsigned int rgb1, const unsigned int rgb2)
+{
+ if (rgb1 == rgb2)
+ {
+ return false;
+ }
+
+ static const hq_vec THRESHOLD = 0x00300706;
+
+ const hq_vec yuv1 = RGBtoYUV[rgb1];
+ const hq_vec yuv2 = RGBtoYUV[rgb2];
+
+ const hq_vec delta1 = yuv1 - yuv2;
+ const hq_vec delta2 = yuv2 - yuv1;
+
+ const hq_vec delta = delta1 | delta2;
+ const hq_vec result = delta - THRESHOLD;
+
+ return 0 != result;
+}
+
+void DLL hq4x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL )
+{
+ int i, j, k;
+ int w[10];
+ int c[10];
+
+ // +----+----+----+
+ // | | | |
+ // | w1 | w2 | w3 |
+ // +----+----+----+
+ // | | | |
+ // | w4 | w5 | w6 |
+ // +----+----+----+
+ // | | | |
+ // | w7 | w8 | w9 |
+ // +----+----+----+
+
+ for (j = 0; j < Yres; j++)
+ {
+ for (i = 0; i < Xres; i++)
+ {
+ if (j == 0)
+ {
+ w[1] = 0;
+ w[2] = 0;
+ w[3] = 0;
+ }
+ else
+ {
+ if (i > 0)
+ w[1] = *(pIn - Xres - 1);
+ else
+ w[1] = 0;
+
+ w[2] = *(pIn - Xres);
+
+ if (i < Xres - 1)
+ w[3] = *(pIn - Xres + 1);
+ else
+ w[3] = 0;
+ }
+
+ if (i > 0)
+ w[4] = *(pIn - 1);
+ else
+ w[4] = 0;
+
+ w[5] = *(pIn);
+
+ if (i < Xres - 1)
+ w[6] = *(pIn + 1);
+ else
+ w[6] = 0;
+
+ if (j == Yres - 1)
+ {
+ w[7] = 0;
+ w[8] = 0;
+ w[9] = 0;
+ }
+ else
+ {
+ if (i > 0)
+ w[7] = *(pIn + Xres - 1);
+ else
+ w[7] = 0;
+
+ w[8] = *(pIn + Xres);
+
+ if (i < Xres-1)
+ w[9] = *(pIn + Xres + 1);
+ else
+ w[9] = 0;
+ }
+
+ int pattern = 0;
+
+ if ( Diff(w[5],w[1]) ) pattern |= 0x0001;
+ if ( Diff(w[5],w[2]) ) pattern |= 0x0002;
+ if ( Diff(w[5],w[3]) ) pattern |= 0x0004;
+ if ( Diff(w[5],w[4]) ) pattern |= 0x0008;
+ if ( Diff(w[5],w[6]) ) pattern |= 0x0010;
+ if ( Diff(w[5],w[7]) ) pattern |= 0x0020;
+ if ( Diff(w[5],w[8]) ) pattern |= 0x0040;
+ if ( Diff(w[5],w[9]) ) pattern |= 0x0080;
+
+ for (k=1; k<=9; k++)
+ c[k] = LUT16to32[w[k]];
+
+ switch (pattern)
+ {
+ case 0:
+ case 1:
+ case 4:
+ case 32:
+ case 128:
+ case 5:
+ case 132:
+ case 160:
+ case 33:
+ case 129:
+ case 36:
+ case 133:
+ case 164:
+ case 161:
+ case 37:
+ case 165:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 2:
+ case 34:
+ case 130:
+ case 162:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 16:
+ case 17:
+ case 48:
+ case 49:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 64:
+ case 65:
+ case 68:
+ case 69:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 8:
+ case 12:
+ case 136:
+ case 140:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 3:
+ case 35:
+ case 131:
+ case 163:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 6:
+ case 38:
+ case 134:
+ case 166:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 20:
+ case 21:
+ case 52:
+ case 53:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 144:
+ case 145:
+ case 176:
+ case 177:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 192:
+ case 193:
+ case 196:
+ case 197:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 96:
+ case 97:
+ case 100:
+ case 101:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 40:
+ case 44:
+ case 168:
+ case 172:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 9:
+ case 13:
+ case 137:
+ case 141:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 18:
+ case 50:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL12_0
+ PIXEL13_50
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 80:
+ case 81:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_61
+ PIXEL21_30
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 72:
+ case 76:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_70
+ PIXEL13_60
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL21_0
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 10:
+ case 138:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ PIXEL11_0
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 66:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 24:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 7:
+ case 39:
+ case 135:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 148:
+ case 149:
+ case 180:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 224:
+ case 228:
+ case 225:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 41:
+ case 169:
+ case 45:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 22:
+ case 54:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_0
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 208:
+ case 209:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 104:
+ case 108:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_70
+ PIXEL13_60
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 11:
+ case 139:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 19:
+ case 51:
+ {
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL00_12
+ PIXEL01_14
+ PIXEL02_83
+ PIXEL03_50
+ PIXEL12_70
+ PIXEL13_21
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 146:
+ case 178:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL23_32
+ PIXEL33_82
+ }
+ else
+ {
+ PIXEL02_21
+ PIXEL03_50
+ PIXEL12_70
+ PIXEL13_83
+ PIXEL23_13
+ PIXEL33_11
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_32
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_82
+ break;
+ }
+ case 84:
+ case 85:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_81
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL03_81
+ PIXEL13_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL03_12
+ PIXEL13_14
+ PIXEL22_70
+ PIXEL23_83
+ PIXEL32_21
+ PIXEL33_50
+ }
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_31
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 112:
+ case 113:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_82
+ PIXEL21_32
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_70
+ PIXEL23_21
+ PIXEL30_11
+ PIXEL31_13
+ PIXEL32_83
+ PIXEL33_50
+ }
+ break;
+ }
+ case 200:
+ case 204:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_70
+ PIXEL13_60
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_31
+ PIXEL33_81
+ }
+ else
+ {
+ PIXEL20_21
+ PIXEL21_70
+ PIXEL30_50
+ PIXEL31_83
+ PIXEL32_14
+ PIXEL33_12
+ }
+ PIXEL22_31
+ PIXEL23_81
+ break;
+ }
+ case 73:
+ case 77:
+ {
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL00_82
+ PIXEL10_32
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL00_11
+ PIXEL10_13
+ PIXEL20_83
+ PIXEL21_70
+ PIXEL30_50
+ PIXEL31_21
+ }
+ PIXEL01_82
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL11_32
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 42:
+ case 170:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL20_31
+ PIXEL30_81
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_21
+ PIXEL10_83
+ PIXEL11_70
+ PIXEL20_14
+ PIXEL30_12
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL21_31
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL31_81
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 14:
+ case 142:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_83
+ PIXEL02_13
+ PIXEL03_11
+ PIXEL10_21
+ PIXEL11_70
+ }
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 67:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 70:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 28:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 152:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 194:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 98:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 56:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 25:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 26:
+ case 31:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL11_0
+ PIXEL12_0
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 82:
+ case 214:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_0
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 88:
+ case 248:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ break;
+ }
+ case 74:
+ case 107:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_61
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 27:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 86:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_0
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 216:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 106:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_61
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 30:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_0
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 210:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 120:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 75:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 29:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 198:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 184:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 99:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 57:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 71:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 156:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 226:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 60:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 195:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 102:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 153:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 58:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 83:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL20_61
+ PIXEL21_30
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 92:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_31
+ PIXEL13_31
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ break;
+ }
+ case 202:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_61
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 78:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL12_32
+ PIXEL13_82
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 154:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 114:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL20_82
+ PIXEL21_32
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ PIXEL30_82
+ PIXEL31_32
+ break;
+ }
+ case 89:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_30
+ PIXEL13_10
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ break;
+ }
+ case 90:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ break;
+ }
+ case 55:
+ case 23:
+ {
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL12_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL00_12
+ PIXEL01_14
+ PIXEL02_83
+ PIXEL03_50
+ PIXEL12_70
+ PIXEL13_21
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 182:
+ case 150:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL23_32
+ PIXEL33_82
+ }
+ else
+ {
+ PIXEL02_21
+ PIXEL03_50
+ PIXEL12_70
+ PIXEL13_83
+ PIXEL23_13
+ PIXEL33_11
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_32
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_82
+ break;
+ }
+ case 213:
+ case 212:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_81
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL03_81
+ PIXEL13_31
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL03_12
+ PIXEL13_14
+ PIXEL22_70
+ PIXEL23_83
+ PIXEL32_21
+ PIXEL33_50
+ }
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_31
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 241:
+ case 240:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_82
+ PIXEL21_32
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL22_70
+ PIXEL23_21
+ PIXEL30_11
+ PIXEL31_13
+ PIXEL32_83
+ PIXEL33_50
+ }
+ break;
+ }
+ case 236:
+ case 232:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_70
+ PIXEL13_60
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL30_0
+ PIXEL31_0
+ PIXEL32_31
+ PIXEL33_81
+ }
+ else
+ {
+ PIXEL20_21
+ PIXEL21_70
+ PIXEL30_50
+ PIXEL31_83
+ PIXEL32_14
+ PIXEL33_12
+ }
+ PIXEL22_31
+ PIXEL23_81
+ break;
+ }
+ case 109:
+ case 105:
+ {
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL00_82
+ PIXEL10_32
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL00_11
+ PIXEL10_13
+ PIXEL20_83
+ PIXEL21_70
+ PIXEL30_50
+ PIXEL31_21
+ }
+ PIXEL01_82
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL11_32
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 171:
+ case 43:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL20_31
+ PIXEL30_81
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_21
+ PIXEL10_83
+ PIXEL11_70
+ PIXEL20_14
+ PIXEL30_12
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL21_31
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL31_81
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 143:
+ case 15:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_0
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_83
+ PIXEL02_13
+ PIXEL03_11
+ PIXEL10_21
+ PIXEL11_70
+ }
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 124:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_31
+ PIXEL13_31
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 203:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 62:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_0
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 211:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 118:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_0
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 217:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 110:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_32
+ PIXEL13_82
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 155:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 188:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 185:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 61:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 157:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 103:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 227:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 230:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 199:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 220:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_31
+ PIXEL13_31
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ break;
+ }
+ case 158:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL12_0
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 234:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_61
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 242:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_82
+ PIXEL31_32
+ break;
+ }
+ case 59:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL11_0
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 121:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_30
+ PIXEL13_10
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ break;
+ }
+ case 87:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_0
+ PIXEL20_61
+ PIXEL21_30
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 79:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL11_0
+ PIXEL12_32
+ PIXEL13_82
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 122:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ break;
+ }
+ case 94:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL12_0
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ break;
+ }
+ case 218:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ break;
+ }
+ case 91:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL11_0
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ break;
+ }
+ case 229:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 167:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 173:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 181:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 186:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 115:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL20_82
+ PIXEL21_32
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ PIXEL30_82
+ PIXEL31_32
+ break;
+ }
+ case 93:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_31
+ PIXEL13_31
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ break;
+ }
+ case 206:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL12_32
+ PIXEL13_82
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 205:
+ case 201:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_70
+ PIXEL13_60
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ }
+ else
+ {
+ PIXEL20_12
+ PIXEL21_0
+ PIXEL30_20
+ PIXEL31_11
+ }
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 174:
+ case 46:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL10_10
+ PIXEL11_30
+ }
+ else
+ {
+ PIXEL00_20
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_0
+ }
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 179:
+ case 147:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ }
+ else
+ {
+ PIXEL02_11
+ PIXEL03_20
+ PIXEL12_0
+ PIXEL13_12
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 117:
+ case 116:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_82
+ PIXEL21_32
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ }
+ else
+ {
+ PIXEL22_0
+ PIXEL23_11
+ PIXEL32_12
+ PIXEL33_20
+ }
+ PIXEL30_82
+ PIXEL31_32
+ break;
+ }
+ case 189:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 231:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 126:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_0
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 219:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 125:
+ {
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL00_82
+ PIXEL10_32
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL00_11
+ PIXEL10_13
+ PIXEL20_83
+ PIXEL21_70
+ PIXEL30_50
+ PIXEL31_21
+ }
+ PIXEL01_82
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL11_32
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 221:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_81
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL03_81
+ PIXEL13_31
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL03_12
+ PIXEL13_14
+ PIXEL22_70
+ PIXEL23_83
+ PIXEL32_21
+ PIXEL33_50
+ }
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_31
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 207:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_0
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_83
+ PIXEL02_13
+ PIXEL03_11
+ PIXEL10_21
+ PIXEL11_70
+ }
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_31
+ PIXEL23_81
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 238:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_32
+ PIXEL13_82
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL30_0
+ PIXEL31_0
+ PIXEL32_31
+ PIXEL33_81
+ }
+ else
+ {
+ PIXEL20_21
+ PIXEL21_70
+ PIXEL30_50
+ PIXEL31_83
+ PIXEL32_14
+ PIXEL33_12
+ }
+ PIXEL22_31
+ PIXEL23_81
+ break;
+ }
+ case 190:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL23_32
+ PIXEL33_82
+ }
+ else
+ {
+ PIXEL02_21
+ PIXEL03_50
+ PIXEL12_70
+ PIXEL13_83
+ PIXEL23_13
+ PIXEL33_11
+ }
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_32
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_82
+ break;
+ }
+ case 187:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL20_31
+ PIXEL30_81
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_21
+ PIXEL10_83
+ PIXEL11_70
+ PIXEL20_14
+ PIXEL30_12
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL21_31
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL31_81
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 243:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_82
+ PIXEL21_32
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL22_70
+ PIXEL23_21
+ PIXEL30_11
+ PIXEL31_13
+ PIXEL32_83
+ PIXEL33_50
+ }
+ break;
+ }
+ case 119:
+ {
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL12_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL00_12
+ PIXEL01_14
+ PIXEL02_83
+ PIXEL03_50
+ PIXEL12_70
+ PIXEL13_21
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 237:
+ case 233:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_60
+ PIXEL03_20
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_70
+ PIXEL13_60
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL22_31
+ PIXEL23_81
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL30_0
+ }
+ else
+ {
+ PIXEL30_20
+ }
+ PIXEL31_0
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 175:
+ case 47:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_0
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_70
+ PIXEL23_60
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_60
+ PIXEL33_20
+ break;
+ }
+ case 183:
+ case 151:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL03_0
+ }
+ else
+ {
+ PIXEL03_20
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL20_60
+ PIXEL21_70
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_20
+ PIXEL31_60
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 245:
+ case 244:
+ {
+ PIXEL00_20
+ PIXEL01_60
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_60
+ PIXEL11_70
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL33_20
+ }
+ break;
+ }
+ case 250:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_30
+ PIXEL13_10
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ break;
+ }
+ case 123:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_10
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 95:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL11_0
+ PIXEL12_0
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_80
+ PIXEL31_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 222:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_0
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 252:
+ {
+ PIXEL00_80
+ PIXEL01_61
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_31
+ PIXEL13_31
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL32_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL33_20
+ }
+ break;
+ }
+ case 249:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_61
+ PIXEL03_80
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL30_0
+ }
+ else
+ {
+ PIXEL30_20
+ }
+ PIXEL31_0
+ break;
+ }
+ case 235:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_61
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL22_31
+ PIXEL23_81
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL30_0
+ }
+ else
+ {
+ PIXEL30_20
+ }
+ PIXEL31_0
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 111:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_0
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL12_32
+ PIXEL13_82
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_61
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 63:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL12_0
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_61
+ PIXEL33_80
+ break;
+ }
+ case 159:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL03_0
+ }
+ else
+ {
+ PIXEL03_20
+ }
+ PIXEL11_0
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_80
+ PIXEL31_61
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 215:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL03_0
+ }
+ else
+ {
+ PIXEL03_20
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL20_61
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 246:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_61
+ PIXEL11_30
+ PIXEL12_0
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL33_20
+ }
+ break;
+ }
+ case 254:
+ {
+ PIXEL00_80
+ PIXEL01_10
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_10
+ PIXEL11_30
+ PIXEL12_0
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL32_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL33_20
+ }
+ break;
+ }
+ case 253:
+ {
+ PIXEL00_82
+ PIXEL01_82
+ PIXEL02_81
+ PIXEL03_81
+ PIXEL10_32
+ PIXEL11_32
+ PIXEL12_31
+ PIXEL13_31
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL22_0
+ PIXEL23_0
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL30_0
+ }
+ else
+ {
+ PIXEL30_20
+ }
+ PIXEL31_0
+ PIXEL32_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL33_20
+ }
+ break;
+ }
+ case 251:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_10
+ PIXEL03_80
+ PIXEL11_0
+ PIXEL12_30
+ PIXEL13_10
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL30_0
+ }
+ else
+ {
+ PIXEL30_20
+ }
+ PIXEL31_0
+ break;
+ }
+ case 239:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_0
+ PIXEL02_32
+ PIXEL03_82
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL12_32
+ PIXEL13_82
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL22_31
+ PIXEL23_81
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL30_0
+ }
+ else
+ {
+ PIXEL30_20
+ }
+ PIXEL31_0
+ PIXEL32_31
+ PIXEL33_81
+ break;
+ }
+ case 127:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL02_0
+ PIXEL03_0
+ PIXEL13_0
+ }
+ else
+ {
+ PIXEL02_50
+ PIXEL03_50
+ PIXEL13_50
+ }
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL12_0
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL20_0
+ PIXEL30_0
+ PIXEL31_0
+ }
+ else
+ {
+ PIXEL20_50
+ PIXEL30_50
+ PIXEL31_50
+ }
+ PIXEL21_0
+ PIXEL22_30
+ PIXEL23_10
+ PIXEL32_10
+ PIXEL33_80
+ break;
+ }
+ case 191:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_0
+ PIXEL02_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL03_0
+ }
+ else
+ {
+ PIXEL03_20
+ }
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL20_31
+ PIXEL21_31
+ PIXEL22_32
+ PIXEL23_32
+ PIXEL30_81
+ PIXEL31_81
+ PIXEL32_82
+ PIXEL33_82
+ break;
+ }
+ case 223:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ PIXEL01_0
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_50
+ PIXEL01_50
+ PIXEL10_50
+ }
+ PIXEL02_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL03_0
+ }
+ else
+ {
+ PIXEL03_20
+ }
+ PIXEL11_0
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL20_10
+ PIXEL21_30
+ PIXEL22_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL23_0
+ PIXEL32_0
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL23_50
+ PIXEL32_50
+ PIXEL33_50
+ }
+ PIXEL30_80
+ PIXEL31_10
+ break;
+ }
+ case 247:
+ {
+ PIXEL00_81
+ PIXEL01_31
+ PIXEL02_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL03_0
+ }
+ else
+ {
+ PIXEL03_20
+ }
+ PIXEL10_81
+ PIXEL11_31
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL20_82
+ PIXEL21_32
+ PIXEL22_0
+ PIXEL23_0
+ PIXEL30_82
+ PIXEL31_32
+ PIXEL32_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL33_20
+ }
+ break;
+ }
+ case 255:
+ {
+ if (Diff(w[4], w[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_0
+ PIXEL02_0
+ if (Diff(w[2], w[6]))
+ {
+ PIXEL03_0
+ }
+ else
+ {
+ PIXEL03_20
+ }
+ PIXEL10_0
+ PIXEL11_0
+ PIXEL12_0
+ PIXEL13_0
+ PIXEL20_0
+ PIXEL21_0
+ PIXEL22_0
+ PIXEL23_0
+ if (Diff(w[8], w[4]))
+ {
+ PIXEL30_0
+ }
+ else
+ {
+ PIXEL30_20
+ }
+ PIXEL31_0
+ PIXEL32_0
+ if (Diff(w[6], w[8]))
+ {
+ PIXEL33_0
+ }
+ else
+ {
+ PIXEL33_20
+ }
+ break;
+ }
+ }
+ pIn++; // next source pixel (just increment since it's an int*)
+ pOut += 16; // skip 4 pixels (4 bytes * 4 pixels)
+ }
+ pOut += BpL; // skip next 3 rows
+ pOut += BpL;
+ pOut += BpL;
+ }
+ hq_vec::reset();
+}
+
+void DLL InitLUTs()
+{
+ int i, j, k, r, g, b, Y, u, v;
+
+#if 0 // colorOutlines() after hqresize
+ for (i=0; i<65536; i++)
+ LUT16to32[i] = 0x00404040;
+ for (i=0; i<65536; i++)
+ LUT16to32[i+65536] = 0xFF000000 + ((i & 0xF800) << 8) + ((i & 0x07E0) << 5) + ((i & 0x001F) << 3);
+#else // colorOutlines() before hqresize
+ for (i=0; i<65536; i++)
+ LUT16to32[i] = ((i & 0xF800) << 8) + ((i & 0x07E0) << 5) + ((i & 0x001F) << 3);
+ for (i=0; i<65536; i++)
+ LUT16to32[i+65536] = 0xFF000000 + LUT16to32[i];
+#endif
+
+ for (i=0; i<65536; i++)
+ RGBtoYUV[i] = 0xFF000000;
+
+ for (i=0; i<32; i++)
+ for (j=0; j<64; j++)
+ for (k=0; k<32; k++)
+ {
+ r = i << 3;
+ g = j << 2;
+ b = k << 3;
+ Y = (r + g + b) >> 2;
+ u = 128 + ((r - b) >> 2);
+ v = 128 + ((-r + 2*g -b)>>3);
+ RGBtoYUV[ 65536 + (i << 11) + (j << 5) + k ] = (Y<<16) + (u<<8) + v;
+ }
+}
+
+/*
+int DLL hq4x_32 ( CImage &ImageIn, CImage &ImageOut )
+{
+ if ( ImageIn.Convert32To17() != 0 )
+ {
+ printf( "ERROR: conversion to 17 bit failed\n" );
+ return 1;
+ }
+
+ if ( ImageOut.Init( ImageIn.m_Xres*4, ImageIn.m_Yres*4, 32 ) != 0 )
+ {
+ printf( "ERROR: ImageOut.Init()\n" );
+ return 1;
+ };
+
+ InitLUTs();
+ hq4x_32( (int*)ImageIn.m_pBitmap, ImageOut.m_pBitmap, ImageIn.m_Xres, ImageIn.m_Yres, ImageOut.m_Xres*4 );
+
+ printf( "\nOK\n" );
+ return 0;
+}
+*/
+
+}
\ No newline at end of file
diff --git a/src/gl/hqnx_asm/hqnx_asm.h b/src/gl/hqnx_asm/hqnx_asm.h
new file mode 100644
index 000000000..9ced349fa
--- /dev/null
+++ b/src/gl/hqnx_asm/hqnx_asm.h
@@ -0,0 +1,234 @@
+//hqnx filter library
+//----------------------------------------------------------
+//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )
+//Copyright (C) 2009 Benjamin Berkels
+//Copyright (C) 2012-2014 Alexey Lysiuk
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU Lesser General Public
+//License as published by the Free Software Foundation; either
+//version 2.1 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+//Lesser General Public License for more details.
+//
+//You should have received a copy of the GNU Lesser General Public
+//License along with this program; if not, write to the Free Software
+//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#ifndef __HQNX_H__
+#define __HQNX_H__
+
+#ifdef _MSC_VER
+#pragma warning(disable:4799)
+#endif // _MSC_VER
+
+#include "hqnx_asm_Image.h"
+
+// IMPORTANT NOTE!
+// The following is not a generic vectorized math class
+// Each member function or overloaded operator does specific task to simplify client code
+// To re-implement this class for different platform you need check very carefully
+// the Intel C++ Intrinsic Reference at http://software.intel.com/file/18072/
+
+#if defined _MSC_VER && defined _M_X64
+
+// Implementation via SSE2 intrinsics
+// MSVC doesn't support MMX intrinsics on x64
+
+#include
+
+class hq_vec
+{
+public:
+ hq_vec(const int value)
+ : m_value(_mm_cvtsi32_si128(value))
+ {
+ }
+
+ static hq_vec load(const int source)
+ {
+ return _mm_unpacklo_epi8(_mm_cvtsi32_si128(source), _mm_cvtsi32_si128(0));
+ }
+
+ static hq_vec expand(const short source)
+ {
+ return _mm_set_epi16(source, source, source, source, source, source, source, source);
+ }
+
+ void store(unsigned char* const destination) const
+ {
+ *reinterpret_cast(destination) = _mm_cvtsi128_si32(_mm_packus_epi16(m_value, _mm_cvtsi32_si128(0)));
+ }
+
+ static void reset()
+ {
+ }
+
+ hq_vec& operator+=(const hq_vec& right)
+ {
+ m_value = _mm_add_epi16(m_value, right.m_value);
+ return *this;
+ }
+
+ hq_vec& operator*=(const hq_vec& right)
+ {
+ m_value = _mm_mullo_epi16(m_value, right.m_value);
+ return *this;
+ }
+
+ hq_vec& operator<<(const int count)
+ {
+ m_value = _mm_sll_epi16(m_value, _mm_cvtsi32_si128(count));
+ return *this;
+ }
+
+ hq_vec& operator>>(const int count)
+ {
+ m_value = _mm_srl_epi16(m_value, _mm_cvtsi32_si128(count));
+ return *this;
+ }
+
+private:
+ __m128i m_value;
+
+ hq_vec(const __m128i value)
+ : m_value(value)
+ {
+ }
+
+ friend hq_vec operator- (const hq_vec&, const hq_vec&);
+ friend hq_vec operator* (const hq_vec&, const hq_vec&);
+ friend hq_vec operator| (const hq_vec&, const hq_vec&);
+ friend bool operator!=(const int, const hq_vec&);
+};
+
+inline hq_vec operator-(const hq_vec& left, const hq_vec& right)
+{
+ return _mm_subs_epu8(left.m_value, right.m_value);
+}
+
+inline hq_vec operator*(const hq_vec& left, const hq_vec& right)
+{
+ return _mm_mullo_epi16(left.m_value, right.m_value);
+}
+
+inline hq_vec operator|(const hq_vec& left, const hq_vec& right)
+{
+ return _mm_or_si128(left.m_value, right.m_value);
+}
+
+inline bool operator!=(const int left, const hq_vec& right)
+{
+ return left != _mm_cvtsi128_si32(right.m_value);
+}
+
+#else // _M_X64
+
+// Implementation via MMX intrinsics
+
+#include
+
+class hq_vec
+{
+public:
+ hq_vec(const int value)
+ : m_value(_mm_cvtsi32_si64(value))
+ {
+ }
+
+ static hq_vec load(const int source)
+ {
+ return _mm_unpacklo_pi8(_mm_cvtsi32_si64(source), _mm_cvtsi32_si64(0));
+ }
+
+ static hq_vec expand(const short source)
+ {
+ return _mm_set_pi16(source, source, source, source);
+ }
+
+ void store(unsigned char* const destination) const
+ {
+ *reinterpret_cast(destination) = _mm_cvtsi64_si32(_mm_packs_pu16(m_value, _mm_cvtsi32_si64(0)));
+ }
+
+ static void reset()
+ {
+ _mm_empty();
+ }
+
+ hq_vec& operator+=(const hq_vec& right)
+ {
+ m_value = _mm_add_pi16(m_value, right.m_value);
+ return *this;
+ }
+
+ hq_vec& operator*=(const hq_vec& right)
+ {
+ m_value = _mm_mullo_pi16(m_value, right.m_value);
+ return *this;
+ }
+
+ hq_vec& operator<<(const int count)
+ {
+ m_value = _mm_sll_pi16(m_value, _mm_cvtsi32_si64(count));
+ return *this;
+ }
+
+ hq_vec& operator>>(const int count)
+ {
+ m_value = _mm_srl_pi16(m_value, _mm_cvtsi32_si64(count));
+ return *this;
+ }
+
+private:
+ __m64 m_value;
+
+ hq_vec(const __m64 value)
+ : m_value(value)
+ {
+ }
+
+ friend hq_vec operator- (const hq_vec&, const hq_vec&);
+ friend hq_vec operator* (const hq_vec&, const hq_vec&);
+ friend hq_vec operator| (const hq_vec&, const hq_vec&);
+ friend bool operator!=(const int, const hq_vec&);
+};
+
+inline hq_vec operator-(const hq_vec& left, const hq_vec& right)
+{
+ return _mm_subs_pu8(left.m_value, right.m_value);
+}
+
+inline hq_vec operator*(const hq_vec& left, const hq_vec& right)
+{
+ return _mm_mullo_pi16(left.m_value, right.m_value);
+}
+
+inline hq_vec operator|(const hq_vec& left, const hq_vec& right)
+{
+ return _mm_or_si64(left.m_value, right.m_value);
+}
+
+inline bool operator!=(const int left, const hq_vec& right)
+{
+ return left != _mm_cvtsi64_si32(right.m_value);
+}
+
+#endif // _MSC_VER && _M_X64
+
+namespace HQnX_asm
+{
+void DLL hq2x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL );
+void DLL hq3x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL );
+void DLL hq4x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL );
+int DLL hq4x_32 ( CImage &ImageIn, CImage &ImageOut );
+
+void DLL InitLUTs();
+
+}
+
+
+#endif //__HQNX_H__
\ No newline at end of file
diff --git a/src/gl/hqnx_asm/hqnx_asm_Image.cpp b/src/gl/hqnx_asm/hqnx_asm_Image.cpp
new file mode 100644
index 000000000..7af120173
--- /dev/null
+++ b/src/gl/hqnx_asm/hqnx_asm_Image.cpp
@@ -0,0 +1,1183 @@
+//CImage class - loading and saving BMP and TGA files
+//----------------------------------------------------------
+//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU Lesser General Public
+//License as published by the Free Software Foundation; either
+//version 2.1 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+//Lesser General Public License for more details.
+//
+//You should have received a copy of the GNU Lesser General Public
+//License along with this program; if not, write to the Free Software
+//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#include
+#include
+#include "hqnx_asm_Image.h"
+
+#ifndef _MSC_VER
+#define _stricmp strcasecmp
+#endif
+
+namespace HQnX_asm
+{
+
+DLL CImage::CImage()
+{
+ m_Xres = m_Yres = m_NumPixel = 0;
+ m_pBitmap = NULL;
+}
+
+DLL CImage::~CImage()
+{
+ Destroy();
+}
+
+int DLL CImage::Init( int X, int Y, unsigned short BitPerPixel )
+{
+ if (m_pBitmap != NULL)
+ free(m_pBitmap);
+
+ m_Xres = X;
+ m_Yres = Y;
+ m_BitPerPixel = BitPerPixel<=8 ? 8 : BitPerPixel<=16 ? 16 : BitPerPixel<=24 ? 24 : 32;
+ m_BytePerPixel = m_BitPerPixel >> 3;
+ m_NumPixel = m_Xres*m_Yres;
+ int size = m_NumPixel*((m_BitPerPixel+7)/8);
+ m_pBitmap=(unsigned char *)malloc(size);
+ return (m_pBitmap != NULL) ? 0 : 1;
+}
+
+int DLL CImage::SetImage(unsigned char *img, int width, int height, int bpp)
+{
+ Init(width, height, bpp);
+
+ memcpy(m_pBitmap, img, m_NumPixel * m_BytePerPixel);
+
+ return 0;
+}
+
+int DLL CImage::Destroy()
+{
+ if (m_pBitmap)
+ {
+ free(m_pBitmap);
+ m_pBitmap = NULL;
+ }
+ m_Xres = 0;
+ m_Yres = 0;
+ m_NumPixel = 0;
+ m_BitPerPixel = 0;
+ return 0;
+}
+
+int DLL CImage::Convert32To17( void )
+{
+ int nRes = eConvUnknownFormat;
+
+ if ( m_BitPerPixel == 32 )
+ {
+ if ( m_pBitmap != NULL )
+ {
+ unsigned char * pTemp8 = m_pBitmap;
+ unsigned int * pTemp32 = (unsigned int *)m_pBitmap;
+ unsigned int a, r, g, b;
+ for ( int i=0; i> 3;
+ g = (*(pTemp8++)) >> 2;
+ r = (*(pTemp8++)) >> 3;
+ a = *(pTemp8++);
+ *pTemp32 = (r << 11) + (g << 5) + b + (a > 127 ? 0x10000 : 0);
+ pTemp32++;
+ }
+ }
+ else
+ nRes = eConvSourceMemory;
+
+ nRes = 0;
+ }
+
+ return nRes;
+}
+
+int DLL CImage::ConvertTo32( void )
+{
+ int nRes = eConvUnknownFormat;
+
+ if ( m_pBitmap == NULL )
+ return eConvSourceMemory;
+
+ switch ( m_BitPerPixel )
+ {
+ case 8:
+ {
+ nRes = 0;
+ m_BitPerPixel = 32;
+ unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*4);
+ if ( pNewBitmap != NULL )
+ {
+ unsigned char * pTemp8 = m_pBitmap;
+ unsigned char * pTemp32 = pNewBitmap;
+ unsigned char c;
+ for ( int i=0; i> 3);
+ *(pTemp24++) = ((rgb & 0xF800) >> 8);
+ }
+ free(m_pBitmap);
+ m_pBitmap = pNewBitmap;
+ }
+ else
+ nRes = eConvDestMemory;
+
+ break;
+ }
+ case 32:
+ {
+ nRes = 0;
+ m_BitPerPixel = 24;
+ unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*3);
+ if ( pNewBitmap != NULL )
+ {
+ unsigned char * pTemp32 = m_pBitmap;
+ unsigned char * pTemp24 = pNewBitmap;
+ for ( int i=0; i> 3;
+ g = m_Pal[c].g >> 2;
+ b = m_Pal[c].b >> 3;
+ *(pTemp16++) = (r << 11) + (g << 5) + b;
+ }
+ free(m_pBitmap);
+ m_pBitmap = pNewBitmap;
+ }
+ else
+ nRes = eConvDestMemory;
+
+ break;
+ }
+ case 24:
+ {
+ nRes = 0;
+ m_BitPerPixel = 16;
+ unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*2);
+ if ( pNewBitmap != NULL )
+ {
+ unsigned char * pTemp24 = m_pBitmap;
+ unsigned short * pTemp16 = (unsigned short *)pNewBitmap;
+ unsigned short r, g, b;
+ for ( int i=0; i> 3;
+ g = (*(pTemp24++)) >> 2;
+ r = (*(pTemp24++)) >> 3;
+ *(pTemp16++) = (r << 11) + (g << 5) + b;
+ }
+ free(m_pBitmap);
+ m_pBitmap = pNewBitmap;
+ }
+ else
+ nRes = eConvDestMemory;
+
+ break;
+ }
+ case 32:
+ {
+ nRes = 0;
+ m_BitPerPixel = 16;
+ unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*2);
+ if ( pNewBitmap != NULL )
+ {
+ unsigned char * pTemp32 = m_pBitmap;
+ unsigned short * pTemp16 = (unsigned short *)pNewBitmap;
+ unsigned short r, g, b;
+ for ( int i=0; i> 3;
+ g = (*(pTemp32++)) >> 2;
+ r = (*(pTemp32++)) >> 3;
+ pTemp32++;
+ *(pTemp16++) = (r << 11) + (g << 5) + b;
+ }
+ free(m_pBitmap);
+ m_pBitmap = pNewBitmap;
+ }
+ else
+ nRes = eConvDestMemory;
+
+ break;
+ }
+ }
+
+ return nRes;
+}
+
+int CImage::Convert8To17( int transindex )
+{
+ int nRes = eConvUnknownFormat;
+
+ if ( m_BitPerPixel == 8 )
+ {
+ m_BitPerPixel = 32;
+ unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*4);
+ if ( pNewBitmap != NULL )
+ {
+ unsigned char * pTemp8 = m_pBitmap;
+ unsigned int * pTemp32 = (unsigned int *)pNewBitmap;
+ unsigned int r, g, b;
+ unsigned char c;
+ for ( int i=0; i> 3;
+ g = m_Pal[c].g >> 2;
+ b = m_Pal[c].b >> 3;
+ *(pTemp32++) = (r << 11) + (g << 5) + b + (transindex != c ? 0x10000 : 0);
+ }
+ free(m_pBitmap);
+ m_pBitmap = pNewBitmap;
+ }
+ else
+ nRes = eConvDestMemory;
+
+ nRes = 0;
+ }
+
+ return nRes;
+}
+
+int CImage::SaveBmp(char *szFilename)
+{
+ _BMPFILEHEADER fh;
+ _BMPIMAGEHEADER ih;
+ unsigned char BmpPal[256][4];
+ long int SuffLen;
+ long int Dummy = 0;
+ unsigned char * pBuf;
+ short i;
+
+ if (!(f = fopen(szFilename, "wb"))) return eSaveBmpFileOpen;
+ if ( m_pBitmap == NULL ) return eSaveBmpSourceMemory;
+
+ fh.bfType=0x4D42;
+ if (m_BitPerPixel==8)
+ {
+ SuffLen=((m_Xres+3)/4)*4-m_Xres;
+ ih.biSize=0x28;
+ ih.biWidth=m_Xres;
+ ih.biHeight=m_Yres;
+ ih.biPlanes=1;
+ ih.biBitCount=8;
+ ih.biCompression=0;
+ ih.biSizeImage=(m_Xres+SuffLen)*m_Yres;
+ ih.biXPelsPerMeter=ih.biYPelsPerMeter=0x2E23; // 300dpi (pixels per meter)
+ ih.biClrUsed=ih.biClrImportant=0;
+ fh.bfSize=(ih.biSizeImage)+0x0436;
+ fh.bfRes1=0;
+ fh.bfOffBits=0x0436;
+ if (fwrite(&fh, 14, 1, f) != 1) return eSaveBmpFileWrite;
+ if (fwrite(&ih, 40, 1, f) != 1) return eSaveBmpFileWrite;
+ for (i=0; i<256; i++)
+ {
+ BmpPal[i][0]=m_Pal[i].b;
+ BmpPal[i][1]=m_Pal[i].g;
+ BmpPal[i][2]=m_Pal[i].r;
+ BmpPal[i][3]=0;
+ }
+ if (fwrite(&BmpPal, 1024, 1, f) != 1) return eSaveBmpFileWrite;
+ pBuf=m_pBitmap;
+ pBuf+=m_NumPixel;
+ for (i=0; i0)
+ {
+ if (fwrite(&Dummy, SuffLen, 1, f) != 1) return eSaveBmpFileWrite;
+ }
+ }
+ }
+ else
+ if (m_BitPerPixel==24)
+ {
+ SuffLen=((m_Xres*3+3)/4)*4-m_Xres*3;
+ ih.biSize=0x28;
+ ih.biWidth=m_Xres;
+ ih.biHeight=m_Yres;
+ ih.biPlanes=1;
+ ih.biBitCount=24;
+ ih.biCompression=0;
+ ih.biSizeImage=(m_Xres*3+SuffLen)*m_Yres;
+ ih.biXPelsPerMeter=ih.biYPelsPerMeter=0x2E23; // 300dpi (pixels per meter)
+ ih.biClrUsed=ih.biClrImportant=0;
+ fh.bfSize=(ih.biSizeImage)+0x0036;
+ fh.bfRes1=0;
+ fh.bfOffBits=0x0036;
+ if (fwrite(&fh, 14, 1, f) != 1) return eSaveBmpFileWrite;
+ if (fwrite(&ih, 40, 1, f) != 1) return eSaveBmpFileWrite;
+ pBuf=m_pBitmap;
+ pBuf+=m_NumPixel*3;
+ for (i=0; i0)
+ {
+ if (fwrite(&Dummy, SuffLen, 1, f) != 1) return eSaveBmpFileWrite;
+ }
+ }
+ }
+ else
+ return eSaveBmpColorDepth;
+
+ fclose(f);
+
+ return 0;
+}
+
+int CImage::LoadBmp(char *szFilename)
+{
+ _BMPFILEHEADER fh;
+ _BMPIMAGEHEADEROLD ih_old;
+ _BMPIMAGEHEADER ih;
+ unsigned char BmpPal[256][4];
+ long int biSize;
+ long int SuffLen;
+ long int Dummy = 0;
+ unsigned char * pBuf;
+ short i;
+ long int xres, yres;
+ unsigned short bits;
+
+ if (!(f = fopen(szFilename, "rb"))) return eLoadBmpFileOpen;
+ if (fread(&fh, 14, 1, f) != 1) return eLoadBmpFileRead;
+ if (fh.bfType != 0x4D42) return eLoadBmpBadFormat;
+ if (fread(&biSize, 4, 1, f) != 1) return eLoadBmpFileRead;
+ if (biSize > 12)
+ {
+ fseek( f, -4, SEEK_CUR );
+ if (fread(&ih, biSize, 1, f) != 1) return eLoadBmpFileRead;
+ xres = ih.biWidth;
+ yres = ih.biHeight;
+ bits = ih.biBitCount;
+ }
+ else
+ {
+ fseek( f, -4, SEEK_CUR );
+ if (fread(&ih_old, biSize, 1, f) != 1) return eLoadBmpFileRead;
+ xres = ih_old.biWidth;
+ yres = ih_old.biHeight;
+ bits = ih_old.biBitCount;
+ }
+
+ if ( Init( xres, yres, bits ) != 0 ) return eLoadBmpInit;
+ if (m_BitPerPixel==8)
+ {
+ SuffLen=((m_Xres+3)/4)*4-m_Xres;
+ if (fread(&BmpPal, 1024, 1, f) != 1) return eLoadBmpFileRead;
+ for (i=0; i<256; i++)
+ {
+ m_Pal[i].b=BmpPal[i][0];
+ m_Pal[i].g=BmpPal[i][1];
+ m_Pal[i].r=BmpPal[i][2];
+ }
+ pBuf=m_pBitmap;
+ pBuf+=m_NumPixel;
+ for (i=0; i0)
+ {
+ if (fread(&Dummy, SuffLen, 1, f) != 1) return eLoadBmpFileRead;
+ }
+ }
+ }
+ else
+ if (m_BitPerPixel==24)
+ {
+ SuffLen=((m_Xres*3+3)/4)*4-(m_Xres*3);
+ pBuf=m_pBitmap;
+ pBuf+=m_NumPixel*3;
+ for (i=0; i0)
+ {
+ if (fread(&Dummy, SuffLen, 1, f) != 1) return eLoadBmpFileRead;
+ }
+ }
+ }
+ else
+ return eLoadBmpColorDepth;
+
+ fclose(f);
+
+ return 0;
+}
+
+void CImage::Output( void )
+{
+ fwrite(m_cBuf, m_nCount, 1, f);
+ m_nCount=0;
+}
+
+void CImage::Output( char c )
+{
+ if ( m_nCount == sizeof(m_cBuf) )
+ {
+ fwrite(m_cBuf, m_nCount, 1, f);
+ m_nCount=0;
+ }
+ m_cBuf[m_nCount++] = c;
+}
+
+void CImage::Output( char * pcData, int nSize )
+{
+ for ( int i=0; i 0 )
+ {
+ Output( nDif-1 );
+ Output( (char*)(pcolBuf+i-nDif-nRep), nDif );
+ }
+ nDif = 1;
+ }
+ }
+ else
+ {
+ if ( bEqual && (nRep<127) )
+ nRep++;
+ else
+ {
+ Output( nRep+128 );
+ Output( (char*)&colOld, 1 );
+ nRep = 0;
+ nDif = 1;
+ }
+ }
+ }
+
+ if ( nRep == 0 )
+ {
+ Output( nDif-1 );
+ Output( (char*)(pcolBuf+m_Xres-nDif), nDif );
+ }
+ else
+ {
+ Output( nRep+128 );
+ Output( (char*)&colOld, 1 );
+ }
+ Output();
+ }
+ }
+ }
+ else
+ if (m_BitPerPixel==24)
+ {
+ fh.tiImageType = bCompressed ? 10 : 2;
+ if (fwrite(&fh, sizeof(fh), 1, f) != 1) return eSaveTgaFileWrite;
+
+ _BGR * pcolBuf = (_BGR *)m_pBitmap;
+ pcolBuf += m_NumPixel;
+
+ if ( !bCompressed )
+ {
+ for (j=0; j 0 )
+ {
+ Output( nDif-1 );
+ Output( (char*)(pcolBuf+i-nDif-nRep), sizeof(_BGR)*nDif );
+ }
+ nDif = 1;
+ }
+ }
+ else
+ {
+ if ( bEqual && (nRep<127) )
+ nRep++;
+ else
+ {
+ Output( nRep+128 );
+ Output( (char*)&colOld, sizeof(_BGR) );
+ nRep = 0;
+ nDif = 1;
+ }
+ }
+ }
+
+ if ( nRep == 0 )
+ {
+ Output( nDif-1 );
+ Output( (char*)(pcolBuf+m_Xres-nDif), sizeof(_BGR)*nDif );
+ }
+ else
+ {
+ Output( nRep+128 );
+ Output( (char*)&colOld, sizeof(_BGR) );
+ }
+ Output();
+ }
+ }
+ }
+ else
+ if (m_BitPerPixel==32)
+ {
+ fh.tiImageType = bCompressed ? 10 : 2;
+ fh.tiAttrBits = 8;
+ if (fwrite(&fh, sizeof(fh), 1, f) != 1) return eSaveTgaFileWrite;
+
+ _BGRA * pcolBuf = (_BGRA *)m_pBitmap;
+ pcolBuf += m_NumPixel;
+
+ if ( !bCompressed )
+ {
+ for (j=0; j 0 )
+ {
+ Output( nDif-1 );
+ Output( (char*)(pcolBuf+i-nDif-nRep), sizeof(_BGRA)*nDif );
+ }
+ nDif = 1;
+ }
+ }
+ else
+ {
+ if ( bEqual && (nRep<127) )
+ nRep++;
+ else
+ {
+ Output( nRep+128 );
+ Output( (char*)&colOld, sizeof(_BGRA) );
+ nRep = 0;
+ nDif = 1;
+ }
+ }
+ }
+
+ if ( nRep == 0 )
+ {
+ Output( nDif-1 );
+ Output( (char*)(pcolBuf+m_Xres-nDif), sizeof(_BGRA)*nDif );
+ }
+ else
+ {
+ Output( nRep+128 );
+ Output( (char*)&colOld, sizeof(_BGRA) );
+ }
+ Output();
+ }
+ }
+ }
+ else
+ return eSaveTgaColorDepth;
+
+ fclose(f);
+
+ return 0;
+}
+
+int CImage::LoadTga(char *szFilename)
+{
+ _TGAHEADER fh;
+ int i, j, k;
+ unsigned char nCount;
+
+ if (!(f = fopen(szFilename, "rb"))) return eLoadTgaFileOpen;
+ if (fread(&fh, sizeof(fh), 1, f) != 1) return eLoadTgaFileRead;
+ bool bCompressed = (( fh.tiImageType & 8 ) != 0);
+ if ((fh.tiBitPerPixel<=0) || (fh.tiBitPerPixel>32))
+ return eLoadTgaBadFormat;
+
+ if ( Init( fh.tiXres, fh.tiYres, fh.tiBitPerPixel ) != 0 )
+ return eLoadTgaInit;
+
+ if ( m_BitPerPixel == 8 )
+ {
+ if ( fh.tiPaletteIncluded == 1 )
+ {
+ if ( fh.tiPaletteBpp == 24)
+ {
+ if (fread(&m_Pal, 3, fh.tiPaletteSize, f) != fh.tiPaletteSize)
+ return eLoadTgaFileRead;
+ }
+ else
+ if ( fh.tiPaletteBpp == 32)
+ {
+ unsigned char BmpPal[256][4];
+
+ if (fread(&BmpPal, 4, fh.tiPaletteSize, f) != fh.tiPaletteSize)
+ return eLoadTgaFileRead;
+
+ for (i=0; i= m_pBitmap )
+ {
+ nCount = Input();
+ if ((nCount & 128)==0)
+ {
+ for (k=0; k<=nCount; k++)
+ {
+ colCur = Input();
+ *(pcolBuf+i) = colCur;
+ if ( (++i) == m_Xres )
+ {
+ i=0;
+ pcolBuf -= m_Xres;
+ break;
+ }
+ }
+ }
+ else
+ {
+ colCur = Input();
+ for (k=0; k<=nCount-128; k++)
+ {
+ *(pcolBuf+i) = colCur;
+ if ( (++i) == m_Xres )
+ {
+ i=0;
+ pcolBuf -= m_Xres;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ if ( m_BitPerPixel == 24 )
+ {
+ _BGR * pcolBuf = (_BGR *)m_pBitmap;
+ pcolBuf += m_NumPixel;
+
+ if ( !bCompressed )
+ {
+ for (j=0; j= (_BGR *)m_pBitmap )
+ {
+ nCount = Input();
+ if ((nCount & 128)==0)
+ {
+ for (k=0; k<=nCount; k++)
+ {
+ colCur.b = Input();
+ colCur.g = Input();
+ colCur.r = Input();
+ *(pcolBuf+i) = colCur;
+ if ( (++i) == m_Xres )
+ {
+ i=0;
+ pcolBuf -= m_Xres;
+ break;
+ }
+ }
+ }
+ else
+ {
+ colCur.b = Input();
+ colCur.g = Input();
+ colCur.r = Input();
+ for (k=0; k<=nCount-128; k++)
+ {
+ *(pcolBuf+i) = colCur;
+ if ( (++i) == m_Xres )
+ {
+ i=0;
+ pcolBuf -= m_Xres;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ if ( m_BitPerPixel == 32 )
+ {
+ _BGRA * pcolBuf = (_BGRA *)m_pBitmap;
+ pcolBuf += m_NumPixel;
+
+ if ( !bCompressed )
+ {
+ for (j=0; j= (_BGRA *)m_pBitmap )
+ {
+ nCount = Input();
+ if ((nCount & 128)==0)
+ {
+ for (k=0; k<=nCount; k++)
+ {
+ colCur.b = Input();
+ colCur.g = Input();
+ colCur.r = Input();
+ colCur.a = Input();
+ *(pcolBuf+i) = colCur;
+ if ( (++i) == m_Xres )
+ {
+ i=0;
+ pcolBuf -= m_Xres;
+ break;
+ }
+ }
+ }
+ else
+ {
+ colCur.b = Input();
+ colCur.g = Input();
+ colCur.r = Input();
+ colCur.a = Input();
+ for (k=0; k<=nCount-128; k++)
+ {
+ *(pcolBuf+i) = colCur;
+ if ( (++i) == m_Xres )
+ {
+ i=0;
+ pcolBuf -= m_Xres;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ return eLoadTgaColorDepth;
+
+ fclose(f);
+
+ return 0;
+}
+
+int DLL CImage::Load(char *szFilename)
+{
+ int nRes = 0;
+
+ if ( szFilename != NULL )
+ {
+ char * szExt = strrchr( szFilename, '.' );
+ int nNotTGA = 1;
+
+ if ( szExt != NULL )
+ nNotTGA = _stricmp( szExt, ".tga" );
+
+ if ( nNotTGA != 0 )
+ nRes = LoadBmp( szFilename );
+ else
+ nRes = LoadTga( szFilename );
+ }
+ else
+ nRes = eLoadFilename;
+
+ return nRes;
+}
+
+int DLL CImage::Save(char *szFilename)
+{
+ int nRes = 0;
+ int nNotTGA = 1;
+
+ if ( szFilename != NULL )
+ {
+ char * szExt = strrchr( szFilename, '.' );
+
+ if ( szExt != NULL )
+ nNotTGA = _stricmp( szExt, ".tga" );
+
+ if ( nNotTGA != 0 )
+ {
+ if (( m_BitPerPixel == 16 ) || ( m_BitPerPixel == 32 ))
+ nRes = ConvertTo24();
+
+ if (nRes == 0)
+ nRes = SaveBmp( szFilename );
+ }
+ else
+ {
+ if ( m_BitPerPixel == 16 )
+ nRes = ConvertTo24();
+
+ if (nRes == 0)
+ nRes = SaveTga( szFilename, true );
+ }
+ }
+ else
+ nRes = eSaveFilename;
+
+ return nRes;
+}
+
+}
\ No newline at end of file
diff --git a/src/gl/hqnx_asm/hqnx_asm_Image.h b/src/gl/hqnx_asm/hqnx_asm_Image.h
new file mode 100644
index 000000000..38f0f1d6a
--- /dev/null
+++ b/src/gl/hqnx_asm/hqnx_asm_Image.h
@@ -0,0 +1,152 @@
+//CImage class - loading and saving BMP and TGA files
+//----------------------------------------------------------
+//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU Lesser General Public
+//License as published by the Free Software Foundation; either
+//version 2.1 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+//Lesser General Public License for more details.
+//
+//You should have received a copy of the GNU Lesser General Public
+//License along with this program; if not, write to the Free Software
+//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//#ifdef WIN32
+//#define DLL __declspec(dllexport)
+//#else
+#define DLL
+//#endif
+
+#include
+#pragma once
+#ifdef _MSC_VER
+#pragma warning(disable: 4103)
+#endif // _MSC_VER
+#pragma pack(1)
+
+namespace HQnX_asm
+{
+
+typedef struct { unsigned char b, g, r; } _BGR;
+typedef struct { unsigned char b, g, r, a; } _BGRA;
+
+class CImage
+{
+ public:
+ DLL CImage();
+ DLL ~CImage();
+
+ enum CImageErrors
+ {
+ eConvUnknownFormat = 10,
+ eConvSourceMemory = 11,
+ eConvDestMemory = 12,
+
+ eSaveBmpFileOpen = 20,
+ eSaveBmpFileWrite = 21,
+ eSaveBmpSourceMemory = 22,
+ eSaveBmpColorDepth = 23,
+
+ eLoadBmpFileOpen = 30,
+ eLoadBmpFileRead = 31,
+ eLoadBmpBadFormat = 32,
+ eLoadBmpInit = 33,
+ eLoadBmpColorDepth = 34,
+
+ eSaveTgaFileOpen = 40,
+ eSaveTgaFileWrite = 41,
+ eSaveTgaSourceMemory = 42,
+ eSaveTgaColorDepth = 43,
+
+ eLoadTgaFileOpen = 50,
+ eLoadTgaFileRead = 51,
+ eLoadTgaBadFormat = 52,
+ eLoadTgaInit = 53,
+ eLoadTgaColorDepth = 54,
+
+ eLoadFilename = 60,
+ eSaveFilename = 61,
+ };
+
+ struct _BMPFILEHEADER
+ {
+ unsigned short bfType;
+ long int bfSize, bfRes1, bfOffBits;
+ };
+
+ struct _BMPIMAGEHEADEROLD
+ {
+ long int biSize;
+ unsigned short biWidth, biHeight;
+ unsigned short biPlanes, biBitCount;
+ };
+
+ struct _BMPIMAGEHEADER
+ {
+ long int biSize, biWidth, biHeight;
+ unsigned short biPlanes, biBitCount;
+ long int biCompression, biSizeImage;
+ long int biXPelsPerMeter, biYPelsPerMeter;
+ long int biClrUsed, biClrImportant;
+ };
+
+ struct _TGAHEADER
+ {
+ unsigned char tiIdentSize;
+ unsigned char tiPaletteIncluded;
+ unsigned char tiImageType;
+ unsigned short tiPaletteStart;
+ unsigned short tiPaletteSize;
+ unsigned char tiPaletteBpp;
+ unsigned short tiX0;
+ unsigned short tiY0;
+ unsigned short tiXres;
+ unsigned short tiYres;
+ unsigned char tiBitPerPixel;
+ unsigned char tiAttrBits;
+ };
+
+ public:
+ int DLL Init( int Xres, int Yres, unsigned short BitPerPixel );
+ int DLL SetImage(unsigned char *img, int width, int height, int bpp);
+ int DLL Destroy();
+ int DLL ConvertTo32( void );
+ int DLL ConvertTo24( void );
+ int DLL ConvertTo16( void );
+ int DLL Convert8To17( int transindex );
+ int DLL Convert32To17( void );
+ int SaveBmp(char *szFilename);
+ int LoadBmp(char *szFilename);
+ int SaveTga(char *szFilename, bool bCompressed );
+ int LoadTga(char *szFilename);
+ int DLL Load(char *szFilename);
+ int DLL Save(char *szFilename);
+
+ private:
+ void Output( char * pcData, int nSize );
+ void Output( char c );
+ void Output( void );
+ unsigned char Input( void );
+
+ public:
+ int m_Xres, m_Yres;
+ unsigned short m_BitPerPixel;
+ unsigned short m_BytePerPixel;
+ unsigned char * m_pBitmap;
+ _BGR m_Pal[256];
+
+ private:
+ int m_NumPixel;
+ FILE * f;
+ int m_nCount;
+ char m_cBuf[32768];
+};
+
+#pragma pack()
+
+}
\ No newline at end of file
diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp
new file mode 100644
index 000000000..f3dfbd569
--- /dev/null
+++ b/src/gl/models/gl_models.cpp
@@ -0,0 +1,1131 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_models.cpp
+**
+** General model handling code
+**
+**/
+
+#include "gl/system/gl_system.h"
+#include "w_wad.h"
+#include "cmdlib.h"
+#include "sc_man.h"
+#include "m_crc32.h"
+#include "c_console.h"
+#include "g_game.h"
+#include "doomstat.h"
+#include "g_level.h"
+#include "r_state.h"
+#include "d_player.h"
+//#include "resources/voxels.h"
+//#include "gl/gl_intern.h"
+
+#include "gl/system/gl_interface.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/models/gl_models.h"
+#include "gl/textures/gl_material.h"
+#include "gl/utility/gl_geometric.h"
+#include "gl/utility/gl_convert.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/shaders/gl_shader.h"
+
+static inline float GetTimeFloat()
+{
+ return (float)I_MSTime() * (float)TICRATE / 1000.0f;
+}
+
+CVAR(Bool, gl_interpolate_model_frames, true, CVAR_ARCHIVE)
+CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE)
+EXTERN_CVAR(Int, gl_fogmode)
+
+extern TDeletingArray Voxels;
+extern TDeletingArray VoxelDefs;
+
+DeletingModelArray Models;
+
+
+void gl_LoadModels()
+{
+ /*
+ for (int i = Models.Size() - 1; i >= 0; i--)
+ {
+ Models[i]->BuildVertexBuffer();
+ }
+ */
+}
+
+void gl_FlushModels()
+{
+ for (int i = Models.Size() - 1; i >= 0; i--)
+ {
+ Models[i]->DestroyVertexBuffer();
+ }
+}
+
+//===========================================================================
+//
+// Uses a hardware buffer if either single frame (i.e. no interpolation needed)
+// or shading is available (interpolation is done by the vertex shader)
+//
+// If interpolation has to be done on the CPU side this will fall back
+// to CPU-side arrays.
+//
+//===========================================================================
+
+FModelVertexBuffer::FModelVertexBuffer(bool needindex, bool singleframe)
+ : FVertexBuffer(singleframe || !gl.legacyMode)
+{
+ vbo_ptr = nullptr;
+ ibo_id = 0;
+ if (needindex)
+ {
+ glGenBuffers(1, &ibo_id); // The index buffer can always be a real buffer.
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FModelVertexBuffer::BindVBO()
+{
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ if (!gl.legacyMode)
+ {
+ glEnableVertexAttribArray(VATTR_VERTEX);
+ glEnableVertexAttribArray(VATTR_TEXCOORD);
+ glEnableVertexAttribArray(VATTR_VERTEX2);
+ glEnableVertexAttribArray(VATTR_NORMAL);
+ glDisableVertexAttribArray(VATTR_COLOR);
+ }
+ else
+ {
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+FModelVertexBuffer::~FModelVertexBuffer()
+{
+ if (ibo_id != 0)
+ {
+ glDeleteBuffers(1, &ibo_id);
+ }
+ if (vbo_ptr != nullptr)
+ {
+ delete[] vbo_ptr;
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+FModelVertex *FModelVertexBuffer::LockVertexBuffer(unsigned int size)
+{
+ if (vbo_id > 0)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ glBufferData(GL_ARRAY_BUFFER, size * sizeof(FModelVertex), nullptr, GL_STATIC_DRAW);
+ if (!gl.legacyMode)
+ return (FModelVertex*)glMapBufferRange(GL_ARRAY_BUFFER, 0, size * sizeof(FModelVertex), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+ else
+ return (FModelVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+ }
+ else
+ {
+ if (vbo_ptr != nullptr) delete[] vbo_ptr;
+ vbo_ptr = new FModelVertex[size];
+ memset(vbo_ptr, 0, size * sizeof(FModelVertex));
+ return vbo_ptr;
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FModelVertexBuffer::UnlockVertexBuffer()
+{
+ if (vbo_id > 0)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+unsigned int *FModelVertexBuffer::LockIndexBuffer(unsigned int size)
+{
+ if (ibo_id != 0)
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, size * sizeof(unsigned int), NULL, GL_STATIC_DRAW);
+ if (!gl.legacyMode)
+ return (unsigned int*)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, size * sizeof(unsigned int), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+ else
+ return (unsigned int*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FModelVertexBuffer::UnlockIndexBuffer()
+{
+ if (ibo_id > 0)
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id);
+ glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+ }
+}
+
+
+//===========================================================================
+//
+// Sets up the buffer starts for frame interpolation
+// This must be called after gl_RenderState.Apply!
+//
+//===========================================================================
+static TArray iBuffer;
+
+unsigned int FModelVertexBuffer::SetupFrame(unsigned int frame1, unsigned int frame2, unsigned int size)
+{
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
+ if (vbo_id > 0)
+ {
+ if (!gl.legacyMode)
+ {
+ glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FModelVertex), &VMO[frame1].x);
+ glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FModelVertex), &VMO[frame1].u);
+ glVertexAttribPointer(VATTR_VERTEX2, 3, GL_FLOAT, false, sizeof(FModelVertex), &VMO[frame2].x);
+ glVertexAttribPointer(VATTR_NORMAL, 4, GL_UNSIGNED_INT_2_10_10_10_REV, false, sizeof(FModelVertex), &VMO[frame2].packedNormal);
+ }
+ else
+ {
+ // only used for single frame models so there is no vertex2 here, which has no use without a shader.
+ glVertexPointer(3, GL_FLOAT, sizeof(FModelVertex), &VMO[frame1].x);
+ glTexCoordPointer(2, GL_FLOAT, sizeof(FModelVertex), &VMO[frame1].u);
+ }
+ }
+ else if (frame1 == frame2 || size == 0 || gl_RenderState.GetInterpolationFactor() == 0.f)
+ {
+ glVertexPointer(3, GL_FLOAT, sizeof(FModelVertex), &vbo_ptr[frame1].x);
+ glTexCoordPointer(2, GL_FLOAT, sizeof(FModelVertex), &vbo_ptr[frame1].u);
+ }
+ else
+ {
+ // must interpolate
+ iBuffer.Resize(size);
+ glVertexPointer(3, GL_FLOAT, sizeof(FModelVertex), &iBuffer[0].x);
+ glTexCoordPointer(2, GL_FLOAT, sizeof(FModelVertex), &vbo_ptr[frame1].u);
+ float frac = gl_RenderState.GetInterpolationFactor();
+ for (unsigned i = 0; i < size; i++)
+ {
+ iBuffer[i].x = vbo_ptr[frame1 + i].x * (1.f - frac) + vbo_ptr[frame2 + i].x * frac;
+ iBuffer[i].y = vbo_ptr[frame1 + i].y * (1.f - frac) + vbo_ptr[frame2 + i].y * frac;
+ iBuffer[i].z = vbo_ptr[frame1 + i].z * (1.f - frac) + vbo_ptr[frame2 + i].z * frac;
+ }
+ }
+ return frame1;
+}
+
+//===========================================================================
+//
+// FModel::~FModel
+//
+//===========================================================================
+
+FModel::~FModel()
+{
+ if (mVBuf != nullptr) delete mVBuf;
+}
+
+
+
+
+static TArray SpriteModelFrames;
+static int * SpriteModelHash;
+//TArray StateModelFrames;
+
+static void DeleteModelHash()
+{
+ if (SpriteModelHash != nullptr) delete [] SpriteModelHash;
+ SpriteModelHash = nullptr;
+}
+
+//===========================================================================
+//
+// FindGFXFile
+//
+//===========================================================================
+
+static int FindGFXFile(FString & fn)
+{
+ int lump = Wads.CheckNumForFullName(fn); // if we find something that matches the name plus the extension, return it and do not enter the substitution logic below.
+ if (lump != -1) return lump;
+
+ int best = -1;
+ int dot = fn.LastIndexOf('.');
+ int slash = fn.LastIndexOf('/');
+ if (dot > slash) fn.Truncate(dot);
+
+ static const char * extensions[] = { ".png", ".jpg", ".tga", ".pcx", nullptr };
+
+ for (const char ** extp=extensions; *extp; extp++)
+ {
+ int lump = Wads.CheckNumForFullName(fn + *extp);
+ if (lump >= best) best = lump;
+ }
+ return best;
+}
+
+
+//===========================================================================
+//
+// LoadSkin
+//
+//===========================================================================
+
+FTextureID LoadSkin(const char * path, const char * fn)
+{
+ FString buffer;
+
+ buffer.Format("%s%s", path, fn);
+
+ int texlump = FindGFXFile(buffer);
+ if (texlump>=0)
+ {
+ return TexMan.CheckForTexture(Wads.GetLumpFullName(texlump), FTexture::TEX_Any, FTextureManager::TEXMAN_TryAny);
+ }
+ else
+ {
+ return FNullTextureID();
+ }
+}
+
+//===========================================================================
+//
+// ModelFrameHash
+//
+//===========================================================================
+
+static int ModelFrameHash(FSpriteModelFrame * smf)
+{
+ const uint32_t *table = GetCRCTable ();
+ uint32_t hash = 0xffffffff;
+
+ const char * s = (const char *)(&smf->type); // this uses type, sprite and frame for hashing
+ const char * se= (const char *)(&smf->hashnext);
+
+ for (; smFileName.CompareNoCase(fullname)) return i;
+ }
+
+ int len = Wads.LumpLength(lump);
+ FMemLump lumpd = Wads.ReadLump(lump);
+ char * buffer = (char*)lumpd.GetMem();
+
+ if (!memcmp(buffer, "DMDM", 4))
+ {
+ model = new FDMDModel;
+ }
+ else if (!memcmp(buffer, "IDP2", 4))
+ {
+ model = new FMD2Model;
+ }
+ else if (!memcmp(buffer, "IDP3", 4))
+ {
+ model = new FMD3Model;
+ }
+
+ if (model != nullptr)
+ {
+ if (!model->Load(path, lump, buffer, len))
+ {
+ delete model;
+ return -1;
+ }
+ }
+ else
+ {
+ // try loading as a voxel
+ FVoxel *voxel = R_LoadKVX(lump);
+ if (voxel != nullptr)
+ {
+ model = new FVoxelModel(voxel, true);
+ }
+ else
+ {
+ Printf("LoadModel: Unknown model format in '%s'\n", fullname.GetChars());
+ return -1;
+ }
+ }
+ // The vertex buffer cannot be initialized here because this gets called before OpenGL is initialized
+ model->mFileName = fullname;
+ return Models.Push(model);
+}
+
+//===========================================================================
+//
+// gl_InitModels
+//
+//===========================================================================
+
+void gl_InitModels()
+{
+ int Lump, lastLump;
+ FString path;
+ int index, surface;
+ int i;
+
+ FSpriteModelFrame smf;
+
+ lastLump = 0;
+
+ for(unsigned i=0;iVoxelIndex = Models.Push(md);
+ }
+ // now create GL model frames for the voxeldefs
+ for (unsigned i = 0; i < VoxelDefs.Size(); i++)
+ {
+ FVoxelModel *md = (FVoxelModel*)Models[VoxelDefs[i]->Voxel->VoxelIndex];
+ memset(&smf, 0, sizeof(smf));
+ smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1;
+ smf.modelIDs[0] = VoxelDefs[i]->Voxel->VoxelIndex;
+ smf.skinIDs[0] = md->GetPaletteTexture();
+ smf.xscale = smf.yscale = smf.zscale = VoxelDefs[i]->Scale;
+ smf.angleoffset = VoxelDefs[i]->AngleOffset.Degrees;
+ if (VoxelDefs[i]->PlacedSpin != 0)
+ {
+ smf.yrotate = 1.f;
+ smf.rotationSpeed = VoxelDefs[i]->PlacedSpin / 55.55f;
+ smf.flags |= MDL_ROTATING;
+ }
+ VoxelDefs[i]->VoxeldefIndex = SpriteModelFrames.Push(smf);
+ if (VoxelDefs[i]->PlacedSpin != VoxelDefs[i]->DroppedSpin)
+ {
+ if (VoxelDefs[i]->DroppedSpin != 0)
+ {
+ smf.yrotate = 1.f;
+ smf.rotationSpeed = VoxelDefs[i]->DroppedSpin / 55.55f;
+ smf.flags |= MDL_ROTATING;
+ }
+ else
+ {
+ smf.yrotate = 0;
+ smf.rotationSpeed = 0;
+ smf.flags &= ~MDL_ROTATING;
+ }
+ SpriteModelFrames.Push(smf);
+ }
+ }
+
+ memset(&smf, 0, sizeof(smf));
+ smf.modelIDs[0] = smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1;
+ while ((Lump = Wads.FindLump("MODELDEF", &lastLump)) != -1)
+ {
+ FScanner sc(Lump);
+ while (sc.GetString())
+ {
+ if (sc.Compare("model"))
+ {
+ path = "";
+ sc.MustGetString();
+ memset(&smf, 0, sizeof(smf));
+ smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1;
+ smf.xscale=smf.yscale=smf.zscale=1.f;
+
+ smf.type = PClass::FindClass(sc.String);
+ if (!smf.type || smf.type->Defaults == nullptr)
+ {
+ sc.ScriptError("MODELDEF: Unknown actor type '%s'\n", sc.String);
+ }
+ sc.MustGetStringName("{");
+ while (!sc.CheckString("}"))
+ {
+ sc.MustGetString();
+ if (sc.Compare("path"))
+ {
+ sc.MustGetString();
+ FixPathSeperator(sc.String);
+ path = sc.String;
+ if (path[(int)path.Len()-1]!='/') path+='/';
+ }
+ else if (sc.Compare("model"))
+ {
+ sc.MustGetNumber();
+ index = sc.Number;
+ if (index < 0 || index >= MAX_MODELS_PER_FRAME)
+ {
+ sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
+ }
+ sc.MustGetString();
+ FixPathSeperator(sc.String);
+ smf.modelIDs[index] = FindModel(path.GetChars(), sc.String);
+ if (smf.modelIDs[index] == -1)
+ {
+ Printf("%s: model not found in %s\n", sc.String, path.GetChars());
+ }
+ }
+ else if (sc.Compare("scale"))
+ {
+ sc.MustGetFloat();
+ smf.xscale = sc.Float;
+ sc.MustGetFloat();
+ smf.yscale = sc.Float;
+ sc.MustGetFloat();
+ smf.zscale = sc.Float;
+ }
+ // [BB] Added zoffset reading.
+ // Now it must be considered deprecated.
+ else if (sc.Compare("zoffset"))
+ {
+ sc.MustGetFloat();
+ smf.zoffset=sc.Float;
+ }
+ // Offset reading.
+ else if (sc.Compare("offset"))
+ {
+ sc.MustGetFloat();
+ smf.xoffset = sc.Float;
+ sc.MustGetFloat();
+ smf.yoffset = sc.Float;
+ sc.MustGetFloat();
+ smf.zoffset = sc.Float;
+ }
+ // angleoffset, pitchoffset and rolloffset reading.
+ else if (sc.Compare("angleoffset"))
+ {
+ sc.MustGetFloat();
+ smf.angleoffset = sc.Float;
+ }
+ else if (sc.Compare("pitchoffset"))
+ {
+ sc.MustGetFloat();
+ smf.pitchoffset = sc.Float;
+ }
+ else if (sc.Compare("rolloffset"))
+ {
+ sc.MustGetFloat();
+ smf.rolloffset = sc.Float;
+ }
+ // [BB] Added model flags reading.
+ else if (sc.Compare("ignoretranslation"))
+ {
+ smf.flags |= MDL_IGNORETRANSLATION;
+ }
+ else if (sc.Compare("pitchfrommomentum"))
+ {
+ smf.flags |= MDL_PITCHFROMMOMENTUM;
+ }
+ else if (sc.Compare("inheritactorpitch"))
+ {
+ smf.flags |= MDL_USEACTORPITCH | MDL_BADROTATION;
+ }
+ else if (sc.Compare("inheritactorroll"))
+ {
+ smf.flags |= MDL_USEACTORROLL;
+ }
+ else if (sc.Compare("useactorpitch"))
+ {
+ smf.flags |= MDL_USEACTORPITCH;
+ }
+ else if (sc.Compare("useactorroll"))
+ {
+ smf.flags |= MDL_USEACTORROLL;
+ }
+ else if (sc.Compare("rotating"))
+ {
+ smf.flags |= MDL_ROTATING;
+ smf.xrotate = 0.;
+ smf.yrotate = 1.;
+ smf.zrotate = 0.;
+ smf.rotationCenterX = 0.;
+ smf.rotationCenterY = 0.;
+ smf.rotationCenterZ = 0.;
+ smf.rotationSpeed = 1.;
+ }
+ else if (sc.Compare("rotation-speed"))
+ {
+ sc.MustGetFloat();
+ smf.rotationSpeed = sc.Float;
+ }
+ else if (sc.Compare("rotation-vector"))
+ {
+ sc.MustGetFloat();
+ smf.xrotate = sc.Float;
+ sc.MustGetFloat();
+ smf.yrotate = sc.Float;
+ sc.MustGetFloat();
+ smf.zrotate = sc.Float;
+ }
+ else if (sc.Compare("rotation-center"))
+ {
+ sc.MustGetFloat();
+ smf.rotationCenterX = sc.Float;
+ sc.MustGetFloat();
+ smf.rotationCenterY = sc.Float;
+ sc.MustGetFloat();
+ smf.rotationCenterZ = sc.Float;
+ }
+ else if (sc.Compare("interpolatedoubledframes"))
+ {
+ smf.flags |= MDL_INTERPOLATEDOUBLEDFRAMES;
+ }
+ else if (sc.Compare("nointerpolation"))
+ {
+ smf.flags |= MDL_NOINTERPOLATION;
+ }
+ else if (sc.Compare("skin"))
+ {
+ sc.MustGetNumber();
+ index=sc.Number;
+ if (index<0 || index>=MAX_MODELS_PER_FRAME)
+ {
+ sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
+ }
+ sc.MustGetString();
+ FixPathSeperator(sc.String);
+ if (sc.Compare(""))
+ {
+ smf.skinIDs[index]=FNullTextureID();
+ }
+ else
+ {
+ smf.skinIDs[index] = LoadSkin(path.GetChars(), sc.String);
+ if (!smf.skinIDs[index].isValid())
+ {
+ Printf("Skin '%s' not found in '%s'\n",
+ sc.String, smf.type->TypeName.GetChars());
+ }
+ }
+ }
+ else if (sc.Compare("surfaceskin"))
+ {
+ sc.MustGetNumber();
+ index = sc.Number;
+ sc.MustGetNumber();
+ surface = sc.Number;
+
+ if (index<0 || index >= MAX_MODELS_PER_FRAME)
+ {
+ sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
+ }
+
+ if (surface<0 || surface >= MD3_MAX_SURFACES)
+ {
+ sc.ScriptError("Invalid MD3 Surface %d in %s", MD3_MAX_SURFACES, smf.type->TypeName.GetChars());
+ }
+
+ sc.MustGetString();
+ FixPathSeperator(sc.String);
+ if (sc.Compare(""))
+ {
+ smf.surfaceskinIDs[index][surface] = FNullTextureID();
+ }
+ else
+ {
+ smf.surfaceskinIDs[index][surface] = LoadSkin(path.GetChars(), sc.String);
+ if (!smf.surfaceskinIDs[index][surface].isValid())
+ {
+ Printf("Surface Skin '%s' not found in '%s'\n",
+ sc.String, smf.type->TypeName.GetChars());
+ }
+ }
+ }
+ else if (sc.Compare("frameindex") || sc.Compare("frame"))
+ {
+ bool isframe=!!sc.Compare("frame");
+
+ sc.MustGetString();
+ smf.sprite = -1;
+ for (i = 0; i < (int)sprites.Size (); ++i)
+ {
+ if (strnicmp (sprites[i].name, sc.String, 4) == 0)
+ {
+ if (sprites[i].numframes==0)
+ {
+ //sc.ScriptError("Sprite %s has no frames", sc.String);
+ }
+ smf.sprite = i;
+ break;
+ }
+ }
+ if (smf.sprite==-1)
+ {
+ sc.ScriptError("Unknown sprite %s in model definition for %s", sc.String, smf.type->TypeName.GetChars());
+ }
+
+ sc.MustGetString();
+ FString framechars = sc.String;
+
+ sc.MustGetNumber();
+ index=sc.Number;
+ if (index<0 || index>=MAX_MODELS_PER_FRAME)
+ {
+ sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
+ }
+ if (isframe)
+ {
+ sc.MustGetString();
+ if (smf.modelIDs[index] != -1)
+ {
+ FModel *model = Models[smf.modelIDs[index]];
+ smf.modelframes[index] = model->FindFrame(sc.String);
+ if (smf.modelframes[index]==-1) sc.ScriptError("Unknown frame '%s' in %s", sc.String, smf.type->TypeName.GetChars());
+ }
+ else smf.modelframes[index] = -1;
+ }
+ else
+ {
+ sc.MustGetNumber();
+ smf.modelframes[index] = sc.Number;
+ }
+
+ for(i=0; framechars[i]>0; i++)
+ {
+ char map[29]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ int c = toupper(framechars[i])-'A';
+
+ if (c<0 || c>=29)
+ {
+ sc.ScriptError("Invalid frame character %c found", c+'A');
+ }
+ if (map[c]) continue;
+ smf.frame=c;
+ SpriteModelFrames.Push(smf);
+ GetDefaultByType(smf.type)->hasmodel = true;
+ map[c]=1;
+ }
+ }
+ else
+ {
+ sc.ScriptMessage("Unrecognized string \"%s\"", sc.String);
+ }
+ }
+ }
+ }
+ }
+
+ // create a hash table for quick access
+ SpriteModelHash = new int[SpriteModelFrames.Size ()];
+ atterm(DeleteModelHash);
+ memset(SpriteModelHash, 0xff, SpriteModelFrames.Size () * sizeof(int));
+
+ for (i = 0; i < (int)SpriteModelFrames.Size (); i++)
+ {
+ int j = ModelFrameHash(&SpriteModelFrames[i]) % SpriteModelFrames.Size ();
+
+ SpriteModelFrames[i].hashnext = SpriteModelHash[j];
+ SpriteModelHash[j]=i;
+ }
+}
+
+
+//===========================================================================
+//
+// gl_FindModelFrame
+//
+//===========================================================================
+EXTERN_CVAR (Bool, r_drawvoxels)
+
+FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped)
+{
+ if (GetDefaultByType(ti)->hasmodel)
+ {
+ FSpriteModelFrame smf;
+
+ memset(&smf, 0, sizeof(smf));
+ smf.type=ti;
+ smf.sprite=sprite;
+ smf.frame=frame;
+
+ int hash = SpriteModelHash[ModelFrameHash(&smf) % SpriteModelFrames.Size()];
+
+ while (hash>=0)
+ {
+ FSpriteModelFrame * smff = &SpriteModelFrames[hash];
+ if (smff->type==ti && smff->sprite==sprite && smff->frame==frame) return smff;
+ hash=smff->hashnext;
+ }
+ }
+
+ // Check for voxel replacements
+ if (r_drawvoxels)
+ {
+ spritedef_t *sprdef = &sprites[sprite];
+ if (frame < sprdef->numframes)
+ {
+ spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + frame];
+ if (sprframe->Voxel != nullptr)
+ {
+ int index = sprframe->Voxel->VoxeldefIndex;
+ if (dropped && sprframe->Voxel->DroppedSpin !=sprframe->Voxel->PlacedSpin) index++;
+ return &SpriteModelFrames[index];
+ }
+ }
+ }
+ return nullptr;
+}
+
+
+//===========================================================================
+//
+// gl_RenderModel
+//
+//===========================================================================
+
+void gl_RenderFrameModels( const FSpriteModelFrame *smf,
+ const FState *curState,
+ const int curTics,
+ const PClass *ti,
+ Matrix3x4 *normaltransform,
+ int translation)
+{
+ // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation
+ // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame.
+ FSpriteModelFrame * smfNext = nullptr;
+ double inter = 0.;
+ if( gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION) )
+ {
+ FState *nextState = curState->GetNextState( );
+ if( curState != nextState && nextState )
+ {
+ // [BB] To interpolate at more than 35 fps we take tic fractions into account.
+ float ticFraction = 0.;
+ // [BB] In case the tic counter is frozen we have to leave ticFraction at zero.
+ if ( ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN) )
+ {
+ float time = GetTimeFloat();
+ ticFraction = (time - static_cast(time));
+ }
+ inter = static_cast(curState->Tics - curTics - ticFraction)/static_cast(curState->Tics);
+
+ // [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics.
+ // In this case inter is negative and we need to set it to zero.
+ if ( inter < 0. )
+ inter = 0.;
+ else
+ {
+ // [BB] Workaround for actors that use the same frame twice in a row.
+ // Most of the standard Doom monsters do this in their see state.
+ if ( (smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES) )
+ {
+ const FState *prevState = curState - 1;
+ if ( (curState->sprite == prevState->sprite) && ( curState->Frame == prevState->Frame) )
+ {
+ inter /= 2.;
+ inter += 0.5;
+ }
+ if ( (curState->sprite == nextState->sprite) && ( curState->Frame == nextState->Frame) )
+ {
+ inter /= 2.;
+ nextState = nextState->GetNextState( );
+ }
+ }
+ if ( inter != 0.0 )
+ smfNext = gl_FindModelFrame(ti, nextState->sprite, nextState->Frame, false);
+ }
+ }
+ }
+
+ for(int i=0; imodelIDs[i] != -1)
+ {
+ FModel * mdl = Models[smf->modelIDs[i]];
+ FTexture *tex = smf->skinIDs[i].isValid()? TexMan(smf->skinIDs[i]) : nullptr;
+ mdl->BuildVertexBuffer();
+ gl_RenderState.SetVertexBuffer(mdl->mVBuf);
+
+ mdl->PushSpriteMDLFrame(smf, i);
+
+ if ( smfNext && smf->modelframes[i] != smfNext->modelframes[i] )
+ mdl->RenderFrame(tex, smf->modelframes[i], smfNext->modelframes[i], inter, translation);
+ else
+ mdl->RenderFrame(tex, smf->modelframes[i], smf->modelframes[i], 0.f, translation);
+
+ gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
+ }
+ }
+}
+
+void gl_RenderModel(GLSprite * spr)
+{
+ FSpriteModelFrame * smf = spr->modelframe;
+
+
+ // Setup transformation.
+ glDepthFunc(GL_LEQUAL);
+ gl_RenderState.EnableTexture(true);
+ // [BB] In case the model should be rendered translucent, do back face culling.
+ // This solves a few of the problems caused by the lack of depth sorting.
+ // TO-DO: Implement proper depth sorting.
+ if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] ))
+ {
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CW);
+ }
+
+ int translation = 0;
+ if ( !(smf->flags & MDL_IGNORETRANSLATION) )
+ translation = spr->actor->Translation;
+
+
+ // y scale for a sprite means height, i.e. z in the world!
+ float scaleFactorX = spr->actor->Scale.X * smf->xscale;
+ float scaleFactorY = spr->actor->Scale.X * smf->yscale;
+ float scaleFactorZ = spr->actor->Scale.Y * smf->zscale;
+ float pitch = 0;
+ float roll = 0;
+ float rotateOffset = 0;
+ float angle = spr->actor->Angles.Yaw.Degrees;
+
+ // [BB] Workaround for the missing pitch information.
+ if ( (smf->flags & MDL_PITCHFROMMOMENTUM) )
+ {
+ const double x = spr->actor->Vel.X;
+ const double y = spr->actor->Vel.Y;
+ const double z = spr->actor->Vel.Z;
+
+ if (spr->actor->Vel.LengthSquared() > EQUAL_EPSILON)
+ {
+ // [BB] Calculate the pitch using spherical coordinates.
+ if (z || x || y) pitch = float(atan(z / sqrt(x*x + y*y)) / M_PI * 180);
+
+ // Correcting pitch if model is moving backwards
+ if (fabs(x) > EQUAL_EPSILON || fabs(y) > EQUAL_EPSILON)
+ {
+ if ((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1;
+ }
+ else pitch = fabs(pitch);
+ }
+ }
+
+ if( smf->flags & MDL_ROTATING )
+ {
+ const float time = smf->rotationSpeed*GetTimeFloat()/200.f;
+ rotateOffset = float((time - xs_FloorToInt(time)) *360.f );
+ }
+
+ // Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing.
+ // If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the momentum vector pitch.
+ if (smf->flags & MDL_USEACTORPITCH)
+ {
+ double d = spr->actor->Angles.Pitch.Degrees;
+ if (smf->flags & MDL_BADROTATION) pitch -= d;
+ else pitch += d;
+ }
+ if(smf->flags & MDL_USEACTORROLL) roll += spr->actor->Angles.Roll.Degrees;
+
+ gl_RenderState.mModelMatrix.loadIdentity();
+
+ // Model space => World space
+ gl_RenderState.mModelMatrix.translate(spr->x, spr->z, spr->y );
+
+ // Applying model transformations:
+ // 1) Applying actor angle, pitch and roll to the model
+ gl_RenderState.mModelMatrix.rotate(-angle, 0, 1, 0);
+ gl_RenderState.mModelMatrix.rotate(pitch, 0, 0, 1);
+ gl_RenderState.mModelMatrix.rotate(-roll, 1, 0, 0);
+
+ // 2) Applying Doomsday like rotation of the weapon pickup models
+ // The rotation angle is based on the elapsed time.
+
+ if( smf->flags & MDL_ROTATING )
+ {
+ gl_RenderState.mModelMatrix.translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ);
+ gl_RenderState.mModelMatrix.rotate(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate);
+ gl_RenderState.mModelMatrix.translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ);
+ }
+
+ // 3) Scaling model.
+ gl_RenderState.mModelMatrix.scale(scaleFactorX, scaleFactorZ, scaleFactorY);
+
+ // 4) Aplying model offsets (model offsets do not depend on model scalings).
+ gl_RenderState.mModelMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale);
+
+ // 5) Applying model rotations.
+ gl_RenderState.mModelMatrix.rotate(-smf->angleoffset, 0, 1, 0);
+ gl_RenderState.mModelMatrix.rotate(smf->pitchoffset, 0, 0, 1);
+ gl_RenderState.mModelMatrix.rotate(-smf->rolloffset, 1, 0, 0);
+
+ // consider the pixel stretching. For non-voxels this must be factored out here
+ float stretch = (smf->modelIDs[0] != -1 ? Models[smf->modelIDs[0]]->getAspectFactor() : 1.f) / glset.pixelstretch;
+ gl_RenderState.mModelMatrix.scale(1, stretch, 1);
+
+
+ gl_RenderState.EnableModelMatrix(true);
+ gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, spr->actor->GetClass(), nullptr, translation );
+ gl_RenderState.EnableModelMatrix(false);
+
+ glDepthFunc(GL_LESS);
+ if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] ))
+ glDisable(GL_CULL_FACE);
+}
+
+
+//===========================================================================
+//
+// gl_RenderHUDModel
+//
+//===========================================================================
+
+void gl_RenderHUDModel(DPSprite *psp, float ofsX, float ofsY)
+{
+ AActor * playermo=players[consoleplayer].camera;
+ FSpriteModelFrame *smf = gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false);
+
+ // [BB] No model found for this sprite, so we can't render anything.
+ if ( smf == nullptr )
+ return;
+
+ glDepthFunc(GL_LEQUAL);
+
+ // [BB] In case the model should be rendered translucent, do back face culling.
+ // This solves a few of the problems caused by the lack of depth sorting.
+ // TO-DO: Implement proper depth sorting.
+ if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] ))
+ {
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CCW);
+ }
+
+ // [BB] The model has to be drawn independently from the position of the player,
+ // so we have to reset the view matrix.
+ gl_RenderState.mViewMatrix.loadIdentity();
+
+ // Scaling model (y scale for a sprite means height, i.e. z in the world!).
+ gl_RenderState.mViewMatrix.scale(smf->xscale, smf->zscale, smf->yscale);
+
+ // Aplying model offsets (model offsets do not depend on model scalings).
+ gl_RenderState.mViewMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale);
+
+ // [BB] Weapon bob, very similar to the normal Doom weapon bob.
+ gl_RenderState.mViewMatrix.rotate(ofsX/4, 0, 1, 0);
+ gl_RenderState.mViewMatrix.rotate((ofsY-WEAPONTOP)/-4., 1, 0, 0);
+
+ // [BB] For some reason the jDoom models need to be rotated.
+ gl_RenderState.mViewMatrix.rotate(90.f, 0, 1, 0);
+
+ // Applying angleoffset, pitchoffset, rolloffset.
+ gl_RenderState.mViewMatrix.rotate(-smf->angleoffset, 0, 1, 0);
+ gl_RenderState.mViewMatrix.rotate(smf->pitchoffset, 0, 0, 1);
+ gl_RenderState.mViewMatrix.rotate(-smf->rolloffset, 1, 0, 0);
+ gl_RenderState.ApplyMatrices();
+
+ gl_RenderFrameModels( smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0 );
+
+ glDepthFunc(GL_LESS);
+ if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] ))
+ glDisable(GL_CULL_FACE);
+}
+
+//===========================================================================
+//
+// gl_IsHUDModelForPlayerAvailable
+//
+//===========================================================================
+
+bool gl_IsHUDModelForPlayerAvailable (player_t * player)
+{
+ if (player == nullptr || player->ReadyWeapon == nullptr)
+ return false;
+
+ DPSprite *psp = player->FindPSprite(PSP_WEAPON);
+
+ if (psp == nullptr || psp->GetState() == nullptr)
+ return false;
+
+ FState* state = psp->GetState();
+ FSpriteModelFrame *smf = gl_FindModelFrame(player->ReadyWeapon->GetClass(), state->sprite, state->GetFrame(), false);
+ return ( smf != nullptr );
+}
+
diff --git a/src/gl/models/gl_models.h b/src/gl/models/gl_models.h
new file mode 100644
index 000000000..f0267b891
--- /dev/null
+++ b/src/gl/models/gl_models.h
@@ -0,0 +1,425 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+
+#ifndef __GL_MODELS_H_
+#define __GL_MODELS_H_
+
+#include "tarray.h"
+#include "gl/utility/gl_geometric.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "p_pspr.h"
+#include "r_data/voxels.h"
+
+
+#define MAX_LODS 4
+
+enum { VX, VZ, VY };
+
+#define MD2_MAGIC 0x32504449
+#define DMD_MAGIC 0x4D444D44
+#define MD3_MAGIC 0x33504449
+#define NUMVERTEXNORMALS 162
+#define MD3_MAX_SURFACES 32
+
+FTextureID LoadSkin(const char * path, const char * fn);
+
+// [JM] Necessary forward declaration
+typedef struct FSpriteModelFrame FSpriteModelFrame;
+
+class FModel
+{
+public:
+
+ FModel()
+ {
+ mVBuf = NULL;
+ }
+ virtual ~FModel();
+
+ virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0;
+ virtual int FindFrame(const char * name) = 0;
+ virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0) = 0;
+ virtual void BuildVertexBuffer() = 0;
+ virtual void AddSkins(BYTE *hitlist) = 0;
+ void DestroyVertexBuffer()
+ {
+ delete mVBuf;
+ mVBuf = NULL;
+ }
+ virtual float getAspectFactor() { return 1.f; }
+
+ const FSpriteModelFrame *curSpriteMDLFrame;
+ int curMDLIndex;
+ void PushSpriteMDLFrame(const FSpriteModelFrame *smf, int index) { curSpriteMDLFrame = smf; curMDLIndex = index; };
+
+ FModelVertexBuffer *mVBuf;
+ FString mFileName;
+};
+
+class FDMDModel : public FModel
+{
+protected:
+
+ struct FTriangle
+ {
+ unsigned short vertexIndices[3];
+ unsigned short textureIndices[3];
+ };
+
+
+ struct DMDHeader
+ {
+ int magic;
+ int version;
+ int flags;
+ };
+
+ struct DMDModelVertex
+ {
+ float xyz[3];
+ };
+
+ struct FTexCoord
+ {
+ short s, t;
+ };
+
+ struct FGLCommandVertex
+ {
+ float s, t;
+ int index;
+ };
+
+ struct DMDInfo
+ {
+ int skinWidth;
+ int skinHeight;
+ int frameSize;
+ int numSkins;
+ int numVertices;
+ int numTexCoords;
+ int numFrames;
+ int numLODs;
+ int offsetSkins;
+ int offsetTexCoords;
+ int offsetFrames;
+ int offsetLODs;
+ int offsetEnd;
+ };
+
+ struct ModelFrame
+ {
+ char name[16];
+ unsigned int vindex;
+ };
+
+ struct ModelFrameVertexData
+ {
+ DMDModelVertex *vertices;
+ DMDModelVertex *normals;
+ };
+
+ struct DMDLoDInfo
+ {
+ int numTriangles;
+ int numGlCommands;
+ int offsetTriangles;
+ int offsetGlCommands;
+ };
+
+ struct DMDLoD
+ {
+ FTriangle * triangles;
+ };
+
+
+ int mLumpNum;
+ DMDHeader header;
+ DMDInfo info;
+ FTextureID * skins;
+ ModelFrame * frames;
+ bool allowTexComp; // Allow texture compression with this.
+
+ // Temp data only needed for buffer construction
+ FTexCoord * texCoords;
+ ModelFrameVertexData *framevtx;
+ DMDLoDInfo lodInfo[MAX_LODS];
+ DMDLoD lods[MAX_LODS];
+
+public:
+ FDMDModel()
+ {
+ mLumpNum = -1;
+ frames = NULL;
+ skins = NULL;
+ for (int i = 0; i < MAX_LODS; i++)
+ {
+ lods[i].triangles = NULL;
+ }
+ info.numLODs = 0;
+ texCoords = NULL;
+ framevtx = NULL;
+ }
+ virtual ~FDMDModel();
+
+ virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length);
+ virtual int FindFrame(const char * name);
+ virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0);
+ virtual void LoadGeometry();
+ virtual void AddSkins(BYTE *hitlist);
+
+ void UnloadGeometry();
+ void BuildVertexBuffer();
+
+};
+
+// This uses the same internal representation as DMD
+class FMD2Model : public FDMDModel
+{
+public:
+ FMD2Model() {}
+ virtual ~FMD2Model();
+
+ virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length);
+ virtual void LoadGeometry();
+
+};
+
+
+class FMD3Model : public FModel
+{
+ struct MD3Tag
+ {
+ // Currently I have no use for this
+ };
+
+ struct MD3TexCoord
+ {
+ float s,t;
+ };
+
+ struct MD3Vertex
+ {
+ float x,y,z;
+ float nx,ny,nz;
+ };
+
+ struct MD3Triangle
+ {
+ int VertIndex[3];
+ };
+
+ struct MD3Surface
+ {
+ int numVertices;
+ int numTriangles;
+ int numSkins;
+
+ FTextureID * skins;
+ MD3Triangle * tris;
+ MD3TexCoord * texcoords;
+ MD3Vertex * vertices;
+
+ unsigned int vindex; // contains numframes arrays of vertices
+ unsigned int iindex;
+
+ MD3Surface()
+ {
+ tris=NULL;
+ vertices=NULL;
+ texcoords=NULL;
+ vindex = iindex = UINT_MAX;
+ }
+
+ ~MD3Surface()
+ {
+ if (skins) delete [] skins;
+ UnloadGeometry();
+ }
+
+ void UnloadGeometry()
+ {
+ if (tris) delete [] tris;
+ if (vertices) delete [] vertices;
+ if (texcoords) delete [] texcoords;
+ tris = NULL;
+ vertices = NULL;
+ texcoords = NULL;
+ }
+ };
+
+ struct MD3Frame
+ {
+ // The bounding box information is of no use in the Doom engine
+ // That will still be done with the actor's size information.
+ char Name[16];
+ float origin[3];
+ };
+
+ int numFrames;
+ int numTags;
+ int numSurfaces;
+ int mLumpNum;
+
+ MD3Frame * frames;
+ MD3Surface * surfaces;
+
+public:
+ FMD3Model() { }
+ virtual ~FMD3Model();
+
+ virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length);
+ virtual int FindFrame(const char * name);
+ virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0);
+ void LoadGeometry();
+ void BuildVertexBuffer();
+ virtual void AddSkins(BYTE *hitlist);
+};
+
+struct FVoxelVertexHash
+{
+ // Returns the hash value for a key.
+ hash_t Hash(const FModelVertex &key)
+ {
+ int ix = xs_RoundToInt(key.x);
+ int iy = xs_RoundToInt(key.y);
+ int iz = xs_RoundToInt(key.z);
+ return (hash_t)(ix + (iy<<9) + (iz<<18));
+ }
+
+ // Compares two keys, returning zero if they are the same.
+ int Compare(const FModelVertex &left, const FModelVertex &right)
+ {
+ return left.x != right.x || left.y != right.y || left.z != right.z || left.u != right.u || left.v != right.v;
+ }
+};
+
+struct FIndexInit
+{
+ void Init(unsigned int &value)
+ {
+ value = 0xffffffff;
+ }
+};
+
+typedef TMap FVoxelMap;
+
+
+class FVoxelModel : public FModel
+{
+protected:
+ FVoxel *mVoxel;
+ bool mOwningVoxel; // if created through MODELDEF deleting this object must also delete the voxel object
+ FTextureID mPalette;
+ unsigned int mNumIndices;
+ TArray mVertices;
+ TArray mIndices;
+
+ void MakeSlabPolys(int x, int y, kvxslab_t *voxptr, FVoxelMap &check);
+ void AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3, int x4, int y4, int z4, BYTE color, FVoxelMap &check);
+ unsigned int AddVertex(FModelVertex &vert, FVoxelMap &check);
+
+public:
+ FVoxelModel(FVoxel *voxel, bool owned);
+ ~FVoxelModel();
+ bool Load(const char * fn, int lumpnum, const char * buffer, int length);
+ void Initialize();
+ virtual int FindFrame(const char * name);
+ virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0);
+ virtual void AddSkins(BYTE *hitlist);
+ FTextureID GetPaletteTexture() const { return mPalette; }
+ void BuildVertexBuffer();
+ float getAspectFactor();
+};
+
+
+
+#define MAX_MODELS_PER_FRAME 4
+
+//
+// [BB] Model rendering flags.
+//
+enum
+{
+ // [BB] Color translations for the model skin are ignored. This is
+ // useful if the skin texture is not using the game palette.
+ MDL_IGNORETRANSLATION = 1,
+ MDL_PITCHFROMMOMENTUM = 2,
+ MDL_ROTATING = 4,
+ MDL_INTERPOLATEDOUBLEDFRAMES = 8,
+ MDL_NOINTERPOLATION = 16,
+ MDL_USEACTORPITCH = 32,
+ MDL_USEACTORROLL = 64,
+ MDL_BADROTATION = 128,
+};
+
+struct FSpriteModelFrame
+{
+ int modelIDs[MAX_MODELS_PER_FRAME];
+ FTextureID skinIDs[MAX_MODELS_PER_FRAME];
+ FTextureID surfaceskinIDs[MAX_MODELS_PER_FRAME][MD3_MAX_SURFACES];
+ int modelframes[MAX_MODELS_PER_FRAME];
+ float xscale, yscale, zscale;
+ // [BB] Added zoffset, rotation parameters and flags.
+ // Added xoffset, yoffset
+ float xoffset, yoffset, zoffset;
+ float xrotate, yrotate, zrotate;
+ float rotationCenterX, rotationCenterY, rotationCenterZ;
+ float rotationSpeed;
+ unsigned int flags;
+ const PClass * type;
+ short sprite;
+ short frame;
+ FState * state; // for later!
+ int hashnext;
+ float angleoffset;
+ // added pithoffset, rolloffset.
+ float pitchoffset, rolloffset; // I don't want to bother with type transformations, so I made this variables float.
+};
+
+class GLSprite;
+
+FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped);
+
+void gl_RenderModel(GLSprite * spr);
+// [BB] HUD weapon model rendering functions.
+void gl_RenderHUDModel(DPSprite *psp, float ofsx, float ofsy);
+bool gl_IsHUDModelForPlayerAvailable (player_t * player);
+
+
+class DeletingModelArray : public TArray
+{
+public:
+
+ ~DeletingModelArray()
+ {
+ for (unsigned i = 0; i> 9) / 127.0f - 0.5f) * M_PI;
+ float cosp = (float) cos(pitch);
+
+ vec[VX] = (float) cos(yaw) * cosp;
+ vec[VY] = (float) sin(yaw) * cosp;
+ vec[VZ] = (float) sin(pitch);
+}
+
+
+//===========================================================================
+//
+// DMD file structure
+//
+//===========================================================================
+
+struct dmd_chunk_t
+{
+ int type;
+ int length; // Next chunk follows...
+};
+
+#pragma pack(1)
+struct dmd_packedVertex_t
+{
+ byte vertex[3];
+ unsigned short normal; // Yaw and pitch.
+};
+
+struct dmd_packedFrame_t
+{
+ float scale[3];
+ float translate[3];
+ char name[16];
+ dmd_packedVertex_t vertices[1];
+};
+#pragma pack()
+
+// Chunk types.
+enum
+{
+ DMC_END, // Must be the last chunk.
+ DMC_INFO // Required; will be expected to exist.
+};
+
+//===========================================================================
+//
+// FDMDModel::Load
+//
+//===========================================================================
+
+bool FDMDModel::Load(const char * path, int lumpnum, const char * buffer, int length)
+{
+ dmd_chunk_t * chunk = (dmd_chunk_t*)(buffer + 12);
+ char *temp;
+ ModelFrame *frame;
+ int i;
+
+ int fileoffset = 12 + sizeof(dmd_chunk_t);
+
+ chunk->type = LittleLong(chunk->type);
+ while (chunk->type != DMC_END)
+ {
+ switch (chunk->type)
+ {
+ case DMC_INFO: // Standard DMD information chunk.
+ memcpy(&info, buffer + fileoffset, LittleLong(chunk->length));
+ info.skinWidth = LittleLong(info.skinWidth);
+ info.skinHeight = LittleLong(info.skinHeight);
+ info.frameSize = LittleLong(info.frameSize);
+ info.numSkins = LittleLong(info.numSkins);
+ info.numVertices = LittleLong(info.numVertices);
+ info.numTexCoords = LittleLong(info.numTexCoords);
+ info.numFrames = LittleLong(info.numFrames);
+ info.numLODs = LittleLong(info.numLODs);
+ info.offsetSkins = LittleLong(info.offsetSkins);
+ info.offsetTexCoords = LittleLong(info.offsetTexCoords);
+ info.offsetFrames = LittleLong(info.offsetFrames);
+ info.offsetLODs = LittleLong(info.offsetLODs);
+ info.offsetEnd = LittleLong(info.offsetEnd);
+ fileoffset += chunk->length;
+ break;
+
+ default:
+ // Just skip all unknown chunks.
+ fileoffset += chunk->length;
+ break;
+ }
+ // Read the next chunk header.
+ chunk = (dmd_chunk_t*)(buffer + fileoffset);
+ chunk->type = LittleLong(chunk->type);
+ fileoffset += sizeof(dmd_chunk_t);
+ }
+
+ // Allocate and load in the data.
+ skins = new FTextureID[info.numSkins];
+
+ for (i = 0; i < info.numSkins; i++)
+ {
+ skins[i] = LoadSkin(path, buffer + info.offsetSkins + i * 64);
+ }
+ temp = (char*)buffer + info.offsetFrames;
+ frames = new ModelFrame[info.numFrames];
+
+ for (i = 0, frame = frames; i < info.numFrames; i++, frame++)
+ {
+ dmd_packedFrame_t *pfr = (dmd_packedFrame_t *)(temp + info.frameSize * i);
+
+ memcpy(frame->name, pfr->name, sizeof(pfr->name));
+ frame->vindex = UINT_MAX;
+ }
+ mLumpNum = lumpnum;
+ return true;
+}
+
+//===========================================================================
+//
+// FDMDModel::LoadGeometry
+//
+//===========================================================================
+
+void FDMDModel::LoadGeometry()
+{
+ static int axis[3] = { VX, VY, VZ };
+ FMemLump lumpdata = Wads.ReadLump(mLumpNum);
+ const char *buffer = (const char *)lumpdata.GetMem();
+ texCoords = new FTexCoord[info.numTexCoords];
+ memcpy(texCoords, buffer + info.offsetTexCoords, info.numTexCoords * sizeof(FTexCoord));
+
+ const char *temp = buffer + info.offsetFrames;
+ framevtx= new ModelFrameVertexData[info.numFrames];
+
+ ModelFrameVertexData *framev;
+ int i, k, c;
+ for(i = 0, framev = framevtx; i < info.numFrames; i++, framev++)
+ {
+ dmd_packedFrame_t *pfr = (dmd_packedFrame_t *) (temp + info.frameSize * i);
+ dmd_packedVertex_t *pVtx;
+
+ framev->vertices = new DMDModelVertex[info.numVertices];
+ framev->normals = new DMDModelVertex[info.numVertices];
+
+ // Translate each vertex.
+ for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++)
+ {
+ UnpackVector((unsigned short)(pVtx->normal), framev->normals[k].xyz);
+ for(c = 0; c < 3; c++)
+ {
+ framev->vertices[k].xyz[axis[c]] =
+ (pVtx->vertex[c] * FLOAT(pfr->scale[c]) + FLOAT(pfr->translate[c]));
+ }
+ }
+ }
+
+ memcpy(lodInfo, buffer+info.offsetLODs, info.numLODs * sizeof(DMDLoDInfo));
+ for(i = 0; i < info.numLODs; i++)
+ {
+ lodInfo[i].numTriangles = LittleLong(lodInfo[i].numTriangles);
+ lodInfo[i].offsetTriangles = LittleLong(lodInfo[i].offsetTriangles);
+ if (lodInfo[i].numTriangles > 0)
+ {
+ lods[i].triangles = new FTriangle[lodInfo[i].numTriangles];
+ memcpy(lods[i].triangles, buffer + lodInfo[i].offsetTriangles, lodInfo[i].numTriangles * sizeof(FTriangle));
+ for (int j = 0; j < lodInfo[i].numTriangles; j++)
+ {
+ for (int k = 0; k < 3; k++)
+ {
+ lods[i].triangles[j].textureIndices[k] = LittleShort(lods[i].triangles[j].textureIndices[k]);
+ lods[i].triangles[j].vertexIndices[k] = LittleShort(lods[i].triangles[j].vertexIndices[k]);
+ }
+ }
+ }
+ }
+
+}
+
+//===========================================================================
+//
+// Deletes everything that's no longer needed after building the vertex buffer
+//
+//===========================================================================
+
+void FDMDModel::UnloadGeometry()
+{
+ int i;
+
+ if (framevtx != NULL)
+ {
+ for (i=0;iLockVertexBuffer(VertexBufferSize);
+
+ for (int i = 0; i < info.numFrames; i++)
+ {
+ DMDModelVertex *vert = framevtx[i].vertices;
+ DMDModelVertex *norm = framevtx[i].normals;
+
+ frames[i].vindex = vindex;
+
+ FTriangle *tri = lods[0].triangles;
+
+ for (int i = 0; i < lodInfo[0].numTriangles; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+
+ int ti = tri->textureIndices[j];
+ int vi = tri->vertexIndices[j];
+
+ FModelVertex *bvert = &vertptr[vindex++];
+ bvert->Set(vert[vi].xyz[0], vert[vi].xyz[1], vert[vi].xyz[2], (float)texCoords[ti].s / info.skinWidth, (float)texCoords[ti].t / info.skinHeight);
+ bvert->SetNormal(norm[vi].xyz[0], norm[vi].xyz[1], norm[vi].xyz[2]);
+ }
+ tri++;
+ }
+ }
+ mVBuf->UnlockVertexBuffer();
+ UnloadGeometry();
+ }
+}
+
+//===========================================================================
+//
+// for skin precaching
+//
+//===========================================================================
+
+void FDMDModel::AddSkins(BYTE *hitlist)
+{
+ for (int i = 0; i < info.numSkins; i++)
+ {
+ if (skins[i].isValid())
+ {
+ hitlist[skins[i].GetIndex()] |= FTexture::TEX_Flat;
+ }
+ }
+}
+
+//===========================================================================
+//
+// FDMDModel::FindFrame
+//
+//===========================================================================
+int FDMDModel::FindFrame(const char * name)
+{
+ for (int i=0;i= info.numFrames || frameno2 >= info.numFrames) return;
+
+ if (!skin)
+ {
+ if (info.numSkins == 0 || !skins[0].isValid()) return;
+ skin = TexMan(skins[0]);
+ if (!skin) return;
+ }
+
+ FMaterial * tex = FMaterial::ValidateTexture(skin, false);
+
+ gl_RenderState.SetMaterial(tex, CLAMP_NONE, translation, -1, false);
+ gl_RenderState.SetInterpolationFactor((float)inter);
+
+ gl_RenderState.Apply();
+ mVBuf->SetupFrame(frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3);
+ glDrawArrays(GL_TRIANGLES, 0, lodInfo[0].numTriangles * 3);
+ gl_RenderState.SetInterpolationFactor(0.f);
+}
+
+
+
+//===========================================================================
+//
+// Internal data structures of MD2 files - only used during loading
+//
+//===========================================================================
+
+struct md2_header_t
+{
+ int magic;
+ int version;
+ int skinWidth;
+ int skinHeight;
+ int frameSize;
+ int numSkins;
+ int numVertices;
+ int numTexCoords;
+ int numTriangles;
+ int numGlCommands;
+ int numFrames;
+ int offsetSkins;
+ int offsetTexCoords;
+ int offsetTriangles;
+ int offsetFrames;
+ int offsetGlCommands;
+ int offsetEnd;
+};
+
+struct md2_triangleVertex_t
+{
+ byte vertex[3];
+ byte lightNormalIndex;
+};
+
+struct md2_packedFrame_t
+{
+ float scale[3];
+ float translate[3];
+ char name[16];
+ md2_triangleVertex_t vertices[1];
+};
+
+//===========================================================================
+//
+// FMD2Model::Load
+//
+//===========================================================================
+
+bool FMD2Model::Load(const char * path, int lumpnum, const char * buffer, int length)
+{
+ md2_header_t * md2header = (md2_header_t *)buffer;
+ ModelFrame *frame;
+ byte *md2_frames;
+ int i;
+
+ // Convert it to DMD.
+ header.magic = MD2_MAGIC;
+ header.version = 8;
+ header.flags = 0;
+ info.skinWidth = LittleLong(md2header->skinWidth);
+ info.skinHeight = LittleLong(md2header->skinHeight);
+ info.frameSize = LittleLong(md2header->frameSize);
+ info.numLODs = 1;
+ info.numSkins = LittleLong(md2header->numSkins);
+ info.numTexCoords = LittleLong(md2header->numTexCoords);
+ info.numVertices = LittleLong(md2header->numVertices);
+ info.numFrames = LittleLong(md2header->numFrames);
+ info.offsetSkins = LittleLong(md2header->offsetSkins);
+ info.offsetTexCoords = LittleLong(md2header->offsetTexCoords);
+ info.offsetFrames = LittleLong(md2header->offsetFrames);
+ info.offsetLODs = LittleLong(md2header->offsetEnd); // Doesn't exist.
+ lodInfo[0].numTriangles = LittleLong(md2header->numTriangles);
+ lodInfo[0].numGlCommands = LittleLong(md2header->numGlCommands);
+ lodInfo[0].offsetTriangles = LittleLong(md2header->offsetTriangles);
+ lodInfo[0].offsetGlCommands = LittleLong(md2header->offsetGlCommands);
+ info.offsetEnd = LittleLong(md2header->offsetEnd);
+
+ if (info.offsetFrames + info.frameSize * info.numFrames > length)
+ {
+ Printf("LoadModel: Model '%s' file too short\n", path);
+ return false;
+ }
+ if (lodInfo[0].numGlCommands <= 0)
+ {
+ Printf("LoadModel: Model '%s' invalid NumGLCommands\n", path);
+ return false;
+ }
+
+ skins = new FTextureID[info.numSkins];
+
+ for (i = 0; i < info.numSkins; i++)
+ {
+ skins[i] = LoadSkin(path, buffer + info.offsetSkins + i * 64);
+ }
+
+ // The frames need to be unpacked.
+ md2_frames = (byte*)buffer + info.offsetFrames;
+ frames = new ModelFrame[info.numFrames];
+
+ for (i = 0, frame = frames; i < info.numFrames; i++, frame++)
+ {
+ md2_packedFrame_t *pfr = (md2_packedFrame_t *)(md2_frames + info.frameSize * i);
+
+ memcpy(frame->name, pfr->name, sizeof(pfr->name));
+ frame->vindex = UINT_MAX;
+ }
+ mLumpNum = lumpnum;
+ return true;
+}
+
+//===========================================================================
+//
+// FMD2Model::LoadGeometry
+//
+//===========================================================================
+
+void FMD2Model::LoadGeometry()
+{
+ static int axis[3] = { VX, VY, VZ };
+ byte *md2_frames;
+ FMemLump lumpdata = Wads.ReadLump(mLumpNum);
+ const char *buffer = (const char *)lumpdata.GetMem();
+
+ texCoords = new FTexCoord[info.numTexCoords];
+ memcpy(texCoords, (byte*)buffer + info.offsetTexCoords, info.numTexCoords * sizeof(FTexCoord));
+
+ md2_frames = (byte*)buffer + info.offsetFrames;
+ framevtx = new ModelFrameVertexData[info.numFrames];
+ ModelFrameVertexData *framev;
+ int i, k, c;
+
+ for(i = 0, framev = framevtx; i < info.numFrames; i++, framev++)
+ {
+ md2_packedFrame_t *pfr = (md2_packedFrame_t *) (md2_frames + info.frameSize * i);
+ md2_triangleVertex_t *pVtx;
+
+ framev->vertices = new DMDModelVertex[info.numVertices];
+ framev->normals = new DMDModelVertex[info.numVertices];
+
+ // Translate each vertex.
+ for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++)
+ {
+ memcpy(framev->normals[k].xyz,
+ avertexnormals[pVtx->lightNormalIndex], sizeof(float) * 3);
+
+ for(c = 0; c < 3; c++)
+ {
+ framev->vertices[k].xyz[axis[c]] =
+ (pVtx->vertex[c] * pfr->scale[c] + pfr->translate[c]);
+ }
+ }
+ }
+
+ lods[0].triangles = new FTriangle[lodInfo[0].numTriangles];
+
+ int cnt = lodInfo[0].numTriangles;
+ memcpy(lods[0].triangles, buffer + lodInfo[0].offsetTriangles, sizeof(FTriangle) * cnt);
+ for (int j = 0; j < cnt; j++)
+ {
+ for (int k = 0; k < 3; k++)
+ {
+ lods[0].triangles[j].textureIndices[k] = LittleShort(lods[0].triangles[j].textureIndices[k]);
+ lods[0].triangles[j].vertexIndices[k] = LittleShort(lods[0].triangles[j].vertexIndices[k]);
+ }
+ }
+}
+
+FMD2Model::~FMD2Model()
+{
+}
+
diff --git a/src/gl/models/gl_models_md3.cpp b/src/gl/models/gl_models_md3.cpp
new file mode 100644
index 000000000..b91473714
--- /dev/null
+++ b/src/gl/models/gl_models_md3.cpp
@@ -0,0 +1,393 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2006-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+
+#include "gl/system/gl_system.h"
+#include "w_wad.h"
+#include "cmdlib.h"
+#include "sc_man.h"
+#include "m_crc32.h"
+
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/models/gl_models.h"
+#include "gl/textures/gl_material.h"
+#include "gl/shaders/gl_shader.h"
+
+#define MAX_QPATH 64
+
+//===========================================================================
+//
+// decode the lat/lng normal to a 3 float normal
+//
+//===========================================================================
+
+static void UnpackVector(unsigned short packed, float & nx, float & ny, float & nz)
+{
+ double lat = ( packed >> 8 ) & 0xff;
+ double lng = ( packed & 0xff );
+ lat *= M_PI/128;
+ lng *= M_PI/128;
+
+ nx = cos(lat) * sin(lng);
+ ny = sin(lat) * sin(lng);
+ nz = cos(lng);
+}
+
+//===========================================================================
+//
+// MD3 File structure
+//
+//===========================================================================
+
+#pragma pack(4)
+struct md3_header_t
+{
+ uint32_t Magic;
+ uint32_t Version;
+ char Name[MAX_QPATH];
+ uint32_t Flags;
+ uint32_t Num_Frames;
+ uint32_t Num_Tags;
+ uint32_t Num_Surfaces;
+ uint32_t Num_Skins;
+ uint32_t Ofs_Frames;
+ uint32_t Ofs_Tags;
+ uint32_t Ofs_Surfaces;
+ uint32_t Ofs_Eof;
+};
+
+struct md3_surface_t
+{
+ uint32_t Magic;
+ char Name[MAX_QPATH];
+ uint32_t Flags;
+ uint32_t Num_Frames;
+ uint32_t Num_Shaders;
+ uint32_t Num_Verts;
+ uint32_t Num_Triangles;
+ uint32_t Ofs_Triangles;
+ uint32_t Ofs_Shaders;
+ uint32_t Ofs_Texcoord;
+ uint32_t Ofs_XYZNormal;
+ uint32_t Ofs_End;
+};
+
+struct md3_triangle_t
+{
+ uint32_t vt_index[3];
+};
+
+struct md3_shader_t
+{
+ char Name[MAX_QPATH];
+ uint32_t index;
+};
+
+struct md3_texcoord_t
+{
+ float s, t;
+};
+
+struct md3_vertex_t
+{
+ short x, y, z, n;
+};
+
+struct md3_frame_t
+{
+ float min_Bounds[3];
+ float max_Bounds[3];
+ float localorigin[3];
+ float radius;
+ char Name[16];
+};
+#pragma pack()
+
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+bool FMD3Model::Load(const char * path, int lumpnum, const char * buffer, int length)
+{
+ md3_header_t * hdr = (md3_header_t *)buffer;
+
+ numFrames = LittleLong(hdr->Num_Frames);
+ numTags = LittleLong(hdr->Num_Tags);
+ numSurfaces = LittleLong(hdr->Num_Surfaces);
+
+ md3_frame_t * frm = (md3_frame_t*)(buffer + LittleLong(hdr->Ofs_Frames));
+
+ frames = new MD3Frame[numFrames];
+ for (int i = 0; i < numFrames; i++)
+ {
+ strncpy(frames[i].Name, frm[i].Name, 16);
+ for (int j = 0; j < 3; j++) frames[i].origin[j] = frm[i].localorigin[j];
+ }
+
+ md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));
+
+ surfaces = new MD3Surface[numSurfaces];
+
+ for (int i = 0; i < numSurfaces; i++)
+ {
+ MD3Surface * s = &surfaces[i];
+ md3_surface_t * ss = surf;
+
+ surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));
+
+ s->numSkins = LittleLong(ss->Num_Shaders);
+ s->numTriangles = LittleLong(ss->Num_Triangles);
+ s->numVertices = LittleLong(ss->Num_Verts);
+
+ // copy shaders (skins)
+ md3_shader_t * shader = (md3_shader_t*)(((char*)ss) + LittleLong(ss->Ofs_Shaders));
+ s->skins = new FTextureID[s->numSkins];
+
+ for (int i = 0; i < s->numSkins; i++)
+ {
+ // [BB] According to the MD3 spec, Name is supposed to include the full path.
+ s->skins[i] = LoadSkin("", shader[i].Name);
+ // [BB] Fall back and check if Name is relative.
+ if (!s->skins[i].isValid())
+ s->skins[i] = LoadSkin(path, shader[i].Name);
+ }
+ }
+ mLumpNum = lumpnum;
+ return true;
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FMD3Model::LoadGeometry()
+{
+ FMemLump lumpdata = Wads.ReadLump(mLumpNum);
+ const char *buffer = (const char *)lumpdata.GetMem();
+ md3_header_t * hdr = (md3_header_t *)buffer;
+ md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));
+
+ for(int i=0;iOfs_End));
+
+ // copy triangle indices
+ md3_triangle_t * tris = (md3_triangle_t*)(((char*)ss)+LittleLong(ss->Ofs_Triangles));
+ s->tris = new MD3Triangle[s->numTriangles];
+
+ for(int i=0;inumTriangles;i++) for (int j=0;j<3;j++)
+ {
+ s->tris[i].VertIndex[j]=LittleLong(tris[i].vt_index[j]);
+ }
+
+ // Load texture coordinates
+ md3_texcoord_t * tc = (md3_texcoord_t*)(((char*)ss)+LittleLong(ss->Ofs_Texcoord));
+ s->texcoords = new MD3TexCoord[s->numVertices];
+
+ for(int i=0;inumVertices;i++)
+ {
+ s->texcoords[i].s = tc[i].s;
+ s->texcoords[i].t = tc[i].t;
+ }
+
+ // Load vertices and texture coordinates
+ md3_vertex_t * vt = (md3_vertex_t*)(((char*)ss)+LittleLong(ss->Ofs_XYZNormal));
+ s->vertices = new MD3Vertex[s->numVertices * numFrames];
+
+ for(int i=0;inumVertices * numFrames;i++)
+ {
+ s->vertices[i].x = LittleShort(vt[i].x)/64.f;
+ s->vertices[i].y = LittleShort(vt[i].y)/64.f;
+ s->vertices[i].z = LittleShort(vt[i].z)/64.f;
+ UnpackVector( LittleShort(vt[i].n), s->vertices[i].nx, s->vertices[i].ny, s->vertices[i].nz);
+ }
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FMD3Model::BuildVertexBuffer()
+{
+ if (mVBuf == nullptr)
+ {
+ LoadGeometry();
+
+ unsigned int vbufsize = 0;
+ unsigned int ibufsize = 0;
+
+ for (int i = 0; i < numSurfaces; i++)
+ {
+ MD3Surface * surf = &surfaces[i];
+ vbufsize += numFrames * surf->numVertices;
+ ibufsize += 3 * surf->numTriangles;
+ }
+
+ mVBuf = new FModelVertexBuffer(true, numFrames == 1);
+ FModelVertex *vertptr = mVBuf->LockVertexBuffer(vbufsize);
+ unsigned int *indxptr = mVBuf->LockIndexBuffer(ibufsize);
+
+ assert(vertptr != nullptr && indxptr != nullptr);
+
+ unsigned int vindex = 0, iindex = 0;
+
+ for (int i = 0; i < numSurfaces; i++)
+ {
+ MD3Surface * surf = &surfaces[i];
+
+ surf->vindex = vindex;
+ surf->iindex = iindex;
+ for (int j = 0; j < numFrames * surf->numVertices; j++)
+ {
+ MD3Vertex* vert = surf->vertices + j;
+
+ FModelVertex *bvert = &vertptr[vindex++];
+
+ int tc = j % surf->numVertices;
+ bvert->Set(vert->x, vert->z, vert->y, surf->texcoords[tc].s, surf->texcoords[tc].t);
+ bvert->SetNormal(vert->nx, vert->nz, vert->ny);
+ }
+
+ for (int k = 0; k < surf->numTriangles; k++)
+ {
+ for (int l = 0; l < 3; l++)
+ {
+ indxptr[iindex++] = surf->tris[k].VertIndex[l];
+ }
+ }
+ surf->UnloadGeometry();
+ }
+ mVBuf->UnlockVertexBuffer();
+ mVBuf->UnlockIndexBuffer();
+ }
+}
+
+
+//===========================================================================
+//
+// for skin precaching
+//
+//===========================================================================
+
+void FMD3Model::AddSkins(BYTE *hitlist)
+{
+ for (int i = 0; i < numSurfaces; i++)
+ {
+ if (curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
+ {
+ hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTexture::TEX_Flat;
+ }
+
+ MD3Surface * surf = &surfaces[i];
+ for (int j = 0; j < surf->numSkins; j++)
+ {
+ if (surf->skins[j].isValid())
+ {
+ hitlist[surf->skins[j].GetIndex()] |= FTexture::TEX_Flat;
+ }
+ }
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+int FMD3Model::FindFrame(const char * name)
+{
+ for (int i=0;i=numFrames || frameno2>=numFrames) return;
+
+ gl_RenderState.SetInterpolationFactor((float)inter);
+ for(int i=0;isurfaceskinIDs[curMDLIndex][i].isValid())
+ {
+ surfaceSkin = TexMan(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i]);
+ }
+ else if(surf->numSkins > 0 && surf->skins[0].isValid())
+ {
+ surfaceSkin = TexMan(surf->skins[0]);
+ }
+
+ if (!surfaceSkin) return;
+ }
+
+ FMaterial * tex = FMaterial::ValidateTexture(surfaceSkin, false);
+
+ gl_RenderState.SetMaterial(tex, CLAMP_NONE, translation, -1, false);
+
+ gl_RenderState.Apply();
+ mVBuf->SetupFrame(surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices);
+ glDrawElements(GL_TRIANGLES, surf->numTriangles * 3, GL_UNSIGNED_INT, (void*)(intptr_t)(surf->iindex * sizeof(unsigned int)));
+ }
+ gl_RenderState.SetInterpolationFactor(0.f);
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+FMD3Model::~FMD3Model()
+{
+ if (frames) delete [] frames;
+ if (surfaces) delete [] surfaces;
+ frames = nullptr;
+ surfaces = nullptr;
+}
diff --git a/src/gl/models/gl_voxels.cpp b/src/gl/models/gl_voxels.cpp
new file mode 100644
index 000000000..6e84bd7ab
--- /dev/null
+++ b/src/gl/models/gl_voxels.cpp
@@ -0,0 +1,450 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2010-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_voxels.cpp
+**
+** Voxel management
+**
+**/
+
+#include "gl/system/gl_system.h"
+#include "w_wad.h"
+#include "cmdlib.h"
+#include "sc_man.h"
+#include "m_crc32.h"
+#include "c_console.h"
+#include "g_game.h"
+#include "doomstat.h"
+#include "g_level.h"
+#include "colormatcher.h"
+#include "textures/bitmap.h"
+//#include "gl/gl_intern.h"
+
+#include "gl/system/gl_interface.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/models/gl_models.h"
+#include "gl/textures/gl_material.h"
+#include "gl/utility/gl_geometric.h"
+#include "gl/utility/gl_convert.h"
+#include "gl/renderer/gl_renderstate.h"
+
+
+//===========================================================================
+//
+// Creates a 16x16 texture from the palette so that we can
+// use the existing palette manipulation code to render the voxel
+// Otherwise all shaders had to be duplicated and the non-shader code
+// would be a lot less efficient.
+//
+//===========================================================================
+
+class FVoxelTexture : public FTexture
+{
+public:
+
+ FVoxelTexture(FVoxel *voxel);
+ ~FVoxelTexture();
+ const BYTE *GetColumn (unsigned int column, const Span **spans_out);
+ const BYTE *GetPixels ();
+ void Unload ();
+
+ int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf);
+ bool UseBasePalette() { return false; }
+
+protected:
+ FVoxel *SourceVox;
+ BYTE *Pixels;
+
+};
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+FVoxelTexture::FVoxelTexture(FVoxel *vox)
+{
+ SourceVox = vox;
+ Width = 16;
+ Height = 16;
+ WidthBits = 4;
+ HeightBits = 4;
+ WidthMask = 15;
+ Pixels = NULL;
+ gl_info.bNoFilter = true;
+ gl_info.bNoCompress = true;
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+FVoxelTexture::~FVoxelTexture()
+{
+}
+
+const BYTE *FVoxelTexture::GetColumn (unsigned int column, const Span **spans_out)
+{
+ // not needed
+ return NULL;
+}
+
+const BYTE *FVoxelTexture::GetPixels ()
+{
+ // GetPixels gets called when a translated palette is used so we still need to implement it here.
+ if (Pixels == NULL)
+ {
+ Pixels = new BYTE[256];
+
+ BYTE *pp = SourceVox->Palette;
+
+ if(pp != NULL)
+ {
+ for(int i=0;i<256;i++, pp+=3)
+ {
+ PalEntry pe;
+ pe.r = (pp[0] << 2) | (pp[0] >> 4);
+ pe.g = (pp[1] << 2) | (pp[1] >> 4);
+ pe.b = (pp[2] << 2) | (pp[2] >> 4);
+ Pixels[i] = ColorMatcher.Pick(pe);
+ }
+ }
+ else
+ {
+ for(int i=0;i<256;i++, pp+=3)
+ {
+ Pixels[i] = (BYTE)i;
+ }
+ }
+ }
+ return Pixels;
+}
+
+void FVoxelTexture::Unload ()
+{
+ if (Pixels != NULL)
+ {
+ delete[] Pixels;
+ Pixels = NULL;
+ }
+}
+
+//===========================================================================
+//
+// FVoxelTexture::CopyTrueColorPixels
+//
+// This creates a dummy 16x16 paletted bitmap and converts that using the
+// voxel palette
+//
+//===========================================================================
+
+int FVoxelTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf)
+{
+ PalEntry pe[256];
+ BYTE bitmap[256];
+ BYTE *pp = SourceVox->Palette;
+
+ if(pp != NULL)
+ {
+ for(int i=0;i<256;i++, pp+=3)
+ {
+ bitmap[i] = (BYTE)i;
+ pe[i].r = (pp[0] << 2) | (pp[0] >> 4);
+ pe[i].g = (pp[1] << 2) | (pp[1] >> 4);
+ pe[i].b = (pp[2] << 2) | (pp[2] >> 4);
+ pe[i].a = 255;
+ }
+ }
+ else
+ {
+ for(int i=0;i<256;i++, pp+=3)
+ {
+ bitmap[i] = (BYTE)i;
+ pe[i] = GPalette.BaseColors[i];
+ pe[i].a = 255;
+ }
+ }
+ bmp->CopyPixelData(x, y, bitmap, Width, Height, 1, 16, rotate, pe, inf);
+ return 0;
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+FVoxelModel::FVoxelModel(FVoxel *voxel, bool owned)
+{
+ mVoxel = voxel;
+ mOwningVoxel = owned;
+ mPalette = TexMan.AddTexture(new FVoxelTexture(voxel));
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+FVoxelModel::~FVoxelModel()
+{
+ if (mOwningVoxel) delete mVoxel;
+}
+
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+unsigned int FVoxelModel::AddVertex(FModelVertex &vert, FVoxelMap &check)
+{
+ unsigned int index = check[vert];
+ if (index == 0xffffffff)
+ {
+ index = check[vert] =mVertices.Push(vert);
+ }
+ return index;
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FVoxelModel::AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3, int x4, int y4, int z4, BYTE col, FVoxelMap &check)
+{
+ float PivotX = mVoxel->Mips[0].Pivot.X;
+ float PivotY = mVoxel->Mips[0].Pivot.Y;
+ float PivotZ = mVoxel->Mips[0].Pivot.Z;
+ int h = mVoxel->Mips[0].SizeZ;
+ FModelVertex vert;
+ unsigned int indx[4];
+
+ vert.packedNormal = 0; // currently this is not being used for voxels.
+ vert.u = (((col & 15) * 255 / 16) + 7) / 255.f;
+ vert.v = (((col / 16) * 255 / 16) + 7) / 255.f;
+
+ vert.x = x1 - PivotX;
+ vert.z = -y1 + PivotY;
+ vert.y = -z1 + PivotZ;
+ indx[0] = AddVertex(vert, check);
+
+ vert.x = x2 - PivotX;
+ vert.z = -y2 + PivotY;
+ vert.y = -z2 + PivotZ;
+ indx[1] = AddVertex(vert, check);
+
+ vert.x = x4 - PivotX;
+ vert.z = -y4 + PivotY;
+ vert.y = -z4 + PivotZ;
+ indx[2] = AddVertex(vert, check);
+
+ vert.x = x3 - PivotX;
+ vert.z = -y3 + PivotY;
+ vert.y = -z3 + PivotZ;
+ indx[3] = AddVertex(vert, check);
+
+
+ mIndices.Push(indx[0]);
+ mIndices.Push(indx[1]);
+ mIndices.Push(indx[3]);
+ mIndices.Push(indx[1]);
+ mIndices.Push(indx[2]);
+ mIndices.Push(indx[3]);
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FVoxelModel::MakeSlabPolys(int x, int y, kvxslab_t *voxptr, FVoxelMap &check)
+{
+ const BYTE *col = voxptr->col;
+ int zleng = voxptr->zleng;
+ int ztop = voxptr->ztop;
+ int cull = voxptr->backfacecull;
+
+ if (cull & 16)
+ {
+ AddFace(x, y, ztop, x+1, y, ztop, x, y+1, ztop, x+1, y+1, ztop, *col, check);
+ }
+ int z = ztop;
+ while (z < ztop+zleng)
+ {
+ int c = 0;
+ while (z+c < ztop+zleng && col[c] == col[0]) c++;
+
+ if (cull & 1)
+ {
+ AddFace(x, y, z, x, y+1, z, x, y, z+c, x, y+1, z+c, *col, check);
+ }
+ if (cull & 2)
+ {
+ AddFace(x+1, y+1, z, x+1, y, z, x+1, y+1, z+c, x+1, y, z+c, *col, check);
+ }
+ if (cull & 4)
+ {
+ AddFace(x+1, y, z, x, y, z, x+1, y, z+c, x, y, z+c, *col, check);
+ }
+ if (cull & 8)
+ {
+ AddFace(x, y+1, z, x+1, y+1, z, x, y+1, z+c, x+1, y+1, z+c, *col, check);
+ }
+ z+=c;
+ col+=c;
+ }
+ if (cull & 32)
+ {
+ int z = ztop+zleng-1;
+ AddFace(x+1, y, z+1, x, y, z+1, x+1, y+1, z+1, x, y+1, z+1, voxptr->col[zleng-1], check);
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FVoxelModel::Initialize()
+{
+ FVoxelMap check;
+ FVoxelMipLevel *mip = &mVoxel->Mips[0];
+ for (int x = 0; x < mip->SizeX; x++)
+ {
+ BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]];
+ short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)];
+ for (int y = 0; y < mip->SizeY; y++)
+ {
+ kvxslab_t *voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]);
+ kvxslab_t *voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]);
+ for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3))
+ {
+ MakeSlabPolys(x, y, voxptr, check);
+ }
+ }
+ }
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FVoxelModel::BuildVertexBuffer()
+{
+ if (mVBuf == NULL)
+ {
+ Initialize();
+
+ mVBuf = new FModelVertexBuffer(true, true);
+ FModelVertex *vertptr = mVBuf->LockVertexBuffer(mVertices.Size());
+ unsigned int *indxptr = mVBuf->LockIndexBuffer(mIndices.Size());
+
+ memcpy(vertptr, &mVertices[0], sizeof(FModelVertex)* mVertices.Size());
+ memcpy(indxptr, &mIndices[0], sizeof(unsigned int)* mIndices.Size());
+
+ mVBuf->UnlockVertexBuffer();
+ mVBuf->UnlockIndexBuffer();
+ mNumIndices = mIndices.Size();
+
+ // delete our temporary buffers
+ mVertices.Clear();
+ mIndices.Clear();
+ mVertices.ShrinkToFit();
+ mIndices.ShrinkToFit();
+ }
+}
+
+
+//===========================================================================
+//
+// for skin precaching
+//
+//===========================================================================
+
+void FVoxelModel::AddSkins(BYTE *hitlist)
+{
+ hitlist[mPalette.GetIndex()] |= FTexture::TEX_Flat;
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+bool FVoxelModel::Load(const char * fn, int lumpnum, const char * buffer, int length)
+{
+ return false; // not needed
+}
+
+//===========================================================================
+//
+// Voxels don't have frames so always return 0
+//
+//===========================================================================
+
+int FVoxelModel::FindFrame(const char * name)
+{
+ return 0;
+}
+
+//===========================================================================
+//
+// Voxels need aspect ratio correction according to the current map's setting
+//
+//===========================================================================
+
+float FVoxelModel::getAspectFactor()
+{
+ return glset.pixelstretch;
+}
+
+//===========================================================================
+//
+// Voxels never interpolate between frames, they only have one.
+//
+//===========================================================================
+
+void FVoxelModel::RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation)
+{
+ FMaterial * tex = FMaterial::ValidateTexture(skin, false);
+ gl_RenderState.SetMaterial(tex, CLAMP_NOFILTER, translation, -1, false);
+
+ gl_RenderState.Apply();
+ mVBuf->SetupFrame(0, 0, 0);
+ glDrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, (void*)(intptr_t)0);
+}
+
diff --git a/src/gl/models/tab_anorms.h b/src/gl/models/tab_anorms.h
new file mode 100644
index 000000000..46467b460
--- /dev/null
+++ b/src/gl/models/tab_anorms.h
@@ -0,0 +1,489 @@
+#ifdef WIN32
+#pragma warning(disable:4305)
+#endif
+
+{
+-0.525731, 0.000000, 0.850651},
+
+{
+-0.442863, 0.238856, 0.864188},
+
+{
+-0.295242, 0.000000, 0.955423},
+
+{
+-0.309017, 0.500000, 0.809017},
+
+{
+-0.162460, 0.262866, 0.951056},
+
+{
+0.000000, 0.000000, 1.000000},
+
+{
+0.000000, 0.850651, 0.525731},
+
+{
+-0.147621, 0.716567, 0.681718},
+
+{
+0.147621, 0.716567, 0.681718},
+
+{
+0.000000, 0.525731, 0.850651},
+
+{
+0.309017, 0.500000, 0.809017},
+
+{
+0.525731, 0.000000, 0.850651},
+
+{
+0.295242, 0.000000, 0.955423},
+
+{
+0.442863, 0.238856, 0.864188},
+
+{
+0.162460, 0.262866, 0.951056},
+
+{
+-0.681718, 0.147621, 0.716567},
+
+{
+-0.809017, 0.309017, 0.500000},
+
+{
+-0.587785, 0.425325, 0.688191},
+
+{
+-0.850651, 0.525731, 0.000000},
+
+{
+-0.864188, 0.442863, 0.238856},
+
+{
+-0.716567, 0.681718, 0.147621},
+
+{
+-0.688191, 0.587785, 0.425325},
+
+{
+-0.500000, 0.809017, 0.309017},
+
+{
+-0.238856, 0.864188, 0.442863},
+
+{
+-0.425325, 0.688191, 0.587785},
+
+{
+-0.716567, 0.681718, -0.147621},
+
+{
+-0.500000, 0.809017, -0.309017},
+
+{
+-0.525731, 0.850651, 0.000000},
+
+{
+0.000000, 0.850651, -0.525731},
+
+{
+-0.238856, 0.864188, -0.442863},
+
+{
+0.000000, 0.955423, -0.295242},
+
+{
+-0.262866, 0.951056, -0.162460},
+
+{
+0.000000, 1.000000, 0.000000},
+
+{
+0.000000, 0.955423, 0.295242},
+
+{
+-0.262866, 0.951056, 0.162460},
+
+{
+0.238856, 0.864188, 0.442863},
+
+{
+0.262866, 0.951056, 0.162460},
+
+{
+0.500000, 0.809017, 0.309017},
+
+{
+0.238856, 0.864188, -0.442863},
+
+{
+0.262866, 0.951056, -0.162460},
+
+{
+0.500000, 0.809017, -0.309017},
+
+{
+0.850651, 0.525731, 0.000000},
+
+{
+0.716567, 0.681718, 0.147621},
+
+{
+0.716567, 0.681718, -0.147621},
+
+{
+0.525731, 0.850651, 0.000000},
+
+{
+0.425325, 0.688191, 0.587785},
+
+{
+0.864188, 0.442863, 0.238856},
+
+{
+0.688191, 0.587785, 0.425325},
+
+{
+0.809017, 0.309017, 0.500000},
+
+{
+0.681718, 0.147621, 0.716567},
+
+{
+0.587785, 0.425325, 0.688191},
+
+{
+0.955423, 0.295242, 0.000000},
+
+{
+1.000000, 0.000000, 0.000000},
+
+{
+0.951056, 0.162460, 0.262866},
+
+{
+0.850651, -0.525731, 0.000000},
+
+{
+0.955423, -0.295242, 0.000000},
+
+{
+0.864188, -0.442863, 0.238856},
+
+{
+0.951056, -0.162460, 0.262866},
+
+{
+0.809017, -0.309017, 0.500000},
+
+{
+0.681718, -0.147621, 0.716567},
+
+{
+0.850651, 0.000000, 0.525731},
+
+{
+0.864188, 0.442863, -0.238856},
+
+{
+0.809017, 0.309017, -0.500000},
+
+{
+0.951056, 0.162460, -0.262866},
+
+{
+0.525731, 0.000000, -0.850651},
+
+{
+0.681718, 0.147621, -0.716567},
+
+{
+0.681718, -0.147621, -0.716567},
+
+{
+0.850651, 0.000000, -0.525731},
+
+{
+0.809017, -0.309017, -0.500000},
+
+{
+0.864188, -0.442863, -0.238856},
+
+{
+0.951056, -0.162460, -0.262866},
+
+{
+0.147621, 0.716567, -0.681718},
+
+{
+0.309017, 0.500000, -0.809017},
+
+{
+0.425325, 0.688191, -0.587785},
+
+{
+0.442863, 0.238856, -0.864188},
+
+{
+0.587785, 0.425325, -0.688191},
+
+{
+0.688191, 0.587785, -0.425325},
+
+{
+-0.147621, 0.716567, -0.681718},
+
+{
+-0.309017, 0.500000, -0.809017},
+
+{
+0.000000, 0.525731, -0.850651},
+
+{
+-0.525731, 0.000000, -0.850651},
+
+{
+-0.442863, 0.238856, -0.864188},
+
+{
+-0.295242, 0.000000, -0.955423},
+
+{
+-0.162460, 0.262866, -0.951056},
+
+{
+0.000000, 0.000000, -1.000000},
+
+{
+0.295242, 0.000000, -0.955423},
+
+{
+0.162460, 0.262866, -0.951056},
+
+{
+-0.442863, -0.238856, -0.864188},
+
+{
+-0.309017, -0.500000, -0.809017},
+
+{
+-0.162460, -0.262866, -0.951056},
+
+{
+0.000000, -0.850651, -0.525731},
+
+{
+-0.147621, -0.716567, -0.681718},
+
+{
+0.147621, -0.716567, -0.681718},
+
+{
+0.000000, -0.525731, -0.850651},
+
+{
+0.309017, -0.500000, -0.809017},
+
+{
+0.442863, -0.238856, -0.864188},
+
+{
+0.162460, -0.262866, -0.951056},
+
+{
+0.238856, -0.864188, -0.442863},
+
+{
+0.500000, -0.809017, -0.309017},
+
+{
+0.425325, -0.688191, -0.587785},
+
+{
+0.716567, -0.681718, -0.147621},
+
+{
+0.688191, -0.587785, -0.425325},
+
+{
+0.587785, -0.425325, -0.688191},
+
+{
+0.000000, -0.955423, -0.295242},
+
+{
+0.000000, -1.000000, 0.000000},
+
+{
+0.262866, -0.951056, -0.162460},
+
+{
+0.000000, -0.850651, 0.525731},
+
+{
+0.000000, -0.955423, 0.295242},
+
+{
+0.238856, -0.864188, 0.442863},
+
+{
+0.262866, -0.951056, 0.162460},
+
+{
+0.500000, -0.809017, 0.309017},
+
+{
+0.716567, -0.681718, 0.147621},
+
+{
+0.525731, -0.850651, 0.000000},
+
+{
+-0.238856, -0.864188, -0.442863},
+
+{
+-0.500000, -0.809017, -0.309017},
+
+{
+-0.262866, -0.951056, -0.162460},
+
+{
+-0.850651, -0.525731, 0.000000},
+
+{
+-0.716567, -0.681718, -0.147621},
+
+{
+-0.716567, -0.681718, 0.147621},
+
+{
+-0.525731, -0.850651, 0.000000},
+
+{
+-0.500000, -0.809017, 0.309017},
+
+{
+-0.238856, -0.864188, 0.442863},
+
+{
+-0.262866, -0.951056, 0.162460},
+
+{
+-0.864188, -0.442863, 0.238856},
+
+{
+-0.809017, -0.309017, 0.500000},
+
+{
+-0.688191, -0.587785, 0.425325},
+
+{
+-0.681718, -0.147621, 0.716567},
+
+{
+-0.442863, -0.238856, 0.864188},
+
+{
+-0.587785, -0.425325, 0.688191},
+
+{
+-0.309017, -0.500000, 0.809017},
+
+{
+-0.147621, -0.716567, 0.681718},
+
+{
+-0.425325, -0.688191, 0.587785},
+
+{
+-0.162460, -0.262866, 0.951056},
+
+{
+0.442863, -0.238856, 0.864188},
+
+{
+0.162460, -0.262866, 0.951056},
+
+{
+0.309017, -0.500000, 0.809017},
+
+{
+0.147621, -0.716567, 0.681718},
+
+{
+0.000000, -0.525731, 0.850651},
+
+{
+0.425325, -0.688191, 0.587785},
+
+{
+0.587785, -0.425325, 0.688191},
+
+{
+0.688191, -0.587785, 0.425325},
+
+{
+-0.955423, 0.295242, 0.000000},
+
+{
+-0.951056, 0.162460, 0.262866},
+
+{
+-1.000000, 0.000000, 0.000000},
+
+{
+-0.850651, 0.000000, 0.525731},
+
+{
+-0.955423, -0.295242, 0.000000},
+
+{
+-0.951056, -0.162460, 0.262866},
+
+{
+-0.864188, 0.442863, -0.238856},
+
+{
+-0.951056, 0.162460, -0.262866},
+
+{
+-0.809017, 0.309017, -0.500000},
+
+{
+-0.864188, -0.442863, -0.238856},
+
+{
+-0.951056, -0.162460, -0.262866},
+
+{
+-0.809017, -0.309017, -0.500000},
+
+{
+-0.681718, 0.147621, -0.716567},
+
+{
+-0.681718, -0.147621, -0.716567},
+
+{
+-0.850651, 0.000000, -0.525731},
+
+{
+-0.688191, 0.587785, -0.425325},
+
+{
+-0.587785, 0.425325, -0.688191},
+
+{
+-0.425325, 0.688191, -0.587785},
+
+{
+-0.425325, -0.688191, -0.587785},
+
+{
+-0.587785, -0.425325, -0.688191},
+
+{
+-0.688191, -0.587785, -0.425325},
diff --git a/src/gl/renderer/gl_2ddrawer.cpp b/src/gl/renderer/gl_2ddrawer.cpp
new file mode 100644
index 000000000..ba78634a3
--- /dev/null
+++ b/src/gl/renderer/gl_2ddrawer.cpp
@@ -0,0 +1,489 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_2ddrawer.h
+** Container class for drawing 2d graphics with a vertex buffer
+**
+**/
+
+#include "gl/system/gl_system.h"
+#include "gl/system/gl_framebuffer.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_2ddrawer.h"
+#include "gl/textures/gl_material.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/textures/gl_translate.h"
+#include "vectors.h"
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+int F2DDrawer::AddData(const F2DDrawer::DataGeneric *data)
+{
+ int addr = mData.Reserve(data->mLen);
+ memcpy(&mData[addr], data, data->mLen);
+ mLastLineCmd = -1;
+ return addr;
+}
+
+//==========================================================================
+//
+// Draws a texture
+//
+//==========================================================================
+
+void F2DDrawer::AddTexture(FTexture *img, DrawParms &parms)
+{
+ double xscale = parms.destwidth / parms.texwidth;
+ double yscale = parms.destheight / parms.texheight;
+ double x = parms.x - parms.left * xscale;
+ double y = parms.y - parms.top * yscale;
+ double w = parms.destwidth;
+ double h = parms.destheight;
+ float u1, v1, u2, v2;
+ int light = 255;
+
+ FMaterial * gltex = FMaterial::ValidateTexture(img, false);
+ if (gltex == nullptr) return;
+
+ DataTexture dg;
+
+ dg.mType = DrawTypeTexture;
+ dg.mLen = (sizeof(dg) + 7) & ~7;
+ dg.mVertCount = 4;
+ dg.mRenderStyle = parms.style;
+ dg.mMasked = !!parms.masked;
+ dg.mTexture = gltex;
+
+ if (parms.colorOverlay && (parms.colorOverlay & 0xffffff) == 0)
+ {
+ // handle black overlays as reduced light.
+ light = 255 - APART(parms.colorOverlay);
+ parms.colorOverlay = 0;
+ }
+ dg.mVertIndex = (int)mVertices.Reserve(parms.colorOverlay == 0? 4 : 8);
+ dg.mColorOverlay = parms.colorOverlay;
+ dg.mTranslation = 0;
+
+ if (!img->bHasCanvas)
+ {
+ if (!parms.alphaChannel)
+ {
+ if (parms.remap != NULL && !parms.remap->Inactive)
+ {
+ GLTranslationPalette * pal = static_cast(parms.remap->GetNative());
+ if (pal) dg.mTranslation = -pal->GetIndex();
+ }
+ }
+ dg.mAlphaTexture = !!(parms.style.Flags & STYLEF_RedIsAlpha);
+ u1 = gltex->GetUL();
+ v1 = gltex->GetVT();
+ u2 = gltex->GetUR();
+ v2 = gltex->GetVB();
+
+ }
+ else
+ {
+ dg.mAlphaTexture = false;
+ u1 = 0.f;
+ v1 = 1.f;
+ u2 = 1.f;
+ v2 = 0.f;
+ }
+
+ if (parms.flipX)
+ std::swap(u1, u2);
+
+
+ if (parms.windowleft > 0 || parms.windowright < parms.texwidth)
+ {
+ double wi = MIN(parms.windowright, parms.texwidth);
+ x += parms.windowleft * xscale;
+ w -= (parms.texwidth - wi + parms.windowleft) * xscale;
+
+ u1 = float(u1 + parms.windowleft / parms.texwidth);
+ u2 = float(u2 - (parms.texwidth - wi) / parms.texwidth);
+ }
+
+ PalEntry color;
+ if (parms.style.Flags & STYLEF_ColorIsFixed)
+ {
+ color = parms.fillcolor;
+ std::swap(color.r, color.b);
+ }
+ else
+ {
+ color = PalEntry(light, light, light);
+ }
+ color.a = (BYTE)(parms.Alpha * 255);
+
+ // scissor test doesn't use the current viewport for the coordinates, so use real screen coordinates
+ dg.mScissor[0] = GLRenderer->ScreenToWindowX(parms.lclip);
+ dg.mScissor[1] = GLRenderer->ScreenToWindowY(parms.dclip);
+ dg.mScissor[2] = GLRenderer->ScreenToWindowX(parms.rclip) - dg.mScissor[0];
+ dg.mScissor[3] = GLRenderer->ScreenToWindowY(parms.uclip) - dg.mScissor[1];
+
+ FSimpleVertex *ptr = &mVertices[dg.mVertIndex];
+ ptr->Set(x, y, 0, u1, v1, color); ptr++;
+ ptr->Set(x, y + h, 0, u1, v2, color); ptr++;
+ ptr->Set(x + w, y, 0, u2, v1, color); ptr++;
+ ptr->Set(x + w, y + h, 0, u2, v2, color); ptr++;
+ if (parms.colorOverlay != 0)
+ {
+ color = parms.colorOverlay;
+ std::swap(color.r, color.b);
+ ptr->Set(x, y, 0, u1, v1, color); ptr++;
+ ptr->Set(x, y + h, 0, u1, v2, color); ptr++;
+ ptr->Set(x + w, y, 0, u2, v1, color); ptr++;
+ ptr->Set(x + w, y + h, 0, u2, v2, color); ptr++;
+ dg.mVertCount = 8;
+ }
+ AddData(&dg);
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void F2DDrawer::AddPoly(FTexture *texture, FVector2 *points, int npoints,
+ double originx, double originy, double scalex, double scaley,
+ DAngle rotation, FDynamicColormap *colormap, int lightlevel)
+{
+ FMaterial *gltexture = FMaterial::ValidateTexture(texture, false);
+
+ if (gltexture == nullptr)
+ {
+ return;
+ }
+ DataSimplePoly poly;
+
+ poly.mType = DrawTypePoly;
+ poly.mLen = (sizeof(poly) + 7) & ~7;
+ poly.mTexture = gltexture;
+ poly.mColormap = colormap;
+ poly.mLightLevel = lightlevel;
+ poly.mVertCount = npoints;
+ poly.mVertIndex = (int)mVertices.Reserve(npoints);
+
+ bool dorotate = rotation != 0;
+
+ float cosrot = cos(rotation.Radians());
+ float sinrot = sin(rotation.Radians());
+
+ float uscale = float(1.f / (texture->GetScaledWidth() * scalex));
+ float vscale = float(1.f / (texture->GetScaledHeight() * scaley));
+ if (texture->bHasCanvas)
+ {
+ vscale = 0 - vscale;
+ }
+ float ox = float(originx);
+ float oy = float(originy);
+
+ for (int i = 0; i < npoints; ++i)
+ {
+ float u = points[i].X - 0.5f - ox;
+ float v = points[i].Y - 0.5f - oy;
+ if (dorotate)
+ {
+ float t = u;
+ u = t * cosrot - v * sinrot;
+ v = v * cosrot + t * sinrot;
+ }
+ mVertices[poly.mVertIndex+i].Set(points[i].X, points[i].Y, 0, u*uscale, v*vscale);
+ }
+ AddData(&poly);
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void F2DDrawer::AddDim(PalEntry color, float damount, int x1, int y1, int w, int h)
+{
+ color.a = uint8_t(damount * 255);
+ std::swap(color.r, color.b);
+
+ DataGeneric dg;
+
+ dg.mType = DrawTypeDim;
+ dg.mLen = (sizeof(dg) + 7) & ~7;
+ dg.mVertCount = 4;
+ dg.mVertIndex = (int)mVertices.Reserve(4);
+ FSimpleVertex *ptr = &mVertices[dg.mVertIndex];
+ ptr->Set(x1, y1, 0, 0, 0, color); ptr++;
+ ptr->Set(x1, y1 + h, 0, 0, 0, color); ptr++;
+ ptr->Set(x1 + w, y1 + h, 0, 0, 0, color); ptr++;
+ ptr->Set(x1 + w, y1, 0, 0, 0, color); ptr++;
+ AddData(&dg);
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void F2DDrawer::AddClear(int left, int top, int right, int bottom, int palcolor, uint32 color)
+{
+ PalEntry p = palcolor == -1 || color != 0 ? (PalEntry)color : GPalette.BaseColors[palcolor];
+ AddDim(p, 1.f, left, top, right - left, bottom - top);
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void F2DDrawer::AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin)
+{
+ float fU1, fU2, fV1, fV2;
+
+ FMaterial *gltexture = FMaterial::ValidateTexture(src, false);
+
+ if (!gltexture) return;
+
+ DataFlatFill dg;
+
+ dg.mType = DrawTypeFlatFill;
+ dg.mLen = (sizeof(dg) + 7) & ~7;
+ dg.mVertCount = 4;
+ dg.mVertIndex = (int)mVertices.Reserve(4);
+ dg.mTexture = gltexture;
+
+ // scaling is not used here.
+ if (!local_origin)
+ {
+ fU1 = float(left) / src->GetWidth();
+ fV1 = float(top) / src->GetHeight();
+ fU2 = float(right) / src->GetWidth();
+ fV2 = float(bottom) / src->GetHeight();
+ }
+ else
+ {
+ fU1 = 0;
+ fV1 = 0;
+ fU2 = float(right - left) / src->GetWidth();
+ fV2 = float(bottom - top) / src->GetHeight();
+ }
+ FSimpleVertex *ptr = &mVertices[dg.mVertIndex];
+ ptr->Set(left, top, 0, fU1, fV1); ptr++;
+ ptr->Set(left, bottom, 0, fU1, fV2); ptr++;
+ ptr->Set(right, top, 0, fU2, fV1); ptr++;
+ ptr->Set(right, bottom, 0, fU2, fV2); ptr++;
+ AddData(&dg);
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color)
+{
+ PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];
+ p.a = 255;
+ std::swap(p.r, p.b);
+
+ DataGeneric dg;
+
+ dg.mType = DrawTypeLine;
+ dg.mLen = (sizeof(dg) + 7) & ~7;
+ dg.mVertCount = 2;
+ dg.mVertIndex = (int)mVertices.Reserve(2);
+ mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p);
+ mVertices[dg.mVertIndex+1].Set(x2, y2, 0, 0, 0, p);
+
+ // Test if we can batch multiple line commands
+ if (mLastLineCmd == -1)
+ {
+ mLastLineCmd = AddData(&dg);
+ }
+ else
+ {
+ DataGeneric *dg = (DataGeneric *)&mData[mLastLineCmd];
+ dg->mVertCount += 2;
+ }
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32 color)
+{
+ PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];
+ p.a = 255;
+ std::swap(p.r, p.b);
+
+ DataGeneric dg;
+
+ dg.mType = DrawTypePixel;
+ dg.mLen = (sizeof(dg) + 7) & ~7;
+ dg.mVertCount = 2;
+ dg.mVertIndex = (int)mVertices.Reserve(1);
+ mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p);
+ AddData(&dg);
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void F2DDrawer::Draw()
+{
+ F2DDrawer::EDrawType lasttype = DrawTypeTexture;
+
+ if (mData.Size() == 0) return;
+ SBYTE savedlightmode = glset.lightmode;
+ // lightmode is only relevant for automap subsectors,
+ // but We cannot use the software light mode here because it doesn't properly calculate the light for 2D rendering.
+ if (glset.lightmode == 8) glset.lightmode = 0;
+
+ set(&mVertices[0], mVertices.Size());
+ for (unsigned i = 0; i < mData.Size();)
+ {
+ DataGeneric *dg = (DataGeneric *)&mData[i];
+ // DrawTypePoly may not use the color part of the vertex buffer because it needs to use gl_SetColor to produce proper output.
+ if (lasttype == DrawTypePoly && dg->mType != DrawTypePoly)
+ {
+ gl_RenderState.ResetColor(); // this is needed to reset the desaturation.
+ EnableColorArray(true);
+ }
+ else if (lasttype != DrawTypePoly && dg->mType == DrawTypePoly)
+ {
+ EnableColorArray(false);
+ }
+ lasttype = dg->mType;
+
+ switch (dg->mType)
+ {
+ default:
+ break;
+
+ case DrawTypeTexture:
+ {
+ DataTexture *dt = static_cast(dg);
+
+ gl_SetRenderStyle(dt->mRenderStyle, !dt->mMasked, false);
+ gl_RenderState.SetMaterial(dt->mTexture, CLAMP_XY_NOMIP, dt->mTranslation, -1, dt->mAlphaTexture);
+ if (dt->mTexture->tex->bHasCanvas) gl_RenderState.SetTextureMode(TM_OPAQUE);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(dt->mScissor[0], dt->mScissor[1], dt->mScissor[2], dt->mScissor[3]);
+
+ gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
+ gl_RenderState.Apply();
+
+ glDrawArrays(GL_TRIANGLE_STRIP, dt->mVertIndex, 4);
+
+ gl_RenderState.BlendEquation(GL_FUNC_ADD);
+ if (dt->mVertCount > 4)
+ {
+ gl_RenderState.SetTextureMode(TM_MASK);
+ gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ gl_RenderState.Apply();
+ glDrawArrays(GL_TRIANGLE_STRIP, dt->mVertIndex + 4, 4);
+ }
+
+ const auto &viewport = GLRenderer->mScreenViewport;
+ glScissor(viewport.left, viewport.top, viewport.width, viewport.height);
+ glDisable(GL_SCISSOR_TEST);
+ gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ gl_RenderState.SetTextureMode(TM_MODULATE);
+ break;
+ }
+
+ case DrawTypePoly:
+ {
+ DataSimplePoly *dsp = static_cast(dg);
+
+ FColormap cm;
+ cm = dsp->mColormap;
+ gl_SetColor(dsp->mLightLevel, 0, cm, 1.f);
+ gl_RenderState.SetMaterial(dsp->mTexture, CLAMP_NONE, 0, -1, false);
+ gl_RenderState.Apply();
+ glDrawArrays(GL_TRIANGLE_FAN, dsp->mVertIndex, dsp->mVertCount);
+ break;
+ }
+
+ case DrawTypeFlatFill:
+ {
+ DataFlatFill *dff = static_cast(dg);
+ gl_RenderState.SetMaterial(dff->mTexture, CLAMP_NONE, 0, -1, false);
+ gl_RenderState.Apply();
+ glDrawArrays(GL_TRIANGLE_STRIP, dg->mVertIndex, dg->mVertCount);
+ break;
+ }
+
+ case DrawTypeDim:
+ gl_RenderState.EnableTexture(false);
+ gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ gl_RenderState.AlphaFunc(GL_GREATER, 0);
+ gl_RenderState.Apply();
+ glDrawArrays(GL_TRIANGLE_FAN, dg->mVertIndex, dg->mVertCount);
+ gl_RenderState.EnableTexture(true);
+ break;
+
+ case DrawTypeLine:
+ gl_RenderState.EnableTexture(false);
+ gl_RenderState.Apply();
+ glDrawArrays(GL_LINES, dg->mVertIndex, dg->mVertCount);
+ gl_RenderState.EnableTexture(true);
+ break;
+
+ case DrawTypePixel:
+ gl_RenderState.EnableTexture(false);
+ gl_RenderState.Apply();
+ glDrawArrays(GL_POINTS, dg->mVertIndex, dg->mVertCount);
+ gl_RenderState.EnableTexture(true);
+ break;
+
+ }
+ i += dg->mLen;
+ }
+ gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
+ glset.lightmode = savedlightmode;
+}
+
+void F2DDrawer::Clear()
+{
+ mVertices.Clear();
+ mData.Clear();
+}
diff --git a/src/gl/renderer/gl_2ddrawer.h b/src/gl/renderer/gl_2ddrawer.h
new file mode 100644
index 000000000..8c27f13d8
--- /dev/null
+++ b/src/gl/renderer/gl_2ddrawer.h
@@ -0,0 +1,74 @@
+#ifndef __2DDRAWER_H
+#define __2DDRAWER_H
+
+#include "tarray.h"
+#include "gl/data/gl_vertexbuffer.h"
+
+class F2DDrawer : public FSimpleVertexBuffer
+{
+ enum EDrawType
+ {
+ DrawTypeTexture,
+ DrawTypeDim,
+ DrawTypeFlatFill,
+ DrawTypePoly,
+ DrawTypeLine,
+ DrawTypePixel
+ };
+
+ struct DataGeneric
+ {
+ EDrawType mType;
+ uint32_t mLen;
+ int mVertIndex;
+ int mVertCount;
+ };
+
+ struct DataTexture : public DataGeneric
+ {
+ FMaterial *mTexture;
+ int mScissor[4];
+ uint32_t mColorOverlay;
+ int mTranslation;
+ FRenderStyle mRenderStyle;
+ bool mMasked;
+ bool mAlphaTexture;
+ };
+
+ struct DataFlatFill : public DataGeneric
+ {
+ FMaterial *mTexture;
+ };
+
+ struct DataSimplePoly : public DataGeneric
+ {
+ FMaterial *mTexture;
+ int mLightLevel;
+ FDynamicColormap *mColormap;
+ };
+
+ TArray mVertices;
+ TArray mData;
+ int mLastLineCmd = -1; // consecutive lines can be batched into a single draw call so keep this info around.
+
+ int AddData(const DataGeneric *data);
+
+public:
+ void AddTexture(FTexture *img, DrawParms &parms);
+ void AddDim(PalEntry color, float damount, int x1, int y1, int w, int h);
+ void AddClear(int left, int top, int right, int bottom, int palcolor, uint32 color);
+ void AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin);
+
+ void AddPoly(FTexture *texture, FVector2 *points, int npoints,
+ double originx, double originy, double scalex, double scaley,
+ DAngle rotation, FDynamicColormap *colormap, int lightlevel);
+
+ void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color);
+ void AddPixel(int x1, int y1, int palcolor, uint32 color);
+
+ void Draw();
+ void Clear();
+};
+
+
+#endif
diff --git a/src/gl/renderer/gl_colormap.h b/src/gl/renderer/gl_colormap.h
new file mode 100644
index 000000000..d66950309
--- /dev/null
+++ b/src/gl/renderer/gl_colormap.h
@@ -0,0 +1,78 @@
+#ifndef __GL_COLORMAP
+#define __GL_COLORMAP
+
+#include "doomtype.h"
+#include "v_palette.h"
+#include "r_data/colormaps.h"
+
+extern DWORD gl_fixedcolormap;
+
+struct lightlist_t;
+
+enum EColorManipulation
+{
+
+ CM_INVALID=-1,
+ CM_DEFAULT=0, // untranslated
+ CM_FIRSTSPECIALCOLORMAP, // first special fixed colormap
+
+ CM_FOGLAYER = 0x10000000, // Sprite shaped fog layer
+
+ // These are not to be passed to the texture manager
+ CM_LITE = 0x20000000, // special values to handle these items without excessive hacking
+ CM_TORCH= 0x20000010, // These are not real color manipulations
+};
+
+#define CM_MAXCOLORMAP int(CM_FIRSTSPECIALCOLORMAP + SpecialColormaps.Size())
+
+ // for internal use
+struct FColormap
+{
+ PalEntry LightColor; // a is saturation (0 full, 31=b/w, other=custom colormap)
+ PalEntry FadeColor; // a is fadedensity>>1
+ int desaturation;
+ int blendfactor;
+
+ void Clear()
+ {
+ LightColor=0xffffff;
+ FadeColor=0;
+ desaturation = 0;
+ blendfactor=0;
+ }
+
+ void ClearColor()
+ {
+ LightColor.r=LightColor.g=LightColor.b=0xff;
+ blendfactor=0;
+ desaturation = 0;
+ }
+
+
+ FColormap & operator=(FDynamicColormap * from)
+ {
+ LightColor = from->Color;
+ desaturation = from->Desaturate;
+ FadeColor = from->Fade;
+ blendfactor = from->Color.a;
+ return * this;
+ }
+
+ void CopyLightColor(FDynamicColormap * from)
+ {
+ LightColor = from->Color;
+ desaturation = from->Desaturate;
+ blendfactor = from->Color.a;
+ }
+
+ void CopyFrom3DLight(lightlist_t *light);
+
+ void Decolorize() // this for 'nocoloredspritelighting' and not the same as desaturation. The normal formula results in a value that's too dark.
+ {
+ int v = (LightColor.r + LightColor.g + LightColor.b) / 3;
+ LightColor.r = LightColor.g = LightColor.b = (255 + v + v) / 3;
+ }
+};
+
+
+#endif
diff --git a/src/gl/renderer/gl_lightdata.cpp b/src/gl/renderer/gl_lightdata.cpp
new file mode 100644
index 000000000..ca2d32c41
--- /dev/null
+++ b/src/gl/renderer/gl_lightdata.cpp
@@ -0,0 +1,552 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2002-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_light.cpp
+** Light level / fog management / dynamic lights
+**
+**/
+
+#include "gl/system/gl_system.h"
+#include "gl/system/gl_interface.h"
+#include "gl/system/gl_cvars.h"
+#include "gl/data/gl_data.h"
+#include "gl/renderer/gl_colormap.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/scene/gl_portal.h"
+#include "c_dispatch.h"
+#include "p_local.h"
+#include "g_level.h"
+#include "r_sky.h"
+
+// externally settable lighting properties
+static float distfogtable[2][256]; // light to fog conversion table for black fog
+static PalEntry outsidefogcolor;
+int fogdensity;
+int outsidefogdensity;
+int skyfog;
+
+CVAR(Int, gl_weaponlight, 8, CVAR_ARCHIVE);
+CUSTOM_CVAR(Bool, gl_enhanced_nightvision, true, CVAR_ARCHIVE|CVAR_NOINITCALL)
+{
+ // The fixed colormap state needs to be reset because if this happens when
+ // a shader is set to CM_LITE or CM_TORCH it won't register the change in behavior caused by this CVAR.
+ if (GLRenderer != nullptr && GLRenderer->mShaderManager != nullptr)
+ {
+ GLRenderer->mShaderManager->ResetFixedColormap();
+ }
+}
+CVAR(Bool, gl_brightfog, false, CVAR_ARCHIVE);
+CVAR(Bool, gl_lightadditivesurfaces, false, CVAR_ARCHIVE);
+CVAR(Bool, gl_attenuate, false, CVAR_ARCHIVE);
+
+
+
+//==========================================================================
+//
+// Sets up the fog tables
+//
+//==========================================================================
+
+CUSTOM_CVAR (Int, gl_distfog, 70, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+{
+ for (int i=0;i<256;i++)
+ {
+
+ if (i<164)
+ {
+ distfogtable[0][i]= (gl_distfog>>1) + (gl_distfog)*(164-i)/164;
+ }
+ else if (i<230)
+ {
+ distfogtable[0][i]= (gl_distfog>>1) - (gl_distfog>>1)*(i-164)/(230-164);
+ }
+ else distfogtable[0][i]=0;
+
+ if (i<128)
+ {
+ distfogtable[1][i]= 6.f + (gl_distfog>>1) + (gl_distfog)*(128-i)/48;
+ }
+ else if (i<216)
+ {
+ distfogtable[1][i]= (216.f-i) / ((216.f-128.f)) * gl_distfog / 10;
+ }
+ else distfogtable[1][i]=0;
+ }
+}
+
+CUSTOM_CVAR(Int,gl_fogmode,1,CVAR_ARCHIVE|CVAR_NOINITCALL)
+{
+ if (self>2) self=2;
+ if (self<0) self=0;
+}
+
+CUSTOM_CVAR(Int, gl_lightmode, 3 ,CVAR_ARCHIVE|CVAR_NOINITCALL)
+{
+ int newself = self;
+ if (newself > 4) newself=8; // use 8 for software lighting to avoid conflicts with the bit mask
+ if (newself < 0) newself=0;
+ if (self != newself) self = newself;
+ glset.lightmode = newself;
+}
+
+
+
+
+//==========================================================================
+//
+// Sets render state to draw the given render style
+// includes: Texture mode, blend equation and blend mode
+//
+//==========================================================================
+
+void gl_GetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending,
+ int *tm, int *sb, int *db, int *be)
+{
+ static int blendstyles[] = { GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA };
+ static int renderops[] = { 0, GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+
+ int srcblend = blendstyles[style.SrcAlpha&3];
+ int dstblend = blendstyles[style.DestAlpha&3];
+ int blendequation = renderops[style.BlendOp&15];
+ int texturemode = drawopaque? TM_OPAQUE : TM_MODULATE;
+
+ if (style.Flags & STYLEF_RedIsAlpha)
+ {
+ texturemode = TM_REDTOALPHA;
+ }
+ else if (style.Flags & STYLEF_ColorIsFixed)
+ {
+ texturemode = TM_MASK;
+ }
+ else if (style.Flags & STYLEF_InvertSource)
+ {
+ // The only place where InvertSource is used is for inverted sprites with the infrared powerup.
+ texturemode = TM_INVERSE;
+ }
+
+ if (blendequation == -1)
+ {
+ srcblend = GL_DST_COLOR;
+ dstblend = GL_ONE_MINUS_SRC_ALPHA;
+ blendequation = GL_FUNC_ADD;
+ }
+
+ if (allowcolorblending && srcblend == GL_SRC_ALPHA && dstblend == GL_ONE && blendequation == GL_FUNC_ADD)
+ {
+ srcblend = GL_SRC_COLOR;
+ }
+
+ *tm = texturemode;
+ *be = blendequation;
+ *sb = srcblend;
+ *db = dstblend;
+}
+
+
+//==========================================================================
+//
+// Set fog parameters for the level
+//
+//==========================================================================
+void gl_SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog)
+{
+ fogdensity=_fogdensity;
+ outsidefogcolor=_outsidefogcolor;
+ outsidefogdensity=_outsidefogdensity;
+ skyfog=_skyfog;
+
+ outsidefogdensity>>=1;
+ fogdensity>>=1;
+}
+
+
+//==========================================================================
+//
+// Get current light level
+//
+//==========================================================================
+
+int gl_CalcLightLevel(int lightlevel, int rellight, bool weapon)
+{
+ int light;
+
+ if (lightlevel == 0) return 0;
+
+ if ((glset.lightmode & 2) && lightlevel < 192 && !weapon)
+ {
+ if (lightlevel > 100)
+ {
+ light = xs_CRoundToInt(192.f - (192 - lightlevel)* 1.87f);
+ if (light + rellight < 20)
+ {
+ light = 20 + (light + rellight - 20) / 5;
+ }
+ else
+ {
+ light += rellight;
+ }
+ }
+ else
+ {
+ light = (lightlevel + rellight) / 5;
+ }
+
+ }
+ else
+ {
+ light=lightlevel+rellight;
+ }
+
+ return clamp(light, 0, 255);
+}
+
+//==========================================================================
+//
+// Get current light color
+//
+//==========================================================================
+
+static PalEntry gl_CalcLightColor(int light, PalEntry pe, int blendfactor)
+{
+ int r,g,b;
+
+ if (glset.lightmode == 8)
+ {
+ return pe;
+ }
+ else if (blendfactor == 0)
+ {
+ r = pe.r * light / 255;
+ g = pe.g * light / 255;
+ b = pe.b * light / 255;
+ }
+ else
+ {
+ // This is what Legacy does with colored light in 3D volumes. No, it doesn't really make sense...
+ // It also doesn't translate well to software style lighting.
+ int mixlight = light * (255 - blendfactor);
+
+ r = (mixlight + pe.r * blendfactor) / 255;
+ g = (mixlight + pe.g * blendfactor) / 255;
+ b = (mixlight + pe.b * blendfactor) / 255;
+ }
+ return PalEntry(255, BYTE(r), BYTE(g), BYTE(b));
+}
+
+//==========================================================================
+//
+// set current light color
+//
+//==========================================================================
+void gl_SetColor(int sectorlightlevel, int rellight, const FColormap &cm, float alpha, bool weapon)
+{
+ if (gl_fixedcolormap != CM_DEFAULT)
+ {
+ gl_RenderState.SetColorAlpha(0xffffff, alpha, 0);
+ gl_RenderState.SetSoftLightLevel(255);
+ }
+ else
+ {
+ int hwlightlevel = gl_CalcLightLevel(sectorlightlevel, rellight, weapon);
+ PalEntry pe = gl_CalcLightColor(hwlightlevel, cm.LightColor, cm.blendfactor);
+ gl_RenderState.SetColorAlpha(pe, alpha, cm.desaturation);
+ gl_RenderState.SetSoftLightLevel(gl_ClampLight(sectorlightlevel + rellight));
+ }
+}
+
+//==========================================================================
+//
+// calculates the current fog density
+//
+// Rules for fog:
+//
+// 1. If bit 4 of gl_lightmode is set always use the level's fog density.
+// This is what Legacy's GL render does.
+// 2. black fog means no fog and always uses the distfogtable based on the level's fog density setting
+// 3. If outside fog is defined and the current fog color is the same as the outside fog
+// the engine always uses the outside fog density to make the fog uniform across the level.
+// If the outside fog's density is undefined it uses the level's fog density and if that is
+// not defined it uses a default of 70.
+// 4. If a global fog density is specified it is being used for all fog on the level
+// 5. If none of the above apply fog density is based on the light level as for the software renderer.
+//
+//==========================================================================
+
+float gl_GetFogDensity(int lightlevel, PalEntry fogcolor)
+{
+ float density;
+
+ if (glset.lightmode&4)
+ {
+ // uses approximations of Legacy's default settings.
+ density = fogdensity? fogdensity : 18;
+ }
+ else if ((fogcolor.d & 0xffffff) == 0)
+ {
+ // case 1: black fog
+ if (glset.lightmode != 8)
+ {
+ density=distfogtable[glset.lightmode!=0][gl_ClampLight(lightlevel)];
+ }
+ else
+ {
+ density = 0;
+ }
+ }
+ else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff))
+ {
+ // case 2. outsidefogdensity has already been set as needed
+ density=outsidefogdensity;
+ }
+ else if (fogdensity!=0)
+ {
+ // case 3: level has fog density set
+ density=fogdensity;
+ }
+ else if (lightlevel < 248)
+ {
+ // case 4: use light level
+ density=clamp(255-lightlevel,30,255);
+ }
+ else
+ {
+ density = 0.f;
+ }
+ return density;
+}
+
+
+//==========================================================================
+//
+// Check fog by current lighting info
+//
+//==========================================================================
+
+bool gl_CheckFog(FColormap *cm, int lightlevel)
+{
+ // Check for fog boundaries. This needs a few more checks for the sectors
+ bool frontfog;
+
+ PalEntry fogcolor = cm->FadeColor;
+
+ if ((fogcolor.d & 0xffffff) == 0)
+ {
+ frontfog = false;
+ }
+ else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff))
+ {
+ frontfog = true;
+ }
+ else if (fogdensity!=0 || (glset.lightmode & 4))
+ {
+ // case 3: level has fog density set
+ frontfog = true;
+ }
+ else
+ {
+ // case 4: use light level
+ frontfog = lightlevel < 248;
+ }
+ return frontfog;
+}
+
+//==========================================================================
+//
+// Check if the current linedef is a candidate for a fog boundary
+//
+// Requirements for a fog boundary:
+// - front sector has no fog
+// - back sector has fog
+// - at least one of both does not have a sky ceiling.
+//
+//==========================================================================
+
+bool gl_CheckFog(sector_t *frontsector, sector_t *backsector)
+{
+ if (gl_fixedcolormap) return false;
+ if (frontsector == backsector) return false; // there can't be a boundary if both sides are in the same sector.
+
+ // Check for fog boundaries. This needs a few more checks for the sectors
+
+ PalEntry fogcolor = frontsector->ColorMap->Fade;
+
+ if ((fogcolor.d & 0xffffff) == 0)
+ {
+ return false;
+ }
+ else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff))
+ {
+ }
+ else if (fogdensity!=0 || (glset.lightmode & 4))
+ {
+ // case 3: level has fog density set
+ }
+ else
+ {
+ // case 4: use light level
+ if (frontsector->lightlevel >= 248) return false;
+ }
+
+ fogcolor = backsector->ColorMap->Fade;
+
+ if ((fogcolor.d & 0xffffff) == 0)
+ {
+ }
+ else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff))
+ {
+ return false;
+ }
+ else if (fogdensity!=0 || (glset.lightmode & 4))
+ {
+ // case 3: level has fog density set
+ return false;
+ }
+ else
+ {
+ // case 4: use light level
+ if (backsector->lightlevel < 248) return false;
+ }
+
+ // in all other cases this might create more problems than it solves.
+ return ((frontsector->GetTexture(sector_t::ceiling)!=skyflatnum ||
+ backsector->GetTexture(sector_t::ceiling)!=skyflatnum));
+}
+
+//==========================================================================
+//
+// Lighting stuff
+//
+//==========================================================================
+
+void gl_SetShaderLight(float level, float olight)
+{
+ const float MAXDIST = 256.f;
+ const float THRESHOLD = 96.f;
+ const float FACTOR = 0.75f;
+
+ if (level > 0)
+ {
+ float lightdist, lightfactor;
+
+ if (olight < THRESHOLD)
+ {
+ lightdist = (MAXDIST/2) + (olight * MAXDIST / THRESHOLD / 2);
+ olight = THRESHOLD;
+ }
+ else lightdist = MAXDIST;
+
+ lightfactor = 1.f + ((olight/level) - 1.f) * FACTOR;
+ if (lightfactor == 1.f) lightdist = 0.f; // save some code in the shader
+ gl_RenderState.SetLightParms(lightfactor, lightdist);
+ }
+ else
+ {
+ gl_RenderState.SetLightParms(1.f, 0.f);
+ }
+}
+
+
+//==========================================================================
+//
+// Sets the fog for the current polygon
+//
+//==========================================================================
+
+void gl_SetFog(int lightlevel, int rellight, const FColormap *cmap, bool isadditive)
+{
+ PalEntry fogcolor;
+ float fogdensity;
+
+ if (level.flags&LEVEL_HASFADETABLE)
+ {
+ fogdensity=70;
+ fogcolor=0x808080;
+ }
+ else if (cmap != NULL && gl_fixedcolormap == 0)
+ {
+ fogcolor = cmap->FadeColor;
+ fogdensity = gl_GetFogDensity(lightlevel, fogcolor);
+ fogcolor.a=0;
+ }
+ else
+ {
+ fogcolor = 0;
+ fogdensity = 0;
+ }
+
+ // Make fog a little denser when inside a skybox
+ if (GLPortal::inskybox) fogdensity+=fogdensity/2;
+
+
+ // no fog in enhanced vision modes!
+ if (fogdensity==0 || gl_fogmode == 0)
+ {
+ gl_RenderState.EnableFog(false);
+ gl_RenderState.SetFog(0,0);
+ }
+ else
+ {
+ if (glset.lightmode == 2 && fogcolor == 0)
+ {
+ float light = gl_CalcLightLevel(lightlevel, rellight, false);
+ gl_SetShaderLight(light, lightlevel);
+ }
+ else
+ {
+ gl_RenderState.SetLightParms(1.f, 0.f);
+ }
+
+ // For additive rendering using the regular fog color here would mean applying it twice
+ // so always use black
+ if (isadditive)
+ {
+ fogcolor=0;
+ }
+
+ gl_RenderState.EnableFog(true);
+ gl_RenderState.SetFog(fogcolor, fogdensity);
+
+ // Korshun: fullbright fog like in software renderer.
+ if (glset.lightmode == 8 && glset.brightfog && fogdensity != 0 && fogcolor != 0)
+ {
+ gl_RenderState.SetSoftLightLevel(255);
+ }
+ }
+}
+
+
+//==========================================================================
+//
+// For testing sky fog sheets
+//
+//==========================================================================
+CCMD(skyfog)
+{
+ if (argv.argc()>1)
+ {
+ skyfog=strtol(argv[1],NULL,0);
+ }
+}
+
diff --git a/src/gl/renderer/gl_lightdata.h b/src/gl/renderer/gl_lightdata.h
new file mode 100644
index 000000000..4cc7a7d04
--- /dev/null
+++ b/src/gl/renderer/gl_lightdata.h
@@ -0,0 +1,64 @@
+#ifndef __GL_LIGHTDATA
+#define __GL_LIGHTDATA
+
+#include "v_palette.h"
+#include "p_3dfloors.h"
+#include "r_data/renderstyle.h"
+#include "gl/renderer/gl_colormap.h"
+
+inline int gl_ClampLight(int lightlevel)
+{
+ return clamp(lightlevel, 0, 255);
+}
+
+void gl_GetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending,
+ int *tm, int *sb, int *db, int *be);
+void gl_SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog);
+
+int gl_CalcLightLevel(int lightlevel, int rellight, bool weapon);
+void gl_SetColor(int light, int rellight, const FColormap &cm, float alpha, bool weapon=false);
+
+float gl_GetFogDensity(int lightlevel, PalEntry fogcolor);
+struct sector_t;
+bool gl_CheckFog(FColormap *cm, int lightlevel);
+bool gl_CheckFog(sector_t *frontsector, sector_t *backsector);
+
+void gl_SetFog(int lightlevel, int rellight, const FColormap *cm, bool isadditive);
+
+inline bool gl_isBlack(PalEntry color)
+{
+ return color.r + color.g + color.b == 0;
+}
+
+inline bool gl_isWhite(PalEntry color)
+{
+ return color.r + color.g + color.b == 3*0xff;
+}
+
+extern DWORD gl_fixedcolormap;
+
+inline bool gl_isFullbright(PalEntry color, int lightlevel)
+{
+ return gl_fixedcolormap || (gl_isWhite(color) && lightlevel==255);
+}
+
+void gl_DeleteAllAttachedLights();
+void gl_RecreateAllAttachedLights();
+
+extern int fogdensity;
+extern int outsidefogdensity;
+extern int skyfog;
+
+
+inline void FColormap::CopyFrom3DLight(lightlist_t *light)
+{
+ LightColor = light->extra_colormap->Color;
+ desaturation = light->extra_colormap->Desaturate;
+ blendfactor = light->extra_colormap->Color.a;
+ if (light->caster && (light->caster->flags&FF_FADEWALLS) && (light->extra_colormap->Fade & 0xffffff) != 0)
+ FadeColor = light->extra_colormap->Fade;
+}
+
+
+
+#endif
\ No newline at end of file
diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp
new file mode 100644
index 000000000..f35071854
--- /dev/null
+++ b/src/gl/renderer/gl_postprocess.cpp
@@ -0,0 +1,851 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2016 Magnus Norddahl
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_postprocess.cpp
+** Post processing effects in the render pipeline
+**
+*/
+
+#include "gl/system/gl_system.h"
+#include "gi.h"
+#include "m_png.h"
+#include "m_random.h"
+#include "st_stuff.h"
+#include "dobject.h"
+#include "doomstat.h"
+#include "g_level.h"
+#include "r_data/r_interpolate.h"
+#include "r_utility.h"
+#include "d_player.h"
+#include "p_effect.h"
+#include "sbar.h"
+#include "po_man.h"
+#include "r_utility.h"
+#include "p_local.h"
+#include "colormatcher.h"
+#include "gl/gl_functions.h"
+#include "gl/system/gl_interface.h"
+#include "gl/system/gl_framebuffer.h"
+#include "gl/system/gl_cvars.h"
+#include "gl/system/gl_debug.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/renderer/gl_renderbuffers.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_postprocessstate.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/shaders/gl_ambientshader.h"
+#include "gl/shaders/gl_bloomshader.h"
+#include "gl/shaders/gl_blurshader.h"
+#include "gl/shaders/gl_tonemapshader.h"
+#include "gl/shaders/gl_colormapshader.h"
+#include "gl/shaders/gl_lensshader.h"
+#include "gl/shaders/gl_fxaashader.h"
+#include "gl/shaders/gl_presentshader.h"
+#include "gl/renderer/gl_2ddrawer.h"
+#include "gl/stereo3d/gl_stereo3d.h"
+
+//==========================================================================
+//
+// CVARs
+//
+//==========================================================================
+CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE);
+CUSTOM_CVAR(Float, gl_bloom_amount, 1.4f, CVAR_ARCHIVE)
+{
+ if (self < 0.1f) self = 0.1f;
+}
+
+CVAR(Float, gl_exposure_scale, 1.3f, CVAR_ARCHIVE)
+CVAR(Float, gl_exposure_min, 0.35f, CVAR_ARCHIVE)
+CVAR(Float, gl_exposure_base, 0.35f, CVAR_ARCHIVE)
+CVAR(Float, gl_exposure_speed, 0.05f, CVAR_ARCHIVE)
+
+CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE)
+{
+ if (self < 0 || self > 5)
+ self = 0;
+}
+
+CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, CVAR_ARCHIVE)
+{
+ if (self < 3 || self > 15 || self % 2 == 0)
+ self = 7;
+}
+
+CVAR(Bool, gl_lens, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+
+CVAR(Float, gl_lens_k, -0.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+CVAR(Float, gl_lens_kcube, 0.1f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+CVAR(Float, gl_lens_chromatic, 1.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+
+CUSTOM_CVAR(Int, gl_fxaa, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+{
+ if (self < 0 || self >= FFXAAShader::Count)
+ {
+ self = 0;
+ }
+}
+
+CUSTOM_CVAR(Int, gl_ssao, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+{
+ if (self < 0 || self > 3)
+ self = 0;
+}
+
+CUSTOM_CVAR(Int, gl_ssao_portals, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+{
+ if (self < 0)
+ self = 0;
+}
+
+CVAR(Float, gl_ssao_strength, 0.7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+CVAR(Int, gl_ssao_debug, 0, 0)
+CVAR(Float, gl_ssao_bias, 0.2f, 0)
+CVAR(Float, gl_ssao_radius, 80.0f, 0)
+CUSTOM_CVAR(Float, gl_ssao_blur, 16.0f, 0)
+{
+ if (self < 0.1f) self = 0.1f;
+}
+
+CUSTOM_CVAR(Float, gl_ssao_exponent, 1.8f, 0)
+{
+ if (self < 0.1f) self = 0.1f;
+}
+
+EXTERN_CVAR(Float, vid_brightness)
+EXTERN_CVAR(Float, vid_contrast)
+
+
+void FGLRenderer::RenderScreenQuad()
+{
+ mVBO->BindVBO();
+ gl_RenderState.ResetVertexBuffer();
+ GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 4);
+}
+
+void FGLRenderer::PostProcessScene()
+{
+ mBuffers->BlitSceneToTexture();
+ UpdateCameraExposure();
+ BloomScene();
+ TonemapScene();
+ ColormapScene();
+ LensDistortScene();
+ ApplyFXAA();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Adds ambient occlusion to the scene
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::AmbientOccludeScene()
+{
+ FGLDebug::PushGroup("AmbientOccludeScene");
+
+ FGLPostProcessState savedState;
+ savedState.SaveTextureBindings(3);
+
+ float bias = gl_ssao_bias;
+ float aoRadius = gl_ssao_radius;
+ const float blurAmount = gl_ssao_blur;
+ float aoStrength = gl_ssao_strength;
+
+ //float tanHalfFovy = tan(fovy * (M_PI / 360.0f));
+ float tanHalfFovy = 1.0f / gl_RenderState.mProjectionMatrix.get()[5];
+ float invFocalLenX = tanHalfFovy * (mBuffers->GetSceneWidth() / (float)mBuffers->GetSceneHeight());
+ float invFocalLenY = tanHalfFovy;
+ float nDotVBias = clamp(bias, 0.0f, 1.0f);
+ float r2 = aoRadius * aoRadius;
+
+ float blurSharpness = 1.0f / blurAmount;
+
+ float sceneScaleX = mSceneViewport.width / (float)mScreenViewport.width;
+ float sceneScaleY = mSceneViewport.height / (float)mScreenViewport.height;
+ float sceneOffsetX = mSceneViewport.left / (float)mScreenViewport.width;
+ float sceneOffsetY = mSceneViewport.top / (float)mScreenViewport.height;
+
+ int randomTexture = clamp(gl_ssao - 1, 0, FGLRenderBuffers::NumAmbientRandomTextures - 1);
+
+ // Calculate linear depth values
+ glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->LinearDepthFB);
+ glViewport(0, 0, mBuffers->AmbientWidth, mBuffers->AmbientHeight);
+ mBuffers->BindSceneDepthTexture(0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ mBuffers->BindSceneColorTexture(1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glActiveTexture(GL_TEXTURE0);
+ mLinearDepthShader->Bind();
+ mLinearDepthShader->DepthTexture.Set(0);
+ mLinearDepthShader->ColorTexture.Set(1);
+ if (gl_multisample > 1) mLinearDepthShader->SampleIndex.Set(0);
+ mLinearDepthShader->LinearizeDepthA.Set(1.0f / GetZFar() - 1.0f / GetZNear());
+ mLinearDepthShader->LinearizeDepthB.Set(MAX(1.0f / GetZNear(), 1.e-8f));
+ mLinearDepthShader->InverseDepthRangeA.Set(1.0f);
+ mLinearDepthShader->InverseDepthRangeB.Set(0.0f);
+ mLinearDepthShader->Scale.Set(sceneScaleX, sceneScaleY);
+ mLinearDepthShader->Offset.Set(sceneOffsetX, sceneOffsetY);
+ RenderScreenQuad();
+
+ // Apply ambient occlusion
+ glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB1);
+ glBindTexture(GL_TEXTURE_2D, mBuffers->LinearDepthTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientRandomTexture[randomTexture]);
+ 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_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ mBuffers->BindSceneNormalTexture(2);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glActiveTexture(GL_TEXTURE0);
+ mSSAOShader->Bind();
+ mSSAOShader->DepthTexture.Set(0);
+ mSSAOShader->RandomTexture.Set(1);
+ mSSAOShader->NormalTexture.Set(2);
+ if (gl_multisample > 1) mSSAOShader->SampleIndex.Set(0);
+ mSSAOShader->UVToViewA.Set(2.0f * invFocalLenX, 2.0f * invFocalLenY);
+ mSSAOShader->UVToViewB.Set(-invFocalLenX, -invFocalLenY);
+ mSSAOShader->InvFullResolution.Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight);
+ mSSAOShader->NDotVBias.Set(nDotVBias);
+ mSSAOShader->NegInvR2.Set(-1.0f / r2);
+ mSSAOShader->RadiusToScreen.Set(aoRadius * 0.5 / tanHalfFovy * mBuffers->AmbientHeight);
+ mSSAOShader->AOMultiplier.Set(1.0f / (1.0f - nDotVBias));
+ mSSAOShader->AOStrength.Set(aoStrength);
+ mSSAOShader->Scale.Set(sceneScaleX, sceneScaleY);
+ mSSAOShader->Offset.Set(sceneOffsetX, sceneOffsetY);
+ RenderScreenQuad();
+
+ // Blur SSAO texture
+ if (gl_ssao_debug < 2)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0);
+ glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ mDepthBlurShader->Bind(false);
+ mDepthBlurShader->BlurSharpness[false].Set(blurSharpness);
+ mDepthBlurShader->InvFullResolution[false].Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight);
+ RenderScreenQuad();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB1);
+ glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture0);
+ mDepthBlurShader->Bind(true);
+ mDepthBlurShader->BlurSharpness[true].Set(blurSharpness);
+ mDepthBlurShader->InvFullResolution[true].Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight);
+ mDepthBlurShader->PowExponent[true].Set(gl_ssao_exponent);
+ RenderScreenQuad();
+ }
+
+ // Add SSAO back to scene texture:
+ mBuffers->BindSceneFB(false);
+ glViewport(mSceneViewport.left, mSceneViewport.top, mSceneViewport.width, mSceneViewport.height);
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ if (gl_ssao_debug != 0)
+ {
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ mBuffers->BindSceneFogTexture(1);
+ mSSAOCombineShader->Bind();
+ mSSAOCombineShader->AODepthTexture.Set(0);
+ mSSAOCombineShader->SceneFogTexture.Set(1);
+ if (gl_multisample > 1) mSSAOCombineShader->SampleCount.Set(gl_multisample);
+ mSSAOCombineShader->Scale.Set(sceneScaleX, sceneScaleY);
+ mSSAOCombineShader->Offset.Set(sceneOffsetX, sceneOffsetY);
+ RenderScreenQuad();
+
+ FGLDebug::PopGroup();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Extracts light average from the scene and updates the camera exposure texture
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::UpdateCameraExposure()
+{
+ if (!gl_bloom && gl_tonemap == 0)
+ return;
+
+ FGLDebug::PushGroup("UpdateCameraExposure");
+
+ FGLPostProcessState savedState;
+ savedState.SaveTextureBindings(2);
+
+ // Extract light level from scene texture:
+ const auto &level0 = mBuffers->ExposureLevels[0];
+ glBindFramebuffer(GL_FRAMEBUFFER, level0.Framebuffer);
+ glViewport(0, 0, level0.Width, level0.Height);
+ mBuffers->BindCurrentTexture(0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ mExposureExtractShader->Bind();
+ mExposureExtractShader->SceneTexture.Set(0);
+ mExposureExtractShader->Scale.Set(mSceneViewport.width / (float)mScreenViewport.width, mSceneViewport.height / (float)mScreenViewport.height);
+ mExposureExtractShader->Offset.Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height);
+ RenderScreenQuad();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ // Find the average value:
+ for (int i = 0; i + 1 < mBuffers->ExposureLevels.Size(); i++)
+ {
+ const auto &level = mBuffers->ExposureLevels[i];
+ const auto &next = mBuffers->ExposureLevels[i + 1];
+
+ glBindFramebuffer(GL_FRAMEBUFFER, next.Framebuffer);
+ glViewport(0, 0, next.Width, next.Height);
+ glBindTexture(GL_TEXTURE_2D, level.Texture);
+ mExposureAverageShader->Bind();
+ mExposureAverageShader->ExposureTexture.Set(0);
+ RenderScreenQuad();
+ }
+
+ // Combine average value with current camera exposure:
+ glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->ExposureFB);
+ glViewport(0, 0, 1, 1);
+ if (!mBuffers->FirstExposureFrame)
+ {
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ else
+ {
+ mBuffers->FirstExposureFrame = false;
+ }
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mBuffers->ExposureLevels.Last().Texture);
+ mExposureCombineShader->Bind();
+ mExposureCombineShader->ExposureTexture.Set(0);
+ mExposureCombineShader->ExposureBase.Set(gl_exposure_base);
+ mExposureCombineShader->ExposureMin.Set(gl_exposure_min);
+ mExposureCombineShader->ExposureScale.Set(gl_exposure_scale);
+ mExposureCombineShader->ExposureSpeed.Set(gl_exposure_speed);
+ RenderScreenQuad();
+ glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
+
+ FGLDebug::PopGroup();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Adds bloom contribution to scene texture
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::BloomScene()
+{
+ // Only bloom things if enabled and no special fixed light mode is active
+ if (!gl_bloom || gl_fixedcolormap != CM_DEFAULT || gl_ssao_debug)
+ return;
+
+ FGLDebug::PushGroup("BloomScene");
+
+ FGLPostProcessState savedState;
+ savedState.SaveTextureBindings(2);
+
+ const float blurAmount = gl_bloom_amount;
+ int sampleCount = gl_bloom_kernel_size;
+
+ const auto &level0 = mBuffers->BloomLevels[0];
+
+ // Extract blooming pixels from scene texture:
+ glBindFramebuffer(GL_FRAMEBUFFER, level0.VFramebuffer);
+ glViewport(0, 0, level0.Width, level0.Height);
+ mBuffers->BindCurrentTexture(0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mBuffers->ExposureTexture);
+ glActiveTexture(GL_TEXTURE0);
+ mBloomExtractShader->Bind();
+ mBloomExtractShader->SceneTexture.Set(0);
+ mBloomExtractShader->ExposureTexture.Set(1);
+ mBloomExtractShader->Scale.Set(mSceneViewport.width / (float)mScreenViewport.width, mSceneViewport.height / (float)mScreenViewport.height);
+ mBloomExtractShader->Offset.Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height);
+ RenderScreenQuad();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ // Blur and downscale:
+ for (int i = 0; i < FGLRenderBuffers::NumBloomLevels - 1; i++)
+ {
+ const auto &level = mBuffers->BloomLevels[i];
+ const auto &next = mBuffers->BloomLevels[i + 1];
+ mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height);
+ mBlurShader->BlurVertical(this, blurAmount, sampleCount, level.HTexture, next.VFramebuffer, next.Width, next.Height);
+ }
+
+ // Blur and upscale:
+ for (int i = FGLRenderBuffers::NumBloomLevels - 1; i > 0; i--)
+ {
+ const auto &level = mBuffers->BloomLevels[i];
+ const auto &next = mBuffers->BloomLevels[i - 1];
+
+ mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height);
+ mBlurShader->BlurVertical(this, blurAmount, sampleCount, level.HTexture, level.VFramebuffer, level.Width, level.Height);
+
+ // Linear upscale:
+ glBindFramebuffer(GL_FRAMEBUFFER, next.VFramebuffer);
+ glViewport(0, 0, next.Width, next.Height);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, level.VTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ mBloomCombineShader->Bind();
+ mBloomCombineShader->BloomTexture.Set(0);
+ RenderScreenQuad();
+ }
+
+ mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height);
+ mBlurShader->BlurVertical(this, blurAmount, sampleCount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height);
+
+ // Add bloom back to scene texture:
+ mBuffers->BindCurrentFB();
+ glViewport(mSceneViewport.left, mSceneViewport.top, mSceneViewport.width, mSceneViewport.height);
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_ONE, GL_ONE);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, level0.VTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ mBloomCombineShader->Bind();
+ mBloomCombineShader->BloomTexture.Set(0);
+ RenderScreenQuad();
+ glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
+
+ FGLDebug::PopGroup();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Tonemap scene texture and place the result in the HUD/2D texture
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::TonemapScene()
+{
+ if (gl_tonemap == 0)
+ return;
+
+ FGLDebug::PushGroup("TonemapScene");
+
+ CreateTonemapPalette();
+
+ FGLPostProcessState savedState;
+ savedState.SaveTextureBindings(2);
+
+ mBuffers->BindNextFB();
+ mBuffers->BindCurrentTexture(0);
+ mTonemapShader->Bind();
+ mTonemapShader->SceneTexture.Set(0);
+
+ if (mTonemapShader->IsPaletteMode())
+ {
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mTonemapPalette->GetTextureHandle(0));
+ 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);
+ glActiveTexture(GL_TEXTURE0);
+
+ mTonemapShader->PaletteLUT.Set(1);
+ }
+ else
+ {
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mBuffers->ExposureTexture);
+ glActiveTexture(GL_TEXTURE0);
+
+ mTonemapShader->ExposureTexture.Set(1);
+ }
+
+ RenderScreenQuad();
+ mBuffers->NextTexture();
+
+ FGLDebug::PopGroup();
+}
+
+void FGLRenderer::CreateTonemapPalette()
+{
+ if (!mTonemapPalette)
+ {
+ TArray lut;
+ lut.Resize(512 * 512 * 4);
+ for (int r = 0; r < 64; r++)
+ {
+ for (int g = 0; g < 64; g++)
+ {
+ for (int b = 0; b < 64; b++)
+ {
+ PalEntry color = GPalette.BaseColors[(BYTE)PTM_BestColor((uint32 *)GPalette.BaseColors, (r << 2) | (r >> 4), (g << 2) | (g >> 4), (b << 2) | (b >> 4), 0, 256)];
+ int index = ((r * 64 + g) * 64 + b) * 4;
+ lut[index] = color.r;
+ lut[index + 1] = color.g;
+ lut[index + 2] = color.b;
+ lut[index + 3] = 255;
+ }
+ }
+ }
+
+ mTonemapPalette = new FHardwareTexture(512, 512, true);
+ mTonemapPalette->CreateTexture(&lut[0], 512, 512, 0, false, 0, "mTonemapPalette");
+ }
+}
+
+void FGLRenderer::ClearTonemapPalette()
+{
+ delete mTonemapPalette;
+ mTonemapPalette = nullptr;
+}
+
+//-----------------------------------------------------------------------------
+//
+// Colormap scene texture and place the result in the HUD/2D texture
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::ColormapScene()
+{
+ if (gl_fixedcolormap < CM_FIRSTSPECIALCOLORMAP || gl_fixedcolormap >= CM_MAXCOLORMAP)
+ return;
+
+ FGLDebug::PushGroup("ColormapScene");
+
+ FGLPostProcessState savedState;
+
+ mBuffers->BindNextFB();
+ mBuffers->BindCurrentTexture(0);
+ mColormapShader->Bind();
+
+ FSpecialColormap *scm = &SpecialColormaps[gl_fixedcolormap - CM_FIRSTSPECIALCOLORMAP];
+ float m[] = { scm->ColorizeEnd[0] - scm->ColorizeStart[0],
+ scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f };
+
+ mColormapShader->MapStart.Set(scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], 0.f);
+ mColormapShader->MapRange.Set(m);
+
+ RenderScreenQuad();
+ mBuffers->NextTexture();
+
+ FGLDebug::PopGroup();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Apply lens distortion and place the result in the HUD/2D texture
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::LensDistortScene()
+{
+ if (gl_lens == 0)
+ return;
+
+ FGLDebug::PushGroup("LensDistortScene");
+
+ float k[4] =
+ {
+ gl_lens_k,
+ gl_lens_k * gl_lens_chromatic,
+ gl_lens_k * gl_lens_chromatic * gl_lens_chromatic,
+ 0.0f
+ };
+ float kcube[4] =
+ {
+ gl_lens_kcube,
+ gl_lens_kcube * gl_lens_chromatic,
+ gl_lens_kcube * gl_lens_chromatic * gl_lens_chromatic,
+ 0.0f
+ };
+
+ float aspect = mSceneViewport.width / (float)mSceneViewport.height;
+
+ // Scale factor to keep sampling within the input texture
+ float r2 = aspect * aspect * 0.25 + 0.25f;
+ float sqrt_r2 = sqrt(r2);
+ float f0 = 1.0f + MAX(r2 * (k[0] + kcube[0] * sqrt_r2), 0.0f);
+ float f2 = 1.0f + MAX(r2 * (k[2] + kcube[2] * sqrt_r2), 0.0f);
+ float f = MAX(f0, f2);
+ float scale = 1.0f / f;
+
+ FGLPostProcessState savedState;
+
+ mBuffers->BindNextFB();
+ mBuffers->BindCurrentTexture(0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ mLensShader->Bind();
+ mLensShader->InputTexture.Set(0);
+ mLensShader->AspectRatio.Set(aspect);
+ mLensShader->Scale.Set(scale);
+ mLensShader->LensDistortionCoefficient.Set(k);
+ mLensShader->CubicDistortionValue.Set(kcube);
+ RenderScreenQuad();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ mBuffers->NextTexture();
+
+ FGLDebug::PopGroup();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Apply FXAA and place the result in the HUD/2D texture
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::ApplyFXAA()
+{
+ if (0 == gl_fxaa)
+ {
+ return;
+ }
+
+ FGLDebug::PushGroup("ApplyFXAA");
+
+ const GLfloat rpcRes[2] =
+ {
+ 1.0f / mBuffers->GetWidth(),
+ 1.0f / mBuffers->GetHeight()
+ };
+
+ FGLPostProcessState savedState;
+
+ mBuffers->BindNextFB();
+ mBuffers->BindCurrentTexture(0);
+ mFXAALumaShader->Bind();
+ mFXAALumaShader->InputTexture.Set(0);
+ RenderScreenQuad();
+ mBuffers->NextTexture();
+
+ mBuffers->BindNextFB();
+ mBuffers->BindCurrentTexture(0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ mFXAAShader->Bind();
+ mFXAAShader->InputTexture.Set(0);
+ mFXAAShader->ReciprocalResolution.Set(rpcRes);
+ RenderScreenQuad();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ mBuffers->NextTexture();
+
+ FGLDebug::PopGroup();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Copies the rendered screen to its final destination
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::Flush()
+{
+ const s3d::Stereo3DMode& stereo3dMode = s3d::Stereo3DMode::getCurrentMode();
+
+ if (stereo3dMode.IsMono() || !FGLRenderBuffers::IsEnabled())
+ {
+ CopyToBackbuffer(nullptr, true);
+ }
+ else
+ {
+ // Render 2D to eye textures
+ for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix)
+ {
+ FGLDebug::PushGroup("Eye2D");
+ mBuffers->BindEyeFB(eye_ix);
+ glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
+ glScissor(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
+ m2DDrawer->Draw();
+ FGLDebug::PopGroup();
+ }
+ m2DDrawer->Clear();
+
+ FGLPostProcessState savedState;
+ FGLDebug::PushGroup("PresentEyes");
+ stereo3dMode.Present();
+ FGLDebug::PopGroup();
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Gamma correct while copying to frame buffer
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma)
+{
+ m2DDrawer->Draw(); // draw all pending 2D stuff before copying the buffer
+ m2DDrawer->Clear();
+
+ FGLDebug::PushGroup("CopyToBackbuffer");
+ if (FGLRenderBuffers::IsEnabled())
+ {
+ FGLPostProcessState savedState;
+ mBuffers->BindOutputFB();
+
+ GL_IRECT box;
+ if (bounds)
+ {
+ box = *bounds;
+ }
+ else
+ {
+ ClearBorders();
+ box = mOutputLetterbox;
+ }
+
+ mBuffers->BindCurrentTexture(0);
+ DrawPresentTexture(box, applyGamma);
+ }
+ else if (!bounds)
+ {
+ FGLPostProcessState savedState;
+ ClearBorders();
+ }
+ FGLDebug::PopGroup();
+}
+
+void FGLRenderer::DrawPresentTexture(const GL_IRECT &box, bool applyGamma)
+{
+ glViewport(box.left, box.top, box.width, box.height);
+
+ glActiveTexture(GL_TEXTURE0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ mPresentShader->Bind();
+ mPresentShader->InputTexture.Set(0);
+ if (!applyGamma || framebuffer->IsHWGammaActive())
+ {
+ mPresentShader->InvGamma.Set(1.0f);
+ mPresentShader->Contrast.Set(1.0f);
+ mPresentShader->Brightness.Set(0.0f);
+ }
+ else
+ {
+ mPresentShader->InvGamma.Set(1.0f / clamp(Gamma, 0.1f, 4.f));
+ mPresentShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f));
+ mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f));
+ }
+ mPresentShader->Scale.Set(mScreenViewport.width / (float)mBuffers->GetWidth(), mScreenViewport.height / (float)mBuffers->GetHeight());
+ RenderScreenQuad();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Fills the black bars around the screen letterbox
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::ClearBorders()
+{
+ const auto &box = mOutputLetterbox;
+
+ int clientWidth = framebuffer->GetClientWidth();
+ int clientHeight = framebuffer->GetClientHeight();
+ if (clientWidth == 0 || clientHeight == 0)
+ return;
+
+ glViewport(0, 0, clientWidth, clientHeight);
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glEnable(GL_SCISSOR_TEST);
+ if (box.top > 0)
+ {
+ glScissor(0, 0, clientWidth, box.top);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ if (clientHeight - box.top - box.height > 0)
+ {
+ glScissor(0, box.top + box.height, clientWidth, clientHeight - box.top - box.height);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ if (box.left > 0)
+ {
+ glScissor(0, box.top, box.left, box.height);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ if (clientWidth - box.left - box.width > 0)
+ {
+ glScissor(box.left + box.width, box.top, clientWidth - box.left - box.width, box.height);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ glDisable(GL_SCISSOR_TEST);
+}
+
+
+// [SP] Re-implemented BestColor for more precision rather than speed. This function is only ever called once until the game palette is changed.
+
+int FGLRenderer::PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num)
+{
+ const PalEntry *pal = (const PalEntry *)pal_in;
+ static double powtable[256];
+ static bool firstTime = true;
+
+ double fbestdist, fdist;
+ int bestcolor;
+
+ if (firstTime)
+ {
+ firstTime = false;
+ for (int x = 0; x < 256; x++) powtable[x] = pow((double)x/255,1.2);
+ }
+
+ for (int color = first; color < num; color++)
+ {
+ double x = powtable[abs(r-pal[color].r)];
+ double y = powtable[abs(g-pal[color].g)];
+ double z = powtable[abs(b-pal[color].b)];
+ fdist = x + y + z;
+ if (color == first || fdist < fbestdist)
+ {
+ if (fdist == 0)
+ return color;
+
+ fbestdist = fdist;
+ bestcolor = color;
+ }
+ }
+ return bestcolor;
+}
+
diff --git a/src/gl/renderer/gl_postprocessstate.cpp b/src/gl/renderer/gl_postprocessstate.cpp
new file mode 100644
index 000000000..fead1435a
--- /dev/null
+++ b/src/gl/renderer/gl_postprocessstate.cpp
@@ -0,0 +1,143 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2016 Magnus Norddahl
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_postprocessstate.cpp
+** Render state maintenance
+**
+**/
+
+#include "templates.h"
+#include "gl/system/gl_system.h"
+#include "gl/system/gl_interface.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/system/gl_cvars.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_postprocessstate.h"
+
+//-----------------------------------------------------------------------------
+//
+// Saves state modified by post processing shaders
+//
+//-----------------------------------------------------------------------------
+
+FGLPostProcessState::FGLPostProcessState()
+{
+ glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
+ glActiveTexture(GL_TEXTURE0);
+ SaveTextureBindings(1);
+
+ glGetBooleanv(GL_BLEND, &blendEnabled);
+ glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled);
+ glGetBooleanv(GL_DEPTH_TEST, &depthEnabled);
+ glGetBooleanv(GL_MULTISAMPLE, &multisampleEnabled);
+ glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
+ glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRgb);
+ glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha);
+ glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRgb);
+ glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha);
+ glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRgb);
+ glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha);
+
+ glDisable(GL_MULTISAMPLE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_BLEND);
+}
+
+void FGLPostProcessState::SaveTextureBindings(unsigned int numUnits)
+{
+ while (textureBinding.Size() < numUnits)
+ {
+ unsigned int i = textureBinding.Size();
+
+ GLint texture;
+ glActiveTexture(GL_TEXTURE0 + i);
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ textureBinding.Push(texture);
+
+ if (gl.flags & RFL_SAMPLER_OBJECTS)
+ {
+ GLint sampler;
+ glGetIntegerv(GL_SAMPLER_BINDING, &sampler);
+ glBindSampler(i, 0);
+ samplerBinding.Push(sampler);
+ }
+ }
+ glActiveTexture(GL_TEXTURE0);
+}
+
+//-----------------------------------------------------------------------------
+//
+// Restores state at the end of post processing
+//
+//-----------------------------------------------------------------------------
+
+FGLPostProcessState::~FGLPostProcessState()
+{
+ if (blendEnabled)
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+
+ if (scissorEnabled)
+ glEnable(GL_SCISSOR_TEST);
+ else
+ glDisable(GL_SCISSOR_TEST);
+
+ if (depthEnabled)
+ glEnable(GL_DEPTH_TEST);
+ else
+ glDisable(GL_DEPTH_TEST);
+
+ if (multisampleEnabled)
+ glEnable(GL_MULTISAMPLE);
+ else
+ glDisable(GL_MULTISAMPLE);
+
+ glBlendEquationSeparate(blendEquationRgb, blendEquationAlpha);
+ glBlendFuncSeparate(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha);
+
+ glUseProgram(currentProgram);
+
+ // Fully unbind to avoid incomplete texture warnings from Nvidia's driver when gl_debug_level 4 is active
+ for (unsigned int i = 0; i < textureBinding.Size(); i++)
+ {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ for (unsigned int i = 0; i < samplerBinding.Size(); i++)
+ {
+ glBindSampler(i, samplerBinding[i]);
+ }
+
+ for (unsigned int i = 0; i < textureBinding.Size(); i++)
+ {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(GL_TEXTURE_2D, textureBinding[i]);
+ }
+
+ glActiveTexture(activeTex);
+}
diff --git a/src/gl/renderer/gl_postprocessstate.h b/src/gl/renderer/gl_postprocessstate.h
new file mode 100644
index 000000000..795a7d4ba
--- /dev/null
+++ b/src/gl/renderer/gl_postprocessstate.h
@@ -0,0 +1,39 @@
+#ifndef __GL_POSTPROCESSSTATE_H
+#define __GL_POSTPROCESSSTATE_H
+
+#include
+#include "gl/system/gl_interface.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_matrix.h"
+#include "c_cvars.h"
+#include "r_defs.h"
+
+class FGLPostProcessState
+{
+public:
+ FGLPostProcessState();
+ ~FGLPostProcessState();
+
+ void SaveTextureBindings(unsigned int numUnits);
+
+private:
+ FGLPostProcessState(const FGLPostProcessState &) = delete;
+ FGLPostProcessState &operator=(const FGLPostProcessState &) = delete;
+
+ GLint activeTex;
+ TArray textureBinding;
+ TArray samplerBinding;
+ GLboolean blendEnabled;
+ GLboolean scissorEnabled;
+ GLboolean depthEnabled;
+ GLboolean multisampleEnabled;
+ GLint currentProgram;
+ GLint blendEquationRgb;
+ GLint blendEquationAlpha;
+ GLint blendSrcRgb;
+ GLint blendSrcAlpha;
+ GLint blendDestRgb;
+ GLint blendDestAlpha;
+};
+
+#endif
diff --git a/src/gl/renderer/gl_quaddrawer.cpp b/src/gl/renderer/gl_quaddrawer.cpp
new file mode 100644
index 000000000..d6a4f98a8
--- /dev/null
+++ b/src/gl/renderer/gl_quaddrawer.cpp
@@ -0,0 +1,69 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+
+#include "gl/system/gl_system.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/renderer/gl_quaddrawer.h"
+#include "gl/data/gl_matrix.h"
+
+/*
+** For handling of dynamically created quads when no persistently mapped
+** buffer or client array is available (i.e. GL 3.x core profiles)
+**
+** In this situation the 4 vertices of a quad primitive are being passed
+** as a matrix uniform because that is a lot faster than any kind of
+** temporary buffer change.
+*/
+
+FFlatVertex FQuadDrawer::buffer[4];
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FQuadDrawer::DoRender(int type)
+{
+ // When this gets called, the render state must already be applied so we can just
+ // send our vertices to the current shader.
+ float matV[16], matT[16];
+
+ for(int i=0;i<4;i++)
+ {
+ matV[i*4+0] = buffer[i].x;
+ matV[i*4+1] = buffer[i].z;
+ matV[i*4+2] = buffer[i].y;
+ matV[i*4+3] = 1;
+ matT[i*4+0] = buffer[i].u;
+ matT[i*4+1] = buffer[i].v;
+ matT[i*4+2] = matT[i*4+3] = 0;
+ }
+ FShader *shader = GLRenderer->mShaderManager->GetActiveShader();
+ glUniformMatrix4fv(shader->vertexmatrix_index, 1, false, matV);
+ glUniformMatrix4fv(shader->texcoordmatrix_index, 1, false, matT);
+ glUniform1i(shader->quadmode_index, 1);
+ GLRenderer->mVBO->RenderArray(type, FFlatVertexBuffer::QUAD_INDEX, 4);
+ glUniform1i(shader->quadmode_index, 0);
+}
diff --git a/src/gl/renderer/gl_quaddrawer.h b/src/gl/renderer/gl_quaddrawer.h
new file mode 100644
index 000000000..b9db60684
--- /dev/null
+++ b/src/gl/renderer/gl_quaddrawer.h
@@ -0,0 +1,44 @@
+#ifndef __QDRAWER_H
+#define __QDRAWER_H
+
+#include "gl/data/gl_vertexbuffer.h"
+
+class FQuadDrawer
+{
+ FFlatVertex *p;
+ int ndx;
+ static FFlatVertex buffer[4];
+
+ void DoRender(int type);
+public:
+
+ FQuadDrawer()
+ {
+ if (gl.buffermethod == BM_DEFERRED)
+ {
+ p = buffer;
+ }
+ else
+ {
+ p = GLRenderer->mVBO->Alloc(4, &ndx);
+ }
+ }
+ void Set(int ndx, float x, float y, float z, float s, float t)
+ {
+ p[ndx].Set(x, y, z, s, t);
+ }
+ void Render(int type)
+ {
+ if (gl.buffermethod == BM_DEFERRED)
+ {
+ DoRender(type);
+ }
+ else
+ {
+ GLRenderer->mVBO->RenderArray(type, ndx, 4);
+ }
+ }
+};
+
+
+#endif
diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp
new file mode 100644
index 000000000..b14ee9852
--- /dev/null
+++ b/src/gl/renderer/gl_renderbuffers.cpp
@@ -0,0 +1,884 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2016 Magnus Norddahl
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_renderbuffers.cpp
+** Render buffers used during rendering
+**
+*/
+
+#include "gl/system/gl_system.h"
+#include "files.h"
+#include "m_swap.h"
+#include "v_video.h"
+#include "gl/gl_functions.h"
+#include "vectors.h"
+#include "gl/system/gl_interface.h"
+#include "gl/system/gl_framebuffer.h"
+#include "gl/system/gl_cvars.h"
+#include "gl/system/gl_debug.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_renderbuffers.h"
+#include "w_wad.h"
+#include "i_system.h"
+#include "doomerrors.h"
+#include
+
+CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+CVAR(Bool, gl_renderbuffers, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
+
+//==========================================================================
+//
+// Initialize render buffers and textures used in rendering passes
+//
+//==========================================================================
+
+FGLRenderBuffers::FGLRenderBuffers()
+{
+ for (int i = 0; i < NumPipelineTextures; i++)
+ {
+ mPipelineTexture[i] = 0;
+ mPipelineFB[i] = 0;
+ }
+
+ for (int i = 0; i < NumAmbientRandomTextures; i++)
+ {
+ AmbientRandomTexture[i] = 0;
+ }
+
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&mOutputFB);
+ glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
+}
+
+//==========================================================================
+//
+// Free render buffer resources
+//
+//==========================================================================
+
+FGLRenderBuffers::~FGLRenderBuffers()
+{
+ ClearScene();
+ ClearPipeline();
+ ClearEyeBuffers();
+ ClearBloom();
+ ClearExposureLevels();
+ ClearAmbientOcclusion();
+}
+
+void FGLRenderBuffers::ClearScene()
+{
+ DeleteFrameBuffer(mSceneFB);
+ DeleteFrameBuffer(mSceneDataFB);
+ if (mSceneUsesTextures)
+ {
+ DeleteTexture(mSceneMultisample);
+ DeleteTexture(mSceneFog);
+ DeleteTexture(mSceneNormal);
+ DeleteTexture(mSceneDepthStencil);
+ }
+ else
+ {
+ DeleteRenderBuffer(mSceneMultisample);
+ DeleteRenderBuffer(mSceneFog);
+ DeleteRenderBuffer(mSceneNormal);
+ DeleteRenderBuffer(mSceneDepthStencil);
+ }
+}
+
+void FGLRenderBuffers::ClearPipeline()
+{
+ for (int i = 0; i < NumPipelineTextures; i++)
+ {
+ DeleteFrameBuffer(mPipelineFB[i]);
+ DeleteTexture(mPipelineTexture[i]);
+ }
+}
+
+void FGLRenderBuffers::ClearBloom()
+{
+ for (int i = 0; i < NumBloomLevels; i++)
+ {
+ auto &level = BloomLevels[i];
+ DeleteFrameBuffer(level.HFramebuffer);
+ DeleteFrameBuffer(level.VFramebuffer);
+ DeleteTexture(level.HTexture);
+ DeleteTexture(level.VTexture);
+ level = FGLBloomTextureLevel();
+ }
+}
+
+void FGLRenderBuffers::ClearExposureLevels()
+{
+ for (auto &level : ExposureLevels)
+ {
+ DeleteTexture(level.Texture);
+ DeleteFrameBuffer(level.Framebuffer);
+ }
+ ExposureLevels.Clear();
+ DeleteTexture(ExposureTexture);
+ DeleteFrameBuffer(ExposureFB);
+}
+
+void FGLRenderBuffers::ClearEyeBuffers()
+{
+ for (auto handle : mEyeFBs)
+ DeleteFrameBuffer(handle);
+
+ for (auto handle : mEyeTextures)
+ DeleteTexture(handle);
+
+ mEyeTextures.Clear();
+ mEyeFBs.Clear();
+}
+
+void FGLRenderBuffers::ClearAmbientOcclusion()
+{
+ DeleteFrameBuffer(LinearDepthFB);
+ DeleteFrameBuffer(AmbientFB0);
+ DeleteFrameBuffer(AmbientFB1);
+ DeleteTexture(LinearDepthTexture);
+ DeleteTexture(AmbientTexture0);
+ DeleteTexture(AmbientTexture1);
+ for (int i = 0; i < NumAmbientRandomTextures; i++)
+ DeleteTexture(AmbientRandomTexture[i]);
+}
+
+void FGLRenderBuffers::DeleteTexture(GLuint &handle)
+{
+ if (handle != 0)
+ glDeleteTextures(1, &handle);
+ handle = 0;
+}
+
+void FGLRenderBuffers::DeleteRenderBuffer(GLuint &handle)
+{
+ if (handle != 0)
+ glDeleteRenderbuffers(1, &handle);
+ handle = 0;
+}
+
+void FGLRenderBuffers::DeleteFrameBuffer(GLuint &handle)
+{
+ if (handle != 0)
+ glDeleteFramebuffers(1, &handle);
+ handle = 0;
+}
+
+//==========================================================================
+//
+// Makes sure all render buffers have sizes suitable for rending at the
+// specified resolution
+//
+//==========================================================================
+
+bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHeight)
+{
+ if (gl_renderbuffers != BuffersActive)
+ {
+ if (BuffersActive)
+ glBindFramebuffer(GL_FRAMEBUFFER, mOutputFB);
+ BuffersActive = gl_renderbuffers;
+ GLRenderer->mShaderManager->ResetFixedColormap();
+ }
+
+ if (!IsEnabled())
+ return false;
+
+ if (width <= 0 || height <= 0)
+ I_FatalError("Requested invalid render buffer sizes: screen = %dx%d", width, height);
+
+ int samples = clamp((int)gl_multisample, 0, mMaxSamples);
+ bool needsSceneTextures = (gl_ssao != 0);
+
+ GLint activeTex;
+ GLint textureBinding;
+ glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
+ glActiveTexture(GL_TEXTURE0);
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
+
+ if (width != mWidth || height != mHeight)
+ CreatePipeline(width, height);
+
+ if (width != mWidth || height != mHeight || mSamples != samples || mSceneUsesTextures != needsSceneTextures)
+ CreateScene(width, height, samples, needsSceneTextures);
+
+ mWidth = width;
+ mHeight = height;
+ mSamples = samples;
+ mSceneUsesTextures = needsSceneTextures;
+
+ // Bloom bluring buffers need to match the scene to avoid bloom bleeding artifacts
+ if (mSceneWidth != sceneWidth || mSceneHeight != sceneHeight)
+ {
+ CreateBloom(sceneWidth, sceneHeight);
+ CreateExposureLevels(sceneWidth, sceneHeight);
+ CreateAmbientOcclusion(sceneWidth, sceneHeight);
+ mSceneWidth = sceneWidth;
+ mSceneHeight = sceneHeight;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, textureBinding);
+ glActiveTexture(activeTex);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ if (FailedCreate)
+ {
+ ClearScene();
+ ClearPipeline();
+ ClearEyeBuffers();
+ ClearBloom();
+ ClearExposureLevels();
+ mWidth = 0;
+ mHeight = 0;
+ mSamples = 0;
+ mSceneWidth = 0;
+ mSceneHeight = 0;
+ }
+
+ return !FailedCreate;
+}
+
+//==========================================================================
+//
+// Creates the scene buffers
+//
+//==========================================================================
+
+void FGLRenderBuffers::CreateScene(int width, int height, int samples, bool needsSceneTextures)
+{
+ ClearScene();
+
+ if (samples > 1)
+ {
+ if (needsSceneTextures)
+ {
+ mSceneMultisample = Create2DMultisampleTexture("SceneMultisample", GL_RGBA16F, width, height, samples, false);
+ mSceneDepthStencil = Create2DMultisampleTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples, false);
+ mSceneFog = Create2DMultisampleTexture("SceneFog", GL_RGBA8, width, height, samples, false);
+ mSceneNormal = Create2DMultisampleTexture("SceneNormal", GL_RGB10_A2, width, height, samples, false);
+ mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisample, 0, 0, mSceneDepthStencil, true);
+ mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisample, mSceneFog, mSceneNormal, mSceneDepthStencil, true);
+ }
+ else
+ {
+ mSceneMultisample = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, width, height, samples);
+ mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples);
+ mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisample, mSceneDepthStencil, true);
+ mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisample, mSceneDepthStencil, true);
+ }
+ }
+ else
+ {
+ if (needsSceneTextures)
+ {
+ mSceneDepthStencil = Create2DTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
+ mSceneFog = Create2DTexture("SceneFog", GL_RGBA8, width, height);
+ mSceneNormal = Create2DTexture("SceneNormal", GL_RGB10_A2, width, height);
+ mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], 0, 0, mSceneDepthStencil, false);
+ mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneFog, mSceneNormal, mSceneDepthStencil, false);
+ }
+ else
+ {
+ mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
+ mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], mSceneDepthStencil, false);
+ mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneDepthStencil, false);
+ }
+ }
+}
+
+//==========================================================================
+//
+// Creates the buffers needed for post processing steps
+//
+//==========================================================================
+
+void FGLRenderBuffers::CreatePipeline(int width, int height)
+{
+ ClearPipeline();
+ ClearEyeBuffers();
+
+ for (int i = 0; i < NumPipelineTextures; i++)
+ {
+ mPipelineTexture[i] = Create2DTexture("PipelineTexture", GL_RGBA16F, width, height);
+ mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i]);
+ }
+}
+
+//==========================================================================
+//
+// Creates bloom pass working buffers
+//
+//==========================================================================
+
+void FGLRenderBuffers::CreateBloom(int width, int height)
+{
+ ClearBloom();
+
+ // No scene, no bloom!
+ if (width <= 0 || height <= 0)
+ return;
+
+ int bloomWidth = (width + 1) / 2;
+ int bloomHeight = (height + 1) / 2;
+ for (int i = 0; i < NumBloomLevels; i++)
+ {
+ auto &level = BloomLevels[i];
+ level.Width = (bloomWidth + 1) / 2;
+ level.Height = (bloomHeight + 1) / 2;
+
+ level.VTexture = Create2DTexture("Bloom.VTexture", GL_RGBA16F, level.Width, level.Height);
+ level.HTexture = Create2DTexture("Bloom.HTexture", GL_RGBA16F, level.Width, level.Height);
+ level.VFramebuffer = CreateFrameBuffer("Bloom.VFramebuffer", level.VTexture);
+ level.HFramebuffer = CreateFrameBuffer("Bloom.HFramebuffer", level.HTexture);
+
+ bloomWidth = level.Width;
+ bloomHeight = level.Height;
+ }
+}
+
+//==========================================================================
+//
+// Creates ambient occlusion working buffers
+//
+//==========================================================================
+
+void FGLRenderBuffers::CreateAmbientOcclusion(int width, int height)
+{
+ ClearAmbientOcclusion();
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ AmbientWidth = (width + 1) / 2;
+ AmbientHeight = (height + 1) / 2;
+ LinearDepthTexture = Create2DTexture("LinearDepthTexture", GL_R32F, AmbientWidth, AmbientHeight);
+ AmbientTexture0 = Create2DTexture("AmbientTexture0", GL_RG16F, AmbientWidth, AmbientHeight);
+ AmbientTexture1 = Create2DTexture("AmbientTexture1", GL_RG16F, AmbientWidth, AmbientHeight);
+ LinearDepthFB = CreateFrameBuffer("LinearDepthFB", LinearDepthTexture);
+ AmbientFB0 = CreateFrameBuffer("AmbientFB0", AmbientTexture0);
+ AmbientFB1 = CreateFrameBuffer("AmbientFB1", AmbientTexture1);
+
+ // Must match quality enum in FSSAOShader::GetDefines
+ double numDirections[NumAmbientRandomTextures] = { 2.0, 4.0, 8.0 };
+
+ std::mt19937 generator(1337);
+ std::uniform_real_distribution distribution(0.0, 1.0);
+ for (int quality = 0; quality < NumAmbientRandomTextures; quality++)
+ {
+ int16_t randomValues[16 * 4];
+
+ for (int i = 0; i < 16; i++)
+ {
+ double angle = 2.0 * M_PI * distribution(generator) / numDirections[quality];
+ double x = cos(angle);
+ double y = sin(angle);
+ double z = distribution(generator);
+ double w = distribution(generator);
+
+ randomValues[i * 4 + 0] = (int16_t)clamp(x * 32767.0, -32768.0, 32767.0);
+ randomValues[i * 4 + 1] = (int16_t)clamp(y * 32767.0, -32768.0, 32767.0);
+ randomValues[i * 4 + 2] = (int16_t)clamp(z * 32767.0, -32768.0, 32767.0);
+ randomValues[i * 4 + 3] = (int16_t)clamp(w * 32767.0, -32768.0, 32767.0);
+ }
+
+ AmbientRandomTexture[quality] = Create2DTexture("AmbientRandomTexture", GL_RGBA16_SNORM, 4, 4, randomValues);
+ }
+}
+
+//==========================================================================
+//
+// Creates camera exposure level buffers
+//
+//==========================================================================
+
+void FGLRenderBuffers::CreateExposureLevels(int width, int height)
+{
+ ClearExposureLevels();
+
+ int i = 0;
+ do
+ {
+ width = MAX(width / 2, 1);
+ height = MAX(height / 2, 1);
+
+ FString textureName, fbName;
+ textureName.Format("Exposure.Texture%d", i);
+ fbName.Format("Exposure.Framebuffer%d", i);
+ i++;
+
+ FGLExposureTextureLevel level;
+ level.Width = width;
+ level.Height = height;
+ level.Texture = Create2DTexture(textureName, GL_R32F, level.Width, level.Height);
+ level.Framebuffer = CreateFrameBuffer(fbName, level.Texture);
+ ExposureLevels.Push(level);
+ } while (width > 1 || height > 1);
+
+ ExposureTexture = Create2DTexture("Exposure.CameraTexture", GL_R32F, 1, 1);
+ ExposureFB = CreateFrameBuffer("Exposure.CameraFB", ExposureTexture);
+
+ FirstExposureFrame = true;
+}
+
+//==========================================================================
+//
+// Creates eye buffers if needed
+//
+//==========================================================================
+
+void FGLRenderBuffers::CreateEyeBuffers(int eye)
+{
+ if (mEyeFBs.Size() > eye)
+ return;
+
+ GLint activeTex, textureBinding, frameBufferBinding;
+ glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
+ glActiveTexture(GL_TEXTURE0);
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding);
+
+ while (mEyeFBs.Size() <= eye)
+ {
+ GLuint texture = Create2DTexture("EyeTexture", GL_RGBA16F, mWidth, mHeight);
+ mEyeTextures.Push(texture);
+ mEyeFBs.Push(CreateFrameBuffer("EyeFB", texture));
+ }
+
+ glBindTexture(GL_TEXTURE_2D, textureBinding);
+ glActiveTexture(activeTex);
+ glBindFramebuffer(GL_FRAMEBUFFER, frameBufferBinding);
+}
+
+//==========================================================================
+//
+// Creates a 2D texture defaulting to linear filtering and clamp to edge
+//
+//==========================================================================
+
+GLuint FGLRenderBuffers::Create2DTexture(const FString &name, GLuint format, int width, int height, const void *data)
+{
+ GLuint handle = 0;
+ glGenTextures(1, &handle);
+ glBindTexture(GL_TEXTURE_2D, handle);
+ FGLDebug::LabelObject(GL_TEXTURE, handle, name);
+
+ GLenum dataformat, datatype;
+ switch (format)
+ {
+ case GL_RGBA8: dataformat = GL_RGBA; datatype = GL_UNSIGNED_BYTE; break;
+ case GL_RGBA16: dataformat = GL_RGBA; datatype = GL_UNSIGNED_SHORT; break;
+ case GL_RGBA16F: dataformat = GL_RGBA; datatype = GL_FLOAT; break;
+ case GL_RGBA32F: dataformat = GL_RGBA; datatype = GL_FLOAT; break;
+ case GL_RGBA16_SNORM: dataformat = GL_RGBA; datatype = GL_SHORT; break;
+ case GL_R32F: dataformat = GL_RED; datatype = GL_FLOAT; break;
+ case GL_R16F: dataformat = GL_RED; datatype = GL_FLOAT; break;
+ case GL_RG32F: dataformat = GL_RG; datatype = GL_FLOAT; break;
+ case GL_RG16F: dataformat = GL_RG; datatype = GL_FLOAT; break;
+ case GL_RGB10_A2: dataformat = GL_RGBA; datatype = GL_UNSIGNED_INT_10_10_10_2; break;
+ case GL_DEPTH_COMPONENT24: dataformat = GL_DEPTH_COMPONENT; datatype = GL_FLOAT; break;
+ case GL_STENCIL_INDEX8: dataformat = GL_STENCIL_INDEX; datatype = GL_INT; break;
+ case GL_DEPTH24_STENCIL8: dataformat = GL_DEPTH_STENCIL; datatype = GL_UNSIGNED_INT_24_8; break;
+ default: I_FatalError("Unknown format passed to FGLRenderBuffers.Create2DTexture");
+ }
+
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, dataformat, datatype, data);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ return handle;
+}
+
+GLuint FGLRenderBuffers::Create2DMultisampleTexture(const FString &name, GLuint format, int width, int height, int samples, bool fixedSampleLocations)
+{
+ GLuint handle = 0;
+ glGenTextures(1, &handle);
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, handle);
+ FGLDebug::LabelObject(GL_TEXTURE, handle, name);
+ glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, fixedSampleLocations);
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
+ return handle;
+}
+
+//==========================================================================
+//
+// Creates a render buffer
+//
+//==========================================================================
+
+GLuint FGLRenderBuffers::CreateRenderBuffer(const FString &name, GLuint format, int width, int height)
+{
+ GLuint handle = 0;
+ glGenRenderbuffers(1, &handle);
+ glBindRenderbuffer(GL_RENDERBUFFER, handle);
+ FGLDebug::LabelObject(GL_RENDERBUFFER, handle, name);
+ glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
+ return handle;
+}
+
+GLuint FGLRenderBuffers::CreateRenderBuffer(const FString &name, GLuint format, int width, int height, int samples)
+{
+ if (samples <= 1)
+ return CreateRenderBuffer(name, format, width, height);
+
+ GLuint handle = 0;
+ glGenRenderbuffers(1, &handle);
+ glBindRenderbuffer(GL_RENDERBUFFER, handle);
+ FGLDebug::LabelObject(GL_RENDERBUFFER, handle, name);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, format, width, height);
+ return handle;
+}
+
+//==========================================================================
+//
+// Creates a frame buffer
+//
+//==========================================================================
+
+GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer)
+{
+ GLuint handle = 0;
+ glGenFramebuffers(1, &handle);
+ glBindFramebuffer(GL_FRAMEBUFFER, handle);
+ FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0);
+ if (CheckFrameBufferCompleteness())
+ ClearFrameBuffer(false, false);
+ return handle;
+}
+
+GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool colorIsARenderBuffer)
+{
+ GLuint handle = 0;
+ glGenFramebuffers(1, &handle);
+ glBindFramebuffer(GL_FRAMEBUFFER, handle);
+ FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name);
+ if (colorIsARenderBuffer)
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer);
+ else
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil);
+ if (CheckFrameBufferCompleteness())
+ ClearFrameBuffer(true, true);
+ return handle;
+}
+
+GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer0, GLuint colorbuffer1, GLuint colorbuffer2, GLuint depthstencil, bool multisample)
+{
+ GLuint handle = 0;
+ glGenFramebuffers(1, &handle);
+ glBindFramebuffer(GL_FRAMEBUFFER, handle);
+ FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name);
+ if (multisample)
+ {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer0, 0);
+ if (colorbuffer1 != 0)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer1, 0);
+ if (colorbuffer2 != 0)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer2, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, depthstencil, 0);
+ }
+ else
+ {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer0, 0);
+ if (colorbuffer1 != 0)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, colorbuffer1, 0);
+ if (colorbuffer2 != 0)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorbuffer2, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthstencil, 0);
+ }
+ if (CheckFrameBufferCompleteness())
+ ClearFrameBuffer(true, true);
+ return handle;
+}
+
+//==========================================================================
+//
+// Verifies that the frame buffer setup is valid
+//
+//==========================================================================
+
+bool FGLRenderBuffers::CheckFrameBufferCompleteness()
+{
+ GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (result == GL_FRAMEBUFFER_COMPLETE)
+ return true;
+
+ FailedCreate = true;
+
+ if (gl_debug_level > 0)
+ {
+ FString error = "glCheckFramebufferStatus failed: ";
+ switch (result)
+ {
+ default: error.AppendFormat("error code %d", (int)result); break;
+ case GL_FRAMEBUFFER_UNDEFINED: error << "GL_FRAMEBUFFER_UNDEFINED"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: error << "GL_FRAMEBUFFER_UNSUPPORTED"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: error << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: error << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break;
+ }
+ Printf("%s\n", error.GetChars());
+ }
+
+ return false;
+}
+
+//==========================================================================
+//
+// Clear frame buffer to make sure it never contains uninitialized data
+//
+//==========================================================================
+
+void FGLRenderBuffers::ClearFrameBuffer(bool stencil, bool depth)
+{
+ GLboolean scissorEnabled;
+ GLint stencilValue;
+ GLdouble depthValue;
+ glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled);
+ glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilValue);
+ glGetDoublev(GL_DEPTH_CLEAR_VALUE, &depthValue);
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClearDepth(0.0);
+ glClearStencil(0);
+ GLenum flags = GL_COLOR_BUFFER_BIT;
+ if (stencil)
+ flags |= GL_STENCIL_BUFFER_BIT;
+ if (depth)
+ flags |= GL_DEPTH_BUFFER_BIT;
+ glClear(flags);
+ glClearStencil(stencilValue);
+ glClearDepth(depthValue);
+ if (scissorEnabled)
+ glEnable(GL_SCISSOR_TEST);
+}
+
+//==========================================================================
+//
+// Resolves the multisample frame buffer by copying it to the scene texture
+//
+//==========================================================================
+
+void FGLRenderBuffers::BlitSceneToTexture()
+{
+ mCurrentPipelineTexture = 0;
+
+ if (mSamples <= 1)
+ return;
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mSceneFB);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture]);
+ glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0)
+ {
+ GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
+ glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments);
+ }
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+}
+
+//==========================================================================
+//
+// Eye textures and their frame buffers
+//
+//==========================================================================
+
+void FGLRenderBuffers::BlitToEyeTexture(int eye)
+{
+ CreateEyeBuffers(eye);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture]);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mEyeFBs[eye]);
+ glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0)
+ {
+ GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
+ glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments);
+ }
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+}
+
+void FGLRenderBuffers::BindEyeTexture(int eye, int texunit)
+{
+ CreateEyeBuffers(eye);
+ glActiveTexture(GL_TEXTURE0 + texunit);
+ glBindTexture(GL_TEXTURE_2D, mEyeTextures[eye]);
+}
+
+void FGLRenderBuffers::BindEyeFB(int eye, bool readBuffer)
+{
+ CreateEyeBuffers(eye);
+ glBindFramebuffer(readBuffer ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER, mEyeFBs[eye]);
+}
+
+//==========================================================================
+//
+// Makes the scene frame buffer active (multisample, depth, stecil, etc.)
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindSceneFB(bool sceneData)
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, sceneData ? mSceneDataFB : mSceneFB);
+}
+
+//==========================================================================
+//
+// Binds the scene color texture to the specified texture unit
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindSceneColorTexture(int index)
+{
+ glActiveTexture(GL_TEXTURE0 + index);
+ if (mSamples > 1)
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneMultisample);
+ else
+ glBindTexture(GL_TEXTURE_2D, mPipelineTexture[0]);
+}
+
+//==========================================================================
+//
+// Binds the scene fog data texture to the specified texture unit
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindSceneFogTexture(int index)
+{
+ glActiveTexture(GL_TEXTURE0 + index);
+ if (mSamples > 1)
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneFog);
+ else
+ glBindTexture(GL_TEXTURE_2D, mSceneFog);
+}
+
+//==========================================================================
+//
+// Binds the scene normal data texture to the specified texture unit
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindSceneNormalTexture(int index)
+{
+ glActiveTexture(GL_TEXTURE0 + index);
+ if (mSamples > 1)
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneNormal);
+ else
+ glBindTexture(GL_TEXTURE_2D, mSceneNormal);
+}
+
+//==========================================================================
+//
+// Binds the depth texture to the specified texture unit
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindSceneDepthTexture(int index)
+{
+ glActiveTexture(GL_TEXTURE0 + index);
+ if (mSamples > 1)
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneDepthStencil);
+ else
+ glBindTexture(GL_TEXTURE_2D, mSceneDepthStencil);
+}
+
+//==========================================================================
+//
+// Binds the current scene/effect/hud texture to the specified texture unit
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindCurrentTexture(int index)
+{
+ glActiveTexture(GL_TEXTURE0 + index);
+ glBindTexture(GL_TEXTURE_2D, mPipelineTexture[mCurrentPipelineTexture]);
+}
+
+//==========================================================================
+//
+// Makes the frame buffer for the current texture active
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindCurrentFB()
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture]);
+}
+
+//==========================================================================
+//
+// Makes the frame buffer for the next texture active
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindNextFB()
+{
+ int out = (mCurrentPipelineTexture + 1) % NumPipelineTextures;
+ glBindFramebuffer(GL_FRAMEBUFFER, mPipelineFB[out]);
+}
+
+//==========================================================================
+//
+// Next pipeline texture now contains the output
+//
+//==========================================================================
+
+void FGLRenderBuffers::NextTexture()
+{
+ mCurrentPipelineTexture = (mCurrentPipelineTexture + 1) % NumPipelineTextures;
+}
+
+//==========================================================================
+//
+// Makes the screen frame buffer active
+//
+//==========================================================================
+
+void FGLRenderBuffers::BindOutputFB()
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, mOutputFB);
+}
+
+//==========================================================================
+//
+// Returns true if render buffers are supported and should be used
+//
+//==========================================================================
+
+bool FGLRenderBuffers::IsEnabled()
+{
+ return BuffersActive && !gl.legacyMode && !FailedCreate;
+}
+
+bool FGLRenderBuffers::FailedCreate = false;
+bool FGLRenderBuffers::BuffersActive = false;
diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h
new file mode 100644
index 000000000..f2f7b7cb9
--- /dev/null
+++ b/src/gl/renderer/gl_renderbuffers.h
@@ -0,0 +1,140 @@
+#ifndef __GL_RENDERBUFFERS_H
+#define __GL_RENDERBUFFERS_H
+
+#include "gl/shaders/gl_shader.h"
+
+class FGLBloomTextureLevel
+{
+public:
+ GLuint VTexture = 0;
+ GLuint VFramebuffer = 0;
+ GLuint HTexture = 0;
+ GLuint HFramebuffer = 0;
+ GLuint Width = 0;
+ GLuint Height = 0;
+};
+
+class FGLExposureTextureLevel
+{
+public:
+ GLuint Texture = 0;
+ GLuint Framebuffer = 0;
+ GLuint Width = 0;
+ GLuint Height = 0;
+};
+
+class FGLRenderBuffers
+{
+public:
+ FGLRenderBuffers();
+ ~FGLRenderBuffers();
+
+ bool Setup(int width, int height, int sceneWidth, int sceneHeight);
+
+ void BindSceneFB(bool sceneData);
+ void BindSceneColorTexture(int index);
+ void BindSceneFogTexture(int index);
+ void BindSceneNormalTexture(int index);
+ void BindSceneDepthTexture(int index);
+ void BlitSceneToTexture();
+
+ void BindCurrentTexture(int index);
+ void BindCurrentFB();
+ void BindNextFB();
+ void NextTexture();
+
+ void BindOutputFB();
+
+ void BlitToEyeTexture(int eye);
+ void BindEyeTexture(int eye, int texunit);
+ void BindEyeFB(int eye, bool readBuffer = false);
+
+ enum { NumBloomLevels = 4 };
+ FGLBloomTextureLevel BloomLevels[NumBloomLevels];
+
+ TArray ExposureLevels;
+ GLuint ExposureTexture = 0;
+ GLuint ExposureFB = 0;
+ bool FirstExposureFrame = true;
+
+ // Ambient occlusion buffers
+ GLuint LinearDepthTexture = 0;
+ GLuint LinearDepthFB = 0;
+ GLuint AmbientTexture0 = 0;
+ GLuint AmbientTexture1 = 0;
+ GLuint AmbientFB0 = 0;
+ GLuint AmbientFB1 = 0;
+ int AmbientWidth = 0;
+ int AmbientHeight = 0;
+ enum { NumAmbientRandomTextures = 3 };
+ GLuint AmbientRandomTexture[NumAmbientRandomTextures];
+
+ static bool IsEnabled();
+
+ int GetWidth() const { return mWidth; }
+ int GetHeight() const { return mHeight; }
+
+ int GetSceneWidth() const { return mSceneWidth; }
+ int GetSceneHeight() const { return mSceneHeight; }
+
+private:
+ void ClearScene();
+ void ClearPipeline();
+ void ClearEyeBuffers();
+ void ClearBloom();
+ void ClearExposureLevels();
+ void ClearAmbientOcclusion();
+ void CreateScene(int width, int height, int samples, bool needsSceneTextures);
+ void CreatePipeline(int width, int height);
+ void CreateBloom(int width, int height);
+ void CreateExposureLevels(int width, int height);
+ void CreateEyeBuffers(int eye);
+ void CreateAmbientOcclusion(int width, int height);
+ GLuint Create2DTexture(const FString &name, GLuint format, int width, int height, const void *data = nullptr);
+ GLuint Create2DMultisampleTexture(const FString &name, GLuint format, int width, int height, int samples, bool fixedSampleLocations);
+ GLuint CreateRenderBuffer(const FString &name, GLuint format, int width, int height);
+ GLuint CreateRenderBuffer(const FString &name, GLuint format, int width, int height, int samples);
+ GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer);
+ GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool colorIsARenderBuffer);
+ GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer0, GLuint colorbuffer1, GLuint colorbuffer2, GLuint depthstencil, bool multisample);
+ bool CheckFrameBufferCompleteness();
+ void ClearFrameBuffer(bool stencil, bool depth);
+ void DeleteTexture(GLuint &handle);
+ void DeleteRenderBuffer(GLuint &handle);
+ void DeleteFrameBuffer(GLuint &handle);
+
+ int mWidth = 0;
+ int mHeight = 0;
+ int mSamples = 0;
+ int mMaxSamples = 0;
+ int mSceneWidth = 0;
+ int mSceneHeight = 0;
+
+ static const int NumPipelineTextures = 2;
+ int mCurrentPipelineTexture = 0;
+
+ // Buffers for the scene
+ GLuint mSceneMultisample = 0;
+ GLuint mSceneDepthStencil = 0;
+ GLuint mSceneFog = 0;
+ GLuint mSceneNormal = 0;
+ GLuint mSceneFB = 0;
+ GLuint mSceneDataFB = 0;
+ bool mSceneUsesTextures = false;
+
+ // Effect/HUD buffers
+ GLuint mPipelineTexture[NumPipelineTextures];
+ GLuint mPipelineFB[NumPipelineTextures];
+
+ // Back buffer frame buffer
+ GLuint mOutputFB = 0;
+
+ // Eye buffers
+ TArray mEyeTextures;
+ TArray mEyeFBs;
+
+ static bool FailedCreate;
+ static bool BuffersActive;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp
new file mode 100644
index 000000000..90687bcb7
--- /dev/null
+++ b/src/gl/renderer/gl_renderer.cpp
@@ -0,0 +1,456 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl1_renderer.cpp
+** Renderer interface
+**
+*/
+
+#include "gl/system/gl_system.h"
+#include "files.h"
+#include "m_swap.h"
+#include "v_video.h"
+#include "r_data/r_translate.h"
+#include "m_png.h"
+#include "m_crc32.h"
+#include "w_wad.h"
+//#include "gl/gl_intern.h"
+#include "gl/gl_functions.h"
+#include "vectors.h"
+#include "doomstat.h"
+
+#include "gl/system/gl_interface.h"
+#include "gl/system/gl_framebuffer.h"
+#include "gl/system/gl_cvars.h"
+#include "gl/system/gl_debug.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/renderer/gl_renderbuffers.h"
+#include "gl/renderer/gl_2ddrawer.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/shaders/gl_ambientshader.h"
+#include "gl/shaders/gl_bloomshader.h"
+#include "gl/shaders/gl_blurshader.h"
+#include "gl/shaders/gl_tonemapshader.h"
+#include "gl/shaders/gl_colormapshader.h"
+#include "gl/shaders/gl_lensshader.h"
+#include "gl/shaders/gl_fxaashader.h"
+#include "gl/shaders/gl_presentshader.h"
+#include "gl/shaders/gl_present3dRowshader.h"
+#include "gl/stereo3d/gl_stereo3d.h"
+#include "gl/textures/gl_texture.h"
+#include "gl/textures/gl_translate.h"
+#include "gl/textures/gl_material.h"
+#include "gl/textures/gl_samplers.h"
+#include "gl/utility/gl_clock.h"
+#include "gl/utility/gl_templates.h"
+#include "gl/models/gl_models.h"
+#include "gl/dynlights/gl_lightbuffer.h"
+
+EXTERN_CVAR(Int, screenblocks)
+
+CVAR(Bool, gl_scale_viewport, true, 0);
+
+//===========================================================================
+//
+// Renderer interface
+//
+//===========================================================================
+
+//-----------------------------------------------------------------------------
+//
+// Initialize
+//
+//-----------------------------------------------------------------------------
+
+FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb)
+{
+ framebuffer = fb;
+ mClipPortal = nullptr;
+ mCurrentPortal = nullptr;
+ mMirrorCount = 0;
+ mPlaneMirrorCount = 0;
+ mLightCount = 0;
+ mAngles = FRotator(0.f, 0.f, 0.f);
+ mViewVector = FVector2(0,0);
+ mVBO = nullptr;
+ mSkyVBO = nullptr;
+ gl_spriteindex = 0;
+ mShaderManager = nullptr;
+ gllight = glpart2 = glpart = mirrortexture = nullptr;
+ mLights = nullptr;
+ m2DDrawer = nullptr;
+ mTonemapPalette = nullptr;
+ mBuffers = nullptr;
+ mPresentShader = nullptr;
+ mPresent3dCheckerShader = nullptr;
+ mPresent3dColumnShader = nullptr;
+ mPresent3dRowShader = nullptr;
+ mBloomExtractShader = nullptr;
+ mBloomCombineShader = nullptr;
+ mExposureExtractShader = nullptr;
+ mExposureAverageShader = nullptr;
+ mExposureCombineShader = nullptr;
+ mBlurShader = nullptr;
+ mTonemapShader = nullptr;
+ mTonemapPalette = nullptr;
+ mColormapShader = nullptr;
+ mLensShader = nullptr;
+ mLinearDepthShader = nullptr;
+ mDepthBlurShader = nullptr;
+ mSSAOShader = nullptr;
+ mSSAOCombineShader = nullptr;
+ mFXAAShader = nullptr;
+ mFXAALumaShader = nullptr;
+}
+
+void gl_LoadModels();
+void gl_FlushModels();
+
+void FGLRenderer::Initialize(int width, int height)
+{
+ mBuffers = new FGLRenderBuffers();
+ mLinearDepthShader = new FLinearDepthShader();
+ mDepthBlurShader = new FDepthBlurShader();
+ mSSAOShader = new FSSAOShader();
+ mSSAOCombineShader = new FSSAOCombineShader();
+ mBloomExtractShader = new FBloomExtractShader();
+ mBloomCombineShader = new FBloomCombineShader();
+ mExposureExtractShader = new FExposureExtractShader();
+ mExposureAverageShader = new FExposureAverageShader();
+ mExposureCombineShader = new FExposureCombineShader();
+ mBlurShader = new FBlurShader();
+ mTonemapShader = new FTonemapShader();
+ mColormapShader = new FColormapShader();
+ mTonemapPalette = nullptr;
+ mLensShader = new FLensShader();
+ mFXAAShader = new FFXAAShader;
+ mFXAALumaShader = new FFXAALumaShader;
+ mPresentShader = new FPresentShader();
+ mPresent3dCheckerShader = new FPresent3DCheckerShader();
+ mPresent3dColumnShader = new FPresent3DColumnShader();
+ mPresent3dRowShader = new FPresent3DRowShader();
+ m2DDrawer = new F2DDrawer;
+
+ // needed for the core profile, because someone decided it was a good idea to remove the default VAO.
+ if (!gl.legacyMode)
+ {
+ glGenVertexArrays(1, &mVAOID);
+ glBindVertexArray(mVAOID);
+ FGLDebug::LabelObject(GL_VERTEX_ARRAY, mVAOID, "FGLRenderer.mVAOID");
+ }
+ else mVAOID = 0;
+
+ gllight = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/gllight.png"), FTexture::TEX_MiscPatch);
+ glpart2 = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/glpart2.png"), FTexture::TEX_MiscPatch);
+ glpart = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/glpart.png"), FTexture::TEX_MiscPatch);
+ mirrortexture = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/mirror.png"), FTexture::TEX_MiscPatch);
+
+ mVBO = new FFlatVertexBuffer(width, height);
+ mSkyVBO = new FSkyVertexBuffer;
+ if (!gl.legacyMode) mLights = new FLightBuffer();
+ else mLights = NULL;
+ gl_RenderState.SetVertexBuffer(mVBO);
+ mFBID = 0;
+ mOldFBID = 0;
+
+ SetupLevel();
+ mShaderManager = new FShaderManager;
+ mSamplerManager = new FSamplerManager;
+ gl_LoadModels();
+}
+
+FGLRenderer::~FGLRenderer()
+{
+ gl_FlushModels();
+ gl_DeleteAllAttachedLights();
+ FMaterial::FlushAll();
+ if (m2DDrawer != nullptr) delete m2DDrawer;
+ if (mShaderManager != NULL) delete mShaderManager;
+ if (mSamplerManager != NULL) delete mSamplerManager;
+ if (mVBO != NULL) delete mVBO;
+ if (mSkyVBO != NULL) delete mSkyVBO;
+ if (mLights != NULL) delete mLights;
+ if (glpart2) delete glpart2;
+ if (glpart) delete glpart;
+ if (mirrortexture) delete mirrortexture;
+ if (mFBID != 0) glDeleteFramebuffers(1, &mFBID);
+ if (mVAOID != 0)
+ {
+ glBindVertexArray(0);
+ glDeleteVertexArrays(1, &mVAOID);
+ }
+ if (mBuffers) delete mBuffers;
+ if (mPresentShader) delete mPresentShader;
+ if (mLinearDepthShader) delete mLinearDepthShader;
+ if (mDepthBlurShader) delete mDepthBlurShader;
+ if (mSSAOShader) delete mSSAOShader;
+ if (mSSAOCombineShader) delete mSSAOCombineShader;
+ if (mPresent3dCheckerShader) delete mPresent3dCheckerShader;
+ if (mPresent3dColumnShader) delete mPresent3dColumnShader;
+ if (mPresent3dRowShader) delete mPresent3dRowShader;
+ if (mBloomExtractShader) delete mBloomExtractShader;
+ if (mBloomCombineShader) delete mBloomCombineShader;
+ if (mExposureExtractShader) delete mExposureExtractShader;
+ if (mExposureAverageShader) delete mExposureAverageShader;
+ if (mExposureCombineShader) delete mExposureCombineShader;
+ if (mBlurShader) delete mBlurShader;
+ if (mTonemapShader) delete mTonemapShader;
+ if (mTonemapPalette) delete mTonemapPalette;
+ if (mColormapShader) delete mColormapShader;
+ if (mLensShader) delete mLensShader;
+ delete mFXAAShader;
+ delete mFXAALumaShader;
+}
+
+//==========================================================================
+//
+// Calculates the viewport values needed for 2D and 3D operations
+//
+//==========================================================================
+
+void FGLRenderer::SetOutputViewport(GL_IRECT *bounds)
+{
+ if (bounds)
+ {
+ mSceneViewport = *bounds;
+ mScreenViewport = *bounds;
+ mOutputLetterbox = *bounds;
+ return;
+ }
+
+ // Special handling so the view with a visible status bar displays properly
+ int height, width;
+ if (screenblocks >= 10)
+ {
+ height = framebuffer->GetHeight();
+ width = framebuffer->GetWidth();
+ }
+ else
+ {
+ height = (screenblocks*framebuffer->GetHeight() / 10) & ~7;
+ width = (screenblocks*framebuffer->GetWidth() / 10);
+ }
+
+ // Back buffer letterbox for the final output
+ int clientWidth = framebuffer->GetClientWidth();
+ int clientHeight = framebuffer->GetClientHeight();
+ if (clientWidth == 0 || clientHeight == 0)
+ {
+ // When window is minimized there may not be any client area.
+ // Pretend to the rest of the render code that we just have a very small window.
+ clientWidth = 160;
+ clientHeight = 120;
+ }
+ int screenWidth = framebuffer->GetWidth();
+ int screenHeight = framebuffer->GetHeight();
+ float scale = MIN(clientWidth / (float)screenWidth, clientHeight / (float)screenHeight);
+ mOutputLetterbox.width = (int)round(screenWidth * scale);
+ mOutputLetterbox.height = (int)round(screenHeight * scale);
+ mOutputLetterbox.left = (clientWidth - mOutputLetterbox.width) / 2;
+ mOutputLetterbox.top = (clientHeight - mOutputLetterbox.height) / 2;
+
+ // The entire renderable area, including the 2D HUD
+ mScreenViewport.left = 0;
+ mScreenViewport.top = 0;
+ mScreenViewport.width = screenWidth;
+ mScreenViewport.height = screenHeight;
+
+ // Viewport for the 3D scene
+ mSceneViewport.left = viewwindowx;
+ mSceneViewport.top = screenHeight - (height + viewwindowy - ((height - viewheight) / 2));
+ mSceneViewport.width = viewwidth;
+ mSceneViewport.height = height;
+
+ // Scale viewports to fit letterbox
+ if (gl_scale_viewport || !FGLRenderBuffers::IsEnabled())
+ {
+ mScreenViewport.width = mOutputLetterbox.width;
+ mScreenViewport.height = mOutputLetterbox.height;
+ mSceneViewport.left = (int)round(mSceneViewport.left * scale);
+ mSceneViewport.top = (int)round(mSceneViewport.top * scale);
+ mSceneViewport.width = (int)round(mSceneViewport.width * scale);
+ mSceneViewport.height = (int)round(mSceneViewport.height * scale);
+
+ // Without render buffers we have to render directly to the letterbox
+ if (!FGLRenderBuffers::IsEnabled())
+ {
+ mScreenViewport.left += mOutputLetterbox.left;
+ mScreenViewport.top += mOutputLetterbox.top;
+ mSceneViewport.left += mOutputLetterbox.left;
+ mSceneViewport.top += mOutputLetterbox.top;
+ }
+ }
+
+ s3d::Stereo3DMode::getCurrentMode().AdjustViewports();
+}
+
+//===========================================================================
+//
+// Calculates the OpenGL window coordinates for a zdoom screen position
+//
+//===========================================================================
+
+int FGLRenderer::ScreenToWindowX(int x)
+{
+ return mScreenViewport.left + (int)round(x * mScreenViewport.width / (float)framebuffer->GetWidth());
+}
+
+int FGLRenderer::ScreenToWindowY(int y)
+{
+ return mScreenViewport.top + mScreenViewport.height - (int)round(y * mScreenViewport.height / (float)framebuffer->GetHeight());
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::SetupLevel()
+{
+ mVBO->CreateVBO();
+}
+
+void FGLRenderer::Begin2D()
+{
+ if (mBuffers->Setup(mScreenViewport.width, mScreenViewport.height, mSceneViewport.width, mSceneViewport.height))
+ {
+ if (mDrawingScene2D)
+ mBuffers->BindSceneFB(false);
+ else
+ mBuffers->BindCurrentFB();
+ }
+ glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
+ glScissor(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
+
+ gl_RenderState.EnableFog(false);
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::ProcessLowerMiniseg(seg_t *seg, sector_t * frontsector, sector_t * backsector)
+{
+ GLWall wall;
+ wall.ProcessLowerMiniseg(seg, frontsector, backsector);
+ rendered_lines++;
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::ProcessSprite(AActor *thing, sector_t *sector, bool thruportal)
+{
+ GLSprite glsprite;
+ glsprite.Process(thing, sector, thruportal);
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::ProcessParticle(particle_t *part, sector_t *sector)
+{
+ GLSprite glsprite;
+ glsprite.ProcessParticle(part, sector);//, 0, 0);
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::ProcessSector(sector_t *fakesector)
+{
+ GLFlat glflat;
+ glflat.ProcessSector(fakesector);
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::FlushTextures()
+{
+ FMaterial::FlushAll();
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+bool FGLRenderer::StartOffscreen()
+{
+ bool firstBind = (mFBID == 0);
+ if (mFBID == 0)
+ glGenFramebuffers(1, &mFBID);
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mOldFBID);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFBID);
+ if (firstBind)
+ FGLDebug::LabelObject(GL_FRAMEBUFFER, mFBID, "OffscreenFB");
+ return true;
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::EndOffscreen()
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, mOldFBID);
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+unsigned char *FGLRenderer::GetTextureBuffer(FTexture *tex, int &w, int &h)
+{
+ FMaterial * gltex = FMaterial::ValidateTexture(tex, false);
+ if (gltex)
+ {
+ return gltex->CreateTexBuffer(0, w, h);
+ }
+ return NULL;
+}
diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h
new file mode 100644
index 000000000..f31736777
--- /dev/null
+++ b/src/gl/renderer/gl_renderer.h
@@ -0,0 +1,259 @@
+#ifndef __GL_RENDERER_H
+#define __GL_RENDERER_H
+
+#include "r_defs.h"
+#include "v_video.h"
+#include "vectors.h"
+#include "r_renderer.h"
+#include "gl/data/gl_matrix.h"
+
+struct particle_t;
+class FCanvasTexture;
+class FFlatVertexBuffer;
+class FSkyVertexBuffer;
+class OpenGLFrameBuffer;
+struct FDrawInfo;
+class FShaderManager;
+class GLPortal;
+class FLightBuffer;
+class FSamplerManager;
+class DPSprite;
+class FGLRenderBuffers;
+class FLinearDepthShader;
+class FDepthBlurShader;
+class FSSAOShader;
+class FSSAOCombineShader;
+class FBloomExtractShader;
+class FBloomCombineShader;
+class FExposureExtractShader;
+class FExposureAverageShader;
+class FExposureCombineShader;
+class FBlurShader;
+class FTonemapShader;
+class FColormapShader;
+class FLensShader;
+class FFXAALumaShader;
+class FFXAAShader;
+class FPresentShader;
+class FPresent3DCheckerShader;
+class FPresent3DColumnShader;
+class FPresent3DRowShader;
+class F2DDrawer;
+class FHardwareTexture;
+
+inline float DEG2RAD(float deg)
+{
+ return deg * float(M_PI / 180.0);
+}
+
+inline float RAD2DEG(float deg)
+{
+ return deg * float(180. / M_PI);
+}
+
+enum SectorRenderFlags
+{
+ // This is used to avoid creating too many drawinfos
+ SSRF_RENDERFLOOR = 1,
+ SSRF_RENDERCEILING = 2,
+ SSRF_RENDER3DPLANES = 4,
+ SSRF_RENDERALL = 7,
+ SSRF_PROCESSED = 8,
+ SSRF_SEEN = 16,
+};
+
+struct GL_IRECT
+{
+ int left,top;
+ int width,height;
+
+
+ void Offset(int xofs,int yofs)
+ {
+ left+=xofs;
+ top+=yofs;
+ }
+};
+
+enum
+{
+ DM_MAINVIEW,
+ DM_OFFSCREEN,
+ DM_PORTAL
+};
+
+class FGLRenderer
+{
+public:
+
+ OpenGLFrameBuffer *framebuffer;
+ GLPortal *mClipPortal;
+ GLPortal *mCurrentPortal;
+ int mMirrorCount;
+ int mPlaneMirrorCount;
+ int mLightCount;
+ float mCurrentFoV;
+ AActor *mViewActor;
+ FShaderManager *mShaderManager;
+ FSamplerManager *mSamplerManager;
+ int gl_spriteindex;
+ unsigned int mFBID;
+ unsigned int mVAOID;
+ int mOldFBID;
+
+ FGLRenderBuffers *mBuffers;
+ FLinearDepthShader *mLinearDepthShader;
+ FSSAOShader *mSSAOShader;
+ FDepthBlurShader *mDepthBlurShader;
+ FSSAOCombineShader *mSSAOCombineShader;
+ FBloomExtractShader *mBloomExtractShader;
+ FBloomCombineShader *mBloomCombineShader;
+ FExposureExtractShader *mExposureExtractShader;
+ FExposureAverageShader *mExposureAverageShader;
+ FExposureCombineShader *mExposureCombineShader;
+ FBlurShader *mBlurShader;
+ FTonemapShader *mTonemapShader;
+ FColormapShader *mColormapShader;
+ FHardwareTexture *mTonemapPalette;
+ FLensShader *mLensShader;
+ FFXAALumaShader *mFXAALumaShader;
+ FFXAAShader *mFXAAShader;
+ FPresentShader *mPresentShader;
+ FPresent3DCheckerShader *mPresent3dCheckerShader;
+ FPresent3DColumnShader *mPresent3dColumnShader;
+ FPresent3DRowShader *mPresent3dRowShader;
+
+ FTexture *gllight;
+ FTexture *glpart2;
+ FTexture *glpart;
+ FTexture *mirrortexture;
+
+ float mSky1Pos, mSky2Pos;
+
+ FRotator mAngles;
+ FVector2 mViewVector;
+
+ FFlatVertexBuffer *mVBO;
+ FSkyVertexBuffer *mSkyVBO;
+ FLightBuffer *mLights;
+ F2DDrawer *m2DDrawer;
+
+ GL_IRECT mScreenViewport;
+ GL_IRECT mSceneViewport;
+ GL_IRECT mOutputLetterbox;
+ bool mDrawingScene2D = false;
+
+ float mSceneClearColor[3];
+
+ FGLRenderer(OpenGLFrameBuffer *fb);
+ ~FGLRenderer() ;
+
+ void SetOutputViewport(GL_IRECT *bounds);
+ int ScreenToWindowX(int x);
+ int ScreenToWindowY(int y);
+
+ angle_t FrustumAngle();
+ void SetViewArea();
+ void Set3DViewport(bool mainview);
+ void Reset3DViewport();
+ sector_t *RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen);
+ void RenderView(player_t *player);
+ void SetViewAngle(DAngle viewangle);
+ void SetupView(float viewx, float viewy, float viewz, DAngle viewangle, bool mirror, bool planemirror);
+
+ void Initialize(int width, int height);
+
+ void CreateScene();
+ void RenderMultipassStuff();
+ void RenderScene(int recursion);
+ void RenderTranslucent();
+ void DrawScene(int drawmode);
+ void DrawBlend(sector_t * viewsector);
+
+ void DrawPSprite (player_t * player,DPSprite *psp,float sx, float sy, bool hudModelStep, int OverrideShader, bool alphatexture);
+ void DrawPlayerSprites(sector_t * viewsector, bool hudModelStep);
+ void DrawTargeterSprites();
+
+ void Begin2D();
+ void ClearBorders();
+
+ void ProcessLowerMiniseg(seg_t *seg, sector_t * frontsector, sector_t * backsector);
+ void ProcessSprite(AActor *thing, sector_t *sector, bool thruportal);
+ void ProcessParticle(particle_t *part, sector_t *sector);
+ void ProcessSector(sector_t *fakesector);
+ void FlushTextures();
+ unsigned char *GetTextureBuffer(FTexture *tex, int &w, int &h);
+ void SetupLevel();
+
+ void RenderScreenQuad();
+ void SetFixedColormap (player_t *player);
+ void WriteSavePic (player_t *player, FileWriter *file, int width, int height);
+ void EndDrawScene(sector_t * viewsector);
+ void PostProcessScene();
+ void AmbientOccludeScene();
+ void UpdateCameraExposure();
+ void BloomScene();
+ void TonemapScene();
+ void ColormapScene();
+ void CreateTonemapPalette();
+ void ClearTonemapPalette();
+ void LensDistortScene();
+ void ApplyFXAA();
+ void CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma);
+ void DrawPresentTexture(const GL_IRECT &box, bool applyGamma);
+ void Flush();
+
+ void SetProjection(float fov, float ratio, float fovratio);
+ void SetProjection(VSMatrix matrix); // raw matrix input from stereo 3d modes
+ void SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror);
+ void ProcessScene(bool toscreen = false);
+
+ bool StartOffscreen();
+ void EndOffscreen();
+
+ void StartSimplePolys();
+ void FinishSimplePolys();
+
+ void FillSimplePoly(FTexture *texture, FVector2 *points, int npoints,
+ double originx, double originy, double scalex, double scaley,
+ DAngle rotation, FDynamicColormap *colormap, int lightlevel, int bottomclip);
+
+ int PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num);
+
+ static float GetZNear() { return 5.f; }
+ static float GetZFar() { return 65536.f; }
+};
+
+// Global functions. Make them members of GLRenderer later?
+void gl_RenderBSPNode (void *node);
+bool gl_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector);
+void gl_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector);
+
+typedef enum
+{
+ area_normal,
+ area_below,
+ area_above,
+ area_default
+} area_t;
+
+extern area_t in_area;
+
+
+sector_t * gl_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back);
+inline sector_t * gl_FakeFlat(sector_t * sec, sector_t * dest, bool back)
+{
+ return gl_FakeFlat(sec, dest, in_area, back);
+}
+
+struct TexFilter_s
+{
+ int minfilter;
+ int magfilter;
+ bool mipmapping;
+} ;
+
+
+extern FGLRenderer *GLRenderer;
+
+#endif
\ No newline at end of file
diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp
new file mode 100644
index 000000000..fa8237cca
--- /dev/null
+++ b/src/gl/renderer/gl_renderstate.cpp
@@ -0,0 +1,395 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2009-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_renderstate.cpp
+** Render state maintenance
+**
+*/
+
+#include "templates.h"
+#include "gl/system/gl_system.h"
+#include "gl/system/gl_interface.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/system/gl_cvars.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/renderer/gl_colormap.h"
+#include "gl/dynlights//gl_lightbuffer.h"
+#include "gl/renderer/gl_renderbuffers.h"
+
+void gl_SetTextureMode(int type);
+
+FRenderState gl_RenderState;
+
+CVAR(Bool, gl_direct_state_change, true, 0)
+
+
+static VSMatrix identityMatrix(1);
+TArray gl_MatrixStack;
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FRenderState::Reset()
+{
+ mTextureEnabled = true;
+ mClipLineEnabled = mSplitEnabled = mBrightmapEnabled = mFogEnabled = mGlowEnabled = false;
+ mColorMask[0] = mColorMask[1] = mColorMask[2] = mColorMask[3] = true;
+ currentColorMask[0] = currentColorMask[1] = currentColorMask[2] = currentColorMask[3] = true;
+ mFogColor.d = -1;
+ mTextureMode = -1;
+ mLightIndex = -1;
+ mDesaturation = 0;
+ mSrcBlend = GL_SRC_ALPHA;
+ mDstBlend = GL_ONE_MINUS_SRC_ALPHA;
+ mAlphaThreshold = 0.5f;
+ mBlendEquation = GL_FUNC_ADD;
+ mModelMatrixEnabled = false;
+ mTextureMatrixEnabled = false;
+ mObjectColor = 0xffffffff;
+ mVertexBuffer = mCurrentVertexBuffer = NULL;
+ mColormapState = CM_DEFAULT;
+ mSoftLight = 0;
+ mLightParms[0] = mLightParms[1] = mLightParms[2] = 0.0f;
+ mLightParms[3] = -1.f;
+ mSpecialEffect = EFF_NONE;
+ mClipHeight = 0.f;
+ mClipHeightDirection = 0.f;
+ mShaderTimer = 0.0f;
+ ClearClipSplit();
+
+ stSrcBlend = stDstBlend = -1;
+ stBlendEquation = -1;
+ stAlphaThreshold = -1.f;
+ stAlphaTest = 0;
+ mLastDepthClamp = true;
+ mInterpolationFactor = 0.0f;
+
+ mColor.Set(1.0f, 1.0f, 1.0f, 1.0f);
+ mCameraPos.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mGlowTop.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mGlowBottom.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mGlowTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mGlowBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mSplitTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mSplitBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mClipLine.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mDynColor.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ mEffectState = 0;
+ activeShader = nullptr;
+ mProjectionMatrix.loadIdentity();
+ mViewMatrix.loadIdentity();
+ mModelMatrix.loadIdentity();
+ mTextureMatrix.loadIdentity();
+ mPassType = NORMAL_PASS;
+}
+
+//==========================================================================
+//
+// Apply shader settings
+//
+//==========================================================================
+
+bool FRenderState::ApplyShader()
+{
+ static const float nulvec[] = { 0.f, 0.f, 0.f, 0.f };
+ if (mSpecialEffect > EFF_NONE)
+ {
+ activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect, mPassType);
+ }
+ else
+ {
+ activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : 4, mAlphaThreshold >= 0.f, mPassType);
+ activeShader->Bind();
+ }
+
+ int fogset = 0;
+
+ if (mFogEnabled)
+ {
+ if ((mFogColor & 0xffffff) == 0)
+ {
+ fogset = gl_fogmode;
+ }
+ else
+ {
+ fogset = -gl_fogmode;
+ }
+ }
+
+ glVertexAttrib4fv(VATTR_COLOR, mColor.vec);
+ glVertexAttrib4fv(VATTR_NORMAL, mNormal.vec);
+
+ activeShader->muDesaturation.Set(mDesaturation / 255.f);
+ activeShader->muFogEnabled.Set(fogset);
+ activeShader->muTextureMode.Set(mTextureMode);
+ activeShader->muCameraPos.Set(mCameraPos.vec);
+ activeShader->muLightParms.Set(mLightParms);
+ activeShader->muFogColor.Set(mFogColor);
+ activeShader->muObjectColor.Set(mObjectColor);
+ activeShader->muDynLightColor.Set(mDynColor.vec);
+ activeShader->muInterpolationFactor.Set(mInterpolationFactor);
+ activeShader->muClipHeight.Set(mClipHeight);
+ activeShader->muClipHeightDirection.Set(mClipHeightDirection);
+ activeShader->muTimer.Set(gl_frameMS * mShaderTimer / 1000.f);
+ activeShader->muAlphaThreshold.Set(mAlphaThreshold);
+ activeShader->muLightIndex.Set(mLightIndex); // will always be -1 for now
+ activeShader->muClipSplit.Set(mClipSplit);
+
+ if (mGlowEnabled)
+ {
+ activeShader->muGlowTopColor.Set(mGlowTop.vec);
+ activeShader->muGlowBottomColor.Set(mGlowBottom.vec);
+ activeShader->muGlowTopPlane.Set(mGlowTopPlane.vec);
+ activeShader->muGlowBottomPlane.Set(mGlowBottomPlane.vec);
+ activeShader->currentglowstate = 1;
+ }
+ else if (activeShader->currentglowstate)
+ {
+ // if glowing is on, disable it.
+ activeShader->muGlowTopColor.Set(nulvec);
+ activeShader->muGlowBottomColor.Set(nulvec);
+ activeShader->muGlowTopPlane.Set(nulvec);
+ activeShader->muGlowBottomPlane.Set(nulvec);
+ activeShader->currentglowstate = 0;
+ }
+
+ if (mSplitEnabled)
+ {
+ activeShader->muSplitTopPlane.Set(mSplitTopPlane.vec);
+ activeShader->muSplitBottomPlane.Set(mSplitBottomPlane.vec);
+ activeShader->currentsplitstate = 1;
+ }
+ else if (activeShader->currentsplitstate)
+ {
+ activeShader->muSplitTopPlane.Set(nulvec);
+ activeShader->muSplitBottomPlane.Set(nulvec);
+ activeShader->currentsplitstate = 0;
+ }
+
+ if (mClipLineEnabled)
+ {
+ activeShader->muClipLine.Set(mClipLine.vec);
+ activeShader->currentcliplinestate = 1;
+ }
+ else if (activeShader->currentcliplinestate)
+ {
+ activeShader->muClipLine.Set(-10000000.0, 0, 0, 0);
+ activeShader->currentcliplinestate = 0;
+ }
+
+ if (mColormapState != activeShader->currentfixedcolormap)
+ {
+ float r, g, b;
+ activeShader->currentfixedcolormap = mColormapState;
+ if (mColormapState == CM_DEFAULT)
+ {
+ activeShader->muFixedColormap.Set(0);
+ }
+ else if (mColormapState > CM_DEFAULT && mColormapState < CM_MAXCOLORMAP)
+ {
+ if (FGLRenderBuffers::IsEnabled())
+ {
+ // When using postprocessing to apply the colormap, we must render the image fullbright here.
+ activeShader->muFixedColormap.Set(2);
+ activeShader->muColormapStart.Set(1, 1, 1, 1.f);
+ }
+ else
+ {
+ FSpecialColormap *scm = &SpecialColormaps[gl_fixedcolormap - CM_FIRSTSPECIALCOLORMAP];
+ float m[] = { scm->ColorizeEnd[0] - scm->ColorizeStart[0],
+ scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f };
+
+ activeShader->muFixedColormap.Set(1);
+ activeShader->muColormapStart.Set(scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], 0.f);
+ activeShader->muColormapRange.Set(m);
+ }
+ }
+ else if (mColormapState == CM_FOGLAYER)
+ {
+ activeShader->muFixedColormap.Set(3);
+ }
+ else if (mColormapState == CM_LITE)
+ {
+ if (gl_enhanced_nightvision)
+ {
+ r = 0.375f, g = 1.0f, b = 0.375f;
+ }
+ else
+ {
+ r = g = b = 1.f;
+ }
+ activeShader->muFixedColormap.Set(2);
+ activeShader->muColormapStart.Set(r, g, b, 1.f);
+ }
+ else if (mColormapState >= CM_TORCH)
+ {
+ int flicker = mColormapState - CM_TORCH;
+ r = (0.8f + (7 - flicker) / 70.0f);
+ if (r > 1.0f) r = 1.0f;
+ b = g = r;
+ if (gl_enhanced_nightvision) b = g * 0.75f;
+ activeShader->muFixedColormap.Set(2);
+ activeShader->muColormapStart.Set(r, g, b, 1.f);
+ }
+ }
+ if (mTextureMatrixEnabled)
+ {
+ mTextureMatrix.matrixToGL(activeShader->texturematrix_index);
+ activeShader->currentTextureMatrixState = true;
+ }
+ else if (activeShader->currentTextureMatrixState)
+ {
+ activeShader->currentTextureMatrixState = false;
+ identityMatrix.matrixToGL(activeShader->texturematrix_index);
+ }
+
+ if (mModelMatrixEnabled)
+ {
+ mModelMatrix.matrixToGL(activeShader->modelmatrix_index);
+ VSMatrix norm;
+ norm.computeNormalMatrix(mModelMatrix);
+ mNormalModelMatrix.matrixToGL(activeShader->normalmodelmatrix_index);
+ activeShader->currentModelMatrixState = true;
+ }
+ else if (activeShader->currentModelMatrixState)
+ {
+ activeShader->currentModelMatrixState = false;
+ identityMatrix.matrixToGL(activeShader->modelmatrix_index);
+ identityMatrix.matrixToGL(activeShader->normalmodelmatrix_index);
+ }
+ return true;
+}
+
+
+//==========================================================================
+//
+// Apply State
+//
+//==========================================================================
+
+void FRenderState::Apply()
+{
+ if (!gl_direct_state_change)
+ {
+ if (mSrcBlend != stSrcBlend || mDstBlend != stDstBlend)
+ {
+ stSrcBlend = mSrcBlend;
+ stDstBlend = mDstBlend;
+ glBlendFunc(mSrcBlend, mDstBlend);
+ }
+ if (mBlendEquation != stBlendEquation)
+ {
+ stBlendEquation = mBlendEquation;
+ glBlendEquation(mBlendEquation);
+ }
+ }
+
+ //ApplyColorMask(); I don't think this is needed.
+
+ if (mVertexBuffer != mCurrentVertexBuffer)
+ {
+ if (mVertexBuffer == NULL) glBindBuffer(GL_ARRAY_BUFFER, 0);
+ else mVertexBuffer->BindVBO();
+ mCurrentVertexBuffer = mVertexBuffer;
+ }
+ if (!gl.legacyMode)
+ {
+ ApplyShader();
+ }
+ else
+ {
+ ApplyFixedFunction();
+ }
+}
+
+
+
+void FRenderState::ApplyColorMask()
+{
+ if ((mColorMask[0] != currentColorMask[0]) ||
+ (mColorMask[1] != currentColorMask[1]) ||
+ (mColorMask[2] != currentColorMask[2]) ||
+ (mColorMask[3] != currentColorMask[3]))
+ {
+ glColorMask(mColorMask[0], mColorMask[1], mColorMask[2], mColorMask[3]);
+ currentColorMask[0] = mColorMask[0];
+ currentColorMask[1] = mColorMask[1];
+ currentColorMask[2] = mColorMask[2];
+ currentColorMask[3] = mColorMask[3];
+ }
+}
+
+void FRenderState::ApplyMatrices()
+{
+ if (GLRenderer->mShaderManager != NULL)
+ {
+ GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix, mPassType);
+ }
+}
+
+void FRenderState::ApplyLightIndex(int index)
+{
+ if (!gl.legacyMode)
+ {
+ if (index > -1 && GLRenderer->mLights->GetBufferType() == GL_UNIFORM_BUFFER)
+ {
+ index = GLRenderer->mLights->BindUBO(index);
+ }
+ activeShader->muLightIndex.Set(index);
+ }
+}
+
+void FRenderState::SetClipHeight(float height, float direction)
+{
+ mClipHeight = height;
+ mClipHeightDirection = direction;
+#if 1
+ // This still doesn't work... :(
+ if (gl.flags & RFL_NO_CLIP_PLANES) return;
+#endif
+ if (direction != 0.f)
+ {
+ /*
+ if (gl.flags & RFL_NO_CLIP_PLANES)
+ {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadMatrixf(mViewMatrix.get());
+ // Plane mirrors never are slopes.
+ double d[4] = { 0, direction, 0, -direction * height };
+ glClipPlane(GL_CLIP_PLANE0, d);
+ glPopMatrix();
+ }
+ */
+ glEnable(GL_CLIP_DISTANCE0);
+ }
+ else
+ {
+ glDisable(GL_CLIP_DISTANCE0); // GL_CLIP_PLANE0 is the same value so no need to make a distinction
+ }
+}
diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h
new file mode 100644
index 000000000..76e2c791c
--- /dev/null
+++ b/src/gl/renderer/gl_renderstate.h
@@ -0,0 +1,518 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2009-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+
+#ifndef __GL_RENDERSTATE_H
+#define __GL_RENDERSTATE_H
+
+#include
+#include "gl/system/gl_interface.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_matrix.h"
+#include "gl/textures/gl_material.h"
+#include "c_cvars.h"
+#include "r_defs.h"
+#include "r_data/r_translate.h"
+
+class FVertexBuffer;
+class FShader;
+extern TArray gl_MatrixStack;
+
+EXTERN_CVAR(Bool, gl_direct_state_change)
+
+struct FStateVec4
+{
+ float vec[4];
+
+ void Set(float r, float g, float b, float a)
+ {
+ vec[0] = r;
+ vec[1] = g;
+ vec[2] = b;
+ vec[3] = a;
+ }
+};
+
+
+enum EEffect
+{
+ EFF_NONE=-1,
+ EFF_FOGBOUNDARY,
+ EFF_SPHEREMAP,
+ EFF_BURN,
+ EFF_STENCIL,
+
+ MAX_EFFECTS
+};
+
+enum EPassType
+{
+ NORMAL_PASS,
+ GBUFFER_PASS,
+ MAX_PASS_TYPES
+};
+
+class FRenderState
+{
+ bool mTextureEnabled;
+ bool mFogEnabled;
+ bool mGlowEnabled;
+ bool mSplitEnabled;
+ bool mClipLineEnabled;
+ bool mBrightmapEnabled;
+ bool mColorMask[4];
+ bool currentColorMask[4];
+ int mLightIndex;
+ int mSpecialEffect;
+ int mTextureMode;
+ int mDesaturation;
+ int mSoftLight;
+ float mLightParms[4];
+ int mSrcBlend, mDstBlend;
+ float mAlphaThreshold;
+ int mBlendEquation;
+ bool mModelMatrixEnabled;
+ bool mTextureMatrixEnabled;
+ bool mLastDepthClamp;
+ float mInterpolationFactor;
+ float mClipHeight, mClipHeightDirection;
+ float mShaderTimer;
+
+ FVertexBuffer *mVertexBuffer, *mCurrentVertexBuffer;
+ FStateVec4 mNormal;
+ FStateVec4 mColor;
+ FStateVec4 mCameraPos;
+ FStateVec4 mGlowTop, mGlowBottom;
+ FStateVec4 mGlowTopPlane, mGlowBottomPlane;
+ FStateVec4 mSplitTopPlane, mSplitBottomPlane;
+ FStateVec4 mClipLine;
+ PalEntry mFogColor;
+ PalEntry mObjectColor;
+ FStateVec4 mDynColor;
+ float mClipSplit[2];
+
+ int mEffectState;
+ int mColormapState;
+
+ float stAlphaThreshold;
+ int stSrcBlend, stDstBlend;
+ bool stAlphaTest;
+ int stBlendEquation;
+
+ FShader *activeShader;
+
+ EPassType mPassType = NORMAL_PASS;
+ int mNumDrawBuffers = 1;
+
+ bool ApplyShader();
+
+public:
+
+ VSMatrix mProjectionMatrix;
+ VSMatrix mViewMatrix;
+ VSMatrix mModelMatrix;
+ VSMatrix mTextureMatrix;
+ VSMatrix mNormalViewMatrix;
+ VSMatrix mNormalModelMatrix;
+
+ FRenderState()
+ {
+ Reset();
+ }
+
+ void Reset();
+
+ void SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader, bool alphatexture)
+ {
+ // textures without their own palette are a special case for use as an alpha texture:
+ // They use the color index directly as an alpha value instead of using the palette's red.
+ // To handle this case, we need to set a special translation for such textures.
+ // Without shaders this translation must be applied to any texture.
+ if (alphatexture)
+ {
+ if (mat->tex->UseBasePalette() || gl.legacyMode) translation = TRANSLATION(TRANSLATION_Standard, 8);
+ }
+ mEffectState = overrideshader >= 0? overrideshader : mat->mShaderIndex;
+ mShaderTimer = mat->tex->gl_info.shaderspeed;
+ mat->Bind(clampmode, translation);
+ }
+
+ void Apply();
+ void ApplyColorMask();
+ void ApplyMatrices();
+ void ApplyLightIndex(int index);
+
+ void SetVertexBuffer(FVertexBuffer *vb)
+ {
+ mVertexBuffer = vb;
+ }
+
+ void ResetVertexBuffer()
+ {
+ // forces rebinding with the next 'apply' call.
+ mCurrentVertexBuffer = NULL;
+ }
+
+ float GetClipHeight()
+ {
+ return mClipHeight;
+ }
+
+ float GetClipHeightDirection()
+ {
+ return mClipHeightDirection;
+ }
+
+ FStateVec4 &GetClipLine()
+ {
+ return mClipLine;
+ }
+
+ bool GetClipLineState()
+ {
+ return mClipLineEnabled;
+ }
+
+ void SetClipHeight(float height, float direction);
+
+ void SetNormal(FVector3 norm)
+ {
+ mNormal.Set(norm.X, norm.Y, norm.Z, 0.f);
+ }
+
+ void SetNormal(float x, float y, float z)
+ {
+ mNormal.Set(x, y, z, 0.f);
+ }
+
+ void SetColor(float r, float g, float b, float a = 1.f, int desat = 0)
+ {
+ mColor.Set(r, g, b, a);
+ mDesaturation = desat;
+ }
+
+ void SetColor(PalEntry pe, int desat = 0)
+ {
+ mColor.Set(pe.r/255.f, pe.g/255.f, pe.b/255.f, pe.a/255.f);
+ mDesaturation = desat;
+ }
+
+ void SetColorAlpha(PalEntry pe, float alpha = 1.f, int desat = 0)
+ {
+ mColor.Set(pe.r/255.f, pe.g/255.f, pe.b/255.f, alpha);
+ mDesaturation = desat;
+ }
+
+ void ResetColor()
+ {
+ mColor.Set(1,1,1,1);
+ mDesaturation = 0;
+ }
+
+ void GetColorMask(bool& r, bool &g, bool& b, bool& a) const
+ {
+ r = mColorMask[0];
+ g = mColorMask[1];
+ b = mColorMask[2];
+ a = mColorMask[3];
+ }
+
+ void SetColorMask(bool r, bool g, bool b, bool a)
+ {
+ mColorMask[0] = r;
+ mColorMask[1] = g;
+ mColorMask[2] = b;
+ mColorMask[3] = a;
+ }
+
+ void ResetColorMask()
+ {
+ for (int i = 0; i < 4; ++i)
+ mColorMask[i] = true;
+ }
+
+ void SetTextureMode(int mode)
+ {
+ mTextureMode = mode;
+ }
+
+ int GetTextureMode()
+ {
+ return mTextureMode;
+ }
+
+ void EnableTexture(bool on)
+ {
+ mTextureEnabled = on;
+ }
+
+ void EnableFog(bool on)
+ {
+ mFogEnabled = on;
+ }
+
+ void SetEffect(int eff)
+ {
+ mSpecialEffect = eff;
+ }
+
+ void EnableGlow(bool on)
+ {
+ mGlowEnabled = on;
+ }
+
+ void EnableSplit(bool on)
+ {
+ if (!(gl.flags & RFL_NO_CLIP_PLANES))
+ {
+ mSplitEnabled = on;
+ if (on)
+ {
+ glEnable(GL_CLIP_DISTANCE3);
+ glEnable(GL_CLIP_DISTANCE4);
+ }
+ else
+ {
+ glDisable(GL_CLIP_DISTANCE3);
+ glDisable(GL_CLIP_DISTANCE4);
+ }
+ }
+ }
+
+ void SetClipLine(line_t *line)
+ {
+ mClipLine.Set(line->v1->fX(), line->v1->fY(), line->Delta().X, line->Delta().Y);
+ }
+
+ void EnableClipLine(bool on)
+ {
+ if (!(gl.flags & RFL_NO_CLIP_PLANES))
+ {
+ mClipLineEnabled = on;
+ if (on)
+ {
+ glEnable(GL_CLIP_DISTANCE0);
+ }
+ else
+ {
+ glDisable(GL_CLIP_DISTANCE0);
+ }
+ }
+ }
+
+ void SetLightIndex(int n)
+ {
+ mLightIndex = n;
+ }
+
+ void EnableBrightmap(bool on)
+ {
+ mBrightmapEnabled = on;
+ }
+
+ void EnableModelMatrix(bool on)
+ {
+ mModelMatrixEnabled = on;
+ }
+
+ void EnableTextureMatrix(bool on)
+ {
+ mTextureMatrixEnabled = on;
+ }
+
+ void SetCameraPos(float x, float y, float z)
+ {
+ mCameraPos.Set(x, z, y, 0);
+ }
+
+ void SetGlowParams(float *t, float *b)
+ {
+ mGlowTop.Set(t[0], t[1], t[2], t[3]);
+ mGlowBottom.Set(b[0], b[1], b[2], b[3]);
+ }
+
+ void SetSoftLightLevel(int level)
+ {
+ if (glset.lightmode == 8) mLightParms[3] = level / 255.f;
+ else mLightParms[3] = -1.f;
+ }
+
+ void SetGlowPlanes(const secplane_t &top, const secplane_t &bottom)
+ {
+ DVector3 tn = top.Normal();
+ DVector3 bn = bottom.Normal();
+ mGlowTopPlane.Set(tn.X, tn.Y, 1. / tn.Z, top.fD());
+ mGlowBottomPlane.Set(bn.X, bn.Y, 1. / bn.Z, bottom.fD());
+ }
+
+ void SetSplitPlanes(const secplane_t &top, const secplane_t &bottom)
+ {
+ DVector3 tn = top.Normal();
+ DVector3 bn = bottom.Normal();
+ mSplitTopPlane.Set(tn.X, tn.Y, 1. / tn.Z, top.fD());
+ mSplitBottomPlane.Set(bn.X, bn.Y, 1. / bn.Z, bottom.fD());
+ }
+
+ void SetDynLight(float r, float g, float b)
+ {
+ mDynColor.Set(r, g, b, 0);
+ }
+
+ void SetObjectColor(PalEntry pe)
+ {
+ mObjectColor = pe;
+ }
+
+ void SetFog(PalEntry c, float d)
+ {
+ const float LOG2E = 1.442692f; // = 1/log(2)
+ mFogColor = c;
+ if (d >= 0.0f) mLightParms[2] = d * (-LOG2E / 64000.f);
+ }
+
+ void SetLightParms(float f, float d)
+ {
+ mLightParms[1] = f;
+ mLightParms[0] = d;
+ }
+
+ void SetFixedColormap(int cm)
+ {
+ mColormapState = cm;
+ }
+
+ int GetFixedColormap()
+ {
+ return mColormapState;
+ }
+
+ PalEntry GetFogColor() const
+ {
+ return mFogColor;
+ }
+
+ void SetClipSplit(float bottom, float top)
+ {
+ mClipSplit[0] = bottom;
+ mClipSplit[1] = top;
+ }
+
+ void SetClipSplit(float *vals)
+ {
+ memcpy(mClipSplit, vals, 2 * sizeof(float));
+ }
+
+ void GetClipSplit(float *out)
+ {
+ memcpy(out, mClipSplit, 2 * sizeof(float));
+ }
+
+ void ClearClipSplit()
+ {
+ mClipSplit[0] = -1000000.f;
+ mClipSplit[1] = 1000000.f;
+ }
+
+ void BlendFunc(int src, int dst)
+ {
+ if (!gl_direct_state_change)
+ {
+ mSrcBlend = src;
+ mDstBlend = dst;
+ }
+ else
+ {
+ glBlendFunc(src, dst);
+ }
+ }
+
+ void AlphaFunc(int func, float thresh)
+ {
+ if (func == GL_GREATER) mAlphaThreshold = thresh;
+ else mAlphaThreshold = thresh - 0.001f;
+ }
+
+ void BlendEquation(int eq)
+ {
+ if (!gl_direct_state_change)
+ {
+ mBlendEquation = eq;
+ }
+ else
+ {
+ glBlendEquation(eq);
+ }
+ }
+
+ // This wraps the depth clamp setting because we frequently need to read it which OpenGL is not particularly performant at...
+ bool SetDepthClamp(bool on)
+ {
+ bool res = mLastDepthClamp;
+ if (!on) glDisable(GL_DEPTH_CLAMP);
+ else glEnable(GL_DEPTH_CLAMP);
+ mLastDepthClamp = on;
+ return res;
+ }
+
+ void SetInterpolationFactor(float fac)
+ {
+ mInterpolationFactor = fac;
+ }
+
+ float GetInterpolationFactor()
+ {
+ return mInterpolationFactor;
+ }
+
+ void SetPassType(EPassType passType)
+ {
+ mPassType = passType;
+ }
+
+ EPassType GetPassType()
+ {
+ return mPassType;
+ }
+
+ void EnableDrawBuffers(int count)
+ {
+ count = MIN(count, 3);
+ if (mNumDrawBuffers != count)
+ {
+ static GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
+ glDrawBuffers(count, buffers);
+ mNumDrawBuffers = count;
+ }
+ }
+
+ int GetPassDrawBufferCount()
+ {
+ return mPassType == GBUFFER_PASS ? 3 : 1;
+ }
+
+ // Backwards compatibility crap follows
+ void ApplyFixedFunction();
+ void DrawColormapOverlay();
+};
+
+extern FRenderState gl_RenderState;
+
+#endif
diff --git a/src/gl/scene/gl_bsp.cpp b/src/gl/scene/gl_bsp.cpp
new file mode 100644
index 000000000..9e5ea258d
--- /dev/null
+++ b/src/gl/scene/gl_bsp.cpp
@@ -0,0 +1,592 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2000-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_bsp.cpp
+** Main rendering loop / BSP traversal / visibility clipping
+**
+**/
+
+#include "p_lnspec.h"
+#include "p_local.h"
+#include "a_sharedglobal.h"
+#include "r_sky.h"
+#include "p_effect.h"
+#include "po_man.h"
+#include "doomdata.h"
+
+#include "gl/renderer/gl_renderer.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/scene/gl_clipper.h"
+#include "gl/scene/gl_portal.h"
+#include "gl/scene/gl_wall.h"
+#include "gl/utility/gl_clock.h"
+
+EXTERN_CVAR(Bool, gl_render_segs)
+
+Clipper clipper;
+
+
+CVAR(Bool, gl_render_things, true, 0)
+CVAR(Bool, gl_render_walls, true, 0)
+CVAR(Bool, gl_render_flats, true, 0)
+
+extern fixed_t viewx, viewy;
+
+static void UnclipSubsector(subsector_t *sub)
+{
+ int count = sub->numlines;
+ seg_t * seg = sub->firstline;
+
+ while (count--)
+ {
+ angle_t startAngle = seg->v2->GetClipAngle();
+ angle_t endAngle = seg->v1->GetClipAngle();
+
+ // Back side, i.e. backface culling - read: endAngle >= startAngle!
+ if (startAngle-endAngle >= ANGLE_180)
+ {
+ clipper.SafeRemoveClipRange(startAngle, endAngle);
+ clipper.SetBlocked(false);
+ }
+ seg++;
+ }
+}
+
+//==========================================================================
+//
+// R_AddLine
+// Clips the given segment
+// and adds any visible pieces to the line list.
+//
+//==========================================================================
+
+// making these 2 variables global instead of passing them as function parameters is faster.
+static subsector_t *currentsubsector;
+static sector_t *currentsector;
+
+static void AddLine (seg_t *seg, bool portalclip)
+{
+#ifdef _DEBUG
+ if (seg->linedef - lines == 38)
+ {
+ int a = 0;
+ }
+#endif
+
+ angle_t startAngle, endAngle;
+ sector_t * backsector = NULL;
+ sector_t bs;
+
+ if (portalclip)
+ {
+ int clipres = GLRenderer->mClipPortal->ClipSeg(seg);
+ if (clipres == GLPortal::PClip_InFront) return;
+ }
+
+ startAngle = seg->v2->GetClipAngle();
+ endAngle = seg->v1->GetClipAngle();
+
+ // Back side, i.e. backface culling - read: endAngle >= startAngle!
+ if (startAngle-endAnglesidedef == NULL)
+ {
+ if (!(currentsubsector->flags & SSECF_DRAWN))
+ {
+ if (clipper.SafeCheckRange(startAngle, endAngle))
+ {
+ currentsubsector->flags |= SSECF_DRAWN;
+ }
+ }
+ return;
+ }
+
+ if (!clipper.SafeCheckRange(startAngle, endAngle))
+ {
+ return;
+ }
+ currentsubsector->flags |= SSECF_DRAWN;
+
+ BYTE ispoly = BYTE(seg->sidedef->Flags & WALLF_POLYOBJ);
+
+ if (!seg->backsector)
+ {
+ clipper.SafeAddClipRange(startAngle, endAngle);
+ }
+ else if (!ispoly) // Two-sided polyobjects never obstruct the view
+ {
+ if (currentsector->sectornum == seg->backsector->sectornum)
+ {
+ if (!seg->linedef->isVisualPortal())
+ {
+ FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::mid));
+ if (!tex || tex->UseType==FTexture::TEX_Null)
+ {
+ // nothing to do here!
+ seg->linedef->validcount=validcount;
+ return;
+ }
+ }
+ backsector=currentsector;
+ }
+ else
+ {
+ // clipping checks are only needed when the backsector is not the same as the front sector
+ gl_CheckViewArea(seg->v1, seg->v2, seg->frontsector, seg->backsector);
+
+ backsector = gl_FakeFlat(seg->backsector, &bs, true);
+
+ if (gl_CheckClip(seg->sidedef, currentsector, backsector))
+ {
+ clipper.SafeAddClipRange(startAngle, endAngle);
+ }
+ }
+ }
+ else
+ {
+ // Backsector for polyobj segs is always the containing sector itself
+ backsector = currentsector;
+ }
+
+ seg->linedef->flags |= ML_MAPPED;
+
+ if (ispoly || seg->linedef->validcount!=validcount)
+ {
+ if (!ispoly) seg->linedef->validcount=validcount;
+
+ if (gl_render_walls)
+ {
+ SetupWall.Clock();
+
+ GLWall wall;
+ wall.sub = currentsubsector;
+ wall.Process(seg, currentsector, backsector);
+ rendered_lines++;
+
+ SetupWall.Unclock();
+ }
+ }
+}
+
+//==========================================================================
+//
+// R_Subsector
+// Determine floor/ceiling planes.
+// Add sprites of things in sector.
+// Draw one or more line segments.
+//
+//==========================================================================
+
+static void PolySubsector(subsector_t * sub)
+{
+ int count = sub->numlines;
+ seg_t * line = sub->firstline;
+
+ while (count--)
+ {
+ if (line->linedef)
+ {
+ AddLine (line, GLRenderer->mClipPortal != NULL);
+ }
+ line++;
+ }
+}
+
+//==========================================================================
+//
+// RenderBSPNode
+// Renders all subsectors below a given node,
+// traversing subtree recursively.
+// Just call with BSP root.
+//
+//==========================================================================
+
+static void RenderPolyBSPNode (void *node)
+{
+ while (!((size_t)node & 1)) // Keep going until found a subsector
+ {
+ node_t *bsp = (node_t *)node;
+
+ // Decide which side the view point is on.
+ int side = R_PointOnSide(viewx, viewy, bsp);
+
+ // Recursively divide front space (toward the viewer).
+ RenderPolyBSPNode (bsp->children[side]);
+
+ // Possibly divide back space (away from the viewer).
+ side ^= 1;
+
+ // It is not necessary to use the slower precise version here
+ if (!clipper.CheckBox(bsp->bbox[side]))
+ {
+ return;
+ }
+
+ node = bsp->children[side];
+ }
+ PolySubsector ((subsector_t *)((BYTE *)node - 1));
+}
+
+//==========================================================================
+//
+// Unlilke the software renderer this function will only draw the walls,
+// not the flats. Those are handled as a whole by the parent subsector.
+//
+//==========================================================================
+
+static void AddPolyobjs(subsector_t *sub)
+{
+ if (sub->BSP == NULL || sub->BSP->bDirty)
+ {
+ sub->BuildPolyBSP();
+ for (unsigned i = 0; i < sub->BSP->Segs.Size(); i++)
+ {
+ sub->BSP->Segs[i].Subsector = sub;
+ sub->BSP->Segs[i].PartnerSeg = NULL;
+ }
+ }
+ if (sub->BSP->Nodes.Size() == 0)
+ {
+ PolySubsector(&sub->BSP->Subsectors[0]);
+ }
+ else
+ {
+ RenderPolyBSPNode(&sub->BSP->Nodes.Last());
+ }
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+static inline void AddLines(subsector_t * sub, sector_t * sector)
+{
+ currentsector = sector;
+ currentsubsector = sub;
+
+ ClipWall.Clock();
+ if (sub->polys != NULL)
+ {
+ AddPolyobjs(sub);
+ }
+ else
+ {
+ int count = sub->numlines;
+ seg_t * seg = sub->firstline;
+
+ while (count--)
+ {
+ if (seg->linedef == NULL)
+ {
+ if (!(sub->flags & SSECF_DRAWN)) AddLine (seg, GLRenderer->mClipPortal != NULL);
+ }
+ else if (!(seg->sidedef->Flags & WALLF_POLYOBJ))
+ {
+ AddLine (seg, GLRenderer->mClipPortal != NULL);
+ }
+ seg++;
+ }
+ }
+ ClipWall.Unclock();
+}
+
+//==========================================================================
+//
+// Adds lines that lie directly on the portal boundary.
+// Only two-sided lines will be handled here, and no polyobjects
+//
+//==========================================================================
+
+inline bool PointOnLine(const DVector2 &pos, const line_t *line)
+{
+ double v = (pos.Y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - pos.X) * line->Delta().Y;
+ return fabs(v) <= EQUAL_EPSILON;
+}
+
+static inline void AddSpecialPortalLines(subsector_t * sub, sector_t * sector, line_t *line)
+{
+ currentsector = sector;
+ currentsubsector = sub;
+
+ ClipWall.Clock();
+ int count = sub->numlines;
+ seg_t * seg = sub->firstline;
+
+ while (count--)
+ {
+ if (seg->linedef != NULL && seg->PartnerSeg != NULL)
+ {
+ if (PointOnLine(seg->v1->fPos(), line) && PointOnLine(seg->v2->fPos(), line))
+ AddLine(seg, false);
+ }
+ seg++;
+ }
+ ClipWall.Unclock();
+}
+
+
+//==========================================================================
+//
+// R_RenderThings
+//
+//==========================================================================
+
+static inline void RenderThings(subsector_t * sub, sector_t * sector)
+{
+
+ SetupSprite.Clock();
+ sector_t * sec=sub->sector;
+ // Handle all things in sector.
+ for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext)
+ {
+ auto thing = p->m_thing;
+ if (thing->validcount == validcount) continue;
+ thing->validcount = validcount;
+
+ FIntCVar *cvar = thing->GetClass()->distancecheck;
+ if (cvar != NULL && *cvar >= 0)
+ {
+ double dist = (thing->Pos() - ViewPos).LengthSquared();
+ double check = (double)**cvar;
+ if (dist >= check * check)
+ {
+ continue;
+ }
+ }
+
+ GLRenderer->ProcessSprite(thing, sector, false);
+ }
+
+ for (msecnode_t *node = sec->render_thinglist; node; node = node->m_snext)
+ {
+ AActor *thing = node->m_thing;
+ FIntCVar *cvar = thing->GetClass()->distancecheck;
+ if (cvar != NULL && *cvar >= 0)
+ {
+ double dist = (thing->Pos() - ViewPos).LengthSquared();
+ double check = (double)**cvar;
+ if (dist >= check * check)
+ {
+ continue;
+ }
+ }
+
+ GLRenderer->ProcessSprite(thing, sector, true);
+ }
+ SetupSprite.Unclock();
+}
+
+
+//==========================================================================
+//
+// R_Subsector
+// Determine floor/ceiling planes.
+// Add sprites of things in sector.
+// Draw one or more line segments.
+//
+//==========================================================================
+
+static void DoSubsector(subsector_t * sub)
+{
+ unsigned int i;
+ sector_t * sector;
+ sector_t * fakesector;
+ sector_t fake;
+
+#ifdef _DEBUG
+ if (sub->sector-sectors==931)
+ {
+ int a = 0;
+ }
+#endif
+
+ sector=sub->sector;
+ if (!sector) return;
+
+ // If the mapsections differ this subsector can't possibly be visible from the current view point
+ if (!(currentmapsection[sub->mapsection>>3] & (1 << (sub->mapsection & 7)))) return;
+ if (sub->flags & SSECF_POLYORG) return; // never render polyobject origin subsectors because their vertices no longer are where one may expect.
+
+ if (gl_drawinfo->ss_renderflags[sub-subsectors] & SSRF_SEEN)
+ {
+ // This means that we have reached a subsector in a portal that has been marked 'seen'
+ // from the other side of the portal. This means we must clear the clipper for the
+ // range this subsector spans before going on.
+ UnclipSubsector(sub);
+ }
+ if (clipper.IsBlocked()) return; // if we are inside a stacked sector portal which hasn't unclipped anything yet.
+
+ fakesector=gl_FakeFlat(sector, &fake, false);
+
+ if (GLRenderer->mClipPortal)
+ {
+ int clipres = GLRenderer->mClipPortal->ClipSubsector(sub);
+ if (clipres == GLPortal::PClip_InFront)
+ {
+ line_t *line = GLRenderer->mClipPortal->ClipLine();
+ // The subsector is out of range, but we still have to check lines that lie directly on the boundary and may expose their upper or lower parts.
+ if (line) AddSpecialPortalLines(sub, fakesector, line);
+ return;
+ }
+ }
+
+ if (sector->validcount != validcount)
+ {
+ GLRenderer->mVBO->CheckUpdate(sector);
+ }
+
+ // [RH] Add particles
+ //int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight);
+ if (gl_render_things)
+ {
+ SetupSprite.Clock();
+
+ for (i = ParticlesInSubsec[DWORD(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext)
+ {
+ GLRenderer->ProcessParticle(&Particles[i], fakesector);
+ }
+ SetupSprite.Unclock();
+ }
+
+ AddLines(sub, fakesector);
+
+ // BSP is traversed by subsector.
+ // A sector might have been split into several
+ // subsectors during BSP building.
+ // Thus we check whether it was already added.
+ if (sector->validcount != validcount)
+ {
+ // Well, now it will be done.
+ sector->validcount = validcount;
+
+ if (gl_render_things)
+ {
+ RenderThings(sub, fakesector);
+ }
+ sector->MoreFlags |= SECF_DRAWN;
+ }
+
+ if (gl_render_flats)
+ {
+ // Subsectors with only 2 lines cannot have any area
+ if (sub->numlines>2 || (sub->hacked&1))
+ {
+ // Exclude the case when it tries to render a sector with a heightsec
+ // but undetermined heightsec state. This can only happen if the
+ // subsector is obstructed but not excluded due to a large bounding box.
+ // Due to the way a BSP works such a subsector can never be visible
+ if (!sector->heightsec || sector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC || in_area!=area_default)
+ {
+ if (sector != sub->render_sector)
+ {
+ sector = sub->render_sector;
+ // the planes of this subsector are faked to belong to another sector
+ // This means we need the heightsec parts and light info of the render sector, not the actual one.
+ fakesector = gl_FakeFlat(sector, &fake, false);
+ }
+
+ BYTE &srf = gl_drawinfo->sectorrenderflags[sub->render_sector->sectornum];
+ if (!(srf & SSRF_PROCESSED))
+ {
+ srf |= SSRF_PROCESSED;
+
+ SetupFlat.Clock();
+ GLRenderer->ProcessSector(fakesector);
+ SetupFlat.Unclock();
+ }
+ // mark subsector as processed - but mark for rendering only if it has an actual area.
+ gl_drawinfo->ss_renderflags[sub-subsectors] =
+ (sub->numlines > 2) ? SSRF_PROCESSED|SSRF_RENDERALL : SSRF_PROCESSED;
+ if (sub->hacked & 1) gl_drawinfo->AddHackedSubsector(sub);
+
+ FPortal *portal;
+
+ portal = fakesector->GetGLPortal(sector_t::ceiling);
+ if (portal != NULL)
+ {
+ GLSectorStackPortal *glportal = portal->GetRenderState();
+ glportal->AddSubsector(sub);
+ }
+
+ portal = fakesector->GetGLPortal(sector_t::floor);
+ if (portal != NULL)
+ {
+ GLSectorStackPortal *glportal = portal->GetRenderState();
+ glportal->AddSubsector(sub);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+//==========================================================================
+//
+// RenderBSPNode
+// Renders all subsectors below a given node,
+// traversing subtree recursively.
+// Just call with BSP root.
+//
+//==========================================================================
+
+void gl_RenderBSPNode (void *node)
+{
+ if (numnodes == 0)
+ {
+ DoSubsector (subsectors);
+ return;
+ }
+ while (!((size_t)node & 1)) // Keep going until found a subsector
+ {
+ node_t *bsp = (node_t *)node;
+
+ // Decide which side the view point is on.
+ int side = R_PointOnSide(viewx, viewy, bsp);
+
+ // Recursively divide front space (toward the viewer).
+ gl_RenderBSPNode (bsp->children[side]);
+
+ // Possibly divide back space (away from the viewer).
+ side ^= 1;
+
+ // It is not necessary to use the slower precise version here
+ if (!clipper.CheckBox(bsp->bbox[side]))
+ {
+ if (!(gl_drawinfo->no_renderflags[bsp-nodes] & SSRF_SEEN))
+ return;
+ }
+
+ node = bsp->children[side];
+ }
+ DoSubsector ((subsector_t *)((BYTE *)node - 1));
+}
+
+
diff --git a/src/gl/scene/gl_clipper.cpp b/src/gl/scene/gl_clipper.cpp
new file mode 100644
index 000000000..cd076d58a
--- /dev/null
+++ b/src/gl/scene/gl_clipper.cpp
@@ -0,0 +1,465 @@
+/*
+*
+** gl_clipper.cpp
+**
+** Handles visibility checks.
+** Loosely based on the JDoom clipper.
+**
+**---------------------------------------------------------------------------
+** Copyright 2003 Tim Stump
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/
+
+#include "gl/scene/gl_clipper.h"
+
+
+
+ClipNode * ClipNode::freelist;
+int Clipper::anglecache;
+
+
+//-----------------------------------------------------------------------------
+//
+// Destructor
+//
+//-----------------------------------------------------------------------------
+
+Clipper::~Clipper()
+{
+ Clear();
+ while (ClipNode::freelist != NULL)
+ {
+ ClipNode * node = ClipNode::freelist;
+ ClipNode::freelist = node->next;
+ delete node;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// RemoveRange
+//
+//-----------------------------------------------------------------------------
+
+void Clipper::RemoveRange(ClipNode * range)
+{
+ if (range == cliphead)
+ {
+ cliphead = cliphead->next;
+ }
+ else
+ {
+ if (range->prev) range->prev->next = range->next;
+ if (range->next) range->next->prev = range->prev;
+ }
+
+ range->Free();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Clear
+//
+//-----------------------------------------------------------------------------
+
+void Clipper::Clear()
+{
+ ClipNode *node = cliphead;
+ ClipNode *temp;
+
+ blocked = false;
+ while (node != NULL)
+ {
+ temp = node;
+ node = node->next;
+ temp->Free();
+ }
+ node = silhouette;
+
+ while (node != NULL)
+ {
+ temp = node;
+ node = node->next;
+ temp->Free();
+ }
+
+ cliphead = NULL;
+ silhouette = NULL;
+ anglecache++;
+}
+
+//-----------------------------------------------------------------------------
+//
+// SetSilhouette
+//
+//-----------------------------------------------------------------------------
+
+void Clipper::SetSilhouette()
+{
+ ClipNode *node = cliphead;
+ ClipNode *last = NULL;
+
+ while (node != NULL)
+ {
+ ClipNode *snode = ClipNode::NewRange(node->start, node->end);
+ if (silhouette == NULL) silhouette = snode;
+ snode->prev = last;
+ if (last != NULL) last->next = snode;
+ last = snode;
+ node = node->next;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// IsRangeVisible
+//
+//-----------------------------------------------------------------------------
+
+bool Clipper::IsRangeVisible(angle_t startAngle, angle_t endAngle)
+{
+ ClipNode *ci;
+ ci = cliphead;
+
+ if (endAngle==0 && ci && ci->start==0) return false;
+
+ while (ci != NULL && ci->start < endAngle)
+ {
+ if (startAngle >= ci->start && endAngle <= ci->end)
+ {
+ return false;
+ }
+ ci = ci->next;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//
+// AddClipRange
+//
+//-----------------------------------------------------------------------------
+
+void Clipper::AddClipRange(angle_t start, angle_t end)
+{
+ ClipNode *node, *temp, *prevNode;
+
+ if (cliphead)
+ {
+ //check to see if range contains any old ranges
+ node = cliphead;
+ while (node != NULL && node->start < end)
+ {
+ if (node->start >= start && node->end <= end)
+ {
+ temp = node;
+ node = node->next;
+ RemoveRange(temp);
+ }
+ else if (node->start<=start && node->end>=end)
+ {
+ return;
+ }
+ else
+ {
+ node = node->next;
+ }
+ }
+
+ //check to see if range overlaps a range (or possibly 2)
+ node = cliphead;
+ while (node != NULL && node->start <= end)
+ {
+ if (node->end >= start)
+ {
+ // we found the first overlapping node
+ if (node->start > start)
+ {
+ // the new range overlaps with this node's start point
+ node->start = start;
+ }
+
+ if (node->end < end)
+ {
+ node->end = end;
+ }
+
+ ClipNode *node2 = node->next;
+ while (node2 && node2->start <= node->end)
+ {
+ if (node2->end > node->end) node->end = node2->end;
+ ClipNode *delnode = node2;
+ node2 = node2->next;
+ RemoveRange(delnode);
+ }
+ return;
+ }
+ node = node->next;
+ }
+
+ //just add range
+ node = cliphead;
+ prevNode = NULL;
+ temp = ClipNode::NewRange(start, end);
+
+ while (node != NULL && node->start < end)
+ {
+ prevNode = node;
+ node = node->next;
+ }
+
+ temp->next = node;
+ if (node == NULL)
+ {
+ temp->prev = prevNode;
+ if (prevNode) prevNode->next = temp;
+ if (!cliphead) cliphead = temp;
+ }
+ else
+ {
+ if (node == cliphead)
+ {
+ cliphead->prev = temp;
+ cliphead = temp;
+ }
+ else
+ {
+ temp->prev = prevNode;
+ prevNode->next = temp;
+ node->prev = temp;
+ }
+ }
+ }
+ else
+ {
+ temp = ClipNode::NewRange(start, end);
+ cliphead = temp;
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// RemoveClipRange
+//
+//-----------------------------------------------------------------------------
+
+void Clipper::RemoveClipRange(angle_t start, angle_t end)
+{
+ ClipNode *node;
+
+ if (silhouette)
+ {
+ node = silhouette;
+ while (node != NULL && node->end <= start)
+ {
+ node = node->next;
+ }
+ if (node != NULL && node->start <= start)
+ {
+ if (node->end >= end) return;
+ start = node->end;
+ node = node->next;
+ }
+ while (node != NULL && node->start < end)
+ {
+ DoRemoveClipRange(start, node->start);
+ start = node->end;
+ node = node->next;
+ }
+ if (start >= end) return;
+ }
+ DoRemoveClipRange(start, end);
+}
+
+//-----------------------------------------------------------------------------
+//
+// RemoveClipRange worker function
+//
+//-----------------------------------------------------------------------------
+
+void Clipper::DoRemoveClipRange(angle_t start, angle_t end)
+{
+ ClipNode *node, *temp;
+
+ if (cliphead)
+ {
+ //check to see if range contains any old ranges
+ node = cliphead;
+ while (node != NULL && node->start < end)
+ {
+ if (node->start >= start && node->end <= end)
+ {
+ temp = node;
+ node = node->next;
+ RemoveRange(temp);
+ }
+ else
+ {
+ node = node->next;
+ }
+ }
+
+ //check to see if range overlaps a range (or possibly 2)
+ node = cliphead;
+ while (node != NULL)
+ {
+ if (node->start >= start && node->start <= end)
+ {
+ node->start = end;
+ break;
+ }
+ else if (node->end >= start && node->end <= end)
+ {
+ node->end=start;
+ }
+ else if (node->start < start && node->end > end)
+ {
+ temp=ClipNode::NewRange(end, node->end);
+ node->end=start;
+ temp->next=node->next;
+ temp->prev=node;
+ node->next=temp;
+ if (temp->next) temp->next->prev=temp;
+ break;
+ }
+ node = node->next;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
+angle_t Clipper::AngleToPseudo(angle_t ang)
+{
+ double vecx = cos(ang * M_PI / ANGLE_180);
+ double vecy = sin(ang * M_PI / ANGLE_180);
+
+ double result = vecy / (fabs(vecx) + fabs(vecy));
+ if (vecx < 0)
+ {
+ result = 2.f - result;
+ }
+ return xs_Fix<30>::ToFix(result);
+}
+
+//-----------------------------------------------------------------------------
+//
+// ! Returns the pseudoangle between the line p1 to (infinity, p1.y) and the
+// line from p1 to p2. The pseudoangle has the property that the ordering of
+// points by true angle around p1 and ordering of points by pseudoangle are the
+// same.
+//
+// For clipping exact angles are not needed. Only the ordering matters.
+// This is about as fast as the fixed point R_PointToAngle2 but without
+// the precision issues associated with that function.
+//
+//-----------------------------------------------------------------------------
+
+fixed_t viewx, viewy;
+
+void R_SetView()
+{
+ viewx = FLOAT2FIXED(ViewPos.X);
+ viewy = FLOAT2FIXED(ViewPos.Y);
+}
+
+angle_t R_PointToPseudoAngle(double x, double y)
+{
+ double vecx = x - ViewPos.X;
+ double vecy = y - ViewPos.Y;
+
+ if (vecx == 0 && vecy == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ double result = vecy / (fabs(vecx) + fabs(vecy));
+ if (vecx < 0)
+ {
+ result = 2.f - result;
+ }
+ return xs_Fix<30>::ToFix(result);
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// R_CheckBBox
+// Checks BSP node/subtree bounding box.
+// Returns true
+// if some part of the bbox might be visible.
+//
+//-----------------------------------------------------------------------------
+ static const BYTE checkcoord[12][4] = // killough -- static const
+ {
+ {3,0,2,1},
+ {3,0,2,0},
+ {3,1,2,0},
+ {0},
+ {2,0,2,1},
+ {0,0,0,0},
+ {3,1,3,0},
+ {0},
+ {2,0,3,1},
+ {2,1,3,1},
+ {2,1,3,0}
+ };
+
+bool Clipper::CheckBox(const float *bspcoord)
+{
+ angle_t angle1, angle2;
+
+ int boxpos;
+ const BYTE* check;
+
+ // Find the corners of the box
+ // that define the edges from current viewpoint.
+ boxpos = (ViewPos.X <= bspcoord[BOXLEFT] ? 0 : ViewPos.X < bspcoord[BOXRIGHT ] ? 1 : 2) +
+ (ViewPos.Y >= bspcoord[BOXTOP ] ? 0 : ViewPos.Y > bspcoord[BOXBOTTOM] ? 4 : 8);
+
+ if (boxpos == 5) return true;
+
+ check = checkcoord[boxpos];
+ angle1 = R_PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[1]]);
+ angle2 = R_PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[3]]);
+
+ return SafeCheckRange(angle2, angle1);
+}
+
diff --git a/src/gl/scene/gl_clipper.h b/src/gl/scene/gl_clipper.h
new file mode 100644
index 000000000..8da75080e
--- /dev/null
+++ b/src/gl/scene/gl_clipper.h
@@ -0,0 +1,159 @@
+#ifndef __GL_CLIPPER
+#define __GL_CLIPPER
+
+#include "doomtype.h"
+#include "xs_Float.h"
+#include "r_utility.h"
+
+class ClipNode
+{
+ friend class Clipper;
+ friend class ClipNodesFreer;
+
+ ClipNode *prev, *next;
+ angle_t start, end;
+ static ClipNode * freelist;
+
+ bool operator== (const ClipNode &other)
+ {
+ return other.start == start && other.end == end;
+ }
+
+ void Free()
+ {
+ next=freelist;
+ freelist=this;
+ }
+
+ static ClipNode * GetNew()
+ {
+ if (freelist)
+ {
+ ClipNode * p=freelist;
+ freelist=p->next;
+ return p;
+ }
+ else return new ClipNode;
+ }
+
+ static ClipNode * NewRange(angle_t start, angle_t end)
+ {
+ ClipNode * c=GetNew();
+
+ c->start=start;
+ c->end=end;
+ c->next=c->prev=NULL;
+ return c;
+ }
+
+};
+
+
+class Clipper
+{
+ ClipNode * clipnodes;
+ ClipNode * cliphead;
+ ClipNode * silhouette; // will be preserved even when RemoveClipRange is called
+ bool blocked;
+
+ static angle_t AngleToPseudo(angle_t ang);
+ bool IsRangeVisible(angle_t startangle, angle_t endangle);
+ void RemoveRange(ClipNode * cn);
+ void AddClipRange(angle_t startangle, angle_t endangle);
+ void RemoveClipRange(angle_t startangle, angle_t endangle);
+ void DoRemoveClipRange(angle_t start, angle_t end);
+
+public:
+
+ static int anglecache;
+
+ Clipper()
+ {
+ blocked = false;
+ clipnodes=cliphead=NULL;
+ }
+
+ ~Clipper();
+
+ void Clear();
+
+
+ void SetSilhouette();
+
+ bool SafeCheckRange(angle_t startAngle, angle_t endAngle)
+ {
+ if(startAngle > endAngle)
+ {
+ return (IsRangeVisible(startAngle, ANGLE_MAX) || IsRangeVisible(0, endAngle));
+ }
+
+ return IsRangeVisible(startAngle, endAngle);
+ }
+
+ void SafeAddClipRange(angle_t startangle, angle_t endangle)
+ {
+ if(startangle > endangle)
+ {
+ // The range has to added in two parts.
+ AddClipRange(startangle, ANGLE_MAX);
+ AddClipRange(0, endangle);
+ }
+ else
+ {
+ // Add the range as usual.
+ AddClipRange(startangle, endangle);
+ }
+ }
+
+ void SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle)
+ {
+ SafeAddClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle));
+ }
+
+
+ void SafeRemoveClipRange(angle_t startangle, angle_t endangle)
+ {
+ if(startangle > endangle)
+ {
+ // The range has to added in two parts.
+ RemoveClipRange(startangle, ANGLE_MAX);
+ RemoveClipRange(0, endangle);
+ }
+ else
+ {
+ // Add the range as usual.
+ RemoveClipRange(startangle, endangle);
+ }
+ }
+
+ void SafeRemoveClipRangeRealAngles(angle_t startangle, angle_t endangle)
+ {
+ SafeRemoveClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle));
+ }
+
+ void SetBlocked(bool on)
+ {
+ blocked = on;
+ }
+
+ bool IsBlocked() const
+ {
+ return blocked;
+ }
+
+ bool CheckBox(const float *bspcoord);
+};
+
+
+extern Clipper clipper;
+
+angle_t R_PointToPseudoAngle(double x, double y);
+void R_SetView();
+
+// Used to speed up angle calculations during clipping
+inline angle_t vertex_t::GetClipAngle()
+{
+ return angletime == Clipper::anglecache? viewangle : (angletime = Clipper::anglecache, viewangle = R_PointToPseudoAngle(p.X, p.Y));
+}
+
+#endif
\ No newline at end of file
diff --git a/src/gl/scene/gl_decal.cpp b/src/gl/scene/gl_decal.cpp
new file mode 100644
index 000000000..7fb680f8e
--- /dev/null
+++ b/src/gl/scene/gl_decal.cpp
@@ -0,0 +1,398 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2003-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_decal.cpp
+** OpenGL decal rendering code
+**
+*/
+
+#include "doomdata.h"
+#include "gl/system/gl_system.h"
+#include "a_sharedglobal.h"
+#include "r_utility.h"
+
+#include "gl/system/gl_cvars.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/renderer/gl_renderer.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/textures/gl_texture.h"
+#include "gl/textures/gl_material.h"
+#include "gl/utility/gl_clock.h"
+#include "gl/renderer/gl_quaddrawer.h"
+
+struct DecalVertex
+{
+ float x,y,z;
+ float u,v;
+};
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void GLWall::DrawDecal(DBaseDecal *decal)
+{
+ line_t * line=seg->linedef;
+ side_t * side=seg->sidedef;
+ int i;
+ float zpos;
+ int light;
+ int rel;
+ float a;
+ bool flipx, flipy;
+ DecalVertex dv[4];
+ FTextureID decalTile;
+
+
+ if (decal->RenderFlags & RF_INVISIBLE) return;
+ if (type==RENDERWALL_FFBLOCK && gltexture->isMasked()) return; // No decals on 3D floors with transparent textures.
+
+ //if (decal->sprite != 0xffff)
+ {
+ decalTile = decal->PicNum;
+ flipx = !!(decal->RenderFlags & RF_XFLIP);
+ flipy = !!(decal->RenderFlags & RF_YFLIP);
+ }
+ /*
+ else
+ {
+ decalTile = SpriteFrames[sprites[decal->sprite].spriteframes + decal->frame].lump[0];
+ flipx = SpriteFrames[sprites[decal->sprite].spriteframes + decal->frame].flip & 1;
+ }
+ */
+
+ FTexture *texture = TexMan[decalTile];
+ if (texture == NULL) return;
+
+ FMaterial *tex;
+
+
+ tex = FMaterial::ValidateTexture(texture, true);
+
+
+ // the sectors are only used for their texture origin coordinates
+ // so we don't need the fake sectors for deep water etc.
+ // As this is a completely split wall fragment no further splits are
+ // necessary for the decal.
+ sector_t *frontsector;
+
+ // for 3d-floor segments use the model sector as reference
+ if ((decal->RenderFlags&RF_CLIPMASK)==RF_CLIPMID) frontsector=decal->Sector;
+ else frontsector=seg->frontsector;
+
+ switch (decal->RenderFlags & RF_RELMASK)
+ {
+ default:
+ // No valid decal can have this type. If one is encountered anyway
+ // it is in some way invalid so skip it.
+ return;
+ //zpos = decal->z;
+ //break;
+
+ case RF_RELUPPER:
+ if (type!=RENDERWALL_TOP) return;
+ if (line->flags & ML_DONTPEGTOP)
+ {
+ zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling);
+ }
+ else
+ {
+ zpos = decal->Z + seg->backsector->GetPlaneTexZ(sector_t::ceiling);
+ }
+ break;
+ case RF_RELLOWER:
+ if (type!=RENDERWALL_BOTTOM) return;
+ if (line->flags & ML_DONTPEGBOTTOM)
+ {
+ zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling);
+ }
+ else
+ {
+ zpos = decal->Z + seg->backsector->GetPlaneTexZ(sector_t::floor);
+ }
+ break;
+ case RF_RELMID:
+ if (type==RENDERWALL_TOP || type==RENDERWALL_BOTTOM) return;
+ if (line->flags & ML_DONTPEGBOTTOM)
+ {
+ zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::floor);
+ }
+ else
+ {
+ zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling);
+ }
+ }
+
+ if (decal->RenderFlags & RF_FULLBRIGHT)
+ {
+ light = 255;
+ rel = 0;
+ }
+ else
+ {
+ light = lightlevel;
+ rel = rellight + getExtraLight();
+ }
+
+ FColormap p = Colormap;
+
+ if (glset.nocoloredspritelighting)
+ {
+ p.Decolorize();
+ }
+
+
+
+ a = decal->Alpha;
+
+ // now clip the decal to the actual polygon
+ float decalwidth = tex->TextureWidth() * decal->ScaleX;
+ float decalheight= tex->TextureHeight() * decal->ScaleY;
+ float decallefto = tex->GetLeftOffset() * decal->ScaleX;
+ float decaltopo = tex->GetTopOffset() * decal->ScaleY;
+
+
+ float leftedge = glseg.fracleft * side->TexelLength;
+ float linelength = glseg.fracright * side->TexelLength - leftedge;
+
+ // texel index of the decal's left edge
+ float decalpixpos = (float)side->TexelLength * decal->LeftDistance - (flipx? decalwidth-decallefto : decallefto) - leftedge;
+
+ float left,right;
+ float lefttex,righttex;
+
+ // decal is off the left edge
+ if (decalpixpos < 0)
+ {
+ left = 0;
+ lefttex = -decalpixpos;
+ }
+ else
+ {
+ left = decalpixpos;
+ lefttex = 0;
+ }
+
+ // decal is off the right edge
+ if (decalpixpos + decalwidth > linelength)
+ {
+ right = linelength;
+ righttex = right - decalpixpos;
+ }
+ else
+ {
+ right = decalpixpos + decalwidth;
+ righttex = decalwidth;
+ }
+ if (right<=left) return; // nothing to draw
+
+ // one texture unit on the wall as vector
+ float vx=(glseg.x2-glseg.x1)/linelength;
+ float vy=(glseg.y2-glseg.y1)/linelength;
+
+ dv[1].x=dv[0].x=glseg.x1+vx*left;
+ dv[1].y=dv[0].y=glseg.y1+vy*left;
+
+ dv[3].x=dv[2].x=glseg.x1+vx*right;
+ dv[3].y=dv[2].y=glseg.y1+vy*right;
+
+ zpos+= (flipy? decalheight-decaltopo : decaltopo);
+
+ dv[1].z=dv[2].z = zpos;
+ dv[0].z=dv[3].z = dv[1].z - decalheight;
+ dv[1].v=dv[2].v = tex->GetVT();
+
+ dv[1].u=dv[0].u = tex->GetU(lefttex / decal->ScaleX);
+ dv[3].u=dv[2].u = tex->GetU(righttex / decal->ScaleX);
+ dv[0].v=dv[3].v = tex->GetVB();
+
+
+ // now clip to the top plane
+ float vzt=(ztop[1]-ztop[0])/linelength;
+ float topleft=this->ztop[0]+vzt*left;
+ float topright=this->ztop[0]+vzt*right;
+
+ // completely below the wall
+ if (topleftzbottom[0]+vzb*left;
+ float bottomright=this->zbottom[0]+vzb*right;
+
+ // completely above the wall
+ if (bottomleft>dv[1].z && bottomright>dv[2].z)
+ return;
+
+ if (bottomleft>dv[0].z || bottomright>dv[3].z)
+ {
+ // decal has to be clipped at the bottom
+ // let texture clamping handle all extreme cases
+ dv[0].v=(dv[1].z-bottomleft)/(dv[1].z-dv[0].z)*(dv[0].v-dv[1].v) + dv[1].v;
+ dv[3].v=(dv[2].z-bottomright)/(dv[2].z-dv[3].z)*(dv[3].v-dv[2].v) + dv[2].v;
+ dv[0].z=bottomleft;
+ dv[3].z=bottomright;
+ }
+
+
+ if (flipx)
+ {
+ float ur = tex->GetUR();
+ for(i=0;i<4;i++) dv[i].u=ur-dv[i].u;
+ }
+ if (flipy)
+ {
+ float vb = tex->GetVB();
+ for(i=0;i<4;i++) dv[i].v=vb-dv[i].v;
+ }
+
+ // calculate dynamic light effect.
+ if (gl_lights && GLRenderer->mLightCount && !gl_fixedcolormap && gl_light_sprites)
+ {
+ // Note: This should be replaced with proper shader based lighting.
+ double x, y;
+ decal->GetXY(seg->sidedef, x, y);
+ gl_SetDynSpriteLight(NULL, x, y, zpos, sub);
+ }
+
+ // alpha color only has an effect when using an alpha texture.
+ if (decal->RenderStyle.Flags & STYLEF_RedIsAlpha)
+ {
+ gl_RenderState.SetObjectColor(decal->AlphaColor|0xff000000);
+ }
+
+
+
+
+ gl_SetRenderStyle(decal->RenderStyle, false, false);
+ gl_RenderState.SetMaterial(tex, CLAMP_XY, decal->Translation, 0, !!(decal->RenderStyle.Flags & STYLEF_RedIsAlpha));
+
+
+ // If srcalpha is one it looks better with a higher alpha threshold
+ if (decal->RenderStyle.SrcAlpha == STYLEALPHA_One) gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold);
+ else gl_RenderState.AlphaFunc(GL_GREATER, 0.f);
+
+
+ gl_SetColor(light, rel, p, a);
+ // for additively drawn decals we must temporarily set the fog color to black.
+ PalEntry fc = gl_RenderState.GetFogColor();
+ if (decal->RenderStyle.BlendOp == STYLEOP_Add && decal->RenderStyle.DestAlpha == STYLEALPHA_One)
+ {
+ gl_RenderState.SetFog(0,-1);
+ }
+
+ gl_RenderState.SetNormal(glseg.Normal());
+
+ FQuadDrawer qd;
+ for (i = 0; i < 4; i++)
+ {
+ qd.Set(i, dv[i].x, dv[i].z, dv[i].y, dv[i].u, dv[i].v);
+ }
+
+ if (lightlist == NULL)
+ {
+ gl_RenderState.Apply();
+ qd.Render(GL_TRIANGLE_FAN);
+ }
+ else
+ {
+ for (unsigned k = 0; k < lightlist->Size(); k++)
+ {
+ secplane_t &lowplane = k == (*lightlist).Size() - 1 ? bottomplane : (*lightlist)[k + 1].plane;
+
+ float low1 = lowplane.ZatPoint(dv[1].x, dv[1].y);
+ float low2 = lowplane.ZatPoint(dv[2].x, dv[2].y);
+
+ if (low1 < dv[1].z || low2 < dv[2].z)
+ {
+ int thisll = (*lightlist)[k].caster != NULL ? gl_ClampLight(*(*lightlist)[k].p_lightlevel) : lightlevel;
+ FColormap thiscm;
+ thiscm.FadeColor = Colormap.FadeColor;
+ thiscm.CopyFrom3DLight(&(*lightlist)[k]);
+ gl_SetColor(thisll, rel, thiscm, a);
+ if (glset.nocoloredspritelighting) thiscm.Decolorize();
+ gl_SetFog(thisll, rel, &thiscm, RenderStyle == STYLE_Add);
+ gl_RenderState.SetSplitPlanes((*lightlist)[k].plane, lowplane);
+
+ gl_RenderState.Apply();
+ qd.Render(GL_TRIANGLE_FAN);
+ }
+ if (low1 <= dv[0].z && low2 <= dv[3].z) break;
+ }
+ }
+
+ rendered_decals++;
+ gl_RenderState.SetTextureMode(TM_MODULATE);
+ gl_RenderState.SetObjectColor(0xffffffff);
+ gl_RenderState.SetFog(fc,-1);
+ gl_RenderState.SetDynLight(0,0,0);
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void GLWall::DoDrawDecals()
+{
+ if (seg->sidedef && seg->sidedef->AttachedDecals)
+ {
+ if (lightlist != NULL)
+ {
+ gl_RenderState.EnableSplit(true);
+ }
+ else
+ {
+ gl_SetFog(lightlevel, rellight + getExtraLight(), &Colormap, false);
+ }
+
+ DBaseDecal *decal = seg->sidedef->AttachedDecals;
+ while (decal)
+ {
+ DrawDecal(decal);
+ decal = decal->WallNext;
+ }
+
+ if (lightlist != NULL)
+ {
+ gl_RenderState.EnableSplit(false);
+ }
+
+ }
+}
+
diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp
new file mode 100644
index 000000000..e4722b3db
--- /dev/null
+++ b/src/gl/scene/gl_drawinfo.cpp
@@ -0,0 +1,1323 @@
+//
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2002-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_drawinfo.cpp
+** Implements the draw info structure which contains most of the
+** data in a scene and the draw lists - including a very thorough BSP
+** style sorting algorithm for translucent objects.
+**
+*/
+
+#include "gl/system/gl_system.h"
+#include "r_sky.h"
+#include "r_utility.h"
+#include "r_state.h"
+#include "doomstat.h"
+
+#include "gl/system/gl_cvars.h"
+#include "gl/data/gl_data.h"
+#include "gl/data/gl_vertexbuffer.h"
+#include "gl/scene/gl_drawinfo.h"
+#include "gl/scene/gl_portal.h"
+#include "gl/renderer/gl_lightdata.h"
+#include "gl/renderer/gl_renderstate.h"
+#include "gl/textures/gl_material.h"
+#include "gl/utility/gl_clock.h"
+#include "gl/utility/gl_templates.h"
+#include "gl/shaders/gl_shader.h"
+#include "gl/stereo3d/scoped_color_mask.h"
+#include "gl/renderer/gl_quaddrawer.h"
+
+FDrawInfo * gl_drawinfo;
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+class StaticSortNodeArray : public TDeletingArray
+{
+ unsigned usecount;
+public:
+ unsigned Size() { return usecount; }
+ void Clear() { usecount=0; }
+ void Release(int start) { usecount=start; }
+ SortNode * GetNew();
+};
+
+
+SortNode * StaticSortNodeArray::GetNew()
+{
+ if (usecount==TArray::Size())
+ {
+ Push(new SortNode);
+ }
+ return operator[](usecount++);
+}
+
+
+static StaticSortNodeArray SortNodes;
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void GLDrawList::Reset()
+{
+ if (sorted) SortNodes.Release(SortNodeStart);
+ sorted=NULL;
+ walls.Clear();
+ flats.Clear();
+ sprites.Clear();
+ drawitems.Clear();
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Translucent polygon sorting - uses a BSP algorithm with an additional 'equal' branch
+
+inline double GLSprite::CalcIntersectionVertex(GLWall * w2)
+{
+ float ax = x1, ay=y1;
+ float bx = x2, by=y2;
+ float cx = w2->glseg.x1, cy=w2->glseg.y1;
+ float dx = w2->glseg.x2, dy=w2->glseg.y2;
+ return ((ay-cy)*(dx-cx)-(ax-cx)*(dy-cy)) / ((bx-ax)*(dy-cy)-(by-ay)*(dx-cx));
+}
+
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+inline void SortNode::UnlinkFromChain()
+{
+ if (parent) parent->next=next;
+ if (next) next->parent=parent;
+ parent=next=NULL;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+inline void SortNode::Link(SortNode * hook)
+{
+ if (hook)
+ {
+ parent=hook->parent;
+ hook->parent=this;
+ }
+ next=hook;
+ if (parent) parent->next=this;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+inline void SortNode::AddToEqual(SortNode *child)
+{
+ child->UnlinkFromChain();
+ child->equal=equal;
+ equal=child;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+inline void SortNode::AddToLeft(SortNode * child)
+{
+ child->UnlinkFromChain();
+ child->Link(left);
+ left=child;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+inline void SortNode::AddToRight(SortNode * child)
+{
+ child->UnlinkFromChain();
+ child->Link(right);
+ right=child;
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void GLDrawList::MakeSortList()
+{
+ SortNode * p, * n, * c;
+ unsigned i;
+
+ SortNodeStart=SortNodes.Size();
+ p=NULL;
+ n=SortNodes.GetNew();
+ for(i=0;iitemindex=(int)i;
+ n->left=n->equal=n->right=NULL;
+ n->parent=p;
+ p=n;
+ if (i!=drawitems.Size()-1)
+ {
+ c=SortNodes.GetNew();
+ n->next=c;
+ n=c;
+ }
+ else
+ {
+ n->next=NULL;
+ }
+ }
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+SortNode * GLDrawList::FindSortPlane(SortNode * head)
+{
+ while (head->next && drawitems[head->itemindex].rendertype!=GLDIT_FLAT)
+ head=head->next;
+ if (drawitems[head->itemindex].rendertype==GLDIT_FLAT) return head;
+ return NULL;
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+SortNode * GLDrawList::FindSortWall(SortNode * head)
+{
+ float farthest = -FLT_MAX;
+ float nearest = FLT_MAX;
+ SortNode * best = NULL;
+ SortNode * node = head;
+ float bestdist = FLT_MAX;
+
+ while (node)
+ {
+ GLDrawItem * it = &drawitems[node->itemindex];
+ if (it->rendertype == GLDIT_WALL)
+ {
+ float d = walls[it->index].ViewDistance;
+ if (d > farthest) farthest = d;
+ if (d < nearest) nearest = d;
+ }
+ node = node->next;
+ }
+ if (farthest == INT_MIN) return NULL;
+ node = head;
+ farthest = (farthest + nearest) / 2;
+ while (node)
+ {
+ GLDrawItem * it = &drawitems[node->itemindex];
+ if (it->rendertype == GLDIT_WALL)
+ {
+ float di = fabsf(walls[it->index].ViewDistance - farthest);
+ if (!best || di < bestdist)
+ {
+ best = node;
+ bestdist = di;
+ }
+ }
+ node = node->next;
+ }
+ return best;
+}
+
+//==========================================================================
+//
+// Note: sloped planes are a huge problem...
+//
+//==========================================================================
+void GLDrawList::SortPlaneIntoPlane(SortNode * head,SortNode * sort)
+{
+ GLFlat * fh=&flats[drawitems[head->itemindex].index];
+ GLFlat * fs=&flats[drawitems[sort->itemindex].index];
+
+ if (fh->z==fs->z)
+ head->AddToEqual(sort);
+ else if ( (fh->zz && fh->ceiling) || (fh->z>fs->z && !fh->ceiling))
+ head->AddToLeft(sort);
+ else
+ head->AddToRight(sort);
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void GLDrawList::SortWallIntoPlane(SortNode * head,SortNode * sort)
+{
+ GLFlat * fh=&flats[drawitems[head->itemindex].index];
+ GLWall * ws=&walls[drawitems[sort->itemindex].index];
+
+ bool ceiling = fh->z > ViewPos.Z;
+
+ if ((ws->ztop[0] > fh->z || ws->ztop[1] > fh->z) && (ws->zbottom[0] < fh->z || ws->zbottom[1] < fh->z))
+ {
+ // We have to split this wall!
+
+ // WARNING: NEVER EVER push a member of an array onto the array itself.
+ // Bad things will happen if the memory must be reallocated!
+ GLWall w = *ws;
+ AddWall(&w);
+
+ // Splitting is done in the shader with clip planes, if available
+ if (gl.flags & RFL_NO_CLIP_PLANES)
+ {
+ GLWall * ws1;
+ ws->vertcount = 0; // invalidate current vertices.
+ ws1=&walls[walls.Size()-1];
+ ws=&walls[drawitems[sort->itemindex].index]; // may have been reallocated!
+ float newtexv = ws->tcs[GLWall::UPLFT].v + ((ws->tcs[GLWall::LOLFT].v - ws->tcs[GLWall::UPLFT].v) / (ws->zbottom[0] - ws->ztop[0])) * (fh->z - ws->ztop[0]);
+
+ // I make the very big assumption here that translucent walls in sloped sectors
+ // and 3D-floors never coexist in the same level. If that were the case this
+ // code would become extremely more complicated.
+ if (!ceiling)
+ {
+ ws->ztop[1] = ws1->zbottom[1] = ws->ztop[0] = ws1->zbottom[0] = fh->z;
+ ws->tcs[GLWall::UPRGT].v = ws1->tcs[GLWall::LORGT].v = ws->tcs[GLWall::UPLFT].v = ws1->tcs[GLWall::LOLFT].v = newtexv;
+ }
+ else
+ {
+ ws1->ztop[1] = ws->zbottom[1] = ws1->ztop[0] = ws->zbottom[0] = fh->z;
+ ws1->tcs[GLWall::UPLFT].v = ws->tcs[GLWall::LOLFT].v = ws1->tcs[GLWall::UPRGT].v = ws->tcs[GLWall::LORGT].v=newtexv;
+ }
+ }
+
+ SortNode * sort2 = SortNodes.GetNew();
+ memset(sort2, 0, sizeof(SortNode));
+ sort2->itemindex = drawitems.Size() - 1;
+
+ head->AddToLeft(sort);
+ head->AddToRight(sort2);
+ }
+ else if ((ws->zbottom[0]z && !ceiling) || (ws->ztop[0]>fh->z && ceiling)) // completely on the left side
+ {
+ head->AddToLeft(sort);
+ }
+ else
+ {
+ head->AddToRight(sort);
+ }
+
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void GLDrawList::SortSpriteIntoPlane(SortNode * head,SortNode * sort)
+{
+ GLFlat * fh=&flats[drawitems[head->itemindex].index];
+ GLSprite * ss=&sprites[drawitems[sort->itemindex].index];
+
+ bool ceiling = fh->z > ViewPos.Z;
+
+ if ((ss->z1>fh->z && ss->z2z) || ss->modelframe)
+ {
+ // We have to split this sprite
+ GLSprite s=*ss;
+ AddSprite(&s); // add a copy to avoid reallocation issues.
+
+ // Splitting is done in the shader with clip planes, if available
+ if (gl.flags & RFL_NO_CLIP_PLANES)
+ {
+ GLSprite * ss1;
+ ss1=&sprites[sprites.Size()-1];
+ ss=&sprites[drawitems[sort->itemindex].index]; // may have been reallocated!
+ float newtexv=ss->vt + ((ss->vb-ss->vt)/(ss->z2-ss->z1))*(fh->z-ss->z1);
+
+ if (!ceiling)
+ {
+ ss->z1=ss1->z2=fh->z;
+ ss->vt=ss1->vb=newtexv;
+ }
+ else
+ {
+ ss1->z1=ss->z2=fh->z;
+ ss1->vt=ss->vb=newtexv;
+ }
+ }
+
+ SortNode * sort2=SortNodes.GetNew();
+ memset(sort2,0,sizeof(SortNode));
+ sort2->itemindex=drawitems.Size()-1;
+
+ head->AddToLeft(sort);
+ head->AddToRight(sort2);
+ }
+ else if ((ss->z2z && !ceiling) || (ss->z1>fh->z && ceiling)) // completely on the left side
+ {
+ head->AddToLeft(sort);
+ }
+ else
+ {
+ head->AddToRight(sort);
+ }
+
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+#define MIN_EQ (0.0005f)
+
+void GLDrawList::SortWallIntoWall(SortNode * head,SortNode * sort)
+{
+ GLWall * wh=&walls[drawitems[head->itemindex].index];
+ GLWall * ws=&walls[drawitems[sort->itemindex].index];
+ GLWall * ws1;
+ float v1=wh->PointOnSide(ws->glseg.x1,ws->glseg.y1);
+ float v2=wh->PointOnSide(ws->glseg.x2,ws->glseg.y2);
+
+ if (fabs(v1)type==RENDERWALL_FOGBOUNDARY && wh->type!=RENDERWALL_FOGBOUNDARY)
+ {
+ head->AddToRight(sort);
+ }
+ else if (ws->type!=RENDERWALL_FOGBOUNDARY && wh->type==RENDERWALL_FOGBOUNDARY)
+ {
+ head->AddToLeft(sort);
+ }
+ else
+ {
+ head->AddToEqual(sort);
+ }
+ }
+ else if (v1AddToLeft(sort);
+ }
+ else if (v1>-MIN_EQ && v2>-MIN_EQ)
+ {
+ head->AddToRight(sort);
+ }
+ else
+ {
+ double r=ws->CalcIntersectionVertex(wh);
+
+ float ix=(float)(ws->glseg.x1+r*(ws->glseg.x2-ws->glseg.x1));
+ float iy=(float)(ws->glseg.y1+r*(ws->glseg.y2-ws->glseg.y1));
+ float iu=(float)(ws->tcs[GLWall::UPLFT].u + r * (ws->tcs[GLWall::UPRGT].u - ws->tcs[GLWall::UPLFT].u));
+ float izt=(float)(ws->ztop[0]+r*(ws->ztop[1]-ws->ztop[0]));
+ float izb=(float)(ws->zbottom[0]+r*(ws->zbottom[1]-ws->zbottom[0]));
+
+ ws->vertcount = 0; // invalidate current vertices.
+ GLWall w=*ws;
+ AddWall(&w);
+ ws1=&walls[walls.Size()-1];
+ ws=&walls[drawitems[sort->itemindex].index]; // may have been reallocated!
+
+ ws1->glseg.x1=ws->glseg.x2=ix;
+ ws1->glseg.y1=ws->glseg.y2=iy;
+ ws1->glseg.fracleft = ws->glseg.fracright = ws->glseg.fracleft + r*(ws->glseg.fracright - ws->glseg.fracleft);
+ ws1->ztop[0]=ws->ztop[1]=izt;
+ ws1->zbottom[0]=ws->zbottom[1]=izb;
+ ws1->tcs[GLWall::LOLFT].u = ws1->tcs[GLWall::UPLFT].u = ws->tcs[GLWall::LORGT].u = ws->tcs[GLWall::UPRGT].u = iu;
+ if (gl.buffermethod == BM_DEFERRED)
+ {
+ ws->MakeVertices(false);
+ ws1->MakeVertices(false);
+ }
+
+ SortNode * sort2=SortNodes.GetNew();
+ memset(sort2,0,sizeof(SortNode));
+ sort2->itemindex=drawitems.Size()-1;
+
+ if (v1>0)
+ {
+ head->AddToLeft(sort2);
+ head->AddToRight(sort);
+ }
+ else
+ {
+ head->AddToLeft(sort);
+ head->AddToRight(sort2);
+ }
+ }
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+EXTERN_CVAR(Int, gl_billboard_mode)
+EXTERN_CVAR(Bool, gl_billboard_faces_camera)
+EXTERN_CVAR(Bool, gl_billboard_particles)
+
+void GLDrawList::SortSpriteIntoWall(SortNode * head,SortNode * sort)
+{
+ GLWall * wh=&walls[drawitems[head->itemindex].index];
+ GLSprite * ss=&sprites[drawitems[sort->itemindex].index];
+ GLSprite * ss1;
+
+ float v1 = wh->PointOnSide(ss->x1, ss->y1);
+ float v2 = wh->PointOnSide(ss->x2, ss->y2);
+
+ if (fabs(v1)