Add the OpenGL 4.6 renderer for YQ2

This commit is contained in:
atsb 2023-09-18 21:23:31 +02:00
commit a10924a14f
47 changed files with 40809 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/build/
/release/
*.mk
*.user
*.d

295
CMakeLists.txt Normal file
View file

@ -0,0 +1,295 @@
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
# Print a message that using the Makefiles is recommended.
message(NOTICE: " The CMakeLists.txt is unmaintained. Use the Makefile if possible.")
# Enforce "Debug" as standard build type.
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
# CMake project configuration.
project(yquake2 C)
# Cmake module search path.
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/stuff/cmake/modules ${CMAKE_MODULE_PATH})
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED OFF)
if(YQUAKE2LIBS)
if(CMAKE_CROSSCOMPILING)
set(CMAKE_FIND_ROOT_PATH ${YQUAKE2LIBS})
else()
set(ENV{CMAKE_PREFIX_PATH} ${YQUAKE2LIBS})
endif()
set(ENV{SDL2DIR} ${YQUAKE2LIBS})
endif()
# Add extended path for FreeBSD and Homebrew on OS X.
list(APPEND CMAKE_PREFIX_PATH /usr/local)
if (MSVC)
add_compile_options(/MP) # parallel build (use all cores, or as many as configured in VS)
# ignore some compiler warnings
add_compile_options(/wd4244 /wd4305) # possible loss of data/truncation (double to float etc; ignore)
add_compile_options(/wd4018) # signed/unsigned missmatch
add_compile_options(/wd4996) # 'function': was declared deprecated (like all that secure CRT stuff)
# don't show me warnings for system headers, why the fuck isn't this default
add_compile_options(/experimental:external /external:W0)
else() # GCC/clang/mingw
# Enforce compiler flags:
# -Wall -> More warnings
# -fno-strict-aliasing -> Quake 2 is far away from strict aliasing
# -fwrapv -> Make signed integer overflows defined
# -fvisibility=hidden -> Force defaultsymbol visibility to hidden
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fno-strict-aliasing -fwrapv -fvisibility=hidden")
# Use -O2 as maximum optimization level. -O3 has it's problems with yquake2.
string(REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
endif() # MSVC'S else-case
# Switch off some annoying warnings
if (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-missing-braces")
elseif (${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
if (CMAKE_C_COMPILER_VERSION GREATER 7.99)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format-truncation -Wno-format-overflow")
endif()
endif()
set(SYSTEMDIR "" CACHE STRING "Override the system default directory")
# These variables will act as our list of include folders and linker flags.
set(yquake2OpenGLLinkerFlags)
set(yquake2SDLLinkerFlags)
# Set directory locations (allowing us to move directories easily)
set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/src)
set(REF_SRC_DIR ${SOURCE_DIR}/client/refresh)
# Operating system.
set(YQ2OSTYPE "${CMAKE_SYSTEM_NAME}" CACHE STRING "Override operation system type")
add_definitions(-DYQ2OSTYPE="${YQ2OSTYPE}")
# Architecture string
# work around CMake's useless/broken CMAKE_SYSTEM_PROCESSOR (taken from dhewm3)
set(cpu ${CMAKE_SYSTEM_PROCESSOR})
# Originally, ${CMAKE_SYSTEM_PROCESSOR} was supposed to contain the *target* CPU, according to CMake's documentation.
# As far as I can tell this has always been broken (always returns host CPU) at least on Windows
# (see e.g. https://cmake.org/pipermail/cmake-developers/2014-September/011405.html) and wasn't reliable on
# other systems either, for example on Linux with 32bit userland but 64bit kernel it returned the kernel CPU type
# (e.g. x86_64 instead of i686). Instead of fixing this, CMake eventually updated their documentation in 3.20,
# now it's officially the same as CMAKE_HOST_SYSTEM_PROCESSOR except when cross-compiling (where it's explicitly set)
# So we gotta figure out the actual target CPU type ourselves..
if(NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_HOST_SYSTEM_PROCESSOR))
# special case: cross-compiling, here CMAKE_SYSTEM_PROCESSOR should be correct, hopefully
# (just leave cpu at ${CMAKE_SYSTEM_PROCESSOR})
elseif(MSVC)
# because all this wasn't ugly enough, it turned out that, unlike standalone CMake, Visual Studio's
# integrated CMake doesn't set CMAKE_GENERATOR_PLATFORM, so I gave up on guessing the CPU arch here
# and moved the CPU detection to MSVC-specific code in neo/sys/platform.h
else() # not MSVC and not cross-compiling, assume GCC or clang (-compatible), seems to work for MinGW as well
execute_process(COMMAND ${CMAKE_C_COMPILER} "-dumpmachine"
RESULT_VARIABLE cc_dumpmachine_res
OUTPUT_VARIABLE cc_dumpmachine_out)
if(cc_dumpmachine_res EQUAL 0)
string(STRIP ${cc_dumpmachine_out} cc_dumpmachine_out) # get rid of trailing newline
message(DEBUG "`${CMAKE_C_COMPILER} -dumpmachine` says: \"${cc_dumpmachine_out}\"")
# gcc -dumpmachine and clang -dumpmachine seem to print something like "x86_64-linux-gnu" (gcc)
# or "x64_64-pc-linux-gnu" (clang) or "i686-w64-mingw32" (32bit mingw-w64) i.e. starting with the CPU,
# then "-" and then OS or whatever - so use everything up to first "-"
string(REGEX MATCH "^[^-]+" cpu ${cc_dumpmachine_out})
message(DEBUG " => CPU architecture extracted from that: \"${cpu}\"")
else()
message(WARNING "${CMAKE_C_COMPILER} -dumpmachine failed with error (code) ${cc_dumpmachine_res}")
message(WARNING "will use the (sometimes incorrect) CMAKE_SYSTEM_PROCESSOR (${cpu}) to determine YQ2ARCH")
endif()
endif()
if(cpu STREQUAL "powerpc")
set(cpu "ppc")
elseif(cpu STREQUAL "aarch64")
# "arm64" is more obvious, and some operating systems (like macOS) use it instead of "aarch64"
set(cpu "arm64")
elseif(cpu MATCHES "[aA][mM][dD]64" OR cpu MATCHES "[xX].*64")
set(cpu "x86_64")
elseif(cpu MATCHES "i.86" OR cpu MATCHES "[xX]86")
set(cpu "i386")
elseif(cpu MATCHES "[aA][rR][mM].*") # some kind of arm..
# On 32bit Raspbian gcc -dumpmachine returns sth starting with "arm-",
# while clang -dumpmachine says "arm6k-..." - try to unify that to "arm"
if(CMAKE_SIZEOF_VOID_P EQUAL 8) # sizeof(void*) == 8 => must be arm64
set(cpu "arm64")
else() # should be 32bit arm then (probably "armv7l" "armv6k" or sth like that)
set(cpu "arm")
endif()
endif()
if(MSVC)
# for MSVC YQ2ARCH is set in code (in src/common/header/common.h)
message(STATUS "Setting YQ2OSTYPE to \"${YQ2OSTYPE}\" - NOT setting YQ2ARCH, because we're targeting MSVC (VisualC++)")
else()
set(ARCH "${cpu}")
add_definitions(-DYQ2ARCH="${ARCH}")
message(STATUS "Setting YQ2OSTYPE to \"${YQ2OSTYPE}\" and YQ2ARCH to \"${ARCH}\".")
endif()
# make sure that ${cpu} isn't used below - if at all use ${ARCH}, but not when compiling with MSVC!
unset(cpu)
# END OF workarounds for CMake's poor choices regarding CPU architecture detection
# Systemwide installation of game assets.
if(${SYSTEMWIDE_SUPPORT})
add_definitions(-DSYSTEMWIDE)
if(NOT ${SYSTEMDIR} STREQUAL "")
add_definitions(-DSYSTEMDIR="${SYSTEMDIR}")
endif()
endif()
# We need to pass some options to minizip / unzip.
add_definitions(-DNOUNCRYPT)
if(NOT (CMAKE_SYSTEM_NAME MATCHES "Linux") AND NOT (CMAKE_SYSTEM_NAME MATCHES "Windows"))
add_definitions(-DIOAPI_NO_64)
endif()
# Required libraries to build the different components of the binaries. Find
# them and add the include/linker directories and flags (in case the package
# manager find it in a weird place).
find_package(SDL2 REQUIRED)
list(APPEND yquake2IncludeDirectories "${SDL2_INCLUDE_DIR}/..")
list(APPEND yquake2SDLLinkerFlags ${SDL2_LIBRARY})
# We need an OpenGL implementation.
set(OpenGL_GL_PREFERENCE GLVND)
find_package(OpenGL REQUIRED)
list(APPEND yquake2IncludeDirectories ${OPENGL_INCLUDE_DIR})
list(APPEND yquake2OpenGLLinkerFlags ${OPENGL_LIBRARIES})
# General linker flags.
if(NOT MSVC)
list(APPEND yquake2LinkerFlags m)
endif()
list(APPEND yquake2LinkerFlags ${CMAKE_DL_LIBS})
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
if(!MSVC)
list(APPEND yquake2LinkerFlags "-static-libgcc")
endif()
else()
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
list(APPEND yquake2LinkerFlags "-rdynamic")
else()
list(APPEND yquake2LinkerFlags "-lnetwork")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
list(APPEND yquake2LinkerFlags "-lsocket -lnsl")
endif()
endif()
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD" AND NOT WIN32)
list(APPEND yquake2LinkerFlags "-Wl,--no-undefined")
endif()
# With all of those libraries and user defined paths
# added, lets give them to the compiler and linker.
include_directories(${yquake2IncludeDirectories})
link_directories(${yquake2LinkerDirectories})
# these settings only work for GCC and clang
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
# If we're building with gcc for i386 let's define -ffloat-store.
# This helps the old and crappy x87 FPU to produce correct values.
# Would be nice if Clang had something comparable.
if ("${ARCH}" STREQUAL "i386" AND ${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffloat-store")
endif()
# Force SSE math on x86_64. All sane compilers should do this
# anyway, just to protect us from broken Linux distros.
if ("${ARCH}" STREQUAL "x86_64")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse")
endif()
if ("${ARCH}" STREQUAL "arm")
if (CMAKE_SIZEOF_VOID_P EQUAL 4)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv6k")
endif()
endif()
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(REF-Windows-Source
${SOURCE_DIR}/backends/windows/shared/hunk.c
)
else()
set(REF-Unix-Source
${SOURCE_DIR}/backends/unix/shared/hunk.c
)
endif()
set(GL4-Source
${REF_SRC_DIR}/gl4/gl4_draw.c
${REF_SRC_DIR}/gl4/gl4_image.c
${REF_SRC_DIR}/gl4/gl4_light.c
${REF_SRC_DIR}/gl4/gl4_lightmap.c
${REF_SRC_DIR}/gl4/gl4_main.c
${REF_SRC_DIR}/gl4/gl4_mesh.c
${REF_SRC_DIR}/gl4/gl4_misc.c
${REF_SRC_DIR}/gl4/gl4_model.c
${REF_SRC_DIR}/gl4/gl4_sdl.c
${REF_SRC_DIR}/gl4/gl4_surf.c
${REF_SRC_DIR}/gl4/gl4_warp.c
${REF_SRC_DIR}/gl4/gl4_shaders.c
${REF_SRC_DIR}/files/models.c
${REF_SRC_DIR}/files/pcx.c
${REF_SRC_DIR}/files/stb.c
${REF_SRC_DIR}/files/surf.c
${REF_SRC_DIR}/files/wal.c
${REF_SRC_DIR}/files/pvs.c
${SOURCE_DIR}/common/shared/shared.c
${SOURCE_DIR}/common/md4.c
)
set(Glad-GL4-Source ${REF_SRC_DIR}/gl4/glad/src/glad.c)
set(GL4-Header
${REF_SRC_DIR}/ref_shared.h
${REF_SRC_DIR}/constants/anorms.h
${REF_SRC_DIR}/constants/anormtab.h
${REF_SRC_DIR}/constants/warpsin.h
${REF_SRC_DIR}/files/stb_image.h
${REF_SRC_DIR}/gl4/header/DG_dynarr.h
${REF_SRC_DIR}/gl4/header/HandmadeMath.h
${REF_SRC_DIR}/gl4/header/local.h
${REF_SRC_DIR}/gl4/header/model.h
${SOURCE_DIR}/common/header/shared.h
)
set(Glad-GL4-Header
${REF_SRC_DIR}/gl4/glad/include/glad/glad.h
${REF_SRC_DIR}/gl4/glad/include/KHR/khrplatform.h
)
# Build the GL4 dynamic library
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
add_library(ref_gl4 MODULE ${GL4-Source} ${Glad-GL4-Source} ${GL4-Header} ${Glad-GL4-Header} ${REF-Windows-Source})
else()
add_library(ref_gl4 MODULE ${GL4-Source} ${Glad-GL4-Source} ${GL4-Header} ${Glad-GL4-Header} ${REF-Unix-Source})
endif()
set_target_properties(ref_gl4 PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}
)
target_include_directories(ref_gl4 PRIVATE ${CMAKE_SOURCE_DIR}/src/client/refresh/gl4/glad/include)
target_link_libraries(ref_gl4 ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags})

515
LICENSE Normal file
View file

@ -0,0 +1,515 @@
Yamagi Quake II contains software developed by multiple individuals,
projects and organisations. Following is a list of this software and
copys of each license:
- Quake II
- Info-ZIP
- Cocoa SDL entry points
- stb_image.h, stb_image_write.h, stb_image_resize.h, stb_vorbis.h
- miniz
Parts of other Quake II Clients were included into the source. They're
covered by the same GPLv2 license as Quake II itself:
- Hecatomb
- Icculus Quake 2
- KMQuake2
- Q2Pro
- QuDoS
- r1q2
- stereo quake
- zeq2
The following code is used in library form and thus not part of Yamagi
Quake II:
- cURL
- libGL
- libopenal
- SDL
===============================================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. 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.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
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 Program or any portion
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
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 Program, 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.
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 Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) 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; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, 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 executable. However, as a
special exception, the source code 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.
If distribution of executable or 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 counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program 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.
5. 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 Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program 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 to
this License.
7. 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
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 Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program 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 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.
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.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program 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.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 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 Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, 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.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
12. 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 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.
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
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This 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.
===============================================================================
This is version 2009-Jan-02 of the Info-ZIP license.
The definitive version of this document should be available at
ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely and
a copy at http://www.info-zip.org/pub/infozip/license.html.
Copyright (c) 1990-2010 Info-ZIP. All rights reserved.
For the purposes of this copyright and license, "Info-ZIP" is defined as
the following set of individuals:
Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
Jean-loup Gailly, Hunter Goatley, Ed Gordon, Ian Gorman, Chris Herborth,
Dirk Haase, Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz,
David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko,
Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs,
Kai Uwe Rommel, Steve Salisbury, Dave Smith, Steven M. Schweda,
Christian Spieler, Cosmin Truta, Antoine Verheijen, Paul von Behren,
Rich Wales, Mike White.
This software is provided "as is," without warranty of any kind, express
or implied. In no event shall Info-ZIP or its contributors be held liable
for any direct, indirect, incidental, special or consequential damages
arising out of the use of or inability to use this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the above disclaimer and the following restrictions:
1. Redistributions of source code (in whole or in part) must retain
the above copyright notice, definition, disclaimer, and this list
of conditions.
2. Redistributions in binary form (compiled executables and libraries)
must reproduce the above copyright notice, definition, disclaimer,
and this list of conditions in documentation and/or other materials
provided with the distribution. Additional documentation is not needed
for executables where a command line license option provides these and
a note regarding this option is in the executable's startup banner. The
sole exception to this condition is redistribution of a standard
UnZipSFX binary (including SFXWiz) as part of a self-extracting archive;
that is permitted without inclusion of this license, as long as the
normal SFX banner has not been removed from the binary or disabled.
3. Altered versions--including, but not limited to, ports to new operating
systems, existing ports with new graphical interfaces, versions with
modified or added functionality, and dynamic, shared, or static library
versions not from Info-ZIP--must be plainly marked as such and must not
be misrepresented as being the original source or, if binaries,
compiled from the original source. Such altered versions also must not
be misrepresented as being Info-ZIP releases--including, but not
limited to, labeling of the altered versions with the names "Info-ZIP"
(or any variation thereof, including, but not limited to, different
capitalizations), "Pocket UnZip," "WiZ" or "MacZip" without the
explicit permission of Info-ZIP. Such altered versions are further
prohibited from misrepresentative use of the Zip-Bugs or Info-ZIP
e-mail addresses or the Info-ZIP URL(s), such as to imply Info-ZIP
will provide support for the altered versions.
4. Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip,"
"UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its
own source and binary releases.
===============================================================================
Main entry point for our Cocoa-ized SDL app
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
Feel free to customize this file to suit your needs
===============================================================================
stb single-file public domain libraries
(stb_image.h, stb_image_resize.h, stb_image_write.h, stb_vorbis.h)
https://github.com/nothings/stb/
Dual-Licensed: Public Domain (Unlicense) or MIT License:
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
===============================================================================
Copyright 2013-2014 RAD Game Tools and Valve Software
Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================================================

478
Makefile Normal file
View file

@ -0,0 +1,478 @@
# ------------------------------------------------------ #
# Makefile for the OpenGL 4.6 renderer lib for Quake II #
# #
# Just type "make" to compile the #
# - OpenGL 4.6 renderer lib (ref_gl4.so / rev_gl4.dll) #
# #
# Dependencies: #
# - SDL 2.0 #
# - libGL #
# #
# Platforms: #
# - FreeBSD #
# - Linux #
# - NetBSD #
# - OpenBSD #
# - Windows #
# ------------------------------------------------------ #
# Variables
# ---------
# - ASAN: Builds with address sanitizer, includes DEBUG.
# - DEBUG: Builds a debug build, forces -O0 and adds debug symbols.
# - VERBOSE: Prints full compile, linker and misc commands.
# - UBSAN: Builds with undefined behavior sanitizer, includes DEBUG.
# ----------
# User configurable options
# -------------------------
# Enables HTTP support through cURL. Used for
# HTTP download.
WITH_CURL:=yes
# Enables the optional OpenAL sound system.
# To use it your system needs libopenal.so.1
# or openal32.dll (we recommend openal-soft)
# installed
WITH_OPENAL:=yes
# Sets an RPATH to $ORIGIN/lib. It can be used to
# inject custom libraries, e.g. a patches libSDL.so
# or libopenal.so. Not supported on Windows.
WITH_RPATH:=yes
# Enable systemwide installation of game assets.
WITH_SYSTEMWIDE:=no
# This will set the default SYSTEMDIR, a non-empty string
# would actually be used. On Windows normals slashes (/)
# instead of backslashed (\) should be used! The string
# MUST NOT be surrounded by quotation marks!
WITH_SYSTEMDIR:=""
# This will set the build options to create an MacOS .app-bundle.
# The app-bundle itself will not be created, but the runtime paths
# will be set to expect the game-data in *.app/
# Contents/Resources
OSX_APP:=yes
# This is an optional configuration file, it'll be used in
# case of presence.
CONFIG_FILE:=config.mk
# ----------
# In case a of a configuration file being present, we'll just use it
ifeq ($(wildcard $(CONFIG_FILE)), $(CONFIG_FILE))
include $(CONFIG_FILE)
endif
# Detect the OS
ifdef SystemRoot
YQ2_OSTYPE ?= Windows
else
YQ2_OSTYPE ?= $(shell uname -s)
endif
# Special case for MinGW
ifneq (,$(findstring MINGW,$(YQ2_OSTYPE)))
YQ2_OSTYPE := Windows
endif
# Detect the architecture
ifeq ($(YQ2_OSTYPE), Windows)
ifdef MINGW_CHOST
ifeq ($(MINGW_CHOST), x86_64-w64-mingw32)
YQ2_ARCH ?= x86_64
else # i686-w64-mingw32
YQ2_ARCH ?= i386
endif
else # windows, but MINGW_CHOST not defined
ifdef PROCESSOR_ARCHITEW6432
# 64 bit Windows
YQ2_ARCH ?= $(PROCESSOR_ARCHITEW6432)
else
# 32 bit Windows
YQ2_ARCH ?= $(PROCESSOR_ARCHITECTURE)
endif
endif # windows but MINGW_CHOST not defined
else
ifneq ($(YQ2_OSTYPE), Darwin)
# Normalize some abiguous YQ2_ARCH strings
YQ2_ARCH ?= $(shell uname -m | sed -e 's/i.86/i386/' -e 's/amd64/x86_64/' -e 's/arm64/aarch64/' -e 's/^arm.*/arm/')
else
YQ2_ARCH ?= $(shell uname -m)
endif
endif
# Detect the compiler
ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1)
COMPILER := clang
COMPILERVER := $(shell $(CC) -dumpversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/')
else ifeq ($(shell $(CC) -v 2>&1 | grep -c -E "(gcc version|gcc-Version)"), 1)
COMPILER := gcc
COMPILERVER := $(shell $(CC) -dumpversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/')
else
COMPILER := unknown
endif
# ASAN includes DEBUG
ifdef ASAN
DEBUG=1
endif
# UBSAN includes DEBUG
ifdef UBSAN
DEBUG=1
endif
# ----------
# Base CFLAGS. These may be overridden by the environment.
# Highest supported optimizations are -O2, higher levels
# will likely break this crappy code.
ifdef DEBUG
CFLAGS ?= -O0 -g -Wall -pipe
ifdef ASAN
override CFLAGS += -fsanitize=address -DUSE_SANITIZER
endif
ifdef UBSAN
override CFLAGS += -fsanitize=undefined -DUSE_SANITIZER
endif
else
CFLAGS ?= -O2 -Wall -pipe -fomit-frame-pointer
endif
# Always needed are:
# -fno-strict-aliasing since the source doesn't comply
# with strict aliasing rules and it's next to impossible
# to get it there...
# -fwrapv for defined integer wrapping. MSVC6 did this
# and the game code requires it.
# -fvisibility=hidden to keep symbols hidden. This is
# mostly best practice and not really necessary.
override CFLAGS += -fno-strict-aliasing -fwrapv -fvisibility=hidden
# -MMD to generate header dependencies. Unsupported by
# the Clang shipped with OS X.
ifneq ($(YQ2_OSTYPE), Darwin)
override CFLAGS += -MMD
endif
# ----------
# ARM needs a sane minimum architecture. We need the `yield`
# opcode, arm6k is the first iteration that supports it. arm6k
# is also the first Raspberry PI generation and older hardware
# is likely too slow to run the game. We're not enforcing the
# minimum architecture, but if you're build for something older
# like arm5 the `yield` opcode isn't compiled in and the game
# (especially q2ded) will consume more CPU time than necessary.
ifeq ($(YQ2_ARCH), arm)
CFLAGS += -march=armv6k
endif
# ----------
# Switch of some annoying warnings.
ifeq ($(COMPILER), clang)
# -Wno-missing-braces because otherwise clang complains
# about totally valid 'vec3_t bla = {0}' constructs.
override CFLAGS += -Wno-missing-braces
else ifeq ($(COMPILER), gcc)
# GCC 8.0 or higher.
ifeq ($(shell test $(COMPILERVER) -ge 80000; echo $$?),0)
# -Wno-format-truncation and -Wno-format-overflow
# because GCC spams about 50 false positives.
override CFLAGS += -Wno-format-truncation -Wno-format-overflow
endif
endif
# ----------
# Defines the operating system and architecture
override CFLAGS += -DYQ2OSTYPE=\"$(YQ2_OSTYPE)\" -DYQ2ARCH=\"$(YQ2_ARCH)\"
# ----------
# For reproduceable builds, look here for details:
# https://reproducible-builds.org/specs/source-date-epoch/
ifdef SOURCE_DATE_EPOCH
override CFLAGS += -DBUILD_DATE=\"$(shell date --utc --date="@${SOURCE_DATE_EPOCH}" +"%b %_d %Y" | sed -e 's/ /\\ /g')\"
endif
# ----------
# Using the default x87 float math on 32bit x86 causes rounding trouble
# -ffloat-store could work around that, but the better solution is to
# just enforce SSE - every x86 CPU since Pentium3 supports that
# and this should even improve the performance on old CPUs
ifeq ($(YQ2_ARCH), i386)
override CFLAGS += -msse -mfpmath=sse
endif
# Force SSE math on x86_64. All sane compilers should do this
# anyway, just to protect us from broken Linux distros.
ifeq ($(YQ2_ARCH), x86_64)
override CFLAGS += -mfpmath=sse
endif
# Disable floating-point expression contraction. While this shouldn't be
# a problem for C (only for C++) better be safe than sorry. See
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100839 for details.
ifeq ($(COMPILER), gcc)
override CFLAGS += -ffp-contract=off
endif
# ----------
# Systemwide installation.
ifeq ($(WITH_SYSTEMWIDE),yes)
override CFLAGS += -DSYSTEMWIDE
ifneq ($(WITH_SYSTEMDIR),"")
override CFLAGS += -DSYSTEMDIR=\"$(WITH_SYSTEMDIR)\"
endif
endif
# ----------
# We don't support encrypted ZIP files.
ZIPCFLAGS := -DNOUNCRYPT
# Just set IOAPI_NO_64 on everything that's not Linux or Windows,
# otherwise minizip will use fopen64(), fseek64() and friends that
# may be unavailable. This is - of course - not really correct, in
# a better world we would set -DIOAPI_NO_64 to force everything to
# fopen(), fseek() and so on and -D_FILE_OFFSET_BITS=64 to let the
# libc headers do their work. Currently we can't do that because
# Quake II uses nearly everywere int instead of off_t...
#
# This may have the side effect that ZIP files larger than 2GB are
# unsupported. But I doubt that anyone has such large files, they
# would likely hit other internal limits.
ifneq ($(YQ2_OSTYPE),Windows)
ifneq ($(YQ2_OSTYPE),Linux)
ZIPCFLAGS += -DIOAPI_NO_64
endif
endif
# ----------
# Extra CFLAGS for SDL.
SDLCFLAGS := $(shell sdl2-config --cflags)
# ----------
# Base include path.
ifeq ($(YQ2_OSTYPE),Linux)
INCLUDE ?= -I/usr/include
else ifeq ($(YQ2_OSTYPE),FreeBSD)
INCLUDE ?= -I/usr/local/include
else ifeq ($(YQ2_OSTYPE),NetBSD)
INCLUDE ?= -I/usr/X11R7/include -I/usr/pkg/include
else ifeq ($(YQ2_OSTYPE),OpenBSD)
INCLUDE ?= -I/usr/local/include
else ifeq ($(YQ2_OSTYPE),Windows)
INCLUDE ?= -I/usr/include
endif
# ----------
# Base LDFLAGS. This is just the library path.
ifeq ($(YQ2_OSTYPE),Linux)
LDFLAGS ?= -L/usr/lib
else ifeq ($(YQ2_OSTYPE),FreeBSD)
LDFLAGS ?= -L/usr/local/lib
else ifeq ($(YQ2_OSTYPE),NetBSD)
LDFLAGS ?= -L/usr/X11R7/lib -Wl,-R/usr/X11R7/lib -L/usr/pkg/lib -Wl,-R/usr/pkg/lib
else ifeq ($(YQ2_OSTYPE),OpenBSD)
LDFLAGS ?= -L/usr/local/lib
else ifeq ($(YQ2_OSTYPE),Windows)
LDFLAGS ?= -L/usr/lib
endif
# Link address sanitizer if requested.
ifdef ASAN
override LDFLAGS += -fsanitize=address
endif
# Link undefined behavior sanitizer if requested.
ifdef UBSAN
override LDFLAGS += -fsanitize=undefined
endif
# Required libraries.
ifeq ($(YQ2_OSTYPE),Linux)
LDLIBS ?= -lm -ldl -rdynamic
else ifeq ($(YQ2_OSTYPE),FreeBSD)
LDLIBS ?= -lm
else ifeq ($(YQ2_OSTYPE),NetBSD)
LDLIBS ?= -lm
else ifeq ($(YQ2_OSTYPE),OpenBSD)
LDLIBS ?= -lm
else ifeq ($(YQ2_OSTYPE),Windows)
LDLIBS ?= -lws2_32 -lwinmm -static-libgcc
else ifeq ($(YQ2_OSTYPE), Darwin)
LDLIBS ?= -arch $(YQ2_ARCH)
else ifeq ($(YQ2_OSTYPE), Haiku)
LDLIBS ?= -lm -lnetwork
else ifeq ($(YQ2_OSTYPE), SunOS)
LDLIBS ?= -lm -lsocket -lnsl
endif
# ASAN and UBSAN must not be linked
# with --no-undefined. OSX and OpenBSD
# don't support it at all.
ifndef ASAN
ifndef UBSAN
ifneq ($(YQ2_OSTYPE), Darwin)
ifneq ($(YQ2_OSTYPE), OpenBSD)
override LDFLAGS += -Wl,--no-undefined
endif
endif
endif
endif
# ----------
# Extra LDFLAGS for SDL
ifeq ($(YQ2_OSTYPE), Darwin)
SDLLDFLAGS := -lSDL2
else # not Darwin
SDLLDFLAGS := $(shell sdl2-config --libs)
endif # Darwin
# The renderer libs don't need libSDL2main, libmingw32 or -mwindows.
ifeq ($(YQ2_OSTYPE), Windows)
DLL_SDLLDFLAGS = $(subst -mwindows,,$(subst -lmingw32,,$(subst -lSDL2main,,$(SDLLDFLAGS))))
endif
# ----------
# When make is invoked by "make VERBOSE=1" print
# the compiler and linker commands.
ifdef VERBOSE
Q :=
else
Q := @
endif
# ----------
# Phony targets
.PHONY : all ref_gl4
# ----------
# Builds everything
all: ref_gl4
# Cleanup
clean:
@echo "===> CLEAN"
${Q}rm -Rf build release/*
cleanall:
@echo "===> CLEAN"
${Q}rm -Rf build release
# ----------
# The OpenGL 4.6 renderer lib
ifeq ($(YQ2_OSTYPE), Windows)
ref_gl4:
@echo "===> Building ref_gl4.dll"
${Q}mkdir -p release
$(MAKE) release/ref_gl4.dll
release/ref_gl4.dll : GLAD_INCLUDE = -Isrc/client/refresh/gl4/glad/include
release/ref_gl4.dll : LDFLAGS += -shared
else # not Windows or Darwin - macOS doesn't support OpenGL 4.6
ref_gl4:
@echo "===> Building ref_gl4.so"
${Q}mkdir -p release
$(MAKE) release/ref_gl4.so
release/ref_gl4.so : GLAD_INCLUDE = -Isrc/client/refresh/gl4/glad/include
release/ref_gl4.so : CFLAGS += -fPIC
release/ref_gl4.so : LDFLAGS += -shared
endif # OS specific ref_gl4 stuff
build/ref_gl4/%.o: %.c
@echo "===> CC $<"
${Q}mkdir -p $(@D)
${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(INCLUDE) $(GLAD_INCLUDE) -o $@ $<
# ----------
REFGL4_OBJS_ := \
src/client/refresh/gl4/gl4_draw.o \
src/client/refresh/gl4/gl4_image.o \
src/client/refresh/gl4/gl4_light.o \
src/client/refresh/gl4/gl4_lightmap.o \
src/client/refresh/gl4/gl4_main.o \
src/client/refresh/gl4/gl4_mesh.o \
src/client/refresh/gl4/gl4_misc.o \
src/client/refresh/gl4/gl4_model.o \
src/client/refresh/gl4/gl4_sdl.o \
src/client/refresh/gl4/gl4_surf.o \
src/client/refresh/gl4/gl4_warp.o \
src/client/refresh/gl4/gl4_shaders.o \
src/client/refresh/files/surf.o \
src/client/refresh/files/models.o \
src/client/refresh/files/pcx.o \
src/client/refresh/files/stb.o \
src/client/refresh/files/wal.o \
src/client/refresh/files/pvs.o \
src/common/shared/shared.o \
src/common/md4.o
REFGL4_OBJS_GLADE_ := \
src/client/refresh/gl4/glad/src/glad.o
ifeq ($(YQ2_OSTYPE), Windows)
REFGL4_OBJS_ += \
src/backends/windows/shared/hunk.o
else # not Windows
REFGL4_OBJS_ += \
src/backends/unix/shared/hunk.o
endif
# ----------
# Rewrite pathes to our object directory.
REFGL4_OBJS = $(patsubst %,build/ref_gl4/%,$(REFGL4_OBJS_))
REFGL4_OBJS += $(patsubst %,build/ref_gl4/%,$(REFGL4_OBJS_GLADE_))
# ----------
# Generate header dependencies.
REFGL4_DEPS= $(REFGL4_OBJS:.o=.d)
# Suck header dependencies in.
-include $(REFGL4_DEPS)
# ----------
# release/ref_gl4.so
ifeq ($(YQ2_OSTYPE), Windows)
release/ref_gl4.dll : $(REFGL4_OBJS)
@echo "===> LD $@"
${Q}$(CC) $(LDFLAGS) $(REFGL4_OBJS) $(LDLIBS) $(DLL_SDLLDFLAGS) -o $@
$(Q)strip $@
else
release/ref_gl4.so : $(REFGL4_OBJS)
@echo "===> LD $@"
${Q}$(CC) $(LDFLAGS) $(REFGL4_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@
endif
# ----------

28
README.md Normal file
View file

@ -0,0 +1,28 @@
# OpenGL 4.6 Renderer Library for Yamagi Quake II
This is an OpenGL 4.6 renderer library for Yamagi Quake II.
## Compilation
You'll need:
* clang or gcc,
* GNU Make,
* SDL2 with `sdl2-config`,
* opengl-headers
Type `make` to compile the library. If the compilation is successfull,
the library can be found under *release/ref_gl4.dll* (Windows) or
*release/ref_gl4.so* (unixoid systems).
You can also use the provided CMakeLists file to build using Visual Studio on Windows.
## Usage
Copy the library next to your Yamagi Quake II executable. You can select
OpenGL 4.6 through the video menu.
If you have run into issues, please attach output logs with OS/driver version
and device name to the bug report. [List](https://openbenchmarking.org/test/pts/yquake2)
of currently tested devices for the reference.

View file

@ -0,0 +1,173 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* This file implements the low level part of the Hunk_* memory system
*
* =======================================================================
*/
/* For mremap() - must be before sys/mman.h include! */
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include <sys/mman.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
#include "../../../common/header/common.h"
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <machine/param.h>
#define MAP_ANONYMOUS MAP_ANON
#endif
#if defined(__APPLE__)
#include <sys/types.h>
#define MAP_ANONYMOUS MAP_ANON
#endif
byte *membase;
size_t maxhunksize;
size_t curhunksize;
void *
Hunk_Begin(int maxsize)
{
/* reserve a huge chunk of memory, but don't commit any yet */
/* plus 32 bytes for cacheline */
maxhunksize = maxsize + sizeof(size_t) + 32;
curhunksize = 0;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
int prot = PROT_READ | PROT_WRITE;
#if defined(MAP_ALIGNED_SUPER)
const size_t hgpagesize = 1UL<<21;
size_t page_size = sysconf(_SC_PAGESIZE);
/* Archs supported has 2MB for super pages size */
if (maxhunksize >= hgpagesize)
{
maxhunksize = (maxhunksize & ~(page_size-1)) + page_size;
flags |= MAP_ALIGNED_SUPER;
}
#endif
#if defined(PROT_MAX)
/* For now it is FreeBSD exclusif but could possibly be extended
to other like DFBSD for example */
prot |= PROT_MAX(prot);
#endif
membase = mmap(0, maxhunksize, prot,
flags, -1, 0);
if ((membase == NULL) || (membase == (byte *)-1))
{
Sys_Error("unable to virtual allocate %d bytes", maxsize);
}
*((size_t *)membase) = curhunksize;
return membase + sizeof(size_t);
}
void *
Hunk_Alloc(int size)
{
byte *buf;
/* round to cacheline */
size = (size + 31) & ~31;
if (curhunksize + size > maxhunksize)
{
Sys_Error("Hunk_Alloc overflow");
}
buf = membase + sizeof(size_t) + curhunksize;
curhunksize += size;
return buf;
}
int
Hunk_End(void)
{
byte *n = NULL;
#if defined(__linux__)
n = (byte *)mremap(membase, maxhunksize, curhunksize + sizeof(size_t), 0);
#elif defined(__NetBSD__)
n = (byte *)mremap(membase, maxhunksize, NULL, curhunksize + sizeof(size_t), 0);
#else
#ifndef round_page
size_t page_size = sysconf(_SC_PAGESIZE);
#define round_page(x) ((((size_t)(x)) + page_size-1) & ~(page_size-1))
#endif
size_t old_size = round_page(maxhunksize);
size_t new_size = round_page(curhunksize + sizeof(size_t));
if (new_size > old_size)
{
/* Can never happen. If it happens something's very wrong. */
n = 0;
}
else if (new_size < old_size)
{
/* Hunk is to big, we need to shrink it. */
n = munmap(membase + new_size, old_size - new_size) + membase;
}
else
{
/* No change necessary. */
n = membase;
}
#endif
if (n != membase)
{
Sys_Error("Hunk_End: Could not remap virtual block (%d)", errno);
}
*((size_t *)membase) = curhunksize + sizeof(size_t);
return curhunksize;
}
void
Hunk_Free(void *base)
{
if (base)
{
byte *m;
m = ((byte *)base) - sizeof(size_t);
if (munmap(m, *((size_t *)m)))
{
Sys_Error("Hunk_Free: munmap failed (%d)", errno);
}
}
}

View file

@ -0,0 +1,15 @@
#ifndef WIN_RESOURCE_H
#define WIN_RESOURCE_H
#define IDI_ICON1 101
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
#endif

View file

@ -0,0 +1,97 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Memory handling functions.
*
* =======================================================================
*/
#include <windows.h>
#include "../../../common/header/common.h"
byte *membase;
int hunkcount;
size_t hunkmaxsize;
size_t cursize;
void *
Hunk_Begin(int maxsize)
{
/* reserve a huge chunk of memory, but don't commit any yet */
/* plus 32 bytes for cacheline */
hunkmaxsize = maxsize + sizeof(size_t) + 32;
cursize = 0;
membase = VirtualAlloc(NULL, hunkmaxsize, MEM_RESERVE, PAGE_NOACCESS);
if (!membase)
{
Sys_Error("VirtualAlloc reserve failed");
}
return (void *)membase;
}
void *
Hunk_Alloc(int size)
{
void *buf;
/* round to cacheline */
size = (size + 31) & ~31;
/* commit pages as needed */
buf = VirtualAlloc(membase, cursize + size, MEM_COMMIT, PAGE_READWRITE);
if (!buf)
{
Sys_Error("VirtualAlloc commit failed.\n");
}
cursize += size;
if (cursize > hunkmaxsize)
{
Sys_Error("Hunk_Alloc overflow");
}
return (void *)(membase + cursize - size);
}
int
Hunk_End(void)
{
hunkcount++;
return cursize;
}
void
Hunk_Free(void *base)
{
if (base)
{
VirtualFree(base, 0, MEM_RELEASE);
}
hunkcount--;
}

View file

@ -0,0 +1,188 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Precalculates anormal values
*
* =======================================================================
*/
{ -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 },

View file

@ -0,0 +1,236 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Precalculated anormal tabulations
*
* =======================================================================
*/
{ 1.23, 1.30, 1.47, 1.35, 1.56, 1.71, 1.37, 1.38, 1.59, 1.60, 1.79, 1.97, 1.88, 1.92, 1.79, 1.02, 0.93, 1.07, 0.82, 0.87,
0.88, 0.94, 0.96, 1.14, 1.11, 0.82, 0.83, 0.89, 0.89, 0.86, 0.94, 0.91, 1.00, 1.21, 0.98, 1.48, 1.30, 1.57, 0.96, 1.07,
1.14, 1.60, 1.61, 1.40, 1.37, 1.72, 1.78, 1.79, 1.93, 1.99, 1.90, 1.68, 1.71, 1.86, 1.60, 1.68, 1.78, 1.86, 1.93, 1.99,
1.97, 1.44, 1.22, 1.49, 0.93, 0.99, 0.99, 1.23, 1.22, 1.44, 1.49, 0.89, 0.89, 0.97, 0.91, 0.98, 1.19, 0.82, 0.76, 0.82,
0.71, 0.72, 0.73, 0.76, 0.79, 0.86, 0.83, 0.72, 0.76, 0.76, 0.89, 0.82, 0.89, 0.82, 0.89, 0.91, 0.83, 0.96, 1.14, 0.97,
1.40, 1.19, 0.98, 0.94, 1.00, 1.07, 1.37, 1.21, 1.48, 1.30, 1.57, 1.61, 1.37, 0.86, 0.83, 0.91, 0.82, 0.82, 0.88, 0.89,
0.96, 1.14, 0.98, 0.87, 0.93, 0.94, 1.02, 1.30, 1.07, 1.35, 1.38, 1.11, 1.56, 1.92, 1.79, 1.79, 1.59, 1.60, 1.72, 1.90,
1.79, 0.80, 0.85, 0.79, 0.93, 0.80, 0.85, 0.77, 0.74, 0.72, 0.77, 0.74, 0.72, 0.70, 0.70, 0.71, 0.76, 0.73, 0.79, 0.79,
0.73, 0.76, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.26, 1.26, 1.48, 1.23, 1.50, 1.71, 1.14, 1.19, 1.38, 1.46, 1.64, 1.94, 1.87, 1.84, 1.71, 1.02, 0.92, 1.00, 0.79, 0.85,
0.84, 0.91, 0.90, 0.98, 0.99, 0.77, 0.77, 0.83, 0.82, 0.79, 0.86, 0.84, 0.92, 0.99, 0.91, 1.24, 1.03, 1.33, 0.88, 0.94,
0.97, 1.41, 1.39, 1.18, 1.11, 1.51, 1.61, 1.59, 1.80, 1.91, 1.76, 1.54, 1.65, 1.76, 1.70, 1.70, 1.85, 1.85, 1.97, 1.99,
1.93, 1.28, 1.09, 1.39, 0.92, 0.97, 0.99, 1.18, 1.26, 1.52, 1.48, 0.83, 0.85, 0.90, 0.88, 0.93, 1.00, 0.77, 0.73, 0.78,
0.72, 0.71, 0.74, 0.75, 0.79, 0.86, 0.81, 0.75, 0.81, 0.79, 0.96, 0.88, 0.94, 0.86, 0.93, 0.92, 0.85, 1.08, 1.33, 1.05,
1.55, 1.31, 1.01, 1.05, 1.27, 1.31, 1.60, 1.47, 1.70, 1.54, 1.76, 1.76, 1.57, 0.93, 0.90, 0.99, 0.88, 0.88, 0.95, 0.97,
1.11, 1.39, 1.20, 0.92, 0.97, 1.01, 1.10, 1.39, 1.22, 1.51, 1.58, 1.32, 1.64, 1.97, 1.85, 1.91, 1.77, 1.74, 1.88, 1.99,
1.91, 0.79, 0.86, 0.80, 0.94, 0.84, 0.88, 0.74, 0.74, 0.71, 0.82, 0.77, 0.76, 0.70, 0.73, 0.72, 0.73, 0.70, 0.74, 0.85,
0.77, 0.82, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.34, 1.27, 1.53, 1.17, 1.46, 1.71, 0.98, 1.05, 1.20, 1.34, 1.48, 1.86, 1.82, 1.71, 1.62, 1.09, 0.94, 0.99, 0.79, 0.85,
0.82, 0.90, 0.87, 0.93, 0.96, 0.76, 0.74, 0.79, 0.76, 0.74, 0.79, 0.78, 0.85, 0.92, 0.85, 1.00, 0.93, 1.06, 0.81, 0.86,
0.89, 1.16, 1.12, 0.97, 0.95, 1.28, 1.38, 1.35, 1.60, 1.77, 1.57, 1.33, 1.50, 1.58, 1.69, 1.63, 1.82, 1.74, 1.91, 1.92,
1.80, 1.04, 0.97, 1.21, 0.90, 0.93, 0.97, 1.05, 1.21, 1.48, 1.37, 0.77, 0.80, 0.84, 0.85, 0.88, 0.92, 0.73, 0.71, 0.74,
0.74, 0.71, 0.75, 0.73, 0.79, 0.84, 0.78, 0.79, 0.86, 0.81, 1.05, 0.94, 0.99, 0.90, 0.95, 0.92, 0.86, 1.24, 1.44, 1.14,
1.59, 1.34, 1.02, 1.27, 1.50, 1.49, 1.80, 1.69, 1.86, 1.72, 1.87, 1.80, 1.69, 1.00, 0.98, 1.23, 0.95, 0.96, 1.09, 1.16,
1.37, 1.63, 1.46, 0.99, 1.10, 1.25, 1.24, 1.51, 1.41, 1.67, 1.77, 1.55, 1.72, 1.95, 1.89, 1.98, 1.91, 1.86, 1.97, 1.99,
1.94, 0.81, 0.89, 0.85, 0.98, 0.90, 0.94, 0.75, 0.78, 0.73, 0.89, 0.83, 0.82, 0.72, 0.77, 0.76, 0.72, 0.70, 0.71, 0.91,
0.83, 0.89, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.46, 1.34, 1.60, 1.16, 1.46, 1.71, 0.94, 0.99, 1.05, 1.26, 1.33, 1.74, 1.76, 1.57, 1.54, 1.23, 0.98, 1.05, 0.83, 0.89,
0.84, 0.92, 0.87, 0.91, 0.96, 0.78, 0.74, 0.79, 0.72, 0.72, 0.75, 0.76, 0.80, 0.88, 0.83, 0.94, 0.87, 0.95, 0.76, 0.80,
0.82, 0.97, 0.96, 0.89, 0.88, 1.08, 1.11, 1.10, 1.37, 1.59, 1.37, 1.07, 1.27, 1.34, 1.57, 1.45, 1.69, 1.55, 1.77, 1.79,
1.60, 0.93, 0.90, 0.99, 0.86, 0.87, 0.93, 0.96, 1.07, 1.35, 1.18, 0.73, 0.76, 0.77, 0.81, 0.82, 0.85, 0.70, 0.71, 0.72,
0.78, 0.73, 0.77, 0.73, 0.79, 0.82, 0.76, 0.83, 0.90, 0.84, 1.18, 0.98, 1.03, 0.92, 0.95, 0.90, 0.86, 1.32, 1.45, 1.15,
1.53, 1.27, 0.99, 1.42, 1.65, 1.58, 1.93, 1.83, 1.94, 1.81, 1.88, 1.74, 1.70, 1.19, 1.17, 1.44, 1.11, 1.15, 1.36, 1.41,
1.61, 1.81, 1.67, 1.22, 1.34, 1.50, 1.42, 1.65, 1.61, 1.82, 1.91, 1.75, 1.80, 1.89, 1.89, 1.98, 1.99, 1.94, 1.98, 1.92,
1.87, 0.86, 0.95, 0.92, 1.14, 0.98, 1.03, 0.79, 0.84, 0.77, 0.97, 0.90, 0.89, 0.76, 0.82, 0.82, 0.74, 0.72, 0.71, 0.98,
0.89, 0.97, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.60, 1.44, 1.68, 1.22, 1.49, 1.71, 0.93, 0.99, 0.99, 1.23, 1.22, 1.60, 1.68, 1.44, 1.49, 1.40, 1.14, 1.19, 0.89, 0.96,
0.89, 0.97, 0.89, 0.91, 0.98, 0.82, 0.76, 0.82, 0.71, 0.72, 0.73, 0.76, 0.79, 0.86, 0.83, 0.91, 0.83, 0.89, 0.72, 0.76,
0.76, 0.89, 0.89, 0.82, 0.82, 0.98, 0.96, 0.97, 1.14, 1.40, 1.19, 0.94, 1.00, 1.07, 1.37, 1.21, 1.48, 1.30, 1.57, 1.61,
1.37, 0.86, 0.83, 0.91, 0.82, 0.82, 0.88, 0.89, 0.96, 1.14, 0.98, 0.70, 0.72, 0.73, 0.77, 0.76, 0.79, 0.70, 0.72, 0.71,
0.82, 0.77, 0.80, 0.74, 0.79, 0.80, 0.74, 0.87, 0.93, 0.85, 1.23, 1.02, 1.02, 0.93, 0.93, 0.87, 0.85, 1.30, 1.35, 1.07,
1.38, 1.11, 0.94, 1.47, 1.71, 1.56, 1.97, 1.88, 1.92, 1.79, 1.79, 1.59, 1.60, 1.30, 1.35, 1.56, 1.37, 1.38, 1.59, 1.60,
1.79, 1.92, 1.79, 1.48, 1.57, 1.72, 1.61, 1.78, 1.79, 1.93, 1.99, 1.90, 1.86, 1.78, 1.86, 1.93, 1.99, 1.97, 1.90, 1.79,
1.72, 0.94, 1.07, 1.00, 1.37, 1.21, 1.30, 0.86, 0.91, 0.83, 1.14, 0.98, 0.96, 0.82, 0.88, 0.89, 0.79, 0.76, 0.73, 1.07,
0.94, 1.11, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.74, 1.57, 1.76, 1.33, 1.54, 1.71, 0.94, 1.05, 0.99, 1.26, 1.16, 1.46, 1.60, 1.34, 1.46, 1.59, 1.37, 1.37, 0.97, 1.11,
0.96, 1.10, 0.95, 0.94, 1.08, 0.89, 0.82, 0.88, 0.72, 0.76, 0.75, 0.80, 0.80, 0.88, 0.87, 0.91, 0.83, 0.87, 0.72, 0.76,
0.74, 0.83, 0.84, 0.78, 0.79, 0.96, 0.89, 0.92, 0.98, 1.23, 1.05, 0.86, 0.92, 0.95, 1.11, 0.98, 1.22, 1.03, 1.34, 1.42,
1.14, 0.79, 0.77, 0.84, 0.78, 0.76, 0.82, 0.82, 0.89, 0.97, 0.90, 0.70, 0.71, 0.71, 0.73, 0.72, 0.74, 0.73, 0.76, 0.72,
0.86, 0.81, 0.82, 0.76, 0.79, 0.77, 0.73, 0.90, 0.95, 0.86, 1.18, 1.03, 0.98, 0.92, 0.90, 0.83, 0.84, 1.19, 1.17, 0.98,
1.15, 0.97, 0.89, 1.42, 1.65, 1.44, 1.93, 1.83, 1.81, 1.67, 1.61, 1.36, 1.41, 1.32, 1.45, 1.58, 1.57, 1.53, 1.74, 1.70,
1.88, 1.94, 1.81, 1.69, 1.77, 1.87, 1.79, 1.89, 1.92, 1.98, 1.99, 1.98, 1.89, 1.65, 1.80, 1.82, 1.91, 1.94, 1.75, 1.61,
1.50, 1.07, 1.34, 1.27, 1.60, 1.45, 1.55, 0.93, 0.99, 0.90, 1.35, 1.18, 1.07, 0.87, 0.93, 0.96, 0.85, 0.82, 0.77, 1.15,
0.99, 1.27, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.86, 1.71, 1.82, 1.48, 1.62, 1.71, 0.98, 1.20, 1.05, 1.34, 1.17, 1.34, 1.53, 1.27, 1.46, 1.77, 1.60, 1.57, 1.16, 1.38,
1.12, 1.35, 1.06, 1.00, 1.28, 0.97, 0.89, 0.95, 0.76, 0.81, 0.79, 0.86, 0.85, 0.92, 0.93, 0.93, 0.85, 0.87, 0.74, 0.78,
0.74, 0.79, 0.82, 0.76, 0.79, 0.96, 0.85, 0.90, 0.94, 1.09, 0.99, 0.81, 0.85, 0.89, 0.95, 0.90, 0.99, 0.94, 1.10, 1.24,
0.98, 0.75, 0.73, 0.78, 0.74, 0.72, 0.77, 0.76, 0.82, 0.89, 0.83, 0.73, 0.71, 0.71, 0.71, 0.70, 0.72, 0.77, 0.80, 0.74,
0.90, 0.85, 0.84, 0.78, 0.79, 0.75, 0.73, 0.92, 0.95, 0.86, 1.05, 0.99, 0.94, 0.90, 0.86, 0.79, 0.81, 1.00, 0.98, 0.91,
0.96, 0.89, 0.83, 1.27, 1.50, 1.23, 1.80, 1.69, 1.63, 1.46, 1.37, 1.09, 1.16, 1.24, 1.44, 1.49, 1.69, 1.59, 1.80, 1.69,
1.87, 1.86, 1.72, 1.82, 1.91, 1.94, 1.92, 1.95, 1.99, 1.98, 1.91, 1.97, 1.89, 1.51, 1.72, 1.67, 1.77, 1.86, 1.55, 1.41,
1.25, 1.33, 1.58, 1.50, 1.80, 1.63, 1.74, 1.04, 1.21, 0.97, 1.48, 1.37, 1.21, 0.93, 0.97, 1.05, 0.92, 0.88, 0.84, 1.14,
1.02, 1.34, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.94, 1.84, 1.87, 1.64, 1.71, 1.71, 1.14, 1.38, 1.19, 1.46, 1.23, 1.26, 1.48, 1.26, 1.50, 1.91, 1.80, 1.76, 1.41, 1.61,
1.39, 1.59, 1.33, 1.24, 1.51, 1.18, 0.97, 1.11, 0.82, 0.88, 0.86, 0.94, 0.92, 0.99, 1.03, 0.98, 0.91, 0.90, 0.79, 0.84,
0.77, 0.79, 0.84, 0.77, 0.83, 0.99, 0.85, 0.91, 0.92, 1.02, 1.00, 0.79, 0.80, 0.86, 0.88, 0.84, 0.92, 0.88, 0.97, 1.10,
0.94, 0.74, 0.71, 0.74, 0.72, 0.70, 0.73, 0.72, 0.76, 0.82, 0.77, 0.77, 0.73, 0.74, 0.71, 0.70, 0.73, 0.83, 0.85, 0.78,
0.92, 0.88, 0.86, 0.81, 0.79, 0.74, 0.75, 0.92, 0.93, 0.85, 0.96, 0.94, 0.88, 0.86, 0.81, 0.75, 0.79, 0.93, 0.90, 0.85,
0.88, 0.82, 0.77, 1.05, 1.27, 0.99, 1.60, 1.47, 1.39, 1.20, 1.11, 0.95, 0.97, 1.08, 1.33, 1.31, 1.70, 1.55, 1.76, 1.57,
1.76, 1.70, 1.54, 1.85, 1.97, 1.91, 1.99, 1.97, 1.99, 1.91, 1.77, 1.88, 1.85, 1.39, 1.64, 1.51, 1.58, 1.74, 1.32, 1.22,
1.01, 1.54, 1.76, 1.65, 1.93, 1.70, 1.85, 1.28, 1.39, 1.09, 1.52, 1.48, 1.26, 0.97, 0.99, 1.18, 1.00, 0.93, 0.90, 1.05,
1.01, 1.31, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.97, 1.92, 1.88, 1.79, 1.79, 1.71, 1.37, 1.59, 1.38, 1.60, 1.35, 1.23, 1.47, 1.30, 1.56, 1.99, 1.93, 1.90, 1.60, 1.78,
1.61, 1.79, 1.57, 1.48, 1.72, 1.40, 1.14, 1.37, 0.89, 0.96, 0.94, 1.07, 1.00, 1.21, 1.30, 1.14, 0.98, 0.96, 0.86, 0.91,
0.83, 0.82, 0.88, 0.82, 0.89, 1.11, 0.87, 0.94, 0.93, 1.02, 1.07, 0.80, 0.79, 0.85, 0.82, 0.80, 0.87, 0.85, 0.93, 1.02,
0.93, 0.77, 0.72, 0.74, 0.71, 0.70, 0.70, 0.71, 0.72, 0.77, 0.74, 0.82, 0.76, 0.79, 0.72, 0.73, 0.76, 0.89, 0.89, 0.82,
0.93, 0.91, 0.86, 0.83, 0.79, 0.73, 0.76, 0.91, 0.89, 0.83, 0.89, 0.89, 0.82, 0.82, 0.76, 0.72, 0.76, 0.86, 0.83, 0.79,
0.82, 0.76, 0.73, 0.94, 1.00, 0.91, 1.37, 1.21, 1.14, 0.98, 0.96, 0.88, 0.89, 0.96, 1.14, 1.07, 1.60, 1.40, 1.61, 1.37,
1.57, 1.48, 1.30, 1.78, 1.93, 1.79, 1.99, 1.92, 1.90, 1.79, 1.59, 1.72, 1.79, 1.30, 1.56, 1.35, 1.38, 1.60, 1.11, 1.07,
0.94, 1.68, 1.86, 1.71, 1.97, 1.68, 1.86, 1.44, 1.49, 1.22, 1.44, 1.49, 1.22, 0.99, 0.99, 1.23, 1.19, 0.98, 0.97, 0.97,
0.98, 1.19, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.94, 1.97, 1.87, 1.91, 1.85, 1.71, 1.60, 1.77, 1.58, 1.74, 1.51, 1.26, 1.48, 1.39, 1.64, 1.99, 1.97, 1.99, 1.70, 1.85,
1.76, 1.91, 1.76, 1.70, 1.88, 1.55, 1.33, 1.57, 0.96, 1.08, 1.05, 1.31, 1.27, 1.47, 1.54, 1.39, 1.20, 1.11, 0.93, 0.99,
0.90, 0.88, 0.95, 0.88, 0.97, 1.32, 0.92, 1.01, 0.97, 1.10, 1.22, 0.84, 0.80, 0.88, 0.79, 0.79, 0.85, 0.86, 0.92, 1.02,
0.94, 0.82, 0.76, 0.77, 0.72, 0.73, 0.70, 0.72, 0.71, 0.74, 0.74, 0.88, 0.81, 0.85, 0.75, 0.77, 0.82, 0.94, 0.93, 0.86,
0.92, 0.92, 0.86, 0.85, 0.79, 0.74, 0.79, 0.88, 0.85, 0.81, 0.82, 0.83, 0.77, 0.78, 0.73, 0.71, 0.75, 0.79, 0.77, 0.74,
0.77, 0.73, 0.70, 0.86, 0.92, 0.84, 1.14, 0.99, 0.98, 0.91, 0.90, 0.84, 0.83, 0.88, 0.97, 0.94, 1.41, 1.18, 1.39, 1.11,
1.33, 1.24, 1.03, 1.61, 1.80, 1.59, 1.91, 1.84, 1.76, 1.64, 1.38, 1.51, 1.71, 1.26, 1.50, 1.23, 1.19, 1.46, 0.99, 1.00,
0.91, 1.70, 1.85, 1.65, 1.93, 1.54, 1.76, 1.52, 1.48, 1.26, 1.28, 1.39, 1.09, 0.99, 0.97, 1.18, 1.31, 1.01, 1.05, 0.90,
0.93, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.86, 1.95, 1.82, 1.98, 1.89, 1.71, 1.80, 1.91, 1.77, 1.86, 1.67, 1.34, 1.53, 1.51, 1.72, 1.92, 1.91, 1.99, 1.69, 1.82,
1.80, 1.94, 1.87, 1.86, 1.97, 1.59, 1.44, 1.69, 1.05, 1.24, 1.27, 1.49, 1.50, 1.69, 1.72, 1.63, 1.46, 1.37, 1.00, 1.23,
0.98, 0.95, 1.09, 0.96, 1.16, 1.55, 0.99, 1.25, 1.10, 1.24, 1.41, 0.90, 0.85, 0.94, 0.79, 0.81, 0.85, 0.89, 0.94, 1.09,
0.98, 0.89, 0.82, 0.83, 0.74, 0.77, 0.72, 0.76, 0.73, 0.75, 0.78, 0.94, 0.86, 0.91, 0.79, 0.83, 0.89, 0.99, 0.95, 0.90,
0.90, 0.92, 0.84, 0.86, 0.79, 0.75, 0.81, 0.85, 0.80, 0.78, 0.76, 0.77, 0.73, 0.74, 0.71, 0.71, 0.73, 0.74, 0.74, 0.71,
0.76, 0.72, 0.70, 0.79, 0.85, 0.78, 0.98, 0.92, 0.93, 0.85, 0.87, 0.82, 0.79, 0.81, 0.89, 0.86, 1.16, 0.97, 1.12, 0.95,
1.06, 1.00, 0.93, 1.38, 1.60, 1.35, 1.77, 1.71, 1.57, 1.48, 1.20, 1.28, 1.62, 1.27, 1.46, 1.17, 1.05, 1.34, 0.96, 0.99,
0.90, 1.63, 1.74, 1.50, 1.80, 1.33, 1.58, 1.48, 1.37, 1.21, 1.04, 1.21, 0.97, 0.97, 0.93, 1.05, 1.34, 1.02, 1.14, 0.84,
0.88, 0.92, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.74, 1.89, 1.76, 1.98, 1.89, 1.71, 1.93, 1.99, 1.91, 1.94, 1.82, 1.46, 1.60, 1.65, 1.80, 1.79, 1.77, 1.92, 1.57, 1.69,
1.74, 1.87, 1.88, 1.94, 1.98, 1.53, 1.45, 1.70, 1.18, 1.32, 1.42, 1.58, 1.65, 1.83, 1.81, 1.81, 1.67, 1.61, 1.19, 1.44,
1.17, 1.11, 1.36, 1.15, 1.41, 1.75, 1.22, 1.50, 1.34, 1.42, 1.61, 0.98, 0.92, 1.03, 0.83, 0.86, 0.89, 0.95, 0.98, 1.23,
1.14, 0.97, 0.89, 0.90, 0.78, 0.82, 0.76, 0.82, 0.77, 0.79, 0.84, 0.98, 0.90, 0.98, 0.83, 0.89, 0.97, 1.03, 0.95, 0.92,
0.86, 0.90, 0.82, 0.86, 0.79, 0.77, 0.84, 0.81, 0.76, 0.76, 0.72, 0.73, 0.70, 0.72, 0.71, 0.73, 0.73, 0.72, 0.74, 0.71,
0.78, 0.74, 0.72, 0.75, 0.80, 0.76, 0.94, 0.88, 0.91, 0.83, 0.87, 0.84, 0.79, 0.76, 0.82, 0.80, 0.97, 0.89, 0.96, 0.88,
0.95, 0.94, 0.87, 1.11, 1.37, 1.10, 1.59, 1.57, 1.37, 1.33, 1.05, 1.08, 1.54, 1.34, 1.46, 1.16, 0.99, 1.26, 0.96, 1.05,
0.92, 1.45, 1.55, 1.27, 1.60, 1.07, 1.34, 1.35, 1.18, 1.07, 0.93, 0.99, 0.90, 0.93, 0.87, 0.96, 1.27, 0.99, 1.15, 0.77,
0.82, 0.85, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.60, 1.78, 1.68, 1.93, 1.86, 1.71, 1.97, 1.99, 1.99, 1.97, 1.93, 1.60, 1.68, 1.78, 1.86, 1.61, 1.57, 1.79, 1.37, 1.48,
1.59, 1.72, 1.79, 1.92, 1.90, 1.38, 1.35, 1.60, 1.23, 1.30, 1.47, 1.56, 1.71, 1.88, 1.79, 1.92, 1.79, 1.79, 1.30, 1.56,
1.35, 1.37, 1.59, 1.38, 1.60, 1.90, 1.48, 1.72, 1.57, 1.61, 1.79, 1.21, 1.00, 1.30, 0.89, 0.94, 0.96, 1.07, 1.14, 1.40,
1.37, 1.14, 0.96, 0.98, 0.82, 0.88, 0.82, 0.89, 0.83, 0.86, 0.91, 1.02, 0.93, 1.07, 0.87, 0.94, 1.11, 1.02, 0.93, 0.93,
0.82, 0.87, 0.80, 0.85, 0.79, 0.80, 0.85, 0.77, 0.72, 0.74, 0.71, 0.70, 0.70, 0.71, 0.72, 0.77, 0.74, 0.72, 0.76, 0.73,
0.82, 0.79, 0.76, 0.73, 0.79, 0.76, 0.93, 0.86, 0.91, 0.83, 0.89, 0.89, 0.82, 0.72, 0.76, 0.76, 0.89, 0.82, 0.89, 0.82,
0.89, 0.91, 0.83, 0.96, 1.14, 0.97, 1.40, 1.44, 1.19, 1.22, 0.99, 0.98, 1.49, 1.44, 1.49, 1.22, 0.99, 1.23, 0.98, 1.19,
0.97, 1.21, 1.30, 1.00, 1.37, 0.94, 1.07, 1.14, 0.98, 0.96, 0.86, 0.91, 0.83, 0.88, 0.82, 0.89, 1.11, 0.94, 1.07, 0.73,
0.76, 0.79, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.46, 1.65, 1.60, 1.82, 1.80, 1.71, 1.93, 1.91, 1.99, 1.94, 1.98, 1.74, 1.76, 1.89, 1.89, 1.42, 1.34, 1.61, 1.11, 1.22,
1.36, 1.50, 1.61, 1.81, 1.75, 1.15, 1.17, 1.41, 1.18, 1.19, 1.42, 1.44, 1.65, 1.83, 1.67, 1.94, 1.81, 1.88, 1.32, 1.58,
1.45, 1.57, 1.74, 1.53, 1.70, 1.98, 1.69, 1.87, 1.77, 1.79, 1.92, 1.45, 1.27, 1.55, 0.97, 1.07, 1.11, 1.34, 1.37, 1.59,
1.60, 1.35, 1.07, 1.18, 0.86, 0.93, 0.87, 0.96, 0.90, 0.93, 0.99, 1.03, 0.95, 1.15, 0.90, 0.99, 1.27, 0.98, 0.90, 0.92,
0.78, 0.83, 0.77, 0.84, 0.79, 0.82, 0.86, 0.73, 0.71, 0.73, 0.72, 0.70, 0.73, 0.72, 0.76, 0.81, 0.76, 0.76, 0.82, 0.77,
0.89, 0.85, 0.82, 0.75, 0.80, 0.80, 0.94, 0.88, 0.94, 0.87, 0.95, 0.96, 0.88, 0.72, 0.74, 0.76, 0.83, 0.78, 0.84, 0.79,
0.87, 0.91, 0.83, 0.89, 0.98, 0.92, 1.23, 1.34, 1.05, 1.16, 0.99, 0.96, 1.46, 1.57, 1.54, 1.33, 1.05, 1.26, 1.08, 1.37,
1.10, 0.98, 1.03, 0.92, 1.14, 0.86, 0.95, 0.97, 0.90, 0.89, 0.79, 0.84, 0.77, 0.82, 0.76, 0.82, 0.97, 0.89, 0.98, 0.71,
0.72, 0.74, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.34, 1.51, 1.53, 1.67, 1.72, 1.71, 1.80, 1.77, 1.91, 1.86, 1.98, 1.86, 1.82, 1.95, 1.89, 1.24, 1.10, 1.41, 0.95, 0.99,
1.09, 1.25, 1.37, 1.63, 1.55, 0.96, 0.98, 1.16, 1.05, 1.00, 1.27, 1.23, 1.50, 1.69, 1.46, 1.86, 1.72, 1.87, 1.24, 1.49,
1.44, 1.69, 1.80, 1.59, 1.69, 1.97, 1.82, 1.94, 1.91, 1.92, 1.99, 1.63, 1.50, 1.74, 1.16, 1.33, 1.38, 1.58, 1.60, 1.77,
1.80, 1.48, 1.21, 1.37, 0.90, 0.97, 0.93, 1.05, 0.97, 1.04, 1.21, 0.99, 0.95, 1.14, 0.92, 1.02, 1.34, 0.94, 0.86, 0.90,
0.74, 0.79, 0.75, 0.81, 0.79, 0.84, 0.86, 0.71, 0.71, 0.73, 0.76, 0.73, 0.77, 0.74, 0.80, 0.85, 0.78, 0.81, 0.89, 0.84,
0.97, 0.92, 0.88, 0.79, 0.85, 0.86, 0.98, 0.92, 1.00, 0.93, 1.06, 1.12, 0.95, 0.74, 0.74, 0.78, 0.79, 0.76, 0.82, 0.79,
0.87, 0.93, 0.85, 0.85, 0.94, 0.90, 1.09, 1.27, 0.99, 1.17, 1.05, 0.96, 1.46, 1.71, 1.62, 1.48, 1.20, 1.34, 1.28, 1.57,
1.35, 0.90, 0.94, 0.85, 0.98, 0.81, 0.89, 0.89, 0.83, 0.82, 0.75, 0.78, 0.73, 0.77, 0.72, 0.76, 0.89, 0.83, 0.91, 0.71,
0.70, 0.72, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 },
{ 1.26, 1.39, 1.48, 1.51, 1.64, 1.71, 1.60, 1.58, 1.77, 1.74, 1.91, 1.94, 1.87, 1.97, 1.85, 1.10, 0.97, 1.22, 0.88, 0.92,
0.95, 1.01, 1.11, 1.39, 1.32, 0.88, 0.90, 0.97, 0.96, 0.93, 1.05, 0.99, 1.27, 1.47, 1.20, 1.70, 1.54, 1.76, 1.08, 1.31,
1.33, 1.70, 1.76, 1.55, 1.57, 1.88, 1.85, 1.91, 1.97, 1.99, 1.99, 1.70, 1.65, 1.85, 1.41, 1.54, 1.61, 1.76, 1.80, 1.91,
1.93, 1.52, 1.26, 1.48, 0.92, 0.99, 0.97, 1.18, 1.09, 1.28, 1.39, 0.94, 0.93, 1.05, 0.92, 1.01, 1.31, 0.88, 0.81, 0.86,
0.72, 0.75, 0.74, 0.79, 0.79, 0.86, 0.85, 0.71, 0.73, 0.75, 0.82, 0.77, 0.83, 0.78, 0.85, 0.88, 0.81, 0.88, 0.97, 0.90,
1.18, 1.00, 0.93, 0.86, 0.92, 0.94, 1.14, 0.99, 1.24, 1.03, 1.33, 1.39, 1.11, 0.79, 0.77, 0.84, 0.79, 0.77, 0.84, 0.83,
0.90, 0.98, 0.91, 0.85, 0.92, 0.91, 1.02, 1.26, 1.00, 1.23, 1.19, 0.99, 1.50, 1.84, 1.71, 1.64, 1.38, 1.46, 1.51, 1.76,
1.59, 0.84, 0.88, 0.80, 0.94, 0.79, 0.86, 0.82, 0.77, 0.76, 0.74, 0.74, 0.71, 0.73, 0.70, 0.72, 0.82, 0.77, 0.85, 0.74,
0.70, 0.73, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }

View file

@ -0,0 +1,805 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* The models file format
*
* =======================================================================
*/
#include "../ref_shared.h"
/*
=================
Mod_LoadAliasModel/Mod_LoadMD2
=================
*/
void *
Mod_LoadMD2 (const char *mod_name, const void *buffer, int modfilelen,
vec3_t mins, vec3_t maxs, struct image_s **skins, findimage_t find_image,
modtype_t *type)
{
dmdl_t *pinmodel, *pheader;
dtriangle_t *pintri, *pouttri;
dstvert_t *pinst, *poutst;
int *pincmd, *poutcmd;
void *extradata;
int version;
int ofs_end;
int i, j;
pinmodel = (dmdl_t *)buffer;
version = LittleLong (pinmodel->version);
if (version != ALIAS_VERSION)
{
R_Printf(PRINT_ALL, "%s: %s has wrong version number (%i should be %i)",
__func__, mod_name, version, ALIAS_VERSION);
return NULL;
}
ofs_end = LittleLong(pinmodel->ofs_end);
if (ofs_end < 0 || ofs_end > modfilelen)
{
R_Printf(PRINT_ALL, "%s: model %s file size(%d) too small, should be %d",
__func__, mod_name, modfilelen, ofs_end);
return NULL;
}
extradata = Hunk_Begin(modfilelen);
pheader = Hunk_Alloc(ofs_end);
// byte swap the header fields and sanity check
for (i=0 ; i<sizeof(dmdl_t)/sizeof(int) ; i++)
((int *)pheader)[i] = LittleLong (((int *)buffer)[i]);
if (pheader->skinheight > MAX_LBM_HEIGHT)
{
R_Printf(PRINT_ALL, "%s: model %s has a skin taller than %d",
__func__, mod_name, MAX_LBM_HEIGHT);
return NULL;
}
if (pheader->num_xyz <= 0)
{
R_Printf(PRINT_ALL, "%s: model %s has no vertices",
__func__, mod_name);
return NULL;
}
if (pheader->num_xyz > MAX_VERTS)
{
R_Printf(PRINT_ALL, "%s: model %s has too many vertices",
__func__, mod_name);
return NULL;
}
if (pheader->num_st <= 0)
{
R_Printf(PRINT_ALL, "%s: model %s has no st vertices",
__func__, mod_name);
return NULL;
}
if (pheader->num_tris <= 0)
{
R_Printf(PRINT_ALL, "%s: model %s has no triangles",
__func__, mod_name);
return NULL;
}
if (pheader->num_frames <= 0)
{
R_Printf(PRINT_ALL, "%s: model %s has no frames",
__func__, mod_name);
return NULL;
}
if (pheader->num_skins > MAX_MD2SKINS)
{
R_Printf(PRINT_ALL, "%s has too many skins (%i > %i), "
"extra sprites will be ignored\n",
mod_name, pheader->num_skins, MAX_MD2SKINS);
pheader->num_skins = MAX_MD2SKINS;
}
//
// load base s and t vertices (not used in gl version)
//
pinst = (dstvert_t *) ((byte *)pinmodel + pheader->ofs_st);
poutst = (dstvert_t *) ((byte *)pheader + pheader->ofs_st);
for (i=0 ; i<pheader->num_st ; i++)
{
poutst[i].s = LittleShort (pinst[i].s);
poutst[i].t = LittleShort (pinst[i].t);
}
//
// load triangle lists
//
pintri = (dtriangle_t *) ((byte *)pinmodel + pheader->ofs_tris);
pouttri = (dtriangle_t *) ((byte *)pheader + pheader->ofs_tris);
for (i=0 ; i<pheader->num_tris ; i++)
{
for (j=0 ; j<3 ; j++)
{
pouttri[i].index_xyz[j] = LittleShort (pintri[i].index_xyz[j]);
pouttri[i].index_st[j] = LittleShort (pintri[i].index_st[j]);
}
}
//
// load the frames
//
for (i=0 ; i<pheader->num_frames ; i++)
{
daliasframe_t *pinframe, *poutframe;
pinframe = (daliasframe_t *) ((byte *)pinmodel
+ pheader->ofs_frames + i * pheader->framesize);
poutframe = (daliasframe_t *) ((byte *)pheader
+ pheader->ofs_frames + i * pheader->framesize);
memcpy (poutframe->name, pinframe->name, sizeof(poutframe->name));
for (j=0 ; j<3 ; j++)
{
poutframe->scale[j] = LittleFloat (pinframe->scale[j]);
poutframe->translate[j] = LittleFloat (pinframe->translate[j]);
}
// verts are all 8 bit, so no swapping needed
memcpy (poutframe->verts, pinframe->verts,
pheader->num_xyz*sizeof(dtrivertx_t));
}
//
// load the glcmds
//
pincmd = (int *) ((byte *)pinmodel + pheader->ofs_glcmds);
poutcmd = (int *) ((byte *)pheader + pheader->ofs_glcmds);
for (i=0; i < pheader->num_glcmds; i++)
{
poutcmd[i] = LittleLong (pincmd[i]);
}
if (poutcmd[pheader->num_glcmds-1] != 0)
{
R_Printf(PRINT_ALL, "%s: Entity %s has possible last element issues with %d verts.\n",
__func__, mod_name, poutcmd[pheader->num_glcmds-1]);
}
// register all skins
memcpy ((char *)pheader + pheader->ofs_skins, (char *)pinmodel + pheader->ofs_skins,
pheader->num_skins*MAX_SKINNAME);
for (i=0 ; i<pheader->num_skins ; i++)
{
skins[i] = find_image((char *)pheader + pheader->ofs_skins + i*MAX_SKINNAME,
it_skin);
}
*type = mod_alias;
mins[0] = -32;
mins[1] = -32;
mins[2] = -32;
maxs[0] = 32;
maxs[1] = 32;
maxs[2] = 32;
return extradata;
}
/*
==============================================================================
SPRITE MODELS
==============================================================================
*/
/*
=================
Mod_LoadSP2
support for .sp2 sprites
=================
*/
void *
Mod_LoadSP2 (const char *mod_name, const void *buffer, int modfilelen,
struct image_s **skins, findimage_t find_image, modtype_t *type)
{
dsprite_t *sprin, *sprout;
void *extradata;
int i;
sprin = (dsprite_t *)buffer;
extradata = Hunk_Begin(modfilelen);
sprout = Hunk_Alloc(modfilelen);
sprout->ident = LittleLong(sprin->ident);
sprout->version = LittleLong(sprin->version);
sprout->numframes = LittleLong(sprin->numframes);
if (sprout->version != SPRITE_VERSION)
{
R_Printf(PRINT_ALL, "%s has wrong version number (%i should be %i)",
mod_name, sprout->version, SPRITE_VERSION);
return NULL;
}
if (sprout->numframes > MAX_MD2SKINS)
{
R_Printf(PRINT_ALL, "%s has too many frames (%i > %i), "
"extra frames will be ignored\n",
mod_name, sprout->numframes, MAX_MD2SKINS);
sprout->numframes = MAX_MD2SKINS;
}
/* byte swap everything */
for (i = 0; i < sprout->numframes; i++)
{
sprout->frames[i].width = LittleLong(sprin->frames[i].width);
sprout->frames[i].height = LittleLong(sprin->frames[i].height);
sprout->frames[i].origin_x = LittleLong(sprin->frames[i].origin_x);
sprout->frames[i].origin_y = LittleLong(sprin->frames[i].origin_y);
memcpy(sprout->frames[i].name, sprin->frames[i].name, MAX_SKINNAME);
skins[i] = find_image((char *)sprout->frames[i].name, it_sprite);
}
*type = mod_sprite;
return extradata;
}
/*
=================
Mod_ReLoad
Reload images in SP2/MD2 (mark registration_sequence)
=================
*/
int
Mod_ReLoadSkins(struct image_s **skins, findimage_t find_image, void *extradata,
modtype_t type)
{
if (type == mod_sprite)
{
dsprite_t *sprout;
int i;
sprout = (dsprite_t *)extradata;
for (i=0; i < sprout->numframes; i++)
{
skins[i] = find_image(sprout->frames[i].name, it_sprite);
}
return sprout->numframes;
}
else if (type == mod_alias)
{
dmdl_t *pheader;
int i;
pheader = (dmdl_t *)extradata;
for (i=0; i < pheader->num_skins; i++)
{
skins[i] = find_image ((char *)pheader + pheader->ofs_skins + i*MAX_SKINNAME, it_skin);
}
return pheader->num_frames;
}
/* Unknow format, no images associated with it */
return 0;
}
/*
=================
Mod_SetParent
=================
*/
static void
Mod_SetParent(mnode_t *node, mnode_t *parent)
{
node->parent = parent;
if (node->contents != CONTENTS_NODE)
{
return;
}
Mod_SetParent (node->children[0], node);
Mod_SetParent (node->children[1], node);
}
/*
=================
Mod_NumberLeafs
=================
*/
static void
Mod_NumberLeafs(mleaf_t *leafs, mnode_t *node, int *r_leaftovis, int *r_vistoleaf,
int *numvisleafs)
{
if (node->contents != CONTENTS_NODE)
{
mleaf_t *leaf;
int leafnum;
leaf = (mleaf_t *)node;
leafnum = leaf - leafs;
if (leaf->contents & CONTENTS_SOLID)
{
return;
}
r_leaftovis[leafnum] = *numvisleafs;
r_vistoleaf[*numvisleafs] = leafnum;
(*numvisleafs) ++;
return;
}
Mod_NumberLeafs(leafs, node->children[0], r_leaftovis, r_vistoleaf,
numvisleafs);
Mod_NumberLeafs(leafs, node->children[1], r_leaftovis, r_vistoleaf,
numvisleafs);
}
/*
=================
Mod_LoadNodes
=================
*/
void
Mod_LoadNodes(const char *name, cplane_t *planes, int numplanes, mleaf_t *leafs,
int numleafs, mnode_t **nodes, int *numnodes, const byte *mod_base,
const lump_t *l)
{
int r_leaftovis[MAX_MAP_LEAFS], r_vistoleaf[MAX_MAP_LEAFS];
int i, count, numvisleafs;
dnode_t *in;
mnode_t *out;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
*nodes = out;
*numnodes = count;
for (i = 0; i < count; i++, in++, out++)
{
int j, planenum;
for (j = 0; j < 3; j++)
{
out->minmaxs[j] = LittleShort(in->mins[j]);
out->minmaxs[3 + j] = LittleShort(in->maxs[j]);
}
planenum = LittleLong(in->planenum);
if (planenum < 0 || planenum >= numplanes)
{
ri.Sys_Error(ERR_DROP, "%s: Incorrect %d < %d planenum.",
__func__, planenum, numplanes);
}
out->plane = planes + planenum;
out->firstsurface = LittleShort(in->firstface);
out->numsurfaces = LittleShort(in->numfaces);
out->contents = CONTENTS_NODE; /* differentiate from leafs */
for (j = 0; j < 2; j++)
{
int leafnum;
leafnum = LittleLong(in->children[j]);
if (leafnum >= 0)
{
if (leafnum < 0 || leafnum >= *numnodes)
{
ri.Sys_Error(ERR_DROP, "%s: Incorrect %d nodenum as leaf.",
__func__, leafnum);
}
out->children[j] = *nodes + leafnum;
}
else
{
leafnum = -1 - leafnum;
if (leafnum < 0 || leafnum >= numleafs)
{
ri.Sys_Error(ERR_DROP, "%s: Incorrect %d leafnum.",
__func__, leafnum);
}
out->children[j] = (mnode_t *)(leafs + leafnum);
}
}
}
Mod_SetParent(*nodes, NULL); /* sets nodes and leafs */
numvisleafs = 0;
Mod_NumberLeafs (leafs, *nodes, r_leaftovis, r_vistoleaf, &numvisleafs);
}
/*
=================
Mod_LoadVisibility
=================
*/
void
Mod_LoadVisibility (dvis_t **vis, const byte *mod_base, const lump_t *l)
{
dvis_t *out;
int i;
if (!l->filelen)
{
*vis = NULL;
return;
}
out = Hunk_Alloc(l->filelen);
*vis = out;
memcpy(out, mod_base + l->fileofs, l->filelen);
out->numclusters = LittleLong(out->numclusters);
for (i = 0; i < out->numclusters; i++)
{
out->bitofs[i][0] = LittleLong(out->bitofs[i][0]);
out->bitofs[i][1] = LittleLong(out->bitofs[i][1]);
}
}
/*
=================
Mod_LoadVertexes
extra for skybox
=================
*/
void
Mod_LoadVertexes(const char *name, mvertex_t **vertexes, int *numvertexes,
const byte *mod_base, const lump_t *l, int extra)
{
dvertex_t *in;
mvertex_t *out;
int i, count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc((count + extra)*sizeof(*out));
/*
* FIXME: Recheck with soft render
* Fix for the problem where the games dumped core
* when changing levels.
*/
memset(out, 0, (count + extra) * sizeof(*out));
*vertexes = out;
*numvertexes = count;
for (i = 0; i < count; i++, in++, out++)
{
out->position[0] = LittleFloat(in->point[0]);
out->position[1] = LittleFloat(in->point[1]);
out->position[2] = LittleFloat(in->point[2]);
}
}
/*
=================
Mod_LoadLighting
=================
*/
void
Mod_LoadLighting(byte **lightdata, const byte *mod_base, const lump_t *l)
{
int size;
if (!l->filelen)
{
*lightdata = NULL;
return;
}
size = l->filelen;
*lightdata = Hunk_Alloc(size);
memcpy(*lightdata, mod_base + l->fileofs, size);
}
/*
=================
Mod_LoadTexinfo
extra for skybox in soft render
=================
*/
void
Mod_LoadTexinfo(const char *name, mtexinfo_t **texinfo, int *numtexinfo,
const byte *mod_base, const lump_t *l, findimage_t find_image,
struct image_s *notexture, int extra)
{
texinfo_t *in;
mtexinfo_t *out, *step;
int i, count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc((count + extra)*sizeof(*out));
*texinfo = out;
*numtexinfo = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
struct image_s *image;
int j, next;
for (j = 0; j < 4; j++)
{
out->vecs[0][j] = LittleFloat(in->vecs[0][j]);
out->vecs[1][j] = LittleFloat(in->vecs[1][j]);
}
out->flags = LittleLong (in->flags);
next = LittleLong (in->nexttexinfo);
if (next > 0)
{
out->next = *texinfo + next;
}
else
{
/*
* Fix for the problem where the game
* domed core when loading a new level.
*/
out->next = NULL;
}
image = GetTexImage(in->texture, find_image);
if (!image)
{
R_Printf(PRINT_ALL, "%s: Couldn't load %s\n",
__func__, in->texture);
image = notexture;
}
out->image = image;
}
// count animation frames
for (i=0 ; i<count ; i++)
{
out = (*texinfo) + i;
out->numframes = 1;
for (step = out->next ; step && step != out ; step=step->next)
{
out->numframes++;
}
}
}
/*
=================
Mod_LoadEdges
extra is used for skybox, which adds 6 surfaces
=================
*/
void
Mod_LoadEdges(const char *name, medge_t **edges, int *numedges,
const byte *mod_base, const lump_t *l, int extra)
{
dedge_t *in;
medge_t *out;
int i, count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc((count + extra) * sizeof(*out));
*edges = out;
*numedges = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
out->v[0] = (unsigned short)LittleShort(in->v[0]);
out->v[1] = (unsigned short)LittleShort(in->v[1]);
}
}
/*
=================
Mod_LoadPlanes
extra is used for skybox, which adds 6 surfaces
=================
*/
void
Mod_LoadPlanes(const char *name, cplane_t **planes, int *numplanes,
const byte *mod_base, const lump_t *l, int extra)
{
int i;
cplane_t *out;
dplane_t *in;
int count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, name);
}
count = l->filelen / sizeof(*in);
// FIXME: why double of count
out = Hunk_Alloc((count * 2 + extra) * sizeof(*out));
*planes = out;
*numplanes = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
int bits, j;
bits = 0;
for (j=0 ; j<3 ; j++)
{
out->normal[j] = LittleFloat (in->normal[j]);
if (out->normal[j] < 0)
bits |= 1<<j;
}
out->dist = LittleFloat (in->dist);
out->type = LittleLong (in->type);
out->signbits = bits;
}
}
/*
=================
Mod_LoadSurfedges
=================
*/
void
Mod_LoadSurfedges(const char *name, int **surfedges, int *numsurfedges,
const byte *mod_base, const lump_t *l, int extra)
{
int i, count;
int *in, *out;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc((count + extra)*sizeof(*out)); // extra for skybox
*surfedges = out;
*numsurfedges = count;
for ( i=0 ; i<count ; i++)
out[i] = LittleLong (in[i]);
}
/*
=================
Mod_LoadSurfedges
calculate the size that Hunk_Alloc(), called by Mod_Load*() from Mod_LoadBrushModel(),
will use (=> includes its padding), so we'll know how big the hunk needs to be
extra is used for skybox, which adds 6 surfaces
=================
*/
int
Mod_CalcLumpHunkSize(const lump_t *l, int inSize, int outSize, int extra)
{
if (l->filelen % inSize)
{
// Mod_Load*() will error out on this because of "funny size"
// don't error out here because in Mod_Load*() it can print the functionname
// (=> tells us what kind of lump) before shutting down the game
return 0;
}
int count = l->filelen / inSize + extra;
int size = count * outSize;
// round to cacheline, like Hunk_Alloc() does
size = (size + 31) & ~31;
return size;
}
/*
===============
Mod_PointInLeaf
===============
*/
mleaf_t *
Mod_PointInLeaf(const vec3_t p, mnode_t *node)
{
if (!node)
{
ri.Sys_Error(ERR_DROP, "%s: bad node.", __func__);
return NULL;
}
while (1)
{
float d;
cplane_t *plane;
if (node->contents != CONTENTS_NODE)
{
return (mleaf_t *)node;
}
plane = node->plane;
d = DotProduct(p, plane->normal) - plane->dist;
if (d > 0)
{
node = node->children[0];
}
else
{
node = node->children[1];
}
}
return NULL; /* never reached */
}

View file

@ -0,0 +1,350 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* The PCX file format
*
* =======================================================================
*/
#include "../ref_shared.h"
// Fix Jennell Jaquays' name in the Quitscreen
// this is 98x11 pixels, each value an index
// into the standard baseq2/pak0/pics/quit.pcx colormap
static unsigned char quitscreenfix[] = {
191,191,191,47,28,39,4,4,39,1,47,28,47,28,29,1,
28,28,47,31,31,1,29,31,1,28,47,47,47,47,29,28,
47,31,30,28,40,40,4,28,28,40,39,40,29,102,102,245,
28,39,4,4,39,103,40,40,1,1,102,94,47,47,1,94,
94,94,94,47,102,245,103,103,103,47,1,102,1,102,29,29,
29,29,47,28,245,31,31,31,47,1,28,1,28,47,1,102, 102,102,
191,191,142,47,4,8,8,8,8,4,47,28,1,28,29,28,
29,29,31,1,47,245,47,47,28,28,31,47,28,1,31,1,
1,245,47,39,8,8,8,40,39,8,8,8,39,1,1,47,
4,8,8,8,8,4,47,29,28,31,28,28,29,28,28,28,
29,28,31,28,47,29,1,28,31,47,1,28,1,1,29,29,
29,47,28,1,28,28,245,28,28,28,28,47,29,28,47,102,102,103,
191,191,142,31,29,36,8,8,36,31,40,39,40,4,1,1,
39,40,39,40,40,31,28,40,40,4,39,40,28,47,31,40,
39,40,4,1,36,8,8,4,47,36,8,8,39,1,1,1,
29,36,8,8,36,4,4,39,40,4,47,1,47,40,40,39,
39,40,28,40,40,47,45,39,40,28,4,39,40,4,39,1,
28,4,40,28,28,4,39,28,47,40,40,39,40,39,28,28,1,103,
1,142,29,142,28,39,8,8,36,36,8,8,8,8,36,1,
8,8,8,8,8,36,39,8,8,8,8,8,36,40,36,8,
8,8,8,36,40,8,8,40,1,4,8,8,40,1,1,31,
28,39,8,8,36,8,8,8,8,8,36,31,36,8,8,8,
8,8,36,8,8,4,40,8,8,36,8,8,8,8,8,36,
40,8,8,40,39,8,8,40,36,8,8,8,8,8,39,29,28,29,
103,191,142,47,28,40,8,8,40,8,8,33,33,8,8,36,
8,8,36,36,8,8,36,8,8,36,36,8,8,36,8,8,
33,33,8,8,36,8,8,4,47,40,8,8,39,47,28,245,
28,40,8,8,40,40,36,36,33,8,8,36,8,8,36,36,
8,8,36,8,8,40,40,8,8,40,4,36,36,33,8,8,
36,8,8,39,39,8,8,36,8,8,33,36,36,39,28,1,47,28,
103,246,1,47,1,39,8,8,40,8,8,8,8,8,8,36,
8,8,4,40,8,8,36,8,8,40,4,8,8,36,8,8,
8,8,8,8,36,8,8,40,29,39,8,8,39,1,1,47,
1,39,8,8,40,36,8,8,8,8,8,36,8,8,4,40,
8,8,36,8,8,40,39,8,8,40,36,8,8,8,8,8,
36,8,8,39,40,8,8,40,36,8,8,8,8,36,28,1,1,29,
103,47,40,40,4,36,8,8,36,8,8,33,36,36,36,4,
8,8,39,4,8,8,36,8,8,4,40,8,8,36,8,8,
33,36,36,36,36,8,8,40,31,40,8,8,40,47,40,40,
4,36,8,8,36,8,8,33,33,8,8,36,8,8,36,36,
8,8,36,8,8,36,36,8,8,36,8,8,33,33,8,8,
36,8,8,36,36,8,8,4,39,36,36,33,8,8,4,40,4,31,
191,40,8,8,8,8,8,36,29,36,8,8,8,8,8,40,
8,8,40,4,8,8,36,8,8,40,39,8,8,39,36,8,
8,8,8,8,39,8,8,39,45,4,8,8,40,40,8,8,
8,8,8,36,29,36,8,8,8,8,8,40,36,8,8,8,
8,8,40,36,8,8,8,8,8,40,36,8,8,8,8,8,
40,36,8,8,8,8,8,36,8,8,8,8,8,36,4,8,8,4,
47,45,40,39,40,39,39,245,246,1,40,40,40,39,4,47,
40,4,28,29,39,40,30,39,39,1,28,40,4,28,1,40,
40,40,39,4,29,40,39,1,1,1,4,4,47,45,40,39,
40,39,39,245,246,29,39,40,40,40,4,47,28,39,39,36,
8,8,4,1,39,40,4,40,40,1,29,4,39,4,40,39,
1,39,36,36,33,8,8,4,39,4,39,4,40,47,36,8,8,40,
1,28,47,28,28,29,1,28,47,28,31,28,28,27,47,28,
45,246,30,28,245,29,47,47,29,30,28,47,27,1,246,47,
47,47,1,28,47,28,47,1,47,47,1,29,29,47,47,28,
28,29,1,47,1,47,47,28,31,47,47,31,47,47,47,4,
8,8,39,245,1,47,28,245,28,47,31,28,47,28,28,28,
40,8,8,8,8,8,36,47,28,1,246,47,1,40,8,8,36,1,
47,1,102,1,102,102,47,94,94,102,47,47,102,102,102,102,
94,1,94,47,102,1,102,47,30,30,102,27,47,102,94,1,
102,47,1,94,102,103,1,102,103,103,47,47,47,29,1,29,
28,28,29,28,1,47,28,31,29,1,47,29,28,1,1,47,
4,39,1,47,47,1,28,28,28,47,1,28,45,28,47,47,
1,40,4,4,40,4,29,28,31,45,47,28,47,47,4,40,28,28
};
static void
fixQuitScreen(byte* px)
{
// overwrite 11 lines, 98 pixels each, from quitscreenfix[]
// starting at line 140, column 188
// quitscreen is 320x240 px
int r, qsIdx = 0;
px += 140*320; // go to line 140
px += 188; // to colum 188
for(r=0; r<11; ++r)
{
memcpy(px, quitscreenfix+qsIdx, 98);
qsIdx += 98;
px += 320;
}
}
void
LoadPCX(const char *origname, byte **pic, byte **palette, int *width, int *height)
{
byte *raw;
pcx_t *pcx;
int x, y;
int len, full_size;
int pcx_width, pcx_height;
qboolean image_issues = false;
int dataByte, runLength;
byte *out, *pix;
char filename[256];
FixFileExt(origname, "pcx", filename, sizeof(filename));
*pic = NULL;
if (palette)
{
*palette = NULL;
}
/* load the file */
len = ri.FS_LoadFile(filename, (void **)&raw);
if (!raw || len < sizeof(pcx_t))
{
R_Printf(PRINT_DEVELOPER, "Bad pcx file %s\n", filename);
return;
}
/* parse the PCX file */
pcx = (pcx_t *)raw;
pcx->xmin = LittleShort(pcx->xmin);
pcx->ymin = LittleShort(pcx->ymin);
pcx->xmax = LittleShort(pcx->xmax);
pcx->ymax = LittleShort(pcx->ymax);
pcx->hres = LittleShort(pcx->hres);
pcx->vres = LittleShort(pcx->vres);
pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
pcx->palette_type = LittleShort(pcx->palette_type);
raw = &pcx->data;
pcx_width = pcx->xmax - pcx->xmin;
pcx_height = pcx->ymax - pcx->ymin;
if ((pcx->manufacturer != 0x0a) || (pcx->version != 5) ||
(pcx->encoding != 1) || (pcx->bits_per_pixel != 8) ||
(pcx_width >= 4096) || (pcx_height >= 4096))
{
R_Printf(PRINT_ALL, "Bad pcx file %s\n", filename);
ri.FS_FreeFile(pcx);
return;
}
full_size = (pcx_height + 1) * (pcx_width + 1);
out = malloc(full_size);
if (!out)
{
R_Printf(PRINT_ALL, "Can't allocate\n");
ri.FS_FreeFile(pcx);
return;
}
*pic = out;
pix = out;
if (palette)
{
*palette = malloc(768);
if (!(*palette))
{
R_Printf(PRINT_ALL, "Can't allocate\n");
free(out);
ri.FS_FreeFile(pcx);
return;
}
if (len > 768)
{
memcpy(*palette, (byte *)pcx + len - 768, 768);
}
else
{
image_issues = true;
}
}
if (width)
{
*width = pcx_width + 1;
}
if (height)
{
*height = pcx_height + 1;
}
for (y = 0; y <= pcx_height; y++, pix += pcx_width + 1)
{
for (x = 0; x <= pcx_width; )
{
if (raw - (byte *)pcx > len)
{
// no place for read
image_issues = true;
x = pcx_width;
break;
}
dataByte = *raw++;
if ((dataByte & 0xC0) == 0xC0)
{
runLength = dataByte & 0x3F;
if (raw - (byte *)pcx > len)
{
// no place for read
image_issues = true;
x = pcx_width;
break;
}
dataByte = *raw++;
}
else
{
runLength = 1;
}
while (runLength-- > 0)
{
if ((*pic + full_size) <= (pix + x))
{
// no place for write
image_issues = true;
x += runLength;
runLength = 0;
}
else
{
pix[x++] = dataByte;
}
}
}
}
if (raw - (byte *)pcx > len)
{
R_Printf(PRINT_DEVELOPER, "PCX file %s was malformed", filename);
free(*pic);
*pic = NULL;
}
else if(pcx_width == 319 && pcx_height == 239
&& Q_strcasecmp(filename, "pics/quit.pcx") == 0
&& Com_BlockChecksum(pcx, len) == 3329419434u)
{
// it's the quit screen, and the baseq2 one (identified by checksum)
// so fix it
fixQuitScreen(*pic);
}
if (image_issues)
{
R_Printf(PRINT_ALL, "PCX file %s has possible size issues.\n", filename);
}
ri.FS_FreeFile(pcx);
}
void
GetPCXInfo(const char *origname, int *width, int *height)
{
pcx_t *pcx;
byte *raw;
char filename[256];
FixFileExt(origname, "pcx", filename, sizeof(filename));
ri.FS_LoadFile(filename, (void **)&raw);
if (!raw)
{
return;
}
pcx = (pcx_t *)raw;
*width = pcx->xmax + 1;
*height = pcx->ymax + 1;
ri.FS_FreeFile(raw);
return;
}
/*
===============
GetPCXPalette
===============
*/
void
GetPCXPalette (byte **colormap, unsigned *d_8to24table)
{
byte *pal;
int i;
/* get the palette and colormap */
LoadPCX ("pics/colormap.pcx", colormap, &pal, NULL, NULL);
if (!*colormap || !pal)
{
ri.Sys_Error (ERR_FATAL, "%s: Couldn't load pics/colormap.pcx",
__func__);
}
for (i=0 ; i<256 ; i++)
{
unsigned v;
int r, g, b;
r = pal[i*3+0];
g = pal[i*3+1];
b = pal[i*3+2];
v = (255U<<24) + (r<<0) + (g<<8) + (b<<16);
d_8to24table[i] = LittleLong(v);
}
d_8to24table[255] &= LittleLong(0xffffff); // 255 is transparent
free (pal);
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* The PVS Decompress
*
* =======================================================================
*/
#include "../ref_shared.h"
/*
===================
Mod_DecompressVis
===================
*/
const byte *
Mod_DecompressVis(const byte *in, int row)
{
YQ2_ALIGNAS_TYPE(int) static byte decompressed[MAX_MAP_LEAFS / 8];
int c;
byte *out;
out = decompressed;
if (!in)
{
/* no vis info, so make all visible */
while (row)
{
*out++ = 0xff;
row--;
}
return decompressed;
}
do
{
if (*in)
{
*out++ = *in++;
continue;
}
c = in[1];
in += 2;
while (c)
{
*out++ = 0;
c--;
}
}
while (out - decompressed < row);
return decompressed;
}
float
Mod_RadiusFromBounds(const vec3_t mins, const vec3_t maxs)
{
int i;
vec3_t corner;
for (i = 0; i < 3; i++)
{
corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
}
return VectorLength(corner);
}

View file

@ -0,0 +1,662 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2015 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* File formats supported by stb_image, for now only tga, png, jpg
* See also https://github.com/nothings/stb
*
* =======================================================================
*/
#include <stdlib.h>
#include "../ref_shared.h"
// don't need HDR stuff
#define STBI_NO_LINEAR
#define STBI_NO_HDR
// make sure STB_image uses standard malloc(), as we'll use standard free() to deallocate
#define STBI_MALLOC(sz) malloc(sz)
#define STBI_REALLOC(p,sz) realloc(p,sz)
#define STBI_FREE(p) free(p)
// Switch of the thread local stuff. Breaks mingw under Windows.
#define STBI_NO_THREAD_LOCALS
// include implementation part of stb_image into this file
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// include resize implementation
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"
/*
* Add extension to file name
*/
void
FixFileExt(const char *origname, const char *ext, char *filename, size_t size)
{
Q_strlcpy(filename, origname, size);
/* Add the extension */
if (strcmp(COM_FileExtension(filename), ext))
{
Q_strlcat(filename, ".", size);
Q_strlcat(filename, ext, size);
}
}
/*
* origname: the filename to be opened, might be without extension
* type: extension of the type we wanna open ("jpg", "png" or "tga")
* pic: pointer RGBA pixel data will be assigned to
*/
static qboolean
LoadSTB(const char *origname, const char* type, byte **pic, int *width, int *height)
{
char filename[256];
FixFileExt(origname, type, filename, sizeof(filename));
*pic = NULL;
byte* rawdata = NULL;
int rawsize = ri.FS_LoadFile(filename, (void **)&rawdata);
if (rawdata == NULL)
{
return false;
}
int w, h, bytesPerPixel;
byte* data = NULL;
data = stbi_load_from_memory(rawdata, rawsize, &w, &h, &bytesPerPixel, STBI_rgb_alpha);
if (data == NULL)
{
R_Printf(PRINT_ALL, "%s couldn't load data from %s: %s!\n", __func__, filename, stbi_failure_reason());
ri.FS_FreeFile(rawdata);
return false;
}
ri.FS_FreeFile(rawdata);
R_Printf(PRINT_DEVELOPER, "%s() loaded: %s\n", __func__, filename);
*pic = data;
*width = w;
*height = h;
return true;
}
qboolean
ResizeSTB(const byte *input_pixels, int input_width, int input_height,
byte *output_pixels, int output_width, int output_height)
{
if (stbir_resize_uint8(input_pixels, input_width, input_height, 0,
output_pixels, output_width, output_height, 0, 4))
return true;
return false;
}
// We have 16 color palette, 256 / 16 should be enough
#define COLOR_DISTANCE 16
void
SmoothColorImage(unsigned *dst, size_t size, size_t rstep)
{
unsigned *full_size;
unsigned last_color;
unsigned *last_diff;
// maximum step for apply
if (rstep < 2)
{
return;
}
// step one pixel back as with check one pixel more
full_size = dst + size - rstep - 1;
last_diff = dst;
last_color = *dst;
// skip current point
dst ++;
while (dst < full_size)
{
if (last_color != *dst)
{
int step = dst - last_diff;
if (step > 1)
{
int a_beg, b_beg, c_beg, d_beg;
int a_end, b_end, c_end, d_end;
int a_step, b_step, c_step, d_step;
int k;
// minimize effect size to rstep
if (step > rstep)
{
// change place for start effect
last_diff += (step - rstep);
step = rstep;
}
// compare next pixels
for(k = 1; k <= step; k ++)
{
if (dst[k] != dst[0])
{
break;
}
}
// step back as pixel different after previous step
k --;
// mirror steps
if (k < step)
{
// R_Printf(PRINT_ALL, "%s %d -> %d\n", __func__, k, step);
// change place for start effect
last_diff += (step - k);
step = k;
}
// update step to correct value
step += k;
dst += k;
// get colors
a_beg = (last_color >> 0 ) & 0xff;
b_beg = (last_color >> 8 ) & 0xff;
c_beg = (last_color >> 16) & 0xff;
d_beg = (last_color >> 24) & 0xff;
a_end = (*dst >> 0 ) & 0xff;
b_end = (*dst >> 8 ) & 0xff;
c_end = (*dst >> 16) & 0xff;
d_end = (*dst >> 24) & 0xff;
a_step = a_end - a_beg;
b_step = b_end - b_beg;
c_step = c_end - c_beg;
d_step = d_end - d_beg;
if ((abs(a_step) <= COLOR_DISTANCE) &&
(abs(b_step) <= COLOR_DISTANCE) &&
(abs(c_step) <= COLOR_DISTANCE) &&
(abs(d_step) <= COLOR_DISTANCE) &&
step > 0)
{
// generate color change steps
a_step = (a_step << 16) / step;
b_step = (b_step << 16) / step;
c_step = (c_step << 16) / step;
d_step = (d_step << 16) / step;
// apply color changes
for (k=0; k < step; k++)
{
*last_diff = (((a_beg + ((a_step * k) >> 16)) << 0) & 0x000000ff) |
(((b_beg + ((b_step * k) >> 16)) << 8) & 0x0000ff00) |
(((c_beg + ((c_step * k) >> 16)) << 16) & 0x00ff0000) |
(((d_beg + ((d_step * k) >> 16)) << 24) & 0xff000000);
last_diff++;
}
}
}
last_color = *dst;
last_diff = dst;
}
dst ++;
}
}
/* https://en.wikipedia.org/wiki/Pixel-art_scaling_algorithms */
void
scale2x(const byte *src, byte *dst, int width, int height)
{
/*
EPX/Scale2×/AdvMAME2×
x A x
C P B -> 1 2
x D x 3 4
1=P; 2=P; 3=P; 4=P;
IF C==A AND C!=D AND A!=B => 1=A
IF A==B AND A!=C AND B!=D => 2=B
IF D==C AND D!=B AND C!=A => 3=C
IF B==D AND B!=A AND D!=C => 4=D
*/
{
const byte *in_buff = src;
byte *out_buff = dst;
byte *out_buff_full = dst + ((width * height) << 2);
while (out_buff < out_buff_full)
{
int x;
for (x = 0; x < width; x ++)
{
// copy one source byte to two destinatuion bytes
*out_buff = *in_buff;
out_buff ++;
*out_buff = *in_buff;
out_buff ++;
// next source pixel
in_buff ++;
}
// copy last line one more time
memcpy(out_buff, out_buff - (width << 1), width << 1);
out_buff += width << 1;
}
}
{
int y, h, w;
h = height - 1;
w = width - 1;
for (y = 0; y < height; y ++)
{
int x;
for (x = 0; x < width; x ++)
{
byte a, b, c, d, p;
p = src[(width * (y )) + (x )];
a = (y > 0) ? src[(width * (y - 1)) + (x )] : p;
b = (x < w) ? src[(width * (y )) + (x + 1)] : p;
c = (x > 0) ? src[(width * (y )) + (x - 1)] : p;
d = (y < h) ? src[(width * (y + 1)) + (x )] : p;
if ((c == a) && (c != d) && (a != b))
{
dst[(2 * width * ((y * 2) )) + ((x * 2) )] = a;
}
if ((a == b) && (a != c) && (b != d))
{
dst[(2 * width * ((y * 2) )) + ((x * 2) + 1)] = b;
}
if ((d == c) && (d != b) && (c != a))
{
dst[(2 * width * ((y * 2) + 1)) + ((x * 2) )] = c;
}
if ((b == d) && (b != a) && (d != c))
{
dst[(2 * width * ((y * 2) + 1)) + ((x * 2) + 1)] = d;
}
}
}
}
}
void
scale3x(const byte *src, byte *dst, int width, int height)
{
/*
Scale3×/AdvMAME3× and ScaleFX
A B C 1 2 3
D E F -> 4 5 6
G H I 7 8 9
1=E; 2=E; 3=E; 4=E; 5=E; 6=E; 7=E; 8=E; 9=E;
IF D==B AND D!=H AND B!=F => 1=D
IF (D==B AND D!=H AND B!=F AND E!=C) OR (B==F AND B!=D AND F!=H AND E!=A) => 2=B
IF B==F AND B!=D AND F!=H => 3=F
IF (H==D AND H!=F AND D!=B AND E!=A) OR (D==B AND D!=H AND B!=F AND E!=G) => 4=D
5=E
IF (B==F AND B!=D AND F!=H AND E!=I) OR (F==H AND F!=B AND H!=D AND E!=C) => 6=F
IF H==D AND H!=F AND D!=B => 7=D
IF (F==H AND F!=B AND H!=D AND E!=G) OR (H==D AND H!=F AND D!=B AND E!=I) => 8=H
IF F==H AND F!=B AND H!=D => 9=F
*/
{
const byte *in_buff = src;
byte *out_buff = dst;
byte *out_buff_full = dst + ((width * height) * 9);
while (out_buff < out_buff_full)
{
int x;
for (x = 0; x < width; x ++)
{
// copy one source byte to two destinatuion bytes
*out_buff = *in_buff;
out_buff ++;
*out_buff = *in_buff;
out_buff ++;
*out_buff = *in_buff;
out_buff ++;
// next source pixel
in_buff ++;
}
// copy last line one more time
memcpy(out_buff, out_buff - (width * 3), width * 3);
out_buff += width * 3;
// copy last line one more time
memcpy(out_buff, out_buff - (width * 3), width * 3);
out_buff += width * 3;
}
}
{
int y, z, w;
z = height - 1;
w = width - 1;
for (y = 0; y < height; y ++)
{
int x;
for (x = 0; x < width; x ++)
{
byte a, b, c, d, e, f, g, h, i;
e = src[(width * y) + x];
a = ((y > 0) && (x > 0)) ? src[(width * (y - 1)) + (x - 1)] : e;
b = ((y > 0) && (x )) ? src[(width * (y - 1)) + (x )] : e;
c = ((y > 0) && (x < w)) ? src[(width * (y - 1)) + (x + 1)] : e;
d = ( (x > 0)) ? src[(width * (y )) + (x - 1)] : e;
f = ( (x < w)) ? src[(width * (y )) + (x + 1)] : e;
g = ((y < z) && (x > 0)) ? src[(width * (y + 1)) + (x - 1)] : e;
h = ((y < z) && (x )) ? src[(width * (y + 1)) + (x )] : e;
i = ((y < z) && (x < w)) ? src[(width * (y + 1)) + (x + 1)] : e;
if ((d == b) && (d != h) && (b != f))
{
dst[(3 * width * ((y * 3) )) + ((x * 3) )] = d;
}
if (((d == b) && (d != h) && (b != f) && (e != c)) ||
((b == f) && (b != d) && (f != h) && (e != a)))
{
dst[(3 * width * ((y * 3) )) + ((x * 3) + 1)] = b;
}
if ((b == f) && (b != d) && (f != h))
{
dst[(3 * width * ((y * 3) )) + ((x * 3) + 2)] = f;
}
if (((h == d) && (h != f) && (d != b) && (e != a)) ||
((d == b) && (d != h) && (b != f) && (e != g)))
{
dst[(3 * width * ((y * 3) + 1)) + ((x * 3) )] = d;
}
if (((b == f) && (b != d) && (f != h) && (e != i)) ||
((f == h) && (f != b) && (h != d) && (e != c)))
{
dst[(3 * width * ((y * 3) + 1)) + ((x * 3) + 2)] = f;
}
if ((h == d) && (h != f) && (d != b))
{
dst[(3 * width * ((y * 3) + 2)) + ((x * 3) )] = d;
}
if (((f == h) && (f != b) && (h != d) && (e != g)) ||
((h == d) && (h != f) && (d != b) && (e != i)))
{
dst[(3 * width * ((y * 3) + 2)) + ((x * 3) + 1)] = h;
}
if ((f == h) && (f != b) && (h != d))
{
dst[(3 * width * ((y * 3) + 2)) + ((x * 3) + 2)] = f;
}
}
}
}
}
static struct image_s *
LoadHiColorImage(const char *name, const char* namewe, const char *ext,
imagetype_t type, loadimage_t load_image)
{
int realwidth = 0, realheight = 0;
int width = 0, height = 0;
struct image_s *image = NULL;
byte *pic = NULL;
/* Get size of the original texture */
if (strcmp(ext, "pcx") == 0)
{
GetPCXInfo(name, &realwidth, &realheight);
}
else if (strcmp(ext, "wal") == 0)
{
GetWalInfo(name, &realwidth, &realheight);
}
else if (strcmp(ext, "m8") == 0)
{
GetM8Info(name, &realwidth, &realheight);
}
else if (strcmp(ext, "m32") == 0)
{
GetM32Info(name, &realwidth, &realheight);
}
/* try to load a tga, png or jpg (in that order/priority) */
if ( LoadSTB(namewe, "tga", &pic, &width, &height)
|| LoadSTB(namewe, "png", &pic, &width, &height)
|| LoadSTB(namewe, "jpg", &pic, &width, &height) )
{
if (width >= realwidth && height >= realheight)
{
if (realheight == 0 || realwidth == 0)
{
realheight = height;
realwidth = width;
}
image = load_image(name, pic,
width, realwidth,
height, realheight,
width * height,
type, 32);
}
}
if (pic)
{
free(pic);
}
return image;
}
struct image_s *
R_LoadImage(const char *name, const char* namewe, const char *ext, imagetype_t type,
qboolean r_retexturing, loadimage_t load_image)
{
struct image_s *image = NULL;
// with retexturing and not skin
if (r_retexturing)
{
image = LoadHiColorImage(name, namewe, ext, type, load_image);
}
if (!image)
{
if (!strcmp(ext, "pcx"))
{
byte *pic = NULL;
byte *palette = NULL;
int width = 0, height = 0;
LoadPCX (namewe, &pic, &palette, &width, &height);
if (!pic)
return NULL;
image = load_image(name, pic,
width, width,
height, height,
width * height, type, 8);
if (palette)
{
free(palette);
}
free(pic);
}
else if (!strcmp(ext, "wal"))
{
image = LoadWal(namewe, type, load_image);
}
else if (!strcmp(ext, "m8"))
{
image = LoadM8(namewe, type, load_image);
}
else if (!strcmp(ext, "m32"))
{
image = LoadM32(namewe, type, load_image);
}
else if (!strcmp(ext, "tga") ||
!strcmp(ext, "png") ||
!strcmp(ext, "jpg"))
{
byte *pic = NULL;
int width = 0, height = 0;
if (LoadSTB (namewe, ext, &pic, &width, &height) && pic)
{
image = load_image(name, pic,
width, width,
height, height,
width * height,
type, 32);
free(pic);
}
}
}
return image;
}
struct image_s*
GetSkyImage(const char *skyname, const char* surfname, qboolean palettedtexture,
findimage_t find_image)
{
struct image_s *image = NULL;
char pathname[MAX_QPATH];
/* Quake 2 */
if (palettedtexture)
{
Com_sprintf(pathname, sizeof(pathname), "env/%s%s.pcx",
skyname, surfname);
image = find_image(pathname, it_sky);
}
if (!image)
{
Com_sprintf(pathname, sizeof(pathname), "env/%s%s.tga",
skyname, surfname);
image = find_image(pathname, it_sky);
}
/* Heretic 2 */
if (!image)
{
Com_sprintf(pathname, sizeof(pathname), "pics/Skies/%s%s.m32",
skyname, surfname);
image = find_image(pathname, it_sky);
}
if (!image)
{
Com_sprintf(pathname, sizeof(pathname), "pics/Skies/%s%s.m8",
skyname, surfname);
image = find_image(pathname, it_sky);
}
return image;
}
struct image_s *
GetTexImage(const char *name, findimage_t find_image)
{
struct image_s *image = NULL;
char pathname[MAX_QPATH];
/* Quake 2 */
Com_sprintf(pathname, sizeof(pathname), "textures/%s.wal", name);
image = find_image(pathname, it_wall);
/* Heretic 2 */
if (!image)
{
Com_sprintf(pathname, sizeof(pathname), "textures/%s.m32", name);
image = find_image(pathname, it_wall);
}
if (!image)
{
Com_sprintf(pathname, sizeof(pathname), "textures/%s.m8", name);
image = find_image(pathname, it_wall);
}
return image;
}
struct image_s *
R_FindPic(const char *name, findimage_t find_image)
{
struct image_s *image = NULL;
if ((name[0] != '/') && (name[0] != '\\'))
{
char pathname[MAX_QPATH];
/* Quake 2 */
Com_sprintf(pathname, sizeof(pathname), "pics/%s.pcx", name);
image = find_image(pathname, it_pic);
/* Heretic 2 */
if (!image)
{
Com_sprintf(pathname, sizeof(pathname), "pics/misc/%s.m32", name);
image = find_image(pathname, it_pic);
}
if (!image)
{
Com_sprintf(pathname, sizeof(pathname), "pics/misc/%s.m8", name);
image = find_image(pathname, it_pic);
}
}
else
{
image = find_image(name + 1, it_pic);
}
return image;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,186 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Surface logic
*
* =======================================================================
*/
#include "../ref_shared.h"
/*
===============
R_TextureAnimation
Returns the proper texture for a given time and base texture
===============
*/
struct image_s *
R_TextureAnimation(const entity_t *currententity, const mtexinfo_t *tex)
{
int c;
if (!tex->next)
return tex->image;
if (!currententity)
return tex->image;
c = currententity->frame % tex->numframes;
while (c && tex)
{
tex = tex->next;
c--;
}
return tex->image;
}
qboolean
R_AreaVisible(const byte *areabits, mleaf_t *pleaf)
{
int area;
// check for door connected areas
if (!areabits)
return true;
area = pleaf->area;
if ((areabits[area >> 3] & (1 << (area & 7))))
return true;
return false; // not visible
}
/*
=============
R_MarkLights
bit: 1 << i for light number i, will be or'ed into msurface_t::dlightbits
if surface is affected by this light
=============
*/
void
R_MarkLights(dlight_t *light, int bit, mnode_t *node, int r_dlightframecount,
marksurfacelights_t mark_surface_lights)
{
cplane_t *splitplane;
float dist;
int intensity;
if (node->contents != CONTENTS_NODE)
return;
splitplane = node->plane;
dist = DotProduct(light->origin, splitplane->normal) - splitplane->dist;
intensity = light->intensity;
if (dist > intensity - DLIGHT_CUTOFF) // (dist > light->intensity)
{
R_MarkLights (light, bit, node->children[0], r_dlightframecount,
mark_surface_lights);
return;
}
if (dist < -intensity + DLIGHT_CUTOFF) // (dist < -light->intensity)
{
R_MarkLights(light, bit, node->children[1], r_dlightframecount,
mark_surface_lights);
return;
}
mark_surface_lights(light, bit, node, r_dlightframecount);
R_MarkLights(light, bit, node->children[0], r_dlightframecount,
mark_surface_lights);
R_MarkLights(light, bit, node->children[1], r_dlightframecount,
mark_surface_lights);
}
/*
* Returns true if the box is completely outside the frustom
*/
qboolean
R_CullBox(vec3_t mins, vec3_t maxs, cplane_t *frustum)
{
int i;
for (i = 0; i < 4; i++)
{
if (BOX_ON_PLANE_SIDE(mins, maxs, frustum + i) == 2)
{
return true;
}
}
return false;
}
static int
R_SignbitsForPlane(cplane_t *out)
{
int bits, j;
/* for fast box on planeside test */
bits = 0;
for (j = 0; j < 3; j++)
{
if (out->normal[j] < 0)
{
bits |= 1 << j;
}
}
return bits;
}
void
R_SetFrustum(vec3_t vup, vec3_t vpn, vec3_t vright, vec3_t r_origin,
float fov_x, float fov_y, cplane_t *frustum)
{
int i;
/* rotate VPN right by FOV_X/2 degrees */
RotatePointAroundVector(frustum[0].normal, vup, vpn,
-(90 - fov_x / 2));
/* rotate VPN left by FOV_X/2 degrees */
RotatePointAroundVector(frustum[1].normal,
vup, vpn, 90 - fov_x / 2);
/* rotate VPN up by FOV_X/2 degrees */
RotatePointAroundVector(frustum[2].normal,
vright, vpn, 90 - fov_y / 2);
/* rotate VPN down by FOV_X/2 degrees */
RotatePointAroundVector(frustum[3].normal, vright, vpn,
-(90 - fov_y / 2));
#if defined(__GNUC__)
# pragma GCC unroll 4
#endif
for (i = 0; i < 4; i++)
{
frustum[i].type = PLANE_ANYZ;
frustum[i].dist = DotProduct(r_origin, frustum[i].normal);
frustum[i].signbits = R_SignbitsForPlane(&frustum[i]);
}
}

View file

@ -0,0 +1,280 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* The Wal image format
*
* =======================================================================
*/
#include "../ref_shared.h"
struct image_s *
LoadWal(const char *origname, imagetype_t type, loadimage_t load_image)
{
int width, height, ofs, size;
struct image_s *image;
char name[256];
miptex_t *mt;
FixFileExt(origname, "wal", name, sizeof(name));
size = ri.FS_LoadFile(name, (void **)&mt);
if (!mt)
{
return NULL;
}
if (size < sizeof(miptex_t))
{
R_Printf(PRINT_ALL, "%s: can't load %s, small header\n", __func__, name);
ri.FS_FreeFile((void *)mt);
return NULL;
}
width = LittleLong(mt->width);
height = LittleLong(mt->height);
ofs = LittleLong(mt->offsets[0]);
if ((ofs <= 0) || (width <= 0) || (height <= 0) ||
(((size - ofs) / height) < width))
{
R_Printf(PRINT_ALL, "%s: can't load %s, small body\n", __func__, name);
ri.FS_FreeFile((void *)mt);
return NULL;
}
image = load_image(name, (byte *)mt + ofs,
width, 0,
height, 0,
(size - ofs), type, 8);
ri.FS_FreeFile((void *)mt);
return image;
}
struct image_s *
LoadM8(const char *origname, imagetype_t type, loadimage_t load_image)
{
m8tex_t *mt;
int width, height, ofs, size, i;
struct image_s *image;
char name[256];
unsigned char *image_buffer = NULL;
FixFileExt(origname, "m8", name, sizeof(name));
size = ri.FS_LoadFile(name, (void **)&mt);
if (!mt)
{
return NULL;
}
if (size < sizeof(m8tex_t))
{
R_Printf(PRINT_ALL, "%s: can't load %s, small header\n", __func__, name);
ri.FS_FreeFile((void *)mt);
return NULL;
}
if (LittleLong (mt->version) != M8_VERSION)
{
R_Printf(PRINT_ALL, "%s: can't load %s, wrong magic value.\n", __func__, name);
ri.FS_FreeFile ((void *)mt);
return NULL;
}
width = LittleLong(mt->width[0]);
height = LittleLong(mt->height[0]);
ofs = LittleLong(mt->offsets[0]);
if ((ofs <= 0) || (width <= 0) || (height <= 0) ||
(((size - ofs) / height) < width))
{
R_Printf(PRINT_ALL, "%s: can't load %s, small body\n", __func__, name);
ri.FS_FreeFile((void *)mt);
return NULL;
}
image_buffer = malloc ((size - ofs) * 4);
for(i=0; i<(size - ofs); i++)
{
unsigned char value = *((byte *)mt + ofs + i);
image_buffer[i * 4 + 0] = mt->palette[value].r;
image_buffer[i * 4 + 1] = mt->palette[value].g;
image_buffer[i * 4 + 2] = mt->palette[value].b;
image_buffer[i * 4 + 3] = value == 255 ? 0 : 255;
}
image = load_image(name, image_buffer,
width, 0,
height, 0,
(size - ofs), type, 32);
free(image_buffer);
ri.FS_FreeFile((void *)mt);
return image;
}
struct image_s *
LoadM32(const char *origname, imagetype_t type, loadimage_t load_image)
{
m32tex_t *mt;
int width, height, ofs, size;
struct image_s *image;
char name[256];
FixFileExt(origname, "m32", name, sizeof(name));
size = ri.FS_LoadFile(name, (void **)&mt);
if (!mt)
{
return NULL;
}
if (size < sizeof(m32tex_t))
{
R_Printf(PRINT_ALL, "%s: can't load %s, small header\n", __func__, name);
ri.FS_FreeFile((void *)mt);
return NULL;
}
if (LittleLong (mt->version) != M32_VERSION)
{
R_Printf(PRINT_ALL, "%s: can't load %s, wrong magic value.\n", __func__, name);
ri.FS_FreeFile ((void *)mt);
return NULL;
}
width = LittleLong (mt->width[0]);
height = LittleLong (mt->height[0]);
ofs = LittleLong (mt->offsets[0]);
if ((ofs <= 0) || (width <= 0) || (height <= 0) ||
(((size - ofs) / height) < (width * 4)))
{
R_Printf(PRINT_ALL, "%s: can't load %s, small body\n", __func__, name);
ri.FS_FreeFile((void *)mt);
return NULL;
}
image = load_image(name, (byte *)mt + ofs,
width, 0,
height, 0,
(size - ofs) / 4, type, 32);
ri.FS_FreeFile ((void *)mt);
return image;
}
void
GetWalInfo(const char *origname, int *width, int *height)
{
miptex_t *mt;
int size;
char filename[256];
FixFileExt(origname, "wal", filename, sizeof(filename));
size = ri.FS_LoadFile(filename, (void **)&mt);
if (!mt)
{
return;
}
if (size < sizeof(miptex_t))
{
ri.FS_FreeFile((void *)mt);
return;
}
*width = LittleLong(mt->width);
*height = LittleLong(mt->height);
ri.FS_FreeFile((void *)mt);
return;
}
void
GetM8Info(const char *origname, int *width, int *height)
{
m8tex_t *mt;
int size;
char filename[256];
FixFileExt(origname, "m8", filename, sizeof(filename));
size = ri.FS_LoadFile(filename, (void **)&mt);
if (!mt)
{
return;
}
if (size < sizeof(m8tex_t) || LittleLong (mt->version) != M8_VERSION)
{
ri.FS_FreeFile((void *)mt);
return;
}
*width = LittleLong(mt->width[0]);
*height = LittleLong(mt->height[0]);
ri.FS_FreeFile((void *)mt);
return;
}
void
GetM32Info(const char *origname, int *width, int *height)
{
m32tex_t *mt;
int size;
char filename[256];
FixFileExt(origname, "m32", filename, sizeof(filename));
size = ri.FS_LoadFile(filename, (void **)&mt);
if (!mt)
{
return;
}
if (size < sizeof(m32tex_t) || LittleLong (mt->version) != M32_VERSION)
{
ri.FS_FreeFile((void *)mt);
return;
}
*width = LittleLong(mt->width[0]);
*height = LittleLong(mt->height[0]);
ri.FS_FreeFile((void *)mt);
return;
}

View file

@ -0,0 +1,413 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Drawing of all images that are not textures
*
* =======================================================================
*/
#include "header/local.h"
unsigned d_8to24table[256];
gl4image_t *draw_chars;
static GLuint vbo2D = 0, vao2D = 0, vao2Dcolor = 0; // vao2D is for textured rendering, vao2Dcolor for color-only
void
GL4_Draw_InitLocal(void)
{
/* load console characters */
draw_chars = R_FindPic("conchars", (findimage_t)GL4_FindImage);
if (!draw_chars)
{
ri.Sys_Error(ERR_FATAL, "%s: Couldn't load pics/conchars.pcx",
__func__);
}
// set up attribute layout for 2D textured rendering
glGenVertexArrays(1, &vao2D);
glBindVertexArray(vao2D);
glGenBuffers(1, &vbo2D);
GL4_BindVBO(vbo2D);
GL4_UseProgram(gl4state.si2D.shaderProgram);
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
// Note: the glVertexAttribPointer() configuration is stored in the VAO, not the shader or sth
// (that's why I use one VAO per 2D shader)
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
glEnableVertexAttribArray(GL4_ATTRIB_TEXCOORD);
qglVertexAttribPointer(GL4_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 2*sizeof(float));
// set up attribute layout for 2D flat color rendering
glGenVertexArrays(1, &vao2Dcolor);
glBindVertexArray(vao2Dcolor);
GL4_BindVBO(vbo2D); // yes, both VAOs share the same VBO
GL4_UseProgram(gl4state.si2Dcolor.shaderProgram);
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), 0);
GL4_BindVAO(0);
}
void
GL4_Draw_ShutdownLocal(void)
{
glDeleteBuffers(1, &vbo2D);
vbo2D = 0;
glDeleteVertexArrays(1, &vao2D);
vao2D = 0;
glDeleteVertexArrays(1, &vao2Dcolor);
vao2Dcolor = 0;
}
// bind the texture before calling this
static void
drawTexturedRectangle(float x, float y, float w, float h,
float sl, float tl, float sh, float th)
{
/*
* x,y+h x+w,y+h
* sl,th--------sh,th
* | |
* | |
* | |
* sl,tl--------sh,tl
* x,y x+w,y
*/
GLfloat vBuf[16] = {
// X, Y, S, T
x, y+h, sl, th,
x, y, sl, tl,
x+w, y+h, sh, th,
x+w, y, sh, tl
};
GL4_BindVAO(vao2D);
// Note: while vao2D "remembers" its vbo for drawing, binding the vao does *not*
// implicitly bind the vbo, so I need to explicitly bind it before glBufferData()
GL4_BindVBO(vbo2D);
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//glMultiDrawArrays(mode, first, count, drawcount) ??
}
/*
* Draws one 8*8 graphics character with 0 being transparent.
* It can be clipped to the top of the screen to allow the console to be
* smoothly scrolled off.
*/
void
GL4_Draw_CharScaled(int x, int y, int num, float scale)
{
int row, col;
float frow, fcol, size, scaledSize;
num &= 255;
if ((num & 127) == 32)
{
return; /* space */
}
if (y <= -8)
{
return; /* totally off screen */
}
row = num >> 4;
col = num & 15;
frow = row * 0.0625;
fcol = col * 0.0625;
size = 0.0625;
scaledSize = 8*scale;
// TODO: batchen?
GL4_UseProgram(gl4state.si2D.shaderProgram);
GL4_Bind(draw_chars->texnum);
drawTexturedRectangle(x, y, scaledSize, scaledSize, fcol, frow, fcol+size, frow+size);
}
gl4image_t *
GL4_Draw_FindPic(char *name)
{
return R_FindPic(name, (findimage_t)GL4_FindImage);
}
void
GL4_Draw_GetPicSize(int *w, int *h, char *pic)
{
gl4image_t *gl;
gl = R_FindPic(pic, (findimage_t)GL4_FindImage);
if (!gl)
{
*w = *h = -1;
return;
}
*w = gl->width;
*h = gl->height;
}
void
GL4_Draw_StretchPic(int x, int y, int w, int h, char *pic)
{
gl4image_t *gl = R_FindPic(pic, (findimage_t)GL4_FindImage);
if (!gl)
{
R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic);
return;
}
GL4_UseProgram(gl4state.si2D.shaderProgram);
GL4_Bind(gl->texnum);
drawTexturedRectangle(x, y, w, h, gl->sl, gl->tl, gl->sh, gl->th);
}
void
GL4_Draw_PicScaled(int x, int y, char *pic, float factor)
{
gl4image_t *gl = R_FindPic(pic, (findimage_t)GL4_FindImage);
if (!gl)
{
R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic);
return;
}
GL4_UseProgram(gl4state.si2D.shaderProgram);
GL4_Bind(gl->texnum);
drawTexturedRectangle(x, y, gl->width*factor, gl->height*factor, gl->sl, gl->tl, gl->sh, gl->th);
}
/*
* This repeats a 64*64 tile graphic to fill
* the screen around a sized down
* refresh window.
*/
void
GL4_Draw_TileClear(int x, int y, int w, int h, char *pic)
{
gl4image_t *image = R_FindPic(pic, (findimage_t)GL4_FindImage);
if (!image)
{
R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic);
return;
}
GL4_UseProgram(gl4state.si2D.shaderProgram);
GL4_Bind(image->texnum);
drawTexturedRectangle(x, y, w, h, x/64.0f, y/64.0f, (x+w)/64.0f, (y+h)/64.0f);
}
void
GL4_DrawFrameBufferObject(int x, int y, int w, int h, GLuint fboTexture, const float v_blend[4])
{
qboolean underwater = (gl4_newrefdef.rdflags & RDF_UNDERWATER) != 0;
gl4ShaderInfo_t* shader = underwater ? &gl4state.si2DpostProcessWater
: &gl4state.si2DpostProcess;
GL4_UseProgram(shader->shaderProgram);
GL4_Bind(fboTexture);
if(underwater && shader->uniLmScalesOrTime != -1)
{
glUniform1f(shader->uniLmScalesOrTime, gl4_newrefdef.time);
}
if(shader->uniVblend != -1)
{
glUniform4fv(shader->uniVblend, 1, v_blend);
}
drawTexturedRectangle(x, y, w, h, 0, 1, 1, 0);
}
/*
* Fills a box of pixels with a single color
*/
void
GL4_Draw_Fill(int x, int y, int w, int h, int c)
{
union
{
unsigned c;
byte v[4];
} color;
int i;
if ((unsigned)c > 255)
{
ri.Sys_Error(ERR_FATAL, "Draw_Fill: bad color");
}
color.c = d_8to24table[c];
GLfloat vBuf[8] = {
// X, Y
x, y+h,
x, y,
x+w, y+h,
x+w, y
};
for(i=0; i<3; ++i)
{
gl4state.uniCommonData.color.Elements[i] = color.v[i] * (1.0f/255.0f);
}
gl4state.uniCommonData.color.A = 1.0f;
GL4_UpdateUBOCommon();
GL4_UseProgram(gl4state.si2Dcolor.shaderProgram);
GL4_BindVAO(vao2Dcolor);
GL4_BindVBO(vbo2D);
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
// in GL1 this is called R_Flash() (which just calls R_PolyBlend())
// now implemented in 2D mode and called after SetGL2D() because
// it's pretty similar to GL4_Draw_FadeScreen()
void
GL4_Draw_Flash(const float color[4], float x, float y, float w, float h)
{
if (gl_polyblend->value == 0)
{
return;
}
int i=0;
GLfloat vBuf[8] = {
// X, Y
x, y+h,
x, y,
x+w, y+h,
x+w, y
};
glEnable(GL_BLEND);
for(i=0; i<4; ++i) gl4state.uniCommonData.color.Elements[i] = color[i];
GL4_UpdateUBOCommon();
GL4_UseProgram(gl4state.si2Dcolor.shaderProgram);
GL4_BindVAO(vao2Dcolor);
GL4_BindVBO(vbo2D);
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisable(GL_BLEND);
}
void
GL4_Draw_FadeScreen(void)
{
float color[4] = {0, 0, 0, 0.6f};
GL4_Draw_Flash(color, 0, 0, vid.width, vid.height);
}
void
GL4_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, const byte *data, int bits)
{
int i, j;
GL4_Bind(0);
unsigned image32[320*240]; /* was 256 * 256, but we want a bit more space */
unsigned* img = image32;
if (bits == 32)
{
img = (unsigned *)data;
}
else
{
if(cols*rows > 320*240)
{
/* in case there is a bigger video after all,
* malloc enough space to hold the frame */
img = (unsigned*)malloc(cols*rows*4);
}
for(i=0; i<rows; ++i)
{
int rowOffset = i*cols;
for(j=0; j<cols; ++j)
{
byte palIdx = data[rowOffset+j];
img[rowOffset+j] = gl4_rawpalette[palIdx];
}
}
}
GL4_UseProgram(gl4state.si2D.shaderProgram);
GLuint glTex;
glGenTextures(1, &glTex);
GL4_SelectTMU(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, glTex);
glTexImage2D(GL_TEXTURE_2D, 0, gl4_tex_solid_format,
cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
if(img != image32 && img != (unsigned *)data)
{
free(img);
}
// Note: gl_filter_min could be GL_*_MIPMAP_* so we can't use it for min filter here (=> no mipmaps)
// but gl_filter_max (either GL_LINEAR or GL_NEAREST) should do the trick.
GLint filter = (r_videos_unfiltered->value == 0) ? gl_filter_max : GL_NEAREST;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
drawTexturedRectangle(x, y, w, h, 0.0f, 0.0f, 1.0f, 1.0f);
glDeleteTextures(1, &glTex);
GL4_Bind(0);
}

View file

@ -0,0 +1,845 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Texture handling for OpenGL4
*
* =======================================================================
*/
#include "header/local.h"
typedef struct
{
char *name;
int minimize, maximize;
} glmode_t;
glmode_t modes[] = {
{"GL_NEAREST", GL_NEAREST, GL_NEAREST},
{"GL_LINEAR", GL_LINEAR, GL_LINEAR},
{"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
{"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
{"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
{"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
};
int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
int gl_filter_max = GL_LINEAR;
gl4image_t gl4textures[MAX_GL4TEXTURES];
int numgl4textures = 0;
static int image_max = 0;
void
GL4_TextureMode(char *string)
{
const int num_modes = sizeof(modes)/sizeof(modes[0]);
int i;
for (i = 0; i < num_modes; i++)
{
if (!Q_stricmp(modes[i].name, string))
{
break;
}
}
if (i == num_modes)
{
R_Printf(PRINT_ALL, "bad filter name '%s' (probably from gl_texturemode)\n", string);
return;
}
gl_filter_min = modes[i].minimize;
gl_filter_max = modes[i].maximize;
/* clamp selected anisotropy */
if (gl4config.anisotropic)
{
if (gl_anisotropic->value > gl4config.max_anisotropy)
{
ri.Cvar_SetValue("r_anisotropic", gl4config.max_anisotropy);
}
}
else
{
ri.Cvar_SetValue("r_anisotropic", 0.0);
}
gl4image_t *glt;
const char* nolerplist = gl_nolerp_list->string;
const char* lerplist = r_lerp_list->string;
qboolean unfiltered2D = r_2D_unfiltered->value != 0;
/* change all the existing texture objects */
for (i = 0, glt = gl4textures; i < numgl4textures; i++, glt++)
{
qboolean nolerp = false;
/* r_2D_unfiltered and gl_nolerp_list allow rendering stuff unfiltered even if gl_filter_* is filtered */
if (unfiltered2D && glt->type == it_pic)
{
// exception to that exception: stuff on the r_lerp_list
nolerp = (lerplist== NULL) || (strstr(lerplist, glt->name) == NULL);
}
else if(nolerplist != NULL && strstr(nolerplist, glt->name) != NULL)
{
nolerp = true;
}
GL4_SelectTMU(GL_TEXTURE0);
GL4_Bind(glt->texnum);
if ((glt->type != it_pic) && (glt->type != it_sky)) /* mipmapped texture */
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
/* Set anisotropic filter if supported and enabled */
if (gl4config.anisotropic && gl_anisotropic->value)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, max(gl_anisotropic->value, 1.f));
}
}
else /* texture has no mipmaps */
{
if (nolerp)
{
// this texture shouldn't be filtered at all (no gl_nolerp_list or r_2D_unfiltered case)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
else
{
// we can't use gl_filter_min which might be GL_*_MIPMAP_*
// also, there's no anisotropic filtering for textures w/o mipmaps
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
}
}
}
void
GL4_Bind(GLuint texnum)
{
extern gl4image_t *draw_chars;
if (gl_nobind->value && draw_chars) /* performance evaluation option */
{
texnum = draw_chars->texnum;
}
if (gl4state.currenttexture == texnum)
{
return;
}
gl4state.currenttexture = texnum;
GL4_SelectTMU(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texnum);
}
void
GL4_BindLightmap(int lightmapnum)
{
int i=0;
if(lightmapnum < 0 || lightmapnum >= MAX_LIGHTMAPS)
{
R_Printf(PRINT_ALL, "WARNING: Invalid lightmapnum %i used!\n", lightmapnum);
return;
}
if (gl4state.currentlightmap == lightmapnum)
{
return;
}
gl4state.currentlightmap = lightmapnum;
for(i=0; i<MAX_LIGHTMAPS_PER_SURFACE; ++i)
{
// this assumes that GL_TEXTURE<i+1> = GL_TEXTURE<i> + 1
// at least for GL_TEXTURE0 .. GL_TEXTURE31 that's true
GL4_SelectTMU(GL_TEXTURE1+i);
glBindTexture(GL_TEXTURE_2D, gl4state.lightmap_textureIDs[lightmapnum][i]);
}
}
/*
* Returns has_alpha
*/
qboolean
GL4_Upload32(unsigned *data, int width, int height, qboolean mipmap)
{
qboolean res;
int i;
int c = width * height;
byte *scan = ((byte *)data) + 3;
int comp = gl4_tex_solid_format;
int samples = gl4_solid_format;
for (i = 0; i < c; i++, scan += 4)
{
if (*scan != 255)
{
samples = gl4_alpha_format;
comp = gl4_tex_alpha_format;
break;
}
}
glTexImage2D(GL_TEXTURE_2D, 0, comp, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, data);
res = (samples == gl4_alpha_format);
if (mipmap)
{
// TODO: some hardware may require mipmapping disabled for NPOT textures!
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
else // if the texture has no mipmaps, we can't use gl_filter_min which might be GL_*_MIPMAP_*
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
if (mipmap && gl4config.anisotropic && gl_anisotropic->value)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, max(gl_anisotropic->value, 1.f));
}
return res;
}
/*
* Returns has_alpha
*/
qboolean
GL4_Upload8(byte *data, int width, int height, qboolean mipmap, qboolean is_sky)
{
int s = width * height;
unsigned *trans = malloc(s * sizeof(unsigned));
for (int i = 0; i < s; i++)
{
int p = data[i];
trans[i] = d_8to24table[p];
/* transparent, so scan around for
another color to avoid alpha fringes */
if (p == 255)
{
if ((i > width) && (data[i - width] != 255))
{
p = data[i - width];
}
else if ((i < s - width) && (data[i + width] != 255))
{
p = data[i + width];
}
else if ((i > 0) && (data[i - 1] != 255))
{
p = data[i - 1];
}
else if ((i < s - 1) && (data[i + 1] != 255))
{
p = data[i + 1];
}
else
{
p = 0;
}
/* copy rgb components */
((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0];
((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1];
((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2];
}
}
qboolean ret = GL4_Upload32(trans, width, height, mipmap);
free(trans);
return ret;
}
typedef struct
{
short x, y;
} floodfill_t;
/* must be a power of 2 */
#define FLOODFILL_FIFO_SIZE 0x1000
#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)
#define FLOODFILL_STEP(off, dx, dy) \
{ \
if (pos[off] == fillcolor) \
{ \
pos[off] = 255; \
fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
} \
else if (pos[off] != 255) \
{ \
fdc = pos[off]; \
} \
}
/*
* Fill background pixels so mipmapping doesn't have haloes
*/
static void
FloodFillSkin(byte *skin, int skinwidth, int skinheight)
{
byte fillcolor = *skin; /* assume this is the pixel to fill */
floodfill_t fifo[FLOODFILL_FIFO_SIZE];
int inpt = 0, outpt = 0;
int filledcolor = 0;
int i;
/* attempt to find opaque black */
for (i = 0; i < 256; ++i)
{
if (LittleLong(d_8to24table[i]) == (255 << 0)) /* alpha 1.0 */
{
filledcolor = i;
break;
}
}
/* can't fill to filled color or to transparent color (used as visited marker) */
if ((fillcolor == filledcolor) || (fillcolor == 255))
{
return;
}
fifo[inpt].x = 0, fifo[inpt].y = 0;
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
while (outpt != inpt)
{
int x = fifo[outpt].x, y = fifo[outpt].y;
int fdc = filledcolor;
byte *pos = &skin[x + skinwidth * y];
outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
if (x > 0)
{
FLOODFILL_STEP(-1, -1, 0);
}
if (x < skinwidth - 1)
{
FLOODFILL_STEP(1, 1, 0);
}
if (y > 0)
{
FLOODFILL_STEP(-skinwidth, 0, -1);
}
if (y < skinheight - 1)
{
FLOODFILL_STEP(skinwidth, 0, 1);
}
skin[x + skinwidth * y] = fdc;
}
}
/*
* This is also used as an entry point for the generated r_notexture
*/
gl4image_t *
GL4_LoadPic(char *name, byte *pic, int width, int realwidth,
int height, int realheight, size_t data_size,
imagetype_t type, int bits)
{
gl4image_t *image = NULL;
GLuint texNum=0;
int i;
qboolean nolerp = false;
if (r_2D_unfiltered->value && type == it_pic)
{
// if r_2D_unfiltered is true(ish), nolerp should usually be true,
// *unless* the texture is on the r_lerp_list
nolerp = (r_lerp_list->string == NULL) || (strstr(r_lerp_list->string, name) == NULL);
}
else if (gl_nolerp_list != NULL && gl_nolerp_list->string != NULL)
{
nolerp = strstr(gl_nolerp_list->string, name) != NULL;
}
/* find a free gl4image_t */
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
{
if (image->texnum == 0)
{
break;
}
}
if (i == numgl4textures)
{
if (numgl4textures == MAX_GL4TEXTURES)
{
ri.Sys_Error(ERR_DROP, "MAX_GLTEXTURES");
}
numgl4textures++;
}
image = &gl4textures[i];
if (strlen(name) >= sizeof(image->name))
{
ri.Sys_Error(ERR_DROP, "%s: \"%s\" is too long", __func__, name);
}
strcpy(image->name, name);
image->registration_sequence = registration_sequence;
image->width = width;
image->height = height;
image->type = type;
if ((type == it_skin) && (bits == 8))
{
FloodFillSkin(pic, width, height);
}
image->is_lava = (strstr(name, "lava") != NULL);
// image->scrap = false; // TODO: reintroduce scrap? would allow optimizations in 2D rendering..
glGenTextures(1, &texNum);
image->texnum = texNum;
GL4_SelectTMU(GL_TEXTURE0);
GL4_Bind(texNum);
if (bits == 8)
{
// resize 8bit images only when we forced such logic
if (r_scale8bittextures->value)
{
byte *image_converted;
int scale = 2;
// scale 3 times if lerp image
if (!nolerp && (vid.height >= 240 * 3))
scale = 3;
image_converted = malloc(width * height * scale * scale);
if (!image_converted)
return NULL;
if (scale == 3) {
scale3x(pic, image_converted, width, height);
} else {
scale2x(pic, image_converted, width, height);
}
image->has_alpha = GL4_Upload8(image_converted, width * scale, height * scale,
(image->type != it_pic && image->type != it_sky),
image->type == it_sky);
free(image_converted);
}
else
{
image->has_alpha = GL4_Upload8(pic, width, height,
(image->type != it_pic && image->type != it_sky),
image->type == it_sky);
}
}
else
{
image->has_alpha = GL4_Upload32((unsigned *)pic, width, height,
(image->type != it_pic && image->type != it_sky));
}
if (realwidth && realheight)
{
if ((realwidth <= image->width) && (realheight <= image->height))
{
image->width = realwidth;
image->height = realheight;
}
else
{
R_Printf(PRINT_DEVELOPER,
"Warning, image '%s' has hi-res replacement smaller than the original! (%d x %d) < (%d x %d)\n",
name, image->width, image->height, realwidth, realheight);
}
}
image->sl = 0;
image->sh = 1;
image->tl = 0;
image->th = 1;
if (nolerp)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
#if 0 // TODO: the scrap could allow batch rendering 2D stuff? not sure it's worth the hassle..
/* load little pics into the scrap */
if (!nolerp && (image->type == it_pic) && (bits == 8) &&
(image->width < 64) && (image->height < 64))
{
int x, y;
int i, j, k;
int texnum;
texnum = Scrap_AllocBlock(image->width, image->height, &x, &y);
if (texnum == -1)
{
goto nonscrap;
}
scrap_dirty = true;
/* copy the texels into the scrap block */
k = 0;
for (i = 0; i < image->height; i++)
{
for (j = 0; j < image->width; j++, k++)
{
scrap_texels[texnum][(y + i) * BLOCK_WIDTH + x + j] = pic[k];
}
}
image->texnum = TEXNUM_SCRAPS + texnum;
image->scrap = true;
image->has_alpha = true;
image->sl = (x + 0.01) / (float)BLOCK_WIDTH;
image->sh = (x + image->width - 0.01) / (float)BLOCK_WIDTH;
image->tl = (y + 0.01) / (float)BLOCK_WIDTH;
image->th = (y + image->height - 0.01) / (float)BLOCK_WIDTH;
}
else
{
nonscrap:
image->scrap = false;
image->texnum = TEXNUM_IMAGES + (image - gltextures);
R_Bind(image->texnum);
if (bits == 8)
{
image->has_alpha = R_Upload8(pic, width, height,
(image->type != it_pic && image->type != it_sky),
image->type == it_sky);
}
else
{
image->has_alpha = R_Upload32((unsigned *)pic, width, height,
(image->type != it_pic && image->type != it_sky));
}
image->upload_width = upload_width; /* after power of 2 and scales */
image->upload_height = upload_height;
image->paletted = uploaded_paletted;
if (realwidth && realheight)
{
if ((realwidth <= image->width) && (realheight <= image->height))
{
image->width = realwidth;
image->height = realheight;
}
else
{
R_Printf(PRINT_DEVELOPER,
"Warning, image '%s' has hi-res replacement smaller than the original! (%d x %d) < (%d x %d)\n",
name, image->width, image->height, realwidth, realheight);
}
}
image->sl = 0;
image->sh = 1;
image->tl = 0;
image->th = 1;
if (nolerp)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
}
#endif // 0
return image;
}
/*
* Finds or loads the given image or NULL
*/
gl4image_t *
GL4_FindImage(const char *name, imagetype_t type)
{
gl4image_t *image;
int i, len;
char *ptr;
char namewe[256];
const char* ext;
if (!name)
{
return NULL;
}
ext = COM_FileExtension(name);
if(!ext[0])
{
/* file has no extension */
return NULL;
}
len = strlen(name);
/* Remove the extension */
memset(namewe, 0, 256);
memcpy(namewe, name, len - (strlen(ext) + 1));
if (len < 5)
{
return NULL;
}
/* fix backslashes */
while ((ptr = strchr(name, '\\')))
{
*ptr = '/';
}
/* look for it */
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
{
if (!strcmp(name, image->name))
{
image->registration_sequence = registration_sequence;
return image;
}
}
//
// load the pic from disk
//
image = (gl4image_t *)R_LoadImage(name, namewe, ext, type,
r_retexturing->value, (loadimage_t)GL4_LoadPic);
if (!image && r_validation->value)
{
R_Printf(PRINT_ALL, "%s: can't load %s\n", __func__, name);
}
return image;
}
gl4image_t *
GL4_RegisterSkin(char *name)
{
return GL4_FindImage(name, it_skin);
}
/*
* Any image that was not touched on
* this registration sequence
* will be freed.
*/
void
GL4_FreeUnusedImages(void)
{
int i;
gl4image_t *image;
/* never free r_notexture or particle texture */
gl4_notexture->registration_sequence = registration_sequence;
gl4_particletexture->registration_sequence = registration_sequence;
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
{
if (image->registration_sequence == registration_sequence)
{
continue; /* used this sequence */
}
if (!image->registration_sequence)
{
continue; /* free image_t slot */
}
if (image->type == it_pic)
{
continue; /* don't free pics */
}
/* free it */
glDeleteTextures(1, &image->texnum);
memset(image, 0, sizeof(*image));
}
}
qboolean
GL4_ImageHasFreeSpace(void)
{
int i, used;
gl4image_t *image;
used = 0;
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
{
if (!image->name[0])
continue;
if (image->registration_sequence == registration_sequence)
{
used ++;
}
}
if (image_max < used)
{
image_max = used;
}
// should same size of free slots as currently used
return (numgl4textures + used) < MAX_GL4TEXTURES;
}
void
GL4_ShutdownImages(void)
{
int i;
gl4image_t *image;
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
{
if (!image->registration_sequence)
{
continue; /* free image_t slot */
}
/* free it */
glDeleteTextures(1, &image->texnum);
memset(image, 0, sizeof(*image));
}
}
static qboolean IsNPOT(int v)
{
unsigned int uv = v;
// just try all the power of two values between 1 and 1 << 15 (32k)
for(unsigned int i=0; i<16; ++i)
{
unsigned int pot = (1u << i);
if(uv & pot)
{
return uv != pot;
}
}
return true;
}
void
GL4_ImageList_f(void)
{
int i, used, texels;
qboolean freeup;
gl4image_t *image;
const char *formatstrings[2] = {
"RGB ",
"RGBA"
};
const char* potstrings[2] = {
" POT", "NPOT"
};
R_Printf(PRINT_ALL, "------------------\n");
texels = 0;
used = 0;
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
{
int w, h;
char *in_use = "";
qboolean isNPOT = false;
if (image->texnum == 0)
{
continue;
}
if (image->registration_sequence == registration_sequence)
{
in_use = "*";
used++;
}
w = image->width;
h = image->height;
isNPOT = IsNPOT(w) || IsNPOT(h);
texels += w*h;
char imageType = '?';
switch (image->type)
{
case it_skin:
imageType = 'M';
break;
case it_sprite:
imageType = 'S';
break;
case it_wall:
imageType = 'W';
break;
case it_pic:
imageType = 'P';
break;
case it_sky:
imageType = 'Y';
break;
default:
imageType = '?';
break;
}
char isLava = image->is_lava ? 'L' : ' ';
R_Printf(PRINT_ALL, "%c%c %3i %3i %s %s: %s %s\n", imageType, isLava, w, h,
formatstrings[image->has_alpha], potstrings[isNPOT], image->name, in_use);
}
R_Printf(PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels);
freeup = GL4_ImageHasFreeSpace();
R_Printf(PRINT_ALL, "Used %d of %d images%s.\n", used, image_max, freeup ? ", has free space" : "");
}

View file

@ -0,0 +1,403 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Lightmaps and dynamic lighting
*
* =======================================================================
*/
#include "header/local.h"
extern gl4lightmapstate_t gl4_lms;
int r_dlightframecount;
static vec3_t pointcolor;
static cplane_t *lightplane; /* used as shadow plane */
vec3_t lightspot;
void
GL4_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node, int r_dlightframecount)
{
msurface_t *surf;
int i;
/* mark the polygons */
surf = gl4_worldmodel->surfaces + node->firstsurface;
for (i = 0; i < node->numsurfaces; i++, surf++)
{
int sidebit;
float dist;
if (surf->dlightframe != r_dlightframecount)
{
surf->dlightbits = 0;
surf->dlightframe = r_dlightframecount;
}
dist = DotProduct(light->origin, surf->plane->normal) - surf->plane->dist;
if (dist >= 0)
{
sidebit = 0;
}
else
{
sidebit = SURF_PLANEBACK;
}
if ((surf->flags & SURF_PLANEBACK) != sidebit)
{
continue;
}
surf->dlightbits |= bit;
}
}
void
GL4_PushDlights(void)
{
int i;
dlight_t *l;
/* because the count hasn't advanced yet for this frame */
r_dlightframecount = gl4_framecount + 1;
l = gl4_newrefdef.dlights;
gl4state.uniLightsData.numDynLights = gl4_newrefdef.num_dlights;
for (i = 0; i < gl4_newrefdef.num_dlights; i++, l++)
{
gl4UniDynLight* udl = &gl4state.uniLightsData.dynLights[i];
R_MarkLights(l, 1 << i, gl4_worldmodel->nodes, r_dlightframecount, GL4_MarkSurfaceLights);
VectorCopy(l->origin, udl->origin);
VectorCopy(l->color, udl->color);
udl->intensity = l->intensity;
}
assert(MAX_DLIGHTS == 32 && "If MAX_DLIGHTS changes, remember to adjust the uniform buffer definition in the shader!");
if(i < MAX_DLIGHTS)
{
memset(&gl4state.uniLightsData.dynLights[i], 0, (MAX_DLIGHTS-i)*sizeof(gl4state.uniLightsData.dynLights[0]));
}
GL4_UpdateUBOLights();
}
static int
RecursiveLightPoint(mnode_t *node, vec3_t start, vec3_t end)
{
float front, back, frac;
int side;
cplane_t *plane;
vec3_t mid;
msurface_t *surf;
int s, t, ds, dt;
int i;
mtexinfo_t *tex;
byte *lightmap;
int maps;
int r;
if (node->contents != CONTENTS_NODE)
{
return -1; /* didn't hit anything */
}
/* calculate mid point */
plane = node->plane;
front = DotProduct(start, plane->normal) - plane->dist;
back = DotProduct(end, plane->normal) - plane->dist;
side = front < 0;
if ((back < 0) == side)
{
return RecursiveLightPoint(node->children[side], start, end);
}
frac = front / (front - back);
mid[0] = start[0] + (end[0] - start[0]) * frac;
mid[1] = start[1] + (end[1] - start[1]) * frac;
mid[2] = start[2] + (end[2] - start[2]) * frac;
/* go down front side */
r = RecursiveLightPoint(node->children[side], start, mid);
if (r >= 0)
{
return r; /* hit something */
}
if ((back < 0) == side)
{
return -1; /* didn't hit anuthing */
}
/* check for impact on this node */
VectorCopy(mid, lightspot);
lightplane = plane;
surf = gl4_worldmodel->surfaces + node->firstsurface;
for (i = 0; i < node->numsurfaces; i++, surf++)
{
if (surf->flags & (SURF_DRAWTURB | SURF_DRAWSKY))
{
continue; /* no lightmaps */
}
tex = surf->texinfo;
s = DotProduct(mid, tex->vecs[0]) + tex->vecs[0][3];
t = DotProduct(mid, tex->vecs[1]) + tex->vecs[1][3];
if ((s < surf->texturemins[0]) ||
(t < surf->texturemins[1]))
{
continue;
}
ds = s - surf->texturemins[0];
dt = t - surf->texturemins[1];
if ((ds > surf->extents[0]) || (dt > surf->extents[1]))
{
continue;
}
if (!surf->samples)
{
return 0;
}
ds >>= 4;
dt >>= 4;
lightmap = surf->samples;
VectorCopy(vec3_origin, pointcolor);
lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds);
for (maps = 0; maps < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[maps] != 255;
maps++)
{
const float *rgb;
int j;
rgb = gl4_newrefdef.lightstyles[surf->styles[maps]].rgb;
/* Apply light level to models */
for (j = 0; j < 3; j++)
{
float scale;
scale = rgb[j] * r_modulate->value;
pointcolor[j] += lightmap[j] * scale * (1.0 / 255);
}
lightmap += 3 * ((surf->extents[0] >> 4) + 1) *
((surf->extents[1] >> 4) + 1);
}
return 1;
}
/* go down back side */
return RecursiveLightPoint(node->children[!side], mid, end);
}
void
GL4_LightPoint(entity_t *currententity, vec3_t p, vec3_t color)
{
vec3_t end;
float r;
int lnum;
dlight_t *dl;
vec3_t dist;
float add;
if (!gl4_worldmodel->lightdata || !currententity)
{
color[0] = color[1] = color[2] = 1.0;
return;
}
end[0] = p[0];
end[1] = p[1];
end[2] = p[2] - 2048;
// TODO: don't just aggregate the color, but also save position of brightest+nearest light
// for shadow position and maybe lighting on model?
r = RecursiveLightPoint(gl4_worldmodel->nodes, p, end);
if (r == -1)
{
VectorCopy(vec3_origin, color);
}
else
{
VectorCopy(pointcolor, color);
}
/* add dynamic lights */
dl = gl4_newrefdef.dlights;
for (lnum = 0; lnum < gl4_newrefdef.num_dlights; lnum++, dl++)
{
VectorSubtract(currententity->origin,
dl->origin, dist);
add = dl->intensity - VectorLength(dist);
add *= (1.0f / 256.0f);
if (add > 0)
{
VectorMA(color, add, dl->color, color);
}
}
VectorScale(color, r_modulate->value, color);
}
/*
* Combine and scale multiple lightmaps into the floating format in blocklights
*/
void
GL4_BuildLightMap(msurface_t *surf, int offsetInLMbuf, int stride)
{
int smax, tmax;
int r, g, b, a, max;
int i, j, size, map, numMaps;
byte *lightmap;
if (surf->texinfo->flags &
(SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP))
{
ri.Sys_Error(ERR_DROP, "GL4_BuildLightMap called for non-lit surface");
}
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
size = smax * tmax;
stride -= (smax << 2);
if (size > 34*34*3)
{
ri.Sys_Error(ERR_DROP, "Bad s_blocklights size");
}
// count number of lightmaps surf actually has
for (numMaps = 0; numMaps < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[numMaps] != 255; ++numMaps)
{}
if (!surf->samples)
{
// no lightmap samples? set at least one lightmap to fullbright, rest to 0 as normal
if (numMaps == 0) numMaps = 1; // make sure at least one lightmap is set to fullbright
for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE; ++map)
{
// we always create 4 (MAX_LIGHTMAPS_PER_SURFACE) lightmaps.
// if surf has less (numMaps < 4), the remaining ones are zeroed out.
// this makes sure that all 4 lightmap textures in gl4state.lightmap_textureIDs[i] have the same layout
// and the shader can use the same texture coordinates for all of them
int c = (map < numMaps) ? 255 : 0;
byte* dest = gl4_lms.lightmap_buffers[map] + offsetInLMbuf;
for (i = 0; i < tmax; i++, dest += stride)
{
memset(dest, c, 4*smax);
dest += 4*smax;
}
}
return;
}
/* add all the lightmaps */
// Note: dynamic lights aren't handled here anymore, they're handled in the shader
// as we don't apply scale here anymore, nor blend the numMaps lightmaps together,
// the code has gotten a lot easier and we can copy directly from surf->samples to dest
// without converting to float first etc
lightmap = surf->samples;
for(map=0; map<numMaps; ++map)
{
byte* dest = gl4_lms.lightmap_buffers[map] + offsetInLMbuf;
int idxInLightmap = 0;
for (i = 0; i < tmax; i++, dest += stride)
{
for (j = 0; j < smax; j++)
{
r = lightmap[idxInLightmap * 3 + 0];
g = lightmap[idxInLightmap * 3 + 1];
b = lightmap[idxInLightmap * 3 + 2];
/* determine the brightest of the three color components */
if (r > g) max = r;
else max = g;
if (b > max) max = b;
/* alpha is ONLY used for the mono lightmap case. For this
reason we set it to the brightest of the color components
so that things don't get too dim. */
a = max;
dest[0] = r;
dest[1] = g;
dest[2] = b;
dest[3] = a;
dest += 4;
++idxInLightmap;
}
}
lightmap += size * 3; /* skip to next lightmap */
}
for ( ; map < MAX_LIGHTMAPS_PER_SURFACE; ++map)
{
// like above, fill up remaining lightmaps with 0
byte* dest = gl4_lms.lightmap_buffers[map] + offsetInLMbuf;
for (i = 0; i < tmax; i++, dest += stride)
{
memset(dest, 0, 4*smax);
dest += 4*smax;
}
}
}

View file

@ -0,0 +1,268 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Lightmap handling
*
* =======================================================================
*/
#include "header/local.h"
#define TEXNUM_LIGHTMAPS 1024
extern gl4lightmapstate_t gl4_lms;
void
GL4_LM_InitBlock(void)
{
memset(gl4_lms.allocated, 0, sizeof(gl4_lms.allocated));
}
void
GL4_LM_UploadBlock(void)
{
int map;
// NOTE: we don't use the dynamic lightmap anymore - all lightmaps are loaded at level load
// and not changed after that. they're blended dynamically depending on light styles
// though, and dynamic lights are (will be) applied in shader, hopefully per fragment.
GL4_BindLightmap(gl4_lms.current_lightmap_texture);
// upload all 4 lightmaps
for(map=0; map < MAX_LIGHTMAPS_PER_SURFACE; ++map)
{
GL4_SelectTMU(GL_TEXTURE1+map); // this relies on GL_TEXTURE2 being GL_TEXTURE1+1 etc
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl4_lms.internal_format = GL_LIGHTMAP_FORMAT;
glTexImage2D(GL_TEXTURE_2D, 0, gl4_lms.internal_format,
BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE, gl4_lms.lightmap_buffers[map]);
}
if (++gl4_lms.current_lightmap_texture == MAX_LIGHTMAPS)
{
ri.Sys_Error(ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n");
}
}
/*
* returns a texture number and the position inside it
*/
qboolean
GL4_LM_AllocBlock(int w, int h, int *x, int *y)
{
int i, j;
int best, best2;
best = BLOCK_HEIGHT;
for (i = 0; i < BLOCK_WIDTH - w; i++)
{
best2 = 0;
for (j = 0; j < w; j++)
{
if (gl4_lms.allocated[i + j] >= best)
{
break;
}
if (gl4_lms.allocated[i + j] > best2)
{
best2 = gl4_lms.allocated[i + j];
}
}
if (j == w)
{
/* this is a valid spot */
*x = i;
*y = best = best2;
}
}
if (best + h > BLOCK_HEIGHT)
{
return false;
}
for (i = 0; i < w; i++)
{
gl4_lms.allocated[*x + i] = best + h;
}
return true;
}
void
GL4_LM_BuildPolygonFromSurface(gl4model_t *currentmodel, msurface_t *fa)
{
int i, lindex, lnumverts;
medge_t *pedges, *r_pedge;
float *vec;
float s, t;
glpoly_t *poly;
vec3_t total;
vec3_t normal;
/* reconstruct the polygon */
pedges = currentmodel->edges;
lnumverts = fa->numedges;
VectorClear(total);
/* draw texture */
poly = Hunk_Alloc(sizeof(glpoly_t) +
(lnumverts - 4) * sizeof(gl4_3D_vtx_t));
poly->next = fa->polys;
poly->flags = fa->flags;
fa->polys = poly;
poly->numverts = lnumverts;
VectorCopy(fa->plane->normal, normal);
if(fa->flags & SURF_PLANEBACK)
{
// if for some reason the normal sticks to the back of the plane, invert it
// so it's usable for the shader
for (i=0; i<3; ++i) normal[i] = -normal[i];
}
for (i = 0; i < lnumverts; i++)
{
gl4_3D_vtx_t* vert = &poly->vertices[i];
lindex = currentmodel->surfedges[fa->firstedge + i];
if (lindex > 0)
{
r_pedge = &pedges[lindex];
vec = currentmodel->vertexes[r_pedge->v[0]].position;
}
else
{
r_pedge = &pedges[-lindex];
vec = currentmodel->vertexes[r_pedge->v[1]].position;
}
s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
s /= fa->texinfo->image->width;
t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
t /= fa->texinfo->image->height;
VectorAdd(total, vec, total);
VectorCopy(vec, vert->pos);
vert->texCoord[0] = s;
vert->texCoord[1] = t;
/* lightmap texture coordinates */
s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
s -= fa->texturemins[0];
s += fa->light_s * 16;
s += 8;
s /= BLOCK_WIDTH * 16; /* fa->texinfo->texture->width; */
t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
t -= fa->texturemins[1];
t += fa->light_t * 16;
t += 8;
t /= BLOCK_HEIGHT * 16; /* fa->texinfo->texture->height; */
vert->lmTexCoord[0] = s;
vert->lmTexCoord[1] = t;
VectorCopy(normal, vert->normal);
vert->lightFlags = 0;
}
}
void
GL4_LM_CreateSurfaceLightmap(msurface_t *surf)
{
int smax, tmax;
if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB))
{
return;
}
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
if (!GL4_LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t))
{
GL4_LM_UploadBlock();
GL4_LM_InitBlock();
if (!GL4_LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t))
{
ri.Sys_Error(ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed\n",
smax, tmax);
}
}
surf->lightmaptexturenum = gl4_lms.current_lightmap_texture;
GL4_BuildLightMap(surf, (surf->light_t * BLOCK_WIDTH + surf->light_s) * LIGHTMAP_BYTES, BLOCK_WIDTH * LIGHTMAP_BYTES);
}
void
GL4_LM_BeginBuildingLightmaps(gl4model_t *m)
{
static lightstyle_t lightstyles[MAX_LIGHTSTYLES];
int i;
memset(gl4_lms.allocated, 0, sizeof(gl4_lms.allocated));
gl4_framecount = 1; /* no dlightcache */
/* setup the base lightstyles so the lightmaps
won't have to be regenerated the first time
they're seen */
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
lightstyles[i].rgb[0] = 1;
lightstyles[i].rgb[1] = 1;
lightstyles[i].rgb[2] = 1;
lightstyles[i].white = 3;
}
gl4_newrefdef.lightstyles = lightstyles;
gl4_lms.current_lightmap_texture = 0;
gl4_lms.internal_format = GL_LIGHTMAP_FORMAT;
// Note: the dynamic lightmap used to be initialized here, we don't use that anymore.
}
void
GL4_LM_EndBuildingLightmaps(void)
{
GL4_LM_UploadBlock();
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,171 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Misc OpenGL4 refresher functions
*
* =======================================================================
*/
#include "header/local.h"
gl4image_t *gl4_notexture; /* use for bad textures */
gl4image_t *gl4_particletexture; /* little dot for particles */
void
GL4_SetDefaultState(void)
{
glClearColor(1, 0, 0.5, 0.5);
#ifndef YQ2_GL3_GLES
// in GLES this is only supported with an extension:
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_multisample_compatibility.txt
// but apparently it's just enabled by default if set in the context?
glDisable(GL_MULTISAMPLE);
#endif
glCullFace(GL_FRONT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
#ifndef YQ2_GL3_GLES
// in GLES GL_FILL is the only supported mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// TODO: gl1_texturealphamode?
GL4_TextureMode(gl_texturemode->string);
//R_TextureAlphaMode(gl1_texturealphamode->string);
//R_TextureSolidMode(gl1_texturesolidmode->string);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#ifndef YQ2_GL3_GLES // see above
if (gl_msaa_samples->value)
{
glEnable(GL_MULTISAMPLE);
// glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); TODO what is this for?
}
#endif
}
static byte dottexture[8][8] = {
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 0, 0, 0},
{0, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 1, 1, 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},
};
void
GL4_InitParticleTexture(void)
{
int x, y;
byte data[8][8][4];
/* particle texture */
for (x = 0; x < 8; x++)
{
for (y = 0; y < 8; y++)
{
data[y][x][0] = 255;
data[y][x][1] = 255;
data[y][x][2] = 255;
data[y][x][3] = dottexture[x][y] * 255;
}
}
gl4_particletexture = GL4_LoadPic("***particle***", (byte *)data,
8, 0, 8, 0, 8 * 8, it_sprite, 32);
/* also use this for bad textures, but without alpha */
for (x = 0; x < 8; x++)
{
for (y = 0; y < 8; y++)
{
data[y][x][0] = dottexture[x & 3][y & 3] * 255;
data[y][x][1] = 0;
data[y][x][2] = 0;
data[y][x][3] = 255;
}
}
gl4_notexture = GL4_LoadPic("***r_notexture***", (byte *)data,
8, 0, 8, 0, 8 * 8, it_wall, 32);
}
void
GL4_ScreenShot(void)
{
int w=vid.width, h=vid.height;
#ifdef YQ2_GL3_GLES
// My RPi4's GLES3 doesn't like GL_RGB, so use GL_RGBA with GLES
// TODO: we could convert the screenshot to RGB before writing
// so the resulting file is smaller
static const int comps = 4;
#else // Desktop GL
static const int comps = 3;
#endif
byte *buffer = malloc(w*h*comps);
if (!buffer)
{
R_Printf(PRINT_ALL, "GL4_ScreenShot: Couldn't malloc %d bytes\n", w*h*3);
return;
}
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, w, h, (comps == 4) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, buffer);
// the pixels are now row-wise left to right, bottom to top,
// but we need them row-wise left to right, top to bottom.
// so swap bottom rows with top rows
{
size_t bytesPerRow = comps*w;
YQ2_VLA(byte, rowBuffer, bytesPerRow);
byte *curRowL = buffer; // first byte of first row
byte *curRowH = buffer + bytesPerRow*(h-1); // first byte of last row
while(curRowL < curRowH)
{
memcpy(rowBuffer, curRowL, bytesPerRow);
memcpy(curRowL, curRowH, bytesPerRow);
memcpy(curRowH, rowBuffer, bytesPerRow);
curRowL += bytesPerRow;
curRowH -= bytesPerRow;
}
YQ2_VLAFREE(rowBuffer);
}
ri.Vid_WriteScreenshot(w, h, comps, buffer);
free(buffer);
}

View file

@ -0,0 +1,828 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Model loading and caching for OpenGL4. Includes the .bsp file format
*
* =======================================================================
*/
#include "header/local.h"
enum { MAX_MOD_KNOWN = 512 };
YQ2_ALIGNAS_TYPE(int) static byte mod_novis[MAX_MAP_LEAFS / 8];
gl4model_t mod_known[MAX_MOD_KNOWN];
static int mod_numknown;
static int mod_max = 0;
int registration_sequence;
//===============================================================================
static qboolean
Mod_HasFreeSpace(void)
{
int i, used;
gl4model_t *mod;
used = 0;
for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
{
if (!mod->name[0])
continue;
if (mod->registration_sequence == registration_sequence)
{
used ++;
}
}
if (mod_max < used)
{
mod_max = used;
}
// should same size of free slots as currently used
return (mod_numknown + mod_max) < MAX_MOD_KNOWN;
}
const byte*
GL4_Mod_ClusterPVS(int cluster, const gl4model_t *model)
{
if ((cluster == -1) || !model->vis)
{
return mod_novis;
}
return Mod_DecompressVis((byte *)model->vis +
model->vis->bitofs[cluster][DVIS_PVS],
(model->vis->numclusters + 7) >> 3);
}
void
GL4_Mod_Modellist_f(void)
{
int i, total, used;
gl4model_t *mod;
qboolean freeup;
total = 0;
used = 0;
R_Printf(PRINT_ALL, "Loaded models:\n");
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
char *in_use = "";
if (mod->registration_sequence == registration_sequence)
{
in_use = "*";
used ++;
}
if (!mod->name[0])
{
continue;
}
R_Printf(PRINT_ALL, "%8i : %s %s\n",
mod->extradatasize, mod->name, in_use);
total += mod->extradatasize;
}
R_Printf(PRINT_ALL, "Total resident: %i\n", total);
// update statistics
freeup = Mod_HasFreeSpace();
R_Printf(PRINT_ALL, "Used %d of %d models%s.\n", used, mod_max, freeup ? ", has free space" : "");
}
void
GL4_Mod_Init(void)
{
mod_max = 0;
memset(mod_novis, 0xff, sizeof(mod_novis));
}
static void
Mod_LoadSubmodels(gl4model_t *loadmodel, byte *mod_base, lump_t *l)
{
dmodel_t *in;
gl4model_t *out;
int i, j, count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
loadmodel->submodels = out;
loadmodel->numsubmodels = count;
for (i = 0; i < count; i++, in++, out++)
{
if (i == 0)
{
// copy parent as template for first model
memcpy(out, loadmodel, sizeof(*out));
}
else
{
// copy first as template for model
memcpy(out, loadmodel->submodels, sizeof(*out));
}
Com_sprintf (out->name, sizeof(out->name), "*%d", i);
for (j = 0; j < 3; j++)
{
/* spread the mins / maxs by a pixel */
out->mins[j] = LittleFloat(in->mins[j]) - 1;
out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
out->origin[j] = LittleFloat(in->origin[j]);
}
out->radius = Mod_RadiusFromBounds(out->mins, out->maxs);
out->firstnode = LittleLong(in->headnode);
out->firstmodelsurface = LittleLong(in->firstface);
out->nummodelsurfaces = LittleLong(in->numfaces);
// visleafs
out->numleafs = 0;
// check limits
if (out->firstnode >= loadmodel->numnodes)
{
ri.Sys_Error(ERR_DROP, "%s: Inline model %i has bad firstnode",
__func__, i);
}
}
}
/*
* Fills in s->texturemins[] and s->extents[]
*/
static void
Mod_CalcSurfaceExtents(gl4model_t *loadmodel, msurface_t *s)
{
float mins[2], maxs[2], val;
int i, j, e;
mvertex_t *v;
mtexinfo_t *tex;
int bmins[2], bmaxs[2];
mins[0] = mins[1] = 999999;
maxs[0] = maxs[1] = -99999;
tex = s->texinfo;
for (i = 0; i < s->numedges; i++)
{
e = loadmodel->surfedges[s->firstedge + i];
if (e >= 0)
{
v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
}
else
{
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
}
for (j = 0; j < 2; j++)
{
val = v->position[0] * tex->vecs[j][0] +
v->position[1] * tex->vecs[j][1] +
v->position[2] * tex->vecs[j][2] +
tex->vecs[j][3];
if (val < mins[j])
{
mins[j] = val;
}
if (val > maxs[j])
{
maxs[j] = val;
}
}
}
for (i = 0; i < 2; i++)
{
bmins[i] = floor(mins[i] / 16);
bmaxs[i] = ceil(maxs[i] / 16);
s->texturemins[i] = bmins[i] * 16;
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
}
}
extern void
GL4_SubdivideSurface(msurface_t *fa, gl4model_t* loadmodel);
static int calcTexinfoAndFacesSize(byte *mod_base, const lump_t *fl, const lump_t *tl)
{
dface_t* face_in = (void *)(mod_base + fl->fileofs);
texinfo_t* texinfo_in = (void *)(mod_base + tl->fileofs);
if (fl->filelen % sizeof(*face_in) || tl->filelen % sizeof(*texinfo_in))
{
// will error out when actually loading it
return 0;
}
int ret = 0;
int face_count = fl->filelen / sizeof(*face_in);
int texinfo_count = tl->filelen / sizeof(*texinfo_in);
{
// out = Hunk_Alloc(count * sizeof(*out));
int baseSize = face_count * sizeof(msurface_t);
baseSize = (baseSize + 31) & ~31;
ret += baseSize;
int ti_size = texinfo_count * sizeof(mtexinfo_t);
ti_size = (ti_size + 31) & ~31;
ret += ti_size;
}
int numWarpFaces = 0;
for (int surfnum = 0; surfnum < face_count; surfnum++, face_in++)
{
int numverts = LittleShort(face_in->numedges);
int ti = LittleShort(face_in->texinfo);
if ((ti < 0) || (ti >= texinfo_count))
{
return 0; // will error out
}
int texFlags = LittleLong(texinfo_in[ti].flags);
/* set the drawing flags */
if (texFlags & SURF_WARP)
{
if (numverts > 60)
return 0; // will error out in R_SubdividePolygon()
// GL4_SubdivideSurface(out, loadmodel); /* cut up polygon for warps */
// for each (pot. recursive) call to R_SubdividePolygon():
// sizeof(glpoly_t) + ((numverts - 4) + 2) * sizeof(gl4_3D_vtx_t)
// this is tricky, how much is allocated depends on the size of the surface
// which we don't know (we'd need the vertices etc to know, but we can't load
// those without allocating...)
// so we just count warped faces and use a generous estimate below
++numWarpFaces;
}
else
{
// GL4_LM_BuildPolygonFromSurface(out);
// => poly = Hunk_Alloc(sizeof(glpoly_t) + (numverts - 4) * sizeof(gl4_3D_vtx_t));
int polySize = sizeof(glpoly_t) + (numverts - 4) * sizeof(gl4_3D_vtx_t);
polySize = (polySize + 31) & ~31;
ret += polySize;
}
}
// yeah, this is a bit hacky, but it looks like for each warped face
// 256-55000 bytes are allocated (usually on the lower end),
// so just assume 48k per face to be safe
ret += numWarpFaces * 49152;
ret += 5000000; // and 5MB extra just in case
return ret;
}
static void
Mod_LoadFaces(gl4model_t *loadmodel, byte *mod_base, lump_t *l)
{
dface_t *in;
msurface_t *out;
int i, count, surfnum;
int planenum, side;
int ti;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
loadmodel->surfaces = out;
loadmodel->numsurfaces = count;
GL4_LM_BeginBuildingLightmaps(loadmodel);
for (surfnum = 0; surfnum < count; surfnum++, in++, out++)
{
out->firstedge = LittleLong(in->firstedge);
out->numedges = LittleShort(in->numedges);
out->flags = 0;
out->polys = NULL;
planenum = LittleShort(in->planenum);
side = LittleShort(in->side);
if (side)
{
out->flags |= SURF_PLANEBACK;
}
if (planenum < 0 || planenum >= loadmodel->numplanes)
{
ri.Sys_Error(ERR_DROP, "%s: Incorrect %d planenum.",
__func__, planenum);
}
out->plane = loadmodel->planes + planenum;
ti = LittleShort(in->texinfo);
if ((ti < 0) || (ti >= loadmodel->numtexinfo))
{
ri.Sys_Error(ERR_DROP, "%s: bad texinfo number",
__func__);
}
out->texinfo = loadmodel->texinfo + ti;
Mod_CalcSurfaceExtents(loadmodel, out);
/* lighting info */
for (i = 0; i < MAX_LIGHTMAPS_PER_SURFACE; i++)
{
out->styles[i] = in->styles[i];
}
i = LittleLong(in->lightofs);
if (i == -1)
{
out->samples = NULL;
}
else
{
out->samples = loadmodel->lightdata + i;
}
/* set the drawing flags */
if (out->texinfo->flags & SURF_WARP)
{
out->flags |= SURF_DRAWTURB;
for (i = 0; i < 2; i++)
{
out->extents[i] = 16384;
out->texturemins[i] = -8192;
}
GL4_SubdivideSurface(out, loadmodel); /* cut up polygon for warps */
}
if (r_fixsurfsky->value)
{
if (out->texinfo->flags & SURF_SKY)
{
out->flags |= SURF_DRAWSKY;
}
}
/* create lightmaps and polygons */
if (!(out->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)))
{
GL4_LM_CreateSurfaceLightmap(out);
}
if (!(out->texinfo->flags & SURF_WARP))
{
GL4_LM_BuildPolygonFromSurface(loadmodel, out);
}
}
GL4_LM_EndBuildingLightmaps();
}
static void
Mod_LoadLeafs(gl4model_t *loadmodel, byte *mod_base, lump_t *l)
{
dleaf_t *in;
mleaf_t *out;
int i, j, count, p;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
loadmodel->leafs = out;
loadmodel->numleafs = count;
for (i = 0; i < count; i++, in++, out++)
{
unsigned firstleafface;
for (j = 0; j < 3; j++)
{
out->minmaxs[j] = LittleShort(in->mins[j]);
out->minmaxs[3 + j] = LittleShort(in->maxs[j]);
}
p = LittleLong(in->contents);
out->contents = p;
out->cluster = LittleShort(in->cluster);
out->area = LittleShort(in->area);
// make unsigned long from signed short
firstleafface = LittleShort(in->firstleafface) & 0xFFFF;
out->nummarksurfaces = LittleShort(in->numleaffaces) & 0xFFFF;
out->firstmarksurface = loadmodel->marksurfaces + firstleafface;
if ((firstleafface + out->nummarksurfaces) > loadmodel->nummarksurfaces)
{
ri.Sys_Error(ERR_DROP, "%s: wrong marksurfaces position in %s",
__func__, loadmodel->name);
}
}
}
static void
Mod_LoadMarksurfaces(gl4model_t *loadmodel, byte *mod_base, lump_t *l)
{
int i, j, count;
short *in;
msurface_t **out;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
__func__, loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
loadmodel->marksurfaces = out;
loadmodel->nummarksurfaces = count;
for (i = 0; i < count; i++)
{
j = LittleShort(in[i]);
if ((j < 0) || (j >= loadmodel->numsurfaces))
{
ri.Sys_Error(ERR_DROP, "%s: bad surface number", __func__);
}
out[i] = loadmodel->surfaces + j;
}
}
static void
Mod_LoadBrushModel(gl4model_t *mod, void *buffer, int modfilelen)
{
int i;
dheader_t *header;
byte *mod_base;
if (mod != mod_known)
{
ri.Sys_Error(ERR_DROP, "Loaded a brush model after the world");
}
header = (dheader_t *)buffer;
i = LittleLong(header->version);
if (i != BSPVERSION)
{
ri.Sys_Error(ERR_DROP, "%s: %s has wrong version number (%i should be %i)",
__func__, mod->name, i, BSPVERSION);
}
/* swap all the lumps */
mod_base = (byte *)header;
for (i = 0; i < sizeof(dheader_t) / 4; i++)
{
((int *)header)[i] = LittleLong(((int *)header)[i]);
}
// calculate the needed hunksize from the lumps
int hunkSize = 0;
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VERTEXES], sizeof(dvertex_t), sizeof(mvertex_t), 0);
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_EDGES], sizeof(dedge_t), sizeof(medge_t), 0);
hunkSize += sizeof(medge_t) + 31; // for count+1 in Mod_LoadEdges()
int surfEdgeCount = (header->lumps[LUMP_SURFEDGES].filelen+sizeof(int)-1)/sizeof(int);
if(surfEdgeCount < MAX_MAP_SURFEDGES) // else it errors out later anyway
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_SURFEDGES], sizeof(int), sizeof(int), 0);
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LIGHTING], 1, 1, 0);
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_PLANES], sizeof(dplane_t), sizeof(cplane_t)*2, 0);
hunkSize += calcTexinfoAndFacesSize(mod_base, &header->lumps[LUMP_FACES], &header->lumps[LUMP_TEXINFO]);
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFFACES], sizeof(short), sizeof(msurface_t *), 0); // yes, out is indeed a pointer!
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VISIBILITY], 1, 1, 0);
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFS], sizeof(dleaf_t), sizeof(mleaf_t), 0);
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_NODES], sizeof(dnode_t), sizeof(mnode_t), 0);
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_MODELS], sizeof(dmodel_t), sizeof(gl4model_t), 0);
mod->extradata = Hunk_Begin(hunkSize);
mod->type = mod_brush;
/* load into heap */
Mod_LoadVertexes(mod->name, &mod->vertexes, &mod->numvertexes, mod_base,
&header->lumps[LUMP_VERTEXES], 0);
Mod_LoadEdges(mod->name, &mod->edges, &mod->numedges,
mod_base, &header->lumps[LUMP_EDGES], 1);
Mod_LoadSurfedges(mod->name, &mod->surfedges, &mod->numsurfedges,
mod_base, &header->lumps[LUMP_SURFEDGES], 0);
Mod_LoadLighting(&mod->lightdata, mod_base, &header->lumps[LUMP_LIGHTING]);
Mod_LoadPlanes (mod->name, &mod->planes, &mod->numplanes,
mod_base, &header->lumps[LUMP_PLANES], 0);
Mod_LoadTexinfo (mod->name, &mod->texinfo, &mod->numtexinfo,
mod_base, &header->lumps[LUMP_TEXINFO], (findimage_t)GL4_FindImage,
gl4_notexture, 0);
Mod_LoadFaces(mod, mod_base, &header->lumps[LUMP_FACES]);
Mod_LoadMarksurfaces(mod, mod_base, &header->lumps[LUMP_LEAFFACES]);
Mod_LoadVisibility(&mod->vis, mod_base, &header->lumps[LUMP_VISIBILITY]);
Mod_LoadLeafs(mod, mod_base, &header->lumps[LUMP_LEAFS]);
Mod_LoadNodes(mod->name, mod->planes, mod->numplanes, mod->leafs,
mod->numleafs, &mod->nodes, &mod->numnodes, mod_base,
&header->lumps[LUMP_NODES]);
Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS]);
mod->numframes = 2; /* regular and alternate animation */
}
static void
Mod_Free(gl4model_t *mod)
{
Hunk_Free(mod->extradata);
memset(mod, 0, sizeof(*mod));
}
void
GL4_Mod_FreeAll(void)
{
int i;
for (i = 0; i < mod_numknown; i++)
{
if (mod_known[i].extradatasize)
{
Mod_Free(&mod_known[i]);
}
}
}
/*
* Loads in a model for the given name
*/
static gl4model_t *
Mod_ForName (char *name, gl4model_t *parent_model, qboolean crash)
{
gl4model_t *mod;
void *buf;
int i, modfilelen;
if (!name[0])
{
ri.Sys_Error(ERR_DROP, "%s: NULL name", __func__);
}
/* inline models are grabbed only from worldmodel */
if (name[0] == '*' && parent_model)
{
i = (int)strtol(name + 1, (char **)NULL, 10);
if (i < 1 || i >= parent_model->numsubmodels)
{
ri.Sys_Error(ERR_DROP, "%s: bad inline model number",
__func__);
}
return &parent_model->submodels[i];
}
/* search the currently loaded models */
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
if (!mod->name[0])
{
continue;
}
if (!strcmp(mod->name, name))
{
return mod;
}
}
/* find a free model slot spot */
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
if (!mod->name[0])
{
break; /* free spot */
}
}
if (i == mod_numknown)
{
if (mod_numknown == MAX_MOD_KNOWN)
{
ri.Sys_Error(ERR_DROP, "mod_numknown == MAX_MOD_KNOWN");
}
mod_numknown++;
}
strcpy(mod->name, name);
/* load the file */
modfilelen = ri.FS_LoadFile(mod->name, (void **)&buf);
if (!buf)
{
if (crash)
{
ri.Sys_Error(ERR_DROP, "%s: %s not found",
__func__, mod->name);
}
memset(mod->name, 0, sizeof(mod->name));
return NULL;
}
/* call the apropriate loader */
switch (LittleLong(*(unsigned *)buf))
{
case IDALIASHEADER:
{
mod->extradata = Mod_LoadMD2(mod->name, buf, modfilelen,
mod->mins, mod->maxs,
(struct image_s **)mod->skins, (findimage_t)GL4_FindImage,
&(mod->type));
if (!mod->extradata)
{
ri.Sys_Error(ERR_DROP, "%s: Failed to load %s",
__func__, mod->name);
}
};
break;
case IDSPRITEHEADER:
{
mod->extradata = Mod_LoadSP2(mod->name, buf, modfilelen,
(struct image_s **)mod->skins, (findimage_t)GL4_FindImage,
&(mod->type));
if (!mod->extradata)
{
ri.Sys_Error(ERR_DROP, "%s: Failed to load %s",
__func__, mod->name);
}
}
break;
case IDBSPHEADER:
Mod_LoadBrushModel(mod, buf, modfilelen);
break;
default:
ri.Sys_Error(ERR_DROP, "%s: unknown fileid for %s",
__func__, mod->name);
break;
}
mod->extradatasize = Hunk_End();
ri.FS_FreeFile(buf);
return mod;
}
/*
* Specifies the model that will be used as the world
*/
void
GL4_BeginRegistration(char *model)
{
char fullname[MAX_QPATH];
cvar_t *flushmap;
registration_sequence++;
gl4_oldviewcluster = -1; /* force markleafs */
gl4state.currentlightmap = -1;
Com_sprintf(fullname, sizeof(fullname), "maps/%s.bsp", model);
/* explicitly free the old map if different
this guarantees that mod_known[0] is the
world map */
flushmap = ri.Cvar_Get("flushmap", "0", 0);
if (strcmp(mod_known[0].name, fullname) || flushmap->value)
{
Mod_Free(&mod_known[0]);
}
gl4_worldmodel = Mod_ForName(fullname, NULL, true);
gl4_viewcluster = -1;
}
struct model_s *
GL4_RegisterModel(char *name)
{
gl4model_t *mod;
mod = Mod_ForName(name, gl4_worldmodel, false);
if (mod)
{
mod->registration_sequence = registration_sequence;
/* register any images used by the models */
if (mod->type == mod_brush)
{
int i;
for (i = 0; i < mod->numtexinfo; i++)
{
mod->texinfo[i].image->registration_sequence = registration_sequence;
}
}
else
{
/* numframes is unused for SP2 but lets set it also */
mod->numframes = Mod_ReLoadSkins((struct image_s **)mod->skins,
(findimage_t)GL4_FindImage, mod->extradata, mod->type);
}
}
return mod;
}
void
GL4_EndRegistration(void)
{
int i;
gl4model_t *mod;
if (Mod_HasFreeSpace() && GL4_ImageHasFreeSpace())
{
// should be enough space for load next maps
return;
}
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
if (!mod->name[0])
{
continue;
}
if (mod->registration_sequence != registration_sequence)
{
/* don't need this model */
Mod_Free(mod);
}
}
GL4_FreeUnusedImages();
}

View file

@ -0,0 +1,436 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* SDL backend for the GL4 renderer. Everything that needs to be on the
* renderer side of thing. Also all glad (or whatever OpenGL loader I
* end up using) specific things.
*
* =======================================================================
*/
#include "header/local.h"
#include <SDL2/SDL.h>
static SDL_Window* window = NULL;
static SDL_GLContext context = NULL;
static qboolean vsyncActive = false;
qboolean IsHighDPIaware = false;
// --------
enum {
// Not all GL.h header know about GL_DEBUG_SEVERITY_NOTIFICATION_*.
// DG: yes, it's the same value in GLES3.2
QGL_DEBUG_SEVERITY_NOTIFICATION = 0x826B
};
/*
* Callback function for debug output.
*/
static void APIENTRY
DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
const GLchar *message, const void *userParam)
{
const char* sourceStr = "Source: Unknown";
const char* typeStr = "Type: Unknown";
const char* severityStr = "Severity: Unknown";
switch (severity)
{
#ifdef YQ2_GL3_GLES
#define SVRCASE(X, STR) case GL_DEBUG_SEVERITY_ ## X ## _KHR : severityStr = STR; break;
#else // Desktop GL
#define SVRCASE(X, STR) case GL_DEBUG_SEVERITY_ ## X ## _ARB : severityStr = STR; break;
#endif
case QGL_DEBUG_SEVERITY_NOTIFICATION: return;
SVRCASE(HIGH, "Severity: High")
SVRCASE(MEDIUM, "Severity: Medium")
SVRCASE(LOW, "Severity: Low")
#undef SVRCASE
}
switch (source)
{
#ifdef YQ2_GL3_GLES
#define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _KHR: sourceStr = "Source: " #X; break;
#else
#define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _ARB: sourceStr = "Source: " #X; break;
#endif
SRCCASE(API);
SRCCASE(WINDOW_SYSTEM);
SRCCASE(SHADER_COMPILER);
SRCCASE(THIRD_PARTY);
SRCCASE(APPLICATION);
SRCCASE(OTHER);
#undef SRCCASE
}
switch(type)
{
#ifdef YQ2_GL3_GLES
#define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _KHR: typeStr = "Type: " #X; break;
#else
#define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _ARB: typeStr = "Type: " #X; break;
#endif
TYPECASE(ERROR);
TYPECASE(DEPRECATED_BEHAVIOR);
TYPECASE(UNDEFINED_BEHAVIOR);
TYPECASE(PORTABILITY);
TYPECASE(PERFORMANCE);
TYPECASE(OTHER);
#undef TYPECASE
}
// use PRINT_ALL - this is only called with gl4_debugcontext != 0 anyway.
R_Printf(PRINT_ALL, "GLDBG %s %s %s: %s\n", sourceStr, typeStr, severityStr, message);
}
// ---------
/*
* Swaps the buffers and shows the next frame.
*/
void GL4_EndFrame(void)
{
if(gl4config.useBigVBO)
{
// I think this is a good point to orphan the VBO and get a fresh one
GL4_BindVAO(gl4state.vao3D);
GL4_BindVBO(gl4state.vbo3D);
glBufferData(GL_ARRAY_BUFFER, gl4state.vbo3Dsize, NULL, GL_STREAM_DRAW);
gl4state.vbo3DcurOffset = 0;
}
SDL_GL_SwapWindow(window);
}
/*
* Returns whether the vsync is enabled.
*/
qboolean GL4_IsVsyncActive(void)
{
return vsyncActive;
}
/*
* Enables or disabes the vsync.
*/
void GL4_SetVsync(void)
{
// Make sure that the user given
// value is SDL compatible...
int vsync = 0;
if (r_vsync->value == 1)
{
vsync = 1;
}
else if (r_vsync->value == 2)
{
vsync = -1;
}
if (SDL_GL_SetSwapInterval(vsync) == -1)
{
if (vsync == -1)
{
// Not every system supports adaptive
// vsync, fallback to normal vsync.
R_Printf(PRINT_ALL, "Failed to set adaptive vsync, reverting to normal vsync.\n");
SDL_GL_SetSwapInterval(1);
}
}
vsyncActive = SDL_GL_GetSwapInterval() != 0;
}
/*
* This function returns the flags used at the SDL window
* creation by GLimp_InitGraphics(). In case of error -1
* is returned.
*/
int GL4_PrepareForWindow(void)
{
// Mkay, let's try to load the libGL,
const char *libgl;
cvar_t *gl4_libgl = ri.Cvar_Get("gl4_libgl", "", CVAR_ARCHIVE);
if (strlen(gl4_libgl->string) == 0)
{
libgl = NULL;
}
else
{
libgl = gl4_libgl->string;
}
while (1)
{
if (SDL_GL_LoadLibrary(libgl) < 0)
{
if (libgl == NULL)
{
ri.Sys_Error(ERR_FATAL, "%s: Couldn't load libGL: %s!",
__func__, SDL_GetError());
return -1;
}
else
{
R_Printf(PRINT_ALL, "%s: Couldn't load libGL: %s!\n",
__func__, SDL_GetError());
R_Printf(PRINT_ALL, "Retrying with default...\n");
ri.Cvar_Set("gl3_libgl", "");
libgl = NULL;
}
}
else
{
break;
}
}
// Set GL context attributs bound to the window.
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
if (SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8) == 0)
{
gl4config.stencil = true;
}
else
{
gl4config.stencil = false;
}
#ifdef YQ2_GL3_GLES3
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
#else // Desktop GL
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif
// Set GL context flags.
int contextFlags = 0;
#ifndef YQ2_GL3_GLES // Desktop GL (at least RPi4 doesn't like this for GLES3)
contextFlags |= SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG;
#endif
if (gl4_debugcontext && gl4_debugcontext->value)
{
contextFlags |= SDL_GL_CONTEXT_DEBUG_FLAG;
}
if (contextFlags != 0)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextFlags);
}
// Let's see if the driver supports MSAA.
int msaa_samples = 0;
if (gl_msaa_samples->value)
{
msaa_samples = gl_msaa_samples->value;
if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) < 0)
{
R_Printf(PRINT_ALL, "MSAA is unsupported: %s\n", SDL_GetError());
ri.Cvar_SetValue ("r_msaa_samples", 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
}
else if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa_samples) < 0)
{
R_Printf(PRINT_ALL, "MSAA %ix is unsupported: %s\n", msaa_samples, SDL_GetError());
ri.Cvar_SetValue("r_msaa_samples", 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
}
}
else
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
}
return SDL_WINDOW_OPENGL;
}
/*
* Initializes the OpenGL context. Returns true at
* success and false at failure.
*/
int GL4_InitContext(void* win)
{
// Coders are stupid.
if (win == NULL)
{
ri.Sys_Error(ERR_FATAL, "R_InitContext() must not be called with NULL argument!");
return false;
}
window = (SDL_Window *)win;
// Initialize GL context.
context = SDL_GL_CreateContext(window);
if(context == NULL)
{
R_Printf(PRINT_ALL, "GL4_InitContext(): Creating OpenGL Context failed: %s\n", SDL_GetError());
window = NULL;
return false;
}
// Check if we've got the requested MSAA.
int msaa_samples = 0;
if (gl_msaa_samples->value)
{
if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaa_samples) == 0)
{
ri.Cvar_SetValue("r_msaa_samples", msaa_samples);
}
}
// Check if we've got at least 8 stencil bits
int stencil_bits = 0;
if (gl4config.stencil)
{
if (SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_bits) < 0 || stencil_bits < 8)
{
gl4config.stencil = false;
}
}
// Enable vsync if requested.
GL4_SetVsync();
// Load GL pointrs through GLAD and check context.
#ifdef YQ2_GL3_GLES
if( !gladLoadGLES2Loader(SDL_GL_GetProcAddress))
#else // Desktop GL
if( !gladLoadGLLoader(SDL_GL_GetProcAddress))
#endif
{
R_Printf(PRINT_ALL, "GL4_InitContext(): ERROR: loading OpenGL function pointers failed!\n");
return false;
}
#ifdef YQ2_GL3_GLES3
else if (GLVersion.major < 3)
#else // Desktop GL
else if (GLVersion.major < 4 || (GLVersion.major == 4 && GLVersion.minor < 6))
#endif
{
R_Printf(PRINT_ALL, "GL4_InitContext(): ERROR: glad only got GL version %d.%d!\n", GLVersion.major, GLVersion.minor);
return false;
}
else
{
R_Printf(PRINT_ALL, "Successfully loaded OpenGL function pointers using glad, got version %d.%d!\n", GLVersion.major, GLVersion.minor);
}
#ifdef YQ2_GL3_GLES
gl4config.debug_output = GLAD_GL_KHR_debug != 0;
#else // Desktop GL
gl4config.debug_output = GLAD_GL_ARB_debug_output != 0;
#endif
gl4config.anisotropic = GLAD_GL_ARB_texture_filter_anisotropic != 0;
gl4config.major_version = GLVersion.major;
gl4config.minor_version = GLVersion.minor;
// Debug context setup.
if (gl4_debugcontext && gl4_debugcontext->value && gl4config.debug_output)
{
#ifdef YQ2_GL3_GLES
glDebugMessageCallbackKHR(DebugCallback, NULL);
// Call GL3_DebugCallback() synchronously, i.e. directly when and
// where the error happens (so we can get the cause in a backtrace)
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
#else // Desktop GL
glDebugMessageCallbackARB(DebugCallback, NULL);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
#endif
}
// Window title - set here so we can display renderer name in it.
char title[40] = {0};
#ifdef YQ2_GL3_GLES3
snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL ES 3.0", YQ2VERSION);
#else
snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL 4.6", YQ2VERSION);
#endif
SDL_SetWindowTitle(window, title);
#if SDL_VERSION_ATLEAST(2, 26, 0)
// Figure out if we are high dpi aware.
int flags = SDL_GetWindowFlags(win);
IsHighDPIaware = (flags & SDL_WINDOW_ALLOW_HIGHDPI) ? true : false;
#endif
return true;
}
/*
* Fills the actual size of the drawable into width and height.
*/
void GL4_GetDrawableSize(int* width, int* height)
{
SDL_GL_GetDrawableSize(window, width, height);
}
/*
* Shuts the GL context down.
*/
void GL4_ShutdownContext()
{
if (window)
{
if(context)
{
SDL_GL_DeleteContext(context);
context = NULL;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,885 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Surface generation and drawing
*
* =======================================================================
*/
#include <assert.h>
#include <stddef.h> // ofsetof()
#include "header/local.h"
int c_visible_lightmaps;
int c_visible_textures;
static vec3_t modelorg; /* relative to viewpoint */
static msurface_t *gl4_alpha_surfaces;
gl4lightmapstate_t gl4_lms;
#define BACKFACE_EPSILON 0.01
extern gl4image_t gl4textures[MAX_GL4TEXTURES];
extern int numgl4textures;
void GL4_SurfInit(void)
{
// init the VAO and VBO for the standard vertexdata: 10 floats and 1 uint
// (X, Y, Z), (S, T), (LMS, LMT), (normX, normY, normZ) ; lightFlags - last two groups for lightmap/dynlights
glGenVertexArrays(1, &gl4state.vao3D);
GL4_BindVAO(gl4state.vao3D);
glGenBuffers(1, &gl4state.vbo3D);
GL4_BindVBO(gl4state.vbo3D);
if(gl4config.useBigVBO)
{
gl4state.vbo3Dsize = 5*1024*1024; // a 5MB buffer seems to work well?
gl4state.vbo3DcurOffset = 0;
glBufferData(GL_ARRAY_BUFFER, gl4state.vbo3Dsize, NULL, GL_STREAM_DRAW); // allocate/reserve that data
}
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(gl4_3D_vtx_t), 0);
glEnableVertexAttribArray(GL4_ATTRIB_TEXCOORD);
qglVertexAttribPointer(GL4_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(gl4_3D_vtx_t), offsetof(gl4_3D_vtx_t, texCoord));
glEnableVertexAttribArray(GL4_ATTRIB_LMTEXCOORD);
qglVertexAttribPointer(GL4_ATTRIB_LMTEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(gl4_3D_vtx_t), offsetof(gl4_3D_vtx_t, lmTexCoord));
glEnableVertexAttribArray(GL4_ATTRIB_NORMAL);
qglVertexAttribPointer(GL4_ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(gl4_3D_vtx_t), offsetof(gl4_3D_vtx_t, normal));
glEnableVertexAttribArray(GL4_ATTRIB_LIGHTFLAGS);
qglVertexAttribIPointer(GL4_ATTRIB_LIGHTFLAGS, 1, GL_UNSIGNED_INT, sizeof(gl4_3D_vtx_t), offsetof(gl4_3D_vtx_t, lightFlags));
// init VAO and VBO for model vertexdata: 9 floats
// (X,Y,Z), (S,T), (R,G,B,A)
glGenVertexArrays(1, &gl4state.vaoAlias);
GL4_BindVAO(gl4state.vaoAlias);
glGenBuffers(1, &gl4state.vboAlias);
GL4_BindVBO(gl4state.vboAlias);
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0);
glEnableVertexAttribArray(GL4_ATTRIB_TEXCOORD);
qglVertexAttribPointer(GL4_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 3*sizeof(GLfloat));
glEnableVertexAttribArray(GL4_ATTRIB_COLOR);
qglVertexAttribPointer(GL4_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 5*sizeof(GLfloat));
glGenBuffers(1, &gl4state.eboAlias);
// init VAO and VBO for particle vertexdata: 9 floats
// (X,Y,Z), (point_size,distace_to_camera), (R,G,B,A)
glGenVertexArrays(1, &gl4state.vaoParticle);
GL4_BindVAO(gl4state.vaoParticle);
glGenBuffers(1, &gl4state.vboParticle);
GL4_BindVBO(gl4state.vboParticle);
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0);
// TODO: maybe move point size and camera origin to UBO and calculate distance in vertex shader
glEnableVertexAttribArray(GL4_ATTRIB_TEXCOORD); // it's abused for (point_size, distance) here..
qglVertexAttribPointer(GL4_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 3*sizeof(GLfloat));
glEnableVertexAttribArray(GL4_ATTRIB_COLOR);
qglVertexAttribPointer(GL4_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 5*sizeof(GLfloat));
}
void GL4_SurfShutdown(void)
{
glDeleteBuffers(1, &gl4state.vbo3D);
gl4state.vbo3D = 0;
glDeleteVertexArrays(1, &gl4state.vao3D);
gl4state.vao3D = 0;
glDeleteBuffers(1, &gl4state.eboAlias);
gl4state.eboAlias = 0;
glDeleteBuffers(1, &gl4state.vboAlias);
gl4state.vboAlias = 0;
glDeleteVertexArrays(1, &gl4state.vaoAlias);
gl4state.vaoAlias = 0;
}
static void
SetLightFlags(msurface_t *surf)
{
unsigned int lightFlags = 0;
if (surf->dlightframe == gl4_framecount)
{
lightFlags = surf->dlightbits;
}
gl4_3D_vtx_t* verts = surf->polys->vertices;
int numVerts = surf->polys->numverts;
for(int i=0; i<numVerts; ++i)
{
verts[i].lightFlags = lightFlags;
}
}
static void
SetAllLightFlags(msurface_t *surf)
{
unsigned int lightFlags = 0xffffffff;
gl4_3D_vtx_t* verts = surf->polys->vertices;
int numVerts = surf->polys->numverts;
for(int i=0; i<numVerts; ++i)
{
verts[i].lightFlags = lightFlags;
}
}
void
GL4_DrawGLPoly(msurface_t *fa)
{
glpoly_t *p = fa->polys;
GL4_BindVAO(gl4state.vao3D);
GL4_BindVBO(gl4state.vbo3D);
GL4_BufferAndDraw3D(p->vertices, p->numverts, GL_TRIANGLE_FAN);
}
void
GL4_DrawGLFlowingPoly(msurface_t *fa)
{
glpoly_t *p;
float scroll;
p = fa->polys;
scroll = -64.0f * ((gl4_newrefdef.time / 40.0f) - (int)(gl4_newrefdef.time / 40.0f));
if (scroll == 0.0f)
{
scroll = -64.0f;
}
if(gl4state.uni3DData.scroll != scroll)
{
gl4state.uni3DData.scroll = scroll;
GL4_UpdateUBO3D();
}
GL4_BindVAO(gl4state.vao3D);
GL4_BindVBO(gl4state.vbo3D);
GL4_BufferAndDraw3D(p->vertices, p->numverts, GL_TRIANGLE_FAN);
}
static void
DrawTriangleOutlines(void)
{
STUB_ONCE("TODO: Implement for gl_showtris support!");
#if 0
int i, j;
glpoly_t *p;
if (!gl_showtris->value)
{
return;
}
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glColor4f(1, 1, 1, 1);
for (i = 0; i < MAX_LIGHTMAPS; i++)
{
msurface_t *surf;
for (surf = gl4_lms.lightmap_surfaces[i];
surf != 0;
surf = surf->lightmapchain)
{
p = surf->polys;
for ( ; p; p = p->chain)
{
for (j = 2; j < p->numverts; j++)
{
GLfloat vtx[12];
unsigned int k;
for (k=0; k<3; k++)
{
vtx[0+k] = p->vertices [ 0 ][ k ];
vtx[3+k] = p->vertices [ j - 1 ][ k ];
vtx[6+k] = p->vertices [ j ][ k ];
vtx[9+k] = p->vertices [ 0 ][ k ];
}
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, vtx );
glDrawArrays( GL_LINE_STRIP, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY );
}
}
}
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
#endif // 0
}
static void
UpdateLMscales(const hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE], gl4ShaderInfo_t* si)
{
int i;
qboolean hasChanged = false;
for(i=0; i<MAX_LIGHTMAPS_PER_SURFACE; ++i)
{
if(hasChanged)
{
si->lmScales[i] = lmScales[i];
}
else if( si->lmScales[i].R != lmScales[i].R
|| si->lmScales[i].G != lmScales[i].G
|| si->lmScales[i].B != lmScales[i].B
|| si->lmScales[i].A != lmScales[i].A )
{
si->lmScales[i] = lmScales[i];
hasChanged = true;
}
}
if(hasChanged)
{
glUniform4fv(si->uniLmScalesOrTime, MAX_LIGHTMAPS_PER_SURFACE, si->lmScales[0].Elements);
}
}
static void
RenderBrushPoly(entity_t *currententity, msurface_t *fa)
{
int map;
gl4image_t *image;
c_brush_polys++;
image = R_TextureAnimation(currententity, fa->texinfo);
if (fa->flags & SURF_DRAWTURB)
{
GL4_Bind(image->texnum);
GL4_EmitWaterPolys(fa);
return;
}
else
{
GL4_Bind(image->texnum);
}
hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE] = {0};
lmScales[0] = HMM_Vec4(1.0f, 1.0f, 1.0f, 1.0f);
GL4_BindLightmap(fa->lightmaptexturenum);
// Any dynamic lights on this surface?
for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE && fa->styles[map] != 255; map++)
{
lmScales[map].R = gl4_newrefdef.lightstyles[fa->styles[map]].rgb[0];
lmScales[map].G = gl4_newrefdef.lightstyles[fa->styles[map]].rgb[1];
lmScales[map].B = gl4_newrefdef.lightstyles[fa->styles[map]].rgb[2];
lmScales[map].A = 1.0f;
}
if (fa->texinfo->flags & SURF_FLOWING)
{
GL4_UseProgram(gl4state.si3DlmFlow.shaderProgram);
UpdateLMscales(lmScales, &gl4state.si3DlmFlow);
GL4_DrawGLFlowingPoly(fa);
}
else
{
GL4_UseProgram(gl4state.si3Dlm.shaderProgram);
UpdateLMscales(lmScales, &gl4state.si3Dlm);
GL4_DrawGLPoly(fa);
}
// Note: lightmap chains are gone, lightmaps are rendered together with normal texture in one pass
}
/*
* Draw water surfaces and windows.
* The BSP tree is waled front to back, so unwinding the chain
* of alpha_surfaces will draw back to front, giving proper ordering.
*/
void
GL4_DrawAlphaSurfaces(void)
{
msurface_t *s;
/* go back to the world matrix */
gl4state.uni3DData.transModelMat4 = gl4_identityMat4;
GL4_UpdateUBO3D();
glEnable(GL_BLEND);
for (s = gl4_alpha_surfaces; s != NULL; s = s->texturechain)
{
GL4_Bind(s->texinfo->image->texnum);
c_brush_polys++;
float alpha = 1.0f;
if (s->texinfo->flags & SURF_TRANS33)
{
alpha = 0.333f;
}
else if (s->texinfo->flags & SURF_TRANS66)
{
alpha = 0.666f;
}
if(alpha != gl4state.uni3DData.alpha)
{
gl4state.uni3DData.alpha = alpha;
GL4_UpdateUBO3D();
}
if (s->flags & SURF_DRAWTURB)
{
GL4_EmitWaterPolys(s);
}
else if (s->texinfo->flags & SURF_FLOWING)
{
GL4_UseProgram(gl4state.si3DtransFlow.shaderProgram);
GL4_DrawGLFlowingPoly(s);
}
else
{
GL4_UseProgram(gl4state.si3Dtrans.shaderProgram);
GL4_DrawGLPoly(s);
}
}
gl4state.uni3DData.alpha = 1.0f;
GL4_UpdateUBO3D();
glDisable(GL_BLEND);
gl4_alpha_surfaces = NULL;
}
static void
DrawTextureChains(entity_t *currententity)
{
int i;
msurface_t *s;
gl4image_t *image;
c_visible_textures = 0;
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
{
if (!image->registration_sequence)
{
continue;
}
s = image->texturechain;
if (!s)
{
continue;
}
c_visible_textures++;
for ( ; s; s = s->texturechain)
{
SetLightFlags(s);
RenderBrushPoly(currententity, s);
}
image->texturechain = NULL;
}
// TODO: maybe one loop for normal faces and one for SURF_DRAWTURB ???
}
static void
RenderLightmappedPoly(entity_t *currententity, msurface_t *surf)
{
int map;
gl4image_t *image = R_TextureAnimation(currententity, surf->texinfo);
hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE] = {0};
lmScales[0] = HMM_Vec4(1.0f, 1.0f, 1.0f, 1.0f);
assert((surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) == 0
&& "RenderLightMappedPoly mustn't be called with transparent, sky or warping surfaces!");
// Any dynamic lights on this surface?
for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[map] != 255; map++)
{
lmScales[map].R = gl4_newrefdef.lightstyles[surf->styles[map]].rgb[0];
lmScales[map].G = gl4_newrefdef.lightstyles[surf->styles[map]].rgb[1];
lmScales[map].B = gl4_newrefdef.lightstyles[surf->styles[map]].rgb[2];
lmScales[map].A = 1.0f;
}
c_brush_polys++;
GL4_Bind(image->texnum);
GL4_BindLightmap(surf->lightmaptexturenum);
if (surf->texinfo->flags & SURF_FLOWING)
{
GL4_UseProgram(gl4state.si3DlmFlow.shaderProgram);
UpdateLMscales(lmScales, &gl4state.si3DlmFlow);
GL4_DrawGLFlowingPoly(surf);
}
else
{
GL4_UseProgram(gl4state.si3Dlm.shaderProgram);
UpdateLMscales(lmScales, &gl4state.si3Dlm);
GL4_DrawGLPoly(surf);
}
}
static void
DrawInlineBModel(entity_t *currententity, gl4model_t *currentmodel)
{
int i, k;
cplane_t *pplane;
float dot;
msurface_t *psurf;
dlight_t *lt;
/* calculate dynamic lighting for bmodel */
lt = gl4_newrefdef.dlights;
for (k = 0; k < gl4_newrefdef.num_dlights; k++, lt++)
{
R_MarkLights(lt, 1 << k, currentmodel->nodes + currentmodel->firstnode,
r_dlightframecount, GL4_MarkSurfaceLights);
}
psurf = &currentmodel->surfaces[currentmodel->firstmodelsurface];
if (currententity->flags & RF_TRANSLUCENT)
{
glEnable(GL_BLEND);
/* TODO: should I care about the 0.25 part? we'll just set alpha to 0.33 or 0.66 depending on surface flag..
glColor4f(1, 1, 1, 0.25);
R_TexEnv(GL_MODULATE);
*/
}
/* draw texture */
for (i = 0; i < currentmodel->nummodelsurfaces; i++, psurf++)
{
/* find which side of the node we are on */
pplane = psurf->plane;
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
/* draw the polygon */
if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
{
if (psurf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66))
{
/* add to the translucent chain */
psurf->texturechain = gl4_alpha_surfaces;
gl4_alpha_surfaces = psurf;
}
else if(!(psurf->flags & SURF_DRAWTURB))
{
SetAllLightFlags(psurf);
RenderLightmappedPoly(currententity, psurf);
}
else
{
RenderBrushPoly(currententity, psurf);
}
}
}
if (currententity->flags & RF_TRANSLUCENT)
{
glDisable(GL_BLEND);
}
}
void
GL4_DrawBrushModel(entity_t *e, gl4model_t *currentmodel)
{
vec3_t mins, maxs;
int i;
qboolean rotated;
if (currentmodel->nummodelsurfaces == 0)
{
return;
}
gl4state.currenttexture = -1;
if (e->angles[0] || e->angles[1] || e->angles[2])
{
rotated = true;
for (i = 0; i < 3; i++)
{
mins[i] = e->origin[i] - currentmodel->radius;
maxs[i] = e->origin[i] + currentmodel->radius;
}
}
else
{
rotated = false;
VectorAdd(e->origin, currentmodel->mins, mins);
VectorAdd(e->origin, currentmodel->maxs, maxs);
}
if (r_cull->value && R_CullBox(mins, maxs, frustum))
{
return;
}
if (gl_zfix->value)
{
glEnable(GL_POLYGON_OFFSET_FILL);
}
VectorSubtract(gl4_newrefdef.vieworg, e->origin, modelorg);
if (rotated)
{
vec3_t temp;
vec3_t forward, right, up;
VectorCopy(modelorg, temp);
AngleVectors(e->angles, forward, right, up);
modelorg[0] = DotProduct(temp, forward);
modelorg[1] = -DotProduct(temp, right);
modelorg[2] = DotProduct(temp, up);
}
//glPushMatrix();
hmm_mat4 oldMat = gl4state.uni3DData.transModelMat4;
e->angles[0] = -e->angles[0];
e->angles[2] = -e->angles[2];
GL4_RotateForEntity(e);
e->angles[0] = -e->angles[0];
e->angles[2] = -e->angles[2];
DrawInlineBModel(e, currentmodel);
// glPopMatrix();
gl4state.uni3DData.transModelMat4 = oldMat;
GL4_UpdateUBO3D();
if (gl_zfix->value)
{
glDisable(GL_POLYGON_OFFSET_FILL);
}
}
static void
RecursiveWorldNode(entity_t *currententity, mnode_t *node)
{
int c, side, sidebit;
cplane_t *plane;
msurface_t *surf, **mark;
mleaf_t *pleaf;
float dot;
gl4image_t *image;
if (node->contents == CONTENTS_SOLID)
{
return; /* solid */
}
if (node->visframe != gl4_visframecount)
{
return;
}
if (r_cull->value && R_CullBox(node->minmaxs, node->minmaxs + 3, frustum))
{
return;
}
/* if a leaf node, draw stuff */
if (node->contents != CONTENTS_NODE)
{
pleaf = (mleaf_t *)node;
/* check for door connected areas */
// check for door connected areas
if (!R_AreaVisible(gl4_newrefdef.areabits, pleaf))
return; // not visible
mark = pleaf->firstmarksurface;
c = pleaf->nummarksurfaces;
if (c)
{
do
{
(*mark)->visframe = gl4_framecount;
mark++;
}
while (--c);
}
return;
}
/* node is just a decision point, so go down the apropriate
sides find which side of the node we are on */
plane = node->plane;
switch (plane->type)
{
case PLANE_X:
dot = modelorg[0] - plane->dist;
break;
case PLANE_Y:
dot = modelorg[1] - plane->dist;
break;
case PLANE_Z:
dot = modelorg[2] - plane->dist;
break;
default:
dot = DotProduct(modelorg, plane->normal) - plane->dist;
break;
}
if (dot >= 0)
{
side = 0;
sidebit = 0;
}
else
{
side = 1;
sidebit = SURF_PLANEBACK;
}
/* recurse down the children, front side first */
RecursiveWorldNode(currententity, node->children[side]);
/* draw stuff */
for (c = node->numsurfaces,
surf = gl4_worldmodel->surfaces + node->firstsurface;
c; c--, surf++)
{
if (surf->visframe != gl4_framecount)
{
continue;
}
if ((surf->flags & SURF_PLANEBACK) != sidebit)
{
continue; /* wrong side */
}
if (surf->texinfo->flags & SURF_SKY)
{
/* just adds to visible sky bounds */
GL4_AddSkySurface(surf);
}
else if (surf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66))
{
/* add to the translucent chain */
surf->texturechain = gl4_alpha_surfaces;
gl4_alpha_surfaces = surf;
gl4_alpha_surfaces->texinfo->image = R_TextureAnimation(currententity, surf->texinfo);
}
else
{
// calling RenderLightmappedPoly() here probably isn't optimal, rendering everything
// through texturechains should be faster, because far less glBindTexture() is needed
// (and it might allow batching the drawcalls of surfaces with the same texture)
#if 0
if(!(surf->flags & SURF_DRAWTURB))
{
RenderLightmappedPoly(surf);
}
else
#endif // 0
{
/* the polygon is visible, so add it to the texture sorted chain */
image = R_TextureAnimation(currententity, surf->texinfo);
surf->texturechain = image->texturechain;
image->texturechain = surf;
}
}
}
/* recurse down the back side */
RecursiveWorldNode(currententity, node->children[!side]);
}
void
GL4_DrawWorld(void)
{
entity_t ent;
if (!r_drawworld->value)
{
return;
}
if (gl4_newrefdef.rdflags & RDF_NOWORLDMODEL)
{
return;
}
VectorCopy(gl4_newrefdef.vieworg, modelorg);
/* auto cycle the world frame for texture animation */
memset(&ent, 0, sizeof(ent));
ent.frame = (int)(gl4_newrefdef.time * 2);
gl4state.currenttexture = -1;
GL4_ClearSkyBox();
RecursiveWorldNode(&ent, gl4_worldmodel->nodes);
DrawTextureChains(&ent);
GL4_DrawSkyBox();
DrawTriangleOutlines();
}
/*
* Mark the leaves and nodes that are
* in the PVS for the current cluster
*/
void
GL4_MarkLeaves(void)
{
const byte *vis;
YQ2_ALIGNAS_TYPE(int) byte fatvis[MAX_MAP_LEAFS / 8];
mnode_t *node;
int i, c;
mleaf_t *leaf;
int cluster;
if ((gl4_oldviewcluster == gl4_viewcluster) &&
(gl4_oldviewcluster2 == gl4_viewcluster2) &&
!r_novis->value &&
(gl4_viewcluster != -1))
{
return;
}
/* development aid to let you run around
and see exactly where the pvs ends */
if (r_lockpvs->value)
{
return;
}
gl4_visframecount++;
gl4_oldviewcluster = gl4_viewcluster;
gl4_oldviewcluster2 = gl4_viewcluster2;
if (r_novis->value || (gl4_viewcluster == -1) || !gl4_worldmodel->vis)
{
/* mark everything */
for (i = 0; i < gl4_worldmodel->numleafs; i++)
{
gl4_worldmodel->leafs[i].visframe = gl4_visframecount;
}
for (i = 0; i < gl4_worldmodel->numnodes; i++)
{
gl4_worldmodel->nodes[i].visframe = gl4_visframecount;
}
return;
}
vis = GL4_Mod_ClusterPVS(gl4_viewcluster, gl4_worldmodel);
/* may have to combine two clusters because of solid water boundaries */
if (gl4_viewcluster2 != gl4_viewcluster)
{
memcpy(fatvis, vis, (gl4_worldmodel->numleafs + 7) / 8);
vis = GL4_Mod_ClusterPVS(gl4_viewcluster2, gl4_worldmodel);
c = (gl4_worldmodel->numleafs + 31) / 32;
for (i = 0; i < c; i++)
{
((int *)fatvis)[i] |= ((int *)vis)[i];
}
vis = fatvis;
}
for (i = 0, leaf = gl4_worldmodel->leafs;
i < gl4_worldmodel->numleafs;
i++, leaf++)
{
cluster = leaf->cluster;
if (cluster == -1)
{
continue;
}
if (vis[cluster >> 3] & (1 << (cluster & 7)))
{
node = (mnode_t *)leaf;
do
{
if (node->visframe == gl4_visframecount)
{
break;
}
node->visframe = gl4_visframecount;
node = node->parent;
}
while (node);
}
}
}

View file

@ -0,0 +1,751 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Warps. Used on water surfaces und for skybox rotation.
*
* =======================================================================
*/
#include "header/local.h"
static void
R_BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
{
int i, j;
float *v;
mins[0] = mins[1] = mins[2] = 9999;
maxs[0] = maxs[1] = maxs[2] = -9999;
v = verts;
for (i = 0; i < numverts; i++)
{
for (j = 0; j < 3; j++, v++)
{
if (*v < mins[j])
{
mins[j] = *v;
}
if (*v > maxs[j])
{
maxs[j] = *v;
}
}
}
}
static const float SUBDIVIDE_SIZE = 64.0f;
static void
R_SubdividePolygon(int numverts, float *verts, msurface_t *warpface)
{
int i, j, k;
vec3_t mins, maxs;
float m;
float *v;
vec3_t front[64], back[64];
int f, b;
float dist[64];
float frac;
glpoly_t *poly;
float s, t;
vec3_t total;
float total_s, total_t;
vec3_t normal;
VectorCopy(warpface->plane->normal, normal);
if (numverts > 60)
{
ri.Sys_Error(ERR_DROP, "numverts = %i", numverts);
}
R_BoundPoly(numverts, verts, mins, maxs);
for (i = 0; i < 3; i++)
{
m = (mins[i] + maxs[i]) * 0.5;
m = SUBDIVIDE_SIZE * floor(m / SUBDIVIDE_SIZE + 0.5);
if (maxs[i] - m < 8)
{
continue;
}
if (m - mins[i] < 8)
{
continue;
}
/* cut it */
v = verts + i;
for (j = 0; j < numverts; j++, v += 3)
{
dist[j] = *v - m;
}
/* wrap cases */
dist[j] = dist[0];
v -= i;
VectorCopy(verts, v);
f = b = 0;
v = verts;
for (j = 0; j < numverts; j++, v += 3)
{
if (dist[j] >= 0)
{
VectorCopy(v, front[f]);
f++;
}
if (dist[j] <= 0)
{
VectorCopy(v, back[b]);
b++;
}
if ((dist[j] == 0) || (dist[j + 1] == 0))
{
continue;
}
if ((dist[j] > 0) != (dist[j + 1] > 0))
{
/* clip point */
frac = dist[j] / (dist[j] - dist[j + 1]);
for (k = 0; k < 3; k++)
{
front[f][k] = back[b][k] = v[k] + frac * (v[3 + k] - v[k]);
}
f++;
b++;
}
}
R_SubdividePolygon(f, front[0], warpface);
R_SubdividePolygon(b, back[0], warpface);
return;
}
/* add a point in the center to help keep warp valid */
poly = Hunk_Alloc(sizeof(glpoly_t) + ((numverts - 4) + 2) * sizeof(gl4_3D_vtx_t));
poly->next = warpface->polys;
warpface->polys = poly;
poly->numverts = numverts + 2;
VectorClear(total);
total_s = 0;
total_t = 0;
for (i = 0; i < numverts; i++, verts += 3)
{
VectorCopy(verts, poly->vertices[i + 1].pos);
s = DotProduct(verts, warpface->texinfo->vecs[0]);
t = DotProduct(verts, warpface->texinfo->vecs[1]);
total_s += s;
total_t += t;
VectorAdd(total, verts, total);
poly->vertices[i + 1].texCoord[0] = s;
poly->vertices[i + 1].texCoord[1] = t;
VectorCopy(normal, poly->vertices[i + 1].normal);
poly->vertices[i + 1].lightFlags = 0;
}
VectorScale(total, (1.0 / numverts), poly->vertices[0].pos);
poly->vertices[0].texCoord[0] = total_s / numverts;
poly->vertices[0].texCoord[1] = total_t / numverts;
VectorCopy(normal, poly->vertices[0].normal);
/* copy first vertex to last */
//memcpy(poly->vertices[i + 1], poly->vertices[1], sizeof(poly->vertices[0]));
poly->vertices[i + 1] = poly->vertices[1];
}
/*
* Breaks a polygon up along axial 64 unit
* boundaries so that turbulent and sky warps
* can be done reasonably.
*/
void
GL4_SubdivideSurface(msurface_t *fa, gl4model_t* loadmodel)
{
vec3_t verts[64];
int numverts;
int i;
int lindex;
float *vec;
/* convert edges back to a normal polygon */
numverts = 0;
for (i = 0; i < fa->numedges; i++)
{
lindex = loadmodel->surfedges[fa->firstedge + i];
if (lindex > 0)
{
vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
}
else
{
vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
}
VectorCopy(vec, verts[numverts]);
numverts++;
}
R_SubdividePolygon(numverts, verts[0], fa);
}
/*
* Does a water warp on the pre-fragmented glpoly_t chain
*/
void
GL4_EmitWaterPolys(msurface_t *fa)
{
glpoly_t *bp;
float scroll = 0.0f;
if (fa->texinfo->flags & SURF_FLOWING)
{
scroll = -64.0f * ((gl4_newrefdef.time * 0.5) - (int)(gl4_newrefdef.time * 0.5));
if (scroll == 0.0f) // this is done in GL4_DrawGLFlowingPoly() TODO: keep?
{
scroll = -64.0f;
}
}
qboolean updateUni3D = false;
if(gl4state.uni3DData.scroll != scroll)
{
gl4state.uni3DData.scroll = scroll;
updateUni3D = true;
}
// these surfaces (mostly water and lava, I think?) don't have a lightmap.
// rendering water at full brightness looks bad (esp. for water in dark environments)
// so default use a factor of 0.5 (ontop of intensity)
// but lava should be bright and glowing, so use full brightness there
float lightScale = fa->texinfo->image->is_lava ? 1.0f : 0.5f;
if(lightScale != gl4state.uni3DData.lightScaleForTurb)
{
gl4state.uni3DData.lightScaleForTurb = lightScale;
updateUni3D = true;
}
if(updateUni3D)
{
GL4_UpdateUBO3D();
}
GL4_UseProgram(gl4state.si3Dturb.shaderProgram);
GL4_BindVAO(gl4state.vao3D);
GL4_BindVBO(gl4state.vbo3D);
for (bp = fa->polys; bp != NULL; bp = bp->next)
{
GL4_BufferAndDraw3D(bp->vertices, bp->numverts, GL_TRIANGLE_FAN);
}
}
// ########### below: Sky-specific stuff ##########
#define ON_EPSILON 0.1 /* point on plane side epsilon */
enum { MAX_CLIP_VERTS = 64 };
static const int skytexorder[6] = {0, 2, 1, 3, 4, 5};
static float skymins[2][6], skymaxs[2][6];
static float sky_min, sky_max;
static float skyrotate;
static vec3_t skyaxis;
static gl4image_t* sky_images[6];
/* 3dstudio environment map names */
static const char* suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
vec3_t skyclip[6] = {
{1, 1, 0},
{1, -1, 0},
{0, -1, 1},
{0, 1, 1},
{1, 0, 1},
{-1, 0, 1}
};
int c_sky;
int st_to_vec[6][3] = {
{3, -1, 2},
{-3, 1, 2},
{1, 3, 2},
{-1, -3, 2},
{-2, -1, 3}, /* 0 degrees yaw, look straight up */
{2, -1, -3} /* look straight down */
};
int vec_to_st[6][3] = {
{-2, 3, 1},
{2, 3, -1},
{1, 3, 2},
{-1, 3, -2},
{-2, -1, 3},
{-2, 1, -3}
};
void
GL4_SetSky(char *name, float rotate, vec3_t axis)
{
char skyname[MAX_QPATH];
int i;
Q_strlcpy(skyname, name, sizeof(skyname));
skyrotate = rotate;
VectorCopy(axis, skyaxis);
for (i = 0; i < 6; i++)
{
gl4image_t *image;
image = (gl4image_t *)GetSkyImage(skyname, suf[i],
r_palettedtexture->value, (findimage_t)GL4_FindImage);
if (!image)
{
R_Printf(PRINT_ALL, "%s: can't load %s:%s sky\n",
__func__, skyname, suf[i]);
image = gl4_notexture;
}
sky_images[i] = image;
}
sky_min = 1.0 / 512;
sky_max = 511.0 / 512;
}
static void
DrawSkyPolygon(int nump, vec3_t vecs)
{
int i, j;
vec3_t v, av;
float s, t, dv;
int axis;
float *vp;
c_sky++;
/* decide which face it maps to */
VectorCopy(vec3_origin, v);
for (i = 0, vp = vecs; i < nump; i++, vp += 3)
{
VectorAdd(vp, v, v);
}
av[0] = fabs(v[0]);
av[1] = fabs(v[1]);
av[2] = fabs(v[2]);
if ((av[0] > av[1]) && (av[0] > av[2]))
{
if (v[0] < 0)
{
axis = 1;
}
else
{
axis = 0;
}
}
else if ((av[1] > av[2]) && (av[1] > av[0]))
{
if (v[1] < 0)
{
axis = 3;
}
else
{
axis = 2;
}
}
else
{
if (v[2] < 0)
{
axis = 5;
}
else
{
axis = 4;
}
}
/* project new texture coords */
for (i = 0; i < nump; i++, vecs += 3)
{
j = vec_to_st[axis][2];
if (j > 0)
{
dv = vecs[j - 1];
}
else
{
dv = -vecs[-j - 1];
}
if (dv < 0.001)
{
continue; /* don't divide by zero */
}
j = vec_to_st[axis][0];
if (j < 0)
{
s = -vecs[-j - 1] / dv;
}
else
{
s = vecs[j - 1] / dv;
}
j = vec_to_st[axis][1];
if (j < 0)
{
t = -vecs[-j - 1] / dv;
}
else
{
t = vecs[j - 1] / dv;
}
if (s < skymins[0][axis])
{
skymins[0][axis] = s;
}
if (t < skymins[1][axis])
{
skymins[1][axis] = t;
}
if (s > skymaxs[0][axis])
{
skymaxs[0][axis] = s;
}
if (t > skymaxs[1][axis])
{
skymaxs[1][axis] = t;
}
}
}
static void
ClipSkyPolygon(int nump, vec3_t vecs, int stage)
{
float *norm;
float *v;
qboolean front, back;
float d, e;
float dists[MAX_CLIP_VERTS];
int sides[MAX_CLIP_VERTS];
vec3_t newv[2][MAX_CLIP_VERTS];
int newc[2];
int i, j;
if (nump > MAX_CLIP_VERTS - 2)
{
ri.Sys_Error(ERR_DROP, "R_ClipSkyPolygon: MAX_CLIP_VERTS");
}
if (stage == 6)
{
/* fully clipped, so draw it */
DrawSkyPolygon(nump, vecs);
return;
}
front = back = false;
norm = skyclip[stage];
for (i = 0, v = vecs; i < nump; i++, v += 3)
{
d = DotProduct(v, norm);
if (d > ON_EPSILON)
{
front = true;
sides[i] = SIDE_FRONT;
}
else if (d < -ON_EPSILON)
{
back = true;
sides[i] = SIDE_BACK;
}
else
{
sides[i] = SIDE_ON;
}
dists[i] = d;
}
if (!front || !back)
{
/* not clipped */
ClipSkyPolygon(nump, vecs, stage + 1);
return;
}
/* clip it */
sides[i] = sides[0];
dists[i] = dists[0];
VectorCopy(vecs, (vecs + (i * 3)));
newc[0] = newc[1] = 0;
for (i = 0, v = vecs; i < nump; i++, v += 3)
{
switch (sides[i])
{
case SIDE_FRONT:
VectorCopy(v, newv[0][newc[0]]);
newc[0]++;
break;
case SIDE_BACK:
VectorCopy(v, newv[1][newc[1]]);
newc[1]++;
break;
case SIDE_ON:
VectorCopy(v, newv[0][newc[0]]);
newc[0]++;
VectorCopy(v, newv[1][newc[1]]);
newc[1]++;
break;
}
if ((sides[i] == SIDE_ON) ||
(sides[i + 1] == SIDE_ON) ||
(sides[i + 1] == sides[i]))
{
continue;
}
d = dists[i] / (dists[i] - dists[i + 1]);
for (j = 0; j < 3; j++)
{
e = v[j] + d * (v[j + 3] - v[j]);
newv[0][newc[0]][j] = e;
newv[1][newc[1]][j] = e;
}
newc[0]++;
newc[1]++;
}
/* continue */
ClipSkyPolygon(newc[0], newv[0][0], stage + 1);
ClipSkyPolygon(newc[1], newv[1][0], stage + 1);
}
void
GL4_AddSkySurface(msurface_t *fa)
{
int i;
vec3_t verts[MAX_CLIP_VERTS];
glpoly_t *p;
/* calculate vertex values for sky box */
for (p = fa->polys; p; p = p->next)
{
for (i = 0; i < p->numverts; i++)
{
VectorSubtract(p->vertices[i].pos, gl4_origin, verts[i]);
}
ClipSkyPolygon(p->numverts, verts[0], 0);
}
}
void
GL4_ClearSkyBox(void)
{
int i;
for (i = 0; i < 6; i++)
{
skymins[0][i] = skymins[1][i] = 9999;
skymaxs[0][i] = skymaxs[1][i] = -9999;
}
}
static void
MakeSkyVec(float s, float t, int axis, gl4_3D_vtx_t* vert)
{
vec3_t v, b;
int j, k;
float dist = (r_farsee->value == 0) ? 2300.0f : 4096.0f;
b[0] = s * dist;
b[1] = t * dist;
b[2] = dist;
for (j = 0; j < 3; j++)
{
k = st_to_vec[axis][j];
if (k < 0)
{
v[j] = -b[-k - 1];
}
else
{
v[j] = b[k - 1];
}
}
/* avoid bilerp seam */
s = (s + 1) * 0.5;
t = (t + 1) * 0.5;
if (s < sky_min)
{
s = sky_min;
}
else if (s > sky_max)
{
s = sky_max;
}
if (t < sky_min)
{
t = sky_min;
}
else if (t > sky_max)
{
t = sky_max;
}
t = 1.0 - t;
VectorCopy(v, vert->pos);
vert->texCoord[0] = s;
vert->texCoord[1] = t;
vert->lmTexCoord[0] = vert->lmTexCoord[1] = 0.0f;
}
void
GL4_DrawSkyBox(void)
{
int i;
if (skyrotate)
{ /* check for no sky at all */
for (i = 0; i < 6; i++)
{
if ((skymins[0][i] < skymaxs[0][i]) &&
(skymins[1][i] < skymaxs[1][i]))
{
break;
}
}
if (i == 6)
{
return; /* nothing visible */
}
}
// glPushMatrix();
hmm_mat4 origModelMat = gl4state.uni3DData.transModelMat4;
// glTranslatef(gl4_origin[0], gl4_origin[1], gl4_origin[2]);
hmm_vec3 transl = HMM_Vec3(gl4_origin[0], gl4_origin[1], gl4_origin[2]);
hmm_mat4 modMVmat = HMM_MultiplyMat4(origModelMat, HMM_Translate(transl));
if(skyrotate != 0.0f)
{
// glRotatef(gl4_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]);
hmm_vec3 rotAxis = HMM_Vec3(skyaxis[0], skyaxis[1], skyaxis[2]);
modMVmat = HMM_MultiplyMat4(modMVmat, HMM_Rotate(gl4_newrefdef.time * skyrotate, rotAxis));
}
gl4state.uni3DData.transModelMat4 = modMVmat;
GL4_UpdateUBO3D();
GL4_UseProgram(gl4state.si3Dsky.shaderProgram);
GL4_BindVAO(gl4state.vao3D);
GL4_BindVBO(gl4state.vbo3D);
// TODO: this could all be done in one drawcall.. but.. whatever, it's <= 6 drawcalls/frame
gl4_3D_vtx_t skyVertices[4];
for (i = 0; i < 6; i++)
{
if (skyrotate != 0.0f)
{
skymins[0][i] = -1;
skymins[1][i] = -1;
skymaxs[0][i] = 1;
skymaxs[1][i] = 1;
}
if ((skymins[0][i] >= skymaxs[0][i]) ||
(skymins[1][i] >= skymaxs[1][i]))
{
continue;
}
GL4_Bind(sky_images[skytexorder[i]]->texnum);
MakeSkyVec( skymins [ 0 ] [ i ], skymins [ 1 ] [ i ], i, &skyVertices[0] );
MakeSkyVec( skymins [ 0 ] [ i ], skymaxs [ 1 ] [ i ], i, &skyVertices[1] );
MakeSkyVec( skymaxs [ 0 ] [ i ], skymaxs [ 1 ] [ i ], i, &skyVertices[2] );
MakeSkyVec( skymaxs [ 0 ] [ i ], skymins [ 1 ] [ i ], i, &skyVertices[3] );
GL4_BufferAndDraw3D(skyVertices, 4, GL_TRIANGLE_FAN);
}
// glPopMatrix();
gl4state.uni3DData.transModelMat4 = origModelMat;
GL4_UpdateUBO3D();
}

View file

@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,960 @@
/*
* A header-only typesafe dynamic array implementation for plain C,
* kinda like C++ std::vector. This code is compatible with C++, but should
* only be used with POD (plain old data) types, as it uses memcpy() etc
* instead of copy/move construction/assignment.
* It requires a new type (created with the DA_TYPEDEF(ELEMENT_TYPE, ARRAY_TYPE_NAME)
* macro) for each kind of element you want to put in a dynamic array; however
* the "functions" to manipulate the array are actually macros and the same
* for all element types.
* The array elements are accessed via dynArr.p[i] or da_get(dynArr, i)
* - the latter checks whether i is a valid index and asserts if not.
*
* One thing to keep in mind is that, because of using macros, the arguments to
* the "functions" are usually evaluated more than once, so you should avoid
* putting things with side effect (like function-calls with side effects or i++)
* into them. Notable exceptions are the value arguments (v) of da_push()
* and da_insert(), so it's still ok to do da_push(arr, fun_with_sideffects());
* or da_insert(a, 3, x++);
*
* The function-like da_* macros are short aliases of dg_dynarr_* macros.
* If the short names clash with anything in your code or other headers
* you are using, you can, before #including this header, do
* #define DG_DYNARR_NO_SHORTNAMES
* and use the long dg_dynarr_* forms of the macros instead.
*
* Using this library in your project:
* Put this file somewhere in your project.
* In *one* of your .c/.cpp files, do
* #define DG_DYNARR_IMPLEMENTATION
* #include "DG_dynarr.h"
* to create the implementation of this library in that file.
* You can just #include "DG_dynarr.h" (without the #define) in other source
* files to use it there.
*
* See below this comment block for a usage example.
*
* You can #define your own allocators, assertion and the amount of runtime
* checking of indexes, see CONFIGURATION section in the code for more information.
*
*
* This is heavily inspired by Sean Barrett's stretchy_buffer.h
* ( see: https://github.com/nothings/stb/blob/master/stretchy_buffer.h )
* However I wanted to have a struct that holds the array pointer and the length
* and capacity, so that struct always remains at the same address while the
* array memory might be reallocated.
* I can live with arr.p[i] instead of arr[i], but I like how he managed to use
* macros to create an API that doesn't force the user to specify the stored
* type over and over again, so I stole some of his tricks :-)
*
* This has been tested with GCC 4.8 and clang 3.8 (-std=gnu89, -std=c99 and as C++;
* -std=c89 works if you convert the C++-style comments to C comments) and
* Microsoft Visual Studio 6 and 2010 (32bit) and 2013 (32bit and 64bit).
* I guess it works with all (recentish) C++ compilers and C compilers supporting
* C99 or even C89 + C++ comments (otherwise converting the comments should help).
*
* (C) 2016 Daniel Gibson
*
* LICENSE
* This software is dual-licensed to the public domain and under the following
* license: you are granted a perpetual, irrevocable license to copy, modify,
* publish, and distribute this file as you see fit.
* No warranty implied; use at your own risk.
*/
#if 0 // Usage Example:
#define DG_DYNARR_IMPLEMENTATION // this define is only needed in *one* .c/.cpp file!
#include "DG_dynarr.h"
DA_TYPEDEF(int, MyIntArrType); // creates MyIntArrType - a dynamic array for ints
void printIntArr(MyIntArrType* arr, const char* name)
{
// note that arr is a pointer here, so use *arr in the da_*() functions.
printf("%s = {", name);
if(da_count(*arr) > 0)
printf(" %d", arr->p[0]);
for(int i=1; i<da_count(*arr); ++i)
printf(", %d", arr->p[i]);
printf(" }\n");
}
void myFunction()
{
MyIntArrType a1 = {0}; // make sure to zero out the struct
// instead of = {0}; you could also call da_init(a1);
da_push(a1, 42);
assert(da_count(a1) == 1 && a1.p[0] == 42);
int* addedElements = da_addn_uninit(a1, 3);
assert(da_count(a1) == 4);
for(size_t i=0; i<3; ++i)
addedElements[i] = i+5;
printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7 }"
MyIntArrType a2;
da_init(a2);
da_addn(a2, a1.p, da_count(a1)); // copy all elements from a1 to a2
assert(da_count(a2) == 4);
da_insert(a2, 1, 11);
printIntArr(&a2, "a2"); // "a2 = { 42, 11, 5, 6, 7 }"
da_delete(a2, 2);
printIntArr(&a2, "a2"); // "a2 = { 42, 11, 6, 7 }"
da_deletefast(a2, 0);
printIntArr(&a2, "a2"); // "a2 = { 7, 11, 6 }"
da_push(a1, 3);
printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7, 3 }"
int x=da_pop(a1);
printf("x = %d\n", x); // "x = 3"
printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7 }"
da_free(a1); // make sure not to leak memory!
da_free(a2);
}
#endif // 0 (usage example)
#ifndef DG__DYNARR_H
#define DG__DYNARR_H
// ######### CONFIGURATION #########
// following: some #defines that you can tweak to your liking
// you can reduce some overhead by defining DG_DYNARR_INDEX_CHECK_LEVEL to 2, 1 or 0
#ifndef DG_DYNARR_INDEX_CHECK_LEVEL
// 0: (almost) no index checking
// 1: macros "returning" something return a.p[0] or NULL if the index was invalid
// 2: assertions in all macros taking indexes that make sure they're valid
// 3: 1 and 2
#define DG_DYNARR_INDEX_CHECK_LEVEL 3
#endif // DG_DYNARR_INDEX_CHECK_LEVEL
// you can #define your own DG_DYNARR_ASSERT(condition, msgstring)
// that will be used for all assertions in this code.
#ifndef DG_DYNARR_ASSERT
#include <assert.h>
#define DG_DYNARR_ASSERT(cond, msg) assert((cond) && msg)
#endif
// you can #define DG_DYNARR_OUT_OF_MEMORY to some code that will be executed
// if allocating memory fails
// it's needed only before the #define DG_DYNARR_IMPLEMENTATION #include of
// this header, so the following is here only for reference and commented out
/*
#ifndef DG_DYNARR_OUT_OF_MEMORY
#define DG_DYNARR_OUT_OF_MEMORY DG_DYNARR_ASSERT(0, "Out of Memory!");
#endif
*/
// By default, C's malloc(), realloc() and free() is used to allocate/free heap memory
// (see beginning of "#ifdef DG_DYNARR_IMPLEMENTATION" block below).
// You can #define DG_DYNARR_MALLOC, DG_DYNARR_REALLOC and DG_DYNARR_FREE yourself
// to provide alternative implementations like Win32 Heap(Re)Alloc/HeapFree
// it's needed only before the #define DG_DYNARR_IMPLEMENTATION #include of
// this header, so the following is here only for reference and commented out
/*
#define DG_DYNARR_MALLOC(elemSize, numElems) malloc(elemSize*numElems)
// oldNumElems is not used for C's realloc, but maybe you need it for
// your allocator to copy the old elements over
#define DG_DYNARR_REALLOC(ptr, elemSize, oldNumElems, newCapacity) \
realloc(ptr, elemSize*newCapacity);
#define DG_DYNARR_FREE(ptr) free(ptr)
*/
// if you want to prepend something to the non inline (DG_DYNARR_INLINE) functions,
// like "__declspec(dllexport)" or whatever, #define DG_DYNARR_DEF
#ifndef DG_DYNARR_DEF
// by defaults it's empty.
#define DG_DYNARR_DEF
#endif
// some functions are inline, in case your compiler doesn't like "static inline"
// but wants "__inline__" or something instead, #define DG_DYNARR_INLINE accordingly.
#ifndef DG_DYNARR_INLINE
// for pre-C99 compilers you might have to use something compiler-specific (or maybe only "static")
#ifdef _MSC_VER
#define DG_DYNARR_INLINE static __inline
#else
#define DG_DYNARR_INLINE static inline
#endif
#endif
// ############### Short da_* aliases for the long names ###############
#ifndef DG_DYNARR_NO_SHORTNAMES
// this macro is used to create an array type (struct) for elements of TYPE
// use like DA_TYPEDEF(int, MyIntArrType); MyIntArrType ia = {0}; da_push(ia, 42); ...
#define DA_TYPEDEF(TYPE, NewArrayTypeName) \
DG_DYNARR_TYPEDEF(TYPE, NewArrayTypeName)
// makes sure the array is initialized and can be used.
// either do YourArray arr = {0}; or YourArray arr; da_init(arr);
#define da_init(a) \
dg_dynarr_init(a)
/*
* This allows you to provide an external buffer that'll be used as long as it's big enough
* once you add more elements than buf can hold, fresh memory will be allocated on the heap
* Use like:
* DA_TYPEDEF(double, MyDoubleArrType);
* MyDoubleArrType arr;
* double buf[8];
* dg_dynarr_init_external(arr, buf, 8);
* dg_dynarr_push(arr, 1.23);
* ...
*/
#define da_init_external(a, buf, buf_cap) \
dg_dynarr_init_external(a, buf, buf_cap)
// use this to free the memory allocated by dg_dynarr once you don't need the array anymore
// Note: it is safe to add new elements to the array after da_free()
// it will allocate new memory, just like it would directly after da_init()
#define da_free(a) \
dg_dynarr_free(a)
// add an element to the array (appended at the end)
#define da_push(a, v) \
dg_dynarr_push(a, v)
// add an element to the array (appended at the end)
// does the same as push, just for consistency with addn (like insert and insertn)
#define da_add(a, v) \
dg_dynarr_add(a, v)
// append n elements to a and initialize them from array vals, doesn't return anything
// ! vals (and all other args) are evaluated multiple times !
#define da_addn(a, vals, n) \
dg_dynarr_addn(a, vals, n)
// add n elements to the end of the array and zeroes them with memset()
// returns pointer to first added element, NULL if out of memory (array is empty then)
#define da_addn_zeroed(a, n) \
dg_dynarr_addn_zeroed(a, n)
// add n elements to the end of the array, will remain uninitialized
// returns pointer to first added element, NULL if out of memory (array is empty then)
#define da_addn_uninit(a, n) \
dg_dynarr_addn_uninit(a, n)
// insert a single value v at index idx
#define da_insert(a, idx, v) \
dg_dynarr_insert(a, idx, v)
// insert n elements into a at idx, initialize them from array vals
// doesn't return anything
// ! vals (and all other args) is evaluated multiple times !
#define da_insertn(a, idx, vals, n) \
dg_dynarr_insertn(a, idx, vals, n)
// insert n elements into a at idx and zeroe them with memset()
// returns pointer to first inserted element or NULL if out of memory
#define da_insertn_zeroed(a, idx, n) \
dg_dynarr_insertn_zeroed(a, idx, n)
// insert n uninitialized elements into a at idx;
// returns pointer to first inserted element or NULL if out of memory
#define da_insertn_uninit(a, idx, n) \
dg_dynarr_insertn_uninit(a, idx, n)
// set a single value v at index idx - like "a.p[idx] = v;" but with checks (unless disabled)
#define da_set(a, idx, v) \
dg_dynarr_set(a, idx, v)
// overwrite n elements of a, starting at idx, with values from array vals
// doesn't return anything
// ! vals (and all other args) is evaluated multiple times !
#define da_setn(a, idx, vals, n) \
dg_dynarr_setn(a, idx, vals, n)
// delete the element at idx, moving all following elements (=> keeps order)
#define da_delete(a, idx) \
dg_dynarr_delete(a, idx)
// delete n elements starting at idx, moving all following elements (=> keeps order)
#define da_deleten(a, idx, n) \
dg_dynarr_deleten(a, idx, n)
// delete the element at idx, move the last element there (=> doesn't keep order)
#define da_deletefast(a, idx) \
dg_dynarr_deletefast(a, idx)
// delete n elements starting at idx, move the last n elements there (=> doesn't keep order)
#define da_deletenfast(a, idx, n) \
dg_dynarr_deletenfast(a, idx, n)
// removes all elements from the array, but does not free the buffer
// (if you want to free the buffer too, just use da_free())
#define da_clear(a) \
dg_dynarr_clear(a)
// sets the logical number of elements in the array
// if cnt > dg_dynarr_count(a), the logical count will be increased accordingly
// and the new elements will be uninitialized
#define da_setcount(a, cnt) \
dg_dynarr_setcount(a, cnt)
// make sure the array can store cap elements without reallocating
// logical count remains unchanged
#define da_reserve(a, cap) \
dg_dynarr_reserve(a, cap)
// this makes sure a only uses as much memory as for its elements
// => maybe useful if a used to contain a huge amount of elements,
// but you deleted most of them and want to free some memory
// Note however that this implies an allocation and copying the remaining
// elements, so only do this if it frees enough memory to be worthwhile!
#define da_shrink_to_fit(a) \
dg_dynarr_shrink_to_fit(a)
// removes and returns the last element of the array
#define da_pop(a) \
dg_dynarr_pop(a)
// returns the last element of the array
#define da_last(a) \
dg_dynarr_last(a)
// returns the pointer *to* the last element of the array
// (in contrast to dg_dynarr_end() which returns a pointer *after* the last element)
// returns NULL if array is empty
#define da_lastptr(a) \
dg_dynarr_lastptr(a)
// get element at index idx (like a.p[idx]), but with checks
// (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0)
#define da_get(a, idx) \
dg_dynarr_get(a,idx)
// get pointer to element at index idx (like &a.p[idx]), but with checks
// and it returns NULL if idx is invalid
#define da_getptr(a, idx) \
dg_dynarr_getptr(a, idx)
// returns a pointer to the first element of the array
// (together with dg_dynarr_end() you can do C++-style iterating)
#define da_begin(a) \
dg_dynarr_begin(a)
// returns a pointer to the past-the-end element of the array
// Allows C++-style iterating, in case you're into that kind of thing:
// for(T *it=da_begin(a), *end=da_end(a); it!=end; ++it) foo(*it);
// (see da_lastptr() to get a pointer *to* the last element)
#define da_end(a) \
dg_dynarr_end(a)
// returns (logical) number of elements currently in the array
#define da_count(a) \
dg_dynarr_count(a)
// get the current reserved capacity of the array
#define da_capacity(a) \
dg_dynarr_capacity(a)
// returns 1 if the array is empty, else 0
#define da_empty(a) \
dg_dynarr_empty(a)
// returns 1 if the last (re)allocation when inserting failed (Out Of Memory)
// or if the array has never allocated any memory yet, else 0
// deleting the contents when growing fails instead of keeping old may seem
// a bit uncool, but it's simple and OOM should rarely happen on modern systems
// anyway - after all you need to deplete both RAM and swap/pagefile.sys
#define da_oom(a) \
dg_dynarr_oom(a)
// sort a using the given qsort()-comparator cmp
// (just a slim wrapper around qsort())
#define da_sort(a, cmp) \
dg_dynarr_sort(a, cmp)
#endif // DG_DYNARR_NO_SHORTNAMES
// ######### Implementation of the actual macros (using the long names) ##########
// use like DG_DYNARR_TYPEDEF(int, MyIntArrType); MyIntArrType ia = {0}; dg_dynarr_push(ia, 42); ...
#define DG_DYNARR_TYPEDEF(TYPE, NewArrayTypeName) \
typedef struct { TYPE* p; dg__dynarr_md md; } NewArrayTypeName;
// makes sure the array is initialized and can be used.
// either do YourArray arr = {0}; or YourArray arr; dg_dynarr_init(arr);
#define dg_dynarr_init(a) \
dg__dynarr_init((void**)&(a).p, &(a).md, NULL, 0)
// this allows you to provide an external buffer that'll be used as long as it's big enough
// once you add more elements than buf can hold, fresh memory will be allocated on the heap
#define dg_dynarr_init_external(a, buf, buf_cap) \
dg__dynarr_init((void**)&(a).p, &(a).md, (buf), (buf_cap))
// use this to free the memory allocated by dg_dynarr
// Note: it is safe to add new elements to the array after dg_dynarr_free()
// it will allocate new memory, just like it would directly after dg_dynarr_init()
#define dg_dynarr_free(a) \
dg__dynarr_free((void**)&(a).p, &(a).md)
// add an element to the array (appended at the end)
#define dg_dynarr_push(a, v) \
(dg__dynarr_maybegrowadd(dg__dynarr_unp(a), 1) ? (((a).p[(a).md.cnt++] = (v)),0) : 0)
// add an element to the array (appended at the end)
// does the same as push, just for consistency with addn (like insert and insertn)
#define dg_dynarr_add(a, v) \
dg_dynarr_push((a), (v))
// append n elements to a and initialize them from array vals, doesn't return anything
// ! vals (and all other args) are evaluated multiple times !
#define dg_dynarr_addn(a, vals, n) do { \
DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL als vals to dg_dynarr_addn!"); \
if((vals)!=NULL && dg__dynarr_add(dg__dynarr_unp(a), n, 0)) { \
size_t i_=(a).md.cnt-(n), v_=0; \
while(i_<(a).md.cnt) (a).p[i_++]=(vals)[v_++]; \
} } DG__DYNARR_WHILE0
// add n elements to the end of the array and zeroe them with memset()
// returns pointer to first added element, NULL if out of memory (array is empty then)
#define dg_dynarr_addn_zeroed(a, n) \
(dg__dynarr_add(dg__dynarr_unp(a), (n), 1) ? &(a).p[(a).md.cnt-(size_t)(n)] : NULL)
// add n elements to the end of the array, which are uninitialized
// returns pointer to first added element, NULL if out of memory (array is empty then)
#define dg_dynarr_addn_uninit(a, n) \
(dg__dynarr_add(dg__dynarr_unp(a), (n), 0) ? &(a).p[(a).md.cnt-(size_t)(n)] : NULL)
// insert a single value v at index idx
#define dg_dynarr_insert(a, idx, v) \
(dg__dynarr_checkidxle((a),(idx)), \
dg__dynarr_insert(dg__dynarr_unp(a), (idx), 1, 0), \
(a).p[dg__dynarr_idx((a).md, (idx))] = (v))
// insert n elements into a at idx, initialize them from array vals
// doesn't return anything
// ! vals (and all other args) is evaluated multiple times !
#define dg_dynarr_insertn(a, idx, vals, n) do { \
DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL as vals to dg_dynarr_insertn!"); \
dg__dynarr_checkidxle((a),(idx)); \
if((vals)!=NULL && dg__dynarr_insert(dg__dynarr_unp(a), (idx), (n), 0)){ \
size_t i_=(idx), v_=0, e_=(idx)+(n); \
while(i_ < e_) (a).p[i_++] = (vals)[v_++]; \
}} DG__DYNARR_WHILE0
// insert n elements into a at idx and zeroe them with memset()
// returns pointer to first inserted element or NULL if out of memory
#define dg_dynarr_insertn_zeroed(a, idx, n) \
(dg__dynarr_checkidxle((a),(idx)), \
dg__dynarr_insert(dg__dynarr_unp(a), (idx), (n), 1) \
? &(a).p[dg__dynarr_idx((a).md, (idx))] : NULL)
// insert n uninitialized elements into a at idx;
// returns pointer to first inserted element or NULL if out of memory
#define dg_dynarr_insertn_uninit(a, idx, n) \
(dg__dynarr_checkidxle((a),(idx)), \
dg__dynarr_insert(dg__dynarr_unp(a), idx, n, 0) \
? &(a).p[dg__dynarr_idx((a).md, (idx))] : NULL)
// set a single value v at index idx - like "a.p[idx] = v;" but with checks (unless disabled)
#define dg_dynarr_set(a, idx, v) \
(dg__dynarr_checkidx((a),(idx)), \
(a).p[dg__dynarr_idx((a).md, (idx))] = (v))
// overwrite n elements of a, starting at idx, with values from array vals
// doesn't return anything
// ! vals (and all other args) is evaluated multiple times !
#define dg_dynarr_setn(a, idx, vals, n) do { \
DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL as vals to dg_dynarr_setn!"); \
size_t idx_=(idx); size_t end_=idx_+(size_t)n; \
dg__dynarr_checkidx((a),idx_); dg__dynarr_checkidx((a),end_-1); \
if((vals)!=NULL && idx_ < (a).md.cnt && end_ <= (a).md.cnt) { \
size_t v_=0; \
while(idx_ < end_) (a).p[idx_++] = (vals)[v_++]; \
}} DG__DYNARR_WHILE0
// delete the element at idx, moving all following elements (=> keeps order)
#define dg_dynarr_delete(a, idx) \
(dg__dynarr_checkidx((a),(idx)), dg__dynarr_delete(dg__dynarr_unp(a), (idx), 1))
// delete n elements starting at idx, moving all following elements (=> keeps order)
#define dg_dynarr_deleten(a, idx, n) \
(dg__dynarr_checkidx((a),(idx)), dg__dynarr_delete(dg__dynarr_unp(a), (idx), (n)))
// TODO: check whether idx+n < count?
// delete the element at idx, move the last element there (=> doesn't keep order)
#define dg_dynarr_deletefast(a, idx) \
(dg__dynarr_checkidx((a),(idx)), dg__dynarr_deletefast(dg__dynarr_unp(a), (idx), 1))
// delete n elements starting at idx, move the last n elements there (=> doesn't keep order)
#define dg_dynarr_deletenfast(a, idx, n) \
(dg__dynarr_checkidx((a),(idx)), dg__dynarr_deletefast(dg__dynarr_unp(a), idx, n))
// TODO: check whether idx+n < count?
// removes all elements from the array, but does not free the buffer
// (if you want to free the buffer too, just use dg_dynarr_free())
#define dg_dynarr_clear(a) \
((a).md.cnt=0)
// sets the logical number of elements in the array
// if cnt > dg_dynarr_count(a), the logical count will be increased accordingly
// and the new elements will be uninitialized
#define dg_dynarr_setcount(a, n) \
(dg__dynarr_maybegrow(dg__dynarr_unp(a), (n)) ? ((a).md.cnt = (n)) : 0)
// make sure the array can store cap elements without reallocating
// logical count remains unchanged
#define dg_dynarr_reserve(a, cap) \
dg__dynarr_maybegrow(dg__dynarr_unp(a), (cap))
// this makes sure a only uses as much memory as for its elements
// => maybe useful if a used to contain a huge amount of elements,
// but you deleted most of them and want to free some memory
// Note however that this implies an allocation and copying the remaining
// elements, so only do this if it frees enough memory to be worthwhile!
#define dg_dynarr_shrink_to_fit(a) \
dg__dynarr_shrink_to_fit(dg__dynarr_unp(a))
#if (DG_DYNARR_INDEX_CHECK_LEVEL == 1) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3)
// removes and returns the last element of the array
#define dg_dynarr_pop(a) \
(dg__dynarr_check_notempty((a), "Don't pop an empty array!"), \
(a).p[((a).md.cnt > 0) ? (--(a).md.cnt) : 0])
// returns the last element of the array
#define dg_dynarr_last(a) \
(dg__dynarr_check_notempty((a), "Don't call da_last() on an empty array!"), \
(a).p[((a).md.cnt > 0) ? ((a).md.cnt-1) : 0])
#elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 2)
// removes and returns the last element of the array
#define dg_dynarr_pop(a) \
(dg__dynarr_check_notempty((a), "Don't pop an empty array!"), \
(a).p[--(a).md.cnt])
// returns the last element of the array
#define dg_dynarr_last(a) \
(dg__dynarr_check_notempty((a), "Don't call da_last() on an empty array!"), \
(a).p[(a).md.cnt-1])
#else // invalid DG_DYNARR_INDEX_CHECK_LEVEL
#error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) !
#endif // DG_DYNARR_INDEX_CHECK_LEVEL
// returns the pointer *to* the last element of the array
// (in contrast to dg_dynarr_end() which returns a pointer *after* the last element)
// returns NULL if array is empty
#define dg_dynarr_lastptr(a) \
(((a).md.cnt > 0) ? ((a).p + (a).md.cnt - 1) : NULL)
// get element at index idx (like a.p[idx]), but with checks
// (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0)
#define dg_dynarr_get(a, idx) \
(dg__dynarr_checkidx((a),(idx)), (a).p[dg__dynarr_idx((a).md, (idx))])
// get pointer to element at index idx (like &a.p[idx]), but with checks
// (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0)
// if index-checks are disabled, it returns NULL on invalid index (else it asserts() before returning)
#define dg_dynarr_getptr(a, idx) \
(dg__dynarr_checkidx((a),(idx)), \
((size_t)(idx) < (a).md.cnt) ? ((a).p+(size_t)(idx)) : NULL)
// returns a pointer to the first element of the array
// (together with dg_dynarr_end() you can do C++-style iterating)
#define dg_dynarr_begin(a) \
((a).p)
// returns a pointer to the past-the-end element of the array
// Allows C++-style iterating, in case you're into that kind of thing:
// for(T *it=dg_dynarr_begin(a), *end=dg_dynarr_end(a); it!=end; ++it) foo(*it);
// (see dg_dynarr_lastptr() to get a pointer *to* the last element)
#define dg_dynarr_end(a) \
((a).p + (a).md.cnt)
// returns (logical) number of elements currently in the array
#define dg_dynarr_count(a) \
((a).md.cnt)
// get the current reserved capacity of the array
#define dg_dynarr_capacity(a) \
((a).md.cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB)
// returns 1 if the array is empty, else 0
#define dg_dynarr_empty(a) \
((a).md.cnt == 0)
// returns 1 if the last (re)allocation when inserting failed (Out Of Memory)
// or if the array has never allocated any memory yet, else 0
// deleting the contents when growing fails instead of keeping old may seem
// a bit uncool, but it's simple and OOM should rarely happen on modern systems
// anyway - after all you need to deplete both RAM and swap/pagefile.sys
// or deplete the address space, which /might/ happen with 32bit applications
// but probably not with 64bit (at least in the foreseeable future)
#define dg_dynarr_oom(a) \
((a).md.cap == 0)
// sort a using the given qsort()-comparator cmp
// (just a slim wrapper around qsort())
#define dg_dynarr_sort(a, cmp) \
qsort((a).p, (a).md.cnt, sizeof((a).p[0]), (cmp))
// ######### Implementation-Details that are not part of the API ##########
#include <stdlib.h> // size_t, malloc(), free(), realloc()
#include <string.h> // memset(), memcpy(), memmove()
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
size_t cnt; // logical number of elements
size_t cap; // cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB is actual capacity (in elements, *not* bytes!)
// if(cap & DG__DYNARR_SIZE_T_MSB) the current memory is not allocated by dg_dynarr,
// but was set with dg_dynarr_init_external()
// that's handy to give an array a base-element storage on the stack, for example
// TODO: alternatively, we could introduce a flag field to this struct and use that,
// so we don't have to calculate & everytime cap is needed
} dg__dynarr_md;
// I used to have the following in an enum, but MSVC assumes enums are always 32bit ints
static const size_t DG__DYNARR_SIZE_T_MSB = ((size_t)1) << (sizeof(size_t)*8 - 1);
static const size_t DG__DYNARR_SIZE_T_ALL_BUT_MSB = (((size_t)1) << (sizeof(size_t)*8 - 1))-1;
// "unpack" the elements of an array struct for use with helper functions
// (to void** arr, dg__dynarr_md* md, size_t itemsize)
#define dg__dynarr_unp(a) \
(void**)&(a).p, &(a).md, sizeof((a).p[0])
// MSVC warns about "conditional expression is constant" when using the
// do { ... } while(0) idiom in macros..
#ifdef _MSC_VER
#if _MSC_VER >= 1400 // MSVC 2005 and newer
// people claim MSVC 2005 and newer support __pragma, even though it's only documented
// for 2008+ (https://msdn.microsoft.com/en-us/library/d9x1s805%28v=vs.90%29.aspx)
// the following workaround is based on
// http://cnicholson.net/2009/03/stupid-c-tricks-dowhile0-and-c4127/
#define DG__DYNARR_WHILE0 \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
while(0) \
__pragma(warning(pop))
#else // older MSVC versions don't support __pragma - I heard this helps for them
#define DG__DYNARR_WHILE0 while(0,0)
#endif
#else // other compilers
#define DG__DYNARR_WHILE0 while(0)
#endif // _MSC_VER
#if (DG_DYNARR_INDEX_CHECK_LEVEL == 2) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3)
#define dg__dynarr_checkidx(a,i) \
DG_DYNARR_ASSERT((size_t)i < a.md.cnt, "index out of bounds!")
// special case for insert operations: == cnt is also ok, insert will append then
#define dg__dynarr_checkidxle(a,i) \
DG_DYNARR_ASSERT((size_t)i <= a.md.cnt, "index out of bounds!")
#define dg__dynarr_check_notempty(a, msg) \
DG_DYNARR_ASSERT(a.md.cnt > 0, msg)
#elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 1)
// no assertions that check if index is valid
#define dg__dynarr_checkidx(a,i) (void)0
#define dg__dynarr_checkidxle(a,i) (void)0
#define dg__dynarr_check_notempty(a, msg) (void)0
#else // invalid DG_DYNARR_INDEX_CHECK_LEVEL
#error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) !
#endif // DG_DYNARR_INDEX_CHECK_LEVEL
#if (DG_DYNARR_INDEX_CHECK_LEVEL == 1) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3)
// the given index, if valid, else 0
#define dg__dynarr_idx(md,i) \
(((size_t)(i) < md.cnt) ? (size_t)(i) : 0)
#elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 2)
// don't check and default to 0 if invalid, but just use the given value
#define dg__dynarr_idx(md,i) (size_t)(i)
#else // invalid DG_DYNARR_INDEX_CHECK_LEVEL
#error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) !
#endif // DG_DYNARR_INDEX_CHECK_LEVEL
// the functions allocating/freeing memory are not implemented inline, but
// in the #ifdef DG_DYNARR_IMPLEMENTATION section
// one reason is that dg__dynarr_grow has the most code in it, the other is
// that windows has weird per-dll heaps so free() or realloc() should be
// called from code in the same dll that allocated the memory - these kind
// of wrapper functions that end up compiled into the exe or *one* dll
// (instead of inline functions compiled into everything) should ensure that.
DG_DYNARR_DEF void
dg__dynarr_free(void** p, dg__dynarr_md* md);
DG_DYNARR_DEF void
dg__dynarr_shrink_to_fit(void** arr, dg__dynarr_md* md, size_t itemsize);
// grow array to have enough space for at least min_needed elements
// if it fails (OOM), the array will be deleted, a.p will be NULL, a.md.cap and a.md.cnt will be 0
// and the functions returns 0; else (on success) it returns 1
DG_DYNARR_DEF int
dg__dynarr_grow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed);
// the following functions are implemented inline, because they're quite short
// and mosty implemented in functions so the macros don't get too ugly
DG_DYNARR_INLINE void
dg__dynarr_init(void** p, dg__dynarr_md* md, void* buf, size_t buf_cap)
{
*p = buf;
md->cnt = 0;
if(buf == NULL) md->cap = 0;
else md->cap = (DG__DYNARR_SIZE_T_MSB | buf_cap);
}
DG_DYNARR_INLINE int
dg__dynarr_maybegrow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed)
{
if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) >= min_needed) return 1;
else return dg__dynarr_grow(arr, md, itemsize, min_needed);
}
DG_DYNARR_INLINE int
dg__dynarr_maybegrowadd(void** arr, dg__dynarr_md* md, size_t itemsize, size_t num_add)
{
size_t min_needed = md->cnt+num_add;
if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) >= min_needed) return 1;
else return dg__dynarr_grow(arr, md, itemsize, min_needed);
}
DG_DYNARR_INLINE int
dg__dynarr_insert(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n, int init0)
{
// allow idx == md->cnt to append
size_t oldCount = md->cnt;
size_t newCount = oldCount+n;
if(idx <= oldCount && dg__dynarr_maybegrow(arr, md, itemsize, newCount))
{
unsigned char* p = (unsigned char*)*arr; // *arr might have changed in dg__dynarr_grow()!
// move all existing items after a[idx] to a[idx+n]
if(idx < oldCount) memmove(p+(idx+n)*itemsize, p+idx*itemsize, itemsize*(oldCount - idx));
// if the memory is supposed to be zeroed, do that
if(init0) memset(p+idx*itemsize, 0, n*itemsize);
md->cnt = newCount;
return 1;
}
return 0;
}
DG_DYNARR_INLINE int
dg__dynarr_add(void** arr, dg__dynarr_md* md, size_t itemsize, size_t n, int init0)
{
size_t cnt = md->cnt;
if(dg__dynarr_maybegrow(arr, md, itemsize, cnt+n))
{
unsigned char* p = (unsigned char*)*arr; // *arr might have changed in dg__dynarr_grow()!
// if the memory is supposed to be zeroed, do that
if(init0) memset(p+cnt*itemsize, 0, n*itemsize);
md->cnt += n;
return 1;
}
return 0;
}
DG_DYNARR_INLINE void
dg__dynarr_delete(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n)
{
size_t cnt = md->cnt;
if(idx < cnt)
{
if(idx+n >= cnt) md->cnt = idx; // removing last element(s) => just reduce count
else
{
unsigned char* p = (unsigned char*)*arr;
// move all items following a[idx+n] to a[idx]
memmove(p+itemsize*idx, p+itemsize*(idx+n), itemsize*(cnt - (idx+n)));
md->cnt -= n;
}
}
}
DG_DYNARR_INLINE void
dg__dynarr_deletefast(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n)
{
size_t cnt = md->cnt;
if(idx < cnt)
{
if(idx+n >= cnt) md->cnt = idx; // removing last element(s) => just reduce count
else
{
unsigned char* p = (unsigned char*)*arr;
// copy the last n items to a[idx] - but handle the case that
// the array has less than n elements left after the deleted elements
size_t numItemsAfterDeleted = cnt - (idx+n);
size_t m = (n < numItemsAfterDeleted) ? n : numItemsAfterDeleted;
memcpy(p+itemsize*idx, p+itemsize*(cnt - m), itemsize*m);
md->cnt -= n;
}
}
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // DG__DYNARR_H
// ############## Implementation of non-inline functions ##############
#ifdef DG_DYNARR_IMPLEMENTATION
// by default, C's malloc(), realloc() and free() is used to allocate/free heap memory.
// you can #define DG_DYNARR_MALLOC, DG_DYNARR_REALLOC and DG_DYNARR_FREE
// to provide alternative implementations like Win32 Heap(Re)Alloc/HeapFree
//
#ifndef DG_DYNARR_MALLOC
#define DG_DYNARR_MALLOC(elemSize, numElems) malloc(elemSize*numElems)
// oldNumElems is not used here, but maybe you need it for your allocator
// to copy the old elements over
#define DG_DYNARR_REALLOC(ptr, elemSize, oldNumElems, newCapacity) \
realloc(ptr, elemSize*newCapacity);
#define DG_DYNARR_FREE(ptr) free(ptr)
#endif
// you can #define DG_DYNARR_OUT_OF_MEMORY to some code that will be executed
// if allocating memory fails
#ifndef DG_DYNARR_OUT_OF_MEMORY
#define DG_DYNARR_OUT_OF_MEMORY DG_DYNARR_ASSERT(0, "Out of Memory!");
#endif
#ifdef __cplusplus
extern "C" {
#endif
DG_DYNARR_DEF void
dg__dynarr_free(void** p, dg__dynarr_md* md)
{
// only free memory if it doesn't point to external memory
if(!(md->cap & DG__DYNARR_SIZE_T_MSB))
{
DG_DYNARR_FREE(*p);
*p = NULL;
md->cap = 0;
}
md->cnt = 0;
}
DG_DYNARR_DEF int
dg__dynarr_grow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed)
{
size_t cap = md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB;
DG_DYNARR_ASSERT(min_needed > cap, "dg__dynarr_grow() should only be called if storage actually needs to grow!");
if(min_needed < DG__DYNARR_SIZE_T_MSB)
{
size_t newcap = (cap > 4) ? (2*cap) : 8; // allocate for at least 8 elements
// make sure not to set DG__DYNARR_SIZE_T_MSB (unlikely anyway)
if(newcap >= DG__DYNARR_SIZE_T_MSB) newcap = DG__DYNARR_SIZE_T_MSB-1;
if(min_needed > newcap) newcap = min_needed;
// the memory was allocated externally, don't free it, just copy contents
if(md->cap & DG__DYNARR_SIZE_T_MSB)
{
void* p = DG_DYNARR_MALLOC(itemsize, newcap);
if(p != NULL) memcpy(p, *arr, itemsize*md->cnt);
*arr = p;
}
else
{
void* p = DG_DYNARR_REALLOC(*arr, itemsize, md->cnt, newcap);
if(p == NULL) DG_DYNARR_FREE(*arr); // realloc failed, at least don't leak memory
*arr = p;
}
// TODO: handle OOM by setting highest bit of count and keeping old data?
if(*arr) md->cap = newcap;
else
{
md->cap = 0;
md->cnt = 0;
DG_DYNARR_OUT_OF_MEMORY ;
return 0;
}
return 1;
}
DG_DYNARR_ASSERT(min_needed < DG__DYNARR_SIZE_T_MSB, "Arrays must stay below SIZE_T_MAX/2 elements!");
return 0;
}
DG_DYNARR_DEF void
dg__dynarr_shrink_to_fit(void** arr, dg__dynarr_md* md, size_t itemsize)
{
// only do this if we allocated the memory ourselves
if(!(md->cap & DG__DYNARR_SIZE_T_MSB))
{
size_t cnt = md->cnt;
if(cnt == 0) dg__dynarr_free(arr, md);
else if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) > cnt)
{
void* p = DG_DYNARR_MALLOC(itemsize, cnt);
if(p != NULL)
{
memcpy(p, *arr, cnt*itemsize);
md->cap = cnt;
DG_DYNARR_FREE(*arr);
*arr = p;
}
}
}
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // DG_DYNARR_IMPLEMENTATION

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,562 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2016-2017 Daniel Gibson
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Local header for the OpenGL4 refresher.
*
* =======================================================================
*/
#ifndef SRC_CLIENT_REFRESH_GL4_HEADER_LOCAL_H_
#define SRC_CLIENT_REFRESH_GL4_HEADER_LOCAL_H_
#ifdef IN_IDE_PARSER
// this is just a hack to get proper auto-completion in IDEs:
// using system headers for their parsers/indexers but glad for real build
// (in glad glFoo is just a #define to glad_glFoo or sth, which screws up autocompletion)
// (you may have to configure your IDE to #define IN_IDE_PARSER, but not for building!)
#ifdef YQ2_GL3_GLES3
#include <GLES3/gl32.h>
#else // desktop GL4
#define GL_GLEXT_PROTOTYPES 1
#include <GL/gl.h>
#include <GL/glext.h>
#endif
#else
#ifdef YQ2_GL3_GLES3
#include "../glad-gles3/include/glad/glad.h"
// yes, this is a bit hacky, but it works :-P
#define glDepthRange glDepthRangef
#else // desktop GL4
#include "../glad/include/glad/glad.h"
#endif
#endif
#include "../../ref_shared.h"
#include "HandmadeMath.h"
#if 0 // only use this for development ..
#define STUB_ONCE(msg) do { \
static int show=1; \
if(show) { \
show = 0; \
R_Printf(PRINT_ALL, "STUB: %s() %s\n", __FUNCTION__, msg); \
} \
} while(0);
#else // .. so make this a no-op in released code
#define STUB_ONCE(msg)
#endif
// a wrapper around glVertexAttribPointer() to stay sane
// (caller doesn't have to cast to GLintptr and then void*)
static inline void
qglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset)
{
glVertexAttribPointer(index, size, type, normalized, stride, (const void*)offset);
}
static inline void
qglVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset)
{
glVertexAttribIPointer(index, size, type, stride, (void*)offset);
}
// attribute locations for vertex shaders
enum {
GL4_ATTRIB_POSITION = 0,
GL4_ATTRIB_TEXCOORD = 1, // for normal texture
GL4_ATTRIB_LMTEXCOORD = 2, // for lightmap
GL4_ATTRIB_COLOR = 3, // per-vertex color
GL4_ATTRIB_NORMAL = 4, // vertex normal
GL4_ATTRIB_LIGHTFLAGS = 5 // uint, each set bit means "dyn light i affects this surface"
};
// always using RGBA now, GLES3 on RPi4 doesn't work otherwise
// and I think all modern GPUs prefer 4byte pixels over 3bytes
static const int gl4_solid_format = GL_RGBA;
static const int gl4_alpha_format = GL_RGBA;
static const int gl4_tex_solid_format = GL_RGBA;
static const int gl4_tex_alpha_format = GL_RGBA;
extern unsigned gl4_rawpalette[256];
extern unsigned d_8to24table[256];
typedef struct
{
const char *renderer_string;
const char *vendor_string;
const char *version_string;
const char *glsl_version_string;
int major_version;
int minor_version;
// ----
qboolean anisotropic; // is GL_EXT_texture_filter_anisotropic supported?
qboolean debug_output; // is GL_ARB_debug_output supported?
qboolean stencil; // Do we have a stencil buffer?
qboolean useBigVBO; // workaround for AMDs windows driver for fewer calls to glBufferData()
// ----
float max_anisotropy;
} gl4config_t;
typedef struct
{
GLuint shaderProgram;
GLint uniVblend;
GLint uniLmScalesOrTime; // for 3D it's lmScales, for 2D underwater PP it's time
hmm_vec4 lmScales[4];
} gl4ShaderInfo_t;
typedef struct
{
GLfloat gamma;
GLfloat intensity;
GLfloat intensity2D; // for HUD, menus etc
// entries of std430 UBOs are aligned to multiples of their own size
// so we'll need to pad accordingly for following vec4
GLfloat _padding;
hmm_vec4 color;
} gl4UniCommon_t;
typedef struct
{
hmm_mat4 transMat4;
} gl4Uni2D_t;
typedef struct
{
hmm_mat4 transProjViewMat4; // gl4state.projMat3D * gl4state.viewMat3D - so we don't have to do this in the shader
hmm_mat4 transModelMat4;
GLfloat scroll; // for SURF_FLOWING
GLfloat time; // for warping surfaces like water & possibly other things
GLfloat alpha; // for translucent surfaces (water, glass, ..)
GLfloat overbrightbits; // gl4_overbrightbits, applied to lightmaps (and elsewhere to models)
GLfloat particleFadeFactor; // gl4_particle_fade_factor, higher => less fading out towards edges
GLfloat lightScaleForTurb; // surfaces with SURF_DRAWTURB (water, lava) don't have lightmaps, use this instead
GLfloat _padding[2]; // again, some padding to ensure this has right size
} gl4Uni3D_t;
extern const hmm_mat4 gl4_identityMat4;
typedef struct
{
vec3_t origin;
GLfloat _padding;
vec3_t color;
GLfloat intensity;
} gl4UniDynLight;
typedef struct
{
gl4UniDynLight dynLights[MAX_DLIGHTS];
GLuint numDynLights;
GLfloat _padding[3];
} gl4UniLights_t;
enum {
// width and height used to be 128, so now we should be able to get the same lightmap data
// that used 32 lightmaps before into one, so 4 lightmaps should be enough
BLOCK_WIDTH = 1024,
BLOCK_HEIGHT = 512,
LIGHTMAP_BYTES = 4,
MAX_LIGHTMAPS = 4,
MAX_LIGHTMAPS_PER_SURFACE = MAXLIGHTMAPS // 4
};
typedef struct
{
// TODO: what of this do we need?
qboolean fullscreen;
int prev_mode;
// each lightmap consists of 4 sub-lightmaps allowing changing shadows on the same surface
// used for switching on/off light and stuff like that.
// most surfaces only have one really and the remaining for are filled with dummy data
GLuint lightmap_textureIDs[MAX_LIGHTMAPS][MAX_LIGHTMAPS_PER_SURFACE]; // instead of lightmap_textures+i use lightmap_textureIDs[i]
GLuint currenttexture; // bound to GL_TEXTURE0
int currentlightmap; // lightmap_textureIDs[currentlightmap] bound to GL_TEXTURE1
GLuint currenttmu; // GL_TEXTURE0 or GL_TEXTURE1
// FBO for postprocess effects (like under-water-warping)
GLuint ppFBO;
GLuint ppFBtex; // ppFBO's texture for color buffer
int ppFBtexWidth, ppFBtexHeight;
GLuint ppFBrbo; // ppFBO's renderbuffer object for depth and stencil buffer
qboolean ppFBObound; // is it currently bound (rendered to)?
//float camera_separation;
//enum stereo_modes stereo_mode;
GLuint currentVAO;
GLuint currentVBO;
GLuint currentEBO;
GLuint currentShaderProgram;
GLuint currentUBO;
// NOTE: make sure si2D is always the first shaderInfo (or adapt GL4_ShutdownShaders())
gl4ShaderInfo_t si2D; // shader for rendering 2D with textures
gl4ShaderInfo_t si2Dcolor; // shader for rendering 2D with flat colors
gl4ShaderInfo_t si2DpostProcess; // shader to render postprocess FBO, when *not* underwater
gl4ShaderInfo_t si2DpostProcessWater; // shader to apply water-warp postprocess effect
gl4ShaderInfo_t si3Dlm; // a regular opaque face (e.g. from brush) with lightmap
// TODO: lm-only variants for gl_lightmap 1
gl4ShaderInfo_t si3Dtrans; // transparent is always w/o lightmap
gl4ShaderInfo_t si3DcolorOnly; // used for beams - no lightmaps
gl4ShaderInfo_t si3Dturb; // for water etc - always without lightmap
gl4ShaderInfo_t si3DlmFlow; // for flowing/scrolling things with lightmap (conveyor, ..?)
gl4ShaderInfo_t si3DtransFlow; // for transparent flowing/scrolling things (=> no lightmap)
gl4ShaderInfo_t si3Dsky; // guess what..
gl4ShaderInfo_t si3Dsprite; // for sprites
gl4ShaderInfo_t si3DspriteAlpha; // for sprites with alpha-testing
gl4ShaderInfo_t si3Dalias; // for models
gl4ShaderInfo_t si3DaliasColor; // for models w/ flat colors
// NOTE: make sure siParticle is always the last shaderInfo (or adapt GL4_ShutdownShaders())
gl4ShaderInfo_t siParticle; // for particles. surprising, right?
GLuint vao3D, vbo3D; // for brushes etc, using 10 floats and one uint as vertex input (x,y,z, s,t, lms,lmt, normX,normY,normZ ; lightFlags)
// the next two are for gl4config.useBigVBO == true
int vbo3Dsize;
int vbo3DcurOffset;
GLuint vaoAlias, vboAlias, eboAlias; // for models, using 9 floats as (x,y,z, s,t, r,g,b,a)
GLuint vaoParticle, vboParticle; // for particles, using 9 floats (x,y,z, size,distance, r,g,b,a)
// UBOs and their data
gl4UniCommon_t uniCommonData;
gl4Uni2D_t uni2DData;
gl4Uni3D_t uni3DData;
gl4UniLights_t uniLightsData;
GLuint uniCommonUBO;
GLuint uni2DUBO;
GLuint uni3DUBO;
GLuint uniLightsUBO;
hmm_mat4 projMat3D;
hmm_mat4 viewMat3D;
} gl4state_t;
extern gl4config_t gl4config;
extern gl4state_t gl4state;
extern viddef_t vid;
extern refdef_t gl4_newrefdef;
extern int gl4_visframecount; /* bumped when going to a new PVS */
extern int gl4_framecount; /* used for dlight push checking */
extern int gl4_viewcluster, gl4_viewcluster2, gl4_oldviewcluster, gl4_oldviewcluster2;
extern int c_brush_polys, c_alias_polys;
extern qboolean IsHighDPIaware;
/* NOTE: struct image_s* is what re.RegisterSkin() etc return so no gl4image_s!
* (I think the client only passes the pointer around and doesn't know the
* definition of this struct, so this being different from struct image_s
* in ref_gl should be ok)
*/
typedef struct image_s
{
char name[MAX_QPATH]; /* game path, including extension */
imagetype_t type;
int width, height; /* source image */
//int upload_width, upload_height; /* after power of two and picmip */
int registration_sequence; /* 0 = free */
struct msurface_s *texturechain; /* for sort-by-texture world drawing */
GLuint texnum; /* gl texture binding */
float sl, tl, sh, th; /* 0,0 - 1,1 unless part of the scrap */
// qboolean scrap; // currently unused
qboolean has_alpha;
qboolean is_lava; // DG: added for lava brightness hack
} gl4image_t;
enum {MAX_GL4TEXTURES = 1024};
// include this down here so it can use gl4image_t
#include "model.h"
typedef struct
{
int internal_format;
int current_lightmap_texture; // index into gl4state.lightmap_textureIDs[]
//msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; - no more lightmap chains, lightmaps are rendered multitextured
int allocated[BLOCK_WIDTH];
/* the lightmap texture data needs to be kept in
main memory so texsubimage can update properly */
byte lightmap_buffers[MAX_LIGHTMAPS_PER_SURFACE][4 * BLOCK_WIDTH * BLOCK_HEIGHT];
} gl4lightmapstate_t;
extern gl4model_t *gl4_worldmodel;
extern float gl4depthmin, gl4depthmax;
extern cplane_t frustum[4];
extern vec3_t gl4_origin;
extern gl4image_t *gl4_notexture; /* use for bad textures */
extern gl4image_t *gl4_particletexture; /* little dot for particles */
extern int gl_filter_min;
extern int gl_filter_max;
static inline void
GL4_UseProgram(GLuint shaderProgram)
{
if(shaderProgram != gl4state.currentShaderProgram)
{
gl4state.currentShaderProgram = shaderProgram;
glUseProgram(shaderProgram);
}
}
static inline void
GL4_BindVAO(GLuint vao)
{
if(vao != gl4state.currentVAO)
{
gl4state.currentVAO = vao;
glBindVertexArray(vao);
}
}
static inline void
GL4_BindVBO(GLuint vbo)
{
if(vbo != gl4state.currentVBO)
{
gl4state.currentVBO = vbo;
glBindBuffer(GL_ARRAY_BUFFER, vbo);
}
}
static inline void
GL4_BindEBO(GLuint ebo)
{
if(ebo != gl4state.currentEBO)
{
gl4state.currentEBO = ebo;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
}
}
extern void GL4_BufferAndDraw3D(const gl4_3D_vtx_t* verts, int numVerts, GLenum drawMode);
extern void GL4_RotateForEntity(entity_t *e);
// gl4_sdl.c
extern int GL4_InitContext(void* win);
extern void GL4_GetDrawableSize(int* width, int* height);
extern int GL4_PrepareForWindow(void);
extern qboolean GL4_IsVsyncActive(void);
extern void GL4_EndFrame(void);
extern void GL4_SetVsync(void);
extern void GL4_ShutdownContext(void);
// gl4_misc.c
extern void GL4_InitParticleTexture(void);
extern void GL4_ScreenShot(void);
extern void GL4_SetDefaultState(void);
// gl4_model.c
extern int registration_sequence;
extern void GL4_Mod_Init(void);
extern void GL4_Mod_FreeAll(void);
extern void GL4_BeginRegistration(char *model);
extern struct model_s * GL4_RegisterModel(char *name);
extern void GL4_EndRegistration(void);
extern void GL4_Mod_Modellist_f(void);
extern const byte* GL4_Mod_ClusterPVS(int cluster, const gl4model_t *model);
// gl4_draw.c
extern void GL4_Draw_InitLocal(void);
extern void GL4_Draw_ShutdownLocal(void);
extern gl4image_t * GL4_Draw_FindPic(char *name);
extern void GL4_Draw_GetPicSize(int *w, int *h, char *pic);
extern void GL4_Draw_PicScaled(int x, int y, char *pic, float factor);
extern void GL4_Draw_StretchPic(int x, int y, int w, int h, char *pic);
extern void GL4_Draw_CharScaled(int x, int y, int num, float scale);
extern void GL4_Draw_TileClear(int x, int y, int w, int h, char *pic);
extern void GL4_DrawFrameBufferObject(int x, int y, int w, int h, GLuint fboTexture, const float v_blend[4]);
extern void GL4_Draw_Fill(int x, int y, int w, int h, int c);
extern void GL4_Draw_FadeScreen(void);
extern void GL4_Draw_Flash(const float color[4], float x, float y, float w, float h);
extern void GL4_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, const byte *data, int bits);
// gl4_image.c
static inline void
GL4_SelectTMU(GLenum tmu)
{
if(gl4state.currenttmu != tmu)
{
glActiveTexture(tmu);
gl4state.currenttmu = tmu;
}
}
extern void GL4_TextureMode(char *string);
extern void GL4_Bind(GLuint texnum);
extern void GL4_BindLightmap(int lightmapnum);
extern gl4image_t *GL4_LoadPic(char *name, byte *pic, int width, int realwidth,
int height, int realheight, size_t data_size,
imagetype_t type, int bits);
extern gl4image_t *GL4_FindImage(const char *name, imagetype_t type);
extern gl4image_t *GL4_RegisterSkin(char *name);
extern void GL4_ShutdownImages(void);
extern void GL4_FreeUnusedImages(void);
extern qboolean GL4_ImageHasFreeSpace(void);
extern void GL4_ImageList_f(void);
// gl4_light.c
extern int r_dlightframecount;
extern void GL4_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node,
int r_dlightframecount);
extern void GL4_PushDlights(void);
extern void GL4_LightPoint(entity_t *currententity, vec3_t p, vec3_t color);
extern void GL4_BuildLightMap(msurface_t *surf, int offsetInLMbuf, int stride);
// gl4_lightmap.c
#define GL_LIGHTMAP_FORMAT GL_RGBA
extern void GL4_LM_InitBlock(void);
extern void GL4_LM_UploadBlock(void);
extern qboolean GL4_LM_AllocBlock(int w, int h, int *x, int *y);
extern void GL4_LM_BuildPolygonFromSurface(gl4model_t *currentmodel, msurface_t *fa);
extern void GL4_LM_CreateSurfaceLightmap(msurface_t *surf);
extern void GL4_LM_BeginBuildingLightmaps(gl4model_t *m);
extern void GL4_LM_EndBuildingLightmaps(void);
// gl4_warp.c
extern void GL4_EmitWaterPolys(msurface_t *fa);
extern void GL4_SubdivideSurface(msurface_t *fa, gl4model_t* loadmodel);
extern void GL4_SetSky(char *name, float rotate, vec3_t axis);
extern void GL4_DrawSkyBox(void);
extern void GL4_ClearSkyBox(void);
extern void GL4_AddSkySurface(msurface_t *fa);
// gl4_surf.c
extern void GL4_SurfInit(void);
extern void GL4_SurfShutdown(void);
extern void GL4_DrawGLPoly(msurface_t *fa);
extern void GL4_DrawGLFlowingPoly(msurface_t *fa);
extern void GL4_DrawTriangleOutlines(void);
extern void GL4_DrawAlphaSurfaces(void);
extern void GL4_DrawBrushModel(entity_t *e, gl4model_t *currentmodel);
extern void GL4_DrawWorld(void);
extern void GL4_MarkLeaves(void);
// gl4_mesh.c
extern void GL4_DrawAliasModel(entity_t *e);
extern void GL4_ResetShadowAliasModels(void);
extern void GL4_DrawAliasShadows(void);
extern void GL4_ShutdownMeshes(void);
// gl4_shaders.c
extern qboolean GL4_RecreateShaders(void);
extern qboolean GL4_InitShaders(void);
extern void GL4_ShutdownShaders(void);
extern void GL4_UpdateUBOCommon(void);
extern void GL4_UpdateUBO2D(void);
extern void GL4_UpdateUBO3D(void);
extern void GL4_UpdateUBOLights(void);
// ############ Cvars ###########
extern cvar_t *gl_msaa_samples;
extern cvar_t *r_vsync;
extern cvar_t *r_retexturing;
extern cvar_t *r_scale8bittextures;
extern cvar_t *vid_fullscreen;
extern cvar_t *r_mode;
extern cvar_t *r_customwidth;
extern cvar_t *r_customheight;
extern cvar_t *r_2D_unfiltered;
extern cvar_t *r_videos_unfiltered;
extern cvar_t *gl_nolerp_list;
extern cvar_t *r_lerp_list;
extern cvar_t *gl_nobind;
extern cvar_t *r_lockpvs;
extern cvar_t *r_novis;
extern cvar_t *r_cull;
extern cvar_t *gl_zfix;
extern cvar_t *r_fullbright;
extern cvar_t *r_norefresh;
extern cvar_t *gl_lefthand;
extern cvar_t *r_gunfov;
extern cvar_t *r_farsee;
extern cvar_t *r_drawworld;
extern cvar_t *vid_gamma;
extern cvar_t *gl4_intensity;
extern cvar_t *gl4_intensity_2D;
extern cvar_t *gl_anisotropic;
extern cvar_t *gl_texturemode;
extern cvar_t *r_lightlevel;
extern cvar_t *gl4_overbrightbits;
extern cvar_t *gl4_particle_fade_factor;
extern cvar_t *gl4_particle_square;
extern cvar_t *gl4_colorlight;
extern cvar_t *gl_polyblend;
extern cvar_t *r_modulate;
extern cvar_t *gl_lightmap;
extern cvar_t *gl_shadows;
extern cvar_t *r_fixsurfsky;
extern cvar_t *r_palettedtexture;
extern cvar_t *r_validation;
extern cvar_t *gl4_debugcontext;
#endif /* SRC_CLIENT_REFRESH_GL4_HEADER_LOCAL_H_ */

View file

@ -0,0 +1,163 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Header for the model stuff.
*
* =======================================================================
*/
#ifndef SRC_CLIENT_REFRESH_GL4_HEADER_MODEL_H_
#define SRC_CLIENT_REFRESH_GL4_HEADER_MODEL_H_
// used for vertex array elements when drawing brushes, sprites, sky and more
// (ok, it has the layout used for rendering brushes, but is not used there)
typedef struct gl4_3D_vtx_s {
vec3_t pos;
float texCoord[2];
float lmTexCoord[2]; // lightmap texture coordinate (sometimes unused)
vec3_t normal;
GLuint lightFlags; // bit i set means: dynlight i affects surface
} gl4_3D_vtx_t;
// used for vertex array elements when drawing models
typedef struct gl4_alias_vtx_s {
GLfloat pos[3];
GLfloat texCoord[2];
GLfloat color[4];
} gl4_alias_vtx_t;
/* in memory representation */
typedef struct glpoly_s
{
struct glpoly_s *next;
struct glpoly_s *chain;
int numverts;
int flags; /* for SURF_UNDERWATER (not needed anymore?) */
gl4_3D_vtx_t vertices[4]; /* variable sized */
} glpoly_t;
typedef struct msurface_s
{
int visframe; /* should be drawn when node is crossed */
cplane_t *plane;
int flags;
int firstedge; /* look up in model->surfedges[], negative numbers */
int numedges; /* are backwards edges */
short texturemins[2];
short extents[2];
int light_s, light_t; /* gl lightmap coordinates */
int dlight_s, dlight_t; /* gl lightmap coordinates for dynamic lightmaps */
glpoly_t *polys; /* multiple if warped */
struct msurface_s *texturechain;
// struct msurface_s *lightmapchain; not used/needed anymore
mtexinfo_t *texinfo;
/* lighting info */
int dlightframe;
int dlightbits;
int lightmaptexturenum;
byte styles[MAXLIGHTMAPS]; // MAXLIGHTMAPS = MAX_LIGHTMAPS_PER_SURFACE (defined in local.h)
// I think cached_light is not used/needed anymore
//float cached_light[MAXLIGHTMAPS]; /* values currently used in lightmap */
byte *samples; /* [numstyles*surfsize] */
} msurface_t;
/* Whole model */
// this, must be struct model_s, not gl4model_s,
// because struct model_s* is returned by re.RegisterModel()
typedef struct model_s
{
char name[MAX_QPATH];
int registration_sequence;
modtype_t type;
int numframes;
int flags;
/* volume occupied by the model graphics */
vec3_t mins, maxs;
float radius;
/* solid volume for clipping */
qboolean clipbox;
vec3_t clipmins, clipmaxs;
/* brush model */
int firstmodelsurface, nummodelsurfaces;
int lightmap; /* only for submodels */
int numsubmodels;
struct model_s *submodels;
int numplanes;
cplane_t *planes;
int numleafs; /* number of visible leafs, not counting 0 */
mleaf_t *leafs;
int numvertexes;
mvertex_t *vertexes;
int numedges;
medge_t *edges;
int numnodes;
int firstnode;
mnode_t *nodes;
int numtexinfo;
mtexinfo_t *texinfo;
int numsurfaces;
msurface_t *surfaces;
int numsurfedges;
int *surfedges;
int nummarksurfaces;
msurface_t **marksurfaces;
dvis_t *vis;
byte *lightdata;
/* for alias models and skins */
gl4image_t *skins[MAX_MD2SKINS];
int extradatasize;
void *extradata;
// submodules
vec3_t origin; // for sounds or lights
} gl4model_t;
#endif /* SRC_CLIENT_REFRESH_GL4_HEADER_MODEL_H_ */

View file

@ -0,0 +1,226 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Header shared between different refreshers (but not with client)
*
* =======================================================================
*/
#ifndef SRC_CLIENT_REFRESH_REF_SHARED_H_
#define SRC_CLIENT_REFRESH_REF_SHARED_H_
#include "../vid/header/ref.h"
#ifdef _MSC_VER
#include <malloc.h>
#define YQ2_VLA(TYPE, VARNAME, NUMELEMS) \
TYPE * VARNAME = (TYPE *) _malloca(sizeof(TYPE) * NUMELEMS)
#define YQ2_VLAFREE(VARNAME) \
_freea(VARNAME); VARNAME=NULL;
#else // other compilers hopefully support C99 VLAs (gcc/mingw and clang do)
#define YQ2_VLA(TYPE, VARNAME, NUMELEMS) \
TYPE VARNAME[NUMELEMS]
#define YQ2_VLAFREE(VARNAME)
#endif
/*
* skins will be outline flood filled and mip mapped
* pics and sprites with alpha will be outline flood filled
* pic won't be mip mapped
*
* model skin
* sprite frame
* wall texture
* pic
*/
typedef enum
{
it_skin,
it_sprite,
it_wall,
it_pic,
it_sky
} imagetype_t;
typedef enum
{
mod_bad,
mod_brush,
mod_sprite,
mod_alias
} modtype_t;
#define MAX_LBM_HEIGHT 480
extern void R_Printf(int level, const char* msg, ...) PRINTF_ATTR(2, 3);
/* Shared images load */
typedef struct image_s* (*loadimage_t)(const char *name, byte *pic, int width, int realwidth,
int height, int realheight, size_t data_size, imagetype_t type, int bits);
extern struct image_s* LoadWal(const char *origname, imagetype_t type, loadimage_t load_image);
extern struct image_s* LoadM8(const char *origname, imagetype_t type, loadimage_t load_image);
extern struct image_s* LoadM32(const char *origname, imagetype_t type, loadimage_t load_image);
extern void FixFileExt(const char *origname, const char *ext, char *filename, size_t size);
extern void GetPCXPalette(byte **colormap, unsigned *d_8to24table);
extern void LoadPCX(const char *origname, byte **pic, byte **palette, int *width, int *height);
extern void GetPCXInfo(const char *origname, int *width, int *height);
extern void GetWalInfo(const char *name, int *width, int *height);
extern void GetM8Info(const char *name, int *width, int *height);
extern void GetM32Info(const char *name, int *width, int *height);
extern qboolean ResizeSTB(const byte *input_pixels, int input_width, int input_height,
byte *output_pixels, int output_width, int output_height);
extern void SmoothColorImage(unsigned *dst, size_t size, size_t rstep);
extern void scale2x(const byte *src, byte *dst, int width, int height);
extern void scale3x(const byte *src, byte *dst, int width, int height);
extern float Mod_RadiusFromBounds(const vec3_t mins, const vec3_t maxs);
extern const byte* Mod_DecompressVis(const byte *in, int row);
/* Shared models struct */
enum {
SIDE_FRONT = 0,
SIDE_BACK = 1,
SIDE_ON = 2
};
// FIXME: differentiate from texinfo SURF_ flags
enum {
SURF_PLANEBACK = 0x02,
SURF_DRAWSKY = 0x04, // sky brush face
SURF_DRAWTURB = 0x10,
SURF_DRAWBACKGROUND = 0x40,
SURF_UNDERWATER = 0x80
};
typedef struct mvertex_s
{
vec3_t position;
} mvertex_t;
typedef struct medge_s
{
unsigned short v[2];
unsigned int cachededgeoffset;
} medge_t;
typedef struct mtexinfo_s
{
float vecs[2][4];
int flags;
int numframes;
struct mtexinfo_s *next; /* animation chain */
struct image_s *image;
} mtexinfo_t;
#define CONTENTS_NODE -1
typedef struct mnode_s
{
/* common with leaf */
int contents; /* CONTENTS_NODE, to differentiate from leafs */
int visframe; /* node needs to be traversed if current */
float minmaxs[6]; /* for bounding box culling */
struct mnode_s *parent;
/* node specific */
cplane_t *plane;
struct mnode_s *children[2];
unsigned short firstsurface;
unsigned short numsurfaces;
} mnode_t;
typedef struct mleaf_s
{
/* common with leaf */
int contents; /* CONTENTS_NODE, to differentiate from leafs */
int visframe; /* node needs to be traversed if current */
float minmaxs[6]; /* for bounding box culling */
struct mnode_s *parent;
/* leaf specific */
int cluster;
int area;
struct msurface_s **firstmarksurface;
int nummarksurfaces;
int key; /* BSP sequence number for leaf's contents */
} mleaf_t;
/* Shared models func */
typedef struct image_s* (*findimage_t)(const char *name, imagetype_t type);
extern void *Mod_LoadMD2 (const char *mod_name, const void *buffer, int modfilelen,
vec3_t mins, vec3_t maxs, struct image_s **skins,
findimage_t find_image, modtype_t *type);
extern void *Mod_LoadSP2 (const char *mod_name, const void *buffer, int modfilelen,
struct image_s **skins, findimage_t find_image, modtype_t *type);
extern int Mod_ReLoadSkins(struct image_s **skins, findimage_t find_image,
void *extradata, modtype_t type);
extern struct image_s *GetSkyImage(const char *skyname, const char* surfname,
qboolean palettedtexture, findimage_t find_image);
extern struct image_s *GetTexImage(const char *name, findimage_t find_image);
extern struct image_s *R_FindPic(const char *name, findimage_t find_image);
extern struct image_s* R_LoadImage(const char *name, const char* namewe, const char *ext,
imagetype_t type, qboolean r_retexturing, loadimage_t load_image);
extern void Mod_LoadNodes(const char *name, cplane_t *planes, int numplanes,
mleaf_t *leafs, int numleafs, mnode_t **nodes, int *numnodes,
const byte *mod_base, const lump_t *l);
extern void Mod_LoadVertexes(const char *name, mvertex_t **vertexes, int *numvertexes,
const byte *mod_base, const lump_t *l, int extra);
extern void Mod_LoadVisibility(dvis_t **vis, const byte *mod_base, const lump_t *l);
extern void Mod_LoadLighting(byte **lightdata, const byte *mod_base, const lump_t *l);
extern void Mod_LoadTexinfo(const char *name, mtexinfo_t **texinfo, int *numtexinfo,
const byte *mod_base, const lump_t *l, findimage_t find_image,
struct image_s *notexture, int extra);
extern void Mod_LoadEdges(const char *name, medge_t **edges, int *numedges,
const byte *mod_base, const lump_t *l, int extra);
extern void Mod_LoadPlanes (const char *name, cplane_t **planes, int *numplanes,
const byte *mod_base, const lump_t *l, int extra);
extern void Mod_LoadSurfedges (const char *name, int **surfedges, int *numsurfedges,
const byte *mod_base, const lump_t *l, int extra);
extern int Mod_CalcLumpHunkSize(const lump_t *l, int inSize, int outSize, int extra);
extern mleaf_t *Mod_PointInLeaf(const vec3_t p, mnode_t *node);
/* Surface logic */
#define DLIGHT_CUTOFF 64
typedef void (*marksurfacelights_t)(dlight_t *light, int bit, mnode_t *node,
int r_dlightframecount);
extern void R_MarkLights (dlight_t *light, int bit, mnode_t *node, int r_dlightframecount,
marksurfacelights_t mark_surface_lights);
extern struct image_s *R_TextureAnimation(const entity_t *currententity,
const mtexinfo_t *tex);
extern qboolean R_AreaVisible(const byte *areabits, mleaf_t *pleaf);
extern qboolean R_CullBox(vec3_t mins, vec3_t maxs, cplane_t *frustum);
extern void R_SetFrustum(vec3_t vup, vec3_t vpn, vec3_t vright, vec3_t r_origin,
float fov_x, float fov_y, cplane_t *frustum);
#endif /* SRC_CLIENT_REFRESH_REF_SHARED_H_ */

292
src/client/vid/header/ref.h Normal file
View file

@ -0,0 +1,292 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*
* =======================================================================
*
* ABI between client and refresher
*
* =======================================================================
*/
#ifndef CL_REF_H
#define CL_REF_H
#include "../../../common/header/common.h"
#include "vid.h"
#define MAX_DLIGHTS 32
#define MAX_ENTITIES 128
#define MAX_PARTICLES 4096
#define MAX_LIGHTSTYLES 256
#define POWERSUIT_SCALE 4.0F
#define SHELL_RED_COLOR 0xF2
#define SHELL_GREEN_COLOR 0xD0
#define SHELL_BLUE_COLOR 0xF3
#define SHELL_RG_COLOR 0xDC
#define SHELL_RB_COLOR 0x68
#define SHELL_BG_COLOR 0x78
#define SHELL_DOUBLE_COLOR 0xDF
#define SHELL_HALF_DAM_COLOR 0x90
#define SHELL_CYAN_COLOR 0x72
#define SHELL_WHITE_COLOR 0xD7
#define ENTITY_FLAGS 68
typedef struct entity_s {
struct model_s *model; /* opaque type outside refresh */
float angles[3];
/* most recent data */
float origin[3]; /* also used as RF_BEAM's "from" */
int frame; /* also used as RF_BEAM's diameter */
/* previous data for lerping */
float oldorigin[3]; /* also used as RF_BEAM's "to" */
int oldframe;
/* misc */
float backlerp; /* 0.0 = current, 1.0 = old */
int skinnum; /* also used as RF_BEAM's palette index */
int lightstyle; /* for flashing entities */
float alpha; /* ignore if RF_TRANSLUCENT isn't set */
struct image_s *skin; /* NULL for inline skin */
int flags;
} entity_t;
typedef struct {
vec3_t origin;
vec3_t color;
float intensity;
} dlight_t;
typedef struct {
vec3_t origin;
int color;
float alpha;
} particle_t;
typedef struct {
float rgb[3]; /* 0.0 - 2.0 */
float white; /* r+g+b */
} lightstyle_t;
typedef struct {
int x, y, width, height; /* in virtual screen coordinates */
float fov_x, fov_y;
float vieworg[3];
float viewangles[3];
float blend[4]; /* rgba 0-1 full screen blend */
float time; /* time is used to auto animate */
int rdflags; /* RDF_UNDERWATER, etc */
byte *areabits; /* if not NULL, only areas with set bits will be drawn */
lightstyle_t *lightstyles; /* [MAX_LIGHTSTYLES] */
int num_entities;
entity_t *entities;
int num_dlights; // <= 32 (MAX_DLIGHTS)
dlight_t *dlights;
int num_particles;
particle_t *particles;
} refdef_t;
// Renderer restart type.
typedef enum {
RESTART_UNDEF,
RESTART_NO,
RESTART_FULL,
RESTART_PARTIAL
} ref_restart_t;
// FIXME: bump API_VERSION?
#define API_VERSION 6
#define EXPORT
#define IMPORT
//
// these are the functions exported by the refresh module
//
typedef struct
{
// if api_version is different, the dll cannot be used
int api_version;
// called when the library is loaded
qboolean (EXPORT *Init) (void);
// called before the library is unloaded
void (EXPORT *Shutdown) (void);
// called by GLimp_InitGraphics() before creating window,
// returns flags for SDL window creation, returns -1 on error
int (EXPORT *PrepareForWindow)(void);
// called by GLimp_InitGraphics() *after* creating window,
// passing the SDL_Window* (void* so we don't spill SDL.h here)
// (or SDL_Surface* for SDL1.2, another reason to use void*)
// returns true (1) on success
int (EXPORT *InitContext)(void* sdl_window);
// called by GLimp_InitGraphics() *after* creating render
// context. Returns the actual drawable size in the width
// and height variables. This may be differend from the
// window size due to high dpi awareness.
void (EXPORT *GetDrawableSize)(int* width, int* height);
// shuts down rendering (OpenGL) context.
void (EXPORT *ShutdownContext)(void);
// returns true if vsync is active, else false
qboolean (EXPORT *IsVSyncActive)(void);
// All data that will be used in a level should be
// registered before rendering any frames to prevent disk hits,
// but they can still be registered at a later time
// if necessary.
//
// EndRegistration will free any remaining data that wasn't registered.
// Any model_s or skin_s pointers from before the BeginRegistration
// are no longer valid after EndRegistration.
//
// Skins and images need to be differentiated, because skins
// are flood filled to eliminate mip map edge errors, and pics have
// an implicit "pics/" prepended to the name. (a pic name that starts with a
// slash will not use the "pics/" prefix or the ".pcx" postfix)
void (EXPORT *BeginRegistration) (char *map);
struct model_s * (EXPORT *RegisterModel) (char *name);
struct image_s * (EXPORT *RegisterSkin) (char *name);
void (EXPORT *SetSky) (char *name, float rotate, vec3_t axis);
void (EXPORT *EndRegistration) (void);
void (EXPORT *RenderFrame) (refdef_t *fd);
struct image_s * (EXPORT *DrawFindPic)(char *name);
void (EXPORT *DrawGetPicSize) (int *w, int *h, char *name); // will return 0 0 if not found
void (EXPORT *DrawPicScaled) (int x, int y, char *pic, float factor);
void (EXPORT *DrawStretchPic) (int x, int y, int w, int h, char *name);
void (EXPORT *DrawCharScaled)(int x, int y, int num, float scale);
void (EXPORT *DrawTileClear) (int x, int y, int w, int h, char *name);
void (EXPORT *DrawFill) (int x, int y, int w, int h, int c);
void (EXPORT *DrawFadeScreen) (void);
/*
* Draw images for cinematic rendering (which can have a different palette if bits equals to 8).
* Note that calls
*/
void (EXPORT *DrawStretchRaw) (int x, int y, int w, int h, int cols, int rows, const byte *data, int bits);
/*
** video mode and refresh state management entry points
*/
void (EXPORT *SetPalette)( const unsigned char *palette); // NULL = game palette
void (EXPORT *BeginFrame)( float camera_separation );
void (EXPORT *EndFrame) (void);
qboolean (EXPORT *EndWorldRenderpass) (void); // finish world rendering, apply postprocess and switch to UI render pass
//void (EXPORT *AppActivate)( qboolean activate );
} refexport_t;
typedef struct
{
YQ2_ATTR_NORETURN_FUNCPTR void (IMPORT *Sys_Error) (int err_level, char *str, ...) PRINTF_ATTR(2, 3);
void (IMPORT *Cmd_AddCommand) (char *name, void(*cmd)(void));
void (IMPORT *Cmd_RemoveCommand) (char *name);
int (IMPORT *Cmd_Argc) (void);
char *(IMPORT *Cmd_Argv) (int i);
void (IMPORT *Cmd_ExecuteText) (int exec_when, char *text);
void (IMPORT *Com_VPrintf) (int print_level, const char *fmt, va_list argptr);
// files will be memory mapped read only
// the returned buffer may be part of a larger pak file,
// or a discrete file from anywhere in the quake search path
// a -1 return means the file does not exist
// NULL can be passed for buf to just determine existance
int (IMPORT *FS_LoadFile) (char *name, void **buf);
void (IMPORT *FS_FreeFile) (void *buf);
// gamedir will be the current directory that generated
// files should be stored to, ie: "f:\quake\id1"
char *(IMPORT *FS_Gamedir) (void);
cvar_t *(IMPORT *Cvar_Get) (char *name, char *value, int flags);
cvar_t *(IMPORT *Cvar_Set) (char *name, char *value);
void (IMPORT *Cvar_SetValue) (char *name, float value);
qboolean (IMPORT *Vid_GetModeInfo)(int *width, int *height, int mode);
void (IMPORT *Vid_MenuInit)( void );
// called with image data of width*height pixel which comp bytes per pixel (must be 3 or 4 for RGB or RGBA)
// expects the pixels data to be row-wise, starting at top left
void (IMPORT *Vid_WriteScreenshot)( int width, int height, int comp, const void* data );
qboolean (IMPORT *GLimp_InitGraphics)(int fullscreen, int *pwidth, int *pheight);
qboolean (IMPORT *GLimp_GetDesktopMode)(int *pwidth, int *pheight);
void (IMPORT *Vid_RequestRestart)(ref_restart_t rs);
} refimport_t;
// this is the only function actually exported at the linker level
typedef refexport_t (EXPORT *GetRefAPI_t) (refimport_t);
// FIXME: #ifdef client/ref around this
extern refexport_t re;
extern refimport_t ri;
/*
* Refresh API
*/
void R_BeginRegistration(char *map);
void R_Clear(void);
struct model_s *R_RegisterModel(char *name);
struct image_s *R_RegisterSkin(char *name);
void R_SetSky(char *name, float rotate, vec3_t axis);
void R_EndRegistration(void);
struct image_s *Draw_FindPic(char *name);
void R_RenderFrame(refdef_t *fd);
void Draw_GetPicSize(int *w, int *h, char *name);
void Draw_StretchPic(int x, int y, int w, int h, char *name);
void Draw_PicScaled(int x, int y, char *pic, float factor);
void Draw_CharScaled(int x, int y, int num, float scale);
void Draw_TileClear(int x, int y, int w, int h, char *name);
void Draw_Fill(int x, int y, int w, int h, int c);
void Draw_FadeScreen(void);
void Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, const byte *data, int bits);
//int R_Init(void *hinstance, void *hWnd);
//void R_Shutdown(void);
void R_SetPalette(const unsigned char *palette);
void R_BeginFrame(float camera_separation);
qboolean R_EndWorldRenderpass(void);
void R_EndFrame(void);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,70 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*
* =======================================================================
*
* API between client and renderer.
*
* =======================================================================
*/
#ifndef CL_VID_H
#define CL_VID_H
#include "../../../common/header/common.h"
// FIXME: Remove it, it's unused.
typedef struct vrect_s {
int x,y,width,height;
} vrect_t;
// Hold the video state.
typedef struct {
int height;
int width;
} viddef_t;
// Global video state.
extern viddef_t viddef;
// Generic stuff.
qboolean VID_HasRenderer(const char *renderer);
void VID_Init(void);
void VID_Shutdown(void);
void VID_CheckChanges(void);
void VID_MenuInit(void);
void VID_MenuDraw(void);
const char *VID_MenuKey(int);
// Stuff provided by platform backend.
extern int glimp_refreshRate;
const char **GLimp_GetDisplayIndices(void);
int GLimp_GetWindowDisplayIndex(void);
int GLimp_GetNumVideoDisplays(void);
qboolean GLimp_Init(void);
void GLimp_Shutdown(void);
qboolean GLimp_InitGraphics(int fullscreen, int *pwidth, int *pheight);
void GLimp_ShutdownGraphics(void);
void GLimp_GrabInput(qboolean grab);
int GLimp_GetRefreshRate(void);
qboolean GLimp_GetDesktopMode(int *pwidth, int *pheight);
#endif

871
src/common/header/common.h Normal file
View file

@ -0,0 +1,871 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Prototypes witch are shared between the client, the server and the
* game. This is the main game API, changes here will most likely
* requiere changes to the game ddl.
*
* =======================================================================
*/
#ifndef CO_COMMON_H
#define CO_COMMON_H
#include "shared.h"
#include "crc.h"
#define YQ2VERSION "8.21pre"
#define BASEDIRNAME "baseq2"
#ifndef YQ2OSTYPE
#error YQ2OSTYPE should be defined by the build system
#endif
#ifndef BUILD_DATE
#define BUILD_DATE __DATE__
#endif
#ifdef _WIN32
#define CFGDIR "YamagiQ2"
#else
#ifndef __HAIKU__
#define CFGDIR ".yq2"
#else
#define CFGDIR "yq2"
#endif
#endif
#ifndef YQ2ARCH
#ifdef _MSC_VER
// Setting YQ2ARCH for VisualC++ from CMake doesn't work when using VS integrated CMake
// so set it in code instead
#ifdef YQ2ARCH
#undef YQ2ARCH
#endif
#ifdef _M_X64
// this matches AMD64 and ARM64EC (but not regular ARM64), but they're supposed to be binary-compatible somehow, so whatever
#define YQ2ARCH "x86_64"
#elif defined(_M_ARM64)
#define YQ2ARCH "arm64"
#elif defined(_M_ARM)
#define YQ2ARCH "arm"
#elif defined(_M_IX86)
#define YQ2ARCH "x86"
#else
// if you're not targeting one of the aforementioned architectures,
// check https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
// to find out how to detect yours and add it here - and please send a patch :)
#error "Unknown CPU architecture!"
// (for a quick and dirty solution, comment out the previous line, but keep in mind
// that savegames may not be compatible with other builds of Yamagi Quake II)
#define YQ2ARCH "UNKNOWN"
#endif // _M_X64 etc
#else // other compilers than MSVC
#error YQ2ARCH should be defined by the build system
#endif // _MSC_VER
#endif // YQ2ARCH
/* ================================================================== */
typedef struct sizebuf_s
{
qboolean allowoverflow; /* if false, do a Com_Error */
qboolean overflowed; /* set to true if the buffer size failed */
byte *data;
int maxsize;
int cursize;
int readcount;
} sizebuf_t;
void SZ_Init(sizebuf_t *buf, byte *data, int length);
void SZ_Clear(sizebuf_t *buf);
void *SZ_GetSpace(sizebuf_t *buf, int length);
void SZ_Write(sizebuf_t *buf, void *data, int length);
void SZ_Print(sizebuf_t *buf, char *data); /* strcats onto the sizebuf */
/* ================================================================== */
struct usercmd_s;
struct entity_state_s;
void MSG_WriteChar(sizebuf_t *sb, int c);
void MSG_WriteByte(sizebuf_t *sb, int c);
void MSG_WriteShort(sizebuf_t *sb, int c);
void MSG_WriteLong(sizebuf_t *sb, int c);
void MSG_WriteFloat(sizebuf_t *sb, float f);
void MSG_WriteString(sizebuf_t *sb, char *s);
void MSG_WriteCoord(sizebuf_t *sb, float f);
void MSG_WritePos(sizebuf_t *sb, vec3_t pos);
void MSG_WriteAngle(sizebuf_t *sb, float f);
void MSG_WriteAngle16(sizebuf_t *sb, float f);
void MSG_WriteDeltaUsercmd(sizebuf_t *sb, struct usercmd_s *from,
struct usercmd_s *cmd);
void MSG_WriteDeltaEntity(struct entity_state_s *from,
struct entity_state_s *to, sizebuf_t *msg,
qboolean force, qboolean newentity);
void MSG_WriteDir(sizebuf_t *sb, vec3_t vector);
void MSG_BeginReading(sizebuf_t *sb);
int MSG_ReadChar(sizebuf_t *sb);
int MSG_ReadByte(sizebuf_t *sb);
int MSG_ReadShort(sizebuf_t *sb);
int MSG_ReadLong(sizebuf_t *sb);
float MSG_ReadFloat(sizebuf_t *sb);
char *MSG_ReadString(sizebuf_t *sb);
char *MSG_ReadStringLine(sizebuf_t *sb);
float MSG_ReadCoord(sizebuf_t *sb);
void MSG_ReadPos(sizebuf_t *sb, vec3_t pos);
float MSG_ReadAngle(sizebuf_t *sb);
float MSG_ReadAngle16(sizebuf_t *sb);
void MSG_ReadDeltaUsercmd(sizebuf_t *sb,
struct usercmd_s *from,
struct usercmd_s *cmd);
void MSG_ReadDir(sizebuf_t *sb, vec3_t vector);
void MSG_ReadData(sizebuf_t *sb, void *buffer, int size);
/* ================================================================== */
extern qboolean bigendien;
extern short BigShort(short l);
extern short LittleShort(short l);
extern int BigLong(int l);
extern int LittleLong(int l);
extern float BigFloat(float l);
extern float LittleFloat(float l);
/* ================================================================== */
int COM_Argc(void);
char *COM_Argv(int arg); /* range and null checked */
void COM_ClearArgv(int arg);
int COM_CheckParm(char *parm);
void COM_AddParm(char *parm);
void COM_Init(void);
void COM_InitArgv(int argc, char **argv);
char *CopyString(char *in);
/* ================================================================== */
void Info_Print(char *s);
/* PROTOCOL */
#define PROTOCOL_VERSION 34
/* ========================================= */
#define PORT_MASTER 27900
#define PORT_CLIENT 27901
#define PORT_SERVER 27910
/* ========================================= */
#define UPDATE_BACKUP 16 /* copies of entity_state_t to keep buffered */
#define UPDATE_MASK (UPDATE_BACKUP - 1)
/* server to client */
enum svc_ops_e
{
svc_bad,
/* these ops are known to the game dll */
svc_muzzleflash,
svc_muzzleflash2,
svc_temp_entity,
svc_layout,
svc_inventory,
/* the rest are private to the client and server */
svc_nop,
svc_disconnect,
svc_reconnect,
svc_sound, /* <see code> */
svc_print, /* [byte] id [string] null terminated string */
svc_stufftext, /* [string] stuffed into client's console buffer, should be \n terminated */
svc_serverdata, /* [long] protocol ... */
svc_configstring, /* [short] [string] */
svc_spawnbaseline,
svc_centerprint, /* [string] to put in center of the screen */
svc_download, /* [short] size [size bytes] */
svc_playerinfo, /* variable */
svc_packetentities, /* [...] */
svc_deltapacketentities, /* [...] */
svc_frame
};
/* ============================================== */
/* client to server */
enum clc_ops_e
{
clc_bad,
clc_nop,
clc_move, /* [[usercmd_t] */
clc_userinfo, /* [[userinfo string] */
clc_stringcmd /* [string] message */
};
/* ============================================== */
/* plyer_state_t communication */
#define PS_M_TYPE (1 << 0)
#define PS_M_ORIGIN (1 << 1)
#define PS_M_VELOCITY (1 << 2)
#define PS_M_TIME (1 << 3)
#define PS_M_FLAGS (1 << 4)
#define PS_M_GRAVITY (1 << 5)
#define PS_M_DELTA_ANGLES (1 << 6)
#define PS_VIEWOFFSET (1 << 7)
#define PS_VIEWANGLES (1 << 8)
#define PS_KICKANGLES (1 << 9)
#define PS_BLEND (1 << 10)
#define PS_FOV (1 << 11)
#define PS_WEAPONINDEX (1 << 12)
#define PS_WEAPONFRAME (1 << 13)
#define PS_RDFLAGS (1 << 14)
/*============================================== */
/* user_cmd_t communication */
/* ms and light always sent, the others are optional */
#define CM_ANGLE1 (1 << 0)
#define CM_ANGLE2 (1 << 1)
#define CM_ANGLE3 (1 << 2)
#define CM_FORWARD (1 << 3)
#define CM_SIDE (1 << 4)
#define CM_UP (1 << 5)
#define CM_BUTTONS (1 << 6)
#define CM_IMPULSE (1 << 7)
/*============================================== */
/* a sound without an ent or pos will be a local only sound */
#define SND_VOLUME (1 << 0) /* a byte */
#define SND_ATTENUATION (1 << 1) /* a byte */
#define SND_POS (1 << 2) /* three coordinates */
#define SND_ENT (1 << 3) /* a short 0-2: channel, 3-12: entity */
#define SND_OFFSET (1 << 4) /* a byte, msec offset from frame start */
#define DEFAULT_SOUND_PACKET_VOLUME 1.0
#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
/*============================================== */
/* entity_state_t communication */
/* try to pack the common update flags into the first byte */
#define U_ORIGIN1 (1 << 0)
#define U_ORIGIN2 (1 << 1)
#define U_ANGLE2 (1 << 2)
#define U_ANGLE3 (1 << 3)
#define U_FRAME8 (1 << 4) /* frame is a byte */
#define U_EVENT (1 << 5)
#define U_REMOVE (1 << 6) /* REMOVE this entity, don't add it */
#define U_MOREBITS1 (1 << 7) /* read one additional byte */
/* second byte */
#define U_NUMBER16 (1 << 8) /* NUMBER8 is implicit if not set */
#define U_ORIGIN3 (1 << 9)
#define U_ANGLE1 (1 << 10)
#define U_MODEL (1 << 11)
#define U_RENDERFX8 (1 << 12) /* fullbright, etc */
#define U_EFFECTS8 (1 << 14) /* autorotate, trails, etc */
#define U_MOREBITS2 (1 << 15) /* read one additional byte */
/* third byte */
#define U_SKIN8 (1 << 16)
#define U_FRAME16 (1 << 17) /* frame is a short */
#define U_RENDERFX16 (1 << 18) /* 8 + 16 = 32 */
#define U_EFFECTS16 (1 << 19) /* 8 + 16 = 32 */
#define U_MODEL2 (1 << 20) /* weapons, flags, etc */
#define U_MODEL3 (1 << 21)
#define U_MODEL4 (1 << 22)
#define U_MOREBITS3 (1 << 23) /* read one additional byte */
/* fourth byte */
#define U_OLDORIGIN (1 << 24)
#define U_SKIN16 (1 << 25)
#define U_SOUND (1 << 26)
#define U_SOLID (1 << 27)
/* CMD - Command text buffering and command execution */
/*
* Any number of commands can be added in a frame, from several different
* sources. Most commands come from either keybindings or console line
* input, but remote servers can also send across commands and entire text
* files can be execed.
*
* The + command line options are also added to the command buffer.
*
* The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute
* ();
*/
#define EXEC_NOW 0 /* don't return until completed */
#define EXEC_INSERT 1 /* insert at current position, but don't run yet */
#define EXEC_APPEND 2 /* add to end of the command buffer */
void Cbuf_Init(void);
/* allocates an initial text buffer that will grow as needed */
void Cbuf_AddText(char *text);
/* as new commands are generated from the console or keybindings, */
/* the text is added to the end of the command buffer. */
void Cbuf_InsertText(char *text);
/* when a command wants to issue other commands immediately, the text is */
/* inserted at the beginning of the buffer, before any remaining unexecuted */
/* commands. */
void Cbuf_ExecuteText(int exec_when, char *text);
/* this can be used in place of either Cbuf_AddText or Cbuf_InsertText */
void Cbuf_AddEarlyCommands(qboolean clear);
/* adds all the +set commands from the command line */
qboolean Cbuf_AddLateCommands(void);
/* adds all the remaining + commands from the command line */
/* Returns true if any late commands were added, which */
/* will keep the demoloop from immediately starting */
void Cbuf_Execute(void);
/* Pulls off \n terminated lines of text from the command buffer and sends */
/* them through Cmd_ExecuteString. Stops when the buffer is empty. */
/* Normally called once per frame, but may be explicitly invoked. */
/* Do not call inside a command function! */
void Cbuf_CopyToDefer(void);
void Cbuf_InsertFromDefer(void);
/* These two functions are used to defer any pending commands while a map */
/* is being loaded */
/*=================================================================== */
/*
* Command execution takes a null terminated string, breaks it into tokens,
* then searches for a command or variable that matches the first token.
*/
typedef void (*xcommand_t)(void);
void Cmd_Init(void);
void Cmd_Shutdown(void);
void Cmd_AddCommand(char *cmd_name, xcommand_t function);
/* called by the init functions of other parts of the program to */
/* register commands and functions to call for them. */
/* The cmd_name is referenced later, so it should not be in temp memory */
/* if function is NULL, the command will be forwarded to the server */
/* as a clc_stringcmd instead of executed locally */
void Cmd_RemoveCommand(char *cmd_name);
qboolean Cmd_Exists(char *cmd_name);
/* used by the cvar code to check for cvar / command name overlap */
char *Cmd_CompleteCommand(char *partial);
char *Cmd_CompleteMapCommand(char *partial);
/* attempts to match a partial command for automatic command line completion */
/* returns NULL if nothing fits */
int Cmd_Argc(void);
char *Cmd_Argv(int arg);
char *Cmd_Args(void);
/* The functions that execute commands get their parameters with these */
/* functions. Cmd_Argv () will return an empty string, not a NULL */
/* if arg > argc, so string operations are always safe. */
void Cmd_TokenizeString(char *text, qboolean macroExpand);
/* Takes a null terminated string. Does not need to be /n terminated. */
/* breaks the string up into arg tokens. */
void Cmd_ExecuteString(char *text);
/* Parses a single line of text into arguments and tries to execute it */
/* as if it was typed at the console */
void Cmd_ForwardToServer(void);
/* adds the current command line as a clc_stringcmd to the client message. */
/* things like godmode, noclip, etc, are commands directed to the server, */
/* so when they are typed in at the console, they will need to be forwarded. */
/* CVAR */
/*
* cvar_t variables are used to hold scalar or string variables that can be
* changed or displayed at the console or prog code as well as accessed
* directly in C code.
*
* The user can access cvars from the console in three ways:
* r_draworder prints the current value
* r_draworder 0 sets the current value to 0
* set r_draworder 0 as above, but creates the cvar if not present
* Cvars are restricted from having the same names as commands to keep this
* interface from being ambiguous.
*/
extern cvar_t *cvar_vars;
cvar_t *Cvar_Get(char *var_name, char *value, int flags);
/* creates the variable if it doesn't exist, or returns the existing one */
/* if it exists, the value will not be changed, but flags will be ORed in */
/* that allows variables to be unarchived without needing bitflags */
cvar_t *Cvar_Set(char *var_name, char *value);
/* will create the variable if it doesn't exist */
cvar_t *Cvar_ForceSet(char *var_name, char *value);
/* will set the variable even if NOSET or LATCH */
cvar_t *Cvar_FullSet(char *var_name, char *value, int flags);
void Cvar_SetValue(char *var_name, float value);
/* expands value to a string and calls Cvar_Set */
float Cvar_VariableValue(char *var_name);
/* returns 0 if not defined or non numeric */
const char *Cvar_VariableString(const char *var_name);
/* returns an empty string if not defined */
char *Cvar_CompleteVariable(char *partial);
/* attempts to match a partial variable name for command line completion */
/* returns NULL if nothing fits */
void Cvar_GetLatchedVars(void);
/* any CVAR_LATCHED variables that have been set will now take effect */
qboolean Cvar_Command(void);
/* called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known */
/* command. Returns true if the command was a variable reference that */
/* was handled. (print or change) */
void Cvar_WriteVariables(char *path);
/* appends lines containing "set variable value" for all variables */
/* with the archive flag set to true. */
void Cvar_Init(void);
void Cvar_Fini(void);
char *Cvar_Userinfo(void);
/* returns an info string containing all the CVAR_USERINFO cvars */
char *Cvar_Serverinfo(void);
/* returns an info string containing all the CVAR_SERVERINFO cvars */
extern qboolean userinfo_modified;
/* this is set each time a CVAR_USERINFO variable is changed */
/* so that the client knows to send it to the server */
/* NET */
#define PORT_ANY -1
#define MAX_MSGLEN 1400 /* max length of a message */
#define PACKET_HEADER 10 /* two ints and a short */
typedef enum
{
NA_LOOPBACK,
NA_BROADCAST,
NA_IP,
NA_IPX,
NA_BROADCAST_IPX,
NA_IP6,
NA_MULTICAST6
} netadrtype_t;
typedef enum {NS_CLIENT, NS_SERVER} netsrc_t;
typedef struct
{
netadrtype_t type;
byte ip[16];
unsigned int scope_id;
byte ipx[10];
unsigned short port;
} netadr_t;
void NET_Init(void);
void NET_Shutdown(void);
void NET_Config(qboolean multiplayer);
qboolean NET_GetPacket(netsrc_t sock, netadr_t *net_from,
sizebuf_t *net_message);
void NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to);
qboolean NET_CompareAdr(netadr_t a, netadr_t b);
qboolean NET_CompareBaseAdr(netadr_t a, netadr_t b);
qboolean NET_IsLocalAddress(netadr_t adr);
char *NET_AdrToString(netadr_t a);
qboolean NET_StringToAdr(const char *s, netadr_t *a);
void NET_Sleep(int msec);
/*=================================================================== */
#define OLD_AVG 0.99
#define MAX_LATENT 32
typedef struct
{
qboolean fatal_error;
netsrc_t sock;
int dropped; /* between last packet and previous */
int last_received; /* for timeouts */
int last_sent; /* for retransmits */
netadr_t remote_address;
int qport; /* qport value to write when transmitting */
/* sequencing variables */
int incoming_sequence;
int incoming_acknowledged;
int incoming_reliable_acknowledged; /* single bit */
int incoming_reliable_sequence; /* single bit, maintained local */
int outgoing_sequence;
int reliable_sequence; /* single bit */
int last_reliable_sequence; /* sequence number of last send */
/* reliable staging and holding areas */
sizebuf_t message; /* writing buffer to send to server */
byte message_buf[MAX_MSGLEN - 16]; /* leave space for header */
/* message is copied to this buffer when it is first transfered */
int reliable_length;
byte reliable_buf[MAX_MSGLEN - 16]; /* unacked reliable message */
} netchan_t;
extern netadr_t net_from;
extern sizebuf_t net_message;
extern byte net_message_buffer[MAX_MSGLEN];
void Netchan_Init(void);
void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport);
qboolean Netchan_NeedReliable(netchan_t *chan);
void Netchan_Transmit(netchan_t *chan, int length, byte *data);
void Netchan_OutOfBand(int net_socket, netadr_t adr, int length, byte *data);
void Netchan_OutOfBandPrint(int net_socket, netadr_t adr, char *format, ...);
qboolean Netchan_Process(netchan_t *chan, sizebuf_t *msg);
qboolean Netchan_CanReliable(netchan_t *chan);
/* CMODEL */
#include "files.h"
cmodel_t *CM_LoadMap(char *name, qboolean clientload, unsigned *checksum);
cmodel_t *CM_InlineModel(char *name); /* *1, *2, etc */
int CM_NumClusters(void);
int CM_NumInlineModels(void);
char *CM_EntityString(void);
/* creates a clipping hull for an arbitrary box */
int CM_HeadnodeForBox(vec3_t mins, vec3_t maxs);
/* returns an ORed contents mask */
int CM_PointContents(vec3_t p, int headnode);
int CM_TransformedPointContents(vec3_t p, int headnode,
vec3_t origin, vec3_t angles);
trace_t CM_BoxTrace(vec3_t start, vec3_t end, vec3_t mins,
vec3_t maxs, int headnode, int brushmask);
trace_t CM_TransformedBoxTrace(vec3_t start, vec3_t end,
vec3_t mins, vec3_t maxs, int headnode,
int brushmask, vec3_t origin, vec3_t angles);
byte *CM_ClusterPVS(int cluster);
byte *CM_ClusterPHS(int cluster);
int CM_PointLeafnum(vec3_t p);
/* call with topnode set to the headnode, returns with topnode */
/* set to the first node that splits the box */
int CM_BoxLeafnums(vec3_t mins, vec3_t maxs, int *list,
int listsize, int *topnode);
int CM_LeafContents(int leafnum);
int CM_LeafCluster(int leafnum);
int CM_LeafArea(int leafnum);
void CM_SetAreaPortalState(int portalnum, qboolean open);
qboolean CM_AreasConnected(int area1, int area2);
int CM_WriteAreaBits(byte *buffer, int area);
qboolean CM_HeadnodeVisible(int headnode, byte *visbits);
void CM_WritePortalState(FILE *f);
/* PLAYER MOVEMENT CODE */
extern float pm_airaccelerate;
void Pmove(pmove_t *pmove);
/* FILESYSTEM */
#define SFF_INPACK 0x20
typedef int fileHandle_t;
typedef enum
{
FS_READ,
FS_WRITE,
FS_APPEND
} fsMode_t;
typedef enum
{
FS_SEEK_CUR,
FS_SEEK_SET,
FS_SEEK_END
} fsOrigin_t;
typedef enum
{
FS_SEARCH_PATH_EXTENSION,
FS_SEARCH_BY_FILTER,
FS_SEARCH_FULL_PATH
} fsSearchType_t;
void FS_DPrintf(const char *format, ...);
int FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only);
void FS_FCloseFile(fileHandle_t f);
int FS_Read(void *buffer, int size, fileHandle_t f);
int FS_FRead(void *buffer, int size, int count, fileHandle_t f);
// returns the filename used to open f, but (if opened from pack) in correct case
// returns NULL if f is no valid handle
const char* FS_GetFilenameForHandle(fileHandle_t f);
char **FS_ListFiles(char *findname, int *numfiles,
unsigned musthave, unsigned canthave);
char **FS_ListFiles2(char *findname, int *numfiles,
unsigned musthave, unsigned canthave);
void FS_FreeList(char **list, int nfiles);
void FS_InitFilesystem(void);
void FS_ShutdownFilesystem(void);
void FS_BuildGameSpecificSearchPath(char *dir);
char *FS_Gamedir(void);
char *FS_NextPath(char *prevpath);
int FS_LoadFile(char *path, void **buffer);
qboolean FS_FileInGamedir(const char *file);
qboolean FS_AddPAKFromGamedir(const char *pak);
const char* FS_GetNextRawPath(const char* lastRawPath);
char **FS_ListMods(int *nummods);
/* a null buffer will just return the file length without loading */
/* a -1 length is not present */
/* properly handles partial reads */
void FS_FreeFile(void *buffer);
void FS_CreatePath(char *path);
/* MISC */
#define ERR_FATAL 0 /* exit the entire game with a popup window */
#define ERR_DROP 1 /* print to console and disconnect from game */
#define ERR_QUIT 2 /* not an error, just a normal exit */
#define EXEC_NOW 0 /* don't return until completed */
#define EXEC_INSERT 1 /* insert at current position, but don't run yet */
#define EXEC_APPEND 2 /* add to end of the command buffer */
#define PRINT_ALL 0
#define PRINT_DEVELOPER 1 /* only print when "developer 1" */
void Com_BeginRedirect(int target, char *buffer, int buffersize, void (*flush)(int, char *));
void Com_EndRedirect(void);
void Com_Printf(char *fmt, ...) PRINTF_ATTR(1, 2);
void Com_DPrintf(char *fmt, ...) PRINTF_ATTR(1, 2);
void Com_VPrintf(int print_level, const char *fmt, va_list argptr); /* print_level is PRINT_ALL or PRINT_DEVELOPER */
void Com_MDPrintf(char *fmt, ...) PRINTF_ATTR(1, 2);
YQ2_ATTR_NORETURN_FUNCPTR void Com_Error(int code, char *fmt, ...) PRINTF_ATTR(2, 3);
YQ2_ATTR_NORETURN void Com_Quit(void);
/* Ugly work around for unsupported
* format specifiers unter mingw. */
#ifdef WIN32
#define YQ2_COM_PRId64 "%I64d"
#define YQ2_COM_PRIdS "%Id"
#else
#define YQ2_COM_PRId64 "%ld"
#define YQ2_COM_PRIdS "%zd"
#endif
// terminate yq2 (with Com_Error()) if VAR is NULL (after malloc() or similar)
// and print message about it
#define YQ2_COM_CHECK_OOM(VAR, ALLOC_FN_NAME, ALLOC_SIZE) \
if(VAR == NULL) { \
Com_Error(ERR_FATAL, "%s for " YQ2_COM_PRIdS " bytes failed in %s() (%s == NULL)! Out of Memory?!\n", \
ALLOC_FN_NAME, (size_t)ALLOC_SIZE, __func__, #VAR); }
int Com_ServerState(void); /* this should have just been a cvar... */
void Com_SetServerState(int state);
unsigned Com_BlockChecksum(void *buffer, int length);
byte COM_BlockSequenceCRCByte(byte *base, int length, int sequence);
extern cvar_t *developer;
extern cvar_t *modder;
extern cvar_t *dedicated;
extern cvar_t *host_speeds;
extern cvar_t *log_stats;
/* External entity files. */
extern cvar_t *sv_entfile;
/* Hack for portable client */
extern qboolean is_portable;
/* Hack for external datadir */
extern char datadir[MAX_OSPATH];
/* Hack for external datadir */
extern char cfgdir[MAX_OSPATH];
/* Hack for working 'game' cmd */
extern char userGivenGame[MAX_QPATH];
extern char **mapnames;
extern int nummaps;
extern FILE *log_stats_file;
/* host_speeds times */
extern int time_before_game;
extern int time_after_game;
extern int time_before_ref;
extern int time_after_ref;
void Z_Free(void *ptr);
void *Z_Malloc(int size); /* returns 0 filled memory */
void *Z_TagMalloc(int size, int tag);
void Z_FreeTags(int tag);
void Qcommon_Init(int argc, char **argv);
void Qcommon_ExecConfigs(qboolean addEarlyCmds);
const char* Qcommon_GetInitialGame(void);
void Qcommon_Shutdown(void);
#define NUMVERTEXNORMALS 162
extern vec3_t bytedirs[NUMVERTEXNORMALS];
/* this is in the client code, but can be used for debugging from server */
void SCR_DebugGraph(float value, int color);
/* CLIENT / SERVER SYSTEMS */
void CL_Init(void);
void CL_Drop(void);
void CL_Shutdown(void);
void CL_Frame(int packetdelta, int renderdelta, int timedelta, qboolean packetframe, qboolean renderframe);
void Con_Print(char *text);
void SCR_BeginLoadingPlaque(void);
void SV_Init(void);
void SV_Shutdown(char *finalmsg, qboolean reconnect);
void SV_Frame(int usec);
/* ======================================================================= */
// Platform specific functions.
// system.c
char *Sys_ConsoleInput(void);
void Sys_ConsoleOutput(char *string);
YQ2_ATTR_NORETURN void Sys_Error(char *error, ...);
YQ2_ATTR_NORETURN void Sys_Quit(void);
void Sys_Init(void);
char *Sys_GetHomeDir(void);
void Sys_Remove(const char *path);
int Sys_Rename(const char *from, const char *to);
void Sys_RemoveDir(const char *path);
long long Sys_Microseconds(void);
void Sys_Nanosleep(int);
void *Sys_GetProcAddress(void *handle, const char *sym);
void Sys_FreeLibrary(void *handle);
void *Sys_LoadLibrary(const char *path, const char *sym, void **handle);
void *Sys_GetGameAPI(void *parms);
void Sys_UnloadGame(void);
void Sys_GetWorkDir(char *buffer, size_t len);
qboolean Sys_SetWorkDir(char *path);
qboolean Sys_Realpath(const char *in, char *out, size_t size);
// Windows only (system.c)
#ifdef _WIN32
void Sys_RedirectStdout(void);
void Sys_SetHighDPIMode(void);
#endif
// misc.c
const char *Sys_GetBinaryDir(void);
void Sys_SetupFPU(void);
/* ======================================================================= */
#endif

33
src/common/header/crc.h Normal file
View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Corresponding header for crc.c
*
* =======================================================================
*/
#ifndef CO_CRC_H
#define CO_CRC_H
void CRC_Init(unsigned short *crcvalue);
unsigned short CRC_Block(byte *start, int count);
#endif

483
src/common/header/files.h Normal file
View file

@ -0,0 +1,483 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* The prototypes for most file formats used by Quake II
*
* =======================================================================
*/
#ifndef CO_FILES_H
#define CO_FILES_H
/* The .pak files are just a linear collapse of a directory tree */
#define IDPAKHEADER (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P')
typedef struct
{
char name[56];
int filepos, filelen;
} dpackfile_t;
typedef struct
{
int ident; /* == IDPAKHEADER */
int dirofs;
int dirlen;
} dpackheader_t;
#define MAX_FILES_IN_PACK 4096
/* PCX files are used for as many images as possible */
typedef struct
{
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
unsigned short xmin, ymin, xmax, ymax;
unsigned short hres, vres;
unsigned char palette[48];
char reserved;
char color_planes;
unsigned short bytes_per_line;
unsigned short palette_type;
char filler[58];
unsigned char data; /* unbounded */
} pcx_t;
/* .MD2 triangle model file format */
#define IDALIASHEADER (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')
#define ALIAS_VERSION 8
#define MAX_TRIANGLES 4096
#define MAX_VERTS 2048
#define MAX_FRAMES 512
#define MAX_MD2SKINS 32
#define MAX_SKINNAME 64
typedef struct
{
short s;
short t;
} dstvert_t;
typedef struct
{
short index_xyz[3];
short index_st[3];
} dtriangle_t;
typedef struct
{
byte v[3]; /* scaled byte to fit in frame mins/maxs */
byte lightnormalindex;
} dtrivertx_t;
#define DTRIVERTX_V0 0
#define DTRIVERTX_V1 1
#define DTRIVERTX_V2 2
#define DTRIVERTX_LNI 3
#define DTRIVERTX_SIZE 4
typedef struct
{
float scale[3]; /* multiply byte verts by this */
float translate[3]; /* then add this */
char name[16]; /* frame name from grabbing */
dtrivertx_t verts[1]; /* variable sized */
} daliasframe_t;
/* the glcmd format:
* - a positive integer starts a tristrip command, followed by that many
* vertex structures.
* - a negative integer starts a trifan command, followed by -x vertexes
* a zero indicates the end of the command list.
* - a vertex consists of a floating point s, a floating point t,
* and an integer vertex index. */
typedef struct
{
int ident;
int version;
int skinwidth;
int skinheight;
int framesize; /* byte size of each frame */
int num_skins;
int num_xyz;
int num_st; /* greater than num_xyz for seams */
int num_tris;
int num_glcmds; /* dwords in strip/fan command list */
int num_frames;
int ofs_skins; /* each skin is a MAX_SKINNAME string */
int ofs_st; /* byte offset from start for stverts */
int ofs_tris; /* offset for dtriangles */
int ofs_frames; /* offset for first frame */
int ofs_glcmds;
int ofs_end; /* end of file */
} dmdl_t;
/* .SP2 sprite file format */
#define IDSPRITEHEADER (('2' << 24) + ('S' << 16) + ('D' << 8) + 'I') /* little-endian "IDS2" */
#define SPRITE_VERSION 2
typedef struct
{
int width, height;
int origin_x, origin_y; /* raster coordinates inside pic */
char name[MAX_SKINNAME]; /* name of pcx file */
} dsprframe_t;
typedef struct
{
int ident;
int version;
int numframes;
dsprframe_t frames[1]; /* variable sized */
} dsprite_t;
/* .WAL texture file format */
#define MIPLEVELS 4
typedef struct miptex_s
{
char name[32];
unsigned width, height;
unsigned offsets[MIPLEVELS]; /* four mip maps stored */
char animname[32]; /* next frame in animation chain */
int flags;
int contents;
int value;
} miptex_t;
/* .M8 texture file format */
#define M8_MIP_LEVELS 16
#define M8_VERSION 0x2
typedef struct {
unsigned char r;
unsigned char g;
unsigned char b;
} rgb_t;
typedef struct m8tex_s
{
unsigned version;
char name[32];
unsigned width[M8_MIP_LEVELS];
unsigned height[M8_MIP_LEVELS];
unsigned offsets[M8_MIP_LEVELS]; /* 16 mip maps stored */
char animname[32]; /* next frame in animation chain */
rgb_t palette[256];
int flags;
int contents;
int value;
} m8tex_t;
/* .M32 texture file format */
#define M32_VERSION 0x4
#define M32_MIP_LEVELS 16
typedef struct m32tex_s
{
int version;
char name[128];
char altname[128]; // texture substitution
char animname[128]; // next frame in animation chain
char damagename[128]; // image that should be shown when damaged
unsigned width[M32_MIP_LEVELS], height[M32_MIP_LEVELS];
unsigned offsets[M32_MIP_LEVELS];
int flags;
int contents;
int value;
float scale_x, scale_y;
int mip_scale;
// detail texturing info
char dt_name[128]; // detailed texture name
float dt_scale_x, dt_scale_y;
float dt_u, dt_v;
float dt_alpha;
int dt_src_blend_mode, dt_dst_blend_mode;
int unused[20]; // future expansion to maintain compatibility with h2
} m32tex_t;
/* .BSP file format */
#define IDBSPHEADER (('P' << 24) + ('S' << 16) + ('B' << 8) + 'I') /* little-endian "IBSP" */
#define BSPVERSION 38
/* upper design bounds: leaffaces, leafbrushes, planes, and
* verts are still bounded by 16 bit short limits */
#define MAX_MAP_MODELS 1024
#define MAX_MAP_BRUSHES 8192
#define MAX_MAP_ENTITIES 2048
#define MAX_MAP_ENTSTRING 0x40000
#define MAX_MAP_TEXINFO 8192
#define MAX_MAP_AREAS 256
#define MAX_MAP_AREAPORTALS 1024
#define MAX_MAP_PLANES 65536
#define MAX_MAP_NODES 65536
#define MAX_MAP_BRUSHSIDES 65536
#define MAX_MAP_LEAFS 65536
#define MAX_MAP_VERTS 65536
#define MAX_MAP_FACES 65536
#define MAX_MAP_LEAFFACES 65536
#define MAX_MAP_LEAFBRUSHES 65536
#define MAX_MAP_PORTALS 65536
#define MAX_MAP_EDGES 128000
#define MAX_MAP_SURFEDGES 256000
#define MAX_MAP_LIGHTING 0x200000
#define MAX_MAP_VISIBILITY 0x100000
/* key / value pair sizes */
#define MAX_KEY 32
#define MAX_VALUE 1024
/* ================================================================== */
typedef struct
{
int fileofs, filelen;
} lump_t;
#define LUMP_ENTITIES 0
#define LUMP_PLANES 1
#define LUMP_VERTEXES 2
#define LUMP_VISIBILITY 3
#define LUMP_NODES 4
#define LUMP_TEXINFO 5
#define LUMP_FACES 6
#define LUMP_LIGHTING 7
#define LUMP_LEAFS 8
#define LUMP_LEAFFACES 9
#define LUMP_LEAFBRUSHES 10
#define LUMP_EDGES 11
#define LUMP_SURFEDGES 12
#define LUMP_MODELS 13
#define LUMP_BRUSHES 14
#define LUMP_BRUSHSIDES 15
#define LUMP_POP 16
#define LUMP_AREAS 17
#define LUMP_AREAPORTALS 18
#define HEADER_LUMPS 19
typedef struct
{
int ident;
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
typedef struct
{
float mins[3], maxs[3];
float origin[3]; /* for sounds or lights */
int headnode;
int firstface, numfaces; /* submodels just draw faces without
walking the bsp tree */
} dmodel_t;
typedef struct
{
float point[3];
} dvertex_t;
/* 0-2 are axial planes */
#define PLANE_X 0
#define PLANE_Y 1
#define PLANE_Z 2
/* 3-5 are non-axial planes snapped to the nearest */
#define PLANE_ANYX 3
#define PLANE_ANYY 4
#define PLANE_ANYZ 5
/* planes (x&~1) and (x&~1)+1 are always opposites */
typedef struct
{
float normal[3];
float dist;
int type; /* PLANE_X - PLANE_ANYZ */
} dplane_t;
/* contents flags are seperate bits
* - given brush can contribute multiple content bits
* - multiple brushes can be in a single leaf */
/* lower bits are stronger, and will eat weaker brushes completely */
#define CONTENTS_SOLID 1 /* an eye is never valid in a solid */
#define CONTENTS_WINDOW 2 /* translucent, but not watery */
#define CONTENTS_AUX 4
#define CONTENTS_LAVA 8
#define CONTENTS_SLIME 16
#define CONTENTS_WATER 32
#define CONTENTS_MIST 64
#define LAST_VISIBLE_CONTENTS 64
/* remaining contents are non-visible, and don't eat brushes */
#define CONTENTS_AREAPORTAL 0x8000
#define CONTENTS_PLAYERCLIP 0x10000
#define CONTENTS_MONSTERCLIP 0x20000
/* currents can be added to any other contents, and may be mixed */
#define CONTENTS_CURRENT_0 0x40000
#define CONTENTS_CURRENT_90 0x80000
#define CONTENTS_CURRENT_180 0x100000
#define CONTENTS_CURRENT_270 0x200000
#define CONTENTS_CURRENT_UP 0x400000
#define CONTENTS_CURRENT_DOWN 0x800000
#define CONTENTS_ORIGIN 0x1000000 /* removed before bsping an entity */
#define CONTENTS_MONSTER 0x2000000 /* should never be on a brush, only in game */
#define CONTENTS_DEADMONSTER 0x4000000
#define CONTENTS_DETAIL 0x8000000 /* brushes to be added after vis leafs */
#define CONTENTS_TRANSLUCENT 0x10000000 /* auto set if any surface has trans */
#define CONTENTS_LADDER 0x20000000
#define SURF_LIGHT 0x1 /* value will hold the light strength */
#define SURF_SLICK 0x2 /* effects game physics */
#define SURF_SKY 0x4 /* don't draw, but add to skybox */
#define SURF_WARP 0x8 /* turbulent water warp */
#define SURF_TRANS33 0x10
#define SURF_TRANS66 0x20
#define SURF_FLOWING 0x40 /* scroll towards angle */
#define SURF_NODRAW 0x80 /* don't bother referencing the texture */
typedef struct
{
int planenum;
int children[2]; /* negative numbers are -(leafs+1), not nodes */
short mins[3]; /* for frustom culling */
short maxs[3];
unsigned short firstface;
unsigned short numfaces; /* counting both sides */
} dnode_t;
typedef struct texinfo_s
{
float vecs[2][4]; /* [s/t][xyz offset] */
int flags; /* miptex flags + overrides light emission, etc */
int value;
char texture[32]; /* texture name (textures*.wal) */
int nexttexinfo; /* for animations, -1 = end of chain */
} texinfo_t;
/* note that edge 0 is never used, because negative edge
nums are used for counterclockwise use of the edge in
a face */
typedef struct
{
unsigned short v[2]; /* vertex numbers */
} dedge_t;
#define MAXLIGHTMAPS 4
typedef struct
{
unsigned short planenum;
short side;
int firstedge; /* we must support > 64k edges */
short numedges;
short texinfo;
/* lighting info */
byte styles[MAXLIGHTMAPS];
int lightofs; /* start of [numstyles*surfsize] samples */
} dface_t;
typedef struct
{
int contents; /* OR of all brushes (not needed?) */
short cluster;
short area;
short mins[3]; /* for frustum culling */
short maxs[3];
unsigned short firstleafface;
unsigned short numleaffaces;
unsigned short firstleafbrush;
unsigned short numleafbrushes;
} dleaf_t;
typedef struct
{
unsigned short planenum; /* facing out of the leaf */
short texinfo;
} dbrushside_t;
typedef struct
{
int firstside;
int numsides;
int contents;
} dbrush_t;
#define ANGLE_UP -1
#define ANGLE_DOWN -2
/* the visibility lump consists of a header with a count, then
* byte offsets for the PVS and PHS of each cluster, then the raw
* compressed bit vectors */
#define DVIS_PVS 0
#define DVIS_PHS 1
typedef struct
{
int numclusters;
int bitofs[8][2]; /* bitofs[numclusters][2] */
} dvis_t;
/* each area has a list of portals that lead into other areas
* when portals are closed, other areas may not be visible or
* hearable even if the vis info says that it should be */
typedef struct
{
int portalnum;
int otherarea;
} dareaportal_t;
typedef struct
{
int numareaportals;
int firstareaportal;
} darea_t;
#endif

1229
src/common/header/shared.h Normal file

File diff suppressed because it is too large Load diff

224
src/common/md4.c Normal file
View file

@ -0,0 +1,224 @@
/*
* Public Domain C source implementation of RFC 1320
* - The MD4 Message-Digest Algorithm -
*
* http://www.faqs.org/rfcs/rfc1320.html
* by Steven Fuller
*/
#include <inttypes.h>
#define ROTATELEFT32(x, s) (((x) << (s)) | ((x) >> (32 - (s))))
#define F(X, Y, Z) (((X)&(Y)) | ((~X) & (Z)))
#define G(X, Y, Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)))
#define H(X, Y, Z) ((X) ^ (Y) ^ (Z))
#define S(a, b, c, d, k, s) \
{ \
a += (F((b), (c), (d)) + X[(k)]); \
a = ROTATELEFT32(a, s); \
}
#define T(a, b, c, d, k, s) \
{ \
a += (G((b), (c), (d)) + X[(k)] + 0x5A827999); \
a = ROTATELEFT32(a, s); \
}
#define U(a, b, c, d, k, s) \
{ \
a += (H((b), (c), (d)) + X[(k)] + 0x6ED9EBA1); \
a = ROTATELEFT32(a, s); \
}
static uint32_t X[16];
static uint32_t A, AA;
static uint32_t B, BB;
static uint32_t C, CC;
static uint32_t D, DD;
static void
DoMD4()
{
AA = A;
BB = B;
CC = C;
DD = D;
S(A, B, C, D, 0, 3);
S(D, A, B, C, 1, 7);
S(C, D, A, B, 2, 11);
S(B, C, D, A, 3, 19);
S(A, B, C, D, 4, 3);
S(D, A, B, C, 5, 7);
S(C, D, A, B, 6, 11);
S(B, C, D, A, 7, 19);
S(A, B, C, D, 8, 3);
S(D, A, B, C, 9, 7);
S(C, D, A, B, 10, 11);
S(B, C, D, A, 11, 19);
S(A, B, C, D, 12, 3);
S(D, A, B, C, 13, 7);
S(C, D, A, B, 14, 11);
S(B, C, D, A, 15, 19);
T(A, B, C, D, 0, 3);
T(D, A, B, C, 4, 5);
T(C, D, A, B, 8, 9);
T(B, C, D, A, 12, 13);
T(A, B, C, D, 1, 3);
T(D, A, B, C, 5, 5);
T(C, D, A, B, 9, 9);
T(B, C, D, A, 13, 13);
T(A, B, C, D, 2, 3);
T(D, A, B, C, 6, 5);
T(C, D, A, B, 10, 9);
T(B, C, D, A, 14, 13);
T(A, B, C, D, 3, 3);
T(D, A, B, C, 7, 5);
T(C, D, A, B, 11, 9);
T(B, C, D, A, 15, 13);
U(A, B, C, D, 0, 3);
U(D, A, B, C, 8, 9);
U(C, D, A, B, 4, 11);
U(B, C, D, A, 12, 15);
U(A, B, C, D, 2, 3);
U(D, A, B, C, 10, 9);
U(C, D, A, B, 6, 11);
U(B, C, D, A, 14, 15);
U(A, B, C, D, 1, 3);
U(D, A, B, C, 9, 9);
U(C, D, A, B, 5, 11);
U(B, C, D, A, 13, 15);
U(A, B, C, D, 3, 3);
U(D, A, B, C, 11, 9);
U(C, D, A, B, 7, 11);
U(B, C, D, A, 15, 15);
A += AA;
B += BB;
C += CC;
D += DD;
}
static void
PerformMD4(const unsigned char *buf, int length, unsigned char *digest)
{
int len = length / 64; /* number of full blocks */
int rem = length % 64; /* number of left over bytes */
int i, j;
const unsigned char *ptr = buf;
/* initialize the MD buffer */
A = 0x67452301;
B = 0xEFCDAB89;
C = 0x98BADCFE;
D = 0x10325476;
for (i = 0; i < len; i++)
{
for (j = 0; j < 16; j++)
{
X[j] = ((ptr[0] << 0) | (ptr[1] << 8) |
(ptr[2] << 16) | (ptr[3] << 24));
ptr += 4;
}
DoMD4();
}
i = rem / 4;
for (j = 0; j < i; j++)
{
X[j] = ((ptr[0] << 0) | (ptr[1] << 8) |
(ptr[2] << 16) | (ptr[3] << 24));
ptr += 4;
}
switch (rem % 4)
{
case 0:
X[j] = 0x80U;
break;
case 1:
X[j] = ((ptr[0] << 0) | ((0x80U) << 8));
break;
case 2:
X[j] = ((ptr[0] << 0) | (ptr[1] << 8) | ((0x80U) << 16));
break;
case 3:
X[j] =
((ptr[0] <<
0) | (ptr[1] << 8) | (ptr[2] << 16) | ((0x80U) << 24));
break;
}
j++;
if (j > 14)
{
for ( ; j < 16; j++)
{
X[j] = 0;
}
DoMD4();
j = 0;
}
for ( ; j < 14; j++)
{
X[j] = 0;
}
X[14] = (length & 0x1FFFFFFF) << 3;
X[15] = (length & ~0x1FFFFFFF) >> 29;
DoMD4();
digest[0] = (A & 0x000000FF) >> 0;
digest[1] = (A & 0x0000FF00) >> 8;
digest[2] = (A & 0x00FF0000) >> 16;
digest[3] = (A & 0xFF000000) >> 24;
digest[4] = (B & 0x000000FF) >> 0;
digest[5] = (B & 0x0000FF00) >> 8;
digest[6] = (B & 0x00FF0000) >> 16;
digest[7] = (B & 0xFF000000) >> 24;
digest[8] = (C & 0x000000FF) >> 0;
digest[9] = (C & 0x0000FF00) >> 8;
digest[10] = (C & 0x00FF0000) >> 16;
digest[11] = (C & 0xFF000000) >> 24;
digest[12] = (D & 0x000000FF) >> 0;
digest[13] = (D & 0x0000FF00) >> 8;
digest[14] = (D & 0x00FF0000) >> 16;
digest[15] = (D & 0xFF000000) >> 24;
A = AA = 0;
B = BB = 0;
C = CC = 0;
D = DD = 0;
for (j = 0; j < 16; j++)
{
X[j] = 0;
}
}
unsigned
Com_BlockChecksum(void *buffer, int length)
{
uint32_t digest[4];
unsigned val;
PerformMD4((unsigned char *)buffer, length, (unsigned char *)digest);
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
return val;
}

1412
src/common/shared/shared.c Normal file

File diff suppressed because it is too large Load diff