--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, 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 Library 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.
+\f
+ 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.)
+\f
+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.
+\f
+ 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.
+\f
+ 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
+\f
--- /dev/null
+# Microsoft Developer Studio Project File - Name="Unpack" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Java Virtual Machine Java Project" 0x0809
+
+CFG=Unpack - Java Virtual Machine Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "Unpack.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Unpack.mak" CFG="Unpack - Java Virtual Machine Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Unpack - Java Virtual Machine Release" (based on\
+ "Java Virtual Machine Java Project")
+!MESSAGE "Unpack - Java Virtual Machine Debug" (based on\
+ "Java Virtual Machine Java Project")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+JAVA=jvc.exe
+
+!IF "$(CFG)" == "Unpack - Java Virtual Machine Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Target_Dir ""
+# ADD BASE JAVA /O
+# ADD JAVA /O
+
+!ELSEIF "$(CFG)" == "Unpack - Java Virtual Machine Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Target_Dir ""
+# ADD BASE JAVA /g
+# ADD JAVA /g
+
+!ENDIF
+
+# Begin Target
+
+# Name "Unpack - Java Virtual Machine Release"
+# Name "Unpack - Java Virtual Machine Debug"
+# Begin Source File
+
+SOURCE=.\Unpack.java
+# End Source File
+# End Target
+# End Project
--- /dev/null
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Unpack"=.\Unpack.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+/*
+ * Unpack -- a completely non-object oriented utility...
+ *
+ */
+
+import java.io.*;
+
+class Unpack {
+ static final int IDPAKHEADER = (('K'<<24)+('C'<<16)+('A'<<8)+'P');
+
+ static int intSwap(int i) {
+ int a, b, c, d;
+
+ a = i & 255;
+ b = (i >> 8) & 255;
+ c = (i >> 16) & 255;
+ d = (i >> 24) & 255;
+
+ return (a << 24) + (b << 16) + (c << 8) + d;
+ }
+
+ static boolean patternMatch (String pattern, String s) {
+ int index;
+ int remaining;
+
+ if (pattern.equals(s)) {
+ return true;
+ }
+
+ // fairly lame single wildcard matching
+ index = pattern.indexOf('*');
+ if (index == -1) {
+ return false;
+ }
+ if (!pattern.regionMatches(0, s, 0, index)) {
+ return false;
+ }
+
+ index += 1; // skip the *
+ remaining = pattern.length() - index;
+ if (s.length() < remaining) {
+ return false;
+ }
+
+ if (!pattern.regionMatches(index, s, s.length()-remaining, remaining)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ static void usage() {
+ System.out.println ("Usage: unpack <packfile> <match> <basedir>");
+ System.out.println (" or: unpack -list <packfile>");
+ System.out.println ("<match> may contain a single * wildcard");
+ System.exit (1);
+ }
+
+ public static void main (String[] args) {
+ int ident;
+ int dirofs;
+ int dirlen;
+ int i;
+ int numLumps;
+ byte[] name = new byte[56];
+ String nameString;
+ int filepos;
+ int filelen;
+ RandomAccessFile readLump;
+ DataInputStream directory;
+ String pakName;
+ String pattern;
+
+ if (args.length == 2) {
+ if (!args[0].equals("-list")) {
+ usage();
+ }
+ pakName = args[1];
+ pattern = null;
+ } else if (args.length == 3) {
+ pakName = args[0];
+ pattern = args[1];
+ } else {
+ pakName = null;
+ pattern = null;
+ usage ();
+ }
+
+ try {
+ // one stream to read the directory
+ directory = new DataInputStream(new FileInputStream(pakName));
+
+ // another to read lumps
+ readLump = new RandomAccessFile(pakName, "r");
+
+ // read the header
+ ident = intSwap(directory.readInt());
+ dirofs = intSwap(directory.readInt());
+ dirlen = intSwap(directory.readInt());
+
+ if (ident != IDPAKHEADER) {
+ System.out.println ( pakName + " is not a pakfile.");
+ System.exit (1);
+ }
+
+ // read the directory
+ directory.skipBytes (dirofs - 12);
+ numLumps = dirlen / 64;
+
+ System.out.println (numLumps + " lumps in " + pakName);
+
+ for (i = 0 ; i < numLumps ; i++) {
+ directory.readFully(name);
+ filepos = intSwap(directory.readInt());
+ filelen = intSwap(directory.readInt());
+
+ nameString = new String (name, 0);
+ // chop to the first 0 byte
+ nameString = nameString.substring (0, nameString.indexOf(0));
+
+ if (pattern == null) {
+ // listing mode
+ System.out.println (nameString + " : " + filelen + "bytes");
+ } else if (patternMatch (pattern, nameString) ) {
+ File writeFile;
+ DataOutputStream writeLump;
+ byte[] buffer = new byte[filelen];
+ StringBuffer fixedString;
+ String finalName;
+ int index;
+
+ System.out.println ("Unpaking " + nameString + " " + filelen
+ + " bytes");
+
+ // load the lump
+ readLump.seek(filepos);
+ readLump.readFully(buffer);
+
+ // quake uses forward slashes, but java requires
+ // they only by the host's seperator, which
+ // varies from win to unix
+ fixedString = new StringBuffer (args[2] + File.separator + nameString);
+ for (index = 0 ; index < fixedString.length() ; index++) {
+ if (fixedString.charAt(index) == '/') {
+ fixedString.setCharAt(index, File.separatorChar);
+ }
+ }
+ finalName = fixedString.toString ();
+
+ index = finalName.lastIndexOf(File.separatorChar);
+ if (index != -1) {
+ String finalPath;
+ File writePath;
+
+ finalPath = finalName.substring(0, index);
+ writePath = new File (finalPath);
+ writePath.mkdirs();
+ }
+
+ writeFile = new File (finalName);
+ writeLump = new DataOutputStream ( new FileOutputStream(writeFile) );
+ writeLump.write(buffer);
+ writeLump.close();
+
+ }
+ }
+
+ readLump.close();
+ directory.close();
+
+ } catch (IOException e) {
+ System.out.println ( e.toString() );
+ }
+ }
+
+}
--- /dev/null
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=bspinfo3 - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to bspinfo3 - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "bsp - Win32 Release" && "$(CFG)" != "bsp - Win32 Debug" &&\
+ "$(CFG)" != "qbsp3 - Win32 Release" && "$(CFG)" != "qbsp3 - Win32 Debug" &&\
+ "$(CFG)" != "qvis3 - Win32 Release" && "$(CFG)" != "qvis3 - Win32 Debug" &&\
+ "$(CFG)" != "qrad3 - Win32 Release" && "$(CFG)" != "qrad3 - Win32 Debug" &&\
+ "$(CFG)" != "bspinfo3 - Win32 Release" && "$(CFG)" != "bspinfo3 - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "bsp.mak" CFG="bspinfo3 - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "bsp - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "bsp - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "qbsp3 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qbsp3 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "qvis3 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qvis3 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "qrad3 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qrad3 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "bspinfo3 - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "bspinfo3 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "bspinfo3 - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "bsp - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL :
+
+CLEAN :
+ -@erase
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/bsp.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bsp.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/bsp.pdb" /machine:I386\
+ /out:"$(OUTDIR)/bsp.exe"
+LINK32_OBJS= \
+
+
+!ELSEIF "$(CFG)" == "bsp - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL :
+
+CLEAN :
+ -@erase
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/bsp.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bsp.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/bsp.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/bsp.exe"
+LINK32_OBJS= \
+
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "qbsp3\Release"
+# PROP BASE Intermediate_Dir "qbsp3\Release"
+# PROP BASE Target_Dir "qbsp3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "qbsp3\Release"
+# PROP Intermediate_Dir "qbsp3\Release"
+# PROP Target_Dir "qbsp3"
+OUTDIR=.\qbsp3\Release
+INTDIR=.\qbsp3\Release
+
+ALL : "$(OUTDIR)\qbsp3.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\brushbsp.obj"
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\csg.obj"
+ -@erase "$(INTDIR)\faces.obj"
+ -@erase "$(INTDIR)\gldraw.obj"
+ -@erase "$(INTDIR)\glfile.obj"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\leakfile.obj"
+ -@erase "$(INTDIR)\map.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\polylib.obj"
+ -@erase "$(INTDIR)\portals.obj"
+ -@erase "$(INTDIR)\prtfile.obj"
+ -@erase "$(INTDIR)\qbsp3.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\textures.obj"
+ -@erase "$(INTDIR)\threads.obj"
+ -@erase "$(INTDIR)\tree.obj"
+ -@erase "$(INTDIR)\writebsp.obj"
+ -@erase "$(OUTDIR)\qbsp3.exe"
+ -@erase "$(OUTDIR)\qbsp3.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qbsp3.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\qbsp3\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qbsp3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/qbsp3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qbsp3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\brushbsp.obj" \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\csg.obj" \
+ "$(INTDIR)\faces.obj" \
+ "$(INTDIR)\gldraw.obj" \
+ "$(INTDIR)\glfile.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\leakfile.obj" \
+ "$(INTDIR)\map.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\polylib.obj" \
+ "$(INTDIR)\portals.obj" \
+ "$(INTDIR)\prtfile.obj" \
+ "$(INTDIR)\qbsp3.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\textures.obj" \
+ "$(INTDIR)\threads.obj" \
+ "$(INTDIR)\tree.obj" \
+ "$(INTDIR)\writebsp.obj"
+
+"$(OUTDIR)\qbsp3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "qbsp3\Debug"
+# PROP BASE Intermediate_Dir "qbsp3\Debug"
+# PROP BASE Target_Dir "qbsp3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "qbsp3\Debug"
+# PROP Intermediate_Dir "qbsp3\Debug"
+# PROP Target_Dir "qbsp3"
+OUTDIR=.\qbsp3\Debug
+INTDIR=.\qbsp3\Debug
+
+ALL : "$(OUTDIR)\qbsp3.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\brushbsp.obj"
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\csg.obj"
+ -@erase "$(INTDIR)\faces.obj"
+ -@erase "$(INTDIR)\gldraw.obj"
+ -@erase "$(INTDIR)\glfile.obj"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\leakfile.obj"
+ -@erase "$(INTDIR)\map.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\polylib.obj"
+ -@erase "$(INTDIR)\portals.obj"
+ -@erase "$(INTDIR)\prtfile.obj"
+ -@erase "$(INTDIR)\qbsp3.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\textures.obj"
+ -@erase "$(INTDIR)\threads.obj"
+ -@erase "$(INTDIR)\tree.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(INTDIR)\writebsp.obj"
+ -@erase "$(OUTDIR)\qbsp3.exe"
+ -@erase "$(OUTDIR)\qbsp3.ilk"
+ -@erase "$(OUTDIR)\qbsp3.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qbsp3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\qbsp3\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qbsp3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/qbsp3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qbsp3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\brushbsp.obj" \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\csg.obj" \
+ "$(INTDIR)\faces.obj" \
+ "$(INTDIR)\gldraw.obj" \
+ "$(INTDIR)\glfile.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\leakfile.obj" \
+ "$(INTDIR)\map.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\polylib.obj" \
+ "$(INTDIR)\portals.obj" \
+ "$(INTDIR)\prtfile.obj" \
+ "$(INTDIR)\qbsp3.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\textures.obj" \
+ "$(INTDIR)\threads.obj" \
+ "$(INTDIR)\tree.obj" \
+ "$(INTDIR)\writebsp.obj"
+
+"$(OUTDIR)\qbsp3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qvis3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "qvis3\Release"
+# PROP BASE Intermediate_Dir "qvis3\Release"
+# PROP BASE Target_Dir "qvis3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "qvis3\Release"
+# PROP Intermediate_Dir "qvis3\Release"
+# PROP Target_Dir "qvis3"
+OUTDIR=.\qvis3\Release
+INTDIR=.\qvis3\Release
+
+ALL : "$(OUTDIR)\qvis3.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\flow.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\qvis3.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\threads.obj"
+ -@erase "$(OUTDIR)\qvis3.exe"
+ -@erase "$(OUTDIR)\qvis3.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qvis3.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\qvis3\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qvis3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/qvis3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qvis3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\flow.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\qvis3.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\threads.obj"
+
+"$(OUTDIR)\qvis3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qvis3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "qvis3\Debug"
+# PROP BASE Intermediate_Dir "qvis3\Debug"
+# PROP BASE Target_Dir "qvis3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "qvis3\Debug"
+# PROP Intermediate_Dir "qvis3\Debug"
+# PROP Target_Dir "qvis3"
+OUTDIR=.\qvis3\Debug
+INTDIR=.\qvis3\Debug
+
+ALL : "$(OUTDIR)\qvis3.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\flow.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\qvis3.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\threads.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\qvis3.exe"
+ -@erase "$(OUTDIR)\qvis3.ilk"
+ -@erase "$(OUTDIR)\qvis3.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qvis3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\qvis3\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qvis3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/qvis3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qvis3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\flow.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\qvis3.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\threads.obj"
+
+"$(OUTDIR)\qvis3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qrad3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "qrad3\Release"
+# PROP BASE Intermediate_Dir "qrad3\Release"
+# PROP BASE Target_Dir "qrad3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "qrad3\Release"
+# PROP Intermediate_Dir "qrad3\Release"
+# PROP Target_Dir "qrad3"
+OUTDIR=.\qrad3\Release
+INTDIR=.\qrad3\Release
+
+ALL : "$(OUTDIR)\qrad3.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\lightmap.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\patches.obj"
+ -@erase "$(INTDIR)\polylib.obj"
+ -@erase "$(INTDIR)\qrad3.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\threads.obj"
+ -@erase "$(INTDIR)\trace.obj"
+ -@erase "$(OUTDIR)\qrad3.exe"
+ -@erase "$(OUTDIR)\qrad3.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qrad3.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\qrad3\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qrad3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/qrad3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qrad3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\lightmap.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\patches.obj" \
+ "$(INTDIR)\polylib.obj" \
+ "$(INTDIR)\qrad3.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\threads.obj" \
+ "$(INTDIR)\trace.obj"
+
+"$(OUTDIR)\qrad3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qrad3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "qrad3\Debug"
+# PROP BASE Intermediate_Dir "qrad3\Debug"
+# PROP BASE Target_Dir "qrad3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "qrad3\Debug"
+# PROP Intermediate_Dir "qrad3\Debug"
+# PROP Target_Dir "qrad3"
+OUTDIR=.\qrad3\Debug
+INTDIR=.\qrad3\Debug
+
+ALL : "$(OUTDIR)\qrad3.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\lightmap.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\patches.obj"
+ -@erase "$(INTDIR)\polylib.obj"
+ -@erase "$(INTDIR)\qrad3.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\threads.obj"
+ -@erase "$(INTDIR)\trace.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\qrad3.exe"
+ -@erase "$(OUTDIR)\qrad3.ilk"
+ -@erase "$(OUTDIR)\qrad3.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qrad3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\qrad3\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qrad3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/qrad3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qrad3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\lightmap.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\patches.obj" \
+ "$(INTDIR)\polylib.obj" \
+ "$(INTDIR)\qrad3.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\threads.obj" \
+ "$(INTDIR)\trace.obj"
+
+"$(OUTDIR)\qrad3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "bspinfo3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "bspinfo3\Release"
+# PROP BASE Intermediate_Dir "bspinfo3\Release"
+# PROP BASE Target_Dir "bspinfo3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "bspinfo3\Release"
+# PROP Intermediate_Dir "bspinfo3\Release"
+# PROP Target_Dir "bspinfo3"
+OUTDIR=.\bspinfo3\Release
+INTDIR=.\bspinfo3\Release
+
+ALL : "$(OUTDIR)\bspinfo3.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\bspinfo3.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(OUTDIR)\bspinfo3.exe"
+ -@erase "$(OUTDIR)\bspinfo3.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/bspinfo3.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\bspinfo3\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bspinfo3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/bspinfo3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/bspinfo3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\bspinfo3.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\scriplib.obj"
+
+"$(OUTDIR)\bspinfo3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "bspinfo3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "bspinfo3\Debug"
+# PROP BASE Intermediate_Dir "bspinfo3\Debug"
+# PROP BASE Target_Dir "bspinfo3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "bspinfo3\Debug"
+# PROP Intermediate_Dir "bspinfo3\Debug"
+# PROP Target_Dir "bspinfo3"
+OUTDIR=.\bspinfo3\Debug
+INTDIR=.\bspinfo3\Debug
+
+ALL : "$(OUTDIR)\bspinfo3.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\bspinfo3.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\bspinfo3.exe"
+ -@erase "$(OUTDIR)\bspinfo3.ilk"
+ -@erase "$(OUTDIR)\bspinfo3.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/bspinfo3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/"\
+ /c
+CPP_OBJS=.\bspinfo3\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bspinfo3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/bspinfo3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/bspinfo3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\bspinfo3.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\scriplib.obj"
+
+"$(OUTDIR)\bspinfo3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "bsp - Win32 Release"
+# Name "bsp - Win32 Debug"
+
+!IF "$(CFG)" == "bsp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "bsp - Win32 Debug"
+
+!ENDIF
+
+# End Target
+################################################################################
+# Begin Target
+
+# Name "qbsp3 - Win32 Release"
+# Name "qbsp3 - Win32 Debug"
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\writebsp.c
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+DEP_CPP_WRITE=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\writebsp.obj" : $(SOURCE) $(DEP_CPP_WRITE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+DEP_CPP_WRITE=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\writebsp.obj" : $(SOURCE) $(DEP_CPP_WRITE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\brushbsp.c
+DEP_CPP_BRUSH=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\brushbsp.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\csg.c
+DEP_CPP_CSG_C=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\faces.c
+DEP_CPP_FACES=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\faces.obj" : $(SOURCE) $(DEP_CPP_FACES) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\gldraw.c
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+DEP_CPP_GLDRA=\
+ ".\qbsp3\qbsp.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\gldraw.obj" : $(SOURCE) $(DEP_CPP_GLDRA) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+DEP_CPP_GLDRA=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\gldraw.obj" : $(SOURCE) $(DEP_CPP_GLDRA) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\glfile.c
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+DEP_CPP_GLFIL=\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\glfile.obj" : $(SOURCE) $(DEP_CPP_GLFIL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+DEP_CPP_GLFIL=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\glfile.obj" : $(SOURCE) $(DEP_CPP_GLFIL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\leakfile.c
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+DEP_CPP_LEAKF=\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\leakfile.obj" : $(SOURCE) $(DEP_CPP_LEAKF) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+DEP_CPP_LEAKF=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\leakfile.obj" : $(SOURCE) $(DEP_CPP_LEAKF) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\map.c
+DEP_CPP_MAP_C=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\portals.c
+DEP_CPP_PORTA=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\portals.obj" : $(SOURCE) $(DEP_CPP_PORTA) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\prtfile.c
+DEP_CPP_PRTFI=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\prtfile.obj" : $(SOURCE) $(DEP_CPP_PRTFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\qbsp.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\qbsp3.c
+DEP_CPP_QBSP3=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\qbsp3.obj" : $(SOURCE) $(DEP_CPP_QBSP3) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\textures.c
+DEP_CPP_TEXTU=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\tree.c
+DEP_CPP_TREE_=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+ "..\common\threads.h"\
+ ".\qbsp3\qbsp.h"\
+
+
+"$(INTDIR)\tree.obj" : $(SOURCE) $(DEP_CPP_TREE_) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.c
+DEP_CPP_THREA=\
+ "..\common\cmdlib.h"\
+ "..\common\threads.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ "..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\stat.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.c
+DEP_CPP_LBMLI=\
+ "..\common\cmdlib.h"\
+ "..\common\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\polylib.c
+DEP_CPP_POLYL=\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+
+
+"$(INTDIR)\polylib.obj" : $(SOURCE) $(DEP_CPP_POLYL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+ "..\common\cmdlib.h"\
+ "..\common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\polylib.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\qfiles.h
+
+!IF "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# End Target
+################################################################################
+# Begin Target
+
+# Name "qvis3 - Win32 Release"
+# Name "qvis3 - Win32 Debug"
+
+!IF "$(CFG)" == "qvis3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qvis3 - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\qvis3\vis.h
+
+!IF "$(CFG)" == "qvis3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qvis3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qvis3\qvis3.c
+DEP_CPP_QVIS3=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\qfiles.h"\
+ "..\common\threads.h"\
+ ".\qvis3\vis.h"\
+
+
+"$(INTDIR)\qvis3.obj" : $(SOURCE) $(DEP_CPP_QVIS3) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qvis3\flow.c
+DEP_CPP_FLOW_=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\qfiles.h"\
+ ".\qvis3\vis.h"\
+
+
+"$(INTDIR)\flow.obj" : $(SOURCE) $(DEP_CPP_FLOW_) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ "..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\stat.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+ "..\common\cmdlib.h"\
+ "..\common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.c
+DEP_CPP_THREA=\
+ "..\common\cmdlib.h"\
+ "..\common\threads.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+################################################################################
+# Begin Target
+
+# Name "qrad3 - Win32 Release"
+# Name "qrad3 - Win32 Debug"
+
+!IF "$(CFG)" == "qrad3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qrad3 - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\patches.c
+DEP_CPP_PATCH=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\lbmlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\threads.h"\
+ ".\qrad3\qrad.h"\
+
+
+"$(INTDIR)\patches.obj" : $(SOURCE) $(DEP_CPP_PATCH) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\lightmap.c
+DEP_CPP_LIGHT=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\lbmlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\threads.h"\
+ ".\qrad3\qrad.h"\
+
+
+"$(INTDIR)\lightmap.obj" : $(SOURCE) $(DEP_CPP_LIGHT) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\qrad.h
+
+!IF "$(CFG)" == "qrad3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qrad3 - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\qrad3.c
+DEP_CPP_QRAD3=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\lbmlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+ "..\common\qfiles.h"\
+ "..\common\threads.h"\
+ ".\qrad3\qrad.h"\
+
+
+"$(INTDIR)\qrad3.obj" : $(SOURCE) $(DEP_CPP_QRAD3) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\trace.c
+DEP_CPP_TRACE=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\qfiles.h"\
+
+
+"$(INTDIR)\trace.obj" : $(SOURCE) $(DEP_CPP_TRACE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.c
+DEP_CPP_THREA=\
+ "..\common\cmdlib.h"\
+ "..\common\threads.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ "..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\stat.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\polylib.c
+DEP_CPP_POLYL=\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\polylib.h"\
+
+
+"$(INTDIR)\polylib.obj" : $(SOURCE) $(DEP_CPP_POLYL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+ "..\common\cmdlib.h"\
+ "..\common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.c
+DEP_CPP_LBMLI=\
+ "..\common\cmdlib.h"\
+ "..\common\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+################################################################################
+# Begin Target
+
+# Name "bspinfo3 - Win32 Release"
+# Name "bspinfo3 - Win32 Debug"
+
+!IF "$(CFG)" == "bspinfo3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "bspinfo3 - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\bspinfo3\bspinfo3.c
+DEP_CPP_BSPIN=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\qfiles.h"\
+
+
+"$(INTDIR)\bspinfo3.obj" : $(SOURCE) $(DEP_CPP_BSPIN) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ "..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\stat.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\qfiles.h"\
+ "..\common\scriplib.h"\
+
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+ "..\common\cmdlib.h"\
+ "..\common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+void main (int argc, char **argv)
+{
+ int i;
+ char source[1024];
+ int size;
+ FILE *f;
+
+ if (argc == 1)
+ Error ("usage: bspinfo bspfile [bspfiles]");
+
+ for (i=1 ; i<argc ; i++)
+ {
+ printf ("---------------------\n");
+ strcpy (source, argv[i]);
+ DefaultExtension (source, ".bsp");
+ f = fopen (source, "rb");
+ if (f)
+ {
+ size = Q_filelength (f);
+ fclose (f);
+ }
+ else
+ size = 0;
+ printf ("%s: %i\n", source, size);
+
+ LoadBSPFile (source);
+ PrintBSPFileSizes ();
+ printf ("---------------------\n");
+ }
+}
--- /dev/null
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = bspinfo3
+EXE = $(ODIR)/bspinfo3
+all: $(EXE)
+
+_next:
+ make "CFLAGS = -c -g -I../../common -DDOUBLEVEC_T" "ODIR = next"
+
+_irix:
+ make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+
+_irixdebug:
+ make "CFLAGS = -c -O2 -g -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -g" "ODIR = irix"
+
+_irixinst:
+ make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+_irixclean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+ make "CFLAGS = -c -O4 -I../../common -threads -DDOUBLEVEC_T" "LDFLAGS = -threads" "ODIR = osf"
+
+clean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/bspinfo3.o $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/scriplib.o
+
+$(EXE) : $(FILES)
+ cc -o $(EXE) $(LDFLAGS) $(FILES)
+
+$(ODIR)/bspinfo3.o : bspinfo3.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../../common/cmdlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../../common/scriplib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../../common/bspfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+
+int c_nodes;
+int c_nonvis;
+int c_active_brushes;
+
+// if a brush just barely pokes onto the other side,
+// let it slide by without chopping
+#define PLANESIDE_EPSILON 0.001
+//0.1
+
+#define PSIDE_FRONT 1
+#define PSIDE_BACK 2
+#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
+#define PSIDE_FACING 4
+
+
+void FindBrushInTree (node_t *node, int brushnum)
+{
+ bspbrush_t *b;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ for (b=node->brushlist ; b ; b=b->next)
+ if (b->original->brushnum == brushnum)
+ printf ("here\n");
+ return;
+ }
+ FindBrushInTree (node->children[0], brushnum);
+ FindBrushInTree (node->children[1], brushnum);
+}
+
+//==================================================
+
+/*
+================
+DrawBrushList
+================
+*/
+void DrawBrushList (bspbrush_t *brush, node_t *node)
+{
+ int i;
+ side_t *s;
+
+ GLS_BeginScene ();
+ for ( ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ if (!s->winding)
+ continue;
+ if (s->texinfo == TEXINFO_NODE)
+ GLS_Winding (s->winding, 1);
+ else if (!s->visible)
+ GLS_Winding (s->winding, 2);
+ else
+ GLS_Winding (s->winding, 0);
+ }
+ }
+ GLS_EndScene ();
+}
+
+/*
+================
+WriteBrushList
+================
+*/
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
+{
+ int i;
+ side_t *s;
+ FILE *f;
+
+ qprintf ("writing %s\n", name);
+ f = SafeOpenWrite (name);
+
+ for ( ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ if (!s->winding)
+ continue;
+ if (onlyvis && !s->visible)
+ continue;
+ OutputWinding (brush->sides[i].winding, f);
+ }
+ }
+
+ fclose (f);
+}
+
+void PrintBrush (bspbrush_t *brush)
+{
+ int i;
+
+ printf ("brush: %p\n", brush);
+ for (i=0;i<brush->numsides ; i++)
+ {
+ pw(brush->sides[i].winding);
+ printf ("\n");
+ }
+}
+
+/*
+==================
+BoundBrush
+
+Sets the mins/maxs based on the windings
+==================
+*/
+void BoundBrush (bspbrush_t *brush)
+{
+ int i, j;
+ winding_t *w;
+
+ ClearBounds (brush->mins, brush->maxs);
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ AddPointToBounds (w->p[j], brush->mins, brush->maxs);
+ }
+}
+
+/*
+==================
+CreateBrushWindings
+
+==================
+*/
+void CreateBrushWindings (bspbrush_t *brush)
+{
+ int i, j;
+ winding_t *w;
+ side_t *side;
+ plane_t *plane;
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = &brush->sides[i];
+ plane = &mapplanes[side->planenum];
+ w = BaseWindingForPlane (plane->normal, plane->dist);
+ for (j=0 ; j<brush->numsides && w; j++)
+ {
+ if (i == j)
+ continue;
+ if (brush->sides[j].bevel)
+ continue;
+ plane = &mapplanes[brush->sides[j].planenum^1];
+ ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
+ }
+
+ side->winding = w;
+ }
+
+ BoundBrush (brush);
+}
+
+/*
+==================
+BrushFromBounds
+
+Creates a new axial brush
+==================
+*/
+bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
+{
+ bspbrush_t *b;
+ int i;
+ vec3_t normal;
+ vec_t dist;
+
+ b = AllocBrush (6);
+ b->numsides = 6;
+ for (i=0 ; i<3 ; i++)
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ dist = maxs[i];
+ b->sides[i].planenum = FindFloatPlane (normal, dist);
+
+ normal[i] = -1;
+ dist = -mins[i];
+ b->sides[3+i].planenum = FindFloatPlane (normal, dist);
+ }
+
+ CreateBrushWindings (b);
+
+ return b;
+}
+
+/*
+==================
+BrushVolume
+
+==================
+*/
+vec_t BrushVolume (bspbrush_t *brush)
+{
+ int i;
+ winding_t *w;
+ vec3_t corner;
+ vec_t d, area, volume;
+ plane_t *plane;
+
+ if (!brush)
+ return 0;
+
+ // grab the first valid point as the corner
+
+ w = NULL;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (w)
+ break;
+ }
+ if (!w)
+ return 0;
+ VectorCopy (w->p[0], corner);
+
+ // make tetrahedrons to all other faces
+
+ volume = 0;
+ for ( ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ plane = &mapplanes[brush->sides[i].planenum];
+ d = -(DotProduct (corner, plane->normal) - plane->dist);
+ area = WindingArea (w);
+ volume += d*area;
+ }
+
+ volume /= 3;
+ return volume;
+}
+
+/*
+================
+CountBrushList
+================
+*/
+int CountBrushList (bspbrush_t *brushes)
+{
+ int c;
+
+ c = 0;
+ for ( ; brushes ; brushes = brushes->next)
+ c++;
+ return c;
+}
+
+/*
+================
+AllocTree
+================
+*/
+tree_t *AllocTree (void)
+{
+ tree_t *tree;
+
+ tree = malloc(sizeof(*tree));
+ memset (tree, 0, sizeof(*tree));
+ ClearBounds (tree->mins, tree->maxs);
+
+ return tree;
+}
+
+/*
+================
+AllocNode
+================
+*/
+node_t *AllocNode (void)
+{
+ node_t *node;
+
+ node = malloc(sizeof(*node));
+ memset (node, 0, sizeof(*node));
+
+ return node;
+}
+
+
+/*
+================
+AllocBrush
+================
+*/
+bspbrush_t *AllocBrush (int numsides)
+{
+ bspbrush_t *bb;
+ int c;
+
+ c = (int)&(((bspbrush_t *)0)->sides[numsides]);
+ bb = malloc(c);
+ memset (bb, 0, c);
+ if (numthreads == 1)
+ c_active_brushes++;
+ return bb;
+}
+
+/*
+================
+FreeBrush
+================
+*/
+void FreeBrush (bspbrush_t *brushes)
+{
+ int i;
+
+ for (i=0 ; i<brushes->numsides ; i++)
+ if (brushes->sides[i].winding)
+ FreeWinding(brushes->sides[i].winding);
+ free (brushes);
+ if (numthreads == 1)
+ c_active_brushes--;
+}
+
+
+/*
+================
+FreeBrushList
+================
+*/
+void FreeBrushList (bspbrush_t *brushes)
+{
+ bspbrush_t *next;
+
+ for ( ; brushes ; brushes = next)
+ {
+ next = brushes->next;
+
+ FreeBrush (brushes);
+ }
+}
+
+/*
+==================
+CopyBrush
+
+Duplicates the brush, the sides, and the windings
+==================
+*/
+bspbrush_t *CopyBrush (bspbrush_t *brush)
+{
+ bspbrush_t *newbrush;
+ int size;
+ int i;
+
+ size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
+
+ newbrush = AllocBrush (brush->numsides);
+ memcpy (newbrush, brush, size);
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ if (brush->sides[i].winding)
+ newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
+ }
+
+ return newbrush;
+}
+
+
+/*
+==================
+PointInLeaf
+
+==================
+*/
+node_t *PointInLeaf (node_t *node, vec3_t point)
+{
+ vec_t d;
+ plane_t *plane;
+
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &mapplanes[node->planenum];
+ d = DotProduct (point, plane->normal) - plane->dist;
+ if (d > 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ return node;
+}
+
+//========================================================
+
+/*
+==============
+BoxOnPlaneSide
+
+Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH
+==============
+*/
+int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane)
+{
+ int side;
+ int i;
+ vec3_t corners[2];
+ vec_t dist1, dist2;
+
+ // axial planes are easy
+ if (plane->type < 3)
+ {
+ side = 0;
+ if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON)
+ side |= PSIDE_FRONT;
+ if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON)
+ side |= PSIDE_BACK;
+ return side;
+ }
+
+ // create the proper leading and trailing verts for the box
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (plane->normal[i] < 0)
+ {
+ corners[0][i] = mins[i];
+ corners[1][i] = maxs[i];
+ }
+ else
+ {
+ corners[1][i] = mins[i];
+ corners[0][i] = maxs[i];
+ }
+ }
+
+ dist1 = DotProduct (plane->normal, corners[0]) - plane->dist;
+ dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
+ side = 0;
+ if (dist1 >= PLANESIDE_EPSILON)
+ side = PSIDE_FRONT;
+ if (dist2 < PLANESIDE_EPSILON)
+ side |= PSIDE_BACK;
+
+ return side;
+}
+
+/*
+============
+QuickTestBrushToPlanenum
+
+============
+*/
+int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits)
+{
+ int i, num;
+ plane_t *plane;
+ int s;
+
+ *numsplits = 0;
+
+ // if the brush actually uses the planenum,
+ // we can tell the side for sure
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ num = brush->sides[i].planenum;
+ if (num >= 0x10000)
+ Error ("bad planenum");
+ if (num == planenum)
+ return PSIDE_BACK|PSIDE_FACING;
+ if (num == (planenum ^ 1) )
+ return PSIDE_FRONT|PSIDE_FACING;
+ }
+
+ // box on plane side
+ plane = &mapplanes[planenum];
+ s = BoxOnPlaneSide (brush->mins, brush->maxs, plane);
+
+ // if both sides, count the visible faces split
+ if (s == PSIDE_BOTH)
+ {
+ *numsplits += 3;
+ }
+
+ return s;
+}
+
+/*
+============
+TestBrushToPlanenum
+
+============
+*/
+int TestBrushToPlanenum (bspbrush_t *brush, int planenum,
+ int *numsplits, qboolean *hintsplit, int *epsilonbrush)
+{
+ int i, j, num;
+ plane_t *plane;
+ int s;
+ winding_t *w;
+ vec_t d, d_front, d_back;
+ int front, back;
+
+ *numsplits = 0;
+ *hintsplit = false;
+
+ // if the brush actually uses the planenum,
+ // we can tell the side for sure
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ num = brush->sides[i].planenum;
+ if (num >= 0x10000)
+ Error ("bad planenum");
+ if (num == planenum)
+ return PSIDE_BACK|PSIDE_FACING;
+ if (num == (planenum ^ 1) )
+ return PSIDE_FRONT|PSIDE_FACING;
+ }
+
+ // box on plane side
+ plane = &mapplanes[planenum];
+ s = BoxOnPlaneSide (brush->mins, brush->maxs, plane);
+
+ if (s != PSIDE_BOTH)
+ return s;
+
+// if both sides, count the visible faces split
+ d_front = d_back = 0;
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ if (brush->sides[i].texinfo == TEXINFO_NODE)
+ continue; // on node, don't worry about splits
+ if (!brush->sides[i].visible)
+ continue; // we don't care about non-visible
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ front = back = 0;
+ for (j=0 ; j<w->numpoints; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > d_front)
+ d_front = d;
+ if (d < d_back)
+ d_back = d;
+
+ if (d > 0.1) // PLANESIDE_EPSILON)
+ front = 1;
+ if (d < -0.1) // PLANESIDE_EPSILON)
+ back = 1;
+ }
+ if (front && back)
+ {
+ if ( !(brush->sides[i].surf & SURF_SKIP) )
+ {
+ (*numsplits)++;
+ if (brush->sides[i].surf & SURF_HINT)
+ *hintsplit = true;
+ }
+ }
+ }
+
+ if ( (d_front > 0.0 && d_front < 1.0)
+ || (d_back < 0.0 && d_back > -1.0) )
+ (*epsilonbrush)++;
+
+#if 0
+ if (*numsplits == 0)
+ { // didn't really need to be split
+ if (front)
+ s = PSIDE_FRONT;
+ else if (back)
+ s = PSIDE_BACK;
+ else
+ s = 0;
+ }
+#endif
+
+ return s;
+}
+
+//========================================================
+
+/*
+================
+WindingIsTiny
+
+Returns true if the winding would be crunched out of
+existance by the vertex snapping.
+================
+*/
+#define EDGE_LENGTH 0.2
+qboolean WindingIsTiny (winding_t *w)
+{
+#if 0
+ if (WindingArea (w) < 1)
+ return true;
+ return false;
+#else
+ int i, j;
+ vec_t len;
+ vec3_t delta;
+ int edges;
+
+ edges = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = i == w->numpoints - 1 ? 0 : i+1;
+ VectorSubtract (w->p[j], w->p[i], delta);
+ len = VectorLength (delta);
+ if (len > EDGE_LENGTH)
+ {
+ if (++edges == 3)
+ return false;
+ }
+ }
+ return true;
+#endif
+}
+
+/*
+================
+WindingIsHuge
+
+Returns true if the winding still has one of the points
+from basewinding for plane
+================
+*/
+qboolean WindingIsHuge (winding_t *w)
+{
+ int i, j;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ if (w->p[i][j] < -8000 || w->p[i][j] > 8000)
+ return true;
+ }
+ return false;
+}
+
+//============================================================
+
+/*
+================
+Leafnode
+================
+*/
+void LeafNode (node_t *node, bspbrush_t *brushes)
+{
+ bspbrush_t *b;
+ int i;
+
+ node->planenum = PLANENUM_LEAF;
+ node->contents = 0;
+
+ for (b=brushes ; b ; b=b->next)
+ {
+ // if the brush is solid and all of its sides are on nodes,
+ // it eats everything
+ if (b->original->contents & CONTENTS_SOLID)
+ {
+ for (i=0 ; i<b->numsides ; i++)
+ if (b->sides[i].texinfo != TEXINFO_NODE)
+ break;
+ if (i == b->numsides)
+ {
+ node->contents = CONTENTS_SOLID;
+ break;
+ }
+ }
+ node->contents |= b->original->contents;
+ }
+
+ node->brushlist = brushes;
+}
+
+
+//============================================================
+
+void CheckPlaneAgainstParents (int pnum, node_t *node)
+{
+ node_t *p;
+
+ for (p=node->parent ; p ; p=p->parent)
+ {
+ if (p->planenum == pnum)
+ Error ("Tried parent");
+ }
+}
+
+qboolean CheckPlaneAgainstVolume (int pnum, node_t *node)
+{
+ bspbrush_t *front, *back;
+ qboolean good;
+
+ SplitBrush (node->volume, pnum, &front, &back);
+
+ good = (front && back);
+
+ if (front)
+ FreeBrush (front);
+ if (back)
+ FreeBrush (back);
+
+ return good;
+}
+
+/*
+================
+SelectSplitSide
+
+Using a hueristic, choses one of the sides out of the brushlist
+to partition the brushes with.
+Returns NULL if there are no valid planes to split with..
+================
+*/
+side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node)
+{
+ int value, bestvalue;
+ bspbrush_t *brush, *test;
+ side_t *side, *bestside;
+ int i, j, pass, numpasses;
+ int pnum;
+ int s;
+ int front, back, both, facing, splits;
+ int bsplits;
+ int bestsplits;
+ int epsilonbrush;
+ qboolean hintsplit;
+
+ bestside = NULL;
+ bestvalue = -99999;
+ bestsplits = 0;
+
+ // the search order goes: visible-structural, visible-detail,
+ // nonvisible-structural, nonvisible-detail.
+ // If any valid plane is available in a pass, no further
+ // passes will be tried.
+ numpasses = 4;
+ for (pass = 0 ; pass < numpasses ; pass++)
+ {
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ if ( (pass & 1) && !(brush->original->contents & CONTENTS_DETAIL) )
+ continue;
+ if ( !(pass & 1) && (brush->original->contents & CONTENTS_DETAIL) )
+ continue;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = brush->sides + i;
+ if (side->bevel)
+ continue; // never use a bevel as a spliter
+ if (!side->winding)
+ continue; // nothing visible, so it can't split
+ if (side->texinfo == TEXINFO_NODE)
+ continue; // allready a node splitter
+ if (side->tested)
+ continue; // we allready have metrics for this plane
+ if (side->surf & SURF_SKIP)
+ continue; // skip surfaces are never chosen
+ if ( side->visible ^ (pass<2) )
+ continue; // only check visible faces on first pass
+
+ pnum = side->planenum;
+ pnum &= ~1; // allways use positive facing plane
+
+ CheckPlaneAgainstParents (pnum, node);
+
+ if (!CheckPlaneAgainstVolume (pnum, node))
+ continue; // would produce a tiny volume
+
+ front = 0;
+ back = 0;
+ both = 0;
+ facing = 0;
+ splits = 0;
+ epsilonbrush = 0;
+
+ for (test = brushes ; test ; test=test->next)
+ {
+ s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush);
+
+ splits += bsplits;
+ if (bsplits && (s&PSIDE_FACING) )
+ Error ("PSIDE_FACING with splits");
+
+ test->testside = s;
+ // if the brush shares this face, don't bother
+ // testing that facenum as a splitter again
+ if (s & PSIDE_FACING)
+ {
+ facing++;
+ for (j=0 ; j<test->numsides ; j++)
+ {
+ if ( (test->sides[j].planenum&~1) == pnum)
+ test->sides[j].tested = true;
+ }
+ }
+ if (s & PSIDE_FRONT)
+ front++;
+ if (s & PSIDE_BACK)
+ back++;
+ if (s == PSIDE_BOTH)
+ both++;
+ }
+
+ // give a value estimate for using this plane
+
+ value = 5*facing - 5*splits - abs(front-back);
+// value = -5*splits;
+// value = 5*facing - 5*splits;
+ if (mapplanes[pnum].type < 3)
+ value+=5; // axial is better
+ value -= epsilonbrush*1000; // avoid!
+
+ // never split a hint side except with another hint
+ if (hintsplit && !(side->surf & SURF_HINT) )
+ value = -9999999;
+
+ // save off the side test so we don't need
+ // to recalculate it when we actually seperate
+ // the brushes
+ if (value > bestvalue)
+ {
+ bestvalue = value;
+ bestside = side;
+ bestsplits = splits;
+ for (test = brushes ; test ; test=test->next)
+ test->side = test->testside;
+ }
+ }
+ }
+
+ // if we found a good plane, don't bother trying any
+ // other passes
+ if (bestside)
+ {
+ if (pass > 1)
+ {
+ if (numthreads == 1)
+ c_nonvis++;
+ }
+ if (pass > 0)
+ node->detail_seperator = true; // not needed for vis
+ break;
+ }
+ }
+
+ //
+ // clear all the tested flags we set
+ //
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ brush->sides[i].tested = false;
+ }
+
+ return bestside;
+}
+
+
+/*
+==================
+BrushMostlyOnSide
+
+==================
+*/
+int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
+{
+ int i, j;
+ winding_t *w;
+ vec_t d, max;
+ int side;
+
+ max = 0;
+ side = PSIDE_FRONT;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > max)
+ {
+ max = d;
+ side = PSIDE_FRONT;
+ }
+ if (-d > max)
+ {
+ max = -d;
+ side = PSIDE_BACK;
+ }
+ }
+ }
+ return side;
+}
+
+/*
+================
+SplitBrush
+
+Generates two new brushes, leaving the original
+unchanged
+================
+*/
+void SplitBrush (bspbrush_t *brush, int planenum,
+ bspbrush_t **front, bspbrush_t **back)
+{
+ bspbrush_t *b[2];
+ int i, j;
+ winding_t *w, *cw[2], *midwinding;
+ plane_t *plane, *plane2;
+ side_t *s, *cs;
+ float d, d_front, d_back;
+
+ *front = *back = NULL;
+ plane = &mapplanes[planenum];
+
+ // check all points
+ d_front = d_back = 0;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > 0 && d > d_front)
+ d_front = d;
+ if (d < 0 && d < d_back)
+ d_back = d;
+ }
+ }
+ if (d_front < 0.1) // PLANESIDE_EPSILON)
+ { // only on back
+ *back = CopyBrush (brush);
+ return;
+ }
+ if (d_back > -0.1) // PLANESIDE_EPSILON)
+ { // only on front
+ *front = CopyBrush (brush);
+ return;
+ }
+
+ // create a new winding from the split plane
+
+ w = BaseWindingForPlane (plane->normal, plane->dist);
+ for (i=0 ; i<brush->numsides && w ; i++)
+ {
+ plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
+ ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
+ }
+
+ if (!w || WindingIsTiny (w) )
+ { // the brush isn't really split
+ int side;
+
+ side = BrushMostlyOnSide (brush, plane);
+ if (side == PSIDE_FRONT)
+ *front = CopyBrush (brush);
+ if (side == PSIDE_BACK)
+ *back = CopyBrush (brush);
+ return;
+ }
+
+ if (WindingIsHuge (w))
+ {
+ qprintf ("WARNING: huge winding\n");
+ }
+
+ midwinding = w;
+
+ // split it for real
+
+ for (i=0 ; i<2 ; i++)
+ {
+ b[i] = AllocBrush (brush->numsides+1);
+ b[i]->original = brush->original;
+ }
+
+ // split all the current windings
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ w = s->winding;
+ if (!w)
+ continue;
+ ClipWindingEpsilon (w, plane->normal, plane->dist,
+ 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
+ for (j=0 ; j<2 ; j++)
+ {
+ if (!cw[j])
+ continue;
+#if 0
+ if (WindingIsTiny (cw[j]))
+ {
+ FreeWinding (cw[j]);
+ continue;
+ }
+#endif
+ cs = &b[j]->sides[b[j]->numsides];
+ b[j]->numsides++;
+ *cs = *s;
+// cs->planenum = s->planenum;
+// cs->texinfo = s->texinfo;
+// cs->visible = s->visible;
+// cs->original = s->original;
+ cs->winding = cw[j];
+ cs->tested = false;
+ }
+ }
+
+
+ // see if we have valid polygons on both sides
+
+ for (i=0 ; i<2 ; i++)
+ {
+ BoundBrush (b[i]);
+ for (j=0 ; j<3 ; j++)
+ {
+ if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096)
+ {
+ qprintf ("bogus brush after clip\n");
+ break;
+ }
+ }
+
+ if (b[i]->numsides < 3 || j < 3)
+ {
+ FreeBrush (b[i]);
+ b[i] = NULL;
+ }
+ }
+
+ if ( !(b[0] && b[1]) )
+ {
+ if (!b[0] && !b[1])
+ qprintf ("split removed brush\n");
+ else
+ qprintf ("split not on both sides\n");
+ if (b[0])
+ {
+ FreeBrush (b[0]);
+ *front = CopyBrush (brush);
+ }
+ if (b[1])
+ {
+ FreeBrush (b[1]);
+ *back = CopyBrush (brush);
+ }
+ return;
+ }
+
+ // add the midwinding to both sides
+ for (i=0 ; i<2 ; i++)
+ {
+ cs = &b[i]->sides[b[i]->numsides];
+ b[i]->numsides++;
+
+ cs->planenum = planenum^i^1;
+ cs->texinfo = TEXINFO_NODE;
+ cs->visible = false;
+ cs->tested = false;
+ if (i==0)
+ cs->winding = CopyWinding (midwinding);
+ else
+ cs->winding = midwinding;
+ }
+
+{
+ vec_t v1;
+ int i;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ v1 = BrushVolume (b[i]);
+ if (v1 < 1.0)
+ {
+ FreeBrush (b[i]);
+ b[i] = NULL;
+// qprintf ("tiny volume after clip\n");
+ }
+ }
+}
+
+ *front = b[0];
+ *back = b[1];
+}
+
+/*
+================
+SplitBrushList
+================
+*/
+void SplitBrushList (bspbrush_t *brushes,
+ node_t *node, bspbrush_t **front, bspbrush_t **back)
+{
+ bspbrush_t *brush, *newbrush, *newbrush2;
+ side_t *side;
+ int sides;
+ int i;
+
+ *front = *back = NULL;
+
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ sides = brush->side;
+
+ if (sides == PSIDE_BOTH)
+ { // split into two brushes
+ SplitBrush (brush, node->planenum, &newbrush, &newbrush2);
+ if (newbrush)
+ {
+ newbrush->next = *front;
+ *front = newbrush;
+ }
+ if (newbrush2)
+ {
+ newbrush2->next = *back;
+ *back = newbrush2;
+ }
+ continue;
+ }
+
+ newbrush = CopyBrush (brush);
+
+ // if the planenum is actualy a part of the brush
+ // find the plane and flag it as used so it won't be tried
+ // as a splitter again
+ if (sides & PSIDE_FACING)
+ {
+ for (i=0 ; i<newbrush->numsides ; i++)
+ {
+ side = newbrush->sides + i;
+ if ( (side->planenum& ~1) == node->planenum)
+ side->texinfo = TEXINFO_NODE;
+ }
+ }
+
+
+ if (sides & PSIDE_FRONT)
+ {
+ newbrush->next = *front;
+ *front = newbrush;
+ continue;
+ }
+ if (sides & PSIDE_BACK)
+ {
+ newbrush->next = *back;
+ *back = newbrush;
+ continue;
+ }
+ }
+}
+
+
+/*
+================
+BuildTree_r
+================
+*/
+node_t *BuildTree_r (node_t *node, bspbrush_t *brushes)
+{
+ node_t *newnode;
+ side_t *bestside;
+ int i;
+ bspbrush_t *children[2];
+
+ if (numthreads == 1)
+ c_nodes++;
+
+ if (drawflag)
+ DrawBrushList (brushes, node);
+
+ // find the best plane to use as a splitter
+ bestside = SelectSplitSide (brushes, node);
+ if (!bestside)
+ {
+ // leaf node
+ node->side = NULL;
+ node->planenum = -1;
+ LeafNode (node, brushes);
+ return node;
+ }
+
+ // this is a splitplane node
+ node->side = bestside;
+ node->planenum = bestside->planenum & ~1; // always use front facing
+
+ SplitBrushList (brushes, node, &children[0], &children[1]);
+ FreeBrushList (brushes);
+
+ // allocate children before recursing
+ for (i=0 ; i<2 ; i++)
+ {
+ newnode = AllocNode ();
+ newnode->parent = node;
+ node->children[i] = newnode;
+ }
+
+ SplitBrush (node->volume, node->planenum, &node->children[0]->volume,
+ &node->children[1]->volume);
+
+ // recursively process children
+ for (i=0 ; i<2 ; i++)
+ {
+ node->children[i] = BuildTree_r (node->children[i], children[i]);
+ }
+
+ return node;
+}
+
+//===========================================================
+
+/*
+=================
+BrushBSP
+
+The incoming list will be freed before exiting
+=================
+*/
+tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs)
+{
+ node_t *node;
+ bspbrush_t *b;
+ int c_faces, c_nonvisfaces;
+ int c_brushes;
+ tree_t *tree;
+ int i;
+ vec_t volume;
+
+ qprintf ("--- BrushBSP ---\n");
+
+ tree = AllocTree ();
+
+ c_faces = 0;
+ c_nonvisfaces = 0;
+ c_brushes = 0;
+ for (b=brushlist ; b ; b=b->next)
+ {
+ c_brushes++;
+
+ volume = BrushVolume (b);
+ if (volume < microvolume)
+ {
+ printf ("WARNING: entity %i, brush %i: microbrush\n",
+ b->original->entitynum, b->original->brushnum);
+ }
+
+ for (i=0 ; i<b->numsides ; i++)
+ {
+ if (b->sides[i].bevel)
+ continue;
+ if (!b->sides[i].winding)
+ continue;
+ if (b->sides[i].texinfo == TEXINFO_NODE)
+ continue;
+ if (b->sides[i].visible)
+ c_faces++;
+ else
+ c_nonvisfaces++;
+ }
+
+ AddPointToBounds (b->mins, tree->mins, tree->maxs);
+ AddPointToBounds (b->maxs, tree->mins, tree->maxs);
+ }
+
+ qprintf ("%5i brushes\n", c_brushes);
+ qprintf ("%5i visible faces\n", c_faces);
+ qprintf ("%5i nonvisible faces\n", c_nonvisfaces);
+
+ c_nodes = 0;
+ c_nonvis = 0;
+ node = AllocNode ();
+
+ node->volume = BrushFromBounds (mins, maxs);
+
+ tree->headnode = node;
+
+ node = BuildTree_r (node, brushlist);
+ qprintf ("%5i visible nodes\n", c_nodes/2 - c_nonvis);
+ qprintf ("%5i nonvis nodes\n", c_nonvis);
+ qprintf ("%5i leafs\n", (c_nodes+1)/2);
+#if 0
+{ // debug code
+static node_t *tnode;
+vec3_t p;
+
+p[0] = -1469;
+p[1] = -118;
+p[2] = 119;
+tnode = PointInLeaf (tree->headnode, p);
+printf ("contents: %i\n", tnode->contents);
+p[0] = 0;
+}
+#endif
+ return tree;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+/*
+
+tag all brushes with original contents
+brushes may contain multiple contents
+there will be no brush overlap after csg phase
+
+
+
+
+each side has a count of the other sides it splits
+
+the best split will be the one that minimizes the total split counts
+of all remaining sides
+
+precalc side on plane table
+
+evaluate split side
+{
+cost = 0
+for all sides
+ for all sides
+ get
+ if side splits side and splitside is on same child
+ cost++;
+}
+
+
+ */
+
+void SplitBrush2 (bspbrush_t *brush, int planenum,
+ bspbrush_t **front, bspbrush_t **back)
+{
+ SplitBrush (brush, planenum, front, back);
+#if 0
+ if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
+ (*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1
+ if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
+ (*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1
+#endif
+}
+
+/*
+===============
+SubtractBrush
+
+Returns a list of brushes that remain after B is subtracted from A.
+May by empty if A is contained inside B.
+
+The originals are undisturbed.
+===============
+*/
+bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
+{ // a - b = out (list)
+ int i;
+ bspbrush_t *front, *back;
+ bspbrush_t *out, *in;
+
+ in = a;
+ out = NULL;
+ for (i=0 ; i<b->numsides && in ; i++)
+ {
+ SplitBrush2 (in, b->sides[i].planenum, &front, &back);
+ if (in != a)
+ FreeBrush (in);
+ if (front)
+ { // add to list
+ front->next = out;
+ out = front;
+ }
+ in = back;
+ }
+ if (in)
+ FreeBrush (in);
+ else
+ { // didn't really intersect
+ FreeBrushList (out);
+ return a;
+ }
+ return out;
+}
+
+/*
+===============
+IntersectBrush
+
+Returns a single brush made up by the intersection of the
+two provided brushes, or NULL if they are disjoint.
+
+The originals are undisturbed.
+===============
+*/
+bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
+{
+ int i;
+ bspbrush_t *front, *back;
+ bspbrush_t *in;
+
+ in = a;
+ for (i=0 ; i<b->numsides && in ; i++)
+ {
+ SplitBrush2 (in, b->sides[i].planenum, &front, &back);
+ if (in != a)
+ FreeBrush (in);
+ if (front)
+ FreeBrush (front);
+ in = back;
+ }
+
+ if (in == a)
+ return NULL;
+
+ in->next = NULL;
+ return in;
+}
+
+
+/*
+===============
+BrushesDisjoint
+
+Returns true if the two brushes definately do not intersect.
+There will be false negatives for some non-axial combinations.
+===============
+*/
+qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
+{
+ int i, j;
+
+ // check bounding boxes
+ for (i=0 ; i<3 ; i++)
+ if (a->mins[i] >= b->maxs[i]
+ || a->maxs[i] <= b->mins[i])
+ return true; // bounding boxes don't overlap
+
+ // check for opposing planes
+ for (i=0 ; i<a->numsides ; i++)
+ {
+ for (j=0 ; j<b->numsides ; j++)
+ {
+ if (a->sides[i].planenum ==
+ (b->sides[j].planenum^1) )
+ return true; // opposite planes, so not touching
+ }
+ }
+
+ return false; // might intersect
+}
+
+/*
+===============
+IntersectionContents
+
+Returns a content word for the intersection of two brushes.
+Some combinations will generate a combination (water + clip),
+but most will be the stronger of the two contents.
+===============
+*/
+int IntersectionContents (int c1, int c2)
+{
+ int out;
+
+ out = c1 | c2;
+
+ if (out & CONTENTS_SOLID)
+ out = CONTENTS_SOLID;
+
+ return out;
+}
+
+
+int minplanenums[3];
+int maxplanenums[3];
+
+/*
+===============
+ClipBrushToBox
+
+Any planes shared with the box edge will be set to no texinfo
+===============
+*/
+bspbrush_t *ClipBrushToBox (bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs)
+{
+ int i, j;
+ bspbrush_t *front, *back;
+ int p;
+
+ for (j=0 ; j<2 ; j++)
+ {
+ if (brush->maxs[j] > clipmaxs[j])
+ {
+ SplitBrush (brush, maxplanenums[j], &front, &back);
+ if (front)
+ FreeBrush (front);
+ brush = back;
+ if (!brush)
+ return NULL;
+ }
+ if (brush->mins[j] < clipmins[j])
+ {
+ SplitBrush (brush, minplanenums[j], &front, &back);
+ if (back)
+ FreeBrush (back);
+ brush = front;
+ if (!brush)
+ return NULL;
+ }
+ }
+
+ // remove any colinear faces
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ p = brush->sides[i].planenum & ~1;
+ if (p == maxplanenums[0] || p == maxplanenums[1]
+ || p == minplanenums[0] || p == minplanenums[1])
+ {
+ brush->sides[i].texinfo = TEXINFO_NODE;
+ brush->sides[i].visible = false;
+ }
+ }
+ return brush;
+}
+
+/*
+===============
+MakeBspBrushList
+===============
+*/
+bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
+ vec3_t clipmins, vec3_t clipmaxs)
+{
+ mapbrush_t *mb;
+ bspbrush_t *brushlist, *newbrush;
+ int i, j;
+ int c_faces;
+ int c_brushes;
+ int numsides;
+ int vis;
+ vec3_t normal;
+ float dist;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ dist = clipmaxs[i];
+ maxplanenums[i] = FindFloatPlane (normal, dist);
+ dist = clipmins[i];
+ minplanenums[i] = FindFloatPlane (normal, dist);
+ }
+
+ brushlist = NULL;
+ c_faces = 0;
+ c_brushes = 0;
+
+ for (i=startbrush ; i<endbrush ; i++)
+ {
+ mb = &mapbrushes[i];
+
+ numsides = mb->numsides;
+ if (!numsides)
+ continue;
+ // make sure the brush has at least one face showing
+ vis = 0;
+ for (j=0 ; j<numsides ; j++)
+ if (mb->original_sides[j].visible && mb->original_sides[j].winding)
+ vis++;
+#if 0
+ if (!vis)
+ continue; // no faces at all
+#endif
+ // if the brush is outside the clip area, skip it
+ for (j=0 ; j<3 ; j++)
+ if (mb->mins[j] >= clipmaxs[j]
+ || mb->maxs[j] <= clipmins[j])
+ break;
+ if (j != 3)
+ continue;
+
+ //
+ // make a copy of the brush
+ //
+ newbrush = AllocBrush (mb->numsides);
+ newbrush->original = mb;
+ newbrush->numsides = mb->numsides;
+ memcpy (newbrush->sides, mb->original_sides, numsides*sizeof(side_t));
+ for (j=0 ; j<numsides ; j++)
+ {
+ if (newbrush->sides[j].winding)
+ newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
+ if (newbrush->sides[j].surf & SURF_HINT)
+ newbrush->sides[j].visible = true; // hints are always visible
+ }
+ VectorCopy (mb->mins, newbrush->mins);
+ VectorCopy (mb->maxs, newbrush->maxs);
+
+ //
+ // carve off anything outside the clip box
+ //
+ newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
+ if (!newbrush)
+ continue;
+
+ c_faces += vis;
+ c_brushes++;
+
+ newbrush->next = brushlist;
+ brushlist = newbrush;
+ }
+
+ return brushlist;
+}
+
+/*
+===============
+AddBspBrushListToTail
+===============
+*/
+bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
+{
+ bspbrush_t *walk, *next;
+
+ for (walk=list ; walk ; walk=next)
+ { // add to end of list
+ next = walk->next;
+ walk->next = NULL;
+ tail->next = walk;
+ tail = walk;
+ }
+
+ return tail;
+}
+
+/*
+===========
+CullList
+
+Builds a new list that doesn't hold the given brush
+===========
+*/
+bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip1)
+{
+ bspbrush_t *newlist;
+ bspbrush_t *next;
+
+ newlist = NULL;
+
+ for ( ; list ; list = next)
+ {
+ next = list->next;
+ if (list == skip1)
+ {
+ FreeBrush (list);
+ continue;
+ }
+ list->next = newlist;
+ newlist = list;
+ }
+ return newlist;
+}
+
+
+/*
+==================
+WriteBrushMap
+==================
+*/
+void WriteBrushMap (char *name, bspbrush_t *list)
+{
+ FILE *f;
+ side_t *s;
+ int i;
+ winding_t *w;
+
+ printf ("writing %s\n", name);
+ f = fopen (name, "wb");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+
+ for ( ; list ; list=list->next )
+ {
+ fprintf (f, "{\n");
+ for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
+ {
+ w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
+
+ fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+
+ fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);
+ FreeWinding (w);
+ }
+ fprintf (f, "}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+}
+
+/*
+==================
+BrushGE
+
+Returns true if b1 is allowed to bite b2
+==================
+*/
+qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
+{
+ // detail brushes never bite structural brushes
+ if ( (b1->original->contents & CONTENTS_DETAIL)
+ && !(b2->original->contents & CONTENTS_DETAIL) )
+ return false;
+ if (b1->original->contents & CONTENTS_SOLID)
+ return true;
+ return false;
+}
+
+/*
+=================
+ChopBrushes
+
+Carves any intersecting solid brushes into the minimum number
+of non-intersecting brushes.
+=================
+*/
+bspbrush_t *ChopBrushes (bspbrush_t *head)
+{
+ bspbrush_t *b1, *b2, *next;
+ bspbrush_t *tail;
+ bspbrush_t *keep;
+ bspbrush_t *sub, *sub2;
+ int c1, c2;
+
+ qprintf ("---- ChopBrushes ----\n");
+ qprintf ("original brushes: %i\n", CountBrushList (head));
+
+#if 0
+ if (startbrush == 0)
+ WriteBrushList ("before.gl", head, false);
+#endif
+ keep = NULL;
+
+newlist:
+ // find tail
+ if (!head)
+ return NULL;
+ for (tail=head ; tail->next ; tail=tail->next)
+ ;
+
+ for (b1=head ; b1 ; b1=next)
+ {
+ next = b1->next;
+ for (b2=b1->next ; b2 ; b2 = b2->next)
+ {
+ if (BrushesDisjoint (b1, b2))
+ continue;
+
+ sub = NULL;
+ sub2 = NULL;
+ c1 = 999999;
+ c2 = 999999;
+
+ if ( BrushGE (b2, b1) )
+ {
+ sub = SubtractBrush (b1, b2);
+ if (sub == b1)
+ continue; // didn't really intersect
+ if (!sub)
+ { // b1 is swallowed by b2
+ head = CullList (b1, b1);
+ goto newlist;
+ }
+ c1 = CountBrushList (sub);
+ }
+
+ if ( BrushGE (b1, b2) )
+ {
+ sub2 = SubtractBrush (b2, b1);
+ if (sub2 == b2)
+ continue; // didn't really intersect
+ if (!sub2)
+ { // b2 is swallowed by b1
+ FreeBrushList (sub);
+ head = CullList (b1, b2);
+ goto newlist;
+ }
+ c2 = CountBrushList (sub2);
+ }
+
+ if (!sub && !sub2)
+ continue; // neither one can bite
+
+ // only accept if it didn't fragment
+ // (commening this out allows full fragmentation)
+ if (c1 > 1 && c2 > 1)
+ {
+ if (sub2)
+ FreeBrushList (sub2);
+ if (sub)
+ FreeBrushList (sub);
+ continue;
+ }
+
+ if (c1 < c2)
+ {
+ if (sub2)
+ FreeBrushList (sub2);
+ tail = AddBrushListToTail (sub, tail);
+ head = CullList (b1, b1);
+ goto newlist;
+ }
+ else
+ {
+ if (sub)
+ FreeBrushList (sub);
+ tail = AddBrushListToTail (sub2, tail);
+ head = CullList (b1, b2);
+ goto newlist;
+ }
+ }
+
+ if (!b2)
+ { // b1 is no longer intersecting anything, so keep it
+ b1->next = keep;
+ keep = b1;
+ }
+ }
+
+ qprintf ("output brushes: %i\n", CountBrushList (keep));
+#if 0
+ {
+ WriteBrushList ("after.gl", keep, false);
+ WriteBrushMap ("after.map", keep);
+ }
+#endif
+ return keep;
+}
+
+
+/*
+=================
+InitialBrushList
+=================
+*/
+bspbrush_t *InitialBrushList (bspbrush_t *list)
+{
+ bspbrush_t *b;
+ bspbrush_t *out, *newb;
+ int i;
+
+ // only return brushes that have visible faces
+ out = NULL;
+ for (b=list ; b ; b=b->next)
+ {
+#if 0
+ for (i=0 ; i<b->numsides ; i++)
+ if (b->sides[i].visible)
+ break;
+ if (i == b->numsides)
+ continue;
+#endif
+ newb = CopyBrush (b);
+ newb->next = out;
+ out = newb;
+
+ // clear visible, so it must be set by MarkVisibleFaces_r
+ // to be used in the optimized list
+ for (i=0 ; i<b->numsides ; i++)
+ {
+ newb->sides[i].original = &b->sides[i];
+// newb->sides[i].visible = true;
+ b->sides[i].visible = false;
+ }
+ }
+
+ return out;
+}
+
+/*
+=================
+OptimizedBrushList
+=================
+*/
+bspbrush_t *OptimizedBrushList (bspbrush_t *list)
+{
+ bspbrush_t *b;
+ bspbrush_t *out, *newb;
+ int i;
+
+ // only return brushes that have visible faces
+ out = NULL;
+ for (b=list ; b ; b=b->next)
+ {
+ for (i=0 ; i<b->numsides ; i++)
+ if (b->sides[i].visible)
+ break;
+ if (i == b->numsides)
+ continue;
+ newb = CopyBrush (b);
+ newb->next = out;
+ out = newb;
+ }
+
+// WriteBrushList ("vis.gl", out, true);
+
+ return out;
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+/*
+
+ some faces will be removed before saving, but still form nodes:
+
+ the insides of sky volumes
+ meeting planes of different water current volumes
+
+*/
+
+// undefine for dumb linear searches
+#define USE_HASHING
+
+#define INTEGRAL_EPSILON 0.01
+#define POINT_EPSILON 0.5
+#define OFF_EPSILON 0.5
+
+int c_merge;
+int c_subdivide;
+
+int c_totalverts;
+int c_uniqueverts;
+int c_degenerate;
+int c_tjunctions;
+int c_faceoverflows;
+int c_facecollapse;
+int c_badstartverts;
+
+#define MAX_SUPERVERTS 512
+int superverts[MAX_SUPERVERTS];
+int numsuperverts;
+
+face_t *edgefaces[MAX_MAP_EDGES][2];
+int firstmodeledge = 1;
+int firstmodelface;
+
+int c_tryedges;
+
+vec3_t edge_dir;
+vec3_t edge_start;
+vec_t edge_len;
+
+int num_edge_verts;
+int edge_verts[MAX_MAP_VERTS];
+
+
+float subdivide_size = 240;
+
+
+face_t *NewFaceFromFace (face_t *f);
+
+//===========================================================================
+
+typedef struct hashvert_s
+{
+ struct hashvert_s *next;
+ int num;
+} hashvert_t;
+
+
+#define HASH_SIZE 64
+
+
+int vertexchain[MAX_MAP_VERTS]; // the next vertex in a hash chain
+int hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts
+
+face_t *edgefaces[MAX_MAP_EDGES][2];
+
+//============================================================================
+
+
+unsigned HashVec (vec3_t vec)
+{
+ int x, y;
+
+ x = (4096 + (int)(vec[0]+0.5)) >> 7;
+ y = (4096 + (int)(vec[1]+0.5)) >> 7;
+
+ if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE )
+ Error ("HashVec: point outside valid range");
+
+ return y*HASH_SIZE + x;
+}
+
+#ifdef USE_HASHING
+/*
+=============
+GetVertex
+
+Uses hashing
+=============
+*/
+int GetVertexnum (vec3_t in)
+{
+ int h;
+ int i;
+ float *p;
+ vec3_t vert;
+ int vnum;
+
+ c_totalverts++;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON)
+ vert[i] = Q_rint(in[i]);
+ else
+ vert[i] = in[i];
+ }
+
+ h = HashVec (vert);
+
+ for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum])
+ {
+ p = dvertexes[vnum].point;
+ if ( fabs(p[0]-vert[0])<POINT_EPSILON
+ && fabs(p[1]-vert[1])<POINT_EPSILON
+ && fabs(p[2]-vert[2])<POINT_EPSILON )
+ return vnum;
+ }
+
+// emit a vertex
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("numvertexes == MAX_MAP_VERTS");
+
+ dvertexes[numvertexes].point[0] = vert[0];
+ dvertexes[numvertexes].point[1] = vert[1];
+ dvertexes[numvertexes].point[2] = vert[2];
+
+ vertexchain[numvertexes] = hashverts[h];
+ hashverts[h] = numvertexes;
+
+ c_uniqueverts++;
+
+ numvertexes++;
+
+ return numvertexes-1;
+}
+#else
+/*
+==================
+GetVertexnum
+
+Dumb linear search
+==================
+*/
+int GetVertexnum (vec3_t v)
+{
+ int i, j;
+ dvertex_t *dv;
+ vec_t d;
+
+ c_totalverts++;
+
+ // make really close values exactly integral
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON )
+ v[i] = (int)(v[i]+0.5);
+ if (v[i] < -4096 || v[i] > 4096)
+ Error ("GetVertexnum: outside +/- 4096");
+ }
+
+ // search for an existing vertex match
+ for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ d = v[j] - dv->point[j];
+ if ( d > POINT_EPSILON || d < -POINT_EPSILON)
+ break;
+ }
+ if (j == 3)
+ return i; // a match
+ }
+
+ // new point
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("MAX_MAP_VERTS");
+ VectorCopy (v, dv->point);
+ numvertexes++;
+ c_uniqueverts++;
+
+ return numvertexes-1;
+}
+#endif
+
+
+/*
+==================
+FaceFromSuperverts
+
+The faces vertexes have beeb added to the superverts[] array,
+and there may be more there than can be held in a face (MAXEDGES).
+
+If less, the faces vertexnums[] will be filled in, otherwise
+face will reference a tree of split[] faces until all of the
+vertexnums can be added.
+
+superverts[base] will become face->vertexnums[0], and the others
+will be circularly filled in.
+==================
+*/
+void FaceFromSuperverts (node_t *node, face_t *f, int base)
+{
+ face_t *newf;
+ int remaining;
+ int i;
+
+ remaining = numsuperverts;
+ while (remaining > MAXEDGES)
+ { // must split into two faces, because of vertex overload
+ c_faceoverflows++;
+
+ newf = f->split[0] = NewFaceFromFace (f);
+ newf = f->split[0];
+ newf->next = node->faces;
+ node->faces = newf;
+
+ newf->numpoints = MAXEDGES;
+ for (i=0 ; i<MAXEDGES ; i++)
+ newf->vertexnums[i] = superverts[(i+base)%numsuperverts];
+
+ f->split[1] = NewFaceFromFace (f);
+ f = f->split[1];
+ f->next = node->faces;
+ node->faces = f;
+
+ remaining -= (MAXEDGES-2);
+ base = (base+MAXEDGES-1)%numsuperverts;
+ }
+
+ // copy the vertexes back to the face
+ f->numpoints = remaining;
+ for (i=0 ; i<remaining ; i++)
+ f->vertexnums[i] = superverts[(i+base)%numsuperverts];
+}
+
+
+/*
+==================
+EmitFaceVertexes
+==================
+*/
+void EmitFaceVertexes (node_t *node, face_t *f)
+{
+ winding_t *w;
+ int i;
+
+ if (f->merged || f->split[0] || f->split[1])
+ return;
+
+ w = f->w;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ if (noweld)
+ { // make every point unique
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("MAX_MAP_VERTS");
+ superverts[i] = numvertexes;
+ VectorCopy (w->p[i], dvertexes[numvertexes].point);
+ numvertexes++;
+ c_uniqueverts++;
+ c_totalverts++;
+ }
+ else
+ superverts[i] = GetVertexnum (w->p[i]);
+ }
+ numsuperverts = w->numpoints;
+
+ // this may fragment the face if > MAXEDGES
+ FaceFromSuperverts (node, f, 0);
+}
+
+/*
+==================
+EmitVertexes_r
+==================
+*/
+void EmitVertexes_r (node_t *node)
+{
+ int i;
+ face_t *f;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ for (f=node->faces ; f ; f=f->next)
+ {
+ EmitFaceVertexes (node, f);
+ }
+
+ for (i=0 ; i<2 ; i++)
+ EmitVertexes_r (node->children[i]);
+}
+
+
+#ifdef USE_HASHING
+/*
+==========
+FindEdgeVerts
+
+Uses the hash tables to cut down to a small number
+==========
+*/
+void FindEdgeVerts (vec3_t v1, vec3_t v2)
+{
+ int x1, x2, y1, y2, t;
+ int x, y;
+ int vnum;
+
+#if 0
+{
+ int i;
+ num_edge_verts = numvertexes-1;
+ for (i=0 ; i<numvertexes-1 ; i++)
+ edge_verts[i] = i+1;
+}
+#endif
+
+ x1 = (4096 + (int)(v1[0]+0.5)) >> 7;
+ y1 = (4096 + (int)(v1[1]+0.5)) >> 7;
+ x2 = (4096 + (int)(v2[0]+0.5)) >> 7;
+ y2 = (4096 + (int)(v2[1]+0.5)) >> 7;
+
+ if (x1 > x2)
+ {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if (y1 > y2)
+ {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+#if 0
+ x1--;
+ x2++;
+ y1--;
+ y2++;
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 >= HASH_SIZE)
+ x2 = HASH_SIZE;
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 >= HASH_SIZE)
+ y2 = HASH_SIZE;
+#endif
+ num_edge_verts = 0;
+ for (x=x1 ; x <= x2 ; x++)
+ {
+ for (y=y1 ; y <= y2 ; y++)
+ {
+ for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum])
+ {
+ edge_verts[num_edge_verts++] = vnum;
+ }
+ }
+ }
+}
+
+#else
+/*
+==========
+FindEdgeVerts
+
+Forced a dumb check of everything
+==========
+*/
+void FindEdgeVerts (vec3_t v1, vec3_t v2)
+{
+ int i;
+
+ num_edge_verts = numvertexes-1;
+ for (i=0 ; i<num_edge_verts ; i++)
+ edge_verts[i] = i+1;
+}
+#endif
+
+/*
+==========
+TestEdge
+
+Can be recursively reentered
+==========
+*/
+void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert)
+{
+ int j, k;
+ vec_t dist;
+ vec3_t delta;
+ vec3_t exact;
+ vec3_t off;
+ vec_t error;
+ vec3_t p;
+
+ if (p1 == p2)
+ {
+ c_degenerate++;
+ return; // degenerate edge
+ }
+
+ for (k=startvert ; k<num_edge_verts ; k++)
+ {
+ j = edge_verts[k];
+ if (j==p1 || j == p2)
+ continue;
+
+ VectorCopy (dvertexes[j].point, p);
+
+ VectorSubtract (p, edge_start, delta);
+ dist = DotProduct (delta, edge_dir);
+ if (dist <=start || dist >= end)
+ continue; // off an end
+ VectorMA (edge_start, dist, edge_dir, exact);
+ VectorSubtract (p, exact, off);
+ error = VectorLength (off);
+
+ if (fabs(error) > OFF_EPSILON)
+ continue; // not on the edge
+
+ // break the edge
+ c_tjunctions++;
+ TestEdge (start, dist, p1, j, k+1);
+ TestEdge (dist, end, j, p2, k+1);
+ return;
+ }
+
+ // the edge p1 to p2 is now free of tjunctions
+ if (numsuperverts >= MAX_SUPERVERTS)
+ Error ("MAX_SUPERVERTS");
+ superverts[numsuperverts] = p1;
+ numsuperverts++;
+}
+
+/*
+==================
+FixFaceEdges
+
+==================
+*/
+void FixFaceEdges (node_t *node, face_t *f)
+{
+ int p1, p2;
+ int i;
+ vec3_t e2;
+ vec_t len;
+ int count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];
+ int base;
+
+ if (f->merged || f->split[0] || f->split[1])
+ return;
+
+ numsuperverts = 0;
+
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ p1 = f->vertexnums[i];
+ p2 = f->vertexnums[(i+1)%f->numpoints];
+
+ VectorCopy (dvertexes[p1].point, edge_start);
+ VectorCopy (dvertexes[p2].point, e2);
+
+ FindEdgeVerts (edge_start, e2);
+
+ VectorSubtract (e2, edge_start, edge_dir);
+ len = VectorNormalize (edge_dir, edge_dir);
+
+ start[i] = numsuperverts;
+ TestEdge (0, len, p1, p2, 0);
+
+ count[i] = numsuperverts - start[i];
+ }
+
+ if (numsuperverts < 3)
+ { // entire face collapsed
+ f->numpoints = 0;
+ c_facecollapse++;
+ return;
+ }
+
+ // we want to pick a vertex that doesn't have tjunctions
+ // on either side, which can cause artifacts on trifans,
+ // especially underwater
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1)
+ break;
+ }
+ if (i == f->numpoints)
+ {
+ f->badstartvert = true;
+ c_badstartverts++;
+ base = 0;
+ }
+ else
+ { // rotate the vertex order
+ base = start[i];
+ }
+
+ // this may fragment the face if > MAXEDGES
+ FaceFromSuperverts (node, f, base);
+}
+
+/*
+==================
+FixEdges_r
+==================
+*/
+void FixEdges_r (node_t *node)
+{
+ int i;
+ face_t *f;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ for (f=node->faces ; f ; f=f->next)
+ FixFaceEdges (node, f);
+
+ for (i=0 ; i<2 ; i++)
+ FixEdges_r (node->children[i]);
+}
+
+/*
+===========
+FixTjuncs
+
+===========
+*/
+void FixTjuncs (node_t *headnode)
+{
+ // snap and merge all vertexes
+ qprintf ("---- snap verts ----\n");
+ memset (hashverts, 0, sizeof(hashverts));
+ c_totalverts = 0;
+ c_uniqueverts = 0;
+ c_faceoverflows = 0;
+ EmitVertexes_r (headnode);
+ qprintf ("%i unique from %i\n", c_uniqueverts, c_totalverts);
+
+ // break edges on tjunctions
+ qprintf ("---- tjunc ----\n");
+ c_tryedges = 0;
+ c_degenerate = 0;
+ c_facecollapse = 0;
+ c_tjunctions = 0;
+ if (!notjunc)
+ FixEdges_r (headnode);
+ qprintf ("%5i edges degenerated\n", c_degenerate);
+ qprintf ("%5i faces degenerated\n", c_facecollapse);
+ qprintf ("%5i edges added by tjunctions\n", c_tjunctions);
+ qprintf ("%5i faces added by tjunctions\n", c_faceoverflows);
+ qprintf ("%5i bad start verts\n", c_badstartverts);
+}
+
+
+//========================================================
+
+int c_faces;
+
+face_t *AllocFace (void)
+{
+ face_t *f;
+
+ f = malloc(sizeof(*f));
+ memset (f, 0, sizeof(*f));
+ c_faces++;
+
+ return f;
+}
+
+face_t *NewFaceFromFace (face_t *f)
+{
+ face_t *newf;
+
+ newf = AllocFace ();
+ *newf = *f;
+ newf->merged = NULL;
+ newf->split[0] = newf->split[1] = NULL;
+ newf->w = NULL;
+ return newf;
+}
+
+void FreeFace (face_t *f)
+{
+ if (f->w)
+ FreeWinding (f->w);
+ free (f);
+ c_faces--;
+}
+
+//========================================================
+
+/*
+==================
+GetEdge
+
+Called by writebsp.
+Don't allow four way edges
+==================
+*/
+int GetEdge2 (int v1, int v2, face_t *f)
+{
+ dedge_t *edge;
+ int i;
+
+ c_tryedges++;
+
+ if (!noshare)
+ {
+ for (i=firstmodeledge ; i < numedges ; i++)
+ {
+ edge = &dedges[i];
+ if (v1 == edge->v[1] && v2 == edge->v[0]
+ && edgefaces[i][0]->contents == f->contents)
+ {
+ if (edgefaces[i][1])
+ // printf ("WARNING: multiple backward edge\n");
+ continue;
+ edgefaces[i][1] = f;
+ return -i;
+ }
+ #if 0
+ if (v1 == edge->v[0] && v2 == edge->v[1])
+ {
+ printf ("WARNING: multiple forward edge\n");
+ return i;
+ }
+ #endif
+ }
+ }
+
+// emit an edge
+ if (numedges >= MAX_MAP_EDGES)
+ Error ("numedges == MAX_MAP_EDGES");
+ edge = &dedges[numedges];
+ numedges++;
+ edge->v[0] = v1;
+ edge->v[1] = v2;
+ edgefaces[numedges-1][0] = f;
+
+ return numedges-1;
+}
+
+/*
+===========================================================================
+
+FACE MERGING
+
+===========================================================================
+*/
+
+#define CONTINUOUS_EPSILON 0.001
+
+/*
+=============
+TryMergeWinding
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal)
+{
+ vec_t *p1, *p2, *p3, *p4, *back;
+ winding_t *newf;
+ int i, j, k, l;
+ vec3_t normal, delta;
+ vec_t dot;
+ qboolean keep1, keep2;
+
+
+ //
+ // find a common edge
+ //
+ p1 = p2 = NULL; // stop compiler warning
+ j = 0; //
+
+ for (i=0 ; i<f1->numpoints ; i++)
+ {
+ p1 = f1->p[i];
+ p2 = f1->p[(i+1)%f1->numpoints];
+ for (j=0 ; j<f2->numpoints ; j++)
+ {
+ p3 = f2->p[j];
+ p4 = f2->p[(j+1)%f2->numpoints];
+ for (k=0 ; k<3 ; k++)
+ {
+ if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON)
+ break;
+ if (fabs(p2[k] - p3[k]) > EQUAL_EPSILON)
+ break;
+ }
+ if (k==3)
+ break;
+ }
+ if (j < f2->numpoints)
+ break;
+ }
+
+ if (i == f1->numpoints)
+ return NULL; // no matching edges
+
+ //
+ // check slope of connected lines
+ // if the slopes are colinear, the point can be removed
+ //
+ back = f1->p[(i+f1->numpoints-1)%f1->numpoints];
+ VectorSubtract (p1, back, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal, normal);
+
+ back = f2->p[(j+2)%f2->numpoints];
+ VectorSubtract (back, p1, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+ back = f1->p[(i+2)%f1->numpoints];
+ VectorSubtract (back, p2, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal, normal);
+
+ back = f2->p[(j+f2->numpoints-1)%f2->numpoints];
+ VectorSubtract (back, p2, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+ //
+ // build the new polygon
+ //
+ newf = AllocWinding (f1->numpoints + f2->numpoints);
+
+ // copy first polygon
+ for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
+ {
+ if (k==(i+1)%f1->numpoints && !keep2)
+ continue;
+
+ VectorCopy (f1->p[k], newf->p[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ // copy second polygon
+ for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
+ {
+ if (l==(j+1)%f2->numpoints && !keep1)
+ continue;
+ VectorCopy (f2->p[l], newf->p[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ return newf;
+}
+
+/*
+=============
+TryMerge
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+face_t *TryMerge (face_t *f1, face_t *f2, vec3_t planenormal)
+{
+ face_t *newf;
+ winding_t *nw;
+
+ if (!f1->w || !f2->w)
+ return NULL;
+ if (f1->texinfo != f2->texinfo)
+ return NULL;
+ if (f1->planenum != f2->planenum) // on front and back sides
+ return NULL;
+ if (f1->contents != f2->contents)
+ return NULL;
+
+
+ nw = TryMergeWinding (f1->w, f2->w, planenormal);
+ if (!nw)
+ return NULL;
+
+ c_merge++;
+ newf = NewFaceFromFace (f1);
+ newf->w = nw;
+
+ f1->merged = newf;
+ f2->merged = newf;
+
+ return newf;
+}
+
+/*
+===============
+MergeNodeFaces
+===============
+*/
+void MergeNodeFaces (node_t *node)
+{
+ face_t *f1, *f2, *end;
+ face_t *merged;
+ plane_t *plane;
+
+ plane = &mapplanes[node->planenum];
+ merged = NULL;
+
+ for (f1 = node->faces ; f1 ; f1 = f1->next)
+ {
+ if (f1->merged || f1->split[0] || f1->split[1])
+ continue;
+ for (f2 = node->faces ; f2 != f1 ; f2=f2->next)
+ {
+ if (f2->merged || f2->split[0] || f2->split[1])
+ continue;
+ merged = TryMerge (f1, f2, plane->normal);
+ if (!merged)
+ continue;
+
+ // add merged to the end of the node face list
+ // so it will be checked against all the faces again
+ for (end = node->faces ; end->next ; end = end->next)
+ ;
+ merged->next = NULL;
+ end->next = merged;
+ break;
+ }
+ }
+}
+
+//=====================================================================
+
+/*
+===============
+SubdivideFace
+
+Chop up faces that are larger than we want in the surface cache
+===============
+*/
+void SubdivideFace (node_t *node, face_t *f)
+{
+ float mins, maxs;
+ vec_t v;
+ int axis, i;
+ texinfo_t *tex;
+ vec3_t temp;
+ vec_t dist;
+ winding_t *w, *frontw, *backw;
+
+ if (f->merged)
+ return;
+
+// special (non-surface cached) faces don't need subdivision
+ tex = &texinfo[f->texinfo];
+
+ if ( tex->flags & (SURF_WARP|SURF_SKY) )
+ {
+ return;
+ }
+
+ for (axis = 0 ; axis < 2 ; axis++)
+ {
+ while (1)
+ {
+ mins = 999999;
+ maxs = -999999;
+
+ VectorCopy (tex->vecs[axis], temp);
+ w = f->w;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ v = DotProduct (w->p[i], temp);
+ if (v < mins)
+ mins = v;
+ if (v > maxs)
+ maxs = v;
+ }
+#if 0
+ if (maxs - mins <= 0)
+ Error ("zero extents");
+#endif
+ if (axis == 2)
+ { // allow double high walls
+ if (maxs - mins <= subdivide_size/* *2 */)
+ break;
+ }
+ else if (maxs - mins <= subdivide_size)
+ break;
+
+ // split it
+ c_subdivide++;
+
+ v = VectorNormalize (temp, temp);
+
+ dist = (mins + subdivide_size - 16)/v;
+
+ ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw);
+ if (!frontw || !backw)
+ Error ("SubdivideFace: didn't split the polygon");
+
+ f->split[0] = NewFaceFromFace (f);
+ f->split[0]->w = frontw;
+ f->split[0]->next = node->faces;
+ node->faces = f->split[0];
+
+ f->split[1] = NewFaceFromFace (f);
+ f->split[1]->w = backw;
+ f->split[1]->next = node->faces;
+ node->faces = f->split[1];
+
+ SubdivideFace (node, f->split[0]);
+ SubdivideFace (node, f->split[1]);
+ return;
+ }
+ }
+}
+
+void SubdivideNodeFaces (node_t *node)
+{
+ face_t *f;
+
+ for (f = node->faces ; f ; f=f->next)
+ {
+ SubdivideFace (node, f);
+ }
+}
+
+//===========================================================================
+
+int c_nodefaces;
+
+
+/*
+============
+FaceFromPortal
+
+============
+*/
+face_t *FaceFromPortal (portal_t *p, int pside)
+{
+ face_t *f;
+ side_t *side;
+
+ side = p->side;
+ if (!side)
+ return NULL; // portal does not bridge different visible contents
+
+ f = AllocFace ();
+
+ f->texinfo = side->texinfo;
+ f->planenum = (side->planenum & ~1) | pside;
+ f->portal = p;
+
+ if ( (p->nodes[pside]->contents & CONTENTS_WINDOW)
+ && VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents) == CONTENTS_WINDOW )
+ return NULL; // don't show insides of windows
+
+ if (pside)
+ {
+ f->w = ReverseWinding(p->winding);
+ f->contents = p->nodes[1]->contents;
+ }
+ else
+ {
+ f->w = CopyWinding(p->winding);
+ f->contents = p->nodes[0]->contents;
+ }
+ return f;
+}
+
+
+/*
+===============
+MakeFaces_r
+
+If a portal will make a visible face,
+mark the side that originally created it
+
+ solid / empty : solid
+ solid / water : solid
+ water / empty : water
+ water / water : none
+===============
+*/
+void MakeFaces_r (node_t *node)
+{
+ portal_t *p;
+ int s;
+
+ // recurse down to leafs
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ MakeFaces_r (node->children[0]);
+ MakeFaces_r (node->children[1]);
+
+ // merge together all visible faces on the node
+ if (!nomerge)
+ MergeNodeFaces (node);
+ if (!nosubdiv)
+ SubdivideNodeFaces (node);
+
+ return;
+ }
+
+ // solid leafs never have visible faces
+ if (node->contents & CONTENTS_SOLID)
+ return;
+
+ // see which portals are valid
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ p->face[s] = FaceFromPortal (p, s);
+ if (p->face[s])
+ {
+ c_nodefaces++;
+ p->face[s]->next = p->onnode->faces;
+ p->onnode->faces = p->face[s];
+ }
+ }
+}
+
+/*
+============
+MakeFaces
+============
+*/
+void MakeFaces (node_t *node)
+{
+ qprintf ("--- MakeFaces ---\n");
+ c_merge = 0;
+ c_subdivide = 0;
+ c_nodefaces = 0;
+
+ MakeFaces_r (node);
+
+ qprintf ("%5i makefaces\n", c_nodefaces);
+ qprintf ("%5i merged\n", c_merge);
+ qprintf ("%5i subdivided\n", c_subdivide);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include <windows.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glaux.h>
+
+#include "qbsp.h"
+
+// can't use the glvertex3fv functions, because the vec3_t fields
+// could be either floats or doubles, depending on DOUBLEVEC_T
+
+qboolean drawflag;
+vec3_t draw_mins, draw_maxs;
+
+
+#define WIN_SIZE 512
+
+void InitWindow (void)
+{
+ auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
+ auxInitPosition (0, 0, WIN_SIZE, WIN_SIZE);
+ auxInitWindow ("qcsg");
+}
+
+void Draw_ClearWindow (void)
+{
+ static int init;
+ int w, h, g;
+ vec_t mx, my;
+
+ if (!drawflag)
+ return;
+
+ if (!init)
+ {
+ init = true;
+ InitWindow ();
+ }
+
+ glClearColor (1,0.8,0.8,0);
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ w = (draw_maxs[0] - draw_mins[0]);
+ h = (draw_maxs[1] - draw_mins[1]);
+
+ mx = draw_mins[0] + w/2;
+ my = draw_mins[1] + h/2;
+
+ g = w > h ? w : h;
+
+ glLoadIdentity ();
+ gluPerspective (90, 1, 2, 16384);
+ gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
+
+ glColor3f (0,0,0);
+// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glDisable (GL_DEPTH_TEST);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+#if 0
+ glColor4f (1,0,0,0.5);
+ glBegin (GL_POLYGON);
+
+ glVertex3f (0, 500, 0);
+ glVertex3f (0, 900, 0);
+ glVertex3f (0, 900, 100);
+ glVertex3f (0, 500, 100);
+
+ glEnd ();
+#endif
+
+ glFlush ();
+
+}
+
+void Draw_SetRed (void)
+{
+ if (!drawflag)
+ return;
+
+ glColor3f (1,0,0);
+}
+
+void Draw_SetGrey (void)
+{
+ if (!drawflag)
+ return;
+
+ glColor3f (0.5,0.5,0.5);
+}
+
+void Draw_SetBlack (void)
+{
+ if (!drawflag)
+ return;
+
+ glColor3f (0,0,0);
+}
+
+void DrawWinding (winding_t *w)
+{
+ int i;
+
+ if (!drawflag)
+ return;
+
+ glColor4f (0,0,0,0.5);
+ glBegin (GL_LINE_LOOP);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+ glEnd ();
+
+ glColor4f (0,1,0,0.3);
+ glBegin (GL_POLYGON);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+ glEnd ();
+
+ glFlush ();
+}
+
+void DrawAuxWinding (winding_t *w)
+{
+ int i;
+
+ if (!drawflag)
+ return;
+
+ glColor4f (0,0,0,0.5);
+ glBegin (GL_LINE_LOOP);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+ glEnd ();
+
+ glColor4f (1,0,0,0.3);
+ glBegin (GL_POLYGON);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+ glEnd ();
+
+ glFlush ();
+}
+
+//============================================================
+
+#define GLSERV_PORT 25001
+
+qboolean wins_init;
+int draw_socket;
+
+void GLS_BeginScene (void)
+{
+ WSADATA winsockdata;
+ WORD wVersionRequested;
+ struct sockaddr_in address;
+ int r;
+
+ if (!wins_init)
+ {
+ wins_init = true;
+
+ wVersionRequested = MAKEWORD(1, 1);
+
+ r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
+
+ if (r)
+ Error ("Winsock initialization failed.");
+
+ }
+
+ // connect a socket to the server
+
+ draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (draw_socket == -1)
+ Error ("draw_socket failed");
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ address.sin_port = GLSERV_PORT;
+ r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
+ if (r == -1)
+ {
+ closesocket (draw_socket);
+ draw_socket = 0;
+ }
+}
+
+void GLS_Winding (winding_t *w, int code)
+{
+ byte buf[1024];
+ int i, j;
+
+ if (!draw_socket)
+ return;
+
+ ((int *)buf)[0] = w->numpoints;
+ ((int *)buf)[1] = code;
+ for (i=0 ; i<w->numpoints ; i++)
+ for (j=0 ; j<3 ; j++)
+ ((float *)buf)[2+i*3+j] = w->p[i][j];
+
+ send (draw_socket, buf, w->numpoints*12+8, 0);
+}
+
+void GLS_EndScene (void)
+{
+ closesocket (draw_socket);
+ draw_socket = 0;
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+int c_glfaces;
+
+int PortalVisibleSides (portal_t *p)
+{
+ int fcon, bcon;
+
+ if (!p->onnode)
+ return 0; // outside
+
+ fcon = p->nodes[0]->contents;
+ bcon = p->nodes[1]->contents;
+
+ // same contents never create a face
+ if (fcon == bcon)
+ return 0;
+
+ // FIXME: is this correct now?
+ if (!fcon)
+ return 1;
+ if (!bcon)
+ return 2;
+ return 0;
+}
+
+void OutputWinding (winding_t *w, FILE *glview)
+{
+ static int level = 128;
+ vec_t light;
+ int i;
+
+ fprintf (glview, "%i\n", w->numpoints);
+ level+=28;
+ light = (level&255)/255.0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ light,
+ light,
+ light);
+ }
+ fprintf (glview, "\n");
+}
+
+/*
+=============
+OutputPortal
+=============
+*/
+void OutputPortal (portal_t *p, FILE *glview)
+{
+ winding_t *w;
+ int sides;
+
+ sides = PortalVisibleSides (p);
+ if (!sides)
+ return;
+
+ c_glfaces++;
+
+ w = p->winding;
+
+ if (sides == 2) // back side
+ w = ReverseWinding (w);
+
+ OutputWinding (w, glview);
+
+ if (sides == 2)
+ FreeWinding(w);
+}
+
+/*
+=============
+WriteGLView_r
+=============
+*/
+void WriteGLView_r (node_t *node, FILE *glview)
+{
+ portal_t *p, *nextp;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ WriteGLView_r (node->children[0], glview);
+ WriteGLView_r (node->children[1], glview);
+ return;
+ }
+
+ // write all the portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ if (p->nodes[0] == node)
+ {
+ OutputPortal (p, glview);
+ nextp = p->next[0];
+ }
+ else
+ nextp = p->next[1];
+ }
+}
+
+/*
+=============
+WriteGLView
+=============
+*/
+void WriteGLView (tree_t *tree, char *source)
+{
+ char name[1024];
+ FILE *glview;
+
+ c_glfaces = 0;
+ sprintf (name, "%s%s.gl",outbase, source);
+ printf ("Writing %s\n", name);
+
+ glview = fopen (name, "w");
+ if (!glview)
+ Error ("Couldn't open %s", name);
+ WriteGLView_r (tree->headnode, glview);
+ fclose (glview);
+
+ printf ("%5i c_glfaces\n", c_glfaces);
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+/*
+==============================================================================
+
+LEAF FILE GENERATION
+
+Save out name.line for qe3 to read
+==============================================================================
+*/
+
+
+/*
+=============
+LeakFile
+
+Finds the shortest possible chain of portals
+that leads from the outside leaf to a specifically
+occupied leaf
+=============
+*/
+void LeakFile (tree_t *tree)
+{
+ vec3_t mid;
+ FILE *linefile;
+ char filename[1024];
+ node_t *node;
+ int count;
+
+ if (!tree->outside_node.occupied)
+ return;
+
+ qprintf ("--- LeakFile ---\n");
+
+ //
+ // write the points to the file
+ //
+ sprintf (filename, "%s.lin", source);
+ linefile = fopen (filename, "w");
+ if (!linefile)
+ Error ("Couldn't open %s\n", filename);
+
+ count = 0;
+ node = &tree->outside_node;
+ while (node->occupied > 1)
+ {
+ int next;
+ portal_t *p, *nextportal;
+ node_t *nextnode;
+ int s;
+
+ // find the best portal exit
+ next = node->occupied;
+ for (p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (p->nodes[s]->occupied
+ && p->nodes[s]->occupied < next)
+ {
+ nextportal = p;
+ nextnode = p->nodes[s];
+ next = nextnode->occupied;
+ }
+ }
+ node = nextnode;
+ WindingCenter (nextportal->winding, mid);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ count++;
+ }
+ // add the occupant center
+ GetVectorForKey (node->occupant, "origin", mid);
+
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ qprintf ("%5i point linefile\n", count+1);
+
+ fclose (linefile);
+}
+
--- /dev/null
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = qbsp3
+EXE = $(ODIR)/qbsp3
+all: $(EXE)
+
+_next:
+ make "CFLAGS = -c -g -I../../common -DDOUBLEVEC_T" "ODIR = next"
+
+_irix:
+ make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3" "ODIR = irix"
+
+_irixdebug:
+ make "CFLAGS = -c -O2 -g -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -g" "ODIR = irix"
+
+_irixinst:
+ make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3" "ODIR = irix"
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+_irixclean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+ make "CFLAGS = -c -O4 -I../../common -threads -DDOUBLEVEC_T" "LDFLAGS = -threads" "ODIR = osf"
+
+clean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/brushbsp.o $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/faces.o $(ODIR)/nodraw.o $(ODIR)/glfile.o $(ODIR)/leakfile.o $(ODIR)/map.o $(ODIR)/mathlib.o $(ODIR)/polylib.o $(ODIR)/portals.o $(ODIR)/prtfile.o $(ODIR)/qbsp3.o $(ODIR)/scriplib.o $(ODIR)/textures.o $(ODIR)/threads.o $(ODIR)/tree.o $(ODIR)/writebsp.o $(ODIR)/csg.o
+
+$(EXE) : $(FILES)
+ cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
+
+$(ODIR)/brushbsp.o : brushbsp.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/faces.o : faces.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/nodraw.o : nodraw.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/glfile.o : glfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/leakfile.o : leakfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/map.o : map.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/portals.o : portals.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/prtfile.o : prtfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/qbsp3.o : qbsp3.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/tree.o : tree.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/textures.o : textures.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/writebsp.o : writebsp.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/csg.o : csg.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../../common/cmdlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../../common/mathlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/polylib.o : ../../common/polylib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../../common/scriplib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../../common/threads.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../../common/bspfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+extern qboolean onlyents;
+
+int nummapbrushes;
+mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+int nummapbrushsides;
+side_t brushsides[MAX_MAP_SIDES];
+brush_texture_t side_brushtextures[MAX_MAP_SIDES];
+
+int nummapplanes;
+plane_t mapplanes[MAX_MAP_PLANES];
+
+#define PLANE_HASHES 1024
+plane_t *planehash[PLANE_HASHES];
+
+vec3_t map_mins, map_maxs;
+
+// undefine to make plane finding use linear sort
+#define USE_HASHING
+
+void TestExpandBrushes (void);
+
+int c_boxbevels;
+int c_edgebevels;
+
+int c_areaportals;
+
+int c_clipbrushes;
+
+/*
+=============================================================================
+
+PLANE FINDING
+
+=============================================================================
+*/
+
+
+/*
+=================
+PlaneTypeForNormal
+=================
+*/
+int PlaneTypeForNormal (vec3_t normal)
+{
+ vec_t ax, ay, az;
+
+// NOTE: should these have an epsilon around 1.0?
+ if (normal[0] == 1.0 || normal[0] == -1.0)
+ return PLANE_X;
+ if (normal[1] == 1.0 || normal[1] == -1.0)
+ return PLANE_Y;
+ if (normal[2] == 1.0 || normal[2] == -1.0)
+ return PLANE_Z;
+
+ ax = fabs(normal[0]);
+ ay = fabs(normal[1]);
+ az = fabs(normal[2]);
+
+ if (ax >= ay && ax >= az)
+ return PLANE_ANYX;
+ if (ay >= ax && ay >= az)
+ return PLANE_ANYY;
+ return PLANE_ANYZ;
+}
+
+/*
+================
+PlaneEqual
+================
+*/
+#define NORMAL_EPSILON 0.00001
+#define DIST_EPSILON 0.01
+qboolean PlaneEqual (plane_t *p, vec3_t normal, vec_t dist)
+{
+#if 1
+ if (
+ fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
+ && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
+ && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
+ && fabs(p->dist - dist) < DIST_EPSILON )
+ return true;
+#else
+ if (p->normal[0] == normal[0]
+ && p->normal[1] == normal[1]
+ && p->normal[2] == normal[2]
+ && p->dist == dist)
+ return true;
+#endif
+ return false;
+}
+
+/*
+================
+AddPlaneToHash
+================
+*/
+void AddPlaneToHash (plane_t *p)
+{
+ int hash;
+
+ hash = (int)fabs(p->dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ p->hash_chain = planehash[hash];
+ planehash[hash] = p;
+}
+
+/*
+================
+CreateNewFloatPlane
+================
+*/
+int CreateNewFloatPlane (vec3_t normal, vec_t dist)
+{
+ plane_t *p, temp;
+
+ if (VectorLength(normal) < 0.5)
+ Error ("FloatPlane: bad normal");
+ // create a new plane
+ if (nummapplanes+2 > MAX_MAP_PLANES)
+ Error ("MAX_MAP_PLANES");
+
+ p = &mapplanes[nummapplanes];
+ VectorCopy (normal, p->normal);
+ p->dist = dist;
+ p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
+
+ VectorSubtract (vec3_origin, normal, (p+1)->normal);
+ (p+1)->dist = -dist;
+
+ nummapplanes += 2;
+
+ // allways put axial planes facing positive first
+ if (p->type < 3)
+ {
+ if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
+ {
+ // flip order
+ temp = *p;
+ *p = *(p+1);
+ *(p+1) = temp;
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 1;
+ }
+ }
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 2;
+}
+
+/*
+==============
+SnapVector
+==============
+*/
+void SnapVector (vec3_t normal)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ break;
+ }
+ if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = -1;
+ break;
+ }
+ }
+}
+
+/*
+==============
+SnapPlane
+==============
+*/
+void SnapPlane (vec3_t normal, vec_t *dist)
+{
+ SnapVector (normal);
+
+ if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
+ *dist = Q_rint(*dist);
+}
+
+/*
+=============
+FindFloatPlane
+
+=============
+*/
+#ifndef USE_HASHING
+int FindFloatPlane (vec3_t normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+
+ SnapPlane (normal, &dist);
+ for (i=0, p=mapplanes ; i<nummapplanes ; i++, p++)
+ {
+ if (PlaneEqual (p, normal, dist))
+ return i;
+ }
+
+ return CreateNewFloatPlane (normal, dist);
+}
+#else
+int FindFloatPlane (vec3_t normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+ int hash, h;
+
+ SnapPlane (normal, &dist);
+ hash = (int)fabs(dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ // search the border bins as well
+ for (i=-1 ; i<=1 ; i++)
+ {
+ h = (hash+i)&(PLANE_HASHES-1);
+ for (p = planehash[h] ; p ; p=p->hash_chain)
+ {
+ if (PlaneEqual (p, normal, dist))
+ return p-mapplanes;
+ }
+ }
+
+ return CreateNewFloatPlane (normal, dist);
+}
+#endif
+
+/*
+================
+PlaneFromPoints
+================
+*/
+int PlaneFromPoints (int *p0, int *p1, int *p2)
+{
+ vec3_t t1, t2, normal;
+ vec_t dist;
+
+ VectorSubtract (p0, p1, t1);
+ VectorSubtract (p2, p1, t2);
+ CrossProduct (t1, t2, normal);
+ VectorNormalize (normal, normal);
+
+ dist = DotProduct (p0, normal);
+
+ return FindFloatPlane (normal, dist);
+}
+
+
+//====================================================================
+
+
+/*
+===========
+BrushContents
+===========
+*/
+int BrushContents (mapbrush_t *b)
+{
+ int contents;
+ side_t *s;
+ int i;
+ int trans;
+
+ s = &b->original_sides[0];
+ contents = s->contents;
+ trans = texinfo[s->texinfo].flags;
+ for (i=1 ; i<b->numsides ; i++, s++)
+ {
+ s = &b->original_sides[i];
+ trans |= texinfo[s->texinfo].flags;
+ if (s->contents != contents)
+ {
+ printf ("Entity %i, Brush %i: mixed face contents\n"
+ , b->entitynum, b->brushnum);
+ break;
+ }
+ }
+
+ // if any side is translucent, mark the contents
+ // and change solid to window
+ if ( trans & (SURF_TRANS33|SURF_TRANS66) )
+ {
+ contents |= CONTENTS_TRANSLUCENT;
+ if (contents & CONTENTS_SOLID)
+ {
+ contents &= ~CONTENTS_SOLID;
+ contents |= CONTENTS_WINDOW;
+ }
+ }
+
+ return contents;
+}
+
+
+//============================================================================
+
+/*
+=================
+AddBrushBevels
+
+Adds any additional planes necessary to allow the brush to be expanded
+against axial bounding boxes
+=================
+*/
+void AddBrushBevels (mapbrush_t *b)
+{
+ int axis, dir;
+ int i, j, k, l, order;
+ side_t sidetemp;
+ brush_texture_t tdtemp;
+ side_t *s, *s2;
+ vec3_t normal;
+ float dist;
+ winding_t *w, *w2;
+ vec3_t vec, vec2;
+ float d;
+
+ //
+ // add the axial planes
+ //
+ order = 0;
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2, order++)
+ {
+ // see if the plane is allready present
+ for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
+ {
+ if (mapplanes[s->planenum].normal[axis] == dir)
+ break;
+ }
+
+ if (i == b->numsides)
+ { // add a new side
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+ nummapbrushsides++;
+ b->numsides++;
+ VectorClear (normal);
+ normal[axis] = dir;
+ if (dir == 1)
+ dist = b->maxs[axis];
+ else
+ dist = -b->mins[axis];
+ s->planenum = FindFloatPlane (normal, dist);
+ s->texinfo = b->original_sides[0].texinfo;
+ s->contents = b->original_sides[0].contents;
+ s->bevel = true;
+ c_boxbevels++;
+ }
+
+ // if the plane is not in it canonical order, swap it
+ if (i != order)
+ {
+ sidetemp = b->original_sides[order];
+ b->original_sides[order] = b->original_sides[i];
+ b->original_sides[i] = sidetemp;
+
+ j = b->original_sides - brushsides;
+ tdtemp = side_brushtextures[j+order];
+ side_brushtextures[j+order] = side_brushtextures[j+i];
+ side_brushtextures[j+i] = tdtemp;
+ }
+ }
+ }
+
+ //
+ // add the edge bevels
+ //
+ if (b->numsides == 6)
+ return; // pure axial
+
+ // test the non-axial plane edges
+ for (i=6 ; i<b->numsides ; i++)
+ {
+ s = b->original_sides + i;
+ w = s->winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ k = (j+1)%w->numpoints;
+ VectorSubtract (w->p[j], w->p[k], vec);
+ if (VectorNormalize (vec, vec) < 0.5)
+ continue;
+ SnapVector (vec);
+ for (k=0 ; k<3 ; k++)
+ if ( vec[k] == -1 || vec[k] == 1)
+ break; // axial
+ if (k != 3)
+ continue; // only test non-axial edges
+
+ // try the six possible slanted axials from this edge
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2)
+ {
+ // construct a plane
+ VectorClear (vec2);
+ vec2[axis] = dir;
+ CrossProduct (vec, vec2, normal);
+ if (VectorNormalize (normal, normal) < 0.5)
+ continue;
+ dist = DotProduct (w->p[j], normal);
+
+ // if all the points on all the sides are
+ // behind this plane, it is a proper edge bevel
+ for (k=0 ; k<b->numsides ; k++)
+ {
+ // if this plane has allready been used, skip it
+ if (PlaneEqual (&mapplanes[b->original_sides[k].planenum]
+ , normal, dist) )
+ break;
+
+ w2 = b->original_sides[k].winding;
+ if (!w2)
+ continue;
+ for (l=0 ; l<w2->numpoints ; l++)
+ {
+ d = DotProduct (w2->p[l], normal) - dist;
+ if (d > 0.1)
+ break; // point in front
+ }
+ if (l != w2->numpoints)
+ break;
+ }
+
+ if (k != b->numsides)
+ continue; // wasn't part of the outer hull
+ // add this plane
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+ nummapbrushsides++;
+ s2 = &b->original_sides[b->numsides];
+ s2->planenum = FindFloatPlane (normal, dist);
+ s2->texinfo = b->original_sides[0].texinfo;
+ s2->contents = b->original_sides[0].contents;
+ s2->bevel = true;
+ c_edgebevels++;
+ b->numsides++;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+================
+MakeBrushWindings
+
+makes basewindigs for sides and mins / maxs for the brush
+================
+*/
+qboolean MakeBrushWindings (mapbrush_t *ob)
+{
+ int i, j;
+ winding_t *w;
+ side_t *side;
+ plane_t *plane;
+
+ ClearBounds (ob->mins, ob->maxs);
+
+ for (i=0 ; i<ob->numsides ; i++)
+ {
+ plane = &mapplanes[ob->original_sides[i].planenum];
+ w = BaseWindingForPlane (plane->normal, plane->dist);
+ for (j=0 ; j<ob->numsides && w; j++)
+ {
+ if (i == j)
+ continue;
+ if (ob->original_sides[j].bevel)
+ continue;
+ plane = &mapplanes[ob->original_sides[j].planenum^1];
+ ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
+ }
+
+ side = &ob->original_sides[i];
+ side->winding = w;
+ if (w)
+ {
+ side->visible = true;
+ for (j=0 ; j<w->numpoints ; j++)
+ AddPointToBounds (w->p[j], ob->mins, ob->maxs);
+ }
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (ob->mins[0] < -4096 || ob->maxs[0] > 4096)
+ printf ("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum);
+ if (ob->mins[0] > 4096 || ob->maxs[0] < -4096)
+ printf ("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum);
+ }
+
+ return true;
+}
+
+
+/*
+=================
+ParseBrush
+=================
+*/
+void ParseBrush (entity_t *mapent)
+{
+ mapbrush_t *b;
+ int i,j, k;
+ int mt;
+ side_t *side, *s2;
+ int planenum;
+ brush_texture_t td;
+ int planepts[3][3];
+
+ if (nummapbrushes == MAX_MAP_BRUSHES)
+ Error ("nummapbrushes == MAX_MAP_BRUSHES");
+
+ b = &mapbrushes[nummapbrushes];
+ b->original_sides = &brushsides[nummapbrushsides];
+ b->entitynum = num_entities-1;
+ b->brushnum = nummapbrushes - mapent->firstbrush;
+
+ do
+ {
+ if (!GetToken (true))
+ break;
+ if (!strcmp (token, "}") )
+ break;
+
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+ side = &brushsides[nummapbrushsides];
+
+ // read the three point plane definition
+ for (i=0 ; i<3 ; i++)
+ {
+ if (i != 0)
+ GetToken (true);
+ if (strcmp (token, "(") )
+ Error ("parsing brush");
+
+ for (j=0 ; j<3 ; j++)
+ {
+ GetToken (false);
+ planepts[i][j] = atoi(token);
+ }
+
+ GetToken (false);
+ if (strcmp (token, ")") )
+ Error ("parsing brush");
+
+ }
+
+
+ //
+ // read the texturedef
+ //
+ GetToken (false);
+ strcpy (td.name, token);
+
+ GetToken (false);
+ td.shift[0] = atoi(token);
+ GetToken (false);
+ td.shift[1] = atoi(token);
+ GetToken (false);
+ td.rotate = atoi(token);
+ GetToken (false);
+ td.scale[0] = atof(token);
+ GetToken (false);
+ td.scale[1] = atof(token);
+
+ // find default flags and values
+ mt = FindMiptex (td.name);
+ td.flags = textureref[mt].flags;
+ td.value = textureref[mt].value;
+ side->contents = textureref[mt].contents;
+ side->surf = td.flags = textureref[mt].flags;
+
+ if (TokenAvailable())
+ {
+ GetToken (false);
+ side->contents = atoi(token);
+ GetToken (false);
+ side->surf = td.flags = atoi(token);
+ GetToken (false);
+ td.value = atoi(token);
+ }
+
+ // translucent objects are automatically classified as detail
+ if (side->surf & (SURF_TRANS33|SURF_TRANS66) )
+ side->contents |= CONTENTS_DETAIL;
+ if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+ side->contents |= CONTENTS_DETAIL;
+ if (fulldetail)
+ side->contents &= ~CONTENTS_DETAIL;
+ if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1)
+ | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST) ) )
+ side->contents |= CONTENTS_SOLID;
+
+ // hints and skips are never detail, and have no content
+ if (side->surf & (SURF_HINT|SURF_SKIP) )
+ {
+ side->contents = 0;
+ side->surf &= ~CONTENTS_DETAIL;
+ }
+
+
+ //
+ // find the plane number
+ //
+ planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]);
+ if (planenum == -1)
+ {
+ printf ("Entity %i, Brush %i: plane with no normal\n"
+ , b->entitynum, b->brushnum);
+ continue;
+ }
+
+ //
+ // see if the plane has been used already
+ //
+ for (k=0 ; k<b->numsides ; k++)
+ {
+ s2 = b->original_sides + k;
+ if (s2->planenum == planenum)
+ {
+ printf ("Entity %i, Brush %i: duplicate plane\n"
+ , b->entitynum, b->brushnum);
+ break;
+ }
+ if ( s2->planenum == (planenum^1) )
+ {
+ printf ("Entity %i, Brush %i: mirrored plane\n"
+ , b->entitynum, b->brushnum);
+ break;
+ }
+ }
+ if (k != b->numsides)
+ continue; // duplicated
+
+ //
+ // keep this side
+ //
+
+ side = b->original_sides + b->numsides;
+ side->planenum = planenum;
+ side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum],
+ &td, vec3_origin);
+
+ // save the td off in case there is an origin brush and we
+ // have to recalculate the texinfo
+ side_brushtextures[nummapbrushsides] = td;
+
+ nummapbrushsides++;
+ b->numsides++;
+ } while (1);
+
+ // get the content for the entire brush
+ b->contents = BrushContents (b);
+
+ // allow detail brushes to be removed
+ if (nodetail && (b->contents & CONTENTS_DETAIL) )
+ {
+ b->numsides = 0;
+ return;
+ }
+
+ // allow water brushes to be removed
+ if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )
+ {
+ b->numsides = 0;
+ return;
+ }
+
+ // create windings for sides and bounds for brush
+ MakeBrushWindings (b);
+
+ // brushes that will not be visible at all will never be
+ // used as bsp splitters
+ if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+ {
+ c_clipbrushes++;
+ for (i=0 ; i<b->numsides ; i++)
+ b->original_sides[i].texinfo = TEXINFO_NODE;
+ }
+
+ //
+ // origin brushes are removed, but they set
+ // the rotation origin for the rest of the brushes
+ // in the entity. After the entire entity is parsed,
+ // the planenums and texinfos will be adjusted for
+ // the origin brush
+ //
+ if (b->contents & CONTENTS_ORIGIN)
+ {
+ char string[32];
+ vec3_t origin;
+
+ if (num_entities == 1)
+ {
+ Error ("Entity %i, Brush %i: origin brushes not allowed in world"
+ , b->entitynum, b->brushnum);
+ return;
+ }
+
+ VectorAdd (b->mins, b->maxs, origin);
+ VectorScale (origin, 0.5, origin);
+
+ sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
+ SetKeyValue (&entities[b->entitynum], "origin", string);
+
+ VectorCopy (origin, entities[b->entitynum].origin);
+
+ // don't keep this brush
+ b->numsides = 0;
+
+ return;
+ }
+
+ AddBrushBevels (b);
+
+ nummapbrushes++;
+ mapent->numbrushes++;
+}
+
+/*
+================
+MoveBrushesToWorld
+
+Takes all of the brushes from the current entity and
+adds them to the world's brush list.
+
+Used by func_group and func_areaportal
+================
+*/
+void MoveBrushesToWorld (entity_t *mapent)
+{
+ int newbrushes;
+ int worldbrushes;
+ mapbrush_t *temp;
+ int i;
+
+ // this is pretty gross, because the brushes are expected to be
+ // in linear order for each entity
+
+ newbrushes = mapent->numbrushes;
+ worldbrushes = entities[0].numbrushes;
+
+ temp = malloc(newbrushes*sizeof(mapbrush_t));
+ memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
+
+#if 0 // let them keep their original brush numbers
+ for (i=0 ; i<newbrushes ; i++)
+ temp[i].entitynum = 0;
+#endif
+
+ // make space to move the brushes (overlapped copy)
+ memmove (mapbrushes + worldbrushes + newbrushes,
+ mapbrushes + worldbrushes,
+ sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );
+
+ // copy the new brushes down
+ memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
+
+ // fix up indexes
+ entities[0].numbrushes += newbrushes;
+ for (i=1 ; i<num_entities ; i++)
+ entities[i].firstbrush += newbrushes;
+ free (temp);
+
+ mapent->numbrushes = 0;
+}
+
+/*
+================
+ParseMapEntity
+================
+*/
+qboolean ParseMapEntity (void)
+{
+ entity_t *mapent;
+ epair_t *e;
+ side_t *s;
+ int i, j;
+ int startbrush, startsides;
+ vec_t newdist;
+ mapbrush_t *b;
+
+ if (!GetToken (true))
+ return false;
+
+ if (strcmp (token, "{") )
+ Error ("ParseEntity: { not found");
+
+ if (num_entities == MAX_MAP_ENTITIES)
+ Error ("num_entities == MAX_MAP_ENTITIES");
+
+ startbrush = nummapbrushes;
+ startsides = nummapbrushsides;
+
+ mapent = &entities[num_entities];
+ num_entities++;
+ memset (mapent, 0, sizeof(*mapent));
+ mapent->firstbrush = nummapbrushes;
+ mapent->numbrushes = 0;
+// mapent->portalareas[0] = -1;
+// mapent->portalareas[1] = -1;
+
+ do
+ {
+ if (!GetToken (true))
+ Error ("ParseEntity: EOF without closing brace");
+ if (!strcmp (token, "}") )
+ break;
+ if (!strcmp (token, "{") )
+ ParseBrush (mapent);
+ else
+ {
+ e = ParseEpair ();
+ e->next = mapent->epairs;
+ mapent->epairs = e;
+ }
+ } while (1);
+
+ GetVectorForKey (mapent, "origin", mapent->origin);
+
+ //
+ // if there was an origin brush, offset all of the planes and texinfo
+ //
+ if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
+ {
+ for (i=0 ; i<mapent->numbrushes ; i++)
+ {
+ b = &mapbrushes[mapent->firstbrush + i];
+ for (j=0 ; j<b->numsides ; j++)
+ {
+ s = &b->original_sides[j];
+ newdist = mapplanes[s->planenum].dist -
+ DotProduct (mapplanes[s->planenum].normal, mapent->origin);
+ s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
+ s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum],
+ &side_brushtextures[s-brushsides], mapent->origin);
+ }
+ MakeBrushWindings (b);
+ }
+ }
+
+ // group entities are just for editor convenience
+ // toss all brushes into the world entity
+ if (!strcmp ("func_group", ValueForKey (mapent, "classname")))
+ {
+ MoveBrushesToWorld (mapent);
+ mapent->numbrushes = 0;
+ return true;
+ }
+
+ // areaportal entities move their brushes, but don't eliminate
+ // the entity
+ if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))
+ {
+ char str[128];
+
+ if (mapent->numbrushes != 1)
+ Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
+
+ b = &mapbrushes[nummapbrushes-1];
+ b->contents = CONTENTS_AREAPORTAL;
+ c_areaportals++;
+ mapent->areaportalnum = c_areaportals;
+ // set the portal number as "style"
+ sprintf (str, "%i", c_areaportals);
+ SetKeyValue (mapent, "style", str);
+ MoveBrushesToWorld (mapent);
+ return true;
+ }
+
+ return true;
+}
+
+//===================================================================
+
+/*
+================
+LoadMapFile
+================
+*/
+void LoadMapFile (char *filename)
+{
+ int i;
+
+ qprintf ("--- LoadMapFile ---\n");
+
+ LoadScriptFile (filename);
+
+ nummapbrushsides = 0;
+ num_entities = 0;
+
+ while (ParseMapEntity ())
+ {
+ }
+
+ ClearBounds (map_mins, map_maxs);
+ for (i=0 ; i<entities[0].numbrushes ; i++)
+ {
+ if (mapbrushes[i].mins[0] > 4096)
+ continue; // no valid points
+ AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs);
+ AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs);
+ }
+
+ qprintf ("%5i brushes\n", nummapbrushes);
+ qprintf ("%5i clipbrushes\n", c_clipbrushes);
+ qprintf ("%5i total sides\n", nummapbrushsides);
+ qprintf ("%5i boxbevels\n", c_boxbevels);
+ qprintf ("%5i edgebevels\n", c_edgebevels);
+ qprintf ("%5i entities\n", num_entities);
+ qprintf ("%5i planes\n", nummapplanes);
+ qprintf ("%5i areaportals\n", c_areaportals);
+ qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
+ map_maxs[0],map_maxs[1],map_maxs[2]);
+
+// TestExpandBrushes ();
+}
+
+
+//====================================================================
+
+
+/*
+================
+TestExpandBrushes
+
+Expands all the brush planes and saves a new map out
+================
+*/
+void TestExpandBrushes (void)
+{
+ FILE *f;
+ side_t *s;
+ int i, j, bn;
+ winding_t *w;
+ char *name = "expanded.map";
+ mapbrush_t *brush;
+ vec_t dist;
+
+ printf ("writing %s\n", name);
+ f = fopen (name, "wb");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+
+ for (bn=0 ; bn<nummapbrushes ; bn++)
+ {
+ brush = &mapbrushes[bn];
+ fprintf (f, "{\n");
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = brush->original_sides + i;
+ dist = mapplanes[s->planenum].dist;
+ for (j=0 ; j<3 ; j++)
+ dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
+
+ w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist);
+
+ fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+
+ fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);
+ FreeWinding (w);
+ }
+ fprintf (f, "}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+ Error ("can't proceed after expanding brushes");
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+vec3_t draw_mins, draw_maxs;
+qboolean drawflag;
+
+void Draw_ClearWindow (void)
+{
+}
+
+//============================================================
+
+#define GLSERV_PORT 25001
+
+
+void GLS_BeginScene (void)
+{
+}
+
+void GLS_Winding (winding_t *w, int code)
+{
+}
+
+void GLS_EndScene (void)
+{
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+
+int c_active_portals;
+int c_peak_portals;
+int c_boundary;
+int c_boundary_sides;
+
+/*
+===========
+AllocPortal
+===========
+*/
+portal_t *AllocPortal (void)
+{
+ portal_t *p;
+
+ if (numthreads == 1)
+ c_active_portals++;
+ if (c_active_portals > c_peak_portals)
+ c_peak_portals = c_active_portals;
+
+ p = malloc (sizeof(portal_t));
+ memset (p, 0, sizeof(portal_t));
+
+ return p;
+}
+
+void FreePortal (portal_t *p)
+{
+ if (p->winding)
+ FreeWinding (p->winding);
+ if (numthreads == 1)
+ c_active_portals--;
+ free (p);
+}
+
+//==============================================================
+
+/*
+==============
+VisibleContents
+
+Returns the single content bit of the
+strongest visible content present
+==============
+*/
+int VisibleContents (int contents)
+{
+ int i;
+
+ for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
+ if (contents & i )
+ return i;
+
+ return 0;
+}
+
+
+/*
+===============
+ClusterContents
+===============
+*/
+int ClusterContents (node_t *node)
+{
+ int c1, c2, c;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return node->contents;
+
+ c1 = ClusterContents(node->children[0]);
+ c2 = ClusterContents(node->children[1]);
+ c = c1|c2;
+
+ // a cluster may include some solid detail areas, but
+ // still be seen into
+ if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
+ c &= ~CONTENTS_SOLID;
+ return c;
+}
+
+/*
+=============
+Portal_VisFlood
+
+Returns true if the portal is empty or translucent, allowing
+the PVS calculation to see through it.
+The nodes on either side of the portal may actually be clusters,
+not leafs, so all contents should be ored together
+=============
+*/
+qboolean Portal_VisFlood (portal_t *p)
+{
+ int c1, c2;
+
+ if (!p->onnode)
+ return false; // to global outsideleaf
+
+ c1 = ClusterContents(p->nodes[0]);
+ c2 = ClusterContents(p->nodes[1]);
+
+ if (!VisibleContents (c1^c2))
+ return true;
+
+ if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+ c1 = 0;
+ if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+ c2 = 0;
+
+ if ( (c1|c2) & CONTENTS_SOLID )
+ return false; // can't see through solid
+
+ if (! (c1 ^ c2))
+ return true; // identical on both sides
+
+ if (!VisibleContents (c1^c2))
+ return true;
+ return false;
+}
+
+
+/*
+===============
+Portal_EntityFlood
+
+The entity flood determines which areas are
+"outside" on the map, which are then filled in.
+Flowing from side s to side !s
+===============
+*/
+qboolean Portal_EntityFlood (portal_t *p, int s)
+{
+ if (p->nodes[0]->planenum != PLANENUM_LEAF
+ || p->nodes[1]->planenum != PLANENUM_LEAF)
+ Error ("Portal_EntityFlood: not a leaf");
+
+ // can never cross to a solid
+ if ( (p->nodes[0]->contents & CONTENTS_SOLID)
+ || (p->nodes[1]->contents & CONTENTS_SOLID) )
+ return false;
+
+ // can flood through everything else
+ return true;
+}
+
+
+//=============================================================================
+
+int c_tinyportals;
+
+/*
+=============
+AddPortalToNodes
+=============
+*/
+void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
+{
+ if (p->nodes[0] || p->nodes[1])
+ Error ("AddPortalToNode: allready included");
+
+ p->nodes[0] = front;
+ p->next[0] = front->portals;
+ front->portals = p;
+
+ p->nodes[1] = back;
+ p->next[1] = back->portals;
+ back->portals = p;
+}
+
+
+/*
+=============
+RemovePortalFromNode
+=============
+*/
+void RemovePortalFromNode (portal_t *portal, node_t *l)
+{
+ portal_t **pp, *t;
+
+// remove reference to the current portal
+ pp = &l->portals;
+ while (1)
+ {
+ t = *pp;
+ if (!t)
+ Error ("RemovePortalFromNode: portal not in leaf");
+
+ if ( t == portal )
+ break;
+
+ if (t->nodes[0] == l)
+ pp = &t->next[0];
+ else if (t->nodes[1] == l)
+ pp = &t->next[1];
+ else
+ Error ("RemovePortalFromNode: portal not bounding leaf");
+ }
+
+ if (portal->nodes[0] == l)
+ {
+ *pp = portal->next[0];
+ portal->nodes[0] = NULL;
+ }
+ else if (portal->nodes[1] == l)
+ {
+ *pp = portal->next[1];
+ portal->nodes[1] = NULL;
+ }
+}
+
+//============================================================================
+
+void PrintPortal (portal_t *p)
+{
+ int i;
+ winding_t *w;
+
+ w = p->winding;
+ for (i=0 ; i<w->numpoints ; i++)
+ printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
+ , w->p[i][1], w->p[i][2]);
+}
+
+/*
+================
+MakeHeadnodePortals
+
+The created portals will face the global outside_node
+================
+*/
+#define SIDESPACE 8
+void MakeHeadnodePortals (tree_t *tree)
+{
+ vec3_t bounds[2];
+ int i, j, n;
+ portal_t *p, *portals[6];
+ plane_t bplanes[6], *pl;
+ node_t *node;
+
+ node = tree->headnode;
+
+// pad with some space so there will never be null volume leafs
+ for (i=0 ; i<3 ; i++)
+ {
+ bounds[0][i] = tree->mins[i] - SIDESPACE;
+ bounds[1][i] = tree->maxs[i] + SIDESPACE;
+ }
+
+ tree->outside_node.planenum = PLANENUM_LEAF;
+ tree->outside_node.brushlist = NULL;
+ tree->outside_node.portals = NULL;
+ tree->outside_node.contents = 0;
+
+ for (i=0 ; i<3 ; i++)
+ for (j=0 ; j<2 ; j++)
+ {
+ n = j*3 + i;
+
+ p = AllocPortal ();
+ portals[n] = p;
+
+ pl = &bplanes[n];
+ memset (pl, 0, sizeof(*pl));
+ if (j)
+ {
+ pl->normal[i] = -1;
+ pl->dist = -bounds[j][i];
+ }
+ else
+ {
+ pl->normal[i] = 1;
+ pl->dist = bounds[j][i];
+ }
+ p->plane = *pl;
+ p->winding = BaseWindingForPlane (pl->normal, pl->dist);
+ AddPortalToNodes (p, node, &tree->outside_node);
+ }
+
+// clip the basewindings by all the other planes
+ for (i=0 ; i<6 ; i++)
+ {
+ for (j=0 ; j<6 ; j++)
+ {
+ if (j == i)
+ continue;
+ ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
+ }
+ }
+}
+
+//===================================================
+
+
+/*
+================
+BaseWindingForNode
+================
+*/
+#define BASE_WINDING_EPSILON 0.001
+#define SPLIT_WINDING_EPSILON 0.001
+
+winding_t *BaseWindingForNode (node_t *node)
+{
+ winding_t *w;
+ node_t *n;
+ plane_t *plane;
+ vec3_t normal;
+ vec_t dist;
+
+ w = BaseWindingForPlane (mapplanes[node->planenum].normal
+ , mapplanes[node->planenum].dist);
+
+ // clip by all the parents
+ for (n=node->parent ; n && w ; )
+ {
+ plane = &mapplanes[n->planenum];
+
+ if (n->children[0] == node)
+ { // take front
+ ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
+ }
+ else
+ { // take back
+ VectorSubtract (vec3_origin, plane->normal, normal);
+ dist = -plane->dist;
+ ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
+ }
+ node = n;
+ n = n->parent;
+ }
+
+ return w;
+}
+
+//============================================================
+
+qboolean WindingIsTiny (winding_t *w);
+
+/*
+==================
+MakeNodePortal
+
+create the new portal by taking the full plane winding for the cutting plane
+and clipping it by all of parents of this node
+==================
+*/
+void MakeNodePortal (node_t *node)
+{
+ portal_t *new_portal, *p;
+ winding_t *w;
+ vec3_t normal;
+ float dist;
+ int side;
+
+ w = BaseWindingForNode (node);
+
+ // clip the portal by all the other portals in the node
+ for (p = node->portals ; p && w; p = p->next[side])
+ {
+ if (p->nodes[0] == node)
+ {
+ side = 0;
+ VectorCopy (p->plane.normal, normal);
+ dist = p->plane.dist;
+ }
+ else if (p->nodes[1] == node)
+ {
+ side = 1;
+ VectorSubtract (vec3_origin, p->plane.normal, normal);
+ dist = -p->plane.dist;
+ }
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+
+ ChopWindingInPlace (&w, normal, dist, 0.1);
+ }
+
+ if (!w)
+ {
+ return;
+ }
+
+ if (WindingIsTiny (w))
+ {
+ c_tinyportals++;
+ FreeWinding (w);
+ return;
+ }
+
+
+ new_portal = AllocPortal ();
+ new_portal->plane = mapplanes[node->planenum];
+ new_portal->onnode = node;
+ new_portal->winding = w;
+ AddPortalToNodes (new_portal, node->children[0], node->children[1]);
+}
+
+
+/*
+==============
+SplitNodePortals
+
+Move or split the portals that bound node so that the node's
+children have portals instead of node.
+==============
+*/
+void SplitNodePortals (node_t *node)
+{
+ portal_t *p, *next_portal, *new_portal;
+ node_t *f, *b, *other_node;
+ int side;
+ plane_t *plane;
+ winding_t *frontwinding, *backwinding;
+
+ plane = &mapplanes[node->planenum];
+ f = node->children[0];
+ b = node->children[1];
+
+ for (p = node->portals ; p ; p = next_portal)
+ {
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ side = 1;
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+ next_portal = p->next[side];
+
+ other_node = p->nodes[!side];
+ RemovePortalFromNode (p, p->nodes[0]);
+ RemovePortalFromNode (p, p->nodes[1]);
+
+//
+// cut the portal into two portals, one on each side of the cut plane
+//
+ ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
+ SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
+
+ if (frontwinding && WindingIsTiny(frontwinding))
+ {
+ FreeWinding (frontwinding);
+ frontwinding = NULL;
+ c_tinyportals++;
+ }
+
+ if (backwinding && WindingIsTiny(backwinding))
+ {
+ FreeWinding (backwinding);
+ backwinding = NULL;
+ c_tinyportals++;
+ }
+
+ if (!frontwinding && !backwinding)
+ { // tiny windings on both sides
+ continue;
+ }
+
+ if (!frontwinding)
+ {
+ FreeWinding (backwinding);
+ if (side == 0)
+ AddPortalToNodes (p, b, other_node);
+ else
+ AddPortalToNodes (p, other_node, b);
+ continue;
+ }
+ if (!backwinding)
+ {
+ FreeWinding (frontwinding);
+ if (side == 0)
+ AddPortalToNodes (p, f, other_node);
+ else
+ AddPortalToNodes (p, other_node, f);
+ continue;
+ }
+
+ // the winding is split
+ new_portal = AllocPortal ();
+ *new_portal = *p;
+ new_portal->winding = backwinding;
+ FreeWinding (p->winding);
+ p->winding = frontwinding;
+
+ if (side == 0)
+ {
+ AddPortalToNodes (p, f, other_node);
+ AddPortalToNodes (new_portal, b, other_node);
+ }
+ else
+ {
+ AddPortalToNodes (p, other_node, f);
+ AddPortalToNodes (new_portal, other_node, b);
+ }
+ }
+
+ node->portals = NULL;
+}
+
+
+/*
+================
+CalcNodeBounds
+================
+*/
+void CalcNodeBounds (node_t *node)
+{
+ portal_t *p;
+ int s;
+ int i;
+
+ // calc mins/maxs for both leafs and nodes
+ ClearBounds (node->mins, node->maxs);
+ for (p = node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ for (i=0 ; i<p->winding->numpoints ; i++)
+ AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
+ }
+}
+
+
+/*
+==================
+MakeTreePortals_r
+==================
+*/
+void MakeTreePortals_r (node_t *node)
+{
+ int i;
+
+ CalcNodeBounds (node);
+ if (node->mins[0] >= node->maxs[0])
+ {
+ printf ("WARNING: node without a volume\n");
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (node->mins[i] < -8000 || node->maxs[i] > 8000)
+ {
+ printf ("WARNING: node with unbounded volume\n");
+ break;
+ }
+ }
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ MakeNodePortal (node);
+ SplitNodePortals (node);
+
+ MakeTreePortals_r (node->children[0]);
+ MakeTreePortals_r (node->children[1]);
+}
+
+/*
+==================
+MakeTreePortals
+==================
+*/
+void MakeTreePortals (tree_t *tree)
+{
+ MakeHeadnodePortals (tree);
+ MakeTreePortals_r (tree->headnode);
+}
+
+/*
+=========================================================
+
+FLOOD ENTITIES
+
+=========================================================
+*/
+
+/*
+=============
+FloodPortals_r
+=============
+*/
+void FloodPortals_r (node_t *node, int dist)
+{
+ portal_t *p;
+ int s;
+
+ node->occupied = dist;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ if (p->nodes[!s]->occupied)
+ continue;
+
+ if (!Portal_EntityFlood (p, s))
+ continue;
+
+ FloodPortals_r (p->nodes[!s], dist+1);
+ }
+}
+
+/*
+=============
+PlaceOccupant
+=============
+*/
+qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
+{
+ node_t *node;
+ vec_t d;
+ plane_t *plane;
+
+ // find the leaf to start in
+ node = headnode;
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &mapplanes[node->planenum];
+ d = DotProduct (origin, plane->normal) - plane->dist;
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ if (node->contents == CONTENTS_SOLID)
+ return false;
+ node->occupant = occupant;
+
+ FloodPortals_r (node, 1);
+
+ return true;
+}
+
+/*
+=============
+FloodEntities
+
+Marks all nodes that can be reached by entites
+=============
+*/
+qboolean FloodEntities (tree_t *tree)
+{
+ int i;
+ vec3_t origin;
+ char *cl;
+ qboolean inside;
+ node_t *headnode;
+
+ headnode = tree->headnode;
+ qprintf ("--- FloodEntities ---\n");
+ inside = false;
+ tree->outside_node.occupied = 0;
+
+ for (i=1 ; i<num_entities ; i++)
+ {
+ GetVectorForKey (&entities[i], "origin", origin);
+ if (VectorCompare(origin, vec3_origin))
+ continue;
+
+ cl = ValueForKey (&entities[i], "classname");
+ origin[2] += 1; // so objects on floor are ok
+
+ // nudge playerstart around if needed so clipping hulls allways
+ // have a vlaid point
+ if (!strcmp (cl, "info_player_start"))
+ {
+ int x, y;
+
+ for (x=-16 ; x<=16 ; x += 16)
+ {
+ for (y=-16 ; y<=16 ; y += 16)
+ {
+ origin[0] += x;
+ origin[1] += y;
+ if (PlaceOccupant (headnode, origin, &entities[i]))
+ {
+ inside = true;
+ goto gotit;
+ }
+ origin[0] -= x;
+ origin[1] -= y;
+ }
+ }
+gotit: ;
+ }
+ else
+ {
+ if (PlaceOccupant (headnode, origin, &entities[i]))
+ inside = true;
+ }
+ }
+
+ if (!inside)
+ {
+ qprintf ("no entities in open -- no filling\n");
+ }
+ else if (tree->outside_node.occupied)
+ {
+ qprintf ("entity reached from outside -- no filling\n");
+ }
+
+ return (qboolean)(inside && !tree->outside_node.occupied);
+}
+
+/*
+=========================================================
+
+FLOOD AREAS
+
+=========================================================
+*/
+
+int c_areas;
+
+/*
+=============
+FloodAreas_r
+=============
+*/
+void FloodAreas_r (node_t *node)
+{
+ portal_t *p;
+ int s;
+ bspbrush_t *b;
+ entity_t *e;
+
+ if (node->contents == CONTENTS_AREAPORTAL)
+ {
+ // this node is part of an area portal
+ b = node->brushlist;
+ e = &entities[b->original->entitynum];
+
+ // if the current area has allready touched this
+ // portal, we are done
+ if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
+ return;
+
+ // note the current area as bounding the portal
+ if (e->portalareas[1])
+ {
+ printf ("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum);
+ return;
+ }
+ if (e->portalareas[0])
+ e->portalareas[1] = c_areas;
+ else
+ e->portalareas[0] = c_areas;
+
+ return;
+ }
+
+ if (node->area)
+ return; // allready got it
+ node->area = c_areas;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+#if 0
+ if (p->nodes[!s]->occupied)
+ continue;
+#endif
+ if (!Portal_EntityFlood (p, s))
+ continue;
+
+ FloodAreas_r (p->nodes[!s]);
+ }
+}
+
+/*
+=============
+FindAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void FindAreas_r (node_t *node)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FindAreas_r (node->children[0]);
+ FindAreas_r (node->children[1]);
+ return;
+ }
+
+ if (node->area)
+ return; // allready got it
+
+ if (node->contents & CONTENTS_SOLID)
+ return;
+
+ if (!node->occupied)
+ return; // not reachable by entities
+
+ // area portals are allways only flooded into, never
+ // out of
+ if (node->contents == CONTENTS_AREAPORTAL)
+ return;
+
+ c_areas++;
+ FloodAreas_r (node);
+}
+
+/*
+=============
+SetAreaPortalAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void SetAreaPortalAreas_r (node_t *node)
+{
+ bspbrush_t *b;
+ entity_t *e;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ SetAreaPortalAreas_r (node->children[0]);
+ SetAreaPortalAreas_r (node->children[1]);
+ return;
+ }
+
+ if (node->contents == CONTENTS_AREAPORTAL)
+ {
+ if (node->area)
+ return; // allready set
+
+ b = node->brushlist;
+ e = &entities[b->original->entitynum];
+ node->area = e->portalareas[0];
+ if (!e->portalareas[1])
+ {
+ printf ("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum);
+ return;
+ }
+ }
+}
+
+/*
+=============
+EmitAreaPortals
+
+=============
+*/
+void EmitAreaPortals (node_t *headnode)
+{
+ int i, j;
+ entity_t *e;
+ dareaportal_t *dp;
+
+ if (c_areas > MAX_MAP_AREAS)
+ Error ("MAX_MAP_AREAS");
+ numareas = c_areas+1;
+ numareaportals = 1; // leave 0 as an error
+
+ for (i=1 ; i<=c_areas ; i++)
+ {
+ dareas[i].firstareaportal = numareaportals;
+ for (j=0 ; j<num_entities ; j++)
+ {
+ e = &entities[j];
+ if (!e->areaportalnum)
+ continue;
+ dp = &dareaportals[numareaportals];
+ if (e->portalareas[0] == i)
+ {
+ dp->portalnum = e->areaportalnum;
+ dp->otherarea = e->portalareas[1];
+ numareaportals++;
+ }
+ else if (e->portalareas[1] == i)
+ {
+ dp->portalnum = e->areaportalnum;
+ dp->otherarea = e->portalareas[0];
+ numareaportals++;
+ }
+ }
+ dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal;
+ }
+
+ qprintf ("%5i numareas\n", numareas);
+ qprintf ("%5i numareaportals\n", numareaportals);
+}
+
+/*
+=============
+FloodAreas
+
+Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
+=============
+*/
+void FloodAreas (tree_t *tree)
+{
+ qprintf ("--- FloodAreas ---\n");
+ FindAreas_r (tree->headnode);
+ SetAreaPortalAreas_r (tree->headnode);
+ qprintf ("%5i areas\n", c_areas);
+}
+
+//======================================================
+
+int c_outside;
+int c_inside;
+int c_solid;
+
+void FillOutside_r (node_t *node)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FillOutside_r (node->children[0]);
+ FillOutside_r (node->children[1]);
+ return;
+ }
+
+ // anything not reachable by an entity
+ // can be filled away
+ if (!node->occupied)
+ {
+ if (node->contents != CONTENTS_SOLID)
+ {
+ c_outside++;
+ node->contents = CONTENTS_SOLID;
+ }
+ else
+ c_solid++;
+ }
+ else
+ c_inside++;
+
+}
+
+/*
+=============
+FillOutside
+
+Fill all nodes that can't be reached by entities
+=============
+*/
+void FillOutside (node_t *headnode)
+{
+ c_outside = 0;
+ c_inside = 0;
+ c_solid = 0;
+ qprintf ("--- FillOutside ---\n");
+ FillOutside_r (headnode);
+ qprintf ("%5i solid leafs\n", c_solid);
+ qprintf ("%5i leafs filled\n", c_outside);
+ qprintf ("%5i inside leafs\n", c_inside);
+}
+
+
+//==============================================================
+
+/*
+============
+FindPortalSide
+
+Finds a brush side to use for texturing the given portal
+============
+*/
+void FindPortalSide (portal_t *p)
+{
+ int viscontents;
+ bspbrush_t *bb;
+ mapbrush_t *brush;
+ node_t *n;
+ int i,j;
+ int planenum;
+ side_t *side, *bestside;
+ float dot, bestdot;
+ plane_t *p1, *p2;
+
+ // decide which content change is strongest
+ // solid > lava > water, etc
+ viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
+ if (!viscontents)
+ return;
+
+ planenum = p->onnode->planenum;
+ bestside = NULL;
+ bestdot = 0;
+
+ for (j=0 ; j<2 ; j++)
+ {
+ n = p->nodes[j];
+ p1 = &mapplanes[p->onnode->planenum];
+ for (bb=n->brushlist ; bb ; bb=bb->next)
+ {
+ brush = bb->original;
+ if ( !(brush->contents & viscontents) )
+ continue;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = &brush->original_sides[i];
+ if (side->bevel)
+ continue;
+ if (side->texinfo == TEXINFO_NODE)
+ continue; // non-visible
+ if ((side->planenum&~1) == planenum)
+ { // exact match
+ bestside = &brush->original_sides[i];
+ goto gotit;
+ }
+ // see how close the match is
+ p2 = &mapplanes[side->planenum&~1];
+ dot = DotProduct (p1->normal, p2->normal);
+ if (dot > bestdot)
+ {
+ bestdot = dot;
+ bestside = side;
+ }
+ }
+ }
+ }
+
+gotit:
+ if (!bestside)
+ qprintf ("WARNING: side not found for portal\n");
+
+ p->sidefound = true;
+ p->side = bestside;
+}
+
+
+/*
+===============
+MarkVisibleSides_r
+
+===============
+*/
+void MarkVisibleSides_r (node_t *node)
+{
+ portal_t *p;
+ int s;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ MarkVisibleSides_r (node->children[0]);
+ MarkVisibleSides_r (node->children[1]);
+ return;
+ }
+
+ // empty leafs are never boundary leafs
+ if (!node->contents)
+ return;
+
+ // see if there is a visible face
+ for (p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (!p->onnode)
+ continue; // edge of world
+ if (!p->sidefound)
+ FindPortalSide (p);
+ if (p->side)
+ p->side->visible = true;
+ }
+
+}
+
+/*
+=============
+MarkVisibleSides
+
+=============
+*/
+void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush)
+{
+ int i, j;
+ mapbrush_t *mb;
+ int numsides;
+
+ qprintf ("--- MarkVisibleSides ---\n");
+
+ // clear all the visible flags
+ for (i=startbrush ; i<endbrush ; i++)
+ {
+ mb = &mapbrushes[i];
+
+ numsides = mb->numsides;
+ for (j=0 ; j<numsides ; j++)
+ mb->original_sides[j].visible = false;
+ }
+
+ // set visible flags on the sides that are used by portals
+ MarkVisibleSides_r (tree->headnode);
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+/*
+==============================================================================
+
+PORTAL FILE GENERATION
+
+Save out name.prt for qvis to read
+==============================================================================
+*/
+
+
+#define PORTALFILE "PRT1"
+
+FILE *pf;
+int num_visclusters; // clusters the player can be in
+int num_visportals;
+
+void WriteFloat (FILE *f, vec_t v)
+{
+ if ( fabs(v - Q_rint(v)) < 0.001 )
+ fprintf (f,"%i ",(int)Q_rint(v));
+ else
+ fprintf (f,"%f ",v);
+}
+
+/*
+=================
+WritePortalFile_r
+=================
+*/
+void WritePortalFile_r (node_t *node)
+{
+ int i, s;
+ portal_t *p;
+ winding_t *w;
+ vec3_t normal;
+ vec_t dist;
+
+ // decision node
+ if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
+ {
+ WritePortalFile_r (node->children[0]);
+ WritePortalFile_r (node->children[1]);
+ return;
+ }
+
+ if (node->contents & CONTENTS_SOLID)
+ return;
+
+ for (p = node->portals ; p ; p=p->next[s])
+ {
+ w = p->winding;
+ s = (p->nodes[1] == node);
+ if (w && p->nodes[0] == node)
+ {
+ if (!Portal_VisFlood (p))
+ continue;
+ // write out to the file
+
+ // sometimes planes get turned around when they are very near
+ // the changeover point between different axis. interpret the
+ // plane the same way vis will, and flip the side orders if needed
+ // FIXME: is this still relevent?
+ WindingPlane (w, normal, &dist);
+ if ( DotProduct (p->plane.normal, normal) < 0.99 )
+ { // backwards...
+ fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
+ }
+ else
+ fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pf,"(");
+ WriteFloat (pf, w->p[i][0]);
+ WriteFloat (pf, w->p[i][1]);
+ WriteFloat (pf, w->p[i][2]);
+ fprintf (pf,") ");
+ }
+ fprintf (pf,"\n");
+ }
+ }
+
+}
+
+/*
+================
+FillLeafNumbers_r
+
+All of the leafs under node will have the same cluster
+================
+*/
+void FillLeafNumbers_r (node_t *node, int num)
+{
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ if (node->contents & CONTENTS_SOLID)
+ node->cluster = -1;
+ else
+ node->cluster = num;
+ return;
+ }
+ node->cluster = num;
+ FillLeafNumbers_r (node->children[0], num);
+ FillLeafNumbers_r (node->children[1], num);
+}
+
+/*
+================
+NumberLeafs_r
+================
+*/
+void NumberLeafs_r (node_t *node)
+{
+ portal_t *p;
+
+ if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
+ { // decision node
+ node->cluster = -99;
+ NumberLeafs_r (node->children[0]);
+ NumberLeafs_r (node->children[1]);
+ return;
+ }
+
+ // either a leaf or a detail cluster
+
+ if ( node->contents & CONTENTS_SOLID )
+ { // solid block, viewpoint never inside
+ node->cluster = -1;
+ return;
+ }
+
+ FillLeafNumbers_r (node, num_visclusters);
+ num_visclusters++;
+
+ // count the portals
+ for (p = node->portals ; p ; )
+ {
+ if (p->nodes[0] == node) // only write out from first leaf
+ {
+ if (Portal_VisFlood (p))
+ num_visportals++;
+ p = p->next[0];
+ }
+ else
+ p = p->next[1];
+ }
+
+}
+
+
+/*
+================
+CreateVisPortals_r
+================
+*/
+void CreateVisPortals_r (node_t *node)
+{
+ // stop as soon as we get to a detail_seperator, which
+ // means that everything below is in a single cluster
+ if (node->planenum == PLANENUM_LEAF || node->detail_seperator )
+ return;
+
+ MakeNodePortal (node);
+ SplitNodePortals (node);
+
+ CreateVisPortals_r (node->children[0]);
+ CreateVisPortals_r (node->children[1]);
+}
+
+/*
+================
+FinishVisPortals_r
+================
+*/
+void FinishVisPortals2_r (node_t *node)
+{
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ MakeNodePortal (node);
+ SplitNodePortals (node);
+
+ FinishVisPortals2_r (node->children[0]);
+ FinishVisPortals2_r (node->children[1]);
+}
+
+void FinishVisPortals_r (node_t *node)
+{
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ if (node->detail_seperator)
+ {
+ FinishVisPortals2_r (node);
+ return;
+ }
+
+ FinishVisPortals_r (node->children[0]);
+ FinishVisPortals_r (node->children[1]);
+}
+
+
+int clusterleaf;
+void SaveClusters_r (node_t *node)
+{
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ dleafs[clusterleaf++].cluster = node->cluster;
+ return;
+ }
+ SaveClusters_r (node->children[0]);
+ SaveClusters_r (node->children[1]);
+}
+
+/*
+================
+WritePortalFile
+================
+*/
+void WritePortalFile (tree_t *tree)
+{
+ char filename[1024];
+ node_t *headnode;
+
+ qprintf ("--- WritePortalFile ---\n");
+
+ headnode = tree->headnode;
+ num_visclusters = 0;
+ num_visportals = 0;
+
+ FreeTreePortals_r (headnode);
+
+ MakeHeadnodePortals (tree);
+
+ CreateVisPortals_r (headnode);
+
+// set the cluster field in every leaf and count the total number of portals
+
+ NumberLeafs_r (headnode);
+
+// write the file
+ sprintf (filename, "%s.prt", source);
+ printf ("writing %s\n", filename);
+ pf = fopen (filename, "w");
+ if (!pf)
+ Error ("Error opening %s", filename);
+
+ fprintf (pf, "%s\n", PORTALFILE);
+ fprintf (pf, "%i\n", num_visclusters);
+ fprintf (pf, "%i\n", num_visportals);
+
+ qprintf ("%5i visclusters\n", num_visclusters);
+ qprintf ("%5i visportals\n", num_visportals);
+
+ WritePortalFile_r (headnode);
+
+ fclose (pf);
+
+ // we need to store the clusters out now because ordering
+ // issues made us do this after writebsp...
+ clusterleaf = 1;
+ SaveClusters_r (headnode);
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "scriplib.h"
+#include "polylib.h"
+#include "threads.h"
+#include "bspfile.h"
+
+#define MAX_BRUSH_SIDES 128
+#define CLIP_EPSILON 0.1
+
+#define BOGUS_RANGE 8192
+
+#define TEXINFO_NODE -1 // side is allready on a node
+
+typedef struct plane_s
+{
+ vec3_t normal;
+ vec_t dist;
+ int type;
+ struct plane_s *hash_chain;
+} plane_t;
+
+typedef struct
+{
+ vec_t shift[2];
+ vec_t rotate;
+ vec_t scale[2];
+ char name[32];
+ int flags;
+ int value;
+} brush_texture_t;
+
+typedef struct side_s
+{
+ int planenum;
+ int texinfo;
+ winding_t *winding;
+ struct side_s *original; // bspbrush_t sides will reference the mapbrush_t sides
+ int contents; // from miptex
+ int surf; // from miptex
+ qboolean visible; // choose visble planes first
+ qboolean tested; // this plane allready checked as a split
+ qboolean bevel; // don't ever use for bsp splitting
+} side_t;
+
+typedef struct brush_s
+{
+ int entitynum;
+ int brushnum;
+
+ int contents;
+
+ vec3_t mins, maxs;
+
+ int numsides;
+ side_t *original_sides;
+} mapbrush_t;
+
+#define PLANENUM_LEAF -1
+
+#define MAXEDGES 20
+
+typedef struct face_s
+{
+ struct face_s *next; // on node
+
+ // the chain of faces off of a node can be merged or split,
+ // but each face_t along the way will remain in the chain
+ // until the entire tree is freed
+ struct face_s *merged; // if set, this face isn't valid anymore
+ struct face_s *split[2]; // if set, this face isn't valid anymore
+
+ struct portal_s *portal;
+ int texinfo;
+ int planenum;
+ int contents; // faces in different contents can't merge
+ int outputnumber;
+ winding_t *w;
+ int numpoints;
+ qboolean badstartvert; // tjunctions cannot be fixed without a midpoint vertex
+ int vertexnums[MAXEDGES];
+} face_t;
+
+
+
+typedef struct bspbrush_s
+{
+ struct bspbrush_s *next;
+ vec3_t mins, maxs;
+ int side, testside; // side of node during construction
+ mapbrush_t *original;
+ int numsides;
+ side_t sides[6]; // variably sized
+} bspbrush_t;
+
+
+
+#define MAX_NODE_BRUSHES 8
+typedef struct node_s
+{
+ // both leafs and nodes
+ int planenum; // -1 = leaf node
+ struct node_s *parent;
+ vec3_t mins, maxs; // valid after portalization
+ bspbrush_t *volume; // one for each leaf/node
+
+ // nodes only
+ qboolean detail_seperator; // a detail brush caused the split
+ side_t *side; // the side that created the node
+ struct node_s *children[2];
+ face_t *faces;
+
+ // leafs only
+ bspbrush_t *brushlist; // fragments of all brushes in this leaf
+ int contents; // OR of all brush contents
+ int occupied; // 1 or greater can reach entity
+ entity_t *occupant; // for leak file testing
+ int cluster; // for portalfile writing
+ int area; // for areaportals
+ struct portal_s *portals; // also on nodes during construction
+} node_t;
+
+typedef struct portal_s
+{
+ plane_t plane;
+ node_t *onnode; // NULL = outside box
+ node_t *nodes[2]; // [0] = front side of plane
+ struct portal_s *next[2];
+ winding_t *winding;
+
+ qboolean sidefound; // false if ->side hasn't been checked
+ side_t *side; // NULL = non-visible
+ face_t *face[2]; // output face in bsp file
+} portal_t;
+
+typedef struct
+{
+ node_t *headnode;
+ node_t outside_node;
+ vec3_t mins, maxs;
+} tree_t;
+
+extern int entity_num;
+
+extern plane_t mapplanes[MAX_MAP_PLANES];
+extern int nummapplanes;
+
+extern int nummapbrushes;
+extern mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+extern vec3_t map_mins, map_maxs;
+
+#define MAX_MAP_SIDES (MAX_MAP_BRUSHES*6)
+
+extern int nummapbrushsides;
+extern side_t brushsides[MAX_MAP_SIDES];
+
+extern qboolean noprune;
+extern qboolean nodetail;
+extern qboolean fulldetail;
+extern qboolean nomerge;
+extern qboolean nosubdiv;
+extern qboolean nowater;
+extern qboolean noweld;
+extern qboolean noshare;
+extern qboolean notjunc;
+
+extern vec_t microvolume;
+
+extern char outbase[32];
+
+extern char source[1024];
+
+void LoadMapFile (char *filename);
+int FindFloatPlane (vec3_t normal, vec_t dist);
+
+//=============================================================================
+
+// textures.c
+
+typedef struct
+{
+ char name[64];
+ int flags;
+ int value;
+ int contents;
+ char animname[64];
+} textureref_t;
+
+#define MAX_MAP_TEXTURES 1024
+
+extern textureref_t textureref[MAX_MAP_TEXTURES];
+
+int FindMiptex (char *name);
+
+int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, vec3_t origin);
+
+//=============================================================================
+
+void FindGCD (int *v);
+
+mapbrush_t *Brush_LoadEntity (entity_t *ent);
+int PlaneTypeForNormal (vec3_t normal);
+qboolean MakeBrushPlanes (mapbrush_t *b);
+int FindIntPlane (int *inormal, int *iorigin);
+void CreateBrush (int brushnum);
+
+
+//=============================================================================
+
+// draw.c
+
+extern vec3_t draw_mins, draw_maxs;
+extern qboolean drawflag;
+
+void Draw_ClearWindow (void);
+void DrawWinding (winding_t *w);
+
+void GLS_BeginScene (void);
+void GLS_Winding (winding_t *w, int code);
+void GLS_EndScene (void);
+
+//=============================================================================
+
+// csg
+
+bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
+ vec3_t clipmins, vec3_t clipmaxs);
+bspbrush_t *ChopBrushes (bspbrush_t *head);
+bspbrush_t *InitialBrushList (bspbrush_t *list);
+bspbrush_t *OptimizedBrushList (bspbrush_t *list);
+
+void WriteBrushMap (char *name, bspbrush_t *list);
+
+//=============================================================================
+
+// brushbsp
+
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
+
+bspbrush_t *CopyBrush (bspbrush_t *brush);
+
+void SplitBrush (bspbrush_t *brush, int planenum,
+ bspbrush_t **front, bspbrush_t **back);
+
+tree_t *AllocTree (void);
+node_t *AllocNode (void);
+bspbrush_t *AllocBrush (int numsides);
+int CountBrushList (bspbrush_t *brushes);
+void FreeBrush (bspbrush_t *brushes);
+vec_t BrushVolume (bspbrush_t *brush);
+
+void BoundBrush (bspbrush_t *brush);
+void FreeBrushList (bspbrush_t *brushes);
+
+tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs);
+
+//=============================================================================
+
+// portals.c
+
+int VisibleContents (int contents);
+
+void MakeHeadnodePortals (tree_t *tree);
+void MakeNodePortal (node_t *node);
+void SplitNodePortals (node_t *node);
+
+qboolean Portal_VisFlood (portal_t *p);
+
+qboolean FloodEntities (tree_t *tree);
+void FillOutside (node_t *headnode);
+void FloodAreas (tree_t *tree);
+void MarkVisibleSides (tree_t *tree, int start, int end);
+void FreePortal (portal_t *p);
+void EmitAreaPortals (node_t *headnode);
+
+void MakeTreePortals (tree_t *tree);
+
+//=============================================================================
+
+// glfile.c
+
+void OutputWinding (winding_t *w, FILE *glview);
+void WriteGLView (tree_t *tree, char *source);
+
+//=============================================================================
+
+// leakfile.c
+
+void LeakFile (tree_t *tree);
+
+//=============================================================================
+
+// prtfile.c
+
+void WritePortalFile (tree_t *tree);
+
+//=============================================================================
+
+// writebsp.c
+
+void SetModelNumbers (void);
+void SetLightStyles (void);
+
+void BeginBSPFile (void);
+void WriteBSP (node_t *headnode);
+void EndBSPFile (void);
+void BeginModel (void);
+void EndModel (void);
+
+//=============================================================================
+
+// faces.c
+
+void MakeFaces (node_t *headnode);
+void FixTjuncs (node_t *headnode);
+int GetEdge2 (int v1, int v2, face_t *f);
+
+face_t *AllocFace (void);
+void FreeFace (face_t *f);
+
+void MergeNodeFaces (node_t *node);
+
+//=============================================================================
+
+// tree.c
+
+void FreeTree (tree_t *tree);
+void FreeTree_r (node_t *node);
+void PrintTree_r (node_t *node, int depth);
+void FreeTreePortals_r (node_t *node);
+void PruneNodes_r (node_t *node);
+void PruneNodes (node_t *node);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+extern float subdivide_size;
+
+char source[1024];
+char name[1024];
+
+vec_t microvolume = 1.0;
+qboolean noprune;
+qboolean glview;
+qboolean nodetail;
+qboolean fulldetail;
+qboolean onlyents;
+qboolean nomerge;
+qboolean nowater;
+qboolean nofill;
+qboolean nocsg;
+qboolean noweld;
+qboolean noshare;
+qboolean nosubdiv;
+qboolean notjunc;
+qboolean noopt;
+qboolean leaktest;
+qboolean verboseentities;
+
+char outbase[32];
+
+int block_xl = -8, block_xh = 7, block_yl = -8, block_yh = 7;
+
+int entity_num;
+
+
+node_t *block_nodes[10][10];
+
+/*
+============
+BlockTree
+
+============
+*/
+node_t *BlockTree (int xl, int yl, int xh, int yh)
+{
+ node_t *node;
+ vec3_t normal;
+ float dist;
+ int mid;
+
+ if (xl == xh && yl == yh)
+ {
+ node = block_nodes[xl+5][yl+5];
+ if (!node)
+ { // return an empty leaf
+ node = AllocNode ();
+ node->planenum = PLANENUM_LEAF;
+ node->contents = 0; //CONTENTS_SOLID;
+ return node;
+ }
+ return node;
+ }
+
+ // create a seperator along the largest axis
+ node = AllocNode ();
+
+ if (xh - xl > yh - yl)
+ { // split x axis
+ mid = xl + (xh-xl)/2 + 1;
+ normal[0] = 1;
+ normal[1] = 0;
+ normal[2] = 0;
+ dist = mid*1024;
+ node->planenum = FindFloatPlane (normal, dist);
+ node->children[0] = BlockTree ( mid, yl, xh, yh);
+ node->children[1] = BlockTree ( xl, yl, mid-1, yh);
+ }
+ else
+ {
+ mid = yl + (yh-yl)/2 + 1;
+ normal[0] = 0;
+ normal[1] = 1;
+ normal[2] = 0;
+ dist = mid*1024;
+ node->planenum = FindFloatPlane (normal, dist);
+ node->children[0] = BlockTree ( xl, mid, xh, yh);
+ node->children[1] = BlockTree ( xl, yl, xh, mid-1);
+ }
+
+ return node;
+}
+
+/*
+============
+ProcessBlock_Thread
+
+============
+*/
+int brush_start, brush_end;
+void ProcessBlock_Thread (int blocknum)
+{
+ int xblock, yblock;
+ vec3_t mins, maxs;
+ bspbrush_t *brushes;
+ tree_t *tree;
+ node_t *node;
+
+ yblock = block_yl + blocknum / (block_xh-block_xl+1);
+ xblock = block_xl + blocknum % (block_xh-block_xl+1);
+
+ qprintf ("############### block %2i,%2i ###############\n", xblock, yblock);
+
+ mins[0] = xblock*1024;
+ mins[1] = yblock*1024;
+ mins[2] = -4096;
+ maxs[0] = (xblock+1)*1024;
+ maxs[1] = (yblock+1)*1024;
+ maxs[2] = 4096;
+
+ // the makelist and chopbrushes could be cached between the passes...
+ brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs);
+ if (!brushes)
+ {
+ node = AllocNode ();
+ node->planenum = PLANENUM_LEAF;
+ node->contents = CONTENTS_SOLID;
+ block_nodes[xblock+5][yblock+5] = node;
+ return;
+ }
+
+ if (!nocsg)
+ brushes = ChopBrushes (brushes);
+
+ tree = BrushBSP (brushes, mins, maxs);
+
+ block_nodes[xblock+5][yblock+5] = tree->headnode;
+}
+
+/*
+============
+ProcessWorldModel
+
+============
+*/
+void ProcessWorldModel (void)
+{
+ entity_t *e;
+ tree_t *tree;
+ qboolean leaked;
+ qboolean optimize;
+
+ e = &entities[entity_num];
+
+ brush_start = e->firstbrush;
+ brush_end = brush_start + e->numbrushes;
+ leaked = false;
+
+ //
+ // perform per-block operations
+ //
+ if (block_xh * 1024 > map_maxs[0])
+ block_xh = floor(map_maxs[0]/1024.0);
+ if ( (block_xl+1) * 1024 < map_mins[0])
+ block_xl = floor(map_mins[0]/1024.0);
+ if (block_yh * 1024 > map_maxs[1])
+ block_yh = floor(map_maxs[1]/1024.0);
+ if ( (block_yl+1) * 1024 < map_mins[1])
+ block_yl = floor(map_mins[1]/1024.0);
+
+ if (block_xl <-4)
+ block_xl = -4;
+ if (block_yl <-4)
+ block_yl = -4;
+ if (block_xh > 3)
+ block_xh = 3;
+ if (block_yh > 3)
+ block_yh = 3;
+
+ for (optimize = false ; optimize <= true ; optimize++)
+ {
+ qprintf ("--------------------------------------------\n");
+
+ RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1),
+ !verbose, ProcessBlock_Thread);
+
+ //
+ // build the division tree
+ // oversizing the blocks guarantees that all the boundaries
+ // will also get nodes.
+ //
+
+ qprintf ("--------------------------------------------\n");
+
+ tree = AllocTree ();
+ tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
+
+ tree->mins[0] = (block_xl)*1024;
+ tree->mins[1] = (block_yl)*1024;
+ tree->mins[2] = map_mins[2] - 8;
+
+ tree->maxs[0] = (block_xh+1)*1024;
+ tree->maxs[1] = (block_yh+1)*1024;
+ tree->maxs[2] = map_maxs[2] + 8;
+
+ //
+ // perform the global operations
+ //
+ MakeTreePortals (tree);
+
+ if (FloodEntities (tree))
+ FillOutside (tree->headnode);
+ else
+ {
+ printf ("**** leaked ****\n");
+ leaked = true;
+ LeakFile (tree);
+ if (leaktest)
+ {
+ printf ("--- MAP LEAKED ---\n");
+ exit (0);
+ }
+ }
+
+ MarkVisibleSides (tree, brush_start, brush_end);
+ if (noopt || leaked)
+ break;
+ if (!optimize)
+ {
+ FreeTree (tree);
+ }
+ }
+
+ FloodAreas (tree);
+ if (glview)
+ WriteGLView (tree, source);
+ MakeFaces (tree->headnode);
+ FixTjuncs (tree->headnode);
+
+ if (!noprune)
+ PruneNodes (tree->headnode);
+
+ WriteBSP (tree->headnode);
+
+ if (!leaked)
+ WritePortalFile (tree);
+
+ FreeTree (tree);
+}
+
+/*
+============
+ProcessSubModel
+
+============
+*/
+void ProcessSubModel (void)
+{
+ entity_t *e;
+ int start, end;
+ tree_t *tree;
+ bspbrush_t *list;
+ vec3_t mins, maxs;
+
+ e = &entities[entity_num];
+
+ start = e->firstbrush;
+ end = start + e->numbrushes;
+
+ mins[0] = mins[1] = mins[2] = -4096;
+ maxs[0] = maxs[1] = maxs[2] = 4096;
+ list = MakeBspBrushList (start, end, mins, maxs);
+ if (!nocsg)
+ list = ChopBrushes (list);
+ tree = BrushBSP (list, mins, maxs);
+ MakeTreePortals (tree);
+ MarkVisibleSides (tree, start, end);
+ MakeFaces (tree->headnode);
+ FixTjuncs (tree->headnode);
+ WriteBSP (tree->headnode);
+ FreeTree (tree);
+}
+
+/*
+============
+ProcessModels
+============
+*/
+void ProcessModels (void)
+{
+ BeginBSPFile ();
+
+ for (entity_num=0 ; entity_num< num_entities ; entity_num++)
+ {
+ if (!entities[entity_num].numbrushes)
+ continue;
+
+ qprintf ("############### model %i ###############\n", nummodels);
+ BeginModel ();
+ if (entity_num == 0)
+ ProcessWorldModel ();
+ else
+ ProcessSubModel ();
+ EndModel ();
+
+ if (!verboseentities)
+ verbose = false; // don't bother printing submodels
+ }
+
+ EndBSPFile ();
+}
+
+
+/*
+============
+main
+============
+*/
+int main (int argc, char **argv)
+{
+ int i;
+ double start, end;
+ char path[1024];
+
+ printf ("---- qbsp3 ----\n");
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-glview"))
+ {
+ glview = true;
+ }
+ else if (!strcmp(argv[i], "-v"))
+ {
+ printf ("verbose = true\n");
+ verbose = true;
+ }
+ else if (!strcmp(argv[i], "-draw"))
+ {
+ printf ("drawflag = true\n");
+ drawflag = true;
+ }
+ else if (!strcmp(argv[i], "-noweld"))
+ {
+ printf ("noweld = true\n");
+ noweld = true;
+ }
+ else if (!strcmp(argv[i], "-nocsg"))
+ {
+ printf ("nocsg = true\n");
+ nocsg = true;
+ }
+ else if (!strcmp(argv[i], "-noshare"))
+ {
+ printf ("noshare = true\n");
+ noshare = true;
+ }
+ else if (!strcmp(argv[i], "-notjunc"))
+ {
+ printf ("notjunc = true\n");
+ notjunc = true;
+ }
+ else if (!strcmp(argv[i], "-nowater"))
+ {
+ printf ("nowater = true\n");
+ nowater = true;
+ }
+ else if (!strcmp(argv[i], "-noopt"))
+ {
+ printf ("noopt = true\n");
+ noopt = true;
+ }
+ else if (!strcmp(argv[i], "-noprune"))
+ {
+ printf ("noprune = true\n");
+ noprune = true;
+ }
+ else if (!strcmp(argv[i], "-nofill"))
+ {
+ printf ("nofill = true\n");
+ nofill = true;
+ }
+ else if (!strcmp(argv[i], "-nomerge"))
+ {
+ printf ("nomerge = true\n");
+ nomerge = true;
+ }
+ else if (!strcmp(argv[i], "-nosubdiv"))
+ {
+ printf ("nosubdiv = true\n");
+ nosubdiv = true;
+ }
+ else if (!strcmp(argv[i], "-nodetail"))
+ {
+ printf ("nodetail = true\n");
+ nodetail = true;
+ }
+ else if (!strcmp(argv[i], "-fulldetail"))
+ {
+ printf ("fulldetail = true\n");
+ fulldetail = true;
+ }
+ else if (!strcmp(argv[i], "-onlyents"))
+ {
+ printf ("onlyents = true\n");
+ onlyents = true;
+ }
+ else if (!strcmp(argv[i], "-micro"))
+ {
+ microvolume = atof(argv[i+1]);
+ printf ("microvolume = %f\n", microvolume);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-leaktest"))
+ {
+ printf ("leaktest = true\n");
+ leaktest = true;
+ }
+ else if (!strcmp(argv[i], "-verboseentities"))
+ {
+ printf ("verboseentities = true\n");
+ verboseentities = true;
+ }
+ else if (!strcmp(argv[i], "-chop"))
+ {
+ subdivide_size = atof(argv[i+1]);
+ printf ("subdivide_size = %f\n", subdivide_size);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-block"))
+ {
+ block_xl = block_xh = atoi(argv[i+1]);
+ block_yl = block_yh = atoi(argv[i+2]);
+ printf ("block: %i,%i\n", block_xl, block_yl);
+ i+=2;
+ }
+ else if (!strcmp(argv[i], "-blocks"))
+ {
+ block_xl = atoi(argv[i+1]);
+ block_yl = atoi(argv[i+2]);
+ block_xh = atoi(argv[i+3]);
+ block_yh = atoi(argv[i+4]);
+ printf ("blocks: %i,%i to %i,%i\n",
+ block_xl, block_yl, block_xh, block_yh);
+ i+=4;
+ }
+ else if (!strcmp (argv[i],"-tmpout"))
+ {
+ strcpy (outbase, "/tmp");
+ }
+ else if (argv[i][0] == '-')
+ Error ("Unknown option \"%s\"", argv[i]);
+ else
+ break;
+ }
+
+ if (i != argc - 1)
+ Error ("usage: qbsp3 [options] mapfile");
+
+ start = I_FloatTime ();
+
+ ThreadSetDefault ();
+numthreads = 1; // multiple threads aren't helping...
+ SetQdirFromPath (argv[i]);
+
+ strcpy (source, ExpandArg (argv[i]));
+ StripExtension (source);
+
+ // delete portal and line files
+ sprintf (path, "%s.prt", source);
+ remove (path);
+ sprintf (path, "%s.lin", source);
+ remove (path);
+
+ strcpy (name, ExpandArg (argv[i]));
+ DefaultExtension (name, ".map"); // might be .reg
+
+ //
+ // if onlyents, just grab the entites and resave
+ //
+ if (onlyents)
+ {
+ char out[1024];
+
+ sprintf (out, "%s.bsp", source);
+ LoadBSPFile (out);
+ num_entities = 0;
+
+ LoadMapFile (name);
+ SetModelNumbers ();
+ SetLightStyles ();
+
+ UnparseEntities ();
+
+ WriteBSPFile (out);
+ }
+ else
+ {
+ //
+ // start from scratch
+ //
+ LoadMapFile (name);
+ SetModelNumbers ();
+ SetLightStyles ();
+
+ ProcessModels ();
+ }
+
+ end = I_FloatTime ();
+ printf ("%5.0f seconds elapsed\n", end-start);
+
+ return 0;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+int nummiptex;
+textureref_t textureref[MAX_MAP_TEXTURES];
+
+//==========================================================================
+
+
+int FindMiptex (char *name)
+{
+ int i;
+ char path[1024];
+ miptex_t *mt;
+
+ for (i=0 ; i<nummiptex ; i++)
+ if (!strcmp (name, textureref[i].name))
+ {
+ return i;
+ }
+ if (nummiptex == MAX_MAP_TEXTURES)
+ Error ("MAX_MAP_TEXTURES");
+ strcpy (textureref[i].name, name);
+
+ // load the miptex to get the flags and values
+ sprintf (path, "%stextures/%s.wal", gamedir, name);
+ if (TryLoadFile (path, (void **)&mt) != -1)
+ {
+ textureref[i].value = LittleLong (mt->value);
+ textureref[i].flags = LittleLong (mt->flags);
+ textureref[i].contents = LittleLong (mt->contents);
+ strcpy (textureref[i].animname, mt->animname);
+ free (mt);
+ }
+ nummiptex++;
+
+ if (textureref[i].animname[0])
+ FindMiptex (textureref[i].animname);
+
+ return i;
+}
+
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+vec3_t baseaxis[18] =
+{
+{0,0,1}, {1,0,0}, {0,-1,0}, // floor
+{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
+{1,0,0}, {0,1,0}, {0,0,-1}, // west wall
+{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
+{0,1,0}, {1,0,0}, {0,0,-1}, // south wall
+{0,-1,0}, {1,0,0}, {0,0,-1} // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
+{
+ int bestaxis;
+ vec_t dot,best;
+ int i;
+
+ best = 0;
+ bestaxis = 0;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ dot = DotProduct (pln->normal, baseaxis[i*3]);
+ if (dot > best)
+ {
+ best = dot;
+ bestaxis = i;
+ }
+ }
+
+ VectorCopy (baseaxis[bestaxis*3+1], xv);
+ VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+
+
+int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, vec3_t origin)
+{
+ vec3_t vecs[2];
+ int sv, tv;
+ vec_t ang, sinv, cosv;
+ vec_t ns, nt;
+ texinfo_t tx, *tc;
+ int i, j, k;
+ float shift[2];
+ brush_texture_t anim;
+ int mt;
+
+ if (!bt->name[0])
+ return 0;
+
+ memset (&tx, 0, sizeof(tx));
+ strcpy (tx.texture, bt->name);
+
+ TextureAxisFromPlane(plane, vecs[0], vecs[1]);
+
+ shift[0] = DotProduct (origin, vecs[0]);
+ shift[1] = DotProduct (origin, vecs[1]);
+
+ if (!bt->scale[0])
+ bt->scale[0] = 1;
+ if (!bt->scale[1])
+ bt->scale[1] = 1;
+
+
+// rotate axis
+ if (bt->rotate == 0)
+ { sinv = 0 ; cosv = 1; }
+ else if (bt->rotate == 90)
+ { sinv = 1 ; cosv = 0; }
+ else if (bt->rotate == 180)
+ { sinv = 0 ; cosv = -1; }
+ else if (bt->rotate == 270)
+ { sinv = -1 ; cosv = 0; }
+ else
+ {
+ ang = bt->rotate / 180 * Q_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+ }
+
+ if (vecs[0][0])
+ sv = 0;
+ else if (vecs[0][1])
+ sv = 1;
+ else
+ sv = 2;
+
+ if (vecs[1][0])
+ tv = 0;
+ else if (vecs[1][1])
+ tv = 1;
+ else
+ tv = 2;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
+ nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
+ vecs[i][sv] = ns;
+ vecs[i][tv] = nt;
+ }
+
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ tx.vecs[i][j] = vecs[i][j] / bt->scale[i];
+
+ tx.vecs[0][3] = bt->shift[0] + shift[0];
+ tx.vecs[1][3] = bt->shift[1] + shift[1];
+ tx.flags = bt->flags;
+ tx.value = bt->value;
+
+ //
+ // find the texinfo
+ //
+ tc = texinfo;
+ for (i=0 ; i<numtexinfo ; i++, tc++)
+ {
+ if (tc->flags != tx.flags)
+ continue;
+ if (tc->value != tx.value)
+ continue;
+ for (j=0 ; j<2 ; j++)
+ {
+ if (strcmp (tc->texture, tx.texture))
+ goto skip;
+ for (k=0 ; k<4 ; k++)
+ {
+ if (tc->vecs[j][k] != tx.vecs[j][k])
+ goto skip;
+ }
+ }
+ return i;
+skip:;
+ }
+ *tc = tx;
+ numtexinfo++;
+
+ // load the next animation
+ mt = FindMiptex (bt->name);
+ if (textureref[mt].animname[0])
+ {
+ anim = *bt;
+ strcpy (anim.name, textureref[mt].animname);
+ tc->nexttexinfo = TexinfoForBrushTexture (plane, &anim, origin);
+ }
+ else
+ tc->nexttexinfo = -1;
+
+
+ return i;
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include "qbsp.h"
+
+extern int c_nodes;
+
+void RemovePortalFromNode (portal_t *portal, node_t *l);
+
+node_t *NodeForPoint (node_t *node, vec3_t origin)
+{
+ plane_t *plane;
+ vec_t d;
+
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &mapplanes[node->planenum];
+ d = DotProduct (origin, plane->normal) - plane->dist;
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ return node;
+}
+
+
+
+/*
+=============
+FreeTreePortals_r
+=============
+*/
+void FreeTreePortals_r (node_t *node)
+{
+ portal_t *p, *nextp;
+ int s;
+
+ // free children
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FreeTreePortals_r (node->children[0]);
+ FreeTreePortals_r (node->children[1]);
+ }
+
+ // free portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ s = (p->nodes[1] == node);
+ nextp = p->next[s];
+
+ RemovePortalFromNode (p, p->nodes[!s]);
+ FreePortal (p);
+ }
+ node->portals = NULL;
+}
+
+/*
+=============
+FreeTree_r
+=============
+*/
+void FreeTree_r (node_t *node)
+{
+ face_t *f, *nextf;
+
+ // free children
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FreeTree_r (node->children[0]);
+ FreeTree_r (node->children[1]);
+ }
+
+ // free bspbrushes
+ FreeBrushList (node->brushlist);
+
+ // free faces
+ for (f=node->faces ; f ; f=nextf)
+ {
+ nextf = f->next;
+ FreeFace (f);
+ }
+
+ // free the node
+ if (node->volume)
+ FreeBrush (node->volume);
+
+ if (numthreads == 1)
+ c_nodes--;
+ free (node);
+}
+
+
+/*
+=============
+FreeTree
+=============
+*/
+void FreeTree (tree_t *tree)
+{
+ FreeTreePortals_r (tree->headnode);
+ FreeTree_r (tree->headnode);
+ free (tree);
+}
+
+//===============================================================
+
+void PrintTree_r (node_t *node, int depth)
+{
+ int i;
+ plane_t *plane;
+ bspbrush_t *bb;
+
+ for (i=0 ; i<depth ; i++)
+ printf (" ");
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ if (!node->brushlist)
+ printf ("NULL\n");
+ else
+ {
+ for (bb=node->brushlist ; bb ; bb=bb->next)
+ printf ("%i ", bb->original->brushnum);
+ printf ("\n");
+ }
+ return;
+ }
+
+ plane = &mapplanes[node->planenum];
+ printf ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
+ plane->normal[0], plane->normal[1], plane->normal[2],
+ plane->dist);
+ PrintTree_r (node->children[0], depth+1);
+ PrintTree_r (node->children[1], depth+1);
+}
+
+/*
+=========================================================
+
+NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
+
+=========================================================
+*/
+
+int c_pruned;
+
+/*
+============
+PruneNodes_r
+============
+*/
+void PruneNodes_r (node_t *node)
+{
+ bspbrush_t *b, *next;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+ PruneNodes_r (node->children[0]);
+ PruneNodes_r (node->children[1]);
+
+ if ( (node->children[0]->contents & CONTENTS_SOLID)
+ && (node->children[1]->contents & CONTENTS_SOLID) )
+ {
+ if (node->faces)
+ Error ("node->faces seperating CONTENTS_SOLID");
+ if (node->children[0]->faces || node->children[1]->faces)
+ Error ("!node->faces with children");
+
+ // FIXME: free stuff
+ node->planenum = PLANENUM_LEAF;
+ node->contents = CONTENTS_SOLID;
+ node->detail_seperator = false;
+
+ if (node->brushlist)
+ Error ("PruneNodes: node->brushlist");
+
+ // combine brush lists
+ node->brushlist = node->children[1]->brushlist;
+
+ for (b=node->children[0]->brushlist ; b ; b=next)
+ {
+ next = b->next;
+ b->next = node->brushlist;
+ node->brushlist = b;
+ }
+
+ c_pruned++;
+ }
+}
+
+
+void PruneNodes (node_t *node)
+{
+ qprintf ("--- PruneNodes ---\n");
+ c_pruned = 0;
+ PruneNodes_r (node);
+ qprintf ("%5i pruned nodes\n", c_pruned);
+}
+
+//===========================================================
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include "qbsp.h"
+
+int c_nofaces;
+int c_facenodes;
+
+
+/*
+=========================================================
+
+ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
+
+=========================================================
+*/
+
+int planeused[MAX_MAP_PLANES];
+
+/*
+============
+EmitPlanes
+
+There is no oportunity to discard planes, because all of the original
+brushes will be saved in the map.
+============
+*/
+void EmitPlanes (void)
+{
+ int i;
+ dplane_t *dp;
+ plane_t *mp;
+ int planetranslate[MAX_MAP_PLANES];
+
+ mp = mapplanes;
+ for (i=0 ; i<nummapplanes ; i++, mp++)
+ {
+ dp = &dplanes[numplanes];
+ planetranslate[i] = numplanes;
+ VectorCopy ( mp->normal, dp->normal);
+ dp->dist = mp->dist;
+ dp->type = mp->type;
+ numplanes++;
+ }
+}
+
+
+//========================================================
+
+void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
+{
+ int i;
+ int facenum;
+
+ while (f->merged)
+ f = f->merged;
+
+ if (f->split[0])
+ {
+ EmitMarkFace (leaf_p, f->split[0]);
+ EmitMarkFace (leaf_p, f->split[1]);
+ return;
+ }
+
+ facenum = f->outputnumber;
+ if (facenum == -1)
+ return; // degenerate face
+
+ if (facenum < 0 || facenum >= numfaces)
+ Error ("Bad leafface");
+ for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
+ if (dleaffaces[i] == facenum)
+ break; // merged out face
+ if (i == numleaffaces)
+ {
+ if (numleaffaces >= MAX_MAP_LEAFFACES)
+ Error ("MAX_MAP_LEAFFACES");
+
+ dleaffaces[numleaffaces] = facenum;
+ numleaffaces++;
+ }
+
+}
+
+
+/*
+==================
+EmitLeaf
+==================
+*/
+void EmitLeaf (node_t *node)
+{
+ dleaf_t *leaf_p;
+ portal_t *p;
+ int s;
+ face_t *f;
+ bspbrush_t *b;
+ int i;
+ int brushnum;
+
+ // emit a leaf
+ if (numleafs >= MAX_MAP_LEAFS)
+ Error ("MAX_MAP_LEAFS");
+
+ leaf_p = &dleafs[numleafs];
+ numleafs++;
+
+ leaf_p->contents = node->contents;
+ leaf_p->cluster = node->cluster;
+ leaf_p->area = node->area;
+
+ //
+ // write bounding box info
+ //
+ VectorCopy (node->mins, leaf_p->mins);
+ VectorCopy (node->maxs, leaf_p->maxs);
+
+ //
+ // write the leafbrushes
+ //
+ leaf_p->firstleafbrush = numleafbrushes;
+ for (b=node->brushlist ; b ; b=b->next)
+ {
+ if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
+ Error ("MAX_MAP_LEAFBRUSHES");
+
+ brushnum = b->original - mapbrushes;
+ for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
+ if (dleafbrushes[i] == brushnum)
+ break;
+ if (i == numleafbrushes)
+ {
+ dleafbrushes[numleafbrushes] = brushnum;
+ numleafbrushes++;
+ }
+ }
+ leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
+
+ //
+ // write the leaffaces
+ //
+ if (leaf_p->contents & CONTENTS_SOLID)
+ return; // no leaffaces in solids
+
+ leaf_p->firstleafface = numleaffaces;
+
+ for (p = node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ f = p->face[s];
+ if (!f)
+ continue; // not a visible portal
+
+ EmitMarkFace (leaf_p, f);
+ }
+
+ leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
+}
+
+
+/*
+==================
+EmitFace
+==================
+*/
+void EmitFace (face_t *f)
+{
+ dface_t *df;
+ int i;
+ int e;
+
+ f->outputnumber = -1;
+
+ if (f->numpoints < 3)
+ {
+ return; // degenerated
+ }
+ if (f->merged || f->split[0] || f->split[1])
+ {
+ return; // not a final face
+ }
+
+ // save output number so leaffaces can use
+ f->outputnumber = numfaces;
+
+ if (numfaces >= MAX_MAP_FACES)
+ Error ("numfaces == MAX_MAP_FACES");
+ df = &dfaces[numfaces];
+ numfaces++;
+
+ // planenum is used by qlight, but not quake
+ df->planenum = f->planenum & (~1);
+ df->side = f->planenum & 1;
+
+ df->firstedge = numsurfedges;
+ df->numedges = f->numpoints;
+ df->texinfo = f->texinfo;
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+// e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
+ e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
+ if (numsurfedges >= MAX_MAP_SURFEDGES)
+ Error ("numsurfedges == MAX_MAP_SURFEDGES");
+ dsurfedges[numsurfedges] = e;
+ numsurfedges++;
+ }
+}
+
+/*
+============
+EmitDrawingNode_r
+============
+*/
+int EmitDrawNode_r (node_t *node)
+{
+ dnode_t *n;
+ face_t *f;
+ int i;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ EmitLeaf (node);
+ return -numleafs;
+ }
+
+ // emit a node
+ if (numnodes == MAX_MAP_NODES)
+ Error ("MAX_MAP_NODES");
+ n = &dnodes[numnodes];
+ numnodes++;
+
+ VectorCopy (node->mins, n->mins);
+ VectorCopy (node->maxs, n->maxs);
+
+ planeused[node->planenum]++;
+ planeused[node->planenum^1]++;
+
+ if (node->planenum & 1)
+ Error ("WriteDrawNodes_r: odd planenum");
+ n->planenum = node->planenum;
+ n->firstface = numfaces;
+
+ if (!node->faces)
+ c_nofaces++;
+ else
+ c_facenodes++;
+
+ for (f=node->faces ; f ; f=f->next)
+ EmitFace (f);
+
+ n->numfaces = numfaces - n->firstface;
+
+
+ //
+ // recursively output the other nodes
+ //
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i]->planenum == PLANENUM_LEAF)
+ {
+ n->children[i] = -(numleafs + 1);
+ EmitLeaf (node->children[i]);
+ }
+ else
+ {
+ n->children[i] = numnodes;
+ EmitDrawNode_r (node->children[i]);
+ }
+ }
+
+ return n - dnodes;
+}
+
+//=========================================================
+
+
+/*
+============
+WriteBSP
+============
+*/
+void WriteBSP (node_t *headnode)
+{
+ int oldfaces;
+
+ c_nofaces = 0;
+ c_facenodes = 0;
+
+ qprintf ("--- WriteBSP ---\n");
+
+ oldfaces = numfaces;
+ dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
+ EmitAreaPortals (headnode);
+
+ qprintf ("%5i nodes with faces\n", c_facenodes);
+ qprintf ("%5i nodes without faces\n", c_nofaces);
+ qprintf ("%5i faces\n", numfaces-oldfaces);
+}
+
+//===========================================================
+
+/*
+============
+SetModelNumbers
+============
+*/
+void SetModelNumbers (void)
+{
+ int i;
+ int models;
+ char value[10];
+
+ models = 1;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ if (entities[i].numbrushes)
+ {
+ sprintf (value, "*%i", models);
+ models++;
+ SetKeyValue (&entities[i], "model", value);
+ }
+ }
+
+}
+
+/*
+============
+SetLightStyles
+============
+*/
+#define MAX_SWITCHED_LIGHTS 32
+void SetLightStyles (void)
+{
+ int stylenum;
+ char *t;
+ entity_t *e;
+ int i, j;
+ char value[10];
+ char lighttargets[MAX_SWITCHED_LIGHTS][64];
+
+
+ // any light that is controlled (has a targetname)
+ // must have a unique style number generated for it
+
+ stylenum = 0;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ e = &entities[i];
+
+ t = ValueForKey (e, "classname");
+ if (Q_strncasecmp (t, "light", 5))
+ continue;
+ t = ValueForKey (e, "targetname");
+ if (!t[0])
+ continue;
+
+ // find this targetname
+ for (j=0 ; j<stylenum ; j++)
+ if (!strcmp (lighttargets[j], t))
+ break;
+ if (j == stylenum)
+ {
+ if (stylenum == MAX_SWITCHED_LIGHTS)
+ Error ("stylenum == MAX_SWITCHED_LIGHTS");
+ strcpy (lighttargets[j], t);
+ stylenum++;
+ }
+ sprintf (value, "%i", 32 + j);
+ SetKeyValue (e, "style", value);
+ }
+
+}
+
+//===========================================================
+
+/*
+============
+EmitBrushes
+============
+*/
+void EmitBrushes (void)
+{
+ int i, j, bnum, s, x;
+ dbrush_t *db;
+ mapbrush_t *b;
+ dbrushside_t *cp;
+ vec3_t normal;
+ vec_t dist;
+ int planenum;
+
+ numbrushsides = 0;
+ numbrushes = nummapbrushes;
+
+ for (bnum=0 ; bnum<nummapbrushes ; bnum++)
+ {
+ b = &mapbrushes[bnum];
+ db = &dbrushes[bnum];
+
+ db->contents = b->contents;
+ db->firstside = numbrushsides;
+ db->numsides = b->numsides;
+ for (j=0 ; j<b->numsides ; j++)
+ {
+ if (numbrushsides == MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+ cp = &dbrushsides[numbrushsides];
+ numbrushsides++;
+ cp->planenum = b->original_sides[j].planenum;
+ cp->texinfo = b->original_sides[j].texinfo;
+ }
+
+ // add any axis planes not contained in the brush to bevel off corners
+ for (x=0 ; x<3 ; x++)
+ for (s=-1 ; s<=1 ; s+=2)
+ {
+ // add the plane
+ VectorCopy (vec3_origin, normal);
+ normal[x] = s;
+ if (s == -1)
+ dist = -b->mins[x];
+ else
+ dist = b->maxs[x];
+ planenum = FindFloatPlane (normal, dist);
+ for (i=0 ; i<b->numsides ; i++)
+ if (b->original_sides[i].planenum == planenum)
+ break;
+ if (i == b->numsides)
+ {
+ if (numbrushsides >= MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+
+ dbrushsides[numbrushsides].planenum = planenum;
+ dbrushsides[numbrushsides].texinfo =
+ dbrushsides[numbrushsides-1].texinfo;
+ numbrushsides++;
+ db->numsides++;
+ }
+ }
+
+ }
+
+}
+
+//===========================================================
+
+/*
+==================
+BeginBSPFile
+==================
+*/
+void BeginBSPFile (void)
+{
+ // these values may actually be initialized
+ // if the file existed when loaded, so clear them explicitly
+ nummodels = 0;
+ numfaces = 0;
+ numnodes = 0;
+ numbrushsides = 0;
+ numvertexes = 0;
+ numleaffaces = 0;
+ numleafbrushes = 0;
+ numsurfedges = 0;
+
+ // edge 0 is not used, because 0 can't be negated
+ numedges = 1;
+
+ // leave vertex 0 as an error
+ numvertexes = 1;
+
+ // leave leaf 0 as an error
+ numleafs = 1;
+ dleafs[0].contents = CONTENTS_SOLID;
+}
+
+
+/*
+============
+EndBSPFile
+============
+*/
+void EndBSPFile (void)
+{
+ char path[1024];
+ int len;
+ byte *buf;
+
+
+ EmitBrushes ();
+ EmitPlanes ();
+ UnparseEntities ();
+
+ // load the pop
+#if 0
+ sprintf (path, "%s/pics/pop.lmp", gamedir);
+ len = LoadFile (path, &buf);
+ memcpy (dpop, buf, sizeof(dpop));
+ free (buf);
+#endif
+
+ // write the map
+ sprintf (path, "%s.bsp", source);
+ printf ("Writing %s\n", path);
+ WriteBSPFile (path);
+}
+
+
+/*
+==================
+BeginModel
+==================
+*/
+int firstmodleaf;
+extern int firstmodeledge;
+extern int firstmodelface;
+void BeginModel (void)
+{
+ dmodel_t *mod;
+ int start, end;
+ mapbrush_t *b;
+ int j;
+ entity_t *e;
+ vec3_t mins, maxs;
+
+ if (nummodels == MAX_MAP_MODELS)
+ Error ("MAX_MAP_MODELS");
+ mod = &dmodels[nummodels];
+
+ mod->firstface = numfaces;
+
+ firstmodleaf = numleafs;
+ firstmodeledge = numedges;
+ firstmodelface = numfaces;
+
+ //
+ // bound the brushes
+ //
+ e = &entities[entity_num];
+
+ start = e->firstbrush;
+ end = start + e->numbrushes;
+ ClearBounds (mins, maxs);
+
+ for (j=start ; j<end ; j++)
+ {
+ b = &mapbrushes[j];
+ if (!b->numsides)
+ continue; // not a real brush (origin brush)
+ AddPointToBounds (b->mins, mins, maxs);
+ AddPointToBounds (b->maxs, mins, maxs);
+ }
+
+ VectorCopy (mins, mod->mins);
+ VectorCopy (maxs, mod->maxs);
+}
+
+
+/*
+==================
+EndModel
+==================
+*/
+void EndModel (void)
+{
+ dmodel_t *mod;
+
+ mod = &dmodels[nummodels];
+
+ mod->numfaces = numfaces - mod->firstface;
+
+ nummodels++;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include "qrad.h"
+
+#define MAX_LSTYLES 256
+
+typedef struct
+{
+ dface_t *faces[2];
+ qboolean coplanar;
+} edgeshare_t;
+
+edgeshare_t edgeshare[MAX_MAP_EDGES];
+
+int facelinks[MAX_MAP_FACES];
+int planelinks[2][MAX_MAP_PLANES];
+
+/*
+============
+LinkPlaneFaces
+============
+*/
+void LinkPlaneFaces (void)
+{
+ int i;
+ dface_t *f;
+
+ f = dfaces;
+ for (i=0 ; i<numfaces ; i++, f++)
+ {
+ facelinks[i] = planelinks[f->side][f->planenum];
+ planelinks[f->side][f->planenum] = i;
+ }
+}
+
+/*
+============
+PairEdges
+============
+*/
+void PairEdges (void)
+{
+ int i, j, k;
+ dface_t *f;
+ edgeshare_t *e;
+
+ f = dfaces;
+ for (i=0 ; i<numfaces ; i++, f++)
+ {
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ k = dsurfedges[f->firstedge + j];
+ if (k < 0)
+ {
+ e = &edgeshare[-k];
+ e->faces[1] = f;
+ }
+ else
+ {
+ e = &edgeshare[k];
+ e->faces[0] = f;
+ }
+
+ if (e->faces[0] && e->faces[1])
+ {
+ // determine if coplanar
+ if (e->faces[0]->planenum == e->faces[1]->planenum)
+ e->coplanar = true;
+ }
+ }
+ }
+}
+
+/*
+=================================================================
+
+ POINT TRIANGULATION
+
+=================================================================
+*/
+
+typedef struct triedge_s
+{
+ int p0, p1;
+ vec3_t normal;
+ vec_t dist;
+ struct triangle_s *tri;
+} triedge_t;
+
+typedef struct triangle_s
+{
+ triedge_t *edges[3];
+} triangle_t;
+
+#define MAX_TRI_POINTS 1024
+#define MAX_TRI_EDGES (MAX_TRI_POINTS*6)
+#define MAX_TRI_TRIS (MAX_TRI_POINTS*2)
+
+typedef struct
+{
+ int numpoints;
+ int numedges;
+ int numtris;
+ dplane_t *plane;
+ triedge_t *edgematrix[MAX_TRI_POINTS][MAX_TRI_POINTS];
+ patch_t *points[MAX_TRI_POINTS];
+ triedge_t edges[MAX_TRI_EDGES];
+ triangle_t tris[MAX_TRI_TRIS];
+} triangulation_t;
+
+/*
+===============
+AllocTriangulation
+===============
+*/
+triangulation_t *AllocTriangulation (dplane_t *plane)
+{
+ triangulation_t *t;
+
+ t = malloc(sizeof(triangulation_t));
+ t->numpoints = 0;
+ t->numedges = 0;
+ t->numtris = 0;
+
+ t->plane = plane;
+
+// memset (t->edgematrix, 0, sizeof(t->edgematrix));
+
+ return t;
+}
+
+/*
+===============
+FreeTriangulation
+===============
+*/
+void FreeTriangulation (triangulation_t *tr)
+{
+ free (tr);
+}
+
+
+triedge_t *FindEdge (triangulation_t *trian, int p0, int p1)
+{
+ triedge_t *e, *be;
+ vec3_t v1;
+ vec3_t normal;
+ vec_t dist;
+
+ if (trian->edgematrix[p0][p1])
+ return trian->edgematrix[p0][p1];
+
+ if (trian->numedges > MAX_TRI_EDGES-2)
+ Error ("trian->numedges > MAX_TRI_EDGES-2");
+
+ VectorSubtract (trian->points[p1]->origin, trian->points[p0]->origin, v1);
+ VectorNormalize (v1, v1);
+ CrossProduct (v1, trian->plane->normal, normal);
+ dist = DotProduct (trian->points[p0]->origin, normal);
+
+ e = &trian->edges[trian->numedges];
+ e->p0 = p0;
+ e->p1 = p1;
+ e->tri = NULL;
+ VectorCopy (normal, e->normal);
+ e->dist = dist;
+ trian->numedges++;
+ trian->edgematrix[p0][p1] = e;
+
+ be = &trian->edges[trian->numedges];
+ be->p0 = p1;
+ be->p1 = p0;
+ be->tri = NULL;
+ VectorSubtract (vec3_origin, normal, be->normal);
+ be->dist = -dist;
+ trian->numedges++;
+ trian->edgematrix[p1][p0] = be;
+
+ return e;
+}
+
+triangle_t *AllocTriangle (triangulation_t *trian)
+{
+ triangle_t *t;
+
+ if (trian->numtris >= MAX_TRI_TRIS)
+ Error ("trian->numtris >= MAX_TRI_TRIS");
+
+ t = &trian->tris[trian->numtris];
+ trian->numtris++;
+
+ return t;
+}
+
+/*
+============
+TriEdge_r
+============
+*/
+void TriEdge_r (triangulation_t *trian, triedge_t *e)
+{
+ int i, bestp;
+ vec3_t v1, v2;
+ vec_t *p0, *p1, *p;
+ vec_t best, ang;
+ triangle_t *nt;
+
+ if (e->tri)
+ return; // allready connected by someone
+
+ // find the point with the best angle
+ p0 = trian->points[e->p0]->origin;
+ p1 = trian->points[e->p1]->origin;
+ best = 1.1;
+ for (i=0 ; i< trian->numpoints ; i++)
+ {
+ p = trian->points[i]->origin;
+ // a 0 dist will form a degenerate triangle
+ if (DotProduct(p, e->normal) - e->dist < 0)
+ continue; // behind edge
+ VectorSubtract (p0, p, v1);
+ VectorSubtract (p1, p, v2);
+ if (!VectorNormalize (v1,v1))
+ continue;
+ if (!VectorNormalize (v2,v2))
+ continue;
+ ang = DotProduct (v1, v2);
+ if (ang < best)
+ {
+ best = ang;
+ bestp = i;
+ }
+ }
+ if (best >= 1)
+ return; // edge doesn't match anything
+
+ // make a new triangle
+ nt = AllocTriangle (trian);
+ nt->edges[0] = e;
+ nt->edges[1] = FindEdge (trian, e->p1, bestp);
+ nt->edges[2] = FindEdge (trian, bestp, e->p0);
+ for (i=0 ; i<3 ; i++)
+ nt->edges[i]->tri = nt;
+ TriEdge_r (trian, FindEdge (trian, bestp, e->p1));
+ TriEdge_r (trian, FindEdge (trian, e->p0, bestp));
+}
+
+/*
+============
+TriangulatePoints
+============
+*/
+void TriangulatePoints (triangulation_t *trian)
+{
+ vec_t d, bestd;
+ vec3_t v1;
+ int bp1, bp2, i, j;
+ vec_t *p1, *p2;
+ triedge_t *e, *e2;
+
+ if (trian->numpoints < 2)
+ return;
+
+ // find the two closest points
+ bestd = 9999;
+ for (i=0 ; i<trian->numpoints ; i++)
+ {
+ p1 = trian->points[i]->origin;
+ for (j=i+1 ; j<trian->numpoints ; j++)
+ {
+ p2 = trian->points[j]->origin;
+ VectorSubtract (p2, p1, v1);
+ d = VectorLength (v1);
+ if (d < bestd)
+ {
+ bestd = d;
+ bp1 = i;
+ bp2 = j;
+ }
+ }
+ }
+
+ e = FindEdge (trian, bp1, bp2);
+ e2 = FindEdge (trian, bp2, bp1);
+ TriEdge_r (trian, e);
+ TriEdge_r (trian, e2);
+}
+
+/*
+===============
+AddPointToTriangulation
+===============
+*/
+void AddPointToTriangulation (patch_t *patch, triangulation_t *trian)
+{
+ int pnum;
+
+ pnum = trian->numpoints;
+ if (pnum == MAX_TRI_POINTS)
+ Error ("trian->numpoints == MAX_TRI_POINTS");
+ trian->points[pnum] = patch;
+ trian->numpoints++;
+}
+
+/*
+===============
+LerpTriangle
+===============
+*/
+void LerpTriangle (triangulation_t *trian, triangle_t *t, vec3_t point, vec3_t color)
+{
+ patch_t *p1, *p2, *p3;
+ vec3_t base, d1, d2;
+ float x, y, x1, y1, x2, y2;
+
+ p1 = trian->points[t->edges[0]->p0];
+ p2 = trian->points[t->edges[1]->p0];
+ p3 = trian->points[t->edges[2]->p0];
+
+ VectorCopy (p1->totallight, base);
+ VectorSubtract (p2->totallight, base, d1);
+ VectorSubtract (p3->totallight, base, d2);
+
+ x = DotProduct (point, t->edges[0]->normal) - t->edges[0]->dist;
+ y = DotProduct (point, t->edges[2]->normal) - t->edges[2]->dist;
+
+ x1 = 0;
+ y1 = DotProduct (p2->origin, t->edges[2]->normal) - t->edges[2]->dist;
+
+ x2 = DotProduct (p3->origin, t->edges[0]->normal) - t->edges[0]->dist;
+ y2 = 0;
+
+ if (fabs(y1)<ON_EPSILON || fabs(x2)<ON_EPSILON)
+ {
+ VectorCopy (base, color);
+ return;
+ }
+
+ VectorMA (base, x/x2, d2, color);
+ VectorMA (color, y/y1, d1, color);
+}
+
+qboolean PointInTriangle (vec3_t point, triangle_t *t)
+{
+ int i;
+ triedge_t *e;
+ vec_t d;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ e = t->edges[i];
+ d = DotProduct (e->normal, point) - e->dist;
+ if (d < 0)
+ return false; // not inside
+ }
+
+ return true;
+}
+
+/*
+===============
+SampleTriangulation
+===============
+*/
+void SampleTriangulation (vec3_t point, triangulation_t *trian, vec3_t color)
+{
+ triangle_t *t;
+ triedge_t *e;
+ vec_t d, best;
+ patch_t *p0, *p1;
+ vec3_t v1, v2;
+ int i, j;
+
+ if (trian->numpoints == 0)
+ {
+ VectorClear (color);
+ return;
+ }
+ if (trian->numpoints == 1)
+ {
+ VectorCopy (trian->points[0]->totallight, color);
+ return;
+ }
+
+ // search for triangles
+ for (t = trian->tris, j=0 ; j < trian->numtris ; t++, j++)
+ {
+ if (!PointInTriangle (point, t))
+ continue;
+
+ // this is it
+ LerpTriangle (trian, t, point, color);
+ return;
+ }
+
+ // search for exterior edge
+ for (e=trian->edges, j=0 ; j< trian->numedges ; e++, j++)
+ {
+ if (e->tri)
+ continue; // not an exterior edge
+
+ d = DotProduct (point, e->normal) - e->dist;
+ if (d < 0)
+ continue; // not in front of edge
+
+ p0 = trian->points[e->p0];
+ p1 = trian->points[e->p1];
+
+ VectorSubtract (p1->origin, p0->origin, v1);
+ VectorNormalize (v1, v1);
+ VectorSubtract (point, p0->origin, v2);
+ d = DotProduct (v2, v1);
+ if (d < 0)
+ continue;
+ if (d > 1)
+ continue;
+ for (i=0 ; i<3 ; i++)
+ color[i] = p0->totallight[i] + d * (p1->totallight[i] - p0->totallight[i]);
+ return;
+ }
+
+ // search for nearest point
+ best = 99999;
+ p1 = NULL;
+ for (j=0 ; j<trian->numpoints ; j++)
+ {
+ p0 = trian->points[j];
+ VectorSubtract (point, p0->origin, v1);
+ d = VectorLength (v1);
+ if (d < best)
+ {
+ best = d;
+ p1 = p0;
+ }
+ }
+
+ if (!p1)
+ Error ("SampleTriangulation: no points");
+
+ VectorCopy (p1->totallight, color);
+}
+
+/*
+=================================================================
+
+ LIGHTMAP SAMPLE GENERATION
+
+=================================================================
+*/
+
+
+#define SINGLEMAP (64*64*4)
+
+typedef struct
+{
+ vec_t facedist;
+ vec3_t facenormal;
+
+ int numsurfpt;
+ vec3_t surfpt[SINGLEMAP];
+
+ vec3_t modelorg; // for origined bmodels
+
+ vec3_t texorg;
+ vec3_t worldtotex[2]; // s = (world - texorg) . worldtotex[0]
+ vec3_t textoworld[2]; // world = texorg + s * textoworld[0]
+
+ vec_t exactmins[2], exactmaxs[2];
+
+ int texmins[2], texsize[2];
+ int surfnum;
+ dface_t *face;
+} lightinfo_t;
+
+
+/*
+================
+CalcFaceExtents
+
+Fills in s->texmins[] and s->texsize[]
+also sets exactmins[] and exactmaxs[]
+================
+*/
+void CalcFaceExtents (lightinfo_t *l)
+{
+ dface_t *s;
+ vec_t mins[2], maxs[2], val;
+ int i,j, e;
+ dvertex_t *v;
+ texinfo_t *tex;
+ vec3_t vt;
+
+ s = l->face;
+
+ mins[0] = mins[1] = 999999;
+ maxs[0] = maxs[1] = -99999;
+
+ tex = &texinfo[s->texinfo];
+
+ for (i=0 ; i<s->numedges ; i++)
+ {
+ e = dsurfedges[s->firstedge+i];
+ if (e >= 0)
+ v = dvertexes + dedges[e].v[0];
+ else
+ v = dvertexes + dedges[-e].v[1];
+
+// VectorAdd (v->point, l->modelorg, vt);
+ VectorCopy (v->point, vt);
+
+ for (j=0 ; j<2 ; j++)
+ {
+ val = DotProduct (vt, tex->vecs[j]) + tex->vecs[j][3];
+ if (val < mins[j])
+ mins[j] = val;
+ if (val > maxs[j])
+ maxs[j] = val;
+ }
+ }
+
+ for (i=0 ; i<2 ; i++)
+ {
+ l->exactmins[i] = mins[i];
+ l->exactmaxs[i] = maxs[i];
+
+ mins[i] = floor(mins[i]/16);
+ maxs[i] = ceil(maxs[i]/16);
+
+ l->texmins[i] = mins[i];
+ l->texsize[i] = maxs[i] - mins[i];
+ if (l->texsize[0] * l->texsize[1] > SINGLEMAP/4) // div 4 for extrasamples
+ Error ("Surface to large to map");
+ }
+}
+
+/*
+================
+CalcFaceVectors
+
+Fills in texorg, worldtotex. and textoworld
+================
+*/
+void CalcFaceVectors (lightinfo_t *l)
+{
+ texinfo_t *tex;
+ int i, j;
+ vec3_t texnormal;
+ vec_t distscale;
+ vec_t dist, len;
+ int w, h;
+
+ tex = &texinfo[l->face->texinfo];
+
+// convert from float to double
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ l->worldtotex[i][j] = tex->vecs[i][j];
+
+// calculate a normal to the texture axis. points can be moved along this
+// without changing their S/T
+ texnormal[0] = tex->vecs[1][1]*tex->vecs[0][2]
+ - tex->vecs[1][2]*tex->vecs[0][1];
+ texnormal[1] = tex->vecs[1][2]*tex->vecs[0][0]
+ - tex->vecs[1][0]*tex->vecs[0][2];
+ texnormal[2] = tex->vecs[1][0]*tex->vecs[0][1]
+ - tex->vecs[1][1]*tex->vecs[0][0];
+ VectorNormalize (texnormal, texnormal);
+
+// flip it towards plane normal
+ distscale = DotProduct (texnormal, l->facenormal);
+ if (!distscale)
+ {
+ qprintf ("WARNING: Texture axis perpendicular to face\n");
+ distscale = 1;
+ }
+ if (distscale < 0)
+ {
+ distscale = -distscale;
+ VectorSubtract (vec3_origin, texnormal, texnormal);
+ }
+
+// distscale is the ratio of the distance along the texture normal to
+// the distance along the plane normal
+ distscale = 1/distscale;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ len = VectorLength (l->worldtotex[i]);
+ dist = DotProduct (l->worldtotex[i], l->facenormal);
+ dist *= distscale;
+ VectorMA (l->worldtotex[i], -dist, texnormal, l->textoworld[i]);
+ VectorScale (l->textoworld[i], (1/len)*(1/len), l->textoworld[i]);
+ }
+
+
+// calculate texorg on the texture plane
+ for (i=0 ; i<3 ; i++)
+ l->texorg[i] = -tex->vecs[0][3]* l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i];
+
+// project back to the face plane
+ dist = DotProduct (l->texorg, l->facenormal) - l->facedist - 1;
+ dist *= distscale;
+ VectorMA (l->texorg, -dist, texnormal, l->texorg);
+
+ // compensate for org'd bmodels
+ VectorAdd (l->texorg, l->modelorg, l->texorg);
+
+ // total sample count
+ h = l->texsize[1]+1;
+ w = l->texsize[0]+1;
+ l->numsurfpt = w * h;
+}
+
+/*
+=================
+CalcPoints
+
+For each texture aligned grid point, back project onto the plane
+to get the world xyz value of the sample point
+=================
+*/
+void CalcPoints (lightinfo_t *l, float sofs, float tofs)
+{
+ int i;
+ int s, t, j;
+ int w, h, step;
+ vec_t starts, startt, us, ut;
+ vec_t *surf;
+ vec_t mids, midt;
+ vec3_t facemid;
+ dleaf_t *leaf;
+
+ surf = l->surfpt[0];
+ mids = (l->exactmaxs[0] + l->exactmins[0])/2;
+ midt = (l->exactmaxs[1] + l->exactmins[1])/2;
+
+ for (j=0 ; j<3 ; j++)
+ facemid[j] = l->texorg[j] + l->textoworld[0][j]*mids + l->textoworld[1][j]*midt;
+
+ h = l->texsize[1]+1;
+ w = l->texsize[0]+1;
+ l->numsurfpt = w * h;
+
+ starts = l->texmins[0]*16;
+ startt = l->texmins[1]*16;
+ step = 16;
+
+
+ for (t=0 ; t<h ; t++)
+ {
+ for (s=0 ; s<w ; s++, surf+=3)
+ {
+ us = starts + (s+sofs)*step;
+ ut = startt + (t+tofs)*step;
+
+
+ // if a line can be traced from surf to facemid, the point is good
+ for (i=0 ; i<6 ; i++)
+ {
+ // calculate texture point
+ for (j=0 ; j<3 ; j++)
+ surf[j] = l->texorg[j] + l->textoworld[0][j]*us
+ + l->textoworld[1][j]*ut;
+
+ leaf = PointInLeaf (surf);
+ if (leaf->contents != CONTENTS_SOLID)
+ {
+ if (!TestLine_r (0, facemid, surf))
+ break; // got it
+ }
+
+ // nudge it
+ if (i & 1)
+ {
+ if (us > mids)
+ {
+ us -= 8;
+ if (us < mids)
+ us = mids;
+ }
+ else
+ {
+ us += 8;
+ if (us > mids)
+ us = mids;
+ }
+ }
+ else
+ {
+ if (ut > midt)
+ {
+ ut -= 8;
+ if (ut < midt)
+ ut = midt;
+ }
+ else
+ {
+ ut += 8;
+ if (ut > midt)
+ ut = midt;
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
+//==============================================================
+
+
+
+#define MAX_STYLES 32
+typedef struct
+{
+ int numsamples;
+ float *origins;
+ int numstyles;
+ int stylenums[MAX_STYLES];
+ float *samples[MAX_STYLES];
+} facelight_t;
+
+directlight_t *directlights[MAX_MAP_LEAFS];
+facelight_t facelight[MAX_MAP_FACES];
+int numdlights;
+
+/*
+==================
+FindTargetEntity
+==================
+*/
+entity_t *FindTargetEntity (char *target)
+{
+ int i;
+ char *n;
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ n = ValueForKey (&entities[i], "targetname");
+ if (!strcmp (n, target))
+ return &entities[i];
+ }
+
+ return NULL;
+}
+
+//#define DIRECT_LIGHT 3000
+#define DIRECT_LIGHT 3
+
+/*
+=============
+CreateDirectLights
+=============
+*/
+void CreateDirectLights (void)
+{
+ int i;
+ patch_t *p;
+ directlight_t *dl;
+ dleaf_t *leaf;
+ int cluster;
+ entity_t *e, *e2;
+ char *name;
+ char *target;
+ float angle;
+ vec3_t dest;
+ char *_color;
+ float intensity;
+
+ //
+ // surfaces
+ //
+ for (i=0, p=patches ; i<num_patches ; i++, p++)
+ {
+ if (p->totallight[0] < DIRECT_LIGHT
+ && p->totallight[1] < DIRECT_LIGHT
+ && p->totallight[2] < DIRECT_LIGHT)
+ continue;
+
+ numdlights++;
+ dl = malloc(sizeof(directlight_t));
+ memset (dl, 0, sizeof(*dl));
+
+ VectorCopy (p->origin, dl->origin);
+
+ leaf = PointInLeaf (dl->origin);
+ cluster = leaf->cluster;
+ dl->next = directlights[cluster];
+ directlights[cluster] = dl;
+
+ dl->type = emit_surface;
+ VectorCopy (p->plane->normal, dl->normal);
+
+ dl->intensity = ColorNormalize (p->totallight, dl->color);
+ dl->intensity *= p->area * direct_scale;
+ VectorClear (p->totallight); // all sent now
+ }
+
+ //
+ // entities
+ //
+ for (i=0 ; i<num_entities ; i++)
+ {
+ e = &entities[i];
+ name = ValueForKey (e, "classname");
+ if (strncmp (name, "light", 5))
+ continue;
+
+ numdlights++;
+ dl = malloc(sizeof(directlight_t));
+ memset (dl, 0, sizeof(*dl));
+
+ GetVectorForKey (e, "origin", dl->origin);
+ dl->style = FloatForKey (e, "_style");
+ if (!dl->style)
+ dl->style = FloatForKey (e, "style");
+ if (dl->style < 0 || dl->style >= MAX_LSTYLES)
+ dl->style = 0;
+
+ leaf = PointInLeaf (dl->origin);
+ cluster = leaf->cluster;
+
+ dl->next = directlights[cluster];
+ directlights[cluster] = dl;
+
+ intensity = FloatForKey (e, "light");
+ if (!intensity)
+ intensity = FloatForKey (e, "_light");
+ if (!intensity)
+ intensity = 300;
+ _color = ValueForKey (e, "_color");
+ if (_color && _color[1])
+ {
+ sscanf (_color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2]);
+ ColorNormalize (dl->color, dl->color);
+ }
+ else
+ dl->color[0] = dl->color[1] = dl->color[2] = 1.0;
+ dl->intensity = intensity*entity_scale;
+ dl->type = emit_point;
+
+ target = ValueForKey (e, "target");
+
+ if (!strcmp (name, "light_spot") || target[0])
+ {
+ dl->type = emit_spotlight;
+ dl->stopdot = FloatForKey (e, "_cone");
+ if (!dl->stopdot)
+ dl->stopdot = 10;
+ dl->stopdot = cos(dl->stopdot/180*3.14159);
+ if (target[0])
+ { // point towards target
+ e2 = FindTargetEntity (target);
+ if (!e2)
+ printf ("WARNING: light at (%i %i %i) has missing target\n",
+ (int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]);
+ else
+ {
+ GetVectorForKey (e2, "origin", dest);
+ VectorSubtract (dest, dl->origin, dl->normal);
+ VectorNormalize (dl->normal, dl->normal);
+ }
+ }
+ else
+ { // point down angle
+ angle = FloatForKey (e, "angle");
+ if (angle == ANGLE_UP)
+ {
+ dl->normal[0] = dl->normal[1] = 0;
+ dl->normal[2] = 1;
+ }
+ else if (angle == ANGLE_DOWN)
+ {
+ dl->normal[0] = dl->normal[1] = 0;
+ dl->normal[2] = -1;
+ }
+ else
+ {
+ dl->normal[2] = 0;
+ dl->normal[0] = cos (angle/180*3.14159);
+ dl->normal[1] = sin (angle/180*3.14159);
+ }
+ }
+ }
+ }
+
+ qprintf ("%i direct lights\n", numdlights);
+}
+
+/*
+=============
+GatherSampleLight
+
+Lightscale is the normalizer for multisampling
+=============
+*/
+void GatherSampleLight (vec3_t pos, vec3_t normal,
+ float **styletable, int offset, int mapsize, float lightscale)
+{
+ int i;
+ directlight_t *l;
+ byte pvs[(MAX_MAP_LEAFS+7)/8];
+ vec3_t delta;
+ float dot, dot2;
+ float dist;
+ float scale;
+ float *dest;
+
+ // get the PVS for the pos to limit the number of checks
+ if (!PvsForOrigin (pos, pvs))
+ {
+ return;
+ }
+
+ for (i = 0 ; i<dvis->numclusters ; i++)
+ {
+ if ( ! (pvs[ i>>3] & (1<<(i&7))) )
+ continue;
+
+ for (l=directlights[i] ; l ; l=l->next)
+ {
+ VectorSubtract (l->origin, pos, delta);
+ dist = VectorNormalize (delta, delta);
+ dot = DotProduct (delta, normal);
+ if (dot <= 0.001)
+ continue; // behind sample surface
+
+ switch (l->type)
+ {
+ case emit_point:
+ // linear falloff
+ scale = (l->intensity - dist) * dot;
+ break;
+
+ case emit_surface:
+ dot2 = -DotProduct (delta, l->normal);
+ if (dot2 <= 0.001)
+ goto skipadd; // behind light surface
+ scale = (l->intensity / (dist*dist) ) * dot * dot2;
+ break;
+
+ case emit_spotlight:
+ // linear falloff
+ dot2 = -DotProduct (delta, l->normal);
+ if (dot2 <= l->stopdot)
+ goto skipadd; // outside light cone
+ scale = (l->intensity - dist) * dot;
+ break;
+ default:
+ Error ("Bad l->type");
+ }
+
+ if (TestLine_r (0, pos, l->origin))
+ continue; // occluded
+
+ if (scale <= 0)
+ continue;
+
+ // if this style doesn't have a table yet, allocate one
+ if (!styletable[l->style])
+ {
+ styletable[l->style] = malloc (mapsize);
+ memset (styletable[l->style], 0, mapsize);
+ }
+
+ dest = styletable[l->style] + offset;
+ // add some light to it
+ VectorMA (dest, scale*lightscale, l->color, dest);
+
+skipadd: ;
+ }
+ }
+
+}
+
+/*
+=============
+AddSampleToPatch
+
+Take the sample's collected light and
+add it back into the apropriate patch
+for the radiosity pass.
+
+The sample is added to all patches that might include
+any part of it. They are counted and averaged, so it
+doesn't generate extra light.
+=============
+*/
+void AddSampleToPatch (vec3_t pos, vec3_t color, int facenum)
+{
+ patch_t *patch;
+ vec3_t mins, maxs;
+ int i;
+
+ if (numbounce == 0)
+ return;
+ if (color[0] + color[1] + color[2] < 3)
+ return;
+
+ for (patch = face_patches[facenum] ; patch ; patch=patch->next)
+ {
+ // see if the point is in this patch (roughly)
+ WindingBounds (patch->winding, mins, maxs);
+ for (i=0 ; i<3 ; i++)
+ {
+ if (mins[i] > pos[i] + 16)
+ goto nextpatch;
+ if (maxs[i] < pos[i] - 16)
+ goto nextpatch;
+ }
+
+ // add the sample to the patch
+ patch->samples++;
+ VectorAdd (patch->samplelight, color, patch->samplelight);
+nextpatch:;
+ }
+
+}
+
+
+/*
+=============
+BuildFacelights
+=============
+*/
+float sampleofs[5][2] =
+{ {0,0}, {-0.25, -0.25}, {0.25, -0.25}, {0.25, 0.25}, {-0.25, 0.25} };
+
+
+void BuildFacelights (int facenum)
+{
+ dface_t *f;
+ lightinfo_t l[5];
+ float *styletable[MAX_LSTYLES];
+ int i, j;
+ float *spot;
+ patch_t *patch;
+ int numsamples;
+ int tablesize;
+ facelight_t *fl;
+
+ f = &dfaces[facenum];
+
+ if ( texinfo[f->texinfo].flags & (SURF_WARP|SURF_SKY) )
+ return; // non-lit texture
+
+ memset (styletable,0, sizeof(styletable));
+
+ if (extrasamples)
+ numsamples = 5;
+ else
+ numsamples = 1;
+ for (i=0 ; i<numsamples ; i++)
+ {
+ memset (&l[i], 0, sizeof(l[i]));
+ l[i].surfnum = facenum;
+ l[i].face = f;
+ VectorCopy (dplanes[f->planenum].normal, l[i].facenormal);
+ l[i].facedist = dplanes[f->planenum].dist;
+ if (f->side)
+ {
+ VectorSubtract (vec3_origin, l[i].facenormal, l[i].facenormal);
+ l[i].facedist = -l[i].facedist;
+ }
+
+ // get the origin offset for rotating bmodels
+ VectorCopy (face_offset[facenum], l[i].modelorg);
+
+ CalcFaceVectors (&l[i]);
+ CalcFaceExtents (&l[i]);
+ CalcPoints (&l[i], sampleofs[i][0], sampleofs[i][1]);
+ }
+
+ tablesize = l[0].numsurfpt * sizeof(vec3_t);
+ styletable[0] = malloc(tablesize);
+ memset (styletable[0], 0, tablesize);
+
+ fl = &facelight[facenum];
+ fl->numsamples = l[0].numsurfpt;
+ fl->origins = malloc (tablesize);
+ memcpy (fl->origins, l[0].surfpt, tablesize);
+
+ for (i=0 ; i<l[0].numsurfpt ; i++)
+ {
+ for (j=0 ; j<numsamples ; j++)
+ {
+ GatherSampleLight (l[j].surfpt[i], l[0].facenormal, styletable,
+ i*3, tablesize, 1.0/numsamples);
+ }
+
+ // contribute the sample to one or more patches
+ AddSampleToPatch (l[0].surfpt[i], styletable[0]+i*3, facenum);
+ }
+
+ // average up the direct light on each patch for radiosity
+ for (patch = face_patches[facenum] ; patch ; patch=patch->next)
+ {
+ if (patch->samples)
+ {
+ VectorScale (patch->samplelight, 1.0/patch->samples, patch->samplelight);
+ }
+ else
+ {
+// printf ("patch with no samples\n");
+ }
+ }
+
+ for (i=0 ; i<MAX_LSTYLES ; i++)
+ {
+ if (!styletable[i])
+ continue;
+ if (fl->numstyles == MAX_STYLES)
+ break;
+ fl->samples[fl->numstyles] = styletable[i];
+ fl->stylenums[fl->numstyles] = i;
+ fl->numstyles++;
+ }
+
+ // the light from DIRECT_LIGHTS is sent out, but the
+ // texture itself should still be full bright
+
+ if (face_patches[facenum]->baselight[0] >= DIRECT_LIGHT ||
+ face_patches[facenum]->baselight[1] >= DIRECT_LIGHT ||
+ face_patches[facenum]->baselight[2] >= DIRECT_LIGHT
+ )
+ {
+ spot = fl->samples[0];
+ for (i=0 ; i<l[0].numsurfpt ; i++, spot+=3)
+ {
+ VectorAdd (spot, face_patches[facenum]->baselight, spot);
+ }
+ }
+}
+
+
+/*
+=============
+FinalLightFace
+
+Add the indirect lighting on top of the direct
+lighting and save into final map format
+=============
+*/
+void FinalLightFace (int facenum)
+{
+ dface_t *f;
+ int i, j, k, st;
+ vec3_t lb;
+ patch_t *patch;
+ triangulation_t *trian;
+ facelight_t *fl;
+ float minlight;
+ float max, newmax;
+ byte *dest;
+ int pfacenum;
+ vec3_t facemins, facemaxs;
+
+ f = &dfaces[facenum];
+ fl = &facelight[facenum];
+
+ if ( texinfo[f->texinfo].flags & (SURF_WARP|SURF_SKY) )
+ return; // non-lit texture
+
+ ThreadLock ();
+ f->lightofs = lightdatasize;
+ lightdatasize += fl->numstyles*(fl->numsamples*3);
+
+// add green sentinals between lightmaps
+#if 0
+lightdatasize += 64*3;
+for (i=0 ; i<64 ; i++)
+dlightdata[lightdatasize-(i+1)*3 + 1] = 255;
+#endif
+
+ if (lightdatasize > MAX_MAP_LIGHTING)
+ Error ("MAX_MAP_LIGHTING");
+ ThreadUnlock ();
+
+ f->styles[0] = 0;
+ f->styles[1] = f->styles[2] = f->styles[3] = 0xff;
+
+ //
+ // set up the triangulation
+ //
+ if (numbounce > 0)
+ {
+ ClearBounds (facemins, facemaxs);
+ for (i=0 ; i<f->numedges ; i++)
+ {
+ int ednum;
+
+ ednum = dsurfedges[f->firstedge+i];
+ if (ednum >= 0)
+ AddPointToBounds (dvertexes[dedges[ednum].v[0]].point,
+ facemins, facemaxs);
+ else
+ AddPointToBounds (dvertexes[dedges[-ednum].v[1]].point,
+ facemins, facemaxs);
+ }
+
+ trian = AllocTriangulation (&dplanes[f->planenum]);
+
+ // for all faces on the plane, add the nearby patches
+ // to the triangulation
+ for (pfacenum = planelinks[f->side][f->planenum]
+ ; pfacenum ; pfacenum = facelinks[pfacenum])
+ {
+ for (patch = face_patches[pfacenum] ; patch ; patch=patch->next)
+ {
+ for (i=0 ; i < 3 ; i++)
+ {
+ if (facemins[i] - patch->origin[i] > subdiv*2)
+ break;
+ if (patch->origin[i] - facemaxs[i] > subdiv*2)
+ break;
+ }
+ if (i != 3)
+ continue; // not needed for this face
+ AddPointToTriangulation (patch, trian);
+ }
+ }
+ for (i=0 ; i<trian->numpoints ; i++)
+ memset (trian->edgematrix[i], 0, trian->numpoints*sizeof(trian->edgematrix[0][0]) );
+ TriangulatePoints (trian);
+ }
+
+ //
+ // sample the triangulation
+ //
+
+ // _minlight allows models that have faces that would not be
+ // illuminated to receive a mottled light pattern instead of
+ // black
+ minlight = FloatForKey (face_entity[facenum], "_minlight") * 128;
+
+ dest = &dlightdata[f->lightofs];
+
+ if (fl->numstyles > MAXLIGHTMAPS)
+ {
+ fl->numstyles = MAXLIGHTMAPS;
+ printf ("face with too many lightstyles: (%f %f %f)\n",
+ face_patches[facenum]->origin[0],
+ face_patches[facenum]->origin[1],
+ face_patches[facenum]->origin[2]
+ );
+ }
+
+ for (st=0 ; st<fl->numstyles ; st++)
+ {
+ f->styles[st] = fl->stylenums[st];
+ for (j=0 ; j<fl->numsamples ; j++)
+ {
+ VectorCopy ( (fl->samples[st]+j*3), lb);
+ if (numbounce > 0 && st == 0)
+ {
+ vec3_t add;
+
+ SampleTriangulation (fl->origins + j*3, trian, add);
+ VectorAdd (lb, add, lb);
+ }
+ // add an ambient term if desired
+ lb[0] += ambient;
+ lb[1] += ambient;
+ lb[2] += ambient;
+
+ VectorScale (lb, lightscale, lb);
+
+ // we need to clamp without allowing hue to change
+ for (k=0 ; k<3 ; k++)
+ if (lb[k] < 1)
+ lb[k] = 1;
+ max = lb[0];
+ if (lb[1] > max)
+ max = lb[1];
+ if (lb[2] > max)
+ max = lb[2];
+ newmax = max;
+ if (newmax < 0)
+ newmax = 0; // roundoff problems
+ if (newmax < minlight)
+ {
+ newmax = minlight + (rand()%48);
+ }
+ if (newmax > maxlight)
+ newmax = maxlight;
+
+ for (k=0 ; k<3 ; k++)
+ {
+ *dest++ = lb[k]*newmax/max;
+ }
+ }
+ }
+
+ if (numbounce > 0)
+ FreeTriangulation (trian);
+}
--- /dev/null
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = qrad3
+EXE = $(ODIR)/qrad3
+all: $(EXE)
+
+_next:
+ make "CFLAGS = -c -g -I../../common" "ODIR = next"
+
+_irix:
+ make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+
+_irixdebug:
+ make "CFLAGS = -c -g -I../../common -Xcpluscomm" "LDFLAGS = " "ODIR = irix"
+
+_irixinst:
+ make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+_irixclean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+ make "CFLAGS = -c -O4 -I../../common -threads" "LDFLAGS = -threads" "ODIR = osf"
+
+clean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/lbmlib.o $(ODIR)/mathlib.o $(ODIR)/scriplib.o $(ODIR)/polylib.o $(ODIR)/qrad3.o $(ODIR)/threads.o $(ODIR)/trace.o $(ODIR)/lightmap.o $(ODIR)/patches.o
+
+$(EXE) : $(FILES)
+ cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
+
+$(ODIR)/qrad3.o : qrad3.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/patches.o : patches.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/trace.o : trace.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/vismat.o : vismat.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/lightmap.o : lightmap.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../../common/cmdlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/lbmlib.o : ../../common/lbmlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../../common/mathlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/polylib.o : ../../common/polylib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../../common/scriplib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../../common/threads.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../../common/bspfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qrad.h"
+
+vec3_t texture_reflectivity[MAX_MAP_TEXINFO];
+
+/*
+===================================================================
+
+ TEXTURE LIGHT VALUES
+
+===================================================================
+*/
+
+/*
+======================
+CalcTextureReflectivity
+======================
+*/
+void CalcTextureReflectivity (void)
+{
+ int i;
+ int j, k, texels;
+ int color[3];
+ int texel;
+ byte *palette;
+ char path[1024];
+ float r, scale;
+ miptex_t *mt;
+
+ sprintf (path, "%spics/colormap.pcx", gamedir);
+
+ // get the game palette
+ Load256Image (path, NULL, &palette, NULL, NULL);
+
+ // allways set index 0 even if no textures
+ texture_reflectivity[0][0] = 0.5;
+ texture_reflectivity[0][1] = 0.5;
+ texture_reflectivity[0][2] = 0.5;
+
+ for (i=0 ; i<numtexinfo ; i++)
+ {
+ // see if an earlier texinfo allready got the value
+ for (j=0 ; j<i ; j++)
+ {
+ if (!strcmp (texinfo[i].texture, texinfo[j].texture))
+ {
+ VectorCopy (texture_reflectivity[j], texture_reflectivity[i]);
+ break;
+ }
+ }
+ if (j != i)
+ continue;
+
+ // load the wal file
+ sprintf (path, "%stextures/%s.wal", gamedir, texinfo[i].texture);
+ if (TryLoadFile (path, (void **)&mt) == -1)
+ {
+ printf ("Couldn't load %s\n", path);
+ texture_reflectivity[i][0] = 0.5;
+ texture_reflectivity[i][1] = 0.5;
+ texture_reflectivity[i][2] = 0.5;
+ continue;
+ }
+ texels = LittleLong(mt->width)*LittleLong(mt->height);
+ color[0] = color[1] = color[2] = 0;
+
+ for (j=0 ; j<texels ; j++)
+ {
+ texel = ((byte *)mt)[LittleLong(mt->offsets[0]) + j];
+ for (k=0 ; k<3 ; k++)
+ color[k] += palette[texel*3+k];
+ }
+
+ for (j=0 ; j<3 ; j++)
+ {
+ r = color[j]/texels/255.0;
+ texture_reflectivity[i][j] = r;
+ }
+ // scale the reflectivity up, because the textures are
+ // so dim
+ scale = ColorNormalize (texture_reflectivity[i],
+ texture_reflectivity[i]);
+ if (scale < 0.5)
+ {
+ scale *= 2;
+ VectorScale (texture_reflectivity[i], scale, texture_reflectivity[i]);
+ }
+#if 0
+texture_reflectivity[i][0] = 0.5;
+texture_reflectivity[i][1] = 0.5;
+texture_reflectivity[i][2] = 0.5;
+#endif
+ }
+}
+
+/*
+=======================================================================
+
+MAKE FACES
+
+=======================================================================
+*/
+
+/*
+=============
+WindingFromFace
+=============
+*/
+winding_t *WindingFromFace (dface_t *f)
+{
+ int i;
+ int se;
+ dvertex_t *dv;
+ int v;
+ winding_t *w;
+
+ w = AllocWinding (f->numedges);
+ w->numpoints = f->numedges;
+
+ for (i=0 ; i<f->numedges ; i++)
+ {
+ se = dsurfedges[f->firstedge + i];
+ if (se < 0)
+ v = dedges[-se].v[1];
+ else
+ v = dedges[se].v[0];
+
+ dv = &dvertexes[v];
+ VectorCopy (dv->point, w->p[i]);
+ }
+
+ RemoveColinearPoints (w);
+
+ return w;
+}
+
+/*
+=============
+BaseLightForFace
+=============
+*/
+void BaseLightForFace (dface_t *f, vec3_t color)
+{
+ texinfo_t *tx;
+
+ //
+ // check for light emited by texture
+ //
+ tx = &texinfo[f->texinfo];
+ if (!(tx->flags & SURF_LIGHT) || tx->value == 0)
+ {
+ VectorClear (color);
+ return;
+ }
+
+ VectorScale (texture_reflectivity[f->texinfo], tx->value, color);
+}
+
+qboolean IsSky (dface_t *f)
+{
+ texinfo_t *tx;
+
+ tx = &texinfo[f->texinfo];
+ if (tx->flags & SURF_SKY)
+ return true;
+ return false;
+}
+
+/*
+=============
+MakePatchForFace
+=============
+*/
+float totalarea;
+void MakePatchForFace (int fn, winding_t *w)
+{
+ dface_t *f;
+ float area;
+ patch_t *patch;
+ dplane_t *pl;
+ int i;
+ vec3_t color;
+ dleaf_t *leaf;
+
+ f = &dfaces[fn];
+
+ area = WindingArea (w);
+ totalarea += area;
+
+ patch = &patches[num_patches];
+ if (num_patches == MAX_PATCHES)
+ Error ("num_patches == MAX_PATCHES");
+ patch->next = face_patches[fn];
+ face_patches[fn] = patch;
+
+ patch->winding = w;
+
+ if (f->side)
+ patch->plane = &backplanes[f->planenum];
+ else
+ patch->plane = &dplanes[f->planenum];
+ if (face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] )
+ { // origin offset faces must create new planes
+ if (numplanes + fakeplanes >= MAX_MAP_PLANES)
+ Error ("numplanes + fakeplanes >= MAX_MAP_PLANES");
+ pl = &dplanes[numplanes + fakeplanes];
+ fakeplanes++;
+
+ *pl = *(patch->plane);
+ pl->dist += DotProduct (face_offset[fn], pl->normal);
+ patch->plane = pl;
+ }
+
+ WindingCenter (w, patch->origin);
+ VectorAdd (patch->origin, patch->plane->normal, patch->origin);
+ leaf = PointInLeaf(patch->origin);
+ patch->cluster = leaf->cluster;
+ if (patch->cluster == -1)
+ qprintf ("patch->cluster == -1\n");
+
+ patch->area = area;
+ if (patch->area <= 1)
+ patch->area = 1;
+ patch->sky = IsSky (f);
+
+ VectorCopy (texture_reflectivity[f->texinfo], patch->reflectivity);
+
+ // non-bmodel patches can emit light
+ if (fn < dmodels[0].numfaces)
+ {
+ BaseLightForFace (f, patch->baselight);
+
+ ColorNormalize (patch->reflectivity, color);
+
+ for (i=0 ; i<3 ; i++)
+ patch->baselight[i] *= color[i];
+
+ VectorCopy (patch->baselight, patch->totallight);
+ }
+ num_patches++;
+}
+
+
+entity_t *EntityForModel (int modnum)
+{
+ int i;
+ char *s;
+ char name[16];
+
+ sprintf (name, "*%i", modnum);
+ // search the entities for one using modnum
+ for (i=0 ; i<num_entities ; i++)
+ {
+ s = ValueForKey (&entities[i], "model");
+ if (!strcmp (s, name))
+ return &entities[i];
+ }
+
+ return &entities[0];
+}
+
+/*
+=============
+MakePatches
+=============
+*/
+void MakePatches (void)
+{
+ int i, j, k;
+ dface_t *f;
+ int fn;
+ winding_t *w;
+ dmodel_t *mod;
+ vec3_t origin;
+ entity_t *ent;
+
+ qprintf ("%i faces\n", numfaces);
+
+ for (i=0 ; i<nummodels ; i++)
+ {
+ mod = &dmodels[i];
+ ent = EntityForModel (i);
+ // bmodels with origin brushes need to be offset into their
+ // in-use position
+ GetVectorForKey (ent, "origin", origin);
+//VectorCopy (vec3_origin, origin);
+
+ for (j=0 ; j<mod->numfaces ; j++)
+ {
+ fn = mod->firstface + j;
+ face_entity[fn] = ent;
+ VectorCopy (origin, face_offset[fn]);
+ f = &dfaces[fn];
+ w = WindingFromFace (f);
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ VectorAdd (w->p[k], origin, w->p[k]);
+ }
+ MakePatchForFace (fn, w);
+ }
+ }
+
+ qprintf ("%i sqaure feet\n", (int)(totalarea/64));
+}
+
+/*
+=======================================================================
+
+SUBDIVIDE
+
+=======================================================================
+*/
+
+void FinishSplit (patch_t *patch, patch_t *newp)
+{
+ dleaf_t *leaf;
+
+ VectorCopy (patch->baselight, newp->baselight);
+ VectorCopy (patch->totallight, newp->totallight);
+ VectorCopy (patch->reflectivity, newp->reflectivity);
+ newp->plane = patch->plane;
+ newp->sky = patch->sky;
+
+ patch->area = WindingArea (patch->winding);
+ newp->area = WindingArea (newp->winding);
+
+ if (patch->area <= 1)
+ patch->area = 1;
+ if (newp->area <= 1)
+ newp->area = 1;
+
+ WindingCenter (patch->winding, patch->origin);
+ VectorAdd (patch->origin, patch->plane->normal, patch->origin);
+ leaf = PointInLeaf(patch->origin);
+ patch->cluster = leaf->cluster;
+ if (patch->cluster == -1)
+ qprintf ("patch->cluster == -1\n");
+
+ WindingCenter (newp->winding, newp->origin);
+ VectorAdd (newp->origin, newp->plane->normal, newp->origin);
+ leaf = PointInLeaf(newp->origin);
+ newp->cluster = leaf->cluster;
+ if (newp->cluster == -1)
+ qprintf ("patch->cluster == -1\n");
+}
+
+/*
+=============
+SubdividePatch
+
+Chops the patch only if its local bounds exceed the max size
+=============
+*/
+void SubdividePatch (patch_t *patch)
+{
+ winding_t *w, *o1, *o2;
+ vec3_t mins, maxs, total;
+ vec3_t split;
+ vec_t dist;
+ int i, j;
+ vec_t v;
+ patch_t *newp;
+
+ w = patch->winding;
+ mins[0] = mins[1] = mins[2] = 99999;
+ maxs[0] = maxs[1] = maxs[2] = -99999;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ v = w->p[i][j];
+ if (v < mins[j])
+ mins[j] = v;
+ if (v > maxs[j])
+ maxs[j] = v;
+ }
+ }
+ VectorSubtract (maxs, mins, total);
+ for (i=0 ; i<3 ; i++)
+ if (total[i] > (subdiv+1) )
+ break;
+ if (i == 3)
+ {
+ // no splitting needed
+ return;
+ }
+
+ //
+ // split the winding
+ //
+ VectorCopy (vec3_origin, split);
+ split[i] = 1;
+ dist = (mins[i] + maxs[i])*0.5;
+ ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
+
+ //
+ // create a new patch
+ //
+ if (num_patches == MAX_PATCHES)
+ Error ("MAX_PATCHES");
+ newp = &patches[num_patches];
+ num_patches++;
+
+ newp->next = patch->next;
+ patch->next = newp;
+
+ patch->winding = o1;
+ newp->winding = o2;
+
+ FinishSplit (patch, newp);
+
+ SubdividePatch (patch);
+ SubdividePatch (newp);
+}
+
+
+/*
+=============
+DicePatch
+
+Chops the patch by a global grid
+=============
+*/
+void DicePatch (patch_t *patch)
+{
+ winding_t *w, *o1, *o2;
+ vec3_t mins, maxs;
+ vec3_t split;
+ vec_t dist;
+ int i;
+ patch_t *newp;
+
+ w = patch->winding;
+ WindingBounds (w, mins, maxs);
+ for (i=0 ; i<3 ; i++)
+ if (floor((mins[i]+1)/subdiv) < floor((maxs[i]-1)/subdiv))
+ break;
+ if (i == 3)
+ {
+ // no splitting needed
+ return;
+ }
+
+ //
+ // split the winding
+ //
+ VectorCopy (vec3_origin, split);
+ split[i] = 1;
+ dist = subdiv*(1+floor((mins[i]+1)/subdiv));
+ ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
+
+ //
+ // create a new patch
+ //
+ if (num_patches == MAX_PATCHES)
+ Error ("MAX_PATCHES");
+ newp = &patches[num_patches];
+ num_patches++;
+
+ newp->next = patch->next;
+ patch->next = newp;
+
+ patch->winding = o1;
+ newp->winding = o2;
+
+ FinishSplit (patch, newp);
+
+ DicePatch (patch);
+ DicePatch (newp);
+}
+
+
+/*
+=============
+SubdividePatches
+=============
+*/
+void SubdividePatches (void)
+{
+ int i, num;
+
+ if (subdiv < 1)
+ return;
+
+ num = num_patches; // because the list will grow
+ for (i=0 ; i<num ; i++)
+ {
+// SubdividePatch (&patches[i]);
+ DicePatch (&patches[i]);
+ }
+ qprintf ("%i patches after subdivision\n", num_patches);
+}
+
+//=====================================================================
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "polylib.h"
+#include "threads.h"
+#include "lbmlib.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+typedef enum
+{
+ emit_surface,
+ emit_point,
+ emit_spotlight
+} emittype_t;
+
+
+
+typedef struct directlight_s
+{
+ struct directlight_s *next;
+ emittype_t type;
+
+ float intensity;
+ int style;
+ vec3_t origin;
+ vec3_t color;
+ vec3_t normal; // for surfaces and spotlights
+ float stopdot; // for spotlights
+} directlight_t;
+
+
+// the sum of all tranfer->transfer values for a given patch
+// should equal exactly 0x10000, showing that all radiance
+// reaches other patches
+typedef struct
+{
+ unsigned short patch;
+ unsigned short transfer;
+} transfer_t;
+
+
+#define MAX_PATCHES 65000 // larger will cause 32 bit overflows
+
+typedef struct patch_s
+{
+ winding_t *winding;
+ struct patch_s *next; // next in face
+ int numtransfers;
+ transfer_t *transfers;
+
+ int cluster; // for pvs checking
+ vec3_t origin;
+ dplane_t *plane;
+
+ qboolean sky;
+
+ vec3_t totallight; // accumulated by radiosity
+ // does NOT include light
+ // accounted for by direct lighting
+ float area;
+
+ // illuminance * reflectivity = radiosity
+ vec3_t reflectivity;
+ vec3_t baselight; // emissivity only
+
+ // each style 0 lightmap sample in the patch will be
+ // added up to get the average illuminance of the entire patch
+ vec3_t samplelight;
+ int samples; // for averaging direct light
+} patch_t;
+
+extern patch_t *face_patches[MAX_MAP_FACES];
+extern entity_t *face_entity[MAX_MAP_FACES];
+extern vec3_t face_offset[MAX_MAP_FACES]; // for rotating bmodels
+extern patch_t patches[MAX_PATCHES];
+extern unsigned num_patches;
+
+extern int leafparents[MAX_MAP_LEAFS];
+extern int nodeparents[MAX_MAP_NODES];
+
+extern float lightscale;
+
+
+void MakeShadowSplits (void);
+
+//==============================================
+
+
+void BuildVisMatrix (void);
+qboolean CheckVisBit (unsigned p1, unsigned p2);
+
+//==============================================
+
+extern float ambient, maxlight;
+
+void LinkPlaneFaces (void);
+
+extern qboolean extrasamples;
+extern int numbounce;
+
+extern directlight_t *directlights[MAX_MAP_LEAFS];
+
+extern byte nodehit[MAX_MAP_NODES];
+
+void BuildLightmaps (void);
+
+void BuildFacelights (int facenum);
+
+void FinalLightFace (int facenum);
+
+qboolean PvsForOrigin (vec3_t org, byte *pvs);
+
+int TestLine_r (int node, vec3_t start, vec3_t stop);
+
+void CreateDirectLights (void);
+
+dleaf_t *PointInLeaf (vec3_t point);
+
+
+extern dplane_t backplanes[MAX_MAP_PLANES];
+extern int fakeplanes; // created planes for origin offset
+
+extern float subdiv;
+
+extern float direct_scale;
+extern float entity_scale;
+
+int PointInLeafnum (vec3_t point);
+void MakeTnodes (dmodel_t *bm);
+void MakePatches (void);
+void SubdividePatches (void);
+void PairEdges (void);
+void CalcTextureReflectivity (void);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qrad.h"
+
+
+
+/*
+
+NOTES
+-----
+
+every surface must be divided into at least two patches each axis
+
+*/
+
+patch_t *face_patches[MAX_MAP_FACES];
+entity_t *face_entity[MAX_MAP_FACES];
+patch_t patches[MAX_PATCHES];
+unsigned num_patches;
+
+vec3_t radiosity[MAX_PATCHES]; // light leaving a patch
+vec3_t illumination[MAX_PATCHES]; // light arriving at a patch
+
+vec3_t face_offset[MAX_MAP_FACES]; // for rotating bmodels
+dplane_t backplanes[MAX_MAP_PLANES];
+
+char inbase[32], outbase[32];
+
+int fakeplanes; // created planes for origin offset
+
+int numbounce = 8;
+qboolean extrasamples;
+
+float subdiv = 64;
+qboolean dumppatches;
+
+void BuildLightmaps (void);
+int TestLine (vec3_t start, vec3_t stop);
+
+int junk;
+
+float ambient = 0;
+float maxlight = 196;
+
+float lightscale = 1.0;
+
+qboolean glview;
+
+qboolean nopvs;
+
+char source[1024];
+
+float direct_scale = 0.4;
+float entity_scale = 1.0;
+
+/*
+===================================================================
+
+MISC
+
+===================================================================
+*/
+
+
+/*
+=============
+MakeBackplanes
+=============
+*/
+void MakeBackplanes (void)
+{
+ int i;
+
+ for (i=0 ; i<numplanes ; i++)
+ {
+ backplanes[i].dist = -dplanes[i].dist;
+ VectorSubtract (vec3_origin, dplanes[i].normal, backplanes[i].normal);
+ }
+}
+
+int leafparents[MAX_MAP_LEAFS];
+int nodeparents[MAX_MAP_NODES];
+
+/*
+=============
+MakeParents
+=============
+*/
+void MakeParents (int nodenum, int parent)
+{
+ int i, j;
+ dnode_t *node;
+
+ nodeparents[nodenum] = parent;
+ node = &dnodes[nodenum];
+
+ for (i=0 ; i<2 ; i++)
+ {
+ j = node->children[i];
+ if (j < 0)
+ leafparents[-j - 1] = nodenum;
+ else
+ MakeParents (j, nodenum);
+ }
+}
+
+
+/*
+===================================================================
+
+TRANSFER SCALES
+
+===================================================================
+*/
+
+int PointInLeafnum (vec3_t point)
+{
+ int nodenum;
+ vec_t dist;
+ dnode_t *node;
+ dplane_t *plane;
+
+ nodenum = 0;
+ while (nodenum >= 0)
+ {
+ node = &dnodes[nodenum];
+ plane = &dplanes[node->planenum];
+ dist = DotProduct (point, plane->normal) - plane->dist;
+ if (dist > 0)
+ nodenum = node->children[0];
+ else
+ nodenum = node->children[1];
+ }
+
+ return -nodenum - 1;
+}
+
+
+dleaf_t *PointInLeaf (vec3_t point)
+{
+ int num;
+
+ num = PointInLeafnum (point);
+ return &dleafs[num];
+}
+
+
+qboolean PvsForOrigin (vec3_t org, byte *pvs)
+{
+ dleaf_t *leaf;
+
+ if (!visdatasize)
+ {
+ memset (pvs, 255, (numleafs+7)/8 );
+ return true;
+ }
+
+ leaf = PointInLeaf (org);
+ if (leaf->cluster == -1)
+ return false; // in solid leaf
+
+ DecompressVis (dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs);
+ return true;
+}
+
+
+/*
+=============
+MakeTransfers
+
+=============
+*/
+int total_transfer;
+
+void MakeTransfers (int i)
+{
+ int j;
+ vec3_t delta;
+ vec_t dist, scale;
+ float trans;
+ int itrans;
+ patch_t *patch, *patch2;
+ float total;
+ dplane_t plane;
+ vec3_t origin;
+ float transfers[MAX_PATCHES], *all_transfers;
+ int s;
+ int itotal;
+ byte pvs[(MAX_MAP_LEAFS+7)/8];
+ int cluster;
+
+ patch = patches + i;
+ total = 0;
+
+ VectorCopy (patch->origin, origin);
+ plane = *patch->plane;
+
+ if (!PvsForOrigin (patch->origin, pvs))
+ return;
+
+ // find out which patch2s will collect light
+ // from patch
+
+ all_transfers = transfers;
+ patch->numtransfers = 0;
+ for (j=0, patch2 = patches ; j<num_patches ; j++, patch2++)
+ {
+ transfers[j] = 0;
+
+ if (j == i)
+ continue;
+
+ // check pvs bit
+ if (!nopvs)
+ {
+ cluster = patch2->cluster;
+ if (cluster == -1)
+ continue;
+ if ( ! ( pvs[cluster>>3] & (1<<(cluster&7)) ) )
+ continue; // not in pvs
+ }
+
+ // calculate vector
+ VectorSubtract (patch2->origin, origin, delta);
+ dist = VectorNormalize (delta, delta);
+ if (!dist)
+ continue; // should never happen
+
+ // reletive angles
+ scale = DotProduct (delta, plane.normal);
+ scale *= -DotProduct (delta, patch2->plane->normal);
+ if (scale <= 0)
+ continue;
+
+ // check exact tramsfer
+ if (TestLine_r (0, patch->origin, patch2->origin) )
+ continue;
+
+ trans = scale * patch2->area / (dist*dist);
+
+ if (trans < 0)
+ trans = 0; // rounding errors...
+
+ transfers[j] = trans;
+ if (trans > 0)
+ {
+ total += trans;
+ patch->numtransfers++;
+ }
+ }
+
+ // copy the transfers out and normalize
+ // total should be somewhere near PI if everything went right
+ // because partial occlusion isn't accounted for, and nearby
+ // patches have underestimated form factors, it will usually
+ // be higher than PI
+ if (patch->numtransfers)
+ {
+ transfer_t *t;
+
+ if (patch->numtransfers < 0 || patch->numtransfers > MAX_PATCHES)
+ Error ("Weird numtransfers");
+ s = patch->numtransfers * sizeof(transfer_t);
+ patch->transfers = malloc (s);
+ if (!patch->transfers)
+ Error ("Memory allocation failure");
+
+ //
+ // normalize all transfers so all of the light
+ // is transfered to the surroundings
+ //
+ t = patch->transfers;
+ itotal = 0;
+ for (j=0 ; j<num_patches ; j++)
+ {
+ if (transfers[j] <= 0)
+ continue;
+ itrans = transfers[j]*0x10000 / total;
+ itotal += itrans;
+ t->transfer = itrans;
+ t->patch = j;
+ t++;
+ }
+ }
+
+ // don't bother locking around this. not that important.
+ total_transfer += patch->numtransfers;
+}
+
+
+/*
+=============
+FreeTransfers
+=============
+*/
+void FreeTransfers (void)
+{
+ int i;
+
+ for (i=0 ; i<num_patches ; i++)
+ {
+ free (patches[i].transfers);
+ patches[i].transfers = NULL;
+ }
+}
+
+
+//===================================================================
+
+/*
+=============
+WriteWorld
+=============
+*/
+void WriteWorld (char *name)
+{
+ int i, j;
+ FILE *out;
+ patch_t *patch;
+ winding_t *w;
+
+ out = fopen (name, "w");
+ if (!out)
+ Error ("Couldn't open %s", name);
+
+ for (j=0, patch=patches ; j<num_patches ; j++, patch++)
+ {
+ w = patch->winding;
+ fprintf (out, "%i\n", w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ patch->totallight[0],
+ patch->totallight[1],
+ patch->totallight[2]);
+ }
+ fprintf (out, "\n");
+ }
+
+ fclose (out);
+}
+
+/*
+=============
+WriteGlView
+=============
+*/
+void WriteGlView (void)
+{
+ char name[1024];
+ FILE *f;
+ int i, j;
+ patch_t *p;
+ winding_t *w;
+
+ strcpy (name, source);
+ StripExtension (name);
+ strcat (name, ".glr");
+
+ f = fopen (name, "w");
+ if (!f)
+ Error ("Couldn't open %s", f);
+
+ for (j=0 ; j<num_patches ; j++)
+ {
+ p = &patches[j];
+ w = p->winding;
+ fprintf (f, "%i\n", w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (f, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ p->totallight[0]/128,
+ p->totallight[1]/128,
+ p->totallight[2]/128);
+ }
+ fprintf (f, "\n");
+ }
+
+ fclose (f);
+}
+
+
+//==============================================================
+
+/*
+=============
+CollectLight
+=============
+*/
+float CollectLight (void)
+{
+ int i, j;
+ patch_t *patch;
+ vec_t total;
+
+ total = 0;
+
+ for (i=0, patch=patches ; i<num_patches ; i++, patch++)
+ {
+ // skys never collect light, it is just dropped
+ if (patch->sky)
+ {
+ VectorClear (radiosity[i]);
+ VectorClear (illumination[i]);
+ continue;
+ }
+
+ for (j=0 ; j<3 ; j++)
+ {
+ patch->totallight[j] += illumination[i][j] / patch->area;
+ radiosity[i][j] = illumination[i][j] * patch->reflectivity[j];
+ }
+
+ total += radiosity[i][0] + radiosity[i][1] + radiosity[i][2];
+ VectorClear (illumination[i]);
+ }
+
+ return total;
+}
+
+
+/*
+=============
+ShootLight
+
+Send light out to other patches
+ Run multi-threaded
+=============
+*/
+void ShootLight (int patchnum)
+{
+ int k, l;
+ transfer_t *trans;
+ int num;
+ patch_t *patch;
+ vec3_t send;
+
+ // this is the amount of light we are distributing
+ // prescale it so that multiplying by the 16 bit
+ // transfer values gives a proper output value
+ for (k=0 ; k<3 ; k++)
+ send[k] = radiosity[patchnum][k] / 0x10000;
+ patch = &patches[patchnum];
+
+ trans = patch->transfers;
+ num = patch->numtransfers;
+
+ for (k=0 ; k<num ; k++, trans++)
+ {
+ for (l=0 ; l<3 ; l++)
+ illumination[trans->patch][l] += send[l]*trans->transfer;
+ }
+}
+
+/*
+=============
+BounceLight
+=============
+*/
+void BounceLight (void)
+{
+ int i, j;
+ float added;
+ char name[64];
+ patch_t *p;
+
+ for (i=0 ; i<num_patches ; i++)
+ {
+ p = &patches[i];
+ for (j=0 ; j<3 ; j++)
+ {
+// p->totallight[j] = p->samplelight[j];
+ radiosity[i][j] = p->samplelight[j] * p->reflectivity[j] * p->area;
+ }
+ }
+
+ for (i=0 ; i<numbounce ; i++)
+ {
+ RunThreadsOnIndividual (num_patches, false, ShootLight);
+ added = CollectLight ();
+
+ qprintf ("bounce:%i added:%f\n", i, added);
+ if ( dumppatches && (i==0 || i == numbounce-1) )
+ {
+ sprintf (name, "bounce%i.txt", i);
+ WriteWorld (name);
+ }
+ }
+}
+
+
+
+//==============================================================
+
+void CheckPatches (void)
+{
+ int i;
+ patch_t *patch;
+
+ for (i=0 ; i<num_patches ; i++)
+ {
+ patch = &patches[i];
+ if (patch->totallight[0] < 0 || patch->totallight[1] < 0 || patch->totallight[2] < 0)
+ Error ("negative patch totallight\n");
+ }
+}
+
+/*
+=============
+RadWorld
+=============
+*/
+void RadWorld (void)
+{
+ if (numnodes == 0 || numfaces == 0)
+ Error ("Empty map");
+ MakeBackplanes ();
+ MakeParents (0, -1);
+ MakeTnodes (&dmodels[0]);
+
+ // turn each face into a single patch
+ MakePatches ();
+
+ // subdivide patches to a maximum dimension
+ SubdividePatches ();
+
+ // create directlights out of patches and lights
+ CreateDirectLights ();
+
+ // build initial facelights
+ RunThreadsOnIndividual (numfaces, true, BuildFacelights);
+
+ if (numbounce > 0)
+ {
+ // build transfer lists
+ RunThreadsOnIndividual (num_patches, true, MakeTransfers);
+ qprintf ("transfer lists: %5.1f megs\n"
+ , (float)total_transfer * sizeof(transfer_t) / (1024*1024));
+
+ // spread light around
+ BounceLight ();
+
+ FreeTransfers ();
+
+ CheckPatches ();
+ }
+
+ if (glview)
+ WriteGlView ();
+
+ // blend bounced light into direct light and save
+ PairEdges ();
+ LinkPlaneFaces ();
+
+ lightdatasize = 0;
+ RunThreadsOnIndividual (numfaces, true, FinalLightFace);
+}
+
+
+/*
+========
+main
+
+light modelfile
+========
+*/
+int main (int argc, char **argv)
+{
+ int i;
+ double start, end;
+ char name[1024];
+
+ printf ("----- Radiosity ----\n");
+
+ verbose = false;
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i],"-dump"))
+ dumppatches = true;
+ else if (!strcmp(argv[i],"-bounce"))
+ {
+ numbounce = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-v"))
+ {
+ verbose = true;
+ }
+ else if (!strcmp(argv[i],"-extra"))
+ {
+ extrasamples = true;
+ printf ("extrasamples = true\n");
+ }
+ else if (!strcmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-chop"))
+ {
+ subdiv = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-scale"))
+ {
+ lightscale = atof (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-direct"))
+ {
+ direct_scale *= atof(argv[i+1]);
+ printf ("direct light scaling at %f\n", direct_scale);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-entity"))
+ {
+ entity_scale *= atof(argv[i+1]);
+ printf ("entity light scaling at %f\n", entity_scale);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-glview"))
+ {
+ glview = true;
+ printf ("glview = true\n");
+ }
+ else if (!strcmp(argv[i],"-nopvs"))
+ {
+ nopvs = true;
+ printf ("nopvs = true\n");
+ }
+ else if (!strcmp(argv[i],"-ambient"))
+ {
+ ambient = atof (argv[i+1]) * 128;
+ i++;
+ }
+ else if (!strcmp(argv[i],"-maxlight"))
+ {
+ maxlight = atof (argv[i+1]) * 128;
+ i++;
+ }
+ else if (!strcmp (argv[i],"-tmpin"))
+ strcpy (inbase, "/tmp");
+ else if (!strcmp (argv[i],"-tmpout"))
+ strcpy (outbase, "/tmp");
+ else
+ break;
+ }
+
+ ThreadSetDefault ();
+
+ if (maxlight > 255)
+ maxlight = 255;
+
+ if (i != argc - 1)
+ Error ("usage: qrad [-v] [-chop num] [-scale num] [-ambient num] [-maxlight num] [-threads num] bspfile");
+
+ start = I_FloatTime ();
+
+ SetQdirFromPath (argv[i]);
+ strcpy (source, ExpandArg(argv[i]));
+ StripExtension (source);
+ DefaultExtension (source, ".bsp");
+
+// ReadLightFile ();
+
+ sprintf (name, "%s%s", inbase, source);
+ printf ("reading %s\n", name);
+ LoadBSPFile (name);
+ ParseEntities ();
+ CalcTextureReflectivity ();
+
+ if (!visdatasize)
+ {
+ printf ("No vis information, direct lighting only.\n");
+ numbounce = 0;
+ ambient = 0.1;
+ }
+
+ RadWorld ();
+
+ sprintf (name, "%s%s", outbase, source);
+ printf ("writing %s\n", name);
+ WriteBSPFile (name);
+
+ end = I_FloatTime ();
+ printf ("%5.0f seconds elapsed\n", end-start);
+
+ return 0;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+#define ON_EPSILON 0.1
+
+typedef struct tnode_s
+{
+ int type;
+ vec3_t normal;
+ float dist;
+ int children[2];
+ int pad;
+} tnode_t;
+
+tnode_t *tnodes, *tnode_p;
+
+/*
+==============
+MakeTnode
+
+Converts the disk node structure into the efficient tracing structure
+==============
+*/
+void MakeTnode (int nodenum)
+{
+ tnode_t *t;
+ dplane_t *plane;
+ int i;
+ dnode_t *node;
+
+ t = tnode_p++;
+
+ node = dnodes + nodenum;
+ plane = dplanes + node->planenum;
+
+ t->type = plane->type;
+ VectorCopy (plane->normal, t->normal);
+ t->dist = plane->dist;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i] < 0)
+ t->children[i] = (dleafs[-node->children[i] - 1].contents & CONTENTS_SOLID) | (1<<31);
+ else
+ {
+ t->children[i] = tnode_p - tnodes;
+ MakeTnode (node->children[i]);
+ }
+ }
+
+}
+
+
+/*
+=============
+MakeTnodes
+
+Loads the node structure out of a .bsp file to be used for light occlusion
+=============
+*/
+void MakeTnodes (dmodel_t *bm)
+{
+ // 32 byte align the structs
+ tnodes = malloc( (numnodes+1) * sizeof(tnode_t));
+ tnodes = (tnode_t *)(((int)tnodes + 31)&~31);
+ tnode_p = tnodes;
+
+ MakeTnode (0);
+}
+
+
+//==========================================================
+
+
+int TestLine_r (int node, vec3_t start, vec3_t stop)
+{
+ tnode_t *tnode;
+ float front, back;
+ vec3_t mid;
+ float frac;
+ int side;
+ int r;
+
+ if (node & (1<<31))
+ return node & ~(1<<31); // leaf node
+
+ tnode = &tnodes[node];
+ switch (tnode->type)
+ {
+ case PLANE_X:
+ front = start[0] - tnode->dist;
+ back = stop[0] - tnode->dist;
+ break;
+ case PLANE_Y:
+ front = start[1] - tnode->dist;
+ back = stop[1] - tnode->dist;
+ break;
+ case PLANE_Z:
+ front = start[2] - tnode->dist;
+ back = stop[2] - tnode->dist;
+ break;
+ default:
+ front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
+ back = (stop[0]*tnode->normal[0] + stop[1]*tnode->normal[1] + stop[2]*tnode->normal[2]) - tnode->dist;
+ break;
+ }
+
+ if (front >= -ON_EPSILON && back >= -ON_EPSILON)
+ return TestLine_r (tnode->children[0], start, stop);
+
+ if (front < ON_EPSILON && back < ON_EPSILON)
+ return TestLine_r (tnode->children[1], start, stop);
+
+ side = front < 0;
+
+ frac = front / (front-back);
+
+ mid[0] = start[0] + (stop[0] - start[0])*frac;
+ mid[1] = start[1] + (stop[1] - start[1])*frac;
+ mid[2] = start[2] + (stop[2] - start[2])*frac;
+
+ r = TestLine_r (tnode->children[side], start, mid);
+ if (r)
+ return r;
+ return TestLine_r (tnode->children[!side], mid, stop);
+}
+
+int TestLine (vec3_t start, vec3_t stop)
+{
+ return TestLine_r (0, start, stop);
+}
+
+/*
+==============================================================================
+
+LINE TRACING
+
+The major lighting operation is a point to point visibility test, performed
+by recursive subdivision of the line by the BSP tree.
+
+==============================================================================
+*/
+
+typedef struct
+{
+ vec3_t backpt;
+ int side;
+ int node;
+} tracestack_t;
+
+
+/*
+==============
+TestLine
+==============
+*/
+qboolean _TestLine (vec3_t start, vec3_t stop)
+{
+ int node;
+ float front, back;
+ tracestack_t *tstack_p;
+ int side;
+ float frontx,fronty, frontz, backx, backy, backz;
+ tracestack_t tracestack[64];
+ tnode_t *tnode;
+
+ frontx = start[0];
+ fronty = start[1];
+ frontz = start[2];
+ backx = stop[0];
+ backy = stop[1];
+ backz = stop[2];
+
+ tstack_p = tracestack;
+ node = 0;
+
+ while (1)
+ {
+ if (node == CONTENTS_SOLID)
+ {
+#if 0
+ float d1, d2, d3;
+
+ d1 = backx - frontx;
+ d2 = backy - fronty;
+ d3 = backz - frontz;
+
+ if (d1*d1 + d2*d2 + d3*d3 > 1)
+#endif
+ return false; // DONE!
+ }
+
+ while (node < 0)
+ {
+ // pop up the stack for a back side
+ tstack_p--;
+ if (tstack_p < tracestack)
+ return true;
+ node = tstack_p->node;
+
+ // set the hit point for this plane
+
+ frontx = backx;
+ fronty = backy;
+ frontz = backz;
+
+ // go down the back side
+
+ backx = tstack_p->backpt[0];
+ backy = tstack_p->backpt[1];
+ backz = tstack_p->backpt[2];
+
+ node = tnodes[tstack_p->node].children[!tstack_p->side];
+ }
+
+ tnode = &tnodes[node];
+
+ switch (tnode->type)
+ {
+ case PLANE_X:
+ front = frontx - tnode->dist;
+ back = backx - tnode->dist;
+ break;
+ case PLANE_Y:
+ front = fronty - tnode->dist;
+ back = backy - tnode->dist;
+ break;
+ case PLANE_Z:
+ front = frontz - tnode->dist;
+ back = backz - tnode->dist;
+ break;
+ default:
+ front = (frontx*tnode->normal[0] + fronty*tnode->normal[1] + frontz*tnode->normal[2]) - tnode->dist;
+ back = (backx*tnode->normal[0] + backy*tnode->normal[1] + backz*tnode->normal[2]) - tnode->dist;
+ break;
+ }
+
+ if (front > -ON_EPSILON && back > -ON_EPSILON)
+// if (front > 0 && back > 0)
+ {
+ node = tnode->children[0];
+ continue;
+ }
+
+ if (front < ON_EPSILON && back < ON_EPSILON)
+// if (front <= 0 && back <= 0)
+ {
+ node = tnode->children[1];
+ continue;
+ }
+
+ side = front < 0;
+
+ front = front / (front-back);
+
+ tstack_p->node = node;
+ tstack_p->side = side;
+ tstack_p->backpt[0] = backx;
+ tstack_p->backpt[1] = backy;
+ tstack_p->backpt[2] = backz;
+
+ tstack_p++;
+
+ backx = frontx + front*(backx-frontx);
+ backy = fronty + front*(backy-fronty);
+ backz = frontz + front*(backz-frontz);
+
+ node = tnode->children[side];
+ }
+}
+
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include "vis.h"
+
+/*
+
+ each portal will have a list of all possible to see from first portal
+
+ if (!thread->portalmightsee[portalnum])
+
+ portal mightsee
+
+ for p2 = all other portals in leaf
+ get sperating planes
+ for all portals that might be seen by p2
+ mark as unseen if not present in seperating plane
+ flood fill a new mightsee
+ save as passagemightsee
+
+
+ void CalcMightSee (leaf_t *leaf,
+*/
+
+int CountBits (byte *bits, int numbits)
+{
+ int i;
+ int c;
+
+ c = 0;
+ for (i=0 ; i<numbits ; i++)
+ if (bits[i>>3] & (1<<(i&7)) )
+ c++;
+
+ return c;
+}
+
+int c_fullskip;
+int c_portalskip, c_leafskip;
+int c_vistest, c_mighttest;
+
+int c_chop, c_nochop;
+
+int active;
+
+void CheckStack (leaf_t *leaf, threaddata_t *thread)
+{
+ pstack_t *p, *p2;
+
+ for (p=thread->pstack_head.next ; p ; p=p->next)
+ {
+// printf ("=");
+ if (p->leaf == leaf)
+ Error ("CheckStack: leaf recursion");
+ for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
+ if (p2->leaf == p->leaf)
+ Error ("CheckStack: late leaf recursion");
+ }
+// printf ("\n");
+}
+
+
+winding_t *AllocStackWinding (pstack_t *stack)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (stack->freewindings[i])
+ {
+ stack->freewindings[i] = 0;
+ return &stack->windings[i];
+ }
+ }
+
+ Error ("AllocStackWinding: failed");
+
+ return NULL;
+}
+
+void FreeStackWinding (winding_t *w, pstack_t *stack)
+{
+ int i;
+
+ i = w - stack->windings;
+
+ if (i<0 || i>2)
+ return; // not from local
+
+ if (stack->freewindings[i])
+ Error ("FreeStackWinding: allready free");
+ stack->freewindings[i] = 1;
+}
+
+/*
+==============
+ChopWinding
+
+==============
+*/
+winding_t *ChopWinding (winding_t *in, pstack_t *stack, plane_t *split)
+{
+ vec_t dists[128];
+ int sides[128];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *neww;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->points[i], split->normal);
+ dot -= split->dist;
+ dists[i] = dot;
+ if (dot > ON_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+
+ if (!counts[1])
+ return in; // completely on front side
+
+ if (!counts[0])
+ {
+ FreeStackWinding (in, stack);
+ return NULL;
+ }
+
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ neww = AllocStackWinding (stack);
+
+ neww->numpoints = 0;
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->points[i];
+
+ if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
+ {
+ FreeStackWinding (neww, stack);
+ return in; // can't chop -- fall back to original
+ }
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
+ {
+ FreeStackWinding (neww, stack);
+ return in; // can't chop -- fall back to original
+ }
+
+ // generate a split point
+ p2 = in->points[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (split->normal[j] == 1)
+ mid[j] = split->dist;
+ else if (split->normal[j] == -1)
+ mid[j] = -split->dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+// free the original winding
+ FreeStackWinding (in, stack);
+
+ return neww;
+}
+
+
+/*
+==============
+ClipToSeperators
+
+Source, pass, and target are an ordering of portals.
+
+Generates seperating planes canidates by taking two points from source and one
+point from pass, and clips target by them.
+
+If target is totally clipped away, that portal can not be seen through.
+
+Normal clip keeps target on the same side as pass, which is correct if the
+order goes source, pass, target. If the order goes pass, source, target then
+flipclip should be set.
+==============
+*/
+winding_t *ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target, qboolean flipclip, pstack_t *stack)
+{
+ int i, j, k, l;
+ plane_t plane;
+ vec3_t v1, v2;
+ float d;
+ vec_t length;
+ int counts[3];
+ qboolean fliptest;
+
+// check all combinations
+ for (i=0 ; i<source->numpoints ; i++)
+ {
+ l = (i+1)%source->numpoints;
+ VectorSubtract (source->points[l] , source->points[i], v1);
+
+ // fing a vertex of pass that makes a plane that puts all of the
+ // vertexes of pass on the front side and all of the vertexes of
+ // source on the back side
+ for (j=0 ; j<pass->numpoints ; j++)
+ {
+ VectorSubtract (pass->points[j], source->points[i], v2);
+
+ plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
+
+ // if points don't make a valid plane, skip it
+
+ length = plane.normal[0] * plane.normal[0]
+ + plane.normal[1] * plane.normal[1]
+ + plane.normal[2] * plane.normal[2];
+
+ if (length < ON_EPSILON)
+ continue;
+
+ length = 1/sqrt(length);
+
+ plane.normal[0] *= length;
+ plane.normal[1] *= length;
+ plane.normal[2] *= length;
+
+ plane.dist = DotProduct (pass->points[j], plane.normal);
+
+ //
+ // find out which side of the generated seperating plane has the
+ // source portal
+ //
+#if 1
+ fliptest = false;
+ for (k=0 ; k<source->numpoints ; k++)
+ {
+ if (k == i || k == l)
+ continue;
+ d = DotProduct (source->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ { // source is on the negative side, so we want all
+ // pass and target on the positive side
+ fliptest = false;
+ break;
+ }
+ else if (d > ON_EPSILON)
+ { // source is on the positive side, so we want all
+ // pass and target on the negative side
+ fliptest = true;
+ break;
+ }
+ }
+ if (k == source->numpoints)
+ continue; // planar with source portal
+#else
+ fliptest = flipclip;
+#endif
+ //
+ // flip the normal if the source portal is backwards
+ //
+ if (fliptest)
+ {
+ VectorSubtract (vec3_origin, plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+#if 1
+ //
+ // if all of the pass portal points are now on the positive side,
+ // this is the seperating plane
+ //
+ counts[0] = counts[1] = counts[2] = 0;
+ for (k=0 ; k<pass->numpoints ; k++)
+ {
+ if (k==j)
+ continue;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ break;
+ else if (d > ON_EPSILON)
+ counts[0]++;
+ else
+ counts[2]++;
+ }
+ if (k != pass->numpoints)
+ continue; // points on negative side, not a seperating plane
+
+ if (!counts[0])
+ continue; // planar with seperating plane
+#else
+ k = (j+1)%pass->numpoints;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ continue;
+ k = (j+pass->numpoints-1)%pass->numpoints;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ continue;
+#endif
+ //
+ // flip the normal if we want the back side
+ //
+ if (flipclip)
+ {
+ VectorSubtract (vec3_origin, plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+
+ //
+ // clip target by the seperating plane
+ //
+ target = ChopWinding (target, stack, &plane);
+ if (!target)
+ return NULL; // target is not visible
+ }
+ }
+
+ return target;
+}
+
+
+
+/*
+==================
+RecursiveLeafFlow
+
+Flood fill through the leafs
+If src_portal is NULL, this is the originating leaf
+==================
+*/
+void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
+{
+ pstack_t stack;
+ portal_t *p;
+ plane_t backplane;
+ leaf_t *leaf;
+ int i, j;
+ long *test, *might, *vis, more;
+ int pnum;
+
+ thread->c_chains++;
+
+ leaf = &leafs[leafnum];
+// CheckStack (leaf, thread);
+
+ prevstack->next = &stack;
+
+ stack.next = NULL;
+ stack.leaf = leaf;
+ stack.portal = NULL;
+
+ might = (long *)stack.mightsee;
+ vis = (long *)thread->base->portalvis;
+
+// check all portals for flowing into other leafs
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+ pnum = p - portals;
+
+ if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
+ {
+ continue; // can't possibly see it
+ }
+
+ // if the portal can't see anything we haven't allready seen, skip it
+ if (p->status == stat_done)
+ {
+ test = (long *)p->portalvis;
+ }
+ else
+ {
+ test = (long *)p->portalflood;
+ }
+
+ more = 0;
+ for (j=0 ; j<portallongs ; j++)
+ {
+ might[j] = ((long *)prevstack->mightsee)[j] & test[j];
+ more |= (might[j] & ~vis[j]);
+ }
+
+ if (!more &&
+ (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) )
+ { // can't see anything new
+ continue;
+ }
+
+ // get plane of portal, point normal into the neighbor leaf
+ stack.portalplane = p->plane;
+ VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
+ backplane.dist = -p->plane.dist;
+
+// c_portalcheck++;
+
+ stack.portal = p;
+ stack.next = NULL;
+ stack.freewindings[0] = 1;
+ stack.freewindings[1] = 1;
+ stack.freewindings[2] = 1;
+
+#if 1
+{
+float d;
+
+ d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
+ d -= thread->pstack_head.portalplane.dist;
+ if (d < -p->radius)
+ {
+ continue;
+ }
+ else if (d > p->radius)
+ {
+ stack.pass = p->winding;
+ }
+ else
+ {
+ stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+ if (!stack.pass)
+ continue;
+ }
+}
+#else
+ stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+ if (!stack.pass)
+ continue;
+#endif
+
+
+#if 1
+{
+float d;
+
+ d = DotProduct (thread->base->origin, p->plane.normal);
+ d -= p->plane.dist;
+ if (d > p->radius)
+ {
+ continue;
+ }
+ else if (d < -p->radius)
+ {
+ stack.source = prevstack->source;
+ }
+ else
+ {
+ stack.source = ChopWinding (prevstack->source, &stack, &backplane);
+ if (!stack.source)
+ continue;
+ }
+}
+#else
+ stack.source = ChopWinding (prevstack->source, &stack, &backplane);
+ if (!stack.source)
+ continue;
+#endif
+
+ if (!prevstack->pass)
+ { // the second leaf can only be blocked if coplanar
+
+ // mark the portal as visible
+ thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+ RecursiveLeafFlow (p->leaf, thread, &stack);
+ continue;
+ }
+
+ stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack);
+ if (!stack.pass)
+ continue;
+
+ stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack);
+ if (!stack.pass)
+ continue;
+
+ // mark the portal as visible
+ thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+ // flow through it for real
+ RecursiveLeafFlow (p->leaf, thread, &stack);
+ }
+}
+
+
+/*
+===============
+PortalFlow
+
+generates the portalvis bit vector
+===============
+*/
+void PortalFlow (int portalnum)
+{
+ threaddata_t data;
+ int i;
+ portal_t *p;
+ int c_might, c_can;
+
+ p = sorted_portals[portalnum];
+ p->status = stat_working;
+
+ c_might = CountBits (p->portalflood, numportals*2);
+
+ memset (&data, 0, sizeof(data));
+ data.base = p;
+
+ data.pstack_head.portal = p;
+ data.pstack_head.source = p->winding;
+ data.pstack_head.portalplane = p->plane;
+ for (i=0 ; i<portallongs ; i++)
+ ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
+ RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
+
+ p->status = stat_done;
+
+ c_can = CountBits (p->portalvis, numportals*2);
+
+ qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
+ (int)(p - portals), c_might, c_can, data.c_chains);
+}
+
+
+/*
+===============================================================================
+
+This is a rough first-order aproximation that is used to trivially reject some
+of the final calculations.
+
+
+Calculates portalfront and portalflood bit vectors
+
+thinking about:
+
+typedef struct passage_s
+{
+ struct passage_s *next;
+ struct portal_s *to;
+ stryct sep_s *seperators;
+ byte *mightsee;
+} passage_t;
+
+typedef struct portal_s
+{
+ struct passage_s *passages;
+ int leaf; // leaf portal faces into
+} portal_s;
+
+leaf = portal->leaf
+clear
+for all portals
+
+
+calc portal visibility
+ clear bit vector
+ for all passages
+ passage visibility
+
+
+for a portal to be visible to a passage, it must be on the front of
+all seperating planes, and both portals must be behind the mew portal
+
+===============================================================================
+*/
+
+int c_flood, c_vis;
+
+
+/*
+==================
+SimpleFlood
+
+==================
+*/
+void SimpleFlood (portal_t *srcportal, int leafnum)
+{
+ int i;
+ leaf_t *leaf;
+ portal_t *p;
+ int pnum;
+
+ leaf = &leafs[leafnum];
+
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+ pnum = p - portals;
+ if ( ! (srcportal->portalfront[pnum>>3] & (1<<(pnum&7)) ) )
+ continue;
+
+ if (srcportal->portalflood[pnum>>3] & (1<<(pnum&7)) )
+ continue;
+
+ srcportal->portalflood[pnum>>3] |= (1<<(pnum&7));
+
+ SimpleFlood (srcportal, p->leaf);
+ }
+}
+
+/*
+==============
+BasePortalVis
+==============
+*/
+void BasePortalVis (int portalnum)
+{
+ int j, k;
+ portal_t *tp, *p;
+ float d;
+ winding_t *w;
+
+ p = portals+portalnum;
+
+ p->portalfront = malloc (portalbytes);
+ memset (p->portalfront, 0, portalbytes);
+
+ p->portalflood = malloc (portalbytes);
+ memset (p->portalflood, 0, portalbytes);
+
+ p->portalvis = malloc (portalbytes);
+ memset (p->portalvis, 0, portalbytes);
+
+ for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
+ {
+ if (j == portalnum)
+ continue;
+ w = tp->winding;
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ d = DotProduct (w->points[k], p->plane.normal)
+ - p->plane.dist;
+ if (d > ON_EPSILON)
+ break;
+ }
+ if (k == w->numpoints)
+ continue; // no points on front
+
+ w = p->winding;
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ d = DotProduct (w->points[k], tp->plane.normal)
+ - tp->plane.dist;
+ if (d < -ON_EPSILON)
+ break;
+ }
+ if (k == w->numpoints)
+ continue; // no points on front
+
+ p->portalfront[j>>3] |= (1<<(j&7));
+ }
+
+ SimpleFlood (p, p->leaf);
+
+ p->nummightsee = CountBits (p->portalflood, numportals*2);
+// printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
+ c_flood += p->nummightsee;
+}
+
+
+
+
+
+/*
+===============================================================================
+
+This is a second order aproximation
+
+Calculates portalvis bit vector
+
+WAAAAAAY too slow.
+
+===============================================================================
+*/
+
+/*
+==================
+RecursiveLeafBitFlow
+
+==================
+*/
+void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
+{
+ portal_t *p;
+ leaf_t *leaf;
+ int i, j;
+ long more;
+ int pnum;
+ byte newmight[MAX_PORTALS/8];
+
+ leaf = &leafs[leafnum];
+
+// check all portals for flowing into other leafs
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+ pnum = p - portals;
+
+ // if some previous portal can't see it, skip
+ if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) )
+ continue;
+
+ // if this portal can see some portals we mightsee, recurse
+ more = 0;
+ for (j=0 ; j<portallongs ; j++)
+ {
+ ((long *)newmight)[j] = ((long *)mightsee)[j]
+ & ((long *)p->portalflood)[j];
+ more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
+ }
+
+ if (!more)
+ continue; // can't see anything new
+
+ cansee[pnum>>3] |= (1<<(pnum&7));
+
+ RecursiveLeafBitFlow (p->leaf, newmight, cansee);
+ }
+}
+
+/*
+==============
+BetterPortalVis
+==============
+*/
+void BetterPortalVis (int portalnum)
+{
+ portal_t *p;
+
+ p = portals+portalnum;
+
+ RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
+
+ // build leaf vis information
+ p->nummightsee = CountBits (p->portalvis, numportals*2);
+ c_vis += p->nummightsee;
+}
+
+
--- /dev/null
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = qvis3
+EXE = $(ODIR)/qvis3
+all: $(EXE)
+
+_next:
+ make "CFLAGS = -c -g -I../../common" "ODIR = next"
+
+_irix:
+ make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+
+_irixinst:
+ make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+_irixclean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+ make "CFLAGS = -c -O4 -I../../common -threads" "LDFLAGS = -threads -lm" "ODIR = osf"
+
+clean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/mathlib.o $(ODIR)/scriplib.o $(ODIR)/threads.o $(ODIR)/qvis3.o $(ODIR)/flow.o
+
+$(EXE) : $(FILES)
+ cc -o $(EXE) $(LDFLAGS) $(FILES)
+
+$(ODIR)/qvis3.o : qvis3.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/flow.o : flow.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../../common/cmdlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../../common/mathlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/polylib.o : ../../common/polylib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../../common/scriplib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../../common/threads.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../../common/bspfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "vis.h"
+#include "threads.h"
+#include "stdlib.h"
+
+int numportals;
+int portalclusters;
+
+char inbase[32];
+char outbase[32];
+
+portal_t *portals;
+leaf_t *leafs;
+
+int c_portaltest, c_portalpass, c_portalcheck;
+
+byte *uncompressedvis;
+
+byte *vismap, *vismap_p, *vismap_end; // past visfile
+int originalvismapsize;
+
+int leafbytes; // (portalclusters+63)>>3
+int leaflongs;
+
+int portalbytes, portallongs;
+
+qboolean fastvis;
+qboolean nosort;
+
+int testlevel = 2;
+
+int totalvis;
+
+portal_t *sorted_portals[MAX_MAP_PORTALS*2];
+
+
+//=============================================================================
+
+void PlaneFromWinding (winding_t *w, plane_t *plane)
+{
+ vec3_t v1, v2;
+
+// calc plane
+ VectorSubtract (w->points[2], w->points[1], v1);
+ VectorSubtract (w->points[0], w->points[1], v2);
+ CrossProduct (v2, v1, plane->normal);
+ VectorNormalize (plane->normal, plane->normal);
+ plane->dist = DotProduct (w->points[0], plane->normal);
+}
+
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (int points)
+{
+ winding_t *w;
+ int size;
+
+ if (points > MAX_POINTS_ON_WINDING)
+ Error ("NewWinding: %i points", points);
+
+ size = (int)((winding_t *)0)->points[points];
+ w = malloc (size);
+ memset (w, 0, size);
+
+ return w;
+}
+
+
+
+void pw(winding_t *w)
+{
+ int i;
+ for (i=0 ; i<w->numpoints ; i++)
+ printf ("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]);
+}
+
+void prl(leaf_t *l)
+{
+ int i;
+ portal_t *p;
+ plane_t pl;
+
+ for (i=0 ; i<l->numportals ; i++)
+ {
+ p = l->portals[i];
+ pl = p->plane;
+ printf ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
+ }
+}
+
+
+//=============================================================================
+
+/*
+=============
+SortPortals
+
+Sorts the portals from the least complex, so the later ones can reuse
+the earlier information.
+=============
+*/
+int PComp (const void *a, const void *b)
+{
+ if ( (*(portal_t **)a)->nummightsee == (*(portal_t **)b)->nummightsee)
+ return 0;
+ if ( (*(portal_t **)a)->nummightsee < (*(portal_t **)b)->nummightsee)
+ return -1;
+ return 1;
+}
+void SortPortals (void)
+{
+ int i;
+
+ for (i=0 ; i<numportals*2 ; i++)
+ sorted_portals[i] = &portals[i];
+
+ if (nosort)
+ return;
+ qsort (sorted_portals, numportals*2, sizeof(sorted_portals[0]), PComp);
+}
+
+
+/*
+==============
+LeafVectorFromPortalVector
+==============
+*/
+int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
+{
+ int i;
+ portal_t *p;
+ int c_leafs;
+
+
+ memset (leafbits, 0, leafbytes);
+
+ for (i=0 ; i<numportals*2 ; i++)
+ {
+ if (portalbits[i>>3] & (1<<(i&7)) )
+ {
+ p = portals+i;
+ leafbits[p->leaf>>3] |= (1<<(p->leaf&7));
+ }
+ }
+
+ c_leafs = CountBits (leafbits, portalclusters);
+
+ return c_leafs;
+}
+
+
+/*
+===============
+ClusterMerge
+
+Merges the portal visibility for a leaf
+===============
+*/
+void ClusterMerge (int leafnum)
+{
+ leaf_t *leaf;
+ byte portalvector[MAX_PORTALS/8];
+ byte uncompressed[MAX_MAP_LEAFS/8];
+ byte compressed[MAX_MAP_LEAFS/8];
+ int i, j;
+ int numvis;
+ byte *dest;
+ portal_t *p;
+ int pnum;
+
+ // OR together all the portalvis bits
+
+ memset (portalvector, 0, portalbytes);
+ leaf = &leafs[leafnum];
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+ if (p->status != stat_done)
+ Error ("portal not done");
+ for (j=0 ; j<portallongs ; j++)
+ ((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
+ pnum = p - portals;
+ portalvector[pnum>>3] |= 1<<(pnum&7);
+ }
+
+ // convert portal bits to leaf bits
+ numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
+
+ if (uncompressed[leafnum>>3] & (1<<(leafnum&7)))
+ printf ("WARNING: Leaf portals saw into leaf\n");
+
+ uncompressed[leafnum>>3] |= (1<<(leafnum&7));
+ numvis++; // count the leaf itself
+
+ // save uncompressed for PHS calculation
+ memcpy (uncompressedvis + leafnum*leafbytes, uncompressed, leafbytes);
+
+//
+// compress the bit string
+//
+ qprintf ("cluster %4i : %4i visible\n", leafnum, numvis);
+ totalvis += numvis;
+
+ i = CompressVis (uncompressed, compressed);
+
+ dest = vismap_p;
+ vismap_p += i;
+
+ if (vismap_p > vismap_end)
+ Error ("Vismap expansion overflow");
+
+ dvis->bitofs[leafnum][DVIS_PVS] = dest-vismap;
+
+ memcpy (dest, compressed, i);
+}
+
+
+/*
+==================
+CalcPortalVis
+==================
+*/
+void CalcPortalVis (void)
+{
+ int i;
+
+// fastvis just uses mightsee for a very loose bound
+ if (fastvis)
+ {
+ for (i=0 ; i<numportals*2 ; i++)
+ {
+ portals[i].portalvis = portals[i].portalflood;
+ portals[i].status = stat_done;
+ }
+ return;
+ }
+
+ RunThreadsOnIndividual (numportals*2, true, PortalFlow);
+
+}
+
+
+/*
+==================
+CalcVis
+==================
+*/
+void CalcVis (void)
+{
+ int i;
+
+ RunThreadsOnIndividual (numportals*2, true, BasePortalVis);
+
+// RunThreadsOnIndividual (numportals*2, true, BetterPortalVis);
+
+ SortPortals ();
+
+ CalcPortalVis ();
+
+//
+// assemble the leaf vis lists by oring and compressing the portal lists
+//
+ for (i=0 ; i<portalclusters ; i++)
+ ClusterMerge (i);
+
+ printf ("Average clusters visible: %i\n", totalvis / portalclusters);
+}
+
+
+void SetPortalSphere (portal_t *p)
+{
+ int i;
+ vec3_t total, dist;
+ winding_t *w;
+ float r, bestr;
+
+ w = p->winding;
+ VectorCopy (vec3_origin, total);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorAdd (total, w->points[i], total);
+ }
+
+ for (i=0 ; i<3 ; i++)
+ total[i] /= w->numpoints;
+
+ bestr = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorSubtract (w->points[i], total, dist);
+ r = VectorLength (dist);
+ if (r > bestr)
+ bestr = r;
+ }
+ VectorCopy (total, p->origin);
+ p->radius = bestr;
+}
+
+/*
+============
+LoadPortals
+============
+*/
+void LoadPortals (char *name)
+{
+ int i, j;
+ portal_t *p;
+ leaf_t *l;
+ char magic[80];
+ FILE *f;
+ int numpoints;
+ winding_t *w;
+ int leafnums[2];
+ plane_t plane;
+
+ if (!strcmp(name,"-"))
+ f = stdin;
+ else
+ {
+ f = fopen(name, "r");
+ if (!f)
+ Error ("LoadPortals: couldn't read %s\n",name);
+ }
+
+ if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalclusters, &numportals) != 3)
+ Error ("LoadPortals: failed to read header");
+ if (strcmp(magic,PORTALFILE))
+ Error ("LoadPortals: not a portal file");
+
+ printf ("%4i portalclusters\n", portalclusters);
+ printf ("%4i numportals\n", numportals);
+
+ // these counts should take advantage of 64 bit systems automatically
+ leafbytes = ((portalclusters+63)&~63)>>3;
+ leaflongs = leafbytes/sizeof(long);
+
+ portalbytes = ((numportals*2+63)&~63)>>3;
+ portallongs = portalbytes/sizeof(long);
+
+// each file portal is split into two memory portals
+ portals = malloc(2*numportals*sizeof(portal_t));
+ memset (portals, 0, 2*numportals*sizeof(portal_t));
+
+ leafs = malloc(portalclusters*sizeof(leaf_t));
+ memset (leafs, 0, portalclusters*sizeof(leaf_t));
+
+ originalvismapsize = portalclusters*leafbytes;
+ uncompressedvis = malloc(originalvismapsize);
+
+ vismap = vismap_p = dvisdata;
+ dvis->numclusters = portalclusters;
+ vismap_p = (byte *)&dvis->bitofs[portalclusters];
+
+ vismap_end = vismap + MAX_MAP_VISIBILITY;
+
+ for (i=0, p=portals ; i<numportals ; i++)
+ {
+ if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1])
+ != 3)
+ Error ("LoadPortals: reading portal %i", i);
+ if (numpoints > MAX_POINTS_ON_WINDING)
+ Error ("LoadPortals: portal %i has too many points", i);
+ if ( (unsigned)leafnums[0] > portalclusters
+ || (unsigned)leafnums[1] > portalclusters)
+ Error ("LoadPortals: reading portal %i", i);
+
+ w = p->winding = NewWinding (numpoints);
+ w->original = true;
+ w->numpoints = numpoints;
+
+ for (j=0 ; j<numpoints ; j++)
+ {
+ double v[3];
+ int k;
+
+ // scanf into double, then assign to vec_t
+ // so we don't care what size vec_t is
+ if (fscanf (f, "(%lf %lf %lf ) "
+ , &v[0], &v[1], &v[2]) != 3)
+ Error ("LoadPortals: reading portal %i", i);
+ for (k=0 ; k<3 ; k++)
+ w->points[j][k] = v[k];
+ }
+ fscanf (f, "\n");
+
+ // calc plane
+ PlaneFromWinding (w, &plane);
+
+ // create forward portal
+ l = &leafs[leafnums[0]];
+ if (l->numportals == MAX_PORTALS_ON_LEAF)
+ Error ("Leaf with too many portals");
+ l->portals[l->numportals] = p;
+ l->numportals++;
+
+ p->winding = w;
+ VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
+ p->plane.dist = -plane.dist;
+ p->leaf = leafnums[1];
+ SetPortalSphere (p);
+ p++;
+
+ // create backwards portal
+ l = &leafs[leafnums[1]];
+ if (l->numportals == MAX_PORTALS_ON_LEAF)
+ Error ("Leaf with too many portals");
+ l->portals[l->numportals] = p;
+ l->numportals++;
+
+ p->winding = NewWinding(w->numpoints);
+ p->winding->numpoints = w->numpoints;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
+ }
+
+ p->plane = plane;
+ p->leaf = leafnums[0];
+ SetPortalSphere (p);
+ p++;
+
+ }
+
+ fclose (f);
+}
+
+
+/*
+================
+CalcPHS
+
+Calculate the PHS (Potentially Hearable Set)
+by ORing together all the PVS visible from a leaf
+================
+*/
+void CalcPHS (void)
+{
+ int i, j, k, l, index;
+ int bitbyte;
+ long *dest, *src;
+ byte *scan;
+ int count;
+ byte uncompressed[MAX_MAP_LEAFS/8];
+ byte compressed[MAX_MAP_LEAFS/8];
+
+ printf ("Building PHS...\n");
+
+ count = 0;
+ for (i=0 ; i<portalclusters ; i++)
+ {
+ scan = uncompressedvis + i*leafbytes;
+ memcpy (uncompressed, scan, leafbytes);
+ for (j=0 ; j<leafbytes ; j++)
+ {
+ bitbyte = scan[j];
+ if (!bitbyte)
+ continue;
+ for (k=0 ; k<8 ; k++)
+ {
+ if (! (bitbyte & (1<<k)) )
+ continue;
+ // OR this pvs row into the phs
+ index = ((j<<3)+k);
+ if (index >= portalclusters)
+ Error ("Bad bit in PVS"); // pad bits should be 0
+ src = (long *)(uncompressedvis + index*leafbytes);
+ dest = (long *)uncompressed;
+ for (l=0 ; l<leaflongs ; l++)
+ ((long *)uncompressed)[l] |= src[l];
+ }
+ }
+ for (j=0 ; j<portalclusters ; j++)
+ if (uncompressed[j>>3] & (1<<(j&7)) )
+ count++;
+
+ //
+ // compress the bit string
+ //
+ j = CompressVis (uncompressed, compressed);
+
+ dest = (long *)vismap_p;
+ vismap_p += j;
+
+ if (vismap_p > vismap_end)
+ Error ("Vismap expansion overflow");
+
+ dvis->bitofs[i][DVIS_PHS] = (byte *)dest-vismap;
+
+ memcpy (dest, compressed, j);
+ }
+
+ printf ("Average clusters hearable: %i\n", count/portalclusters);
+}
+
+/*
+===========
+main
+===========
+*/
+int main (int argc, char **argv)
+{
+ char portalfile[1024];
+ char source[1024];
+ char name[1024];
+ int i;
+ double start, end;
+
+ printf ("---- vis ----\n");
+
+ verbose = false;
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-fast"))
+ {
+ printf ("fastvis = true\n");
+ fastvis = true;
+ }
+ else if (!strcmp(argv[i], "-level"))
+ {
+ testlevel = atoi(argv[i+1]);
+ printf ("testlevel = %i\n", testlevel);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-v"))
+ {
+ printf ("verbose = true\n");
+ verbose = true;
+ }
+ else if (!strcmp (argv[i],"-nosort"))
+ {
+ printf ("nosort = true\n");
+ nosort = true;
+ }
+ else if (!strcmp (argv[i],"-tmpin"))
+ strcpy (inbase, "/tmp");
+ else if (!strcmp (argv[i],"-tmpout"))
+ strcpy (outbase, "/tmp");
+ else if (argv[i][0] == '-')
+ Error ("Unknown option \"%s\"", argv[i]);
+ else
+ break;
+ }
+
+ if (i != argc - 1)
+ Error ("usage: vis [-threads #] [-level 0-4] [-fast] [-v] bspfile");
+
+ start = I_FloatTime ();
+
+ ThreadSetDefault ();
+
+ SetQdirFromPath (argv[i]);
+ strcpy (source, ExpandArg(argv[i]));
+ StripExtension (source);
+ DefaultExtension (source, ".bsp");
+
+ sprintf (name, "%s%s", inbase, source);
+ printf ("reading %s\n", name);
+ LoadBSPFile (name);
+ if (numnodes == 0 || numfaces == 0)
+ Error ("Empty map");
+
+ sprintf (portalfile, "%s%s", inbase, ExpandArg(argv[i]));
+ StripExtension (portalfile);
+ strcat (portalfile, ".prt");
+
+ printf ("reading %s\n", portalfile);
+ LoadPortals (portalfile);
+
+ CalcVis ();
+
+ CalcPHS ();
+
+ visdatasize = vismap_p - dvisdata;
+ printf ("visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize*2);
+
+ sprintf (name, "%s%s", outbase, source);
+ printf ("writing %s\n", name);
+ WriteBSPFile (name);
+
+ end = I_FloatTime ();
+ printf ("%5.1f seconds elapsed\n", end-start);
+
+ return 0;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+#define MAX_PORTALS 32768
+
+#define PORTALFILE "PRT1"
+
+#define ON_EPSILON 0.1
+
+typedef struct
+{
+ vec3_t normal;
+ float dist;
+} plane_t;
+
+#define MAX_POINTS_ON_WINDING 64
+#define MAX_POINTS_ON_FIXED_WINDING 12
+
+typedef struct
+{
+ qboolean original; // don't free, it's part of the portal
+ int numpoints;
+ vec3_t points[MAX_POINTS_ON_FIXED_WINDING]; // variable sized
+} winding_t;
+
+winding_t *NewWinding (int points);
+void FreeWinding (winding_t *w);
+winding_t *CopyWinding (winding_t *w);
+
+
+typedef enum {stat_none, stat_working, stat_done} vstatus_t;
+typedef struct
+{
+ plane_t plane; // normal pointing into neighbor
+ int leaf; // neighbor
+
+ vec3_t origin; // for fast clip testing
+ float radius;
+
+ winding_t *winding;
+ vstatus_t status;
+ byte *portalfront; // [portals], preliminary
+ byte *portalflood; // [portals], intermediate
+ byte *portalvis; // [portals], final
+
+ int nummightsee; // bit count on portalflood for sort
+} portal_t;
+
+typedef struct seperating_plane_s
+{
+ struct seperating_plane_s *next;
+ plane_t plane; // from portal is on positive side
+} sep_t;
+
+
+typedef struct passage_s
+{
+ struct passage_s *next;
+ int from, to; // leaf numbers
+ sep_t *planes;
+} passage_t;
+
+#define MAX_PORTALS_ON_LEAF 128
+typedef struct leaf_s
+{
+ int numportals;
+ passage_t *passages;
+ portal_t *portals[MAX_PORTALS_ON_LEAF];
+} leaf_t;
+
+
+typedef struct pstack_s
+{
+ byte mightsee[MAX_PORTALS/8]; // bit string
+ struct pstack_s *next;
+ leaf_t *leaf;
+ portal_t *portal; // portal exiting
+ winding_t *source;
+ winding_t *pass;
+
+ winding_t windings[3]; // source, pass, temp in any order
+ int freewindings[3];
+
+ plane_t portalplane;
+} pstack_t;
+
+typedef struct
+{
+ portal_t *base;
+ int c_chains;
+ pstack_t pstack_head;
+} threaddata_t;
+
+
+
+extern int numportals;
+extern int portalclusters;
+
+extern portal_t *portals;
+extern leaf_t *leafs;
+
+extern int c_portaltest, c_portalpass, c_portalcheck;
+extern int c_portalskip, c_leafskip;
+extern int c_vistest, c_mighttest;
+extern int c_chains;
+
+extern byte *vismap, *vismap_p, *vismap_end; // past visfile
+
+extern int testlevel;
+
+extern byte *uncompressed;
+
+extern int leafbytes, leaflongs;
+extern int portalbytes, portallongs;
+
+
+void LeafFlow (int leafnum);
+
+
+void BasePortalVis (int portalnum);
+void BetterPortalVis (int portalnum);
+void PortalFlow (int portalnum);
+
+extern portal_t *sorted_portals[MAX_MAP_PORTALS*2];
+
+int CountBits (byte *bits, int numbits);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "scriplib.h"
+
+void GetLeafNums (void);
+
+//=============================================================================
+
+int nummodels;
+dmodel_t dmodels[MAX_MAP_MODELS];
+
+int visdatasize;
+byte dvisdata[MAX_MAP_VISIBILITY];
+dvis_t *dvis = (dvis_t *)dvisdata;
+
+int lightdatasize;
+byte dlightdata[MAX_MAP_LIGHTING];
+
+int entdatasize;
+char dentdata[MAX_MAP_ENTSTRING];
+
+int numleafs;
+dleaf_t dleafs[MAX_MAP_LEAFS];
+
+int numplanes;
+dplane_t dplanes[MAX_MAP_PLANES];
+
+int numvertexes;
+dvertex_t dvertexes[MAX_MAP_VERTS];
+
+int numnodes;
+dnode_t dnodes[MAX_MAP_NODES];
+
+int numtexinfo;
+texinfo_t texinfo[MAX_MAP_TEXINFO];
+
+int numfaces;
+dface_t dfaces[MAX_MAP_FACES];
+
+int numedges;
+dedge_t dedges[MAX_MAP_EDGES];
+
+int numleaffaces;
+unsigned short dleaffaces[MAX_MAP_LEAFFACES];
+
+int numleafbrushes;
+unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+int numsurfedges;
+int dsurfedges[MAX_MAP_SURFEDGES];
+
+int numbrushes;
+dbrush_t dbrushes[MAX_MAP_BRUSHES];
+
+int numbrushsides;
+dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
+
+int numareas;
+darea_t dareas[MAX_MAP_AREAS];
+
+int numareaportals;
+dareaportal_t dareaportals[MAX_MAP_AREAPORTALS];
+
+byte dpop[256];
+
+/*
+===============
+CompressVis
+
+===============
+*/
+int CompressVis (byte *vis, byte *dest)
+{
+ int j;
+ int rep;
+ int visrow;
+ byte *dest_p;
+
+ dest_p = dest;
+// visrow = (r_numvisleafs + 7)>>3;
+ visrow = (dvis->numclusters + 7)>>3;
+
+ for (j=0 ; j<visrow ; j++)
+ {
+ *dest_p++ = vis[j];
+ if (vis[j])
+ continue;
+
+ rep = 1;
+ for ( j++; j<visrow ; j++)
+ if (vis[j] || rep == 255)
+ break;
+ else
+ rep++;
+ *dest_p++ = rep;
+ j--;
+ }
+
+ return dest_p - dest;
+}
+
+
+/*
+===================
+DecompressVis
+===================
+*/
+void DecompressVis (byte *in, byte *decompressed)
+{
+ int c;
+ byte *out;
+ int row;
+
+// row = (r_numvisleafs+7)>>3;
+ row = (dvis->numclusters+7)>>3;
+ out = decompressed;
+
+ do
+ {
+ if (*in)
+ {
+ *out++ = *in++;
+ continue;
+ }
+
+ c = in[1];
+ if (!c)
+ Error ("DecompressVis: 0 repeat");
+ in += 2;
+ while (c)
+ {
+ *out++ = 0;
+ c--;
+ }
+ } while (out - decompressed < row);
+}
+
+//=============================================================================
+
+/*
+=============
+SwapBSPFile
+
+Byte swaps all data in a bsp file.
+=============
+*/
+void SwapBSPFile (qboolean todisk)
+{
+ int i, j;
+ dmodel_t *d;
+
+
+// models
+ for (i=0 ; i<nummodels ; i++)
+ {
+ d = &dmodels[i];
+
+ d->firstface = LittleLong (d->firstface);
+ d->numfaces = LittleLong (d->numfaces);
+ d->headnode = LittleLong (d->headnode);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ d->mins[j] = LittleFloat(d->mins[j]);
+ d->maxs[j] = LittleFloat(d->maxs[j]);
+ d->origin[j] = LittleFloat(d->origin[j]);
+ }
+ }
+
+//
+// vertexes
+//
+ for (i=0 ; i<numvertexes ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ dvertexes[i].point[j] = LittleFloat (dvertexes[i].point[j]);
+ }
+
+//
+// planes
+//
+ for (i=0 ; i<numplanes ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ dplanes[i].normal[j] = LittleFloat (dplanes[i].normal[j]);
+ dplanes[i].dist = LittleFloat (dplanes[i].dist);
+ dplanes[i].type = LittleLong (dplanes[i].type);
+ }
+
+//
+// texinfos
+//
+ for (i=0 ; i<numtexinfo ; i++)
+ {
+ for (j=0 ; j<8 ; j++)
+ texinfo[i].vecs[0][j] = LittleFloat (texinfo[i].vecs[0][j]);
+ texinfo[i].flags = LittleLong (texinfo[i].flags);
+ texinfo[i].value = LittleLong (texinfo[i].value);
+ texinfo[i].nexttexinfo = LittleLong (texinfo[i].nexttexinfo);
+ }
+
+//
+// faces
+//
+ for (i=0 ; i<numfaces ; i++)
+ {
+ dfaces[i].texinfo = LittleShort (dfaces[i].texinfo);
+ dfaces[i].planenum = LittleShort (dfaces[i].planenum);
+ dfaces[i].side = LittleShort (dfaces[i].side);
+ dfaces[i].lightofs = LittleLong (dfaces[i].lightofs);
+ dfaces[i].firstedge = LittleLong (dfaces[i].firstedge);
+ dfaces[i].numedges = LittleShort (dfaces[i].numedges);
+ }
+
+//
+// nodes
+//
+ for (i=0 ; i<numnodes ; i++)
+ {
+ dnodes[i].planenum = LittleLong (dnodes[i].planenum);
+ for (j=0 ; j<3 ; j++)
+ {
+ dnodes[i].mins[j] = LittleShort (dnodes[i].mins[j]);
+ dnodes[i].maxs[j] = LittleShort (dnodes[i].maxs[j]);
+ }
+ dnodes[i].children[0] = LittleLong (dnodes[i].children[0]);
+ dnodes[i].children[1] = LittleLong (dnodes[i].children[1]);
+ dnodes[i].firstface = LittleShort (dnodes[i].firstface);
+ dnodes[i].numfaces = LittleShort (dnodes[i].numfaces);
+ }
+
+//
+// leafs
+//
+ for (i=0 ; i<numleafs ; i++)
+ {
+ dleafs[i].contents = LittleLong (dleafs[i].contents);
+ dleafs[i].cluster = LittleShort (dleafs[i].cluster);
+ dleafs[i].area = LittleShort (dleafs[i].area);
+ for (j=0 ; j<3 ; j++)
+ {
+ dleafs[i].mins[j] = LittleShort (dleafs[i].mins[j]);
+ dleafs[i].maxs[j] = LittleShort (dleafs[i].maxs[j]);
+ }
+
+ dleafs[i].firstleafface = LittleShort (dleafs[i].firstleafface);
+ dleafs[i].numleaffaces = LittleShort (dleafs[i].numleaffaces);
+ dleafs[i].firstleafbrush = LittleShort (dleafs[i].firstleafbrush);
+ dleafs[i].numleafbrushes = LittleShort (dleafs[i].numleafbrushes);
+ }
+
+//
+// leaffaces
+//
+ for (i=0 ; i<numleaffaces ; i++)
+ dleaffaces[i] = LittleShort (dleaffaces[i]);
+
+//
+// leafbrushes
+//
+ for (i=0 ; i<numleafbrushes ; i++)
+ dleafbrushes[i] = LittleShort (dleafbrushes[i]);
+
+//
+// surfedges
+//
+ for (i=0 ; i<numsurfedges ; i++)
+ dsurfedges[i] = LittleLong (dsurfedges[i]);
+
+//
+// edges
+//
+ for (i=0 ; i<numedges ; i++)
+ {
+ dedges[i].v[0] = LittleShort (dedges[i].v[0]);
+ dedges[i].v[1] = LittleShort (dedges[i].v[1]);
+ }
+
+//
+// brushes
+//
+ for (i=0 ; i<numbrushes ; i++)
+ {
+ dbrushes[i].firstside = LittleLong (dbrushes[i].firstside);
+ dbrushes[i].numsides = LittleLong (dbrushes[i].numsides);
+ dbrushes[i].contents = LittleLong (dbrushes[i].contents);
+ }
+
+//
+// areas
+//
+ for (i=0 ; i<numareas ; i++)
+ {
+ dareas[i].numareaportals = LittleLong (dareas[i].numareaportals);
+ dareas[i].firstareaportal = LittleLong (dareas[i].firstareaportal);
+ }
+
+//
+// areasportals
+//
+ for (i=0 ; i<numareaportals ; i++)
+ {
+ dareaportals[i].portalnum = LittleLong (dareaportals[i].portalnum);
+ dareaportals[i].otherarea = LittleLong (dareaportals[i].otherarea);
+ }
+
+//
+// brushsides
+//
+ for (i=0 ; i<numbrushsides ; i++)
+ {
+ dbrushsides[i].planenum = LittleShort (dbrushsides[i].planenum);
+ dbrushsides[i].texinfo = LittleShort (dbrushsides[i].texinfo);
+ }
+
+//
+// visibility
+//
+ if (todisk)
+ j = dvis->numclusters;
+ else
+ j = LittleLong(dvis->numclusters);
+ dvis->numclusters = LittleLong (dvis->numclusters);
+ for (i=0 ; i<j ; i++)
+ {
+ dvis->bitofs[i][0] = LittleLong (dvis->bitofs[i][0]);
+ dvis->bitofs[i][1] = LittleLong (dvis->bitofs[i][1]);
+ }
+}
+
+
+dheader_t *header;
+
+int CopyLump (int lump, void *dest, int size)
+{
+ int length, ofs;
+
+ length = header->lumps[lump].filelen;
+ ofs = header->lumps[lump].fileofs;
+
+ if (length % size)
+ Error ("LoadBSPFile: odd lump size");
+
+ memcpy (dest, (byte *)header + ofs, length);
+
+ return length / size;
+}
+
+/*
+=============
+LoadBSPFile
+=============
+*/
+void LoadBSPFile (char *filename)
+{
+ int i;
+
+//
+// load the file header
+//
+ LoadFile (filename, (void **)&header);
+
+// swap the header
+ for (i=0 ; i< sizeof(dheader_t)/4 ; i++)
+ ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+ if (header->ident != IDBSPHEADER)
+ Error ("%s is not a IBSP file", filename);
+ if (header->version != BSPVERSION)
+ Error ("%s is version %i, not %i", filename, header->version, BSPVERSION);
+
+ nummodels = CopyLump (LUMP_MODELS, dmodels, sizeof(dmodel_t));
+ numvertexes = CopyLump (LUMP_VERTEXES, dvertexes, sizeof(dvertex_t));
+ numplanes = CopyLump (LUMP_PLANES, dplanes, sizeof(dplane_t));
+ numleafs = CopyLump (LUMP_LEAFS, dleafs, sizeof(dleaf_t));
+ numnodes = CopyLump (LUMP_NODES, dnodes, sizeof(dnode_t));
+ numtexinfo = CopyLump (LUMP_TEXINFO, texinfo, sizeof(texinfo_t));
+ numfaces = CopyLump (LUMP_FACES, dfaces, sizeof(dface_t));
+ numleaffaces = CopyLump (LUMP_LEAFFACES, dleaffaces, sizeof(dleaffaces[0]));
+ numleafbrushes = CopyLump (LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0]));
+ numsurfedges = CopyLump (LUMP_SURFEDGES, dsurfedges, sizeof(dsurfedges[0]));
+ numedges = CopyLump (LUMP_EDGES, dedges, sizeof(dedge_t));
+ numbrushes = CopyLump (LUMP_BRUSHES, dbrushes, sizeof(dbrush_t));
+ numbrushsides = CopyLump (LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t));
+ numareas = CopyLump (LUMP_AREAS, dareas, sizeof(darea_t));
+ numareaportals = CopyLump (LUMP_AREAPORTALS, dareaportals, sizeof(dareaportal_t));
+
+ visdatasize = CopyLump (LUMP_VISIBILITY, dvisdata, 1);
+ lightdatasize = CopyLump (LUMP_LIGHTING, dlightdata, 1);
+ entdatasize = CopyLump (LUMP_ENTITIES, dentdata, 1);
+
+ CopyLump (LUMP_POP, dpop, 1);
+
+ free (header); // everything has been copied out
+
+//
+// swap everything
+//
+ SwapBSPFile (false);
+}
+
+
+/*
+=============
+LoadBSPFileTexinfo
+
+Only loads the texinfo lump, so qdata can scan for textures
+=============
+*/
+void LoadBSPFileTexinfo (char *filename)
+{
+ int i;
+ FILE *f;
+ int length, ofs;
+
+ header = malloc(sizeof(dheader_t));
+
+ f = fopen (filename, "rb");
+ fread (header, sizeof(dheader_t), 1, f);
+
+// swap the header
+ for (i=0 ; i< sizeof(dheader_t)/4 ; i++)
+ ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+ if (header->ident != IDBSPHEADER)
+ Error ("%s is not a IBSP file", filename);
+ if (header->version != BSPVERSION)
+ Error ("%s is version %i, not %i", filename, header->version, BSPVERSION);
+
+
+ length = header->lumps[LUMP_TEXINFO].filelen;
+ ofs = header->lumps[LUMP_TEXINFO].fileofs;
+
+ fseek (f, ofs, SEEK_SET);
+ fread (texinfo, length, 1, f);
+ fclose (f);
+
+ numtexinfo = length / sizeof(texinfo_t);
+
+ free (header); // everything has been copied out
+
+ SwapBSPFile (false);
+}
+
+
+//============================================================================
+
+FILE *wadfile;
+dheader_t outheader;
+
+void AddLump (int lumpnum, void *data, int len)
+{
+ lump_t *lump;
+
+ lump = &header->lumps[lumpnum];
+
+ lump->fileofs = LittleLong( ftell(wadfile) );
+ lump->filelen = LittleLong(len);
+ SafeWrite (wadfile, data, (len+3)&~3);
+}
+
+/*
+=============
+WriteBSPFile
+
+Swaps the bsp file in place, so it should not be referenced again
+=============
+*/
+void WriteBSPFile (char *filename)
+{
+ header = &outheader;
+ memset (header, 0, sizeof(dheader_t));
+
+ SwapBSPFile (true);
+
+ header->ident = LittleLong (IDBSPHEADER);
+ header->version = LittleLong (BSPVERSION);
+
+ wadfile = SafeOpenWrite (filename);
+ SafeWrite (wadfile, header, sizeof(dheader_t)); // overwritten later
+
+ AddLump (LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t));
+ AddLump (LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t));
+ AddLump (LUMP_VERTEXES, dvertexes, numvertexes*sizeof(dvertex_t));
+ AddLump (LUMP_NODES, dnodes, numnodes*sizeof(dnode_t));
+ AddLump (LUMP_TEXINFO, texinfo, numtexinfo*sizeof(texinfo_t));
+ AddLump (LUMP_FACES, dfaces, numfaces*sizeof(dface_t));
+ AddLump (LUMP_BRUSHES, dbrushes, numbrushes*sizeof(dbrush_t));
+ AddLump (LUMP_BRUSHSIDES, dbrushsides, numbrushsides*sizeof(dbrushside_t));
+ AddLump (LUMP_LEAFFACES, dleaffaces, numleaffaces*sizeof(dleaffaces[0]));
+ AddLump (LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes*sizeof(dleafbrushes[0]));
+ AddLump (LUMP_SURFEDGES, dsurfedges, numsurfedges*sizeof(dsurfedges[0]));
+ AddLump (LUMP_EDGES, dedges, numedges*sizeof(dedge_t));
+ AddLump (LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t));
+ AddLump (LUMP_AREAS, dareas, numareas*sizeof(darea_t));
+ AddLump (LUMP_AREAPORTALS, dareaportals, numareaportals*sizeof(dareaportal_t));
+
+ AddLump (LUMP_LIGHTING, dlightdata, lightdatasize);
+ AddLump (LUMP_VISIBILITY, dvisdata, visdatasize);
+ AddLump (LUMP_ENTITIES, dentdata, entdatasize);
+ AddLump (LUMP_POP, dpop, sizeof(dpop));
+
+ fseek (wadfile, 0, SEEK_SET);
+ SafeWrite (wadfile, header, sizeof(dheader_t));
+ fclose (wadfile);
+}
+
+//============================================================================
+
+/*
+=============
+PrintBSPFileSizes
+
+Dumps info about current file
+=============
+*/
+void PrintBSPFileSizes (void)
+{
+ if (!num_entities)
+ ParseEntities ();
+
+ printf ("%5i models %7i\n"
+ ,nummodels, (int)(nummodels*sizeof(dmodel_t)));
+ printf ("%5i brushes %7i\n"
+ ,numbrushes, (int)(numbrushes*sizeof(dbrush_t)));
+ printf ("%5i brushsides %7i\n"
+ ,numbrushsides, (int)(numbrushsides*sizeof(dbrushside_t)));
+ printf ("%5i planes %7i\n"
+ ,numplanes, (int)(numplanes*sizeof(dplane_t)));
+ printf ("%5i texinfo %7i\n"
+ ,numtexinfo, (int)(numtexinfo*sizeof(texinfo_t)));
+ printf ("%5i entdata %7i\n", num_entities, entdatasize);
+
+ printf ("\n");
+
+ printf ("%5i vertexes %7i\n"
+ ,numvertexes, (int)(numvertexes*sizeof(dvertex_t)));
+ printf ("%5i nodes %7i\n"
+ ,numnodes, (int)(numnodes*sizeof(dnode_t)));
+ printf ("%5i faces %7i\n"
+ ,numfaces, (int)(numfaces*sizeof(dface_t)));
+ printf ("%5i leafs %7i\n"
+ ,numleafs, (int)(numleafs*sizeof(dleaf_t)));
+ printf ("%5i leaffaces %7i\n"
+ ,numleaffaces, (int)(numleaffaces*sizeof(dleaffaces[0])));
+ printf ("%5i leafbrushes %7i\n"
+ ,numleafbrushes, (int)(numleafbrushes*sizeof(dleafbrushes[0])));
+ printf ("%5i surfedges %7i\n"
+ ,numsurfedges, (int)(numsurfedges*sizeof(dsurfedges[0])));
+ printf ("%5i edges %7i\n"
+ ,numedges, (int)(numedges*sizeof(dedge_t)));
+ printf (" lightdata %7i\n", lightdatasize);
+ printf (" visdata %7i\n", visdatasize);
+}
+
+
+//============================================
+
+int num_entities;
+entity_t entities[MAX_MAP_ENTITIES];
+
+void StripTrailing (char *e)
+{
+ char *s;
+
+ s = e + strlen(e)-1;
+ while (s >= e && *s <= 32)
+ {
+ *s = 0;
+ s--;
+ }
+}
+
+/*
+=================
+ParseEpair
+=================
+*/
+epair_t *ParseEpair (void)
+{
+ epair_t *e;
+
+ e = malloc (sizeof(epair_t));
+ memset (e, 0, sizeof(epair_t));
+
+ if (strlen(token) >= MAX_KEY-1)
+ Error ("ParseEpar: token too long");
+ e->key = copystring(token);
+ GetToken (false);
+ if (strlen(token) >= MAX_VALUE-1)
+ Error ("ParseEpar: token too long");
+ e->value = copystring(token);
+
+ // strip trailing spaces
+ StripTrailing (e->key);
+ StripTrailing (e->value);
+
+ return e;
+}
+
+
+/*
+================
+ParseEntity
+================
+*/
+qboolean ParseEntity (void)
+{
+ epair_t *e;
+ entity_t *mapent;
+
+ if (!GetToken (true))
+ return false;
+
+ if (strcmp (token, "{") )
+ Error ("ParseEntity: { not found");
+
+ if (num_entities == MAX_MAP_ENTITIES)
+ Error ("num_entities == MAX_MAP_ENTITIES");
+
+ mapent = &entities[num_entities];
+ num_entities++;
+
+ do
+ {
+ if (!GetToken (true))
+ Error ("ParseEntity: EOF without closing brace");
+ if (!strcmp (token, "}") )
+ break;
+ e = ParseEpair ();
+ e->next = mapent->epairs;
+ mapent->epairs = e;
+ } while (1);
+
+ return true;
+}
+
+/*
+================
+ParseEntities
+
+Parses the dentdata string into entities
+================
+*/
+void ParseEntities (void)
+{
+ num_entities = 0;
+ ParseFromMemory (dentdata, entdatasize);
+
+ while (ParseEntity ())
+ {
+ }
+}
+
+
+/*
+================
+UnparseEntities
+
+Generates the dentdata string from all the entities
+================
+*/
+void UnparseEntities (void)
+{
+ char *buf, *end;
+ epair_t *ep;
+ char line[2048];
+ int i;
+ char key[1024], value[1024];
+
+ buf = dentdata;
+ end = buf;
+ *end = 0;
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ ep = entities[i].epairs;
+ if (!ep)
+ continue; // ent got removed
+
+ strcat (end,"{\n");
+ end += 2;
+
+ for (ep = entities[i].epairs ; ep ; ep=ep->next)
+ {
+ strcpy (key, ep->key);
+ StripTrailing (key);
+ strcpy (value, ep->value);
+ StripTrailing (value);
+
+ sprintf (line, "\"%s\" \"%s\"\n", key, value);
+ strcat (end, line);
+ end += strlen(line);
+ }
+ strcat (end,"}\n");
+ end += 2;
+
+ if (end > buf + MAX_MAP_ENTSTRING)
+ Error ("Entity text too long");
+ }
+ entdatasize = end - buf + 1;
+}
+
+void PrintEntity (entity_t *ent)
+{
+ epair_t *ep;
+
+ printf ("------- entity %p -------\n", ent);
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ {
+ printf ("%s = %s\n", ep->key, ep->value);
+ }
+
+}
+
+void SetKeyValue (entity_t *ent, char *key, char *value)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ {
+ free (ep->value);
+ ep->value = copystring(value);
+ return;
+ }
+ ep = malloc (sizeof(*ep));
+ ep->next = ent->epairs;
+ ent->epairs = ep;
+ ep->key = copystring(key);
+ ep->value = copystring(value);
+}
+
+char *ValueForKey (entity_t *ent, char *key)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ return ep->value;
+ return "";
+}
+
+vec_t FloatForKey (entity_t *ent, char *key)
+{
+ char *k;
+
+ k = ValueForKey (ent, key);
+ return atof(k);
+}
+
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
+{
+ char *k;
+ double v1, v2, v3;
+
+ k = ValueForKey (ent, key);
+// scanf into doubles, then assign, so it is vec_t size independent
+ v1 = v2 = v3 = 0;
+ sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
+ vec[0] = v1;
+ vec[1] = v2;
+ vec[2] = v3;
+}
+
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qfiles.h"
+
+
+extern int nummodels;
+extern dmodel_t dmodels[MAX_MAP_MODELS];
+
+extern int visdatasize;
+extern byte dvisdata[MAX_MAP_VISIBILITY];
+extern dvis_t *dvis;
+
+extern int lightdatasize;
+extern byte dlightdata[MAX_MAP_LIGHTING];
+
+extern int entdatasize;
+extern char dentdata[MAX_MAP_ENTSTRING];
+
+extern int numleafs;
+extern dleaf_t dleafs[MAX_MAP_LEAFS];
+
+extern int numplanes;
+extern dplane_t dplanes[MAX_MAP_PLANES];
+
+extern int numvertexes;
+extern dvertex_t dvertexes[MAX_MAP_VERTS];
+
+extern int numnodes;
+extern dnode_t dnodes[MAX_MAP_NODES];
+
+extern int numtexinfo;
+extern texinfo_t texinfo[MAX_MAP_TEXINFO];
+
+extern int numfaces;
+extern dface_t dfaces[MAX_MAP_FACES];
+
+extern int numedges;
+extern dedge_t dedges[MAX_MAP_EDGES];
+
+extern int numleaffaces;
+extern unsigned short dleaffaces[MAX_MAP_LEAFFACES];
+
+extern int numleafbrushes;
+extern unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+extern int numsurfedges;
+extern int dsurfedges[MAX_MAP_SURFEDGES];
+
+extern int numareas;
+extern darea_t dareas[MAX_MAP_AREAS];
+
+extern int numareaportals;
+extern dareaportal_t dareaportals[MAX_MAP_AREAPORTALS];
+
+extern int numbrushes;
+extern dbrush_t dbrushes[MAX_MAP_BRUSHES];
+
+extern int numbrushsides;
+extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
+
+extern byte dpop[256];
+
+void DecompressVis (byte *in, byte *decompressed);
+int CompressVis (byte *vis, byte *dest);
+
+void LoadBSPFile (char *filename);
+void LoadBSPFileTexinfo (char *filename); // just for qdata
+void WriteBSPFile (char *filename);
+void PrintBSPFileSizes (void);
+
+//===============
+
+
+typedef struct epair_s
+{
+ struct epair_s *next;
+ char *key;
+ char *value;
+} epair_t;
+
+typedef struct
+{
+ vec3_t origin;
+ int firstbrush;
+ int numbrushes;
+ epair_t *epairs;
+
+// only valid for func_areaportals
+ int areaportalnum;
+ int portalareas[2];
+} entity_t;
+
+extern int num_entities;
+extern entity_t entities[MAX_MAP_ENTITIES];
+
+void ParseEntities (void);
+void UnparseEntities (void);
+
+void SetKeyValue (entity_t *ent, char *key, char *value);
+char *ValueForKey (entity_t *ent, char *key);
+// will return "" if not present
+
+vec_t FloatForKey (entity_t *ent, char *key);
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+epair_t *ParseEpair (void);
+
+void PrintEntity (entity_t *ent);
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// cmdlib.c
+
+#include "cmdlib.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <direct.h>
+#endif
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+
+#define BASEDIRNAME "quake2"
+#define PATHSEPERATOR '/'
+
+// set these before calling CheckParm
+int myargc;
+char **myargv;
+
+char com_token[1024];
+qboolean com_eof;
+
+qboolean archive;
+char archivedir[1024];
+
+
+/*
+===================
+ExpandWildcards
+
+Mimic unix command line expansion
+===================
+*/
+#define MAX_EX_ARGC 1024
+int ex_argc;
+char *ex_argv[MAX_EX_ARGC];
+#ifdef _WIN32
+#include "io.h"
+void ExpandWildcards (int *argc, char ***argv)
+{
+ struct _finddata_t fileinfo;
+ int handle;
+ int i;
+ char filename[1024];
+ char filebase[1024];
+ char *path;
+
+ ex_argc = 0;
+ for (i=0 ; i<*argc ; i++)
+ {
+ path = (*argv)[i];
+ if ( path[0] == '-'
+ || ( !strstr(path, "*") && !strstr(path, "?") ) )
+ {
+ ex_argv[ex_argc++] = path;
+ continue;
+ }
+
+ handle = _findfirst (path, &fileinfo);
+ if (handle == -1)
+ return;
+
+ ExtractFilePath (path, filebase);
+
+ do
+ {
+ sprintf (filename, "%s%s", filebase, fileinfo.name);
+ ex_argv[ex_argc++] = copystring (filename);
+ } while (_findnext( handle, &fileinfo ) != -1);
+
+ _findclose (handle);
+ }
+
+ *argc = ex_argc;
+ *argv = ex_argv;
+}
+#else
+void ExpandWildcards (int *argc, char ***argv)
+{
+}
+#endif
+
+#ifdef WIN_ERROR
+#include <windows.h>
+/*
+=================
+Error
+
+For abnormal program terminations in windowed apps
+=================
+*/
+void Error (char *error, ...)
+{
+ va_list argptr;
+ char text[1024];
+ char text2[1024];
+ int err;
+
+ err = GetLastError ();
+
+ va_start (argptr,error);
+ vsprintf (text, error,argptr);
+ va_end (argptr);
+
+ sprintf (text2, "%s\nGetLastError() = %i", text, err);
+ MessageBox(NULL, text2, "Error", 0 /* MB_OK */ );
+
+ exit (1);
+}
+
+#else
+/*
+=================
+Error
+
+For abnormal program terminations in console apps
+=================
+*/
+void Error (char *error, ...)
+{
+ va_list argptr;
+
+ printf ("\n************ ERROR ************\n");
+
+ va_start (argptr,error);
+ vprintf (error,argptr);
+ va_end (argptr);
+ printf ("\n");
+
+ exit (1);
+}
+#endif
+
+// only printf if in verbose mode
+qboolean verbose = false;
+void qprintf (char *format, ...)
+{
+ va_list argptr;
+
+ if (!verbose)
+ return;
+
+ va_start (argptr,format);
+ vprintf (format,argptr);
+ va_end (argptr);
+}
+
+
+/*
+
+qdir will hold the path up to the quake directory, including the slash
+
+ f:\quake\
+ /raid/quake/
+
+gamedir will hold qdir + the game directory (id1, id2, etc)
+
+ */
+
+char qdir[1024];
+char gamedir[1024];
+
+void SetQdirFromPath (char *path)
+{
+ char temp[1024];
+ char *c;
+ int len;
+
+ if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':'))
+ { // path is partial
+ Q_getwd (temp);
+ strcat (temp, path);
+ path = temp;
+ }
+
+ // search for "quake2" in path
+
+ len = strlen(BASEDIRNAME);
+ for (c=path+strlen(path)-1 ; c != path ; c--)
+ if (!Q_strncasecmp (c, BASEDIRNAME, len))
+ {
+ strncpy (qdir, path, c+len+1-path);
+ qprintf ("qdir: %s\n", qdir);
+ c += len+1;
+ while (*c)
+ {
+ if (*c == '/' || *c == '\\')
+ {
+ strncpy (gamedir, path, c+1-path);
+ qprintf ("gamedir: %s\n", gamedir);
+ return;
+ }
+ c++;
+ }
+ Error ("No gamedir in %s", path);
+ return;
+ }
+ Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path);
+}
+
+char *ExpandArg (char *path)
+{
+ static char full[1024];
+
+ if (path[0] != '/' && path[0] != '\\' && path[1] != ':')
+ {
+ Q_getwd (full);
+ strcat (full, path);
+ }
+ else
+ strcpy (full, path);
+ return full;
+}
+
+char *ExpandPath (char *path)
+{
+ static char full[1024];
+ if (!qdir)
+ Error ("ExpandPath called without qdir set");
+ if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
+ return path;
+ sprintf (full, "%s%s", qdir, path);
+ return full;
+}
+
+char *ExpandPathAndArchive (char *path)
+{
+ char *expanded;
+ char archivename[1024];
+
+ expanded = ExpandPath (path);
+
+ if (archive)
+ {
+ sprintf (archivename, "%s/%s", archivedir, path);
+ QCopyFile (expanded, archivename);
+ }
+ return expanded;
+}
+
+
+char *copystring(char *s)
+{
+ char *b;
+ b = malloc(strlen(s)+1);
+ strcpy (b, s);
+ return b;
+}
+
+
+
+/*
+================
+I_FloatTime
+================
+*/
+double I_FloatTime (void)
+{
+ time_t t;
+
+ time (&t);
+
+ return t;
+#if 0
+// more precise, less portable
+ struct timeval tp;
+ struct timezone tzp;
+ static int secbase;
+
+ gettimeofday(&tp, &tzp);
+
+ if (!secbase)
+ {
+ secbase = tp.tv_sec;
+ return tp.tv_usec/1000000.0;
+ }
+
+ return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
+#endif
+}
+
+void Q_getwd (char *out)
+{
+#ifdef WIN32
+ _getcwd (out, 256);
+ strcat (out, "\\");
+#else
+ getwd (out);
+ strcat (out, "/");
+#endif
+}
+
+
+void Q_mkdir (char *path)
+{
+#ifdef WIN32
+ if (_mkdir (path) != -1)
+ return;
+#else
+ if (mkdir (path, 0777) != -1)
+ return;
+#endif
+ if (errno != EEXIST)
+ Error ("mkdir %s: %s",path, strerror(errno));
+}
+
+/*
+============
+FileTime
+
+returns -1 if not present
+============
+*/
+int FileTime (char *path)
+{
+ struct stat buf;
+
+ if (stat (path,&buf) == -1)
+ return -1;
+
+ return buf.st_mtime;
+}
+
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+ int c;
+ int len;
+
+ len = 0;
+ com_token[0] = 0;
+
+ if (!data)
+ return NULL;
+
+// skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ {
+ com_eof = true;
+ return NULL; // end of file;
+ }
+ data++;
+ }
+
+// skip // comments
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+
+
+// handle quoted strings specially
+ if (c == '\"')
+ {
+ data++;
+ do
+ {
+ c = *data++;
+ if (c=='\"')
+ {
+ com_token[len] = 0;
+ return data;
+ }
+ com_token[len] = c;
+ len++;
+ } while (1);
+ }
+
+// parse single characters
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+ {
+ com_token[len] = c;
+ len++;
+ com_token[len] = 0;
+ return data+1;
+ }
+
+// parse a regular word
+ do
+ {
+ com_token[len] = c;
+ data++;
+ len++;
+ c = *data;
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+ break;
+ } while (c>32);
+
+ com_token[len] = 0;
+ return data;
+}
+
+
+int Q_strncasecmp (char *s1, char *s2, int n)
+{
+ int c1, c2;
+
+ do
+ {
+ c1 = *s1++;
+ c2 = *s2++;
+
+ if (!n--)
+ return 0; // strings are equal until end point
+
+ if (c1 != c2)
+ {
+ if (c1 >= 'a' && c1 <= 'z')
+ c1 -= ('a' - 'A');
+ if (c2 >= 'a' && c2 <= 'z')
+ c2 -= ('a' - 'A');
+ if (c1 != c2)
+ return -1; // strings not equal
+ }
+ } while (c1);
+
+ return 0; // strings are equal
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+ return Q_strncasecmp (s1, s2, 99999);
+}
+
+
+char *strupr (char *start)
+{
+ char *in;
+ in = start;
+ while (*in)
+ {
+ *in = toupper(*in);
+ in++;
+ }
+ return start;
+}
+
+char *strlower (char *start)
+{
+ char *in;
+ in = start;
+ while (*in)
+ {
+ *in = tolower(*in);
+ in++;
+ }
+ return start;
+}
+
+
+/*
+=============================================================================
+
+ MISC FUNCTIONS
+
+=============================================================================
+*/
+
+
+/*
+=================
+CheckParm
+
+Checks for the given parameter in the program's command line arguments
+Returns the argument number (1 to argc-1) or 0 if not present
+=================
+*/
+int CheckParm (char *check)
+{
+ int i;
+
+ for (i = 1;i<myargc;i++)
+ {
+ if ( !Q_strcasecmp(check, myargv[i]) )
+ return i;
+ }
+
+ return 0;
+}
+
+
+
+/*
+================
+Q_filelength
+================
+*/
+int Q_filelength (FILE *f)
+{
+ int pos;
+ int end;
+
+ pos = ftell (f);
+ fseek (f, 0, SEEK_END);
+ end = ftell (f);
+ fseek (f, pos, SEEK_SET);
+
+ return end;
+}
+
+
+FILE *SafeOpenWrite (char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "wb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+FILE *SafeOpenRead (char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "rb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+
+void SafeRead (FILE *f, void *buffer, int count)
+{
+ if ( fread (buffer, 1, count, f) != (size_t)count)
+ Error ("File read failure");
+}
+
+
+void SafeWrite (FILE *f, void *buffer, int count)
+{
+ if (fwrite (buffer, 1, count, f) != (size_t)count)
+ Error ("File write failure");
+}
+
+
+/*
+==============
+FileExists
+==============
+*/
+qboolean FileExists (char *filename)
+{
+ FILE *f;
+
+ f = fopen (filename, "r");
+ if (!f)
+ return false;
+ fclose (f);
+ return true;
+}
+
+/*
+==============
+LoadFile
+==============
+*/
+int LoadFile (char *filename, void **bufferptr)
+{
+ FILE *f;
+ int length;
+ void *buffer;
+
+ f = SafeOpenRead (filename);
+ length = Q_filelength (f);
+ buffer = malloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+TryLoadFile
+
+Allows failure
+==============
+*/
+int TryLoadFile (char *filename, void **bufferptr)
+{
+ FILE *f;
+ int length;
+ void *buffer;
+
+ *bufferptr = NULL;
+
+ f = fopen (filename, "rb");
+ if (!f)
+ return -1;
+ length = Q_filelength (f);
+ buffer = malloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+SaveFile
+==============
+*/
+void SaveFile (char *filename, void *buffer, int count)
+{
+ FILE *f;
+
+ f = SafeOpenWrite (filename);
+ SafeWrite (f, buffer, count);
+ fclose (f);
+}
+
+
+
+void DefaultExtension (char *path, char *extension)
+{
+ char *src;
+//
+// if path doesnt have a .EXT, append extension
+// (extension should include the .)
+//
+ src = path + strlen(path) - 1;
+
+ while (*src != PATHSEPERATOR && src != path)
+ {
+ if (*src == '.')
+ return; // it has an extension
+ src--;
+ }
+
+ strcat (path, extension);
+}
+
+
+void DefaultPath (char *path, char *basepath)
+{
+ char temp[128];
+
+ if (path[0] == PATHSEPERATOR)
+ return; // absolute path location
+ strcpy (temp,path);
+ strcpy (path,basepath);
+ strcat (path,temp);
+}
+
+
+void StripFilename (char *path)
+{
+ int length;
+
+ length = strlen(path)-1;
+ while (length > 0 && path[length] != PATHSEPERATOR)
+ length--;
+ path[length] = 0;
+}
+
+void StripExtension (char *path)
+{
+ int length;
+
+ length = strlen(path)-1;
+ while (length > 0 && path[length] != '.')
+ {
+ length--;
+ if (path[length] == '/')
+ return; // no extension
+ }
+ if (length)
+ path[length] = 0;
+}
+
+
+/*
+====================
+Extract file parts
+====================
+*/
+// FIXME: should include the slash, otherwise
+// backing to an empty path will be wrong when appending a slash
+void ExtractFilePath (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+ while (src != path && *(src-1) != '\\' && *(src-1) != '/')
+ src--;
+
+ memcpy (dest, path, src-path);
+ dest[src-path] = 0;
+}
+
+void ExtractFileBase (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+ while (src != path && *(src-1) != PATHSEPERATOR)
+ src--;
+
+ while (*src && *src != '.')
+ {
+ *dest++ = *src++;
+ }
+ *dest = 0;
+}
+
+void ExtractFileExtension (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a . or the start
+//
+ while (src != path && *(src-1) != '.')
+ src--;
+ if (src == path)
+ {
+ *dest = 0; // no extension
+ return;
+ }
+
+ strcpy (dest,src);
+}
+
+
+/*
+==============
+ParseNum / ParseHex
+==============
+*/
+int ParseHex (char *hex)
+{
+ char *str;
+ int num;
+
+ num = 0;
+ str = hex;
+
+ while (*str)
+ {
+ num <<= 4;
+ if (*str >= '0' && *str <= '9')
+ num += *str-'0';
+ else if (*str >= 'a' && *str <= 'f')
+ num += 10 + *str-'a';
+ else if (*str >= 'A' && *str <= 'F')
+ num += 10 + *str-'A';
+ else
+ Error ("Bad hex number: %s",hex);
+ str++;
+ }
+
+ return num;
+}
+
+
+int ParseNum (char *str)
+{
+ if (str[0] == '$')
+ return ParseHex (str+1);
+ if (str[0] == '0' && str[1] == 'x')
+ return ParseHex (str+2);
+ return atol (str);
+}
+
+
+
+/*
+============================================================================
+
+ BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+#ifdef _SGI_SOURCE
+#define __BIG_ENDIAN__
+#endif
+
+#ifdef __BIG_ENDIAN__
+
+short LittleShort (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short BigShort (short l)
+{
+ return l;
+}
+
+
+int LittleLong (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int BigLong (int l)
+{
+ return l;
+}
+
+
+float LittleFloat (float l)
+{
+ union {byte b[4]; float f;} in, out;
+
+ in.f = l;
+ out.b[0] = in.b[3];
+ out.b[1] = in.b[2];
+ out.b[2] = in.b[1];
+ out.b[3] = in.b[0];
+
+ return out.f;
+}
+
+float BigFloat (float l)
+{
+ return l;
+}
+
+
+#else
+
+
+short BigShort (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short LittleShort (short l)
+{
+ return l;
+}
+
+
+int BigLong (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int LittleLong (int l)
+{
+ return l;
+}
+
+float BigFloat (float l)
+{
+ union {byte b[4]; float f;} in, out;
+
+ in.f = l;
+ out.b[0] = in.b[3];
+ out.b[1] = in.b[2];
+ out.b[2] = in.b[1];
+ out.b[3] = in.b[0];
+
+ return out.f;
+}
+
+float LittleFloat (float l)
+{
+ return l;
+}
+
+
+#endif
+
+
+//=======================================================
+
+
+// FIXME: byte swap?
+
+// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
+// and the initial and final xor values shown below... in other words, the
+// CCITT standard CRC used by XMODEM
+
+#define CRC_INIT_VALUE 0xffff
+#define CRC_XOR_VALUE 0x0000
+
+static unsigned short crctable[256] =
+{
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+void CRC_Init(unsigned short *crcvalue)
+{
+ *crcvalue = CRC_INIT_VALUE;
+}
+
+void CRC_ProcessByte(unsigned short *crcvalue, byte data)
+{
+ *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
+}
+
+unsigned short CRC_Value(unsigned short crcvalue)
+{
+ return crcvalue ^ CRC_XOR_VALUE;
+}
+//=============================================================================
+
+/*
+============
+CreatePath
+============
+*/
+void CreatePath (char *path)
+{
+ char *ofs, c;
+
+ if (path[1] == ':')
+ path += 2;
+
+ for (ofs = path+1 ; *ofs ; ofs++)
+ {
+ c = *ofs;
+ if (c == '/' || c == '\\')
+ { // create the directory
+ *ofs = 0;
+ Q_mkdir (path);
+ *ofs = c;
+ }
+ }
+}
+
+
+/*
+============
+QCopyFile
+
+ Used to archive source files
+============
+*/
+void QCopyFile (char *from, char *to)
+{
+ void *buffer;
+ int length;
+
+ length = LoadFile (from, &buffer);
+ CreatePath (to);
+ SaveFile (to, buffer, length);
+ free (buffer);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// cmdlib.h
+
+#ifndef __CMDLIB__
+#define __CMDLIB__
+
+#ifdef _WIN32
+#pragma warning(disable : 4244) // MIPS
+#pragma warning(disable : 4136) // X86
+#pragma warning(disable : 4051) // ALPHA
+
+#pragma warning(disable : 4018) // signed/unsigned mismatch
+#pragma warning(disable : 4305) // truncate from double to float
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+
+#ifndef __BYTEBOOL__
+#define __BYTEBOOL__
+typedef enum {false, true} qboolean;
+typedef unsigned char byte;
+#endif
+
+// the dec offsetof macro doesnt work very well...
+#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
+
+
+// set these before calling CheckParm
+extern int myargc;
+extern char **myargv;
+
+char *strupr (char *in);
+char *strlower (char *in);
+int Q_strncasecmp (char *s1, char *s2, int n);
+int Q_strcasecmp (char *s1, char *s2);
+void Q_getwd (char *out);
+
+int Q_filelength (FILE *f);
+int FileTime (char *path);
+
+void Q_mkdir (char *path);
+
+extern char qdir[1024];
+extern char gamedir[1024];
+void SetQdirFromPath (char *path);
+char *ExpandArg (char *path); // from cmd line
+char *ExpandPath (char *path); // from scripts
+char *ExpandPathAndArchive (char *path);
+
+
+double I_FloatTime (void);
+
+void Error (char *error, ...);
+int CheckParm (char *check);
+
+FILE *SafeOpenWrite (char *filename);
+FILE *SafeOpenRead (char *filename);
+void SafeRead (FILE *f, void *buffer, int count);
+void SafeWrite (FILE *f, void *buffer, int count);
+
+int LoadFile (char *filename, void **bufferptr);
+int TryLoadFile (char *filename, void **bufferptr);
+void SaveFile (char *filename, void *buffer, int count);
+qboolean FileExists (char *filename);
+
+void DefaultExtension (char *path, char *extension);
+void DefaultPath (char *path, char *basepath);
+void StripFilename (char *path);
+void StripExtension (char *path);
+
+void ExtractFilePath (char *path, char *dest);
+void ExtractFileBase (char *path, char *dest);
+void ExtractFileExtension (char *path, char *dest);
+
+int ParseNum (char *str);
+
+short BigShort (short l);
+short LittleShort (short l);
+int BigLong (int l);
+int LittleLong (int l);
+float BigFloat (float l);
+float LittleFloat (float l);
+
+
+char *COM_Parse (char *data);
+
+extern char com_token[1024];
+extern qboolean com_eof;
+
+char *copystring(char *s);
+
+
+void CRC_Init(unsigned short *crcvalue);
+void CRC_ProcessByte(unsigned short *crcvalue, byte data);
+unsigned short CRC_Value(unsigned short crcvalue);
+
+void CreatePath (char *path);
+void QCopyFile (char *from, char *to);
+
+extern qboolean archive;
+extern char archivedir[1024];
+
+
+extern qboolean verbose;
+void qprintf (char *format, ...);
+
+void ExpandWildcards (int *argc, char ***argv);
+
+
+// for compression routines
+typedef struct
+{
+ byte *data;
+ int count;
+} cblock_t;
+
+
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//
+// l3dslib.c: library for loading triangles from an Alias triangle file
+//
+
+#include <stdio.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "trilib.h"
+#include "l3dslib.h"
+
+#define MAIN3DS 0x4D4D
+#define EDIT3DS 0x3D3D // this is the start of the editor config
+#define EDIT_OBJECT 0x4000
+#define OBJ_TRIMESH 0x4100
+#define TRI_VERTEXL 0x4110
+#define TRI_FACEL1 0x4120
+
+#define MAXVERTS 2000
+
+typedef struct {
+ int v[4];
+} tri;
+
+float fverts[MAXVERTS][3];
+tri tris[MAXTRIANGLES];
+
+int bytesread, level, numtris, totaltris;
+int vertsfound, trisfound;
+
+triangle_t *ptri;
+
+
+// Alias stores triangles as 3 explicit vertices in .tri files, so even though we
+// start out with a vertex pool and vertex indices for triangles, we have to convert
+// to raw, explicit triangles
+void StoreAliasTriangles (void)
+{
+ int i, j, k;
+
+ if ((totaltris + numtris) > MAXTRIANGLES)
+ Error ("Error: Too many triangles");
+
+ for (i=0; i<numtris ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ ptri[i+totaltris].verts[j][k] = fverts[tris[i].v[j]][k];
+ }
+ }
+ }
+
+ totaltris += numtris;
+ numtris = 0;
+ vertsfound = 0;
+ trisfound = 0;
+}
+
+
+int ParseVertexL (FILE *input)
+{
+ int i, j, startbytesread, numverts;
+ unsigned short tshort;
+
+ if (vertsfound)
+ Error ("Error: Multiple vertex chunks");
+
+ vertsfound = 1;
+ startbytesread = bytesread;
+
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&tshort, sizeof(tshort), 1, input);
+ bytesread += sizeof(tshort);
+ numverts = (int)tshort;
+
+ if (numverts > MAXVERTS)
+ Error ("Error: Too many vertices");
+
+ for (i=0 ; i<numverts ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&fverts[i][j], sizeof(float), 1, input);
+ bytesread += sizeof(float);
+ }
+ }
+
+ if (vertsfound && trisfound)
+ StoreAliasTriangles ();
+
+ return bytesread - startbytesread;
+}
+
+
+int ParseFaceL1 (FILE *input)
+{
+
+ int i, j, startbytesread;
+ unsigned short tshort;
+
+ if (trisfound)
+ Error ("Error: Multiple face chunks");
+
+ trisfound = 1;
+ startbytesread = bytesread;
+
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&tshort, sizeof(tshort), 1, input);
+ bytesread += sizeof(tshort);
+ numtris = (int)tshort;
+
+ if (numtris > MAXTRIANGLES)
+ Error ("Error: Too many triangles");
+
+ for (i=0 ; i<numtris ; i++)
+ {
+ for (j=0 ; j<4 ; j++)
+ {
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&tshort, sizeof(tshort), 1, input);
+ bytesread += sizeof(tshort);
+ tris[i].v[j] = (int)tshort;
+ }
+ }
+
+ if (vertsfound && trisfound)
+ StoreAliasTriangles ();
+
+ return bytesread - startbytesread;
+}
+
+
+int ParseChunk (FILE *input)
+{
+#define BLOCK_SIZE 4096
+ char temp[BLOCK_SIZE];
+ unsigned short type;
+ int i, length, w, t, retval;
+
+ level++;
+ retval = 0;
+
+// chunk type
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&type, sizeof(type), 1, input);
+ bytesread += sizeof(type);
+
+// chunk length
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread (&length, sizeof(length), 1, input);
+ bytesread += sizeof(length);
+ w = length - 6;
+
+// process chunk if we care about it, otherwise skip it
+ switch (type)
+ {
+ case TRI_VERTEXL:
+ w -= ParseVertexL (input);
+ goto ParseSubchunk;
+
+ case TRI_FACEL1:
+ w -= ParseFaceL1 (input);
+ goto ParseSubchunk;
+
+ case EDIT_OBJECT:
+ // read the name
+ i = 0;
+
+ do
+ {
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread (&temp[i], 1, 1, input);
+ i++;
+ w--;
+ bytesread++;
+ } while (temp[i-1]);
+
+ case MAIN3DS:
+ case OBJ_TRIMESH:
+ case EDIT3DS:
+ // parse through subchunks
+ParseSubchunk:
+ while (w > 0)
+ {
+ w -= ParseChunk (input);
+ }
+
+ retval = length;
+ goto Done;
+
+ default:
+ // skip other chunks
+ while (w > 0)
+ {
+ t = w;
+
+ if (t > BLOCK_SIZE)
+ t = BLOCK_SIZE;
+
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread (&temp, t, 1, input);
+ bytesread += t;
+
+ w -= t;
+ }
+
+ retval = length;
+ goto Done;
+ }
+
+Done:
+ level--;
+ return retval;
+}
+
+
+void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles)
+{
+ FILE *input;
+ short int tshort;
+
+ bytesread = 0;
+ level = 0;
+ numtris = 0;
+ totaltris = 0;
+ vertsfound = 0;
+ trisfound = 0;
+
+ if ((input = fopen(filename, "rb")) == 0) {
+ fprintf(stderr,"reader: could not open file '%s'\n", filename);
+ exit(0);
+ }
+
+ fread(&tshort, sizeof(tshort), 1, input);
+
+// should only be MAIN3DS, but some files seem to start with EDIT3DS, with
+// no MAIN3DS
+ if ((tshort != MAIN3DS) && (tshort != EDIT3DS)) {
+ fprintf(stderr,"File is not a 3DS file.\n");
+ exit(0);
+ }
+
+// back to top of file so we can parse the first chunk descriptor
+ fseek(input, 0, SEEK_SET);
+
+ ptri = malloc (MAXTRIANGLES * sizeof(triangle_t));
+
+ *pptri = ptri;
+
+// parse through looking for the relevant chunk tree (MAIN3DS | EDIT3DS | EDIT_OBJECT |
+// OBJ_TRIMESH | {TRI_VERTEXL, TRI_FACEL1}) and skipping other chunks
+ ParseChunk (input);
+
+ if (vertsfound || trisfound)
+ Error ("Incomplete triangle set");
+
+ *numtriangles = totaltris;
+
+ fclose (input);
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//
+// l3dslib.h: header file for loading triangles from a 3DS triangle file
+//
+void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles);
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// lbmlib.c
+
+#include "cmdlib.h"
+#include "lbmlib.h"
+
+
+
+/*
+============================================================================
+
+ LBM STUFF
+
+============================================================================
+*/
+
+
+typedef unsigned char UBYTE;
+//conflicts with windows typedef short WORD;
+typedef unsigned short UWORD;
+typedef long LONG;
+
+typedef enum
+{
+ ms_none,
+ ms_mask,
+ ms_transcolor,
+ ms_lasso
+} mask_t;
+
+typedef enum
+{
+ cm_none,
+ cm_rle1
+} compress_t;
+
+typedef struct
+{
+ UWORD w,h;
+ short x,y;
+ UBYTE nPlanes;
+ UBYTE masking;
+ UBYTE compression;
+ UBYTE pad1;
+ UWORD transparentColor;
+ UBYTE xAspect,yAspect;
+ short pageWidth,pageHeight;
+} bmhd_t;
+
+extern bmhd_t bmhd; // will be in native byte order
+
+
+
+#define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24))
+#define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24))
+#define PBMID ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24))
+#define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24))
+#define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24))
+#define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24))
+
+
+bmhd_t bmhd;
+
+int Align (int l)
+{
+ if (l&1)
+ return l+1;
+ return l;
+}
+
+
+
+/*
+================
+LBMRLEdecompress
+
+Source must be evenly aligned!
+================
+*/
+byte *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth)
+{
+ int count;
+ byte b,rept;
+
+ count = 0;
+
+ do
+ {
+ rept = *source++;
+
+ if (rept > 0x80)
+ {
+ rept = (rept^0xff)+2;
+ b = *source++;
+ memset(unpacked,b,rept);
+ unpacked += rept;
+ }
+ else if (rept < 0x80)
+ {
+ rept++;
+ memcpy(unpacked,source,rept);
+ unpacked += rept;
+ source += rept;
+ }
+ else
+ rept = 0; // rept of 0x80 is NOP
+
+ count += rept;
+
+ } while (count<bpwidth);
+
+ if (count>bpwidth)
+ Error ("Decompression exceeded width!\n");
+
+
+ return source;
+}
+
+
+/*
+=================
+LoadLBM
+=================
+*/
+void LoadLBM (char *filename, byte **picture, byte **palette)
+{
+ byte *LBMbuffer, *picbuffer, *cmapbuffer;
+ int y;
+ byte *LBM_P, *LBMEND_P;
+ byte *pic_p;
+ byte *body_p;
+
+ int formtype,formlength;
+ int chunktype,chunklength;
+
+// qiet compiler warnings
+ picbuffer = NULL;
+ cmapbuffer = NULL;
+
+//
+// load the LBM
+//
+ LoadFile (filename, (void **)&LBMbuffer);
+
+//
+// parse the LBM header
+//
+ LBM_P = LBMbuffer;
+ if ( *(int *)LBMbuffer != LittleLong(FORMID) )
+ Error ("No FORM ID at start of file!\n");
+
+ LBM_P += 4;
+ formlength = BigLong( *(int *)LBM_P );
+ LBM_P += 4;
+ LBMEND_P = LBM_P + Align(formlength);
+
+ formtype = LittleLong(*(int *)LBM_P);
+
+ if (formtype != ILBMID && formtype != PBMID)
+ Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff
+ ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff);
+
+ LBM_P += 4;
+
+//
+// parse chunks
+//
+
+ while (LBM_P < LBMEND_P)
+ {
+ chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24);
+ LBM_P += 4;
+ chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24);
+ LBM_P += 4;
+
+ switch ( chunktype )
+ {
+ case BMHDID:
+ memcpy (&bmhd,LBM_P,sizeof(bmhd));
+ bmhd.w = BigShort(bmhd.w);
+ bmhd.h = BigShort(bmhd.h);
+ bmhd.x = BigShort(bmhd.x);
+ bmhd.y = BigShort(bmhd.y);
+ bmhd.pageWidth = BigShort(bmhd.pageWidth);
+ bmhd.pageHeight = BigShort(bmhd.pageHeight);
+ break;
+
+ case CMAPID:
+ cmapbuffer = malloc (768);
+ memset (cmapbuffer, 0, 768);
+ memcpy (cmapbuffer, LBM_P, chunklength);
+ break;
+
+ case BODYID:
+ body_p = LBM_P;
+
+ pic_p = picbuffer = malloc (bmhd.w*bmhd.h);
+ if (formtype == PBMID)
+ {
+ //
+ // unpack PBM
+ //
+ for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w)
+ {
+ if (bmhd.compression == cm_rle1)
+ body_p = LBMRLEDecompress ((byte *)body_p
+ , pic_p , bmhd.w);
+ else if (bmhd.compression == cm_none)
+ {
+ memcpy (pic_p,body_p,bmhd.w);
+ body_p += Align(bmhd.w);
+ }
+ }
+
+ }
+ else
+ {
+ //
+ // unpack ILBM
+ //
+ Error ("%s is an interlaced LBM, not packed", filename);
+ }
+ break;
+ }
+
+ LBM_P += Align(chunklength);
+ }
+
+ free (LBMbuffer);
+
+ *picture = picbuffer;
+
+ if (palette)
+ *palette = cmapbuffer;
+}
+
+
+/*
+============================================================================
+
+ WRITE LBM
+
+============================================================================
+*/
+
+/*
+==============
+WriteLBMfile
+==============
+*/
+void WriteLBMfile (char *filename, byte *data,
+ int width, int height, byte *palette)
+{
+ byte *lbm, *lbmptr;
+ int *formlength, *bmhdlength, *cmaplength, *bodylength;
+ int length;
+ bmhd_t basebmhd;
+
+ lbm = lbmptr = malloc (width*height+1000);
+
+//
+// start FORM
+//
+ *lbmptr++ = 'F';
+ *lbmptr++ = 'O';
+ *lbmptr++ = 'R';
+ *lbmptr++ = 'M';
+
+ formlength = (int*)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ *lbmptr++ = 'P';
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'M';
+ *lbmptr++ = ' ';
+
+//
+// write BMHD
+//
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'M';
+ *lbmptr++ = 'H';
+ *lbmptr++ = 'D';
+
+ bmhdlength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memset (&basebmhd,0,sizeof(basebmhd));
+ basebmhd.w = BigShort((short)width);
+ basebmhd.h = BigShort((short)height);
+ basebmhd.nPlanes = BigShort(8);
+ basebmhd.xAspect = BigShort(5);
+ basebmhd.yAspect = BigShort(6);
+ basebmhd.pageWidth = BigShort((short)width);
+ basebmhd.pageHeight = BigShort((short)height);
+
+ memcpy (lbmptr,&basebmhd,sizeof(basebmhd));
+ lbmptr += sizeof(basebmhd);
+
+ length = lbmptr-(byte *)bmhdlength-4;
+ *bmhdlength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write CMAP
+//
+ *lbmptr++ = 'C';
+ *lbmptr++ = 'M';
+ *lbmptr++ = 'A';
+ *lbmptr++ = 'P';
+
+ cmaplength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memcpy (lbmptr,palette,768);
+ lbmptr += 768;
+
+ length = lbmptr-(byte *)cmaplength-4;
+ *cmaplength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write BODY
+//
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'O';
+ *lbmptr++ = 'D';
+ *lbmptr++ = 'Y';
+
+ bodylength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memcpy (lbmptr,data,width*height);
+ lbmptr += width*height;
+
+ length = lbmptr-(byte *)bodylength-4;
+ *bodylength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// done
+//
+ length = lbmptr-(byte *)formlength-4;
+ *formlength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write output file
+//
+ SaveFile (filename, lbm, lbmptr-lbm);
+ free (lbm);
+}
+
+
+/*
+============================================================================
+
+LOAD PCX
+
+============================================================================
+*/
+
+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;
+
+
+/*
+==============
+LoadPCX
+==============
+*/
+void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height)
+{
+ byte *raw;
+ pcx_t *pcx;
+ int x, y;
+ int len;
+ int dataByte, runLength;
+ byte *out, *pix;
+
+ //
+ // load the file
+ //
+ len = LoadFile (filename, (void **)&raw);
+
+ //
+ // parse the PCX file
+ //
+ pcx = (pcx_t *)raw;
+ raw = &pcx->data;
+
+ 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);
+
+ if (pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8
+ || pcx->xmax >= 640
+ || pcx->ymax >= 480)
+ Error ("Bad pcx file %s", filename);
+
+ if (palette)
+ {
+ *palette = malloc(768);
+ memcpy (*palette, (byte *)pcx + len - 768, 768);
+ }
+
+ if (width)
+ *width = pcx->xmax+1;
+ if (height)
+ *height = pcx->ymax+1;
+
+ if (!pic)
+ return;
+
+ out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
+ if (!out)
+ Error ("Skin_Cache: couldn't allocate");
+
+ *pic = out;
+
+ pix = out;
+
+ for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
+ {
+ for (x=0 ; x<=pcx->xmax ; )
+ {
+ dataByte = *raw++;
+
+ if((dataByte & 0xC0) == 0xC0)
+ {
+ runLength = dataByte & 0x3F;
+ dataByte = *raw++;
+ }
+ else
+ runLength = 1;
+
+ while(runLength-- > 0)
+ pix[x++] = dataByte;
+ }
+
+ }
+
+ if ( raw - (byte *)pcx > len)
+ Error ("PCX file %s was malformed", filename);
+
+ free (pcx);
+}
+
+/*
+==============
+WritePCXfile
+==============
+*/
+void WritePCXfile (char *filename, byte *data,
+ int width, int height, byte *palette)
+{
+ int i, j, length;
+ pcx_t *pcx;
+ byte *pack;
+
+ pcx = malloc (width*height*2+1000);
+ memset (pcx, 0, sizeof(*pcx));
+
+ pcx->manufacturer = 0x0a; // PCX id
+ pcx->version = 5; // 256 color
+ pcx->encoding = 1; // uncompressed
+ pcx->bits_per_pixel = 8; // 256 color
+ pcx->xmin = 0;
+ pcx->ymin = 0;
+ pcx->xmax = LittleShort((short)(width-1));
+ pcx->ymax = LittleShort((short)(height-1));
+ pcx->hres = LittleShort((short)width);
+ pcx->vres = LittleShort((short)height);
+ pcx->color_planes = 1; // chunky image
+ pcx->bytes_per_line = LittleShort((short)width);
+ pcx->palette_type = LittleShort(2); // not a grey scale
+
+ // pack the image
+ pack = &pcx->data;
+
+ for (i=0 ; i<height ; i++)
+ {
+ for (j=0 ; j<width ; j++)
+ {
+ if ( (*data & 0xc0) != 0xc0)
+ *pack++ = *data++;
+ else
+ {
+ *pack++ = 0xc1;
+ *pack++ = *data++;
+ }
+ }
+ }
+
+ // write the palette
+ *pack++ = 0x0c; // palette ID byte
+ for (i=0 ; i<768 ; i++)
+ *pack++ = *palette++;
+
+// write output file
+ length = pack - (byte *)pcx;
+ SaveFile (filename, pcx, length);
+
+ free (pcx);
+}
+
+
+/*
+============================================================================
+
+LOAD IMAGE
+
+============================================================================
+*/
+
+/*
+==============
+Load256Image
+
+Will load either an lbm or pcx, depending on extension.
+Any of the return pointers can be NULL if you don't want them.
+==============
+*/
+void Load256Image (char *name, byte **pixels, byte **palette,
+ int *width, int *height)
+{
+ char ext[128];
+
+ ExtractFileExtension (name, ext);
+ if (!Q_strcasecmp (ext, "lbm"))
+ {
+ LoadLBM (name, pixels, palette);
+ if (width)
+ *width = bmhd.w;
+ if (height)
+ *height = bmhd.h;
+ }
+ else if (!Q_strcasecmp (ext, "pcx"))
+ {
+ LoadPCX (name, pixels, palette, width, height);
+ }
+ else
+ Error ("%s doesn't have a known image extension", name);
+}
+
+
+/*
+==============
+Save256Image
+
+Will save either an lbm or pcx, depending on extension.
+==============
+*/
+void Save256Image (char *name, byte *pixels, byte *palette,
+ int width, int height)
+{
+ char ext[128];
+
+ ExtractFileExtension (name, ext);
+ if (!Q_strcasecmp (ext, "lbm"))
+ {
+ WriteLBMfile (name, pixels, width, height, palette);
+ }
+ else if (!Q_strcasecmp (ext, "pcx"))
+ {
+ WritePCXfile (name, pixels, width, height, palette);
+ }
+ else
+ Error ("%s doesn't have a known image extension", name);
+}
+
+
+
+
+/*
+============================================================================
+
+TARGA IMAGE
+
+============================================================================
+*/
+
+typedef struct _TargaHeader {
+ unsigned char id_length, colormap_type, image_type;
+ unsigned short colormap_index, colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin, y_origin, width, height;
+ unsigned char pixel_size, attributes;
+} TargaHeader;
+
+int fgetLittleShort (FILE *f)
+{
+ byte b1, b2;
+
+ b1 = fgetc(f);
+ b2 = fgetc(f);
+
+ return (short)(b1 + b2*256);
+}
+
+int fgetLittleLong (FILE *f)
+{
+ byte b1, b2, b3, b4;
+
+ b1 = fgetc(f);
+ b2 = fgetc(f);
+ b3 = fgetc(f);
+ b4 = fgetc(f);
+
+ return b1 + (b2<<8) + (b3<<16) + (b4<<24);
+}
+
+
+/*
+=============
+LoadTGA
+=============
+*/
+void LoadTGA (char *name, byte **pixels, int *width, int *height)
+{
+ int columns, rows, numPixels;
+ byte *pixbuf;
+ int row, column;
+ FILE *fin;
+ byte *targa_rgba;
+ TargaHeader targa_header;
+
+ fin = fopen (name, "rb");
+ if (!fin)
+ Error ("Couldn't read %s", name);
+
+ targa_header.id_length = fgetc(fin);
+ targa_header.colormap_type = fgetc(fin);
+ targa_header.image_type = fgetc(fin);
+
+ targa_header.colormap_index = fgetLittleShort(fin);
+ targa_header.colormap_length = fgetLittleShort(fin);
+ targa_header.colormap_size = fgetc(fin);
+ targa_header.x_origin = fgetLittleShort(fin);
+ targa_header.y_origin = fgetLittleShort(fin);
+ targa_header.width = fgetLittleShort(fin);
+ targa_header.height = fgetLittleShort(fin);
+ targa_header.pixel_size = fgetc(fin);
+ targa_header.attributes = fgetc(fin);
+
+ if (targa_header.image_type!=2
+ && targa_header.image_type!=10)
+ Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
+
+ if (targa_header.colormap_type !=0
+ || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
+ Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+
+ columns = targa_header.width;
+ rows = targa_header.height;
+ numPixels = columns * rows;
+
+ if (width)
+ *width = columns;
+ if (height)
+ *height = rows;
+ targa_rgba = malloc(numPixels*4);
+ *pixels = targa_rgba;
+
+ if (targa_header.id_length != 0)
+ fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment
+
+ if (targa_header.image_type==2) { // Uncompressed, RGB images
+ for(row=rows-1; row>=0; row--) {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; column++) {
+ unsigned char red,green,blue,alphabyte;
+ switch (targa_header.pixel_size) {
+ case 24:
+
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ }
+ }
+ }
+ }
+ else if (targa_header.image_type==10) { // Runlength encoded RGB images
+ unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
+ for(row=rows-1; row>=0; row--) {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; ) {
+ packetHeader=getc(fin);
+ packetSize = 1 + (packetHeader & 0x7f);
+ if (packetHeader & 0x80) { // run-length packet
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ break;
+ }
+
+ for(j=0;j<packetSize;j++) {
+ *pixbuf++=red;
+ *pixbuf++=green;
+ *pixbuf++=blue;
+ *pixbuf++=alphabyte;
+ column++;
+ if (column==columns) { // run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ else { // non run-length packet
+ for(j=0;j<packetSize;j++) {
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ }
+ column++;
+ if (column==columns) { // pixel packet run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ }
+ breakOut:;
+ }
+ }
+
+ fclose(fin);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// piclib.h
+
+
+void LoadLBM (char *filename, byte **picture, byte **palette);
+void WriteLBMfile (char *filename, byte *data, int width, int height
+ , byte *palette);
+void LoadPCX (char *filename, byte **picture, byte **palette, int *width, int *height);
+void WritePCXfile (char *filename, byte *data, int width, int height
+ , byte *palette);
+
+// loads / saves either lbm or pcx, depending on extension
+void Load256Image (char *name, byte **pixels, byte **palette,
+ int *width, int *height);
+void Save256Image (char *name, byte *pixels, byte *palette,
+ int width, int height);
+
+
+void LoadTGA (char *filename, byte **pixels, int *width, int *height);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// mathlib.c -- math primitives
+
+#include "cmdlib.h"
+#include "mathlib.h"
+
+vec3_t vec3_origin = {0,0,0};
+
+
+double VectorLength(vec3_t v)
+{
+ int i;
+ double length;
+
+ length = 0;
+ for (i=0 ; i< 3 ; i++)
+ length += v[i]*v[i];
+ length = sqrt (length); // FIXME
+
+ return length;
+}
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
+ return false;
+
+ return true;
+}
+
+vec_t Q_rint (vec_t in)
+{
+ return floor (in + 0.5);
+}
+
+void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc)
+{
+ vc[0] = va[0] + scale*vb[0];
+ vc[1] = va[1] + scale*vb[1];
+ vc[2] = va[2] + scale*vb[2];
+}
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
+{
+ cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out)
+{
+ out[0] = va[0]-vb[0];
+ out[1] = va[1]-vb[1];
+ out[2] = va[2]-vb[2];
+}
+
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out)
+{
+ out[0] = va[0]+vb[0];
+ out[1] = va[1]+vb[1];
+ out[2] = va[2]+vb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+void _VectorScale (vec3_t v, vec_t scale, vec3_t out)
+{
+ out[0] = v[0] * scale;
+ out[1] = v[1] * scale;
+ out[2] = v[2] * scale;
+}
+
+vec_t VectorNormalize (vec3_t in, vec3_t out)
+{
+ vec_t length, ilength;
+
+ length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
+ if (length == 0)
+ {
+ VectorClear (out);
+ return 0;
+ }
+
+ ilength = 1.0/length;
+ out[0] = in[0]*ilength;
+ out[1] = in[1]*ilength;
+ out[2] = in[2]*ilength;
+
+ return length;
+}
+
+vec_t ColorNormalize (vec3_t in, vec3_t out)
+{
+ float max, scale;
+
+ max = in[0];
+ if (in[1] > max)
+ max = in[1];
+ if (in[2] > max)
+ max = in[2];
+
+ if (max == 0)
+ return 0;
+
+ scale = 1.0 / max;
+
+ VectorScale (in, scale, out);
+
+ return max;
+}
+
+
+
+void VectorInverse (vec3_t v)
+{
+ v[0] = -v[0];
+ v[1] = -v[1];
+ v[2] = -v[2];
+}
+
+void ClearBounds (vec3_t mins, vec3_t maxs)
+{
+ mins[0] = mins[1] = mins[2] = 99999;
+ maxs[0] = maxs[1] = maxs[2] = -99999;
+}
+
+void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs)
+{
+ int i;
+ vec_t val;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ val = v[i];
+ if (val < mins[i])
+ mins[i] = val;
+ if (val > maxs[i])
+ maxs[i] = val;
+ }
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#ifndef __MATHLIB__
+#define __MATHLIB__
+
+// mathlib.h
+
+#include <math.h>
+
+#ifdef DOUBLEVEC_T
+typedef double vec_t;
+#else
+typedef float vec_t;
+#endif
+typedef vec_t vec3_t[3];
+
+#define SIDE_FRONT 0
+#define SIDE_ON 2
+#define SIDE_BACK 1
+#define SIDE_CROSS -2
+
+#define Q_PI 3.14159265358979323846
+
+extern vec3_t vec3_origin;
+
+#define EQUAL_EPSILON 0.001
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2);
+
+#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
+#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
+#define VectorScale(a,b,c) {c[0]=b*a[0];c[1]=b*a[1];c[2]=b*a[2];}
+#define VectorClear(x) {x[0] = x[1] = x[2] = 0;}
+#define VectorNegate(x) {x[0]=-x[0];x[1]=-x[1];x[2]=-x[2];}
+
+vec_t Q_rint (vec_t in);
+vec_t _DotProduct (vec3_t v1, vec3_t v2);
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorCopy (vec3_t in, vec3_t out);
+void _VectorScale (vec3_t v, vec_t scale, vec3_t out);
+
+double VectorLength(vec3_t v);
+
+void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc);
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
+vec_t VectorNormalize (vec3_t in, vec3_t out);
+vec_t ColorNormalize (vec3_t in, vec3_t out);
+void VectorInverse (vec3_t v);
+
+void ClearBounds (vec3_t mins, vec3_t maxs);
+void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs);
+
+#endif
--- /dev/null
+/*
+ mdfour.c
+
+ An implementation of MD4 designed for use in the samba SMB
+ authentication protocol
+
+ Copyright (C) 1997-1998 Andrew Tridgell
+
+ 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:
+
+ Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA
+
+ $Id: mdfour.c,v 1.1 2002/08/23 22:03:27 abster Exp $
+*/
+
+#include <string.h> /* XoXus: needed for memset call */
+#include "mdfour.h"
+
+/* NOTE: This code makes no attempt to be fast!
+
+ It assumes that a int is at least 32 bits long
+*/
+
+static struct mdfour *m;
+
+#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))
+#ifdef LARGE_INT32
+#define lshift(x,s) ((((x)<<(s))&0xFFFFFFFF) | (((x)>>(32-(s)))&0xFFFFFFFF))
+#else
+#define lshift(x,s) (((x)<<(s)) | ((x)>>(32-(s))))
+#endif
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(uint32 *M)
+{
+ int j;
+ uint32 AA, BB, CC, DD;
+ uint32 X[16];
+ uint32 A,B,C,D;
+
+ for (j=0;j<16;j++)
+ X[j] = M[j];
+
+ A = m->A; B = m->B; C = m->C; D = m->D;
+ AA = A; BB = B; CC = C; DD = D;
+
+ ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7);
+ ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19);
+ ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
+ ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
+ ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
+ ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
+ ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
+ ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
+
+ ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
+ ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
+ ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
+ ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
+ ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
+ ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
+ ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
+ ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
+
+ ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
+ ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
+ ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
+ ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
+ ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
+ ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
+ ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9);
+ ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15);
+
+ A += AA; B += BB; C += CC; D += DD;
+
+#ifdef LARGE_INT32
+ A &= 0xFFFFFFFF; B &= 0xFFFFFFFF;
+ C &= 0xFFFFFFFF; D &= 0xFFFFFFFF;
+#endif
+
+ for (j=0;j<16;j++)
+ X[j] = 0;
+
+ m->A = A; m->B = B; m->C = C; m->D = D;
+}
+
+static void copy64(uint32 *M, unsigned char *in)
+{
+ int i;
+
+ for (i=0;i<16;i++)
+ M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
+ (in[i*4+1]<<8) | (in[i*4+0]<<0);
+}
+
+static void copy4(unsigned char *out,uint32 x)
+{
+ out[0] = x&0xFF;
+ out[1] = (x>>8)&0xFF;
+ out[2] = (x>>16)&0xFF;
+ out[3] = (x>>24)&0xFF;
+}
+
+void mdfour_begin(struct mdfour *md)
+{
+ md->A = 0x67452301;
+ md->B = 0xefcdab89;
+ md->C = 0x98badcfe;
+ md->D = 0x10325476;
+ md->totalN = 0;
+}
+
+
+static void mdfour_tail(unsigned char *in, int n)
+{
+ unsigned char buf[128];
+ uint32 M[16];
+ uint32 b;
+
+ m->totalN += n;
+
+ b = m->totalN * 8;
+
+ memset(buf, 0, 128);
+ if (n) memcpy(buf, in, n);
+ buf[n] = 0x80;
+
+ if (n <= 55) {
+ copy4(buf+56, b);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf+120, b);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf+64);
+ mdfour64(M);
+ }
+}
+
+void mdfour_update(struct mdfour *md, unsigned char *in, int n)
+{
+ uint32 M[16];
+
+ if (n == 0) mdfour_tail(in, n);
+
+ m = md;
+
+ while (n >= 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ n -= 64;
+ m->totalN += 64;
+ }
+
+ mdfour_tail(in, n);
+}
+
+
+void mdfour_result(struct mdfour *md, unsigned char *out)
+{
+ m = md;
+
+ copy4(out, m->A);
+ copy4(out+4, m->B);
+ copy4(out+8, m->C);
+ copy4(out+12, m->D);
+}
+
+
+void mdfour(unsigned char *out, unsigned char *in, int n)
+{
+ struct mdfour md;
+ mdfour_begin(&md);
+ mdfour_update(&md, in, n);
+ mdfour_result(&md, out);
+}
+
+///////////////////////////////////////////////////////////////
+// MD4-based checksum utility functions
+//
+// Copyright (C) 2000 Jeff Teunissen <d2deek@pmail.net>
+//
+// Author: Jeff Teunissen <d2deek@pmail.net>
+// Date: 01 Jan 2000
+
+unsigned Com_BlockChecksum (void *buffer, int length)
+{
+ int digest[4];
+ unsigned val;
+
+ mdfour ( (unsigned char *) digest, (unsigned char *) buffer, length );
+
+ val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
+
+ return val;
+}
+
+void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf)
+{
+ mdfour ( outbuf, (unsigned char *) buffer, len );
+}
+
--- /dev/null
+/*
+ mdfour.h
+
+ an implementation of MD4 designed for use in the SMB authentication
+ protocol
+
+ Copyright (C) Andrew Tridgell 1997-1998
+
+ 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:
+
+ Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA
+*/
+
+#ifndef _MDFOUR_H
+#define _MDFOUR_H
+
+#ifndef int32
+#define int32 int
+#endif
+
+#if SIZEOF_INT > 4
+#define LARGE_INT32
+#endif
+
+#ifndef uint32
+#define uint32 unsigned int32
+#endif
+
+struct mdfour {
+ uint32 A, B, C, D;
+ uint32 totalN;
+};
+
+void mdfour_begin(struct mdfour *md); // old: MD4Init
+void mdfour_update(struct mdfour *md, unsigned char *in, int n); //old: MD4Update
+void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final
+void mdfour(unsigned char *out, unsigned char *in, int n);
+
+#endif // _MDFOUR_H
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "polylib.h"
+
+
+extern int numthreads;
+
+// counters are only bumped when running single threaded,
+// because they are an awefull coherence problem
+int c_active_windings;
+int c_peak_windings;
+int c_winding_allocs;
+int c_winding_points;
+
+#define BOGUS_RANGE 8192
+
+void pw(winding_t *w)
+{
+ int i;
+ for (i=0 ; i<w->numpoints ; i++)
+ printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
+}
+
+
+/*
+=============
+AllocWinding
+=============
+*/
+winding_t *AllocWinding (int points)
+{
+ winding_t *w;
+ int s;
+
+ if (numthreads == 1)
+ {
+ c_winding_allocs++;
+ c_winding_points += points;
+ c_active_windings++;
+ if (c_active_windings > c_peak_windings)
+ c_peak_windings = c_active_windings;
+ }
+ s = sizeof(vec_t)*3*points + sizeof(int);
+ w = malloc (s);
+ memset (w, 0, s);
+ return w;
+}
+
+void FreeWinding (winding_t *w)
+{
+ if (*(unsigned *)w == 0xdeaddead)
+ Error ("FreeWinding: freed a freed winding");
+ *(unsigned *)w = 0xdeaddead;
+
+ if (numthreads == 1)
+ c_active_windings--;
+ free (w);
+}
+
+/*
+============
+RemoveColinearPoints
+============
+*/
+int c_removed;
+
+void RemoveColinearPoints (winding_t *w)
+{
+ int i, j, k;
+ vec3_t v1, v2;
+ int nump;
+ vec3_t p[MAX_POINTS_ON_WINDING];
+
+ nump = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = (i+1)%w->numpoints;
+ k = (i+w->numpoints-1)%w->numpoints;
+ VectorSubtract (w->p[j], w->p[i], v1);
+ VectorSubtract (w->p[i], w->p[k], v2);
+ VectorNormalize(v1,v1);
+ VectorNormalize(v2,v2);
+ if (DotProduct(v1, v2) < 0.999)
+ {
+ VectorCopy (w->p[i], p[nump]);
+ nump++;
+ }
+ }
+
+ if (nump == w->numpoints)
+ return;
+
+ if (numthreads == 1)
+ c_removed += w->numpoints - nump;
+ w->numpoints = nump;
+ memcpy (w->p, p, nump*sizeof(p[0]));
+}
+
+/*
+============
+WindingPlane
+============
+*/
+void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist)
+{
+ vec3_t v1, v2;
+
+ VectorSubtract (w->p[1], w->p[0], v1);
+ VectorSubtract (w->p[2], w->p[0], v2);
+ CrossProduct (v2, v1, normal);
+ VectorNormalize (normal, normal);
+ *dist = DotProduct (w->p[0], normal);
+
+}
+
+/*
+=============
+WindingArea
+=============
+*/
+vec_t WindingArea (winding_t *w)
+{
+ int i;
+ vec3_t d1, d2, cross;
+ vec_t total;
+
+ total = 0;
+ for (i=2 ; i<w->numpoints ; i++)
+ {
+ VectorSubtract (w->p[i-1], w->p[0], d1);
+ VectorSubtract (w->p[i], w->p[0], d2);
+ CrossProduct (d1, d2, cross);
+ total += 0.5 * VectorLength ( cross );
+ }
+ return total;
+}
+
+void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs)
+{
+ vec_t v;
+ int i,j;
+
+ mins[0] = mins[1] = mins[2] = 99999;
+ maxs[0] = maxs[1] = maxs[2] = -99999;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ v = w->p[i][j];
+ if (v < mins[j])
+ mins[j] = v;
+ if (v > maxs[j])
+ maxs[j] = v;
+ }
+ }
+}
+
+/*
+=============
+WindingCenter
+=============
+*/
+void WindingCenter (winding_t *w, vec3_t center)
+{
+ int i;
+ float scale;
+
+ VectorCopy (vec3_origin, center);
+ for (i=0 ; i<w->numpoints ; i++)
+ VectorAdd (w->p[i], center, center);
+
+ scale = 1.0/w->numpoints;
+ VectorScale (center, scale, center);
+}
+
+/*
+=================
+BaseWindingForPlane
+=================
+*/
+winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
+{
+ int i, x;
+ vec_t max, v;
+ vec3_t org, vright, vup;
+ winding_t *w;
+
+// find the major axis
+
+ max = -BOGUS_RANGE;
+ x = -1;
+ for (i=0 ; i<3; i++)
+ {
+ v = fabs(normal[i]);
+ if (v > max)
+ {
+ x = i;
+ max = v;
+ }
+ }
+ if (x==-1)
+ Error ("BaseWindingForPlane: no axis found");
+
+ VectorCopy (vec3_origin, vup);
+ switch (x)
+ {
+ case 0:
+ case 1:
+ vup[2] = 1;
+ break;
+ case 2:
+ vup[0] = 1;
+ break;
+ }
+
+ v = DotProduct (vup, normal);
+ VectorMA (vup, -v, normal, vup);
+ VectorNormalize (vup, vup);
+
+ VectorScale (normal, dist, org);
+
+ CrossProduct (vup, normal, vright);
+
+ VectorScale (vup, 8192, vup);
+ VectorScale (vright, 8192, vright);
+
+// project a really big axis aligned box onto the plane
+ w = AllocWinding (4);
+
+ VectorSubtract (org, vright, w->p[0]);
+ VectorAdd (w->p[0], vup, w->p[0]);
+
+ VectorAdd (org, vright, w->p[1]);
+ VectorAdd (w->p[1], vup, w->p[1]);
+
+ VectorAdd (org, vright, w->p[2]);
+ VectorSubtract (w->p[2], vup, w->p[2]);
+
+ VectorSubtract (org, vright, w->p[3]);
+ VectorSubtract (w->p[3], vup, w->p[3]);
+
+ w->numpoints = 4;
+
+ return w;
+}
+
+/*
+==================
+CopyWinding
+==================
+*/
+winding_t *CopyWinding (winding_t *w)
+{
+ int size;
+ winding_t *c;
+
+ c = AllocWinding (w->numpoints);
+ size = (int)((winding_t *)0)->p[w->numpoints];
+ memcpy (c, w, size);
+ return c;
+}
+
+/*
+==================
+ReverseWinding
+==================
+*/
+winding_t *ReverseWinding (winding_t *w)
+{
+ int i;
+ winding_t *c;
+
+ c = AllocWinding (w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
+ }
+ c->numpoints = w->numpoints;
+ return c;
+}
+
+
+/*
+=============
+ClipWindingEpsilon
+=============
+*/
+void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
+ vec_t epsilon, winding_t **front, winding_t **back)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ static vec_t dot; // VC 4.2 optimizer bug if not static
+ int i, j;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *f, *b;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->p[i], normal);
+ dot -= dist;
+ dists[i] = dot;
+ if (dot > epsilon)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -epsilon)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ *front = *back = NULL;
+
+ if (!counts[0])
+ {
+ *back = CopyWinding (in);
+ return;
+ }
+ if (!counts[1])
+ {
+ *front = CopyWinding (in);
+ return;
+ }
+
+ maxpts = in->numpoints+4; // cant use counts[0]+2 because
+ // of fp grouping errors
+
+ *front = f = AllocWinding (maxpts);
+ *back = b = AllocWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->p[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+ if (sides[i] == SIDE_BACK)
+ {
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = in->p[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (normal[j] == 1)
+ mid[j] = dist;
+ else if (normal[j] == -1)
+ mid[j] = -dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (mid, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (f->numpoints > maxpts || b->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+ if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
+ Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+}
+
+
+/*
+=============
+ChopWindingInPlace
+=============
+*/
+void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
+{
+ winding_t *in;
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ static vec_t dot; // VC 4.2 optimizer bug if not static
+ int i, j;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *f;
+ int maxpts;
+
+ in = *inout;
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->p[i], normal);
+ dot -= dist;
+ dists[i] = dot;
+ if (dot > epsilon)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -epsilon)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (!counts[0])
+ {
+ FreeWinding (in);
+ *inout = NULL;
+ return;
+ }
+ if (!counts[1])
+ return; // inout stays the same
+
+ maxpts = in->numpoints+4; // cant use counts[0]+2 because
+ // of fp grouping errors
+
+ f = AllocWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->p[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = in->p[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (normal[j] == 1)
+ mid[j] = dist;
+ else if (normal[j] == -1)
+ mid[j] = -dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+
+ if (f->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+ if (f->numpoints > MAX_POINTS_ON_WINDING)
+ Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+
+ FreeWinding (in);
+ *inout = f;
+}
+
+
+/*
+=================
+ChopWinding
+
+Returns the fragment of in that is on the front side
+of the cliping plane. The original is freed.
+=================
+*/
+winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
+{
+ winding_t *f, *b;
+
+ ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
+ FreeWinding (in);
+ if (b)
+ FreeWinding (b);
+ return f;
+}
+
+
+/*
+=================
+CheckWinding
+
+=================
+*/
+void CheckWinding (winding_t *w)
+{
+ int i, j;
+ vec_t *p1, *p2;
+ vec_t d, edgedist;
+ vec3_t dir, edgenormal, facenormal;
+ vec_t area;
+ vec_t facedist;
+
+ if (w->numpoints < 3)
+ Error ("CheckWinding: %i points",w->numpoints);
+
+ area = WindingArea(w);
+ if (area < 1)
+ Error ("CheckWinding: %f area", area);
+
+ WindingPlane (w, facenormal, &facedist);
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ p1 = w->p[i];
+
+ for (j=0 ; j<3 ; j++)
+ if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
+ Error ("CheckFace: BUGUS_RANGE: %f",p1[j]);
+
+ j = i+1 == w->numpoints ? 0 : i+1;
+
+ // check the point is on the face plane
+ d = DotProduct (p1, facenormal) - facedist;
+ if (d < -ON_EPSILON || d > ON_EPSILON)
+ Error ("CheckWinding: point off plane");
+
+ // check the edge isnt degenerate
+ p2 = w->p[j];
+ VectorSubtract (p2, p1, dir);
+
+ if (VectorLength (dir) < ON_EPSILON)
+ Error ("CheckWinding: degenerate edge");
+
+ CrossProduct (facenormal, dir, edgenormal);
+ VectorNormalize (edgenormal, edgenormal);
+ edgedist = DotProduct (p1, edgenormal);
+ edgedist += ON_EPSILON;
+
+ // all other points must be on front side
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ if (j == i)
+ continue;
+ d = DotProduct (w->p[j], edgenormal);
+ if (d > edgedist)
+ Error ("CheckWinding: non-convex");
+ }
+ }
+}
+
+
+/*
+============
+WindingOnPlaneSide
+============
+*/
+int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
+{
+ qboolean front, back;
+ int i;
+ vec_t d;
+
+ front = false;
+ back = false;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ d = DotProduct (w->p[i], normal) - dist;
+ if (d < -ON_EPSILON)
+ {
+ if (front)
+ return SIDE_CROSS;
+ back = true;
+ continue;
+ }
+ if (d > ON_EPSILON)
+ {
+ if (back)
+ return SIDE_CROSS;
+ front = true;
+ continue;
+ }
+ }
+
+ if (back)
+ return SIDE_BACK;
+ if (front)
+ return SIDE_FRONT;
+ return SIDE_ON;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+typedef struct
+{
+ int numpoints;
+ vec3_t p[4]; // variable sized
+} winding_t;
+
+#define MAX_POINTS_ON_WINDING 64
+
+// you can define on_epsilon in the makefile as tighter
+#ifndef ON_EPSILON
+#define ON_EPSILON 0.1
+#endif
+
+winding_t *AllocWinding (int points);
+vec_t WindingArea (winding_t *w);
+void WindingCenter (winding_t *w, vec3_t center);
+void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
+ vec_t epsilon, winding_t **front, winding_t **back);
+winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist);
+winding_t *CopyWinding (winding_t *w);
+winding_t *ReverseWinding (winding_t *w);
+winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist);
+void CheckWinding (winding_t *w);
+void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist);
+void RemoveColinearPoints (winding_t *w);
+int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist);
+void FreeWinding (winding_t *w);
+void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs);
+
+void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon);
+// frees the original if clipped
+
+void pw(winding_t *w);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//
+// qfiles.h: quake file formats
+// This file must be identical in the quake and utils directories
+//
+
+/*
+========================================================================
+
+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;
+
+
+
+/*
+==============================================================================
+
+ .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 allways opposites
+
+typedef struct
+{
+ float normal[3];
+ float dist;
+ int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+// contents flags are seperate bits
+// a given brush can contribute multiple content bits
+// multiple brushes can be in a single leaf
+
+// these definitions also need to be in q_shared.h!
+
+// 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
+
+#define SURF_HINT 0x100 // make a primary bsp splitter
+#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes
+
+
+
+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
+ int value; // light emission, etc
+ 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;
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// scriplib.c
+
+#include "cmdlib.h"
+#include "scriplib.h"
+
+/*
+=============================================================================
+
+ PARSING STUFF
+
+=============================================================================
+*/
+
+typedef struct
+{
+ char filename[1024];
+ char *buffer,*script_p,*end_p;
+ int line;
+} script_t;
+
+#define MAX_INCLUDES 8
+script_t scriptstack[MAX_INCLUDES];
+script_t *script;
+int scriptline;
+
+char token[MAXTOKEN];
+qboolean endofscript;
+qboolean tokenready; // only true if UnGetToken was just called
+
+/*
+==============
+AddScriptToStack
+==============
+*/
+void AddScriptToStack (char *filename)
+{
+ int size;
+
+ script++;
+ if (script == &scriptstack[MAX_INCLUDES])
+ Error ("script file exceeded MAX_INCLUDES");
+ strcpy (script->filename, ExpandPath (filename) );
+
+ size = LoadFile (script->filename, (void **)&script->buffer);
+
+ printf ("entering %s\n", script->filename);
+
+ script->line = 1;
+
+ script->script_p = script->buffer;
+ script->end_p = script->buffer + size;
+}
+
+
+/*
+==============
+LoadScriptFile
+==============
+*/
+void LoadScriptFile (char *filename)
+{
+ script = scriptstack;
+ AddScriptToStack (filename);
+
+ endofscript = false;
+ tokenready = false;
+}
+
+
+/*
+==============
+ParseFromMemory
+==============
+*/
+void ParseFromMemory (char *buffer, int size)
+{
+ script = scriptstack;
+ script++;
+ if (script == &scriptstack[MAX_INCLUDES])
+ Error ("script file exceeded MAX_INCLUDES");
+ strcpy (script->filename, "memory buffer" );
+
+ script->buffer = buffer;
+ script->line = 1;
+ script->script_p = script->buffer;
+ script->end_p = script->buffer + size;
+
+ endofscript = false;
+ tokenready = false;
+}
+
+
+/*
+==============
+UnGetToken
+
+Signals that the current token was not used, and should be reported
+for the next GetToken. Note that
+
+GetToken (true);
+UnGetToken ();
+GetToken (false);
+
+could cross a line boundary.
+==============
+*/
+void UnGetToken (void)
+{
+ tokenready = true;
+}
+
+
+qboolean EndOfScript (qboolean crossline)
+{
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+
+ if (!strcmp (script->filename, "memory buffer"))
+ {
+ endofscript = true;
+ return false;
+ }
+
+ free (script->buffer);
+ if (script == scriptstack+1)
+ {
+ endofscript = true;
+ return false;
+ }
+ script--;
+ scriptline = script->line;
+ printf ("returning to %s\n", script->filename);
+ return GetToken (crossline);
+}
+
+/*
+==============
+GetToken
+==============
+*/
+qboolean GetToken (qboolean crossline)
+{
+ char *token_p;
+
+ if (tokenready) // is a token allready waiting?
+ {
+ tokenready = false;
+ return true;
+ }
+
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+
+//
+// skip space
+//
+skipspace:
+ while (*script->script_p <= 32)
+ {
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+ if (*script->script_p++ == '\n')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ scriptline = script->line++;
+ }
+ }
+
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+
+ // ; # // comments
+ if (*script->script_p == ';' || *script->script_p == '#'
+ || ( script->script_p[0] == '/' && script->script_p[1] == '/') )
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ while (*script->script_p++ != '\n')
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+ goto skipspace;
+ }
+
+ // /* */ comments
+ if (script->script_p[0] == '/' && script->script_p[1] == '*')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ script->script_p+=2;
+ while (script->script_p[0] != '*' && script->script_p[1] != '/')
+ {
+ script->script_p++;
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+ }
+ script->script_p += 2;
+ goto skipspace;
+ }
+
+//
+// copy token
+//
+ token_p = token;
+
+ if (*script->script_p == '"')
+ {
+ // quoted token
+ script->script_p++;
+ while (*script->script_p != '"')
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+ script->script_p++;
+ }
+ else // regular token
+ while ( *script->script_p > 32 && *script->script_p != ';')
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+
+ *token_p = 0;
+
+ if (!strcmp (token, "$include"))
+ {
+ GetToken (false);
+ AddScriptToStack (token);
+ return GetToken (crossline);
+ }
+
+ return true;
+}
+
+
+/*
+==============
+TokenAvailable
+
+Returns true if there is another token on the line
+==============
+*/
+qboolean TokenAvailable (void)
+{
+ char *search_p;
+
+ search_p = script->script_p;
+
+ if (search_p >= script->end_p)
+ return false;
+
+ while ( *search_p <= 32)
+ {
+ if (*search_p == '\n')
+ return false;
+ search_p++;
+ if (search_p == script->end_p)
+ return false;
+
+ }
+
+ if (*search_p == ';')
+ return false;
+
+ return true;
+}
+
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// scriplib.h
+
+#ifndef __CMDLIB__
+#include "cmdlib.h"
+#endif
+
+#define MAXTOKEN 1024
+
+extern char token[MAXTOKEN];
+extern char *scriptbuffer,*script_p,*scriptend_p;
+extern int grabbed;
+extern int scriptline;
+extern qboolean endofscript;
+
+
+void LoadScriptFile (char *filename);
+void ParseFromMemory (char *buffer, int size);
+
+qboolean GetToken (qboolean crossline);
+void UnGetToken (void);
+qboolean TokenAvailable (void);
+
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "threads.h"
+
+#define MAX_THREADS 64
+
+int dispatch;
+int workcount;
+int oldf;
+qboolean pacifier;
+
+qboolean threaded;
+
+/*
+=============
+GetThreadWork
+
+=============
+*/
+int GetThreadWork (void)
+{
+ int r;
+ int f;
+
+ ThreadLock ();
+
+ if (dispatch == workcount)
+ {
+ ThreadUnlock ();
+ return -1;
+ }
+
+ f = 10*dispatch / workcount;
+ if (f != oldf)
+ {
+ oldf = f;
+ if (pacifier)
+ printf ("%i...", f);
+ }
+
+ r = dispatch;
+ dispatch++;
+ ThreadUnlock ();
+
+ return r;
+}
+
+
+void (*workfunction) (int);
+
+void ThreadWorkerFunction (int threadnum)
+{
+ int work;
+
+ while (1)
+ {
+ work = GetThreadWork ();
+ if (work == -1)
+ break;
+//printf ("thread %i, work %i\n", threadnum, work);
+ workfunction(work);
+ }
+}
+
+void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ if (numthreads == -1)
+ ThreadSetDefault ();
+ workfunction = func;
+ RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction);
+}
+
+
+/*
+===================================================================
+
+WIN32
+
+===================================================================
+*/
+#ifdef WIN32
+
+#define USED
+
+#include <windows.h>
+
+int numthreads = -1;
+CRITICAL_SECTION crit;
+static int enter;
+
+void ThreadSetDefault (void)
+{
+ SYSTEM_INFO info;
+
+ if (numthreads == -1) // not set manually
+ {
+ GetSystemInfo (&info);
+ numthreads = info.dwNumberOfProcessors;
+ if (numthreads < 1 || numthreads > 32)
+ numthreads = 1;
+ }
+
+ qprintf ("%i threads\n", numthreads);
+}
+
+
+void ThreadLock (void)
+{
+ if (!threaded)
+ return;
+ EnterCriticalSection (&crit);
+ if (enter)
+ Error ("Recursive ThreadLock\n");
+ enter = 1;
+}
+
+void ThreadUnlock (void)
+{
+ if (!threaded)
+ return;
+ if (!enter)
+ Error ("ThreadUnlock without lock\n");
+ enter = 0;
+ LeaveCriticalSection (&crit);
+}
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int threadid[MAX_THREADS];
+ HANDLE threadhandle[MAX_THREADS];
+ int i;
+ int start, end;
+
+ start = I_FloatTime ();
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+ threaded = true;
+
+ //
+ // run threads in parallel
+ //
+ InitializeCriticalSection (&crit);
+
+ if (numthreads == 1)
+ { // use same thread
+ func (0);
+ }
+ else
+ {
+ for (i=0 ; i<numthreads ; i++)
+ {
+ threadhandle[i] = CreateThread(
+ NULL, // LPSECURITY_ATTRIBUTES lpsa,
+ 0, // DWORD cbStack,
+ (LPTHREAD_START_ROUTINE)func, // LPTHREAD_START_ROUTINE lpStartAddr,
+ (LPVOID)i, // LPVOID lpvThreadParm,
+ 0, // DWORD fdwCreate,
+ &threadid[i]);
+ }
+
+ for (i=0 ; i<numthreads ; i++)
+ WaitForSingleObject (threadhandle[i], INFINITE);
+ }
+ DeleteCriticalSection (&crit);
+
+ threaded = false;
+ end = I_FloatTime ();
+ if (pacifier)
+ printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+===================================================================
+
+OSF1
+
+===================================================================
+*/
+
+#ifdef __osf__
+#define USED
+
+int numthreads = 4;
+
+void ThreadSetDefault (void)
+{
+ if (numthreads == -1) // not set manually
+ {
+ numthreads = 4;
+ }
+}
+
+
+#include <pthread.h>
+
+pthread_mutex_t *my_mutex;
+
+void ThreadLock (void)
+{
+ if (my_mutex)
+ pthread_mutex_lock (my_mutex);
+}
+
+void ThreadUnlock (void)
+{
+ if (my_mutex)
+ pthread_mutex_unlock (my_mutex);
+}
+
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int i;
+ pthread_t work_threads[MAX_THREADS];
+ pthread_addr_t status;
+ pthread_attr_t attrib;
+ pthread_mutexattr_t mattrib;
+ int start, end;
+
+ start = I_FloatTime ();
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+ threaded = true;
+
+ if (pacifier)
+ setbuf (stdout, NULL);
+
+ if (!my_mutex)
+ {
+ my_mutex = malloc (sizeof(*my_mutex));
+ if (pthread_mutexattr_create (&mattrib) == -1)
+ Error ("pthread_mutex_attr_create failed");
+ if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
+ Error ("pthread_mutexattr_setkind_np failed");
+ if (pthread_mutex_init (my_mutex, mattrib) == -1)
+ Error ("pthread_mutex_init failed");
+ }
+
+ if (pthread_attr_create (&attrib) == -1)
+ Error ("pthread_attr_create failed");
+ if (pthread_attr_setstacksize (&attrib, 0x100000) == -1)
+ Error ("pthread_attr_setstacksize failed");
+
+ for (i=0 ; i<numthreads ; i++)
+ {
+ if (pthread_create(&work_threads[i], attrib
+ , (pthread_startroutine_t)func, (pthread_addr_t)i) == -1)
+ Error ("pthread_create failed");
+ }
+
+ for (i=0 ; i<numthreads ; i++)
+ {
+ if (pthread_join (work_threads[i], &status) == -1)
+ Error ("pthread_join failed");
+ }
+
+ threaded = false;
+
+ end = I_FloatTime ();
+ if (pacifier)
+ printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+===================================================================
+
+IRIX
+
+===================================================================
+*/
+
+#ifdef _MIPS_ISA
+#define USED
+
+#include <task.h>
+#include <abi_mutex.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+
+
+int numthreads = -1;
+abilock_t lck;
+
+void ThreadSetDefault (void)
+{
+ if (numthreads == -1)
+ numthreads = prctl(PR_MAXPPROCS);
+ printf ("%i threads\n", numthreads);
+//@@
+ usconfig (CONF_INITUSERS, numthreads);
+}
+
+
+void ThreadLock (void)
+{
+ spin_lock (&lck);
+}
+
+void ThreadUnlock (void)
+{
+ release_lock (&lck);
+}
+
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int i;
+ int pid[MAX_THREADS];
+ int start, end;
+
+ start = I_FloatTime ();
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+ threaded = true;
+
+ if (pacifier)
+ setbuf (stdout, NULL);
+
+ init_lock (&lck);
+
+ for (i=0 ; i<numthreads-1 ; i++)
+ {
+ pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i
+ , NULL, 0x100000);
+// pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i
+// , NULL, 0x80000);
+ if (pid[i] == -1)
+ {
+ perror ("sproc");
+ Error ("sproc failed");
+ }
+ }
+
+ func(i);
+
+ for (i=0 ; i<numthreads-1 ; i++)
+ wait (NULL);
+
+ threaded = false;
+
+ end = I_FloatTime ();
+ if (pacifier)
+ printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+=======================================================================
+
+ SINGLE THREAD
+
+=======================================================================
+*/
+
+#ifndef USED
+
+int numthreads = 1;
+
+void ThreadSetDefault (void)
+{
+ numthreads = 1;
+}
+
+void ThreadLock (void)
+{
+}
+
+void ThreadUnlock (void)
+{
+}
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int i;
+ int start, end;
+
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+ start = I_FloatTime ();
+#ifdef NeXT
+ if (pacifier)
+ setbuf (stdout, NULL);
+#endif
+ func(0);
+
+ end = I_FloatTime ();
+ if (pacifier)
+ printf (" (%i)\n", end-start);
+}
+
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+extern int numthreads;
+
+void ThreadSetDefault (void);
+int GetThreadWork (void);
+void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int));
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int));
+void ThreadLock (void);
+void ThreadUnlock (void);
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//
+// trilib.c: library for loading triangles from an Alias triangle file
+//
+
+#include <stdio.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "trilib.h"
+
+// on disk representation of a face
+
+
+#define FLOAT_START 99999.0
+#define FLOAT_END -FLOAT_START
+#define MAGIC 123322
+
+//#define NOISY 1
+
+typedef struct {
+ float v[3];
+} vector;
+
+typedef struct
+{
+ vector n; /* normal */
+ vector p; /* point */
+ vector c; /* color */
+ float u; /* u */
+ float v; /* v */
+} aliaspoint_t;
+
+typedef struct {
+ aliaspoint_t pt[3];
+} tf_triangle;
+
+
+void ByteSwapTri (tf_triangle *tri)
+{
+ int i;
+
+ for (i=0 ; i<sizeof(tf_triangle)/4 ; i++)
+ {
+ ((int *)tri)[i] = BigLong (((int *)tri)[i]);
+ }
+}
+
+void LoadTriangleList (char *filename, triangle_t **pptri, int *numtriangles)
+{
+ FILE *input;
+ float start;
+ char name[256], tex[256];
+ int i, count, magic;
+ tf_triangle tri;
+ triangle_t *ptri;
+ int iLevel;
+ int exitpattern;
+ float t;
+
+ t = -FLOAT_START;
+ *((unsigned char *)&exitpattern + 0) = *((unsigned char *)&t + 3);
+ *((unsigned char *)&exitpattern + 1) = *((unsigned char *)&t + 2);
+ *((unsigned char *)&exitpattern + 2) = *((unsigned char *)&t + 1);
+ *((unsigned char *)&exitpattern + 3) = *((unsigned char *)&t + 0);
+
+ if ((input = fopen(filename, "rb")) == 0)
+ Error ("reader: could not open file '%s'", filename);
+
+ iLevel = 0;
+
+ fread(&magic, sizeof(int), 1, input);
+ if (BigLong(magic) != MAGIC)
+ Error ("%s is not a Alias object separated triangle file, magic number is wrong.", filename);
+
+ ptri = malloc (MAXTRIANGLES * sizeof(triangle_t));
+
+ *pptri = ptri;
+
+ while (feof(input) == 0) {
+ if (fread(&start, sizeof(float), 1, input) < 1)
+ break;
+ *(int *)&start = BigLong(*(int *)&start);
+ if (*(int *)&start != exitpattern)
+ {
+ if (start == FLOAT_START) {
+ /* Start of an object or group of objects. */
+ i = -1;
+ do {
+ /* There are probably better ways to read a string from */
+ /* a file, but this does allow you to do error checking */
+ /* (which I'm not doing) on a per character basis. */
+ ++i;
+ fread( &(name[i]), sizeof( char ), 1, input);
+ } while( name[i] != '\0' );
+
+// indent();
+// fprintf(stdout,"OBJECT START: %s\n",name);
+ fread( &count, sizeof(int), 1, input);
+ count = BigLong(count);
+ ++iLevel;
+ if (count != 0) {
+// indent();
+// fprintf(stdout,"NUMBER OF TRIANGLES: %d\n",count);
+
+ i = -1;
+ do {
+ ++i;
+ fread( &(tex[i]), sizeof( char ), 1, input);
+ } while( tex[i] != '\0' );
+
+// indent();
+// fprintf(stdout," Object texture name: '%s'\n",tex);
+ }
+
+ /* Else (count == 0) this is the start of a group, and */
+ /* no texture name is present. */
+ }
+ else if (start == FLOAT_END) {
+ /* End of an object or group. Yes, the name should be */
+ /* obvious from context, but it is in here just to be */
+ /* safe and to provide a little extra information for */
+ /* those who do not wish to write a recursive reader. */
+ /* Mia culpa. */
+ --iLevel;
+ i = -1;
+ do {
+ ++i;
+ fread( &(name[i]), sizeof( char ), 1, input);
+ } while( name[i] != '\0' );
+
+// indent();
+// fprintf(stdout,"OBJECT END: %s\n",name);
+ continue;
+ }
+ }
+
+//
+// read the triangles
+//
+ for (i = 0; i < count; ++i) {
+ int j;
+
+ fread( &tri, sizeof(tf_triangle), 1, input );
+ ByteSwapTri (&tri);
+ for (j=0 ; j<3 ; j++)
+ {
+ int k;
+
+ for (k=0 ; k<3 ; k++)
+ {
+ ptri->verts[j][k] = tri.pt[j].p.v[k];
+ }
+ }
+
+ ptri++;
+
+ if ((ptri - *pptri) >= MAXTRIANGLES)
+ Error ("Error: too many triangles; increase MAXTRIANGLES\n");
+ }
+ }
+
+ *numtriangles = ptri - *pptri;
+
+ fclose (input);
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//
+// trilib.h: header file for loading triangles from an Alias triangle file
+//
+#define MAXTRIANGLES 2048
+
+typedef struct {
+ vec3_t verts[3];
+} triangle_t;
+
+void LoadTriangleList (char *filename, triangle_t **pptri, int *numtriangles);
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+{-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},
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+char mip_prefix[1024]; // directory to dump the textures in
+
+qboolean colormap_issued;
+byte colormap_palette[768];
+
+/*
+==============
+RemapZero
+
+Replaces all 0 bytes in an image with the closest palette entry.
+This is because NT won't let us change index 0, so any palette
+animation leaves those pixels untouched.
+==============
+*/
+void RemapZero (byte *pixels, byte *palette, int width, int height)
+{
+ int i, c;
+ int alt_zero;
+ int value, best;
+
+ alt_zero = 0;
+ best = 9999999;
+ for (i=1 ; i<255 ; i++)
+ {
+ value = palette[i*3+0]+palette[i*3+1]+palette[i*3+2];
+ if (value < best)
+ {
+ best = value;
+ alt_zero = i;
+ }
+ }
+
+ c = width*height;
+ for (i=0 ; i<c ; i++)
+ if (pixels[i] == 0)
+ pixels[i] = alt_zero;
+}
+
+/*
+==============
+Cmd_Grab
+
+$grab filename x y width height
+==============
+*/
+void Cmd_Grab (void)
+{
+ int xl,yl,w,h,y;
+ byte *cropped;
+ char savename[1024];
+ char dest[1024];
+
+ GetToken (false);
+
+ if (token[0] == '/' || token[0] == '\\')
+ sprintf (savename, "%s%s.pcx", gamedir, token+1);
+ else
+ sprintf (savename, "%spics/%s.pcx", gamedir, token);
+
+ if (g_release)
+ {
+ if (token[0] == '/' || token[0] == '\\')
+ sprintf (dest, "%s.pcx", token+1);
+ else
+ sprintf (dest, "pics/%s.pcx", token);
+
+ ReleaseFile (dest);
+ return;
+ }
+
+ GetToken (false);
+ xl = atoi (token);
+ GetToken (false);
+ yl = atoi (token);
+ GetToken (false);
+ w = atoi (token);
+ GetToken (false);
+ h = atoi (token);
+
+ if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight)
+ Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h);
+
+ // crop it to the proper size
+ cropped = malloc (w*h);
+ for (y=0 ; y<h ; y++)
+ {
+ memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
+ }
+
+ // save off the new image
+ printf ("saving %s\n", savename);
+ CreatePath (savename);
+ WritePCXfile (savename, cropped, w, h, lbmpalette);
+
+ free (cropped);
+}
+
+/*
+==============
+Cmd_Raw
+
+$grab filename x y width height
+==============
+*/
+void Cmd_Raw (void)
+{
+ int xl,yl,w,h,y;
+ byte *cropped;
+ char savename[1024];
+ char dest[1024];
+
+ GetToken (false);
+
+ sprintf (savename, "%s%s.lmp", gamedir, token);
+
+ if (g_release)
+ {
+ sprintf (dest, "%s.lmp", token);
+ ReleaseFile (dest);
+ return;
+ }
+
+ GetToken (false);
+ xl = atoi (token);
+ GetToken (false);
+ yl = atoi (token);
+ GetToken (false);
+ w = atoi (token);
+ GetToken (false);
+ h = atoi (token);
+
+ if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight)
+ Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h);
+
+ // crop it to the proper size
+ cropped = malloc (w*h);
+ for (y=0 ; y<h ; y++)
+ {
+ memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
+ }
+
+ // save off the new image
+ printf ("saving %s\n", savename);
+ CreatePath (savename);
+
+ SaveFile (savename, cropped, w*h);
+
+ free (cropped);
+}
+
+/*
+=============================================================================
+
+COLORMAP GRABBING
+
+=============================================================================
+*/
+
+/*
+===============
+BestColor
+===============
+*/
+byte BestColor (int r, int g, int b, int start, int stop)
+{
+ int i;
+ int dr, dg, db;
+ int bestdistortion, distortion;
+ int bestcolor;
+ byte *pal;
+
+//
+// let any color go to 0 as a last resort
+//
+ bestdistortion = 256*256*4;
+ bestcolor = 0;
+
+ pal = colormap_palette + start*3;
+ for (i=start ; i<= stop ; i++)
+ {
+ dr = r - (int)pal[0];
+ dg = g - (int)pal[1];
+ db = b - (int)pal[2];
+ pal += 3;
+ distortion = dr*dr + dg*dg + db*db;
+ if (distortion < bestdistortion)
+ {
+ if (!distortion)
+ return i; // perfect match
+
+ bestdistortion = distortion;
+ bestcolor = i;
+ }
+ }
+
+ return bestcolor;
+}
+
+
+/*
+==============
+Cmd_Colormap
+
+$colormap filename
+
+ the brightes colormap is first in the table (FIXME: reverse this now?)
+
+ 64 rows of 256 : lightmaps
+ 256 rows of 256 : translucency table
+==============
+*/
+void Cmd_Colormap (void)
+{
+ int levels, brights;
+ int l, c;
+ float frac, red, green, blue;
+ float range;
+ byte *cropped, *lump_p;
+ char savename[1024];
+ char dest[1024];
+
+ colormap_issued = true;
+ if (!g_release)
+ memcpy (colormap_palette, lbmpalette, 768);
+
+ if (!TokenAvailable ())
+ { // just setting colormap_issued
+ return;
+ }
+
+ GetToken (false);
+ sprintf (savename, "%spics/%s.pcx", gamedir, token);
+
+ if (g_release)
+ {
+ sprintf (dest, "pics/%s.pcx", token);
+ ReleaseFile (dest);
+ return;
+ }
+
+ range = 2;
+ levels = 64;
+ brights = 1; // ignore 255 (transparent)
+
+ cropped = malloc((levels+256)*256);
+ lump_p = cropped;
+
+// shaded levels
+ for (l=0;l<levels;l++)
+ {
+ frac = range - range*(float)l/(levels-1);
+ for (c=0 ; c<256-brights ; c++)
+ {
+ red = lbmpalette[c*3];
+ green = lbmpalette[c*3+1];
+ blue = lbmpalette[c*3+2];
+
+ red = (int)(red*frac+0.5);
+ green = (int)(green*frac+0.5);
+ blue = (int)(blue*frac+0.5);
+
+//
+// note: 254 instead of 255 because 255 is the transparent color, and we
+// don't want anything remapping to that
+// don't use color 0, because NT can't remap that (or 255)
+//
+ *lump_p++ = BestColor(red,green,blue, 1, 254);
+ }
+
+ // fullbrights allways stay the same
+ for ( ; c<256 ; c++)
+ *lump_p++ = c;
+ }
+
+// 66% transparancy table
+ for (l=0;l<255;l++)
+ {
+ for (c=0 ; c<255 ; c++)
+ {
+ red = lbmpalette[c*3]*0.33 + lbmpalette[l*3]*0.66;
+ green = lbmpalette[c*3+1]*0.33 + lbmpalette[l*3+1]*0.66;
+ blue = lbmpalette[c*3+2]*0.33 + lbmpalette[l*3+2]*0.66;
+
+ *lump_p++ = BestColor(red,green,blue, 1, 254);
+ }
+ *lump_p++ = 255;
+ }
+ for (c=0 ; c<256 ; c++)
+ *lump_p++ = 255;
+
+ // save off the new image
+ printf ("saving %s\n", savename);
+ CreatePath (savename);
+ WritePCXfile (savename, cropped, 256, levels+256, lbmpalette);
+
+ free (cropped);
+}
+
+/*
+=============================================================================
+
+MIPTEX GRABBING
+
+=============================================================================
+*/
+
+byte pixdata[256];
+
+int d_red, d_green, d_blue;
+
+byte palmap[32][32][32];
+qboolean palmap_built;
+
+/*
+=============
+FindColor
+=============
+*/
+int FindColor (int r, int g, int b)
+{
+ int bestcolor;
+
+ if (r > 255)
+ r = 255;
+ if (r < 0)
+ r = 0;
+ if (g > 255)
+ g = 255;
+ if (g < 0)
+ g = 0;
+ if (b > 255)
+ b = 255;
+ if (b < 0)
+ b = 0;
+#ifndef TABLECOLORS
+ bestcolor = BestColor (r, g, b, 0, 254);
+#else
+ bestcolor = palmap[r>>3][g>>3][b>>3];
+#endif
+
+ return bestcolor;
+}
+
+
+void BuildPalmap (void)
+{
+#ifdef TABLECOLORS
+ int r, g, b;
+ int bestcolor;
+
+ if (palmap_built)
+ return;
+ palmap_built = true;
+
+ for (r=4 ; r<256 ; r+=8)
+ {
+ for (g=4 ; g<256 ; g+=8)
+ {
+ for (b=4 ; b<256 ; b+=8)
+ {
+ bestcolor = BestColor (r, g, b, 1, 254);
+ palmap[r>>3][g>>3][b>>3] = bestcolor;
+ }
+ }
+ }
+#endif
+
+ if (!colormap_issued)
+ Error ("You must issue a $colormap command first");
+
+}
+
+/*
+=============
+AveragePixels
+=============
+*/
+byte AveragePixels (int count)
+{
+ int r,g,b;
+ int i;
+ int vis;
+ int pix;
+ int bestcolor;
+ byte *pal;
+ int fullbright;
+
+ vis = 0;
+ r = g = b = 0;
+ fullbright = 0;
+ for (i=0 ; i<count ; i++)
+ {
+ pix = pixdata[i];
+
+ r += lbmpalette[pix*3];
+ g += lbmpalette[pix*3+1];
+ b += lbmpalette[pix*3+2];
+ vis++;
+ }
+
+ r /= vis;
+ g /= vis;
+ b /= vis;
+
+ // error diffusion
+ r += d_red;
+ g += d_green;
+ b += d_blue;
+
+//
+// find the best color
+//
+ bestcolor = FindColor (r, g, b);
+
+ // error diffusion
+ pal = colormap_palette + bestcolor*3;
+ d_red = r - (int)pal[0];
+ d_green = g - (int)pal[1];
+ d_blue = b - (int)pal[2];
+
+ return bestcolor;
+}
+
+
+typedef enum
+{
+ pt_contents,
+ pt_flags,
+ pt_animvalue,
+ pt_flagvalue
+} parmtype_t;
+
+typedef struct
+{
+ char *name;
+ int flags;
+ parmtype_t type;
+} mipparm_t;
+
+mipparm_t mipparms[] =
+{
+ // utility content attributes
+ {"water", CONTENTS_WATER, pt_contents},
+ {"slime", CONTENTS_SLIME, pt_contents}, // mildly damaging
+ {"lava", CONTENTS_LAVA, pt_contents}, // very damaging
+ {"window", CONTENTS_WINDOW, pt_contents}, // solid, but doesn't eat internal textures
+ {"mist", CONTENTS_MIST, pt_contents}, // non-solid window
+ {"origin", CONTENTS_ORIGIN, pt_contents}, // center of rotating brushes
+ {"playerclip", CONTENTS_PLAYERCLIP, pt_contents},
+ {"monsterclip", CONTENTS_MONSTERCLIP, pt_contents},
+
+ // utility surface attributes
+ {"hint", SURF_HINT, pt_flags},
+ {"skip", SURF_SKIP, pt_flags},
+ {"light", SURF_LIGHT, pt_flagvalue}, // value is the light quantity
+
+ // texture chaining
+ {"anim", 0, pt_animvalue}, // value is the next animation
+
+ // server attributes
+ {"slick", SURF_SLICK, pt_flags},
+
+ // drawing attributes
+ {"sky", SURF_SKY, pt_flags},
+ {"warping", SURF_WARP, pt_flags}, // only valid with 64x64 textures
+ {"trans33", SURF_TRANS33, pt_flags}, // translucent should allso set fullbright
+ {"trans66", SURF_TRANS66, pt_flags},
+ {"flowing", SURF_FLOWING, pt_flags}, // flow direction towards angle 0
+ {"nodraw", SURF_NODRAW, pt_flags}, // for clip textures and trigger textures
+
+ {NULL, 0, pt_contents}
+};
+
+
+
+/*
+==============
+Cmd_Mip
+
+$mip filename x y width height <OPTIONS>
+must be multiples of sixteen
+SURF_WINDOW
+==============
+*/
+void Cmd_Mip (void)
+{
+ int x,y,xl,yl,xh,yh,w,h;
+ byte *screen_p, *source;
+ int linedelta;
+ miptex_t *qtex;
+ int miplevel, mipstep;
+ int xx, yy, pix;
+ int count;
+ int flags, value, contents;
+ mipparm_t *mp;
+ char lumpname[64];
+ byte *lump_p;
+ char filename[1024];
+ char animname[64];
+
+ GetToken (false);
+ strcpy (lumpname, token);
+
+ GetToken (false);
+ xl = atoi (token);
+ GetToken (false);
+ yl = atoi (token);
+ GetToken (false);
+ w = atoi (token);
+ GetToken (false);
+ h = atoi (token);
+
+ if ( (w & 15) || (h & 15) )
+ Error ("line %i: miptex sizes must be multiples of 16", scriptline);
+
+ flags = 0;
+ contents = 0;
+ value = 0;
+
+ animname[0] = 0;
+
+ // get optional flags and values
+ while (TokenAvailable ())
+ {
+ GetToken (false);
+
+ for (mp=mipparms ; mp->name ; mp++)
+ {
+ if (!strcmp(mp->name, token))
+ {
+ switch (mp->type)
+ {
+ case pt_animvalue:
+ GetToken (false); // specify the next animation frame
+ strcpy (animname, token);
+ break;
+ case pt_flags:
+ flags |= mp->flags;
+ break;
+ case pt_contents:
+ contents |= mp->flags;
+ break;
+ case pt_flagvalue:
+ flags |= mp->flags;
+ GetToken (false); // specify the light value
+ value = atoi(token);
+ break;
+ }
+ break;
+ }
+ }
+ if (!mp->name)
+ Error ("line %i: unknown parm %s", scriptline, token);
+ }
+
+ sprintf (filename, "%stextures/%s/%s.wal", gamedir, mip_prefix, lumpname);
+ if (g_release)
+ return; // textures are only released by $maps
+
+ xh = xl+w;
+ yh = yl+h;
+
+ qtex = malloc (sizeof(miptex_t) + w*h*2);
+ memset (qtex, 0, sizeof(miptex_t));
+
+ qtex->width = LittleLong(w);
+ qtex->height = LittleLong(h);
+ qtex->flags = LittleLong(flags);
+ qtex->contents = LittleLong(contents);
+ qtex->value = LittleLong(value);
+ sprintf (qtex->name, "%s/%s", mip_prefix, lumpname);
+ if (animname[0])
+ sprintf (qtex->animname, "%s/%s", mip_prefix, animname);
+
+ lump_p = (byte *)(&qtex->value+1);
+
+ screen_p = byteimage + yl*byteimagewidth + xl;
+ linedelta = byteimagewidth - w;
+
+ source = lump_p;
+ qtex->offsets[0] = LittleLong(lump_p - (byte *)qtex);
+
+ for (y=yl ; y<yh ; y++)
+ {
+ for (x=xl ; x<xh ; x++)
+ {
+ pix = *screen_p++;
+ if (pix == 255)
+ pix = 1; // should never happen
+ *lump_p++ = pix;
+ }
+ screen_p += linedelta;
+ }
+
+//
+// subsample for greater mip levels
+//
+ d_red = d_green = d_blue = 0; // no distortion yet
+
+ for (miplevel = 1 ; miplevel<4 ; miplevel++)
+ {
+ qtex->offsets[miplevel] = LittleLong(lump_p - (byte *)qtex);
+
+ mipstep = 1<<miplevel;
+ for (y=0 ; y<h ; y+=mipstep)
+ {
+
+ for (x = 0 ; x<w ; x+= mipstep)
+ {
+ count = 0;
+ for (yy=0 ; yy<mipstep ; yy++)
+ for (xx=0 ; xx<mipstep ; xx++)
+ {
+ pixdata[count] = source[ (y+yy)*w + x + xx ];
+ count++;
+ }
+ *lump_p++ = AveragePixels (count);
+ }
+ }
+ }
+
+//
+// dword align the size
+//
+ while ((int)lump_p&3)
+ *lump_p++ = 0;
+
+//
+// write it out
+//
+ printf ("writing %s\n", filename);
+ SaveFile (filename, (byte *)qtex, lump_p - (byte *)qtex);
+
+ free (qtex);
+}
+
+/*
+===============
+Cmd_Mippal
+===============
+*/
+void Cmd_Mippal (void)
+{
+ colormap_issued = true;
+ if (g_release)
+ return;
+
+ memcpy (colormap_palette, lbmpalette, 768);
+
+ BuildPalmap();
+}
+
+
+/*
+===============
+Cmd_Mipdir
+===============
+*/
+void Cmd_Mipdir (void)
+{
+ char filename[1024];
+
+ GetToken (false);
+ strcpy (mip_prefix, token);
+ // create the directory if needed
+ sprintf (filename, "%stextures", gamedir, mip_prefix);
+ Q_mkdir (filename);
+ sprintf (filename, "%stextures/%s", gamedir, mip_prefix);
+ Q_mkdir (filename);
+}
+
+
+/*
+=============================================================================
+
+ENVIRONMENT MAP GRABBING
+
+Creates six pcx files from tga files without any palette edge seams
+also copies the tga files for GL rendering.
+=============================================================================
+*/
+
+// 3dstudio environment map suffixes
+char *suf[6] = {"rt", "ft", "lf", "bk", "up", "dn"};
+
+/*
+=================
+Cmd_Environment
+=================
+*/
+void Cmd_Environment (void)
+{
+ char name[1024];
+ int i, x, y;
+ byte image[256*256];
+ byte *tga;
+
+ GetToken (false);
+
+ if (g_release)
+ {
+ for (i=0 ; i<6 ; i++)
+ {
+ sprintf (name, "env/%s%s.pcx", token, suf[i]);
+ ReleaseFile (name);
+ sprintf (name, "env/%s%s.tga", token, suf[i]);
+ ReleaseFile (name);
+ }
+ return;
+ }
+ // get the palette
+ BuildPalmap ();
+
+ sprintf (name, "%senv/", gamedir);
+ CreatePath (name);
+
+ // convert the images
+ for (i=0 ; i<6 ; i++)
+ {
+ sprintf (name, "%senv/%s%s.tga", gamedir, token, suf[i]);
+ printf ("loading %s...\n", name);
+ LoadTGA (name, &tga, NULL, NULL);
+
+ for (y=0 ; y<256 ; y++)
+ {
+ for (x=0 ; x<256 ; x++)
+ {
+ image[y*256+x] = FindColor (tga[(y*256+x)*4+0],tga[(y*256+x)*4+1],tga[(y*256+x)*4+2]);
+ }
+ }
+ free (tga);
+ sprintf (name, "%senv/%s%s.pcx", gamedir, token, suf[i]);
+ if (FileTime (name) != -1)
+ printf ("%s already exists, not overwriting.\n", name);
+ else
+ WritePCXfile (name, image, 256, 256, colormap_palette);
+ }
+}
+
--- /dev/null
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = qdata
+EXE = $(ODIR)/qdata
+all: $(EXE)
+
+_next:
+ make "CFLAGS = -c -g -I../common" "ODIR = next"
+
+_irix:
+ make "CFLAGS = -c -Ofast=ip32_10k -I../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+
+_irixdebug:
+ make "CFLAGS = -c -O2 -g -I../common -Xcpluscomm" "LDFLAGS = -g" "ODIR = irix"
+
+_irixinst:
+ make "CFLAGS = -c -Ofast=ip32_10k -I../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+ cp irix/$(EXEBASE) /limbo/quake/bin_irix
+
+_irixclean:
+ rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+ make "CFLAGS = -c -O4 -I../common -threads" "LDFLAGS = -threads" "ODIR = osf"
+
+clean:
+ rm -f next/*.o next/$(EXEBASE)
+ rm -f osf/*.o osf/$(EXEBASE)
+ rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+ cp next/$(EXEBASE) /limbo/quake2/bin_next
+ cp osf/$(EXEBASE) /limbo/quake2/bin_osf
+ cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/qdata.o $(ODIR)/models.o $(ODIR)/sprites.o $(ODIR)/images.o $(ODIR)/cmdlib.o $(ODIR)/scriplib.o $(ODIR)/lbmlib.o $(ODIR)/mathlib.o $(ODIR)/l3dslib.o $(ODIR)/trilib.o $(ODIR)/threads.o $(ODIR)/tables.o
+
+$(EXE) : $(FILES)
+ cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
+
+$(ODIR)/qdata.o : qdata.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/models.o : models.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/sprites.o : sprites.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/images.o : images.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/tables.o : tables.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../common/cmdlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../common/scriplib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/lbmlib.o : ../common/lbmlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../common/mathlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/trilib.o : ../common/trilib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/l3dslib.o : ../common/l3dslib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../common/threads.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+//=================================================================
+
+typedef struct
+{
+ int numnormals;
+ vec3_t normalsum;
+} vertexnormals_t;
+
+typedef struct
+{
+ vec3_t v;
+ int lightnormalindex;
+} trivert_t;
+
+typedef struct
+{
+ vec3_t mins, maxs;
+ char name[16];
+ trivert_t v[MAX_VERTS];
+} frame_t;
+
+//================================================================
+
+frame_t g_frames[MAX_FRAMES];
+
+dmdl_t model;
+
+
+float scale_up; // set by $scale
+vec3_t adjust; // set by $origin
+int g_fixedwidth, g_fixedheight; // set by $skinsize
+
+
+//
+// base frame info
+//
+vec3_t base_xyz[MAX_VERTS];
+dstvert_t base_st[MAX_VERTS];
+dtriangle_t triangles[MAX_TRIANGLES];
+
+int triangle_st[MAX_TRIANGLES][3][2];
+
+// the command list holds counts, s/t values, and xyz indexes
+// that are valid for every frame
+int commands[16384];
+int numcommands;
+int numglverts;
+int used[MAX_TRIANGLES];
+
+char g_skins[MAX_MD2SKINS][64];
+
+char cdarchive[1024];
+char cdpartial[1024];
+char cddir[1024];
+
+char modelname[64]; // empty unless $modelname issued (players)
+
+#define NUMVERTEXNORMALS 162
+
+float avertexnormals[NUMVERTEXNORMALS][3] = {
+#include "anorms.h"
+};
+
+FILE *headerouthandle = NULL;
+
+//==============================================================
+
+/*
+===============
+ClearModel
+===============
+*/
+void ClearModel (void)
+{
+ memset (&model, 0, sizeof(model));
+
+ modelname[0] = 0;
+ scale_up = 1.0;
+ VectorCopy (vec3_origin, adjust);
+ g_fixedwidth = g_fixedheight = 0;
+ g_skipmodel = false;
+}
+
+
+void H_printf(char *fmt, ...)
+{
+ va_list argptr;
+ char name[1024];
+
+ if (!headerouthandle)
+ {
+ sprintf (name, "%s/tris.h", cddir);
+ headerouthandle = SafeOpenWrite (name);
+ fprintf(headerouthandle, "// %s\n\n", cddir);
+ fprintf(headerouthandle, "// This file generated by qdata - Do NOT Modify\n\n");
+ }
+
+ va_start (argptr, fmt);
+ vfprintf (headerouthandle, fmt, argptr);
+ va_end (argptr);
+}
+
+
+/*
+============
+WriteModelFile
+============
+*/
+void WriteModelFile (FILE *modelouthandle)
+{
+ int i;
+ dmdl_t modeltemp;
+ int j, k;
+ frame_t *in;
+ daliasframe_t *out;
+ byte buffer[MAX_VERTS*4+128];
+ float v;
+ int c_on, c_off;
+
+ model.ident = IDALIASHEADER;
+ model.version = ALIAS_VERSION;
+ model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];
+ model.num_glcmds = numcommands;
+ model.ofs_skins = sizeof(dmdl_t);
+ model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
+ model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);
+ model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);
+ model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;
+ model.ofs_end = model.ofs_glcmds + model.num_glcmds*4;
+
+ //
+ // write out the model header
+ //
+ for (i=0 ; i<sizeof(dmdl_t)/4 ; i++)
+ ((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);
+
+ SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));
+
+ //
+ // write out the skin names
+ //
+ SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);
+
+ //
+ // write out the texture coordinates
+ //
+ c_on = c_off = 0;
+ for (i=0 ; i<model.num_st ; i++)
+ {
+ base_st[i].s = LittleShort (base_st[i].s);
+ base_st[i].t = LittleShort (base_st[i].t);
+ }
+
+ SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));
+
+ //
+ // write out the triangles
+ //
+ for (i=0 ; i<model.num_tris ; i++)
+ {
+ int j;
+ dtriangle_t tri;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);
+ tri.index_st[j] = LittleShort (triangles[i].index_st[j]);
+ }
+
+ SafeWrite (modelouthandle, &tri, sizeof(tri));
+ }
+
+ //
+ // write out the frames
+ //
+ for (i=0 ; i<model.num_frames ; i++)
+ {
+ in = &g_frames[i];
+ out = (daliasframe_t *)buffer;
+
+ strcpy (out->name, in->name);
+ for (j=0 ; j<3 ; j++)
+ {
+ out->scale[j] = (in->maxs[j] - in->mins[j])/255;
+ out->translate[j] = in->mins[j];
+ }
+
+ for (j=0 ; j<model.num_xyz ; j++)
+ {
+ // all of these are byte values, so no need to deal with endianness
+ out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
+
+ for (k=0 ; k<3 ; k++)
+ {
+ // scale to byte values & min/max check
+ v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );
+
+ // clamp, so rounding doesn't wrap from 255.6 to 0
+ if (v > 255.0)
+ v = 255.0;
+ if (v < 0)
+ v = 0;
+ out->verts[j].v[k] = v;
+ }
+ }
+
+ for (j=0 ; j<3 ; j++)
+ {
+ out->scale[j] = LittleFloat (out->scale[j]);
+ out->translate[j] = LittleFloat (out->translate[j]);
+ }
+
+ SafeWrite (modelouthandle, out, model.framesize);
+ }
+
+ //
+ // write out glcmds
+ //
+ SafeWrite (modelouthandle, commands, numcommands*4);
+}
+
+
+/*
+===============
+FinishModel
+===============
+*/
+void FinishModel (void)
+{
+ FILE *modelouthandle;
+ int i;
+ char name[1024];
+
+ if (!model.num_frames)
+ return;
+
+//
+// copy to release directory tree if doing a release build
+//
+ if (g_release)
+ {
+ if (modelname[0])
+ sprintf (name, "%s", modelname);
+ else
+ sprintf (name, "%s/tris.md2", cdpartial);
+ ReleaseFile (name);
+
+ for (i=0 ; i<model.num_skins ; i++)
+ {
+ ReleaseFile (g_skins[i]);
+ }
+ model.num_frames = 0;
+ return;
+ }
+
+//
+// write the model output file
+//
+ if (modelname[0])
+ sprintf (name, "%s%s", gamedir, modelname);
+ else
+ sprintf (name, "%s/tris.md2", cddir);
+ printf ("saving to %s\n", name);
+ CreatePath (name);
+ modelouthandle = SafeOpenWrite (name);
+
+ WriteModelFile (modelouthandle);
+
+ printf ("%3dx%3d skin\n", model.skinwidth, model.skinheight);
+ printf ("%4d vertexes\n", model.num_xyz);
+ printf ("%4d triangles\n", model.num_tris);
+ printf ("%4d frame\n", model.num_frames);
+ printf ("%4d glverts\n", numglverts);
+ printf ("%4d glcmd\n", model.num_glcmds);
+ printf ("%4d skins\n", model.num_skins);
+ printf ("file size: %d\n", (int)ftell (modelouthandle) );
+ printf ("---------------------\n");
+
+ fclose (modelouthandle);
+
+ // finish writing header file
+ H_printf("\n");
+
+ // scale_up is usefull to allow step distances to be adjusted
+ H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);
+
+ fclose (headerouthandle);
+ headerouthandle = NULL;
+}
+
+
+/*
+=================================================================
+
+ALIAS MODEL DISPLAY LIST GENERATION
+
+=================================================================
+*/
+
+int strip_xyz[128];
+int strip_st[128];
+int strip_tris[128];
+int stripcount;
+
+/*
+================
+StripLength
+================
+*/
+int StripLength (int starttri, int startv)
+{
+ int m1, m2;
+ int st1, st2;
+ int j;
+ dtriangle_t *last, *check;
+ int k;
+
+ used[starttri] = 2;
+
+ last = &triangles[starttri];
+
+ strip_xyz[0] = last->index_xyz[(startv)%3];
+ strip_xyz[1] = last->index_xyz[(startv+1)%3];
+ strip_xyz[2] = last->index_xyz[(startv+2)%3];
+ strip_st[0] = last->index_st[(startv)%3];
+ strip_st[1] = last->index_st[(startv+1)%3];
+ strip_st[2] = last->index_st[(startv+2)%3];
+
+ strip_tris[0] = starttri;
+ stripcount = 1;
+
+ m1 = last->index_xyz[(startv+2)%3];
+ st1 = last->index_st[(startv+2)%3];
+ m2 = last->index_xyz[(startv+1)%3];
+ st2 = last->index_st[(startv+1)%3];
+
+ // look for a matching triangle
+nexttri:
+ for (j=starttri+1, check=&triangles[starttri+1]
+ ; j<model.num_tris ; j++, check++)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ if (check->index_xyz[k] != m1)
+ continue;
+ if (check->index_st[k] != st1)
+ continue;
+ if (check->index_xyz[ (k+1)%3 ] != m2)
+ continue;
+ if (check->index_st[ (k+1)%3 ] != st2)
+ continue;
+
+ // this is the next part of the fan
+
+ // if we can't use this triangle, this tristrip is done
+ if (used[j])
+ goto done;
+
+ // the new edge
+ if (stripcount & 1)
+ {
+ m2 = check->index_xyz[ (k+2)%3 ];
+ st2 = check->index_st[ (k+2)%3 ];
+ }
+ else
+ {
+ m1 = check->index_xyz[ (k+2)%3 ];
+ st1 = check->index_st[ (k+2)%3 ];
+ }
+
+ strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];
+ strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];
+ strip_tris[stripcount] = j;
+ stripcount++;
+
+ used[j] = 2;
+ goto nexttri;
+ }
+ }
+done:
+
+ // clear the temp used flags
+ for (j=starttri+1 ; j<model.num_tris ; j++)
+ if (used[j] == 2)
+ used[j] = 0;
+
+ return stripcount;
+}
+
+
+/*
+===========
+FanLength
+===========
+*/
+int FanLength (int starttri, int startv)
+{
+ int m1, m2;
+ int st1, st2;
+ int j;
+ dtriangle_t *last, *check;
+ int k;
+
+ used[starttri] = 2;
+
+ last = &triangles[starttri];
+
+ strip_xyz[0] = last->index_xyz[(startv)%3];
+ strip_xyz[1] = last->index_xyz[(startv+1)%3];
+ strip_xyz[2] = last->index_xyz[(startv+2)%3];
+ strip_st[0] = last->index_st[(startv)%3];
+ strip_st[1] = last->index_st[(startv+1)%3];
+ strip_st[2] = last->index_st[(startv+2)%3];
+
+ strip_tris[0] = starttri;
+ stripcount = 1;
+
+ m1 = last->index_xyz[(startv+0)%3];
+ st1 = last->index_st[(startv+0)%3];
+ m2 = last->index_xyz[(startv+2)%3];
+ st2 = last->index_st[(startv+2)%3];
+
+
+ // look for a matching triangle
+nexttri:
+ for (j=starttri+1, check=&triangles[starttri+1]
+ ; j<model.num_tris ; j++, check++)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ if (check->index_xyz[k] != m1)
+ continue;
+ if (check->index_st[k] != st1)
+ continue;
+ if (check->index_xyz[ (k+1)%3 ] != m2)
+ continue;
+ if (check->index_st[ (k+1)%3 ] != st2)
+ continue;
+
+ // this is the next part of the fan
+
+ // if we can't use this triangle, this tristrip is done
+ if (used[j])
+ goto done;
+
+ // the new edge
+ m2 = check->index_xyz[ (k+2)%3 ];
+ st2 = check->index_st[ (k+2)%3 ];
+
+ strip_xyz[stripcount+2] = m2;
+ strip_st[stripcount+2] = st2;
+ strip_tris[stripcount] = j;
+ stripcount++;
+
+ used[j] = 2;
+ goto nexttri;
+ }
+ }
+done:
+
+ // clear the temp used flags
+ for (j=starttri+1 ; j<model.num_tris ; j++)
+ if (used[j] == 2)
+ used[j] = 0;
+
+ return stripcount;
+}
+
+
+
+/*
+================
+BuildGlCmds
+
+Generate a list of trifans or strips
+for the model, which holds for all frames
+================
+*/
+void BuildGlCmds (void)
+{
+ int i, j, k;
+ int startv;
+ float s, t;
+ int len, bestlen, besttype;
+ int best_xyz[1024];
+ int best_st[1024];
+ int best_tris[1024];
+ int type;
+
+ //
+ // build tristrips
+ //
+ numcommands = 0;
+ numglverts = 0;
+ memset (used, 0, sizeof(used));
+ for (i=0 ; i<model.num_tris ; i++)
+ {
+ // pick an unused triangle and start the trifan
+ if (used[i])
+ continue;
+
+ bestlen = 0;
+ for (type = 0 ; type < 2 ; type++)
+// type = 1;
+ {
+ for (startv =0 ; startv < 3 ; startv++)
+ {
+ if (type == 1)
+ len = StripLength (i, startv);
+ else
+ len = FanLength (i, startv);
+ if (len > bestlen)
+ {
+ besttype = type;
+ bestlen = len;
+ for (j=0 ; j<bestlen+2 ; j++)
+ {
+ best_st[j] = strip_st[j];
+ best_xyz[j] = strip_xyz[j];
+ }
+ for (j=0 ; j<bestlen ; j++)
+ best_tris[j] = strip_tris[j];
+ }
+ }
+ }
+
+ // mark the tris on the best strip/fan as used
+ for (j=0 ; j<bestlen ; j++)
+ used[best_tris[j]] = 1;
+
+ if (besttype == 1)
+ commands[numcommands++] = (bestlen+2);
+ else
+ commands[numcommands++] = -(bestlen+2);
+
+ numglverts += bestlen+2;
+
+ for (j=0 ; j<bestlen+2 ; j++)
+ {
+ // emit a vertex into the reorder buffer
+ k = best_st[j];
+
+ // emit s/t coords into the commands stream
+ s = base_st[k].s;
+ t = base_st[k].t;
+
+ s = (s + 0.5) / model.skinwidth;
+ t = (t + 0.5) / model.skinheight;
+
+ *(float *)&commands[numcommands++] = s;
+ *(float *)&commands[numcommands++] = t;
+ *(int *)&commands[numcommands++] = best_xyz[j];
+ }
+ }
+
+ commands[numcommands++] = 0; // end of list marker
+}
+
+
+/*
+===============================================================
+
+BASE FRAME SETUP
+
+===============================================================
+*/
+
+/*
+============
+BuildST
+
+Builds the triangle_st array for the base frame and
+model.skinwidth / model.skinheight
+
+ FIXME: allow this to be loaded from a file for
+ arbitrary mappings
+============
+*/
+void BuildST (triangle_t *ptri, int numtri)
+{
+ int i, j;
+ int width, height, iwidth, iheight, swidth;
+ float basex, basey;
+ float s_scale, t_scale;
+ float scale;
+ vec3_t mins, maxs;
+ float *pbasevert;
+ vec3_t vtemp1, vtemp2, normal;
+
+ //
+ // find bounds of all the verts on the base frame
+ //
+ ClearBounds (mins, maxs);
+
+ for (i=0 ; i<numtri ; i++)
+ for (j=0 ; j<3 ; j++)
+ AddPointToBounds (ptri[i].verts[j], mins, maxs);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ mins[i] = floor(mins[i]);
+ maxs[i] = ceil(maxs[i]);
+ }
+
+ width = maxs[0] - mins[0];
+ height = maxs[2] - mins[2];
+
+ if (!g_fixedwidth)
+ { // old style
+ scale = 8;
+ if (width*scale >= 150)
+ scale = 150.0 / width;
+ if (height*scale >= 190)
+ scale = 190.0 / height;
+
+ s_scale = t_scale = scale;
+
+ iwidth = ceil(width*s_scale);
+ iheight = ceil(height*t_scale);
+
+ iwidth += 4;
+ iheight += 4;
+ }
+ else
+ { // new style
+ iwidth = g_fixedwidth / 2;
+ iheight = g_fixedheight;
+
+ s_scale = (float)(iwidth-4) / width;
+ t_scale = (float)(iheight-4) / height;
+ }
+
+//
+// determine which side of each triangle to map the texture to
+//
+ for (i=0 ; i<numtri ; i++)
+ {
+ VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+ VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+ CrossProduct (vtemp1, vtemp2, normal);
+
+ if (normal[1] > 0)
+ {
+ basex = iwidth + 2;
+ }
+ else
+ {
+ basex = 2;
+ }
+ basey = 2;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ pbasevert = ptri[i].verts[j];
+
+ triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);
+ triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);
+ }
+ }
+
+// make the width a multiple of 4; some hardware requires this, and it ensures
+// dword alignment for each scan
+ swidth = iwidth*2;
+ model.skinwidth = (swidth + 3) & ~3;
+ model.skinheight = iheight;
+}
+
+
+/*
+=================
+Cmd_Base
+=================
+*/
+void Cmd_Base (void)
+{
+ triangle_t *ptri;
+ int i, j, k;
+ int time1;
+ char file1[1024];
+
+ GetToken (false);
+
+ if (g_skipmodel || g_release || g_archive)
+ return;
+
+ printf ("---------------------\n");
+ sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);
+ printf ("%s\n", file1);
+
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s.%s", cddir, token, trifileext);
+
+ time1 = FileTime (file1);
+ if (time1 == -1)
+ Error ("%s doesn't exist", file1);
+
+//
+// load the base triangles
+//
+ if (do3ds)
+ Load3DSTriangleList (file1, &ptri, &model.num_tris);
+ else
+ LoadTriangleList (file1, &ptri, &model.num_tris);
+
+//
+// get the ST values
+//
+ BuildST (ptri, model.num_tris);
+
+//
+// run through all the base triangles, storing each unique vertex in the
+// base vertex list and setting the indirect triangles to point to the base
+// vertices
+//
+ for (i=0 ; i<model.num_tris ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ // get the xyz index
+ for (k=0 ; k<model.num_xyz ; k++)
+ if (VectorCompare (ptri[i].verts[j], base_xyz[k]))
+ break; // this vertex is already in the base vertex list
+
+ if (k == model.num_xyz)
+ { // new index
+ VectorCopy (ptri[i].verts[j], base_xyz[model.num_xyz]);
+ model.num_xyz++;
+ }
+
+ triangles[i].index_xyz[j] = k;
+
+ // get the st index
+ for (k=0 ; k<model.num_st ; k++)
+ if (triangle_st[i][j][0] == base_st[k].s
+ && triangle_st[i][j][1] == base_st[k].t)
+ break; // this vertex is already in the base vertex list
+
+ if (k == model.num_st)
+ { // new index
+ base_st[model.num_st].s = triangle_st[i][j][0];
+ base_st[model.num_st].t = triangle_st[i][j][1];
+ model.num_st++;
+ }
+
+ triangles[i].index_st[j] = k;
+ }
+ }
+
+ // build triangle strips / fans
+ BuildGlCmds ();
+}
+
+//===============================================================
+
+char *FindFrameFile (char *frame)
+{
+ int time1;
+ char file1[1024];
+ static char retname[1024];
+ char base[32];
+ char suffix[32];
+ char *s;
+
+ if (strstr (frame, "."))
+ return frame; // allready in dot format
+
+ // split 'run1' into 'run' and '1'
+ s = frame + strlen(frame)-1;
+
+ while (s != frame && *s >= '0' && *s <= '9')
+ s--;
+
+ strcpy (suffix, s+1);
+ strcpy (base, frame);
+ base[s-frame+1] = 0;
+
+ // check for 'run1.tri'
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, trifileext);
+ time1 = FileTime (file1);
+ if (time1 != -1)
+ {
+ sprintf (retname, "%s%s.%s", base, suffix, trifileext);
+ return retname;
+ }
+
+ // check for 'run.1'
+ sprintf (file1, "%s/%s.%s",cddir, base, suffix);
+ time1 = FileTime (file1);
+ if (time1 != -1)
+ {
+ sprintf (retname, "%s.%s", base, suffix);
+ return retname;
+ }
+
+ Error ("frame %s could not be found",frame);
+ return NULL;
+}
+
+/*
+===============
+GrabFrame
+===============
+*/
+void GrabFrame (char *frame)
+{
+ triangle_t *ptri;
+ int i, j;
+ trivert_t *ptrivert;
+ int num_tris;
+ char file1[1024];
+ frame_t *fr;
+ vertexnormals_t vnorms[MAX_VERTS];
+ int index_xyz;
+ char *framefile;
+
+ // the frame 'run1' will be looked for as either
+ // run.1 or run1.tri, so the new alias sequence save
+ // feature an be used
+ framefile = FindFrameFile (frame);
+
+ sprintf (file1, "%s/%s", cdarchive, framefile);
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s",cddir, framefile);
+
+ printf ("grabbing %s\n", file1);
+
+ if (model.num_frames >= MAX_FRAMES)
+ Error ("model.num_frames >= MAX_FRAMES");
+ fr = &g_frames[model.num_frames];
+ model.num_frames++;
+
+ strcpy (fr->name, frame);
+
+//
+// load the frame
+//
+ if (do3ds)
+ Load3DSTriangleList (file1, &ptri, &num_tris);
+ else
+ LoadTriangleList (file1, &ptri, &num_tris);
+
+ if (num_tris != model.num_tris)
+ Error ("%s: number of triangles doesn't match base frame\n", file1);
+
+//
+// allocate storage for the frame's vertices
+//
+ ptrivert = fr->v;
+
+ for (i=0 ; i<model.num_xyz ; i++)
+ {
+ vnorms[i].numnormals = 0;
+ VectorClear (vnorms[i].normalsum);
+ }
+ ClearBounds (fr->mins, fr->maxs);
+
+//
+// store the frame's vertices in the same order as the base. This assumes the
+// triangles and vertices in this frame are in exactly the same order as in the
+// base
+//
+ for (i=0 ; i<num_tris ; i++)
+ {
+ vec3_t vtemp1, vtemp2, normal;
+ float ftemp;
+
+ VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+ VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+ CrossProduct (vtemp1, vtemp2, normal);
+
+ VectorNormalize (normal, normal);
+
+ // rotate the normal so the model faces down the positive x axis
+ ftemp = normal[0];
+ normal[0] = -normal[1];
+ normal[1] = ftemp;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ index_xyz = triangles[i].index_xyz[j];
+
+ // rotate the vertices so the model faces down the positive x axis
+ // also adjust the vertices to the desired origin
+ ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
+ adjust[0];
+ ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +
+ adjust[1];
+ ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +
+ adjust[2];
+
+ AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
+
+ VectorAdd (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum);
+ vnorms[index_xyz].numnormals++;
+ }
+ }
+
+//
+// calculate the vertex normals, match them to the template list, and store the
+// index of the best match
+//
+ for (i=0 ; i<model.num_xyz ; i++)
+ {
+ int j;
+ vec3_t v;
+ float maxdot;
+ int maxdotindex;
+ int c;
+
+ c = vnorms[i].numnormals;
+ if (!c)
+ Error ("Vertex with no triangles attached");
+
+ VectorScale (vnorms[i].normalsum, 1.0/c, v);
+ VectorNormalize (v, v);
+
+ maxdot = -999999.0;
+ maxdotindex = -1;
+
+ for (j=0 ; j<NUMVERTEXNORMALS ; j++)
+ {
+ float dot;
+
+ dot = DotProduct (v, avertexnormals[j]);
+ if (dot > maxdot)
+ {
+ maxdot = dot;
+ maxdotindex = j;
+ }
+ }
+
+ ptrivert[i].lightnormalindex = maxdotindex;
+ }
+
+ free (ptri);
+}
+
+/*
+===============
+Cmd_Frame
+===============
+*/
+void Cmd_Frame (void)
+{
+ while (TokenAvailable())
+ {
+ GetToken (false);
+ if (g_skipmodel)
+ continue;
+ if (g_release || g_archive)
+ {
+ model.num_frames = 1; // don't skip the writeout
+ continue;
+ }
+
+ H_printf("#define FRAME_%-16s\t%i\n", token, model.num_frames);
+
+ GrabFrame (token);
+ }
+}
+
+
+/*
+===============
+Cmd_Skin
+
+Skins aren't actually stored in the file, only a reference
+is saved out to the header file.
+===============
+*/
+void Cmd_Skin (void)
+{
+ byte *palette;
+ byte *pixels;
+ int width, height;
+ byte *cropped;
+ int y;
+ char name[1024], savename[1024];
+
+ GetToken (false);
+
+ if (model.num_skins == MAX_MD2SKINS)
+ Error ("model.num_skins == MAX_MD2SKINS");
+
+ if (g_skipmodel)
+ return;
+
+ sprintf (name, "%s/%s.lbm", cdarchive, token);
+ strcpy (name, ExpandPathAndArchive( name ) );
+// sprintf (name, "%s/%s.lbm", cddir, token);
+
+ if (TokenAvailable())
+ {
+ GetToken (false);
+ sprintf (g_skins[model.num_skins], "%s.pcx", token);
+ sprintf (savename, "%s%s.pcx", gamedir, g_skins[model.num_skins]);
+ }
+ else
+ {
+ sprintf (savename, "%s/%s.pcx", cddir, token);
+ sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token);
+ }
+
+ model.num_skins++;
+
+ if (g_skipmodel || g_release || g_archive)
+ return;
+
+ // load the image
+ printf ("loading %s\n", name);
+ Load256Image (name, &pixels, &palette, &width, &height);
+ RemapZero (pixels, palette, width, height);
+
+ // crop it to the proper size
+ cropped = malloc (model.skinwidth*model.skinheight);
+ for (y=0 ; y<model.skinheight ; y++)
+ {
+ memcpy (cropped+y*model.skinwidth,
+ pixels+y*width, model.skinwidth);
+ }
+
+ // save off the new image
+ printf ("saving %s\n", savename);
+ CreatePath (savename);
+ WritePCXfile (savename, cropped, model.skinwidth,
+ model.skinheight, palette);
+
+ free (pixels);
+ free (palette);
+ free (cropped);
+}
+
+
+/*
+=================
+Cmd_Origin
+=================
+*/
+void Cmd_Origin (void)
+{
+ // rotate points into frame of reference so model points down the
+ // positive x axis
+ GetToken (false);
+ adjust[1] = -atof (token);
+
+ GetToken (false);
+ adjust[0] = atof (token);
+
+ GetToken (false);
+ adjust[2] = -atof (token);
+}
+
+
+/*
+=================
+Cmd_ScaleUp
+=================
+*/
+void Cmd_ScaleUp (void)
+{
+ GetToken (false);
+ scale_up = atof (token);
+ if (g_skipmodel || g_release || g_archive)
+ return;
+
+ printf ("Scale up: %f\n", scale_up);
+}
+
+
+/*
+=================
+Cmd_Skinsize
+
+Set a skin size other than the default
+=================
+*/
+void Cmd_Skinsize (void)
+{
+ GetToken (false);
+ g_fixedwidth = atoi(token);
+ GetToken (false);
+ g_fixedheight = atoi(token);
+}
+
+/*
+=================
+Cmd_Modelname
+
+Gives a different name/location for the file, instead of the cddir
+=================
+*/
+void Cmd_Modelname (void)
+{
+ GetToken (false);
+ strcpy (modelname, token);
+}
+
+/*
+===============
+Cmd_Cd
+===============
+*/
+void Cmd_Cd (void)
+{
+ FinishModel ();
+ ClearModel ();
+
+ GetToken (false);
+
+ // this is a silly mess...
+ sprintf (cdpartial, "models/%s", token);
+ sprintf (cdarchive, "%smodels/%s", gamedir+strlen(qdir), token);
+ sprintf (cddir, "%s%s", gamedir, cdpartial);
+
+ // if -only was specified and this cd doesn't match,
+ // skip the model (you only need to match leading chars,
+ // so you could regrab all monsters with -only monsters)
+ if (!g_only[0])
+ return;
+ if (strncmp(token, g_only, strlen(g_only)))
+ {
+ g_skipmodel = true;
+ printf ("skipping %s\n", cdpartial);
+ }
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+qboolean g_compress_pak;
+qboolean g_release; // don't grab, copy output data to new tree
+qboolean g_pak; // if true, copy to pak instead of release
+char g_releasedir[1024]; // c:\quake2\baseq2, etc
+qboolean g_archive; // don't grab, copy source data to new tree
+qboolean do3ds;
+char g_only[256]; // if set, only grab this cd
+qboolean g_skipmodel; // set true when a cd is not g_only
+
+char *ext_3ds = "3ds";
+char *ext_tri= "tri";
+char *trifileext;
+
+/*
+=======================================================
+
+ PAK FILES
+
+=======================================================
+*/
+
+unsigned Com_BlockChecksum (void *buffer, int length);
+
+typedef struct
+{
+ char name[56];
+ int filepos, filelen;
+} packfile_t;
+
+typedef struct
+{
+ char id[4];
+ int dirofs;
+ int dirlen;
+} packheader_t;
+
+packfile_t pfiles[16384];
+FILE *pakfile;
+packfile_t *pf;
+packheader_t pakheader;
+
+
+
+/*
+==============
+BeginPak
+==============
+*/
+void BeginPak (char *outname)
+{
+ if (!g_pak)
+ return;
+
+ pakfile = SafeOpenWrite (outname);
+
+ // leave space for header
+ SafeWrite (pakfile, &pakheader, sizeof(pakheader));
+
+ pf = pfiles;
+}
+
+
+/*
+==============
+ReleaseFile
+
+Filename should be gamedir reletive.
+Either copies the file to the release dir, or adds it to
+the pak file.
+==============
+*/
+void ReleaseFile (char *filename)
+{
+ int len;
+ byte *buf;
+ char source[1024];
+ char dest[1024];
+
+ if (!g_release)
+ return;
+
+ sprintf (source, "%s%s", gamedir, filename);
+
+ if (!g_pak)
+ { // copy it
+ sprintf (dest, "%s/%s", g_releasedir, filename);
+ printf ("copying to %s\n", dest);
+ QCopyFile (source, dest);
+ return;
+ }
+
+ // pak it
+ printf ("paking %s\n", filename);
+ if (strlen(filename) >= sizeof(pf->name))
+ Error ("Filename too long for pak: %s", filename);
+
+ len = LoadFile (source, (void **)&buf);
+
+ if (g_compress_pak && len < 4096*1024 )
+ {
+ cblock_t in, out;
+ cblock_t Huffman (cblock_t in);
+
+ in.count = len;
+ in.data = buf;
+
+ out = Huffman (in);
+
+ if (out.count < in.count)
+ {
+ printf (" compressed from %i to %i\n", in.count, out.count);
+ free (in.data);
+ buf = out.data;
+ len = out.count;
+ }
+ else
+ free (out.data);
+ }
+
+ strcpy (pf->name, filename);
+ pf->filepos = LittleLong(ftell(pakfile));
+ pf->filelen = LittleLong(len);
+ pf++;
+
+ SafeWrite (pakfile, buf, len);
+
+ free (buf);
+}
+
+
+/*
+==============
+FinishPak
+==============
+*/
+void FinishPak (void)
+{
+ int dirlen;
+ int d;
+ int i;
+ unsigned checksum;
+
+ if (!g_pak)
+ return;
+
+ pakheader.id[0] = 'P';
+ pakheader.id[1] = 'A';
+ pakheader.id[2] = 'C';
+ pakheader.id[3] = 'K';
+ dirlen = (byte *)pf - (byte *)pfiles;
+ pakheader.dirofs = LittleLong(ftell(pakfile));
+ pakheader.dirlen = LittleLong(dirlen);
+
+ checksum = Com_BlockChecksum ( (void *)pfiles, dirlen );
+
+ SafeWrite (pakfile, pfiles, dirlen);
+
+ i = ftell (pakfile);
+
+ fseek (pakfile, 0, SEEK_SET);
+ SafeWrite (pakfile, &pakheader, sizeof(pakheader));
+ fclose (pakfile);
+
+ d = pf - pfiles;
+ printf ("%i files packed in %i bytes\n",d, i);
+ printf ("checksum: 0x%x\n", checksum);
+}
+
+
+/*
+===============
+Cmd_File
+
+This is only used to cause a file to be copied during a release
+build (default.cfg, maps, etc)
+===============
+*/
+void Cmd_File (void)
+{
+ GetToken (false);
+ ReleaseFile (token);
+}
+
+/*
+===============
+PackDirectory_r
+
+===============
+*/
+#ifdef _WIN32
+#include "io.h"
+void PackDirectory_r (char *dir)
+{
+ struct _finddata_t fileinfo;
+ int handle;
+ char dirstring[1024];
+ char filename[1024];
+
+ sprintf (dirstring, "%s%s/*.*", gamedir, dir);
+
+ handle = _findfirst (dirstring, &fileinfo);
+ if (handle == -1)
+ return;
+
+ do
+ {
+ sprintf (filename, "%s/%s", dir, fileinfo.name);
+ if (fileinfo.attrib & _A_SUBDIR)
+ { // directory
+ if (fileinfo.name[0] != '.') // don't pak . and ..
+ PackDirectory_r (filename);
+ continue;
+ }
+ // copy or pack the file
+ ReleaseFile (filename);
+ } while (_findnext( handle, &fileinfo ) != -1);
+
+ _findclose (handle);
+}
+#else
+
+#include <sys/types.h>
+#ifdef NeXT
+#include <sys/dir.h>
+#else
+#include <sys/dirent.h>
+#endif
+
+void PackDirectory_r (char *dir)
+{
+#ifdef NeXT
+ struct direct **namelist, *ent;
+#else
+ struct dirent **namelist, *ent;
+#endif
+ int count;
+ struct stat st;
+ int i;
+ int len;
+ char fullname[1024];
+ char dirstring[1024];
+ char *name;
+
+ sprintf (dirstring, "%s%s", gamedir, dir);
+ count = scandir(dirstring, &namelist, NULL, NULL);
+
+ for (i=0 ; i<count ; i++)
+ {
+ ent = namelist[i];
+ name = ent->d_name;
+
+ if (name[0] == '.')
+ continue;
+
+ sprintf (fullname, "%s/%s", dir, name);
+ sprintf (dirstring, "%s%s/%s", gamedir, dir, name);
+
+ if (stat (dirstring, &st) == -1)
+ Error ("fstating %s", pf->name);
+ if (st.st_mode & S_IFDIR)
+ { // directory
+ PackDirectory_r (fullname);
+ continue;
+ }
+
+ // copy or pack the file
+ ReleaseFile (fullname);
+ }
+}
+#endif
+
+
+/*
+===============
+Cmd_Dir
+
+This is only used to cause a directory to be copied during a
+release build (sounds, etc)
+===============
+*/
+void Cmd_Dir (void)
+{
+ GetToken (false);
+ PackDirectory_r (token);
+}
+
+//========================================================================
+
+#define MAX_RTEX 16384
+int numrtex;
+char rtex[MAX_RTEX][64];
+
+void ReleaseTexture (char *name)
+{
+ int i;
+ char path[1024];
+
+ for (i=0 ; i<numrtex ; i++)
+ if (!Q_strcasecmp(name, rtex[i]))
+ return;
+
+ if (numrtex == MAX_RTEX)
+ Error ("numrtex == MAX_RTEX");
+
+ strcpy (rtex[i], name);
+ numrtex++;
+
+ sprintf (path, "textures/%s.wal", name);
+ ReleaseFile (path);
+}
+
+/*
+===============
+Cmd_Maps
+
+Only relevent for release and pak files.
+Releases the .bsp files for the maps, and scans all of the files to
+build a list of all textures used, which are then released.
+===============
+*/
+void Cmd_Maps (void)
+{
+ char map[1024];
+ int i;
+
+ while (TokenAvailable ())
+ {
+ GetToken (false);
+ sprintf (map, "maps/%s.bsp", token);
+ ReleaseFile (map);
+
+ if (!g_release)
+ continue;
+
+ // get all the texture references
+ sprintf (map, "%smaps/%s.bsp", gamedir, token);
+ LoadBSPFileTexinfo (map);
+ for (i=0 ; i<numtexinfo ; i++)
+ ReleaseTexture (texinfo[i].texture);
+ }
+}
+
+
+//==============================================================
+
+/*
+===============
+ParseScript
+===============
+*/
+void ParseScript (void)
+{
+ while (1)
+ {
+ do
+ { // look for a line starting with a $ command
+ GetToken (true);
+ if (endofscript)
+ return;
+ if (token[0] == '$')
+ break;
+ while (TokenAvailable())
+ GetToken (false);
+ } while (1);
+
+ //
+ // model commands
+ //
+ if (!strcmp (token, "$modelname"))
+ Cmd_Modelname ();
+ else if (!strcmp (token, "$base"))
+ Cmd_Base ();
+ else if (!strcmp (token, "$cd"))
+ Cmd_Cd ();
+ else if (!strcmp (token, "$origin"))
+ Cmd_Origin ();
+ else if (!strcmp (token, "$scale"))
+ Cmd_ScaleUp ();
+ else if (!strcmp (token, "$frame"))
+ Cmd_Frame ();
+ else if (!strcmp (token, "$skin"))
+ Cmd_Skin ();
+ else if (!strcmp (token, "$skinsize"))
+ Cmd_Skinsize ();
+ //
+ // sprite commands
+ //
+ else if (!strcmp (token, "$spritename"))
+ Cmd_SpriteName ();
+ else if (!strcmp (token, "$load"))
+ Cmd_Load ();
+ else if (!strcmp (token, "$spriteframe"))
+ Cmd_SpriteFrame ();
+ //
+ // image commands
+ //
+ else if (!strcmp (token, "$grab"))
+ Cmd_Grab ();
+ else if (!strcmp (token, "$raw"))
+ Cmd_Raw ();
+ else if (!strcmp (token, "$colormap"))
+ Cmd_Colormap ();
+ else if (!strcmp (token, "$mippal"))
+ Cmd_Mippal ();
+ else if (!strcmp (token, "$mipdir"))
+ Cmd_Mipdir ();
+ else if (!strcmp (token, "$mip"))
+ Cmd_Mip ();
+ else if (!strcmp (token, "$environment"))
+ Cmd_Environment ();
+ //
+ // video
+ //
+ else if (!strcmp (token, "$video"))
+ Cmd_Video ();
+ //
+ // misc
+ //
+ else if (!strcmp (token, "$file"))
+ Cmd_File ();
+ else if (!strcmp (token, "$dir"))
+ Cmd_Dir ();
+ else if (!strcmp (token, "$maps"))
+ Cmd_Maps ();
+ else if (!strcmp (token, "$alphalight"))
+ Cmd_Alphalight ();
+ else if (!strcmp (token, "$inverse16table" ))
+ Cmd_Inverse16Table();
+ else
+ Error ("bad command %s\n", token);
+ }
+}
+
+//=======================================================
+
+/*
+==============
+main
+==============
+*/
+int main (int argc, char **argv)
+{
+ static int i; // VC4.2 compiler bug if auto...
+ char path[1024];
+
+ ExpandWildcards (&argc, &argv);
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i], "-archive"))
+ {
+ // -archive f:/quake2/release/dump_11_30
+ archive = true;
+ strcpy (archivedir, argv[i+1]);
+ printf ("Archiving source to: %s\n", archivedir);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-release"))
+ {
+ g_release = true;
+ strcpy (g_releasedir, argv[i+1]);
+ printf ("Copy output to: %s\n", g_releasedir);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-compress"))
+ {
+ g_compress_pak = true;
+ printf ("Compressing pakfile\n");
+ }
+ else if (!strcmp(argv[i], "-pak"))
+ {
+ g_release = true;
+ g_pak = true;
+ printf ("Building pakfile: %s\n", argv[i+1]);
+ BeginPak (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-only"))
+ {
+ strcpy (g_only, argv[i+1]);
+ printf ("Only grabbing %s\n", g_only);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-3ds"))
+ {
+ do3ds = true;
+ printf ("loading .3ds files\n");
+ }
+ else if (argv[i][0] == '-')
+ Error ("Unknown option \"%s\"", argv[i]);
+ else
+ break;
+ }
+
+ if (i >= argc)
+ Error ("usage: qgrab [-archive <directory>] [-release <directory>] [-only <model>] [-3ds] file.qgr");
+
+ if (do3ds)
+ trifileext = ext_3ds;
+ else
+ trifileext = ext_tri;
+
+ for ( ; i<argc ; i++)
+ {
+ printf ("--------------- %s ---------------\n", argv[i]);
+ // load the script
+ strcpy (path, argv[i]);
+ DefaultExtension (path, ".qdt");
+ SetQdirFromPath (path);
+ LoadScriptFile (ExpandArg(path));
+
+ //
+ // parse it
+ //
+ ParseScript ();
+
+ // write out the last model
+ FinishModel ();
+ FinishSprite ();
+ }
+
+ if (g_pak)
+ FinishPak ();
+
+ return 0;
+}
+
--- /dev/null
+# Microsoft Developer Studio Project File - Name="qdata" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=qdata - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "qdata.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "qdata.mak" CFG="qdata - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "qdata - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qdata - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\Release"
+# PROP Intermediate_Dir ".\Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\Debug"
+# PROP Intermediate_Dir ".\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "qdata - Win32 Release"
+# Name "qdata - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\images.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\l3dslib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\lbmlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\mdfour.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\models.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\qdata.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sprites.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\tables.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\threads.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\trilib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\video.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\anorms.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\bspfile.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\cmdlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\l3dslib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\lbmlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\mathlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\modelgen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\qdata.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\qfiles.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\scriplib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\threads.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\trilib.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "qdata"=.\qdata.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// qdata.h
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "mathlib.h"
+#include "trilib.h"
+#include "lbmlib.h"
+#include "threads.h"
+#include "l3dslib.h"
+#include "bspfile.h"
+
+void Cmd_Modelname (void);
+void Cmd_Base (void);
+void Cmd_Cd (void);
+void Cmd_Origin (void);
+void Cmd_ScaleUp (void);
+void Cmd_Frame (void);
+void Cmd_Modelname (void);
+void Cmd_Skin (void);
+void Cmd_Skinsize (void);
+void FinishModel (void);
+
+void Cmd_Inverse16Table( void );
+
+void Cmd_SpriteName (void);
+void Cmd_Load (void);
+void Cmd_SpriteFrame (void);
+void FinishSprite (void);
+
+void Cmd_Grab (void);
+void Cmd_Raw (void);
+void Cmd_Mip (void);
+void Cmd_Environment (void);
+void Cmd_Colormap (void);
+
+void Cmd_File (void);
+void Cmd_Dir (void);
+void Cmd_StartWad (void);
+void Cmd_EndWad (void);
+void Cmd_Mippal (void);
+void Cmd_Mipdir (void);
+void Cmd_Alphalight (void);
+
+void Cmd_Video (void);
+
+void RemapZero (byte *pixels, byte *palette, int width, int height);
+
+void ReleaseFile (char *filename);
+
+extern byte *byteimage, *lbmpalette;
+extern int byteimagewidth, byteimageheight;
+
+extern qboolean g_release; // don't grab, copy output data to new tree
+extern char g_releasedir[1024]; // c:\quake2\baseq2, etc
+extern qboolean g_archive; // don't grab, copy source data to new tree
+extern qboolean do3ds;
+extern char g_only[256]; // if set, only grab this cd
+extern qboolean g_skipmodel; // set true when a cd is not g_only
+
+extern char *trifileext;
--- /dev/null
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=qdata - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to qdata - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "qdata - Win32 Release" && "$(CFG)" != "qdata - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "qdata.mak" CFG="qdata - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "qdata - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qdata - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "qdata - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\qdata.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\images.obj"
+ -@erase "$(INTDIR)\l3dslib.obj"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\models.obj"
+ -@erase "$(INTDIR)\qdata.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\sprites.obj"
+ -@erase "$(INTDIR)\tables.obj"
+ -@erase "$(INTDIR)\threads.obj"
+ -@erase "$(INTDIR)\trilib.obj"
+ -@erase "$(OUTDIR)\qdata.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /GX /O2 /I "../common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "../common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qdata.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qdata.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/qdata.pdb" /machine:I386 /out:"$(OUTDIR)/qdata.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\images.obj" \
+ "$(INTDIR)\l3dslib.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\models.obj" \
+ "$(INTDIR)\qdata.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\sprites.obj" \
+ "$(INTDIR)\tables.obj" \
+ "$(INTDIR)\threads.obj" \
+ "$(INTDIR)\trilib.obj"
+
+"$(OUTDIR)\qdata.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\qdata.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\images.obj"
+ -@erase "$(INTDIR)\l3dslib.obj"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\models.obj"
+ -@erase "$(INTDIR)\qdata.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\sprites.obj"
+ -@erase "$(INTDIR)\tables.obj"
+ -@erase "$(INTDIR)\threads.obj"
+ -@erase "$(INTDIR)\trilib.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\qdata.exe"
+ -@erase "$(OUTDIR)\qdata.ilk"
+ -@erase "$(OUTDIR)\qdata.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "../common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /I "../common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qdata.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qdata.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/qdata.pdb" /debug /machine:I386 /out:"$(OUTDIR)/qdata.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\images.obj" \
+ "$(INTDIR)\l3dslib.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\models.obj" \
+ "$(INTDIR)\qdata.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\sprites.obj" \
+ "$(INTDIR)\tables.obj" \
+ "$(INTDIR)\threads.obj" \
+ "$(INTDIR)\trilib.obj"
+
+"$(OUTDIR)\qdata.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "qdata - Win32 Release"
+# Name "qdata - Win32 Debug"
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\images.c
+DEP_CPP_IMAGE=\
+ "..\common\qfiles.h"\
+ ".\../common\bspfile.h"\
+ ".\../common\cmdlib.h"\
+ ".\../common\l3dslib.h"\
+ ".\../common\lbmlib.h"\
+ ".\../common\mathlib.h"\
+ ".\../common\scriplib.h"\
+ ".\../common\threads.h"\
+ ".\../common\trilib.h"\
+ ".\qdata.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+
+"$(INTDIR)\images.obj" : $(SOURCE) $(DEP_CPP_IMAGE) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\modelgen.h
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qdata.h
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\sprites.c
+DEP_CPP_SPRIT=\
+ "..\common\qfiles.h"\
+ ".\../common\bspfile.h"\
+ ".\../common\cmdlib.h"\
+ ".\../common\l3dslib.h"\
+ ".\../common\lbmlib.h"\
+ ".\../common\mathlib.h"\
+ ".\../common\scriplib.h"\
+ ".\../common\threads.h"\
+ ".\../common\trilib.h"\
+ ".\qdata.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+
+"$(INTDIR)\sprites.obj" : $(SOURCE) $(DEP_CPP_SPRIT) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\l3dslib.c
+DEP_CPP_L3DSL=\
+ ".\../common\cmdlib.h"\
+ ".\../common\l3dslib.h"\
+ ".\../common\mathlib.h"\
+ ".\../common\trilib.h"\
+
+
+"$(INTDIR)\l3dslib.obj" : $(SOURCE) $(DEP_CPP_L3DSL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.c
+DEP_CPP_LBMLI=\
+ ".\../common\cmdlib.h"\
+ ".\../common\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+ ".\../common\cmdlib.h"\
+ ".\../common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+ ".\../common\cmdlib.h"\
+ ".\../common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.c
+DEP_CPP_THREA=\
+ ".\../common\cmdlib.h"\
+ ".\../common\threads.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\trilib.c
+DEP_CPP_TRILI=\
+ ".\../common\cmdlib.h"\
+ ".\../common\mathlib.h"\
+ ".\../common\trilib.h"\
+
+
+"$(INTDIR)\trilib.obj" : $(SOURCE) $(DEP_CPP_TRILI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ ".\../common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\models.c
+DEP_CPP_MODEL=\
+ "..\common\qfiles.h"\
+ ".\../common\bspfile.h"\
+ ".\../common\cmdlib.h"\
+ ".\../common\l3dslib.h"\
+ ".\../common\lbmlib.h"\
+ ".\../common\mathlib.h"\
+ ".\../common\scriplib.h"\
+ ".\../common\threads.h"\
+ ".\../common\trilib.h"\
+ ".\anorms.h"\
+ ".\qdata.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+
+"$(INTDIR)\models.obj" : $(SOURCE) $(DEP_CPP_MODEL) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qdata.c
+DEP_CPP_QDATA=\
+ "..\common\qfiles.h"\
+ ".\../common\bspfile.h"\
+ ".\../common\cmdlib.h"\
+ ".\../common\l3dslib.h"\
+ ".\../common\lbmlib.h"\
+ ".\../common\mathlib.h"\
+ ".\../common\scriplib.h"\
+ ".\../common\threads.h"\
+ ".\../common\trilib.h"\
+ ".\qdata.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+
+"$(INTDIR)\qdata.obj" : $(SOURCE) $(DEP_CPP_QDATA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.h
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.h
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.h
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\tables.c
+DEP_CPP_TABLE=\
+ "..\common\qfiles.h"\
+ ".\../common\bspfile.h"\
+ ".\../common\cmdlib.h"\
+ ".\../common\l3dslib.h"\
+ ".\../common\lbmlib.h"\
+ ".\../common\mathlib.h"\
+ ".\../common\scriplib.h"\
+ ".\../common\threads.h"\
+ ".\../common\trilib.h"\
+ ".\qdata.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+
+"$(INTDIR)\tables.obj" : $(SOURCE) $(DEP_CPP_TABLE) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.h
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\qfiles.h
+
+!IF "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+ "..\common\qfiles.h"\
+ ".\../common\bspfile.h"\
+ ".\../common\cmdlib.h"\
+ ".\../common\mathlib.h"\
+ ".\../common\scriplib.h"\
+
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+#define MAX_SPRFRAMES MAX_MD2SKINS
+
+dsprite_t sprite;
+dsprframe_t frames[MAX_SPRFRAMES];
+
+byte *byteimage, *lbmpalette;
+int byteimagewidth, byteimageheight;
+
+char spritename[1024];
+
+
+void FinishSprite (void);
+void Cmd_Spritename (void);
+
+
+
+/*
+==============
+FinishSprite
+==============
+*/
+void FinishSprite (void)
+{
+ FILE *spriteouthandle;
+ int i, curframe;
+ dsprite_t spritetemp;
+ char savename[1024];
+
+ if (sprite.numframes == 0)
+ return;
+
+ if (!strlen(spritename))
+ Error ("Didn't name sprite file");
+
+ sprintf (savename, "%s%s.sp2", gamedir, spritename);
+
+ if (g_release)
+ {
+ char name[1024];
+
+ sprintf (name, "%s.sp2", spritename);
+ ReleaseFile (name);
+ spritename[0] = 0; // clear for a new sprite
+ sprite.numframes = 0;
+ return;
+ }
+
+
+ printf ("saving in %s\n", savename);
+ CreatePath (savename);
+ spriteouthandle = SafeOpenWrite (savename);
+
+
+//
+// write out the sprite header
+//
+ spritetemp.ident = LittleLong (IDSPRITEHEADER);
+ spritetemp.version = LittleLong (SPRITE_VERSION);
+ spritetemp.numframes = LittleLong (sprite.numframes);
+
+ SafeWrite (spriteouthandle, &spritetemp, 12);
+
+//
+// write out the frames
+//
+ curframe = 0;
+
+ for (i=0 ; i<sprite.numframes ; i++)
+ {
+ frames[i].width = LittleLong(frames[i].width);
+ frames[i].height = LittleLong(frames[i].height);
+ frames[i].origin_x = LittleLong(frames[i].origin_x);
+ frames[i].origin_y = LittleLong(frames[i].origin_y);
+ }
+ SafeWrite (spriteouthandle, frames, sizeof(frames[0])*sprite.numframes);
+
+ fclose (spriteouthandle);
+
+ spritename[0] = 0; // clear for a new sprite
+ sprite.numframes = 0;
+}
+
+
+/*
+===============
+Cmd_Load
+===============
+*/
+void Cmd_Load (void)
+{
+ char *name;
+
+ GetToken (false);
+
+ if (g_release)
+ return;
+
+ name = ExpandPathAndArchive(token);
+
+ // load the image
+ printf ("loading %s\n", name);
+ Load256Image (name, &byteimage, &lbmpalette,
+ &byteimagewidth, &byteimageheight);
+ RemapZero (byteimage, lbmpalette,
+ byteimagewidth, byteimageheight);
+}
+
+
+/*
+===============
+Cmd_SpriteFrame
+===============
+*/
+void Cmd_SpriteFrame (void)
+{
+ int y,xl,yl,xh,yh,w,h;
+ dsprframe_t *pframe;
+ int ox, oy;
+ byte *cropped;
+ char savename[1024];
+
+ GetToken (false);
+ xl = atoi (token);
+ GetToken (false);
+ yl = atoi (token);
+ GetToken (false);
+ w = atoi (token);
+ GetToken (false);
+ h = atoi (token);
+
+ // origin offset is optional
+ if (TokenAvailable ())
+ {
+ GetToken (false);
+ ox = atoi (token);
+ GetToken (false);
+ oy = atoi (token);
+ }
+ else
+ {
+ ox = w/2;
+ oy = h/2;
+ }
+
+ if ((xl & 0x07) || (yl & 0x07) || (w & 0x07) || (h & 0x07))
+ Error ("Sprite dimensions not multiples of 8\n");
+
+ if ((w > 256) || (h > 256))
+ Error ("Sprite has a dimension longer than 256");
+
+ xh = xl+w;
+ yh = yl+h;
+
+ if (sprite.numframes >= MAX_SPRFRAMES)
+ Error ("Too many frames; increase MAX_SPRFRAMES\n");
+
+ pframe = &frames[sprite.numframes];
+ pframe->width = w;
+ pframe->height = h;
+ pframe->origin_x = ox;
+ pframe->origin_y = oy;
+ sprintf (pframe->name, "%s_%i.pcx", spritename, sprite.numframes);
+ sprintf (savename, "%s%s_%i.pcx", gamedir, spritename, sprite.numframes);
+ sprite.numframes++;
+
+ if (g_release)
+ {
+ ReleaseFile (pframe->name);
+ return;
+ }
+
+ // crop it to the proper size
+ cropped = malloc (w*h);
+ for (y=0 ; y<h ; y++)
+ {
+ memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
+ }
+
+ // save off the new image
+ printf ("saving %s\n", savename);
+ CreatePath (savename);
+ WritePCXfile (savename, cropped, w, h, lbmpalette);
+
+ free (cropped);
+}
+
+
+
+/*
+==============
+Cmd_SpriteName
+==============
+*/
+void Cmd_SpriteName (void)
+{
+ if (sprite.numframes)
+ FinishSprite ();
+
+ GetToken (false);
+ strcpy (spritename, token);
+ memset (&sprite, 0, sizeof(sprite));
+ memset (&frames, 0, sizeof(frames));
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+/*
+=============================================================================
+
+ALPHALIGHT GENERATION
+
+Find alphamap values that best match modulated lightmap values
+
+This isn't used anymore, but I'm keeping it around...
+=============================================================================
+*/
+
+unsigned short alphamap[32*32*32];
+unsigned char inverse16to8table[65536];
+
+/*
+static int FindNearestColor( unsigned int color )
+{
+ int i;
+ int closest_so_far = 0;
+ float closest_distance_so_far = 100000000;
+ float d;
+ float r[2], g[2], b[2];
+
+ // incoming color is assumed to be in 0xRRGGBB format
+ r[0] = ( color & 31 ) << 3;
+ g[0] = ( ( color >> 5 ) & 63 ) << 2;
+ b[0] = ( ( color >> 11 ) & 31 ) << 3;
+
+ for ( i = 0; i < 256; i++ )
+ {
+ r[1] = ( d_8to24table[i] >> 0 ) & 0xFF;
+ g[1] = ( d_8to24table[i] >> 8 ) & 0xFF;
+ b[1] = ( d_8to24table[i] >> 16 ) & 0xFF;
+
+ d = ( r[1] - r[0] ) * ( r[1] - r[0] ) +
+ ( g[1] - g[0] ) * ( g[1] - g[0] ) +
+ ( b[1] - b[0] ) * ( b[1] - b[0] );
+
+ if ( d < closest_distance_so_far )
+ {
+ closest_distance_so_far = d;
+ closest_so_far = i;
+ }
+ }
+
+ return closest_so_far;
+}
+*/
+
+extern byte BestColor( int, int, int, int, int );
+
+void Inverse16_BuildTable( void )
+{
+ int i;
+
+ /*
+ ** create the 16-to-8 table
+ */
+ for ( i = 0; i < 65536; i++ )
+ {
+ int r = i & 31;
+ int g = ( i >> 5 ) & 63;
+ int b = ( i >> 11 ) & 31;
+
+ r <<= 3;
+ g <<= 2;
+ b <<= 3;
+
+ inverse16to8table[i] = BestColor( r, g, b, 0, 255 );
+ }
+}
+
+void Alphalight_Thread (int i)
+{
+ int j;
+ float r, g, b;
+ float mr, mg, mb, ma;
+ float distortion, bestdistortion;
+ float v;
+
+ r = (i>>10) * (1.0/16);
+ g = ((i>>5)&31) * (1.0/16);
+ b = (i&31) * (1.0/16);
+
+ bestdistortion = 999999;
+ for (j=0 ; j<16*16*16*16 ; j++)
+ {
+ mr = (j>>12) * (1.0/16);
+ mg = ((j>>8)&15) * (1.0/16);
+ mb = ((j>>4)&15) * (1.0/16);
+ ma = (j&15) * (1.0/16);
+
+ v = r * 0.5 - (mr*ma + 0.5*(1.0-ma));
+ distortion = v*v;
+ v = g * 0.5 - (mg*ma + 0.5*(1.0-ma));
+ distortion += v*v;
+ v = b * 0.5 - (mb*ma + 0.5*(1.0-ma));
+ distortion += v*v;
+
+ distortion *= 1.0 + ma*4;
+
+ if (distortion < bestdistortion)
+ {
+ bestdistortion = distortion;
+ alphamap[i] = j;
+ }
+ }
+}
+
+void Cmd_Alphalight (void)
+{
+ char savename[1024];
+
+ GetToken (false);
+
+ if (g_release)
+ {
+ ReleaseFile (token);
+ return;
+ }
+
+ sprintf (savename, "%s%s", gamedir, token);
+ printf ("Building alphalight table...\n");
+
+ RunThreadsOnIndividual (32*32*32, true, Alphalight_Thread);
+
+ SaveFile (savename, (byte *)alphamap, sizeof(alphamap));
+}
+
+
+void Cmd_Inverse16Table( void )
+{
+ char savename[1024];
+
+ if ( g_release )
+ {
+ sprintf (savename, "pics/16to8.dat");
+ ReleaseFile( savename );
+ return;
+ }
+
+ sprintf (savename, "%spics/16to8.dat", gamedir);
+ printf ("Building inverse 16-to-8 table...\n");
+
+ Inverse16_BuildTable();
+
+ SaveFile( savename, (byte *) inverse16to8table, sizeof( inverse16to8table ) );
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+byte *soundtrack;
+char base[32];
+
+/*
+===============================================================================
+
+WAV loading
+
+===============================================================================
+*/
+
+typedef struct
+{
+ int rate;
+ int width;
+ int channels;
+ int loopstart;
+ int samples;
+ int dataofs; // chunk starts this many bytes from file start
+} wavinfo_t;
+
+
+byte *data_p;
+byte *iff_end;
+byte *last_chunk;
+byte *iff_data;
+int iff_chunk_len;
+
+
+int samplecounts[0x10000];
+
+wavinfo_t wavinfo;
+
+short GetLittleShort(void)
+{
+ short val = 0;
+ val = *data_p;
+ val = val + (*(data_p+1)<<8);
+ data_p += 2;
+ return val;
+}
+
+int GetLittleLong(void)
+{
+ int val = 0;
+ val = *data_p;
+ val = val + (*(data_p+1)<<8);
+ val = val + (*(data_p+2)<<16);
+ val = val + (*(data_p+3)<<24);
+ data_p += 4;
+ return val;
+}
+
+void FindNextChunk(char *name)
+{
+ while (1)
+ {
+ data_p=last_chunk;
+
+ if (data_p >= iff_end)
+ { // didn't find the chunk
+ data_p = NULL;
+ return;
+ }
+
+ data_p += 4;
+ iff_chunk_len = GetLittleLong();
+ if (iff_chunk_len < 0)
+ {
+ data_p = NULL;
+ return;
+ }
+// if (iff_chunk_len > 1024*1024)
+// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
+ data_p -= 8;
+ last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
+ if (!strncmp(data_p, name, 4))
+ return;
+ }
+}
+
+void FindChunk(char *name)
+{
+ last_chunk = iff_data;
+ FindNextChunk (name);
+}
+
+
+void DumpChunks(void)
+{
+ char str[5];
+
+ str[4] = 0;
+ data_p=iff_data;
+ do
+ {
+ memcpy (str, data_p, 4);
+ data_p += 4;
+ iff_chunk_len = GetLittleLong();
+ printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
+ data_p += (iff_chunk_len + 1) & ~1;
+ } while (data_p < iff_end);
+}
+
+/*
+============
+GetWavinfo
+============
+*/
+wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
+{
+ wavinfo_t info;
+ int i;
+ int format;
+ int samples;
+
+ memset (&info, 0, sizeof(info));
+
+ if (!wav)
+ return info;
+
+ iff_data = wav;
+ iff_end = wav + wavlength;
+
+// find "RIFF" chunk
+ FindChunk("RIFF");
+ if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
+ {
+ printf("Missing RIFF/WAVE chunks\n");
+ return info;
+ }
+
+// get "fmt " chunk
+ iff_data = data_p + 12;
+// DumpChunks ();
+
+ FindChunk("fmt ");
+ if (!data_p)
+ {
+ printf("Missing fmt chunk\n");
+ return info;
+ }
+ data_p += 8;
+ format = GetLittleShort();
+ if (format != 1)
+ {
+ printf("Microsoft PCM format only\n");
+ return info;
+ }
+
+ info.channels = GetLittleShort();
+ info.rate = GetLittleLong();
+ data_p += 4+2;
+ info.width = GetLittleShort() / 8;
+
+// get cue chunk
+ FindChunk("cue ");
+ if (data_p)
+ {
+ data_p += 32;
+ info.loopstart = GetLittleLong();
+// Com_Printf("loopstart=%d\n", sfx->loopstart);
+
+ // if the next chunk is a LIST chunk, look for a cue length marker
+ FindNextChunk ("LIST");
+ if (data_p)
+ {
+ if (!strncmp (data_p + 28, "mark", 4))
+ { // this is not a proper parse, but it works with cooledit...
+ data_p += 24;
+ i = GetLittleLong (); // samples in loop
+ info.samples = info.loopstart + i;
+ }
+ }
+ }
+ else
+ info.loopstart = -1;
+
+// find data chunk
+ FindChunk("data");
+ if (!data_p)
+ {
+ printf("Missing data chunk\n");
+ return info;
+ }
+
+ data_p += 4;
+ samples = GetLittleLong ();
+
+ if (info.samples)
+ {
+ if (samples < info.samples)
+ Error ("Sound %s has a bad loop length", name);
+ }
+ else
+ info.samples = samples;
+
+ info.dataofs = data_p - wav;
+
+ return info;
+}
+
+//=====================================================================
+
+/*
+==============
+LoadSoundtrack
+==============
+*/
+void LoadSoundtrack (void)
+{
+ char name[1024];
+ FILE *f;
+ int len;
+ int i, val, j;
+
+ soundtrack = NULL;
+ sprintf (name, "%svideo/%s/%s.wav", gamedir, base, base);
+ printf ("%s\n", name);
+ f = fopen (name, "rb");
+ if (!f)
+ {
+ printf ("no soundtrack for %s\n", base);
+ return;
+ }
+ len = Q_filelength(f);
+ soundtrack = malloc(len);
+ fread (soundtrack, 1, len, f);
+ fclose (f);
+
+ wavinfo = GetWavinfo (name, soundtrack, len);
+
+ // count samples for compression
+ memset (samplecounts, 0, sizeof(samplecounts));
+
+ j = wavinfo.samples/2;
+ for (i=0 ; i<j ; i++)
+ {
+ val = ((unsigned short *)( soundtrack + wavinfo.dataofs))[i];
+ samplecounts[val]++;
+ }
+ val = 0;
+ for (i=0 ; i<0x10000 ; i++)
+ if (samplecounts[i])
+ val++;
+
+ printf ("%i unique sample values\n", val);
+}
+
+/*
+==================
+WriteSound
+==================
+*/
+void WriteSound (FILE *output, int frame)
+{
+ int start, end;
+ int count;
+ int empty = 0;
+ int i;
+ int sample;
+ int width;
+
+ width = wavinfo.width * wavinfo.channels;
+
+ start = frame*wavinfo.rate/14;
+ end = (frame+1)*wavinfo.rate/14;
+ count = end - start;
+
+ for (i=0 ; i<count ; i++)
+ {
+ sample = start+i;
+ if (sample > wavinfo.samples || !soundtrack)
+ fwrite (&empty, 1, width, output);
+ else
+ fwrite (soundtrack + wavinfo.dataofs + sample*width, 1, width,output);
+ }
+}
+
+//==========================================================================
+
+/*
+==================
+MTF
+==================
+*/
+cblock_t MTF (cblock_t in)
+{
+ int i, j, b, code;
+ byte *out_p;
+ int index[256];
+ cblock_t out;
+
+ out_p = out.data = malloc(in.count + 4);
+
+ // write count
+ *out_p++ = in.count&255;
+ *out_p++ = (in.count>>8)&255;
+ *out_p++ = (in.count>>16)&255;
+ *out_p++ = (in.count>>24)&255;
+
+ for (i=0 ; i<256 ; i++)
+ index[i] = i;
+
+ for (i=0 ; i<in.count ; i++)
+ {
+ b = in.data[i];
+ code = index[b];
+ *out_p++ = code;
+
+ // shuffle b indexes to 0
+ for (j=0 ; j<256 ; j++)
+ if (index[j] < code)
+ index[j]++;
+ index[b] = 0;
+ }
+
+ out.count = out_p - out.data;
+
+ return out;
+}
+
+
+//==========================================================================
+
+int bwt_size;
+byte *bwt_data;
+
+int bwtCompare (const void *elem1, const void *elem2)
+{
+ int i;
+ int i1, i2;
+ int b1, b2;
+
+ i1 = *(int *)elem1;
+ i2 = *(int *)elem2;
+
+ for (i=0 ; i<bwt_size ; i++)
+ {
+ b1 = bwt_data[i1];
+ b2 = bwt_data[i2];
+ if (b1 < b2)
+ return -1;
+ if (b1 > b2)
+ return 1;
+ if (++i1 == bwt_size)
+ i1 = 0;
+ if (++i2 == bwt_size)
+ i2 = 0;
+ }
+
+ return 0;
+}
+
+/*
+==================
+BWT
+==================
+*/
+cblock_t BWT (cblock_t in)
+{
+ int *sorted;
+ int i;
+ byte *out_p;
+ cblock_t out;
+
+ bwt_size = in.count;
+ bwt_data = in.data;
+
+ sorted = malloc(in.count*sizeof(*sorted));
+ for (i=0 ; i<in.count ; i++)
+ sorted[i] = i;
+ qsort (sorted, in.count, sizeof(*sorted), bwtCompare);
+
+ out_p = out.data = malloc(in.count + 8);
+
+ // write count
+ *out_p++ = in.count&255;
+ *out_p++ = (in.count>>8)&255;
+ *out_p++ = (in.count>>16)&255;
+ *out_p++ = (in.count>>24)&255;
+
+ // write head index
+ for (i=0 ; i<in.count ; i++)
+ if (sorted[i] == 0)
+ break;
+ *out_p++ = i&255;
+ *out_p++ = (i>>8)&255;
+ *out_p++ = (i>>16)&255;
+ *out_p++ = (i>>24)&255;
+
+ // write the L column
+ for (i=0 ; i<in.count ; i++)
+ *out_p++ = in.data[(sorted[i]+in.count-1)%in.count];
+
+ free (sorted);
+
+ out.count = out_p - out.data;
+
+ return out;
+}
+
+//==========================================================================
+
+typedef struct hnode_s
+{
+ int count;
+ qboolean used;
+ int children[2];
+} hnode_t;
+
+int numhnodes;
+hnode_t hnodes[512];
+unsigned charbits[256];
+int charbitscount[256];
+
+int SmallestNode (void)
+{
+ int i;
+ int best, bestnode;
+
+ best = 99999999;
+ bestnode = -1;
+ for (i=0 ; i<numhnodes ; i++)
+ {
+ if (hnodes[i].used)
+ continue;
+ if (!hnodes[i].count)
+ continue;
+ if (hnodes[i].count < best)
+ {
+ best = hnodes[i].count;
+ bestnode = i;
+ }
+ }
+
+ if (bestnode == -1)
+ return -1;
+
+ hnodes[bestnode].used = true;
+ return bestnode;
+}
+
+void BuildChars (int nodenum, unsigned bits, int bitcount)
+{
+ hnode_t *node;
+
+ if (nodenum < 256)
+ {
+ if (bitcount > 32)
+ Error ("bitcount > 32");
+ charbits[nodenum] = bits;
+ charbitscount[nodenum] = bitcount;
+ return;
+ }
+
+ node = &hnodes[nodenum];
+ bits <<= 1;
+ BuildChars (node->children[0], bits, bitcount+1);
+ bits |= 1;
+ BuildChars (node->children[1], bits, bitcount+1);
+}
+
+
+/*
+==================
+Huffman
+==================
+*/
+cblock_t Huffman (cblock_t in)
+{
+ int i;
+ hnode_t *node;
+ int outbits, c;
+ unsigned bits;
+ byte *out_p;
+ cblock_t out;
+ int max, maxchar;
+
+ // count
+ memset (hnodes, 0, sizeof(hnodes));
+ for (i=0 ; i<in.count ; i++)
+ hnodes[in.data[i]].count++;
+
+ // normalize counts
+ max = 0;
+ maxchar = 0;
+ for (i=0 ; i<256 ; i++)
+ {
+ if (hnodes[i].count > max)
+ {
+ max = hnodes[i].count;
+ maxchar = i;
+ }
+ }
+ if (max == 0)
+ Error ("Huffman: max == 0");
+
+ for (i=0 ; i<256 ; i++)
+ {
+ hnodes[i].count = (hnodes[i].count*255+max-1) / max;
+ }
+
+ // build the nodes
+ numhnodes = 256;
+ while (numhnodes != 511)
+ {
+ node = &hnodes[numhnodes];
+
+ // pick two lowest counts
+ node->children[0] = SmallestNode ();
+ if (node->children[0] == -1)
+ break; // no more
+
+ node->children[1] = SmallestNode ();
+ if (node->children[1] == -1)
+ {
+ if (node->children[0] != numhnodes-1)
+ Error ("Bad smallestnode");
+ break;
+ }
+ node->count = hnodes[node->children[0]].count +
+ hnodes[node->children[1]].count;
+ numhnodes++;
+ }
+
+ BuildChars (numhnodes-1, 0, 0);
+
+ out_p = out.data = malloc(in.count*2 + 1024);
+ memset (out_p, 0, in.count*2+1024);
+
+ // write count
+ *out_p++ = in.count&255;
+ *out_p++ = (in.count>>8)&255;
+ *out_p++ = (in.count>>16)&255;
+ *out_p++ = (in.count>>24)&255;
+
+ // save out the 256 normalized counts so the tree can be recreated
+ for (i=0 ; i<256 ; i++)
+ *out_p++ = hnodes[i].count;
+
+ // write bits
+ outbits = 0;
+ for (i=0 ; i<in.count ; i++)
+ {
+ c = charbitscount[in.data[i]];
+ bits = charbits[in.data[i]];
+ while (c)
+ {
+ c--;
+ if (bits & (1<<c))
+ out_p[outbits>>3] |= 1<<(outbits&7);
+ outbits++;
+ }
+ }
+
+ out_p += (outbits+7)>>3;
+
+ out.count = out_p - out.data;
+
+ return out;
+}
+
+//==========================================================================
+
+/*
+==================
+RLE
+==================
+*/
+#define RLE_CODE 0xe8
+#define RLE_TRIPPLE 0xe9
+
+int rle_counts[256];
+int rle_bytes[256];
+
+cblock_t RLE (cblock_t in)
+{
+ int i;
+ byte *out_p;
+ int val;
+ int repeat;
+ cblock_t out;
+
+ out_p = out.data = malloc (in.count*2);
+
+ // write count
+ *out_p++ = in.count&255;
+ *out_p++ = (in.count>>8)&255;
+ *out_p++ = (in.count>>16)&255;
+ *out_p++ = (in.count>>24)&255;
+
+ for (i=0 ; i<in.count ; )
+ {
+ val = in.data[i];
+ rle_bytes[val]++;
+ repeat = 1;
+ i++;
+ while (i<in.count && repeat < 255 && in.data[i] == val)
+ {
+ repeat++;
+ i++;
+ }
+if (repeat < 256)
+rle_counts[repeat]++;
+ if (repeat > 3 || val == RLE_CODE)
+ {
+ *out_p++ = RLE_CODE;
+ *out_p++ = val;
+ *out_p++ = repeat;
+ }
+ else
+ {
+ while (repeat--)
+ *out_p++ = val;
+ }
+ }
+
+ out.count = out_p - out.data;
+ return out;
+}
+
+//==========================================================================
+
+unsigned lzss_head[256];
+unsigned lzss_next[0x20000];
+
+/*
+==================
+LZSS
+==================
+*/
+#define BACK_WINDOW 0x10000
+#define BACK_BITS 16
+#define FRONT_WINDOW 16
+#define FRONT_BITS 4
+cblock_t LZSS (cblock_t in)
+{
+ int i;
+ byte *out_p;
+ cblock_t out;
+ int val;
+ int j, start, max;
+ int bestlength, beststart;
+ int outbits;
+
+if (in.count >= sizeof(lzss_next)/4)
+Error ("LZSS: too big");
+
+ memset (lzss_head, -1, sizeof(lzss_head));
+
+ out_p = out.data = malloc (in.count*2);
+ memset (out.data, 0, in.count*2);
+
+ // write count
+ *out_p++ = in.count&255;
+ *out_p++ = (in.count>>8)&255;
+ *out_p++ = (in.count>>16)&255;
+ *out_p++ = (in.count>>24)&255;
+
+ outbits = 0;
+ for (i=0 ; i<in.count ; )
+ {
+ val = in.data[i];
+#if 1
+// chained search
+ bestlength = 0;
+ beststart = 0;
+
+ max = FRONT_WINDOW;
+ if (i + max > in.count)
+ max = in.count - i;
+
+ start = lzss_head[val];
+ while (start != -1 && start >= i-BACK_WINDOW)
+ {
+ // count match length
+ for (j=0 ; j<max ; j++)
+ if (in.data[start+j] != in.data[i+j])
+ break;
+ if (j > bestlength)
+ {
+ bestlength = j;
+ beststart = start;
+ }
+ start = lzss_next[start];
+ }
+
+#else
+// slow simple search
+ // search for a match
+ max = FRONT_WINDOW;
+ if (i + max > in.count)
+ max = in.count - i;
+
+ start = i - BACK_WINDOW;
+ if (start < 0)
+ start = 0;
+ bestlength = 0;
+ beststart = 0;
+ for ( ; start < i ; start++)
+ {
+ if (in.data[start] != val)
+ continue;
+ // count match length
+ for (j=0 ; j<max ; j++)
+ if (in.data[start+j] != in.data[i+j])
+ break;
+ if (j > bestlength)
+ {
+ bestlength = j;
+ beststart = start;
+ }
+ }
+#endif
+ beststart = BACK_WINDOW - (i-beststart);
+
+ if (bestlength < 3)
+ { // output a single char
+ bestlength = 1;
+
+ out_p[outbits>>3] |= 1<<(outbits&7); // set bit to mark char
+ outbits++;
+ for (j=0 ; j<8 ; j++, outbits++)
+ if (val & (1<<j) )
+ out_p[outbits>>3] |= 1<<(outbits&7);
+ }
+ else
+ { // output a phrase
+ outbits++; // leave a 0 bit to mark phrase
+ for (j=0 ; j<BACK_BITS ; j++, outbits++)
+ if (beststart & (1<<j) )
+ out_p[outbits>>3] |= 1<<(outbits&7);
+ for (j=0 ; j<FRONT_BITS ; j++, outbits++)
+ if (bestlength & (1<<j) )
+ out_p[outbits>>3] |= 1<<(outbits&7);
+ }
+
+ while (bestlength--)
+ {
+ val = in.data[i];
+ lzss_next[i] = lzss_head[val];
+ lzss_head[val] = i;
+ i++;
+ }
+ }
+
+ out_p += (outbits+7)>>3;
+ out.count = out_p - out.data;
+ return out;
+}
+
+//==========================================================================
+
+#define MIN_REPT 15
+#define MAX_REPT 0
+#define HUF_TOKENS (256+MAX_REPT)
+
+unsigned charbits1[256][HUF_TOKENS];
+int charbitscount1[256][HUF_TOKENS];
+
+hnode_t hnodes1[256][HUF_TOKENS*2];
+int numhnodes1[256];
+
+int order0counts[256];
+
+/*
+==================
+SmallestNode1
+==================
+*/
+int SmallestNode1 (hnode_t *hnodes, int numhnodes)
+{
+ int i;
+ int best, bestnode;
+
+ best = 99999999;
+ bestnode = -1;
+ for (i=0 ; i<numhnodes ; i++)
+ {
+ if (hnodes[i].used)
+ continue;
+ if (!hnodes[i].count)
+ continue;
+ if (hnodes[i].count < best)
+ {
+ best = hnodes[i].count;
+ bestnode = i;
+ }
+ }
+
+ if (bestnode == -1)
+ return -1;
+
+ hnodes[bestnode].used = true;
+ return bestnode;
+}
+
+
+/*
+==================
+BuildChars1
+==================
+*/
+void BuildChars1 (int prev, int nodenum, unsigned bits, int bitcount)
+{
+ hnode_t *node;
+
+ if (nodenum < HUF_TOKENS)
+ {
+ if (bitcount > 32)
+ Error ("bitcount > 32");
+ charbits1[prev][nodenum] = bits;
+ charbitscount1[prev][nodenum] = bitcount;
+ return;
+ }
+
+ node = &hnodes1[prev][nodenum];
+ bits <<= 1;
+ BuildChars1 (prev, node->children[0], bits, bitcount+1);
+ bits |= 1;
+ BuildChars1 (prev, node->children[1], bits, bitcount+1);
+}
+
+
+/*
+==================
+BuildTree1
+==================
+*/
+void BuildTree1 (int prev)
+{
+ hnode_t *node, *nodebase;
+ int numhnodes;
+
+ // build the nodes
+ numhnodes = HUF_TOKENS;
+ nodebase = hnodes1[prev];
+ while (1)
+ {
+ node = &nodebase[numhnodes];
+
+ // pick two lowest counts
+ node->children[0] = SmallestNode1 (nodebase, numhnodes);
+ if (node->children[0] == -1)
+ break; // no more
+
+ node->children[1] = SmallestNode1 (nodebase, numhnodes);
+ if (node->children[1] == -1)
+ break;
+
+ node->count = nodebase[node->children[0]].count +
+ nodebase[node->children[1]].count;
+ numhnodes++;
+ }
+ numhnodes1[prev] = numhnodes-1;
+ BuildChars1 (prev, numhnodes-1, 0, 0);
+}
+
+
+/*
+==================
+Huffman1_Count
+==================
+*/
+void Huffman1_Count (cblock_t in)
+{
+ int i;
+ int prev;
+ int v;
+ int rept;
+
+ prev = 0;
+ for (i=0 ; i<in.count ; i++)
+ {
+ v = in.data[i];
+ order0counts[v]++;
+ hnodes1[prev][v].count++;
+ prev = v;
+#if 1
+ for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
+ if (in.data[i+rept] != v)
+ break;
+ if (rept > MIN_REPT)
+ {
+ hnodes1[prev][255+rept].count++;
+ i += rept-1;
+ }
+#endif
+ }
+}
+
+
+/*
+==================
+Huffman1_Build
+==================
+*/
+byte scaled[256][HUF_TOKENS];
+void Huffman1_Build (FILE *f)
+{
+ int i, j, v;
+ int max;
+ int total;
+
+ for (i=0 ; i<256 ; i++)
+ {
+ // normalize and save the counts
+ max = 0;
+ for (j=0 ; j<HUF_TOKENS ; j++)
+ {
+ if (hnodes1[i][j].count > max)
+ max = hnodes1[i][j].count;
+ }
+ if (max == 0)
+ max = 1;
+ total = 0;
+ for (j=0 ; j<HUF_TOKENS ; j++)
+ { // easy to overflow 32 bits here!
+ v = (hnodes1[i][j].count*(double)255+max-1)/max;
+ if (v > 255)
+ Error ("v > 255");
+ scaled[i][j] = hnodes1[i][j].count = v;
+ if (v)
+ total++;
+ }
+ if (total == 1)
+ { // must have two tokens
+ if (!scaled[i][0])
+ scaled[i][0] = hnodes1[i][0].count = 1;
+ else
+ scaled[i][1] = hnodes1[i][1].count = 1;
+ }
+
+ BuildTree1 (i);
+ }
+
+#if 0
+ // count up the total bits
+ total = 0;
+ for (i=0 ; i<256 ; i++)
+ for (j=0 ; j<256 ; j++)
+ total += charbitscount1[i][j] * hnodes1[i][j].count;
+
+ total = (total+7)/8;
+ printf ("%i bytes huffman1 compressed\n", total);
+#endif
+
+ fwrite (scaled, 1, sizeof(scaled), f);
+}
+
+/*
+==================
+Huffman1
+
+Order 1 compression with pre-built table
+==================
+*/
+cblock_t Huffman1 (cblock_t in)
+{
+ int i;
+ int outbits, c;
+ unsigned bits;
+ byte *out_p;
+ cblock_t out;
+ int prev;
+ int v;
+ int rept;
+
+ out_p = out.data = malloc(in.count*2 + 1024);
+ memset (out_p, 0, in.count*2+1024);
+
+ // write count
+ *out_p++ = in.count&255;
+ *out_p++ = (in.count>>8)&255;
+ *out_p++ = (in.count>>16)&255;
+ *out_p++ = (in.count>>24)&255;
+
+ // write bits
+ outbits = 0;
+ prev = 0;
+ for (i=0 ; i<in.count ; i++)
+ {
+ v = in.data[i];
+
+ c = charbitscount1[prev][v];
+ bits = charbits1[prev][v];
+ if (!c)
+ Error ("!bits");
+ while (c)
+ {
+ c--;
+ if (bits & (1<<c))
+ out_p[outbits>>3] |= 1<<(outbits&7);
+ outbits++;
+ }
+
+ prev = v;
+#if 1
+ // check for repeat encodes
+ for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
+ if (in.data[i+rept] != v)
+ break;
+ if (rept > MIN_REPT)
+ {
+ c = charbitscount1[prev][255+rept];
+ bits = charbits1[prev][255+rept];
+ if (!c)
+ Error ("!bits");
+ while (c)
+ {
+ c--;
+ if (bits & (1<<c))
+ out_p[outbits>>3] |= 1<<(outbits&7);
+ outbits++;
+ }
+ i += rept-1;
+ }
+#endif
+ }
+
+ out_p += (outbits+7)>>3;
+
+ out.count = out_p - out.data;
+
+ return out;
+}
+
+//==========================================================================
+
+
+/*
+===================
+LoadFrame
+===================
+*/
+cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)
+{
+ int ten3, ten2, ten1, ten0;
+ cblock_t in;
+ int width, height;
+ char name[1024];
+ FILE *f;
+
+ in.data = NULL;
+ in.count = -1;
+
+ ten3 = frame/1000;
+ ten2 = (frame-ten3*1000)/100;
+ ten1 = (frame-ten3*1000-ten2*100)/10;
+ ten0 = frame%10;
+
+ if (digits == 4)
+ sprintf (name, "%svideo/%s/%s%i%i%i%i.pcx", gamedir, base, base, ten3, ten2, ten1, ten0);
+ else
+ sprintf (name, "%svideo/%s/%s%i%i%i.pcx", gamedir, base, base, ten2, ten1, ten0);
+
+ f = fopen(name, "rb");
+ if (!f)
+ {
+ in.data = NULL;
+ return in;
+ }
+ fclose (f);
+
+ printf ("%s\n", name);
+ Load256Image (name, &in.data, palette, &width, &height);
+ in.count = width*height;
+// FIXME: map 0 and 255!
+
+#if 0
+ // rle compress
+ rle = RLE(in);
+ free (in.data);
+
+ return rle;
+#endif
+
+ return in;
+}
+
+/*
+===============
+Cmd_Video
+
+video <directory> <framedigits>
+===============
+*/
+void Cmd_Video (void)
+{
+ char savename[1024];
+ char name[1024];
+ FILE *output;
+ int startframe, frame;
+ byte *palette;
+ int width, height;
+ byte current_palette[768];
+ int command;
+ int i;
+ int digits;
+ cblock_t in, huffman;
+ int swap;
+
+
+ GetToken (false);
+ strcpy (base, token);
+ if (g_release)
+ {
+// sprintf (savename, "video/%s.cin", token);
+// ReleaseFile (savename);
+ return;
+ }
+
+ GetToken (false);
+ digits = atoi(token);
+
+ // optionally skip frames
+ if (TokenAvailable ())
+ {
+ GetToken (false);
+ startframe = atoi(token);
+ }
+ else
+ startframe=0;
+
+ sprintf (savename, "%svideo/%s.cin", gamedir, base);
+
+
+ // clear stuff
+ memset (charbits1, 0, sizeof(charbits1));
+ memset (charbitscount1, 0, sizeof(charbitscount1));
+ memset (hnodes1, 0, sizeof(hnodes1));
+ memset (numhnodes1, 0, sizeof(numhnodes1));
+ memset (order0counts, 0, sizeof(order0counts));
+
+
+ // load the entire sound wav file if present
+ LoadSoundtrack ();
+
+ if (digits == 4)
+ sprintf (name, "%svideo/%s/%s0000.pcx", gamedir, base, base);
+ else
+ sprintf (name, "%svideo/%s/%s000.pcx", gamedir, base, base);
+
+ printf ("%s\n", name);
+ Load256Image (name, NULL, &palette, &width, &height);
+
+ output = fopen (savename, "wb");
+ if (!output)
+ Error ("Can't open %s", savename);
+
+ // write header info
+ i = LittleLong (width);
+ fwrite (&i, 4, 1, output);
+ i = LittleLong (height);
+ fwrite (&i, 4, 1, output);
+ i = LittleLong (wavinfo.rate);
+ fwrite (&i, 4, 1, output);
+ i = LittleLong (wavinfo.width);
+ fwrite (&i, 4, 1, output);
+ i = LittleLong (wavinfo.channels);
+ fwrite (&i, 4, 1, output);
+
+ // build the dictionary
+ for ( frame=startframe ; ; frame++)
+ {
+ printf ("counting ", frame);
+ in = LoadFrame (base, frame, digits, &palette);
+ if (!in.data)
+ break;
+ Huffman1_Count (in);
+ free (in.data);
+ }
+ printf ("\n");
+
+ // build nodes and write counts
+ Huffman1_Build (output);
+
+
+ memset (current_palette, 0, sizeof(current_palette));
+
+ // compress it with the dictionary
+ for (frame=startframe ; ; frame++)
+ {
+ printf ("packing ", frame);
+ in = LoadFrame (base, frame, digits, &palette);
+ if (!in.data)
+ break;
+
+ // see if the palette has changed
+ for (i=0 ; i<768 ; i++)
+ if (palette[i] != current_palette[i])
+ {
+ // write a palette change
+ memcpy (current_palette, palette, sizeof(current_palette));
+ command = LittleLong(1);
+ fwrite (&command, 1, 4, output);
+ fwrite (current_palette, 1, sizeof(current_palette), output);
+ break;
+ }
+ if (i == 768)
+ {
+ command = 0; // no palette change
+ fwrite (&command, 1, 4, output);
+ }
+
+ // save the image
+ huffman = Huffman1 (in);
+ printf ("%5i bytes after huffman1\n", huffman.count);
+
+ swap = LittleLong (huffman.count);
+ fwrite (&swap, 1, sizeof(swap), output);
+
+ fwrite (huffman.data, 1, huffman.count, output);
+
+ // save some sound samples
+ WriteSound (output, frame);
+
+ free (palette);
+ free (in.data);
+ free (huffman.data);
+ }
+ printf ("\n");
+
+ // write end-of-file command
+ command = 2;
+ fwrite (&command, 1, 4, output);
+
+ printf ("Total size: %i\n", ftell (output));
+
+ fclose (output);
+
+ if (soundtrack)
+ free (soundtrack);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include <assert.h>
+#include "qe3.h"
+
+#define MAX_POINTS_ON_WINDING 64
+
+face_t *Face_Alloc( void );
+void Face_Free( face_t *f );
+
+winding_t *NewWinding (int points);
+void FreeWinding (winding_t *w);
+winding_t *Winding_Clone( winding_t *w );
+winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon);
+
+void PrintWinding (winding_t *w)
+{
+ int i;
+
+ printf ("-------------\n");
+ for (i=0 ; i<w->numpoints ; i++)
+ printf ("(%5.2f, %5.2f, %5.2f)\n", w->points[i][0]
+ , w->points[i][1], w->points[i][2]);
+}
+
+void PrintPlane (plane_t *p)
+{
+ printf ("(%5.2f, %5.2f, %5.2f) : %5.2f\n", p->normal[0], p->normal[1],
+ p->normal[2], p->dist);
+}
+
+void PrintVector (vec3_t v)
+{
+ printf ("(%5.2f, %5.2f, %5.2f)\n", v[0], v[1], v[2]);
+}
+
+
+face_t *Face_Clone (face_t *f)
+{
+ face_t *n;
+
+ n = Face_Alloc();
+ n->texdef = f->texdef;
+ memcpy (n->planepts, f->planepts, sizeof(n->planepts));
+
+ // all other fields are derived, and will be set by Brush_Build
+ return n;
+}
+
+//============================================================================
+
+#define BOGUS_RANGE 18000
+
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (int points)
+{
+ winding_t *w;
+ int size;
+
+ if (points > MAX_POINTS_ON_WINDING)
+ Error ("NewWinding: %i points", points);
+
+ size = (int)((winding_t *)0)->points[points];
+ w = malloc (size);
+ memset (w, 0, size);
+ w->maxpoints = points;
+
+ return w;
+}
+
+
+void FreeWinding (winding_t *w)
+{
+ free (w);
+}
+
+
+/*
+==================
+Winding_Clone
+==================
+*/
+winding_t *Winding_Clone(winding_t *w)
+{
+ int size;
+ winding_t *c;
+
+ size = (int)((winding_t *)0)->points[w->numpoints];
+ c = qmalloc (size);
+ memcpy (c, w, size);
+ return c;
+}
+
+
+/*
+==================
+ClipWinding
+
+Clips the winding to the plane, returning the new winding on the positive side
+Frees the input winding.
+If keepon is true, an exactly on-plane winding will be saved, otherwise
+it will be clipped away.
+==================
+*/
+winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING];
+ int sides[MAX_POINTS_ON_WINDING];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *neww;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->points[i], split->normal);
+ dot -= split->dist;
+ dists[i] = dot;
+ if (dot > ON_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (keepon && !counts[0] && !counts[1])
+ return in;
+
+ if (!counts[0])
+ {
+ FreeWinding (in);
+ return NULL;
+ }
+ if (!counts[1])
+ return in;
+
+ maxpts = in->numpoints+4; // can't use counts[0]+2 because
+ // of fp grouping errors
+ neww = NewWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->points[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = in->points[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (split->normal[j] == 1)
+ mid[j] = split->dist;
+ else if (split->normal[j] == -1)
+ mid[j] = -split->dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+ if (neww->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+
+// free the original winding
+ FreeWinding (in);
+
+ return neww;
+}
+
+
+
+/*
+=============================================================================
+
+ TEXTURE COORDINATES
+
+=============================================================================
+*/
+
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+vec3_t baseaxis[18] =
+{
+{0,0,1}, {1,0,0}, {0,-1,0}, // floor
+{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
+{1,0,0}, {0,1,0}, {0,0,-1}, // west wall
+{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
+{0,1,0}, {1,0,0}, {0,0,-1}, // south wall
+{0,-1,0}, {1,0,0}, {0,0,-1} // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
+{
+ int bestaxis;
+ float dot,best;
+ int i;
+
+ best = 0;
+ bestaxis = 0;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ dot = DotProduct (pln->normal, baseaxis[i*3]);
+ if (dot > best)
+ {
+ best = dot;
+ bestaxis = i;
+ }
+ }
+
+ VectorCopy (baseaxis[bestaxis*3+1], xv);
+ VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+float lightaxis[3] = {0.6, 0.8, 1.0};
+/*
+================
+SetShadeForPlane
+
+Light different planes differently to
+improve recognition
+================
+*/
+float SetShadeForPlane (plane_t *p)
+{
+ int i;
+ float f;
+
+ // axial plane
+ for (i=0 ; i<3 ; i++)
+ if (fabs(p->normal[i]) > 0.9)
+ {
+ f = lightaxis[i];
+ return f;
+ }
+
+ // between two axial planes
+ for (i=0 ; i<3 ; i++)
+ if (fabs(p->normal[i]) < 0.1)
+ {
+ f = (lightaxis[(i+1)%3] + lightaxis[(i+2)%3])/2;
+ return f;
+ }
+
+ // other
+ f= (lightaxis[0] + lightaxis[1] + lightaxis[2]) / 3;
+ return f;
+}
+
+vec3_t vecs[2];
+float shift[2];
+
+/*
+================
+BeginTexturingFace
+================
+*/
+void BeginTexturingFace (brush_t *b, face_t *f, qtexture_t *q)
+{
+ vec3_t pvecs[2];
+ int sv, tv;
+ float ang, sinv, cosv;
+ float ns, nt;
+ int i,j;
+ float shade;
+
+ // get natural texture axis
+ TextureAxisFromPlane(&f->plane, pvecs[0], pvecs[1]);
+
+ // set shading for face
+ shade = SetShadeForPlane (&f->plane);
+ if (camera.draw_mode == cd_texture && !b->owner->eclass->fixedsize)
+ {
+ f->d_color[0] =
+ f->d_color[1] =
+ f->d_color[2] = shade;
+ }
+ else
+ {
+ f->d_color[0] = shade*q->color[0];
+ f->d_color[1] = shade*q->color[1];
+ f->d_color[2] = shade*q->color[2];
+ }
+
+ if (camera.draw_mode != cd_texture)
+ return;
+
+ if (!f->texdef.scale[0])
+ f->texdef.scale[0] = 1;
+ if (!f->texdef.scale[1])
+ f->texdef.scale[1] = 1;
+
+
+// rotate axis
+ if (f->texdef.rotate == 0)
+ { sinv = 0 ; cosv = 1; }
+ else if (f->texdef.rotate == 90)
+ { sinv = 1 ; cosv = 0; }
+ else if (f->texdef.rotate == 180)
+ { sinv = 0 ; cosv = -1; }
+ else if (f->texdef.rotate == 270)
+ { sinv = -1 ; cosv = 0; }
+ else
+ {
+ ang = f->texdef.rotate / 180 * Q_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+ }
+
+ if (pvecs[0][0])
+ sv = 0;
+ else if (pvecs[0][1])
+ sv = 1;
+ else
+ sv = 2;
+
+ if (pvecs[1][0])
+ tv = 0;
+ else if (pvecs[1][1])
+ tv = 1;
+ else
+ tv = 2;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv];
+ nt = sinv * pvecs[i][sv] + cosv * pvecs[i][tv];
+ vecs[i][sv] = ns;
+ vecs[i][tv] = nt;
+ }
+
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ vecs[i][j] = vecs[i][j] / f->texdef.scale[i];
+}
+
+
+void _EmitTextureCoordinates (vec3_t v, qtexture_t *q)
+{
+ float s, t;
+
+ s = DotProduct (v, vecs[0]);
+ t = DotProduct (v, vecs[1]);
+
+ s += shift[0];
+ t += shift[1];
+
+ s /= q->width;
+ t /= q->height;
+
+ glTexCoord2f (s, t);
+}
+
+void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f)
+{
+ float s, t, ns, nt;
+ float ang, sinv, cosv;
+ vec3_t vecs[2];
+ texdef_t *td;
+
+ // get natural texture axis
+ TextureAxisFromPlane(&f->plane, vecs[0], vecs[1]);
+
+ td = &f->texdef;
+
+ ang = td->rotate / 180 * Q_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+
+ if (!td->scale[0])
+ td->scale[0] = 1;
+ if (!td->scale[1])
+ td->scale[1] = 1;
+
+ s = DotProduct(xyzst, vecs[0]);
+ t = DotProduct(xyzst, vecs[1]);
+
+ ns = cosv * s - sinv * t;
+ nt = sinv * s + cosv * t;
+
+ s = ns/td->scale[0] + td->shift[0];
+ t = nt/td->scale[1] + td->shift[1];
+
+ // gl scales everything from 0 to 1
+ s /= q->width;
+ t /= q->height;
+
+ xyzst[3] = s;
+ xyzst[4] = t;
+}
+
+//==========================================================================
+
+
+/*
+=================
+BasePolyForPlane
+=================
+*/
+winding_t *BasePolyForPlane (plane_t *p)
+{
+ int i, x;
+ vec_t max, v;
+ vec3_t org, vright, vup;
+ winding_t *w;
+
+// find the major axis
+
+ max = -BOGUS_RANGE;
+ x = -1;
+ for (i=0 ; i<3; i++)
+ {
+ v = fabs(p->normal[i]);
+ if (v > max)
+ {
+ x = i;
+ max = v;
+ }
+ }
+ if (x==-1)
+ Error ("BasePolyForPlane: no axis found");
+
+ VectorCopy (vec3_origin, vup);
+ switch (x)
+ {
+ case 0:
+ case 1:
+ vup[2] = 1;
+ break;
+ case 2:
+ vup[0] = 1;
+ break;
+ }
+
+
+ v = DotProduct (vup, p->normal);
+ VectorMA (vup, -v, p->normal, vup);
+ VectorNormalize (vup);
+
+ VectorScale (p->normal, p->dist, org);
+
+ CrossProduct (vup, p->normal, vright);
+
+ VectorScale (vup, 8192, vup);
+ VectorScale (vright, 8192, vright);
+
+// project a really big axis aligned box onto the plane
+ w = NewWinding (4);
+
+ VectorSubtract (org, vright, w->points[0]);
+ VectorAdd (w->points[0], vup, w->points[0]);
+
+ VectorAdd (org, vright, w->points[1]);
+ VectorAdd (w->points[1], vup, w->points[1]);
+
+ VectorAdd (org, vright, w->points[2]);
+ VectorSubtract (w->points[2], vup, w->points[2]);
+
+ VectorSubtract (org, vright, w->points[3]);
+ VectorSubtract (w->points[3], vup, w->points[3]);
+
+ w->numpoints = 4;
+
+ return w;
+}
+
+void Brush_MakeFacePlanes (brush_t *b)
+{
+ face_t *f;
+ int j;
+ vec3_t t1, t2, t3;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ // convert to a vector / dist plane
+ for (j=0 ; j<3 ; j++)
+ {
+ t1[j] = f->planepts[0][j] - f->planepts[1][j];
+ t2[j] = f->planepts[2][j] - f->planepts[1][j];
+ t3[j] = f->planepts[1][j];
+ }
+
+ CrossProduct(t1,t2, f->plane.normal);
+ if (VectorCompare (f->plane.normal, vec3_origin))
+ printf ("WARNING: brush plane with no normal\n");
+ VectorNormalize (f->plane.normal);
+ f->plane.dist = DotProduct (t3, f->plane.normal);
+ }
+}
+
+void DrawBrushEntityName (brush_t *b)
+{
+ char *name;
+ float a, s, c;
+ vec3_t mid;
+ int i;
+
+ if (!b->owner)
+ return; // during contruction
+
+ if (b->owner == world_entity)
+ return;
+
+ if (b != b->owner->brushes.onext)
+ return; // not key brush
+
+ // draw the angle pointer
+ a = FloatForKey (b->owner, "angle");
+ if (a)
+ {
+ s = sin (a/180*Q_PI);
+ c = cos (a/180*Q_PI);
+ for (i=0 ; i<3 ; i++)
+ mid[i] = (b->mins[i] + b->maxs[i])*0.5;
+
+ glBegin (GL_LINE_STRIP);
+ glVertex3fv (mid);
+ mid[0] += c*8;
+ mid[1] += s*8;
+ glVertex3fv (mid);
+ mid[0] -= c*4;
+ mid[1] -= s*4;
+ mid[0] -= s*4;
+ mid[1] += c*4;
+ glVertex3fv (mid);
+ mid[0] += c*4;
+ mid[1] += s*4;
+ mid[0] += s*4;
+ mid[1] -= c*4;
+ glVertex3fv (mid);
+ mid[0] -= c*4;
+ mid[1] -= s*4;
+ mid[0] += s*4;
+ mid[1] -= c*4;
+ glVertex3fv (mid);
+ glEnd ();
+ }
+
+ if (!g_qeglobals.d_savedinfo.show_names)
+ return;
+
+ name = ValueForKey (b->owner, "classname");
+ glRasterPos2f (b->mins[0]+4, b->mins[1]+4);
+ glCallLists (strlen(name), GL_UNSIGNED_BYTE, name);
+}
+
+/*
+=================
+MakeFaceWinding
+
+returns the visible polygon on a face
+=================
+*/
+winding_t *MakeFaceWinding (brush_t *b, face_t *face)
+{
+ winding_t *w;
+ face_t *clip;
+ plane_t plane;
+ qboolean past;
+
+ // get a poly that covers an effectively infinite area
+ w = BasePolyForPlane (&face->plane);
+
+ // chop the poly by all of the other faces
+ past = false;
+ for (clip = b->brush_faces ; clip && w ; clip=clip->next)
+ {
+ if (clip == face)
+ {
+ past = true;
+ continue;
+ }
+ if (DotProduct (face->plane.normal, clip->plane.normal) > 0.999
+ && fabs(face->plane.dist - clip->plane.dist) < 0.01 )
+ { // identical plane, use the later one
+ if (past)
+ {
+ free (w);
+ return NULL;
+ }
+ continue;
+ }
+
+ // flip the plane, because we want to keep the back side
+ VectorSubtract (vec3_origin,clip->plane.normal, plane.normal);
+ plane.dist = -clip->plane.dist;
+
+ w = ClipWinding (w, &plane, false);
+ if (!w)
+ return w;
+ }
+
+ if (w->numpoints < 3)
+ {
+ free(w);
+ w = NULL;
+ }
+
+ if (!w)
+ printf ("unused plane\n");
+
+ return w;
+}
+
+
+void Brush_SnapPlanepts (brush_t *b)
+{
+ int i, j;
+ face_t *f;
+
+ for (f=b->brush_faces ; f; f=f->next)
+ for (i=0 ; i<3 ; i++)
+ for (j=0 ; j<3 ; j++)
+ f->planepts[i][j] = floor (f->planepts[i][j] + 0.5);
+}
+
+/*
+** Brush_Build
+**
+** Builds a brush rendering data and also sets the min/max bounds
+*/
+#define ZERO_EPSILON 0.001
+void Brush_Build( brush_t *b )
+{
+// int order;
+// face_t *face;
+// winding_t *w;
+ char title[1024];
+
+ if (modified != 1)
+ {
+ modified = true; // mark the map as changed
+ sprintf (title, "%s *", currentmap);
+
+ QE_ConvertDOSToUnixName( title, title );
+ Sys_SetTitle (title);
+ }
+
+ /*
+ ** build the windings and generate the bounding box
+ */
+ Brush_BuildWindings( b );
+
+ /*
+ ** move the points and edges if in select mode
+ */
+ if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)
+ SetupVertexSelection ();
+}
+
+/*
+=================
+Brush_Parse
+
+The brush is NOT linked to any list
+=================
+*/
+brush_t *Brush_Parse (void)
+{
+ brush_t *b;
+ face_t *f;
+ int i,j;
+
+ g_qeglobals.d_parsed_brushes++;
+ b = qmalloc(sizeof(brush_t));
+
+ do
+ {
+ if (!GetToken (true))
+ break;
+ if (!strcmp (token, "}") )
+ break;
+
+ f = Face_Alloc();
+
+ // add the brush to the end of the chain, so
+ // loading and saving a map doesn't reverse the order
+
+ f->next = NULL;
+ if (!b->brush_faces)
+ {
+ b->brush_faces = f;
+ }
+ else
+ {
+ face_t *scan;
+
+ for (scan=b->brush_faces ; scan->next ; scan=scan->next)
+ ;
+ scan->next = f;
+ }
+
+ // read the three point plane definition
+ for (i=0 ; i<3 ; i++)
+ {
+ if (i != 0)
+ GetToken (true);
+ if (strcmp (token, "(") )
+ Error ("parsing brush");
+
+ for (j=0 ; j<3 ; j++)
+ {
+ GetToken (false);
+ f->planepts[i][j] = atoi(token);
+ }
+
+ GetToken (false);
+ if (strcmp (token, ")") )
+ Error ("parsing brush");
+
+ }
+
+ // read the texturedef
+ GetToken (false);
+ strcpy(f->texdef.name, token);
+ GetToken (false);
+ f->texdef.shift[0] = atoi(token);
+ GetToken (false);
+ f->texdef.shift[1] = atoi(token);
+ GetToken (false);
+ f->texdef.rotate = atoi(token);
+ GetToken (false);
+ f->texdef.scale[0] = atof(token);
+ GetToken (false);
+ f->texdef.scale[1] = atof(token);
+
+ // the flags and value field aren't necessarily present
+ f->d_texture = Texture_ForName( f->texdef.name );
+ f->texdef.flags = f->d_texture->flags;
+ f->texdef.value = f->d_texture->value;
+ f->texdef.contents = f->d_texture->contents;
+
+ if (TokenAvailable ())
+ {
+ GetToken (false);
+ f->texdef.contents = atoi(token);
+ GetToken (false);
+ f->texdef.flags = atoi(token);
+ GetToken (false);
+ f->texdef.value = atoi(token);
+ }
+ } while (1);
+
+ return b;
+}
+
+/*
+=================
+Brush_Write
+=================
+*/
+void Brush_Write (brush_t *b, FILE *f)
+{
+ face_t *fa;
+ char *pname;
+ int i;
+
+ fprintf (f, "{\n");
+ for (fa=b->brush_faces ; fa ; fa=fa->next)
+ {
+ for (i=0 ; i<3 ; i++)
+ fprintf (f, "( %i %i %i ) ", (int)fa->planepts[i][0]
+ , (int)fa->planepts[i][1], (int)fa->planepts[i][2]);
+
+ pname = fa->texdef.name;
+ if (pname[0] == 0)
+ pname = "unnamed";
+
+ fprintf (f, "%s %i %i %i ", pname,
+ (int)fa->texdef.shift[0], (int)fa->texdef.shift[1],
+ (int)fa->texdef.rotate);
+
+ if (fa->texdef.scale[0] == (int)fa->texdef.scale[0])
+ fprintf (f, "%i ", (int)fa->texdef.scale[0]);
+ else
+ fprintf (f, "%f ", (float)fa->texdef.scale[0]);
+ if (fa->texdef.scale[1] == (int)fa->texdef.scale[1])
+ fprintf (f, "%i", (int)fa->texdef.scale[1]);
+ else
+ fprintf (f, "%f", (float)fa->texdef.scale[1]);
+
+ // only output flags and value if not default
+ if (fa->texdef.value != fa->d_texture->value
+ || fa->texdef.flags != fa->d_texture->flags
+ || fa->texdef.contents != fa->d_texture->contents)
+ {
+ fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
+ }
+
+ fprintf (f, "\n");
+ }
+ fprintf (f, "}\n");
+}
+
+
+/*
+=============
+Brush_Create
+
+Create non-textured blocks for entities
+The brush is NOT linked to any list
+=============
+*/
+brush_t *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef)
+{
+ int i, j;
+ vec3_t pts[4][2];
+ face_t *f;
+ brush_t *b;
+
+ for (i=0 ; i<3 ; i++)
+ if (maxs[i] < mins[i])
+ Error ("Brush_InitSolid: backwards");
+
+ b = qmalloc (sizeof(brush_t));
+
+ pts[0][0][0] = mins[0];
+ pts[0][0][1] = mins[1];
+
+ pts[1][0][0] = mins[0];
+ pts[1][0][1] = maxs[1];
+
+ pts[2][0][0] = maxs[0];
+ pts[2][0][1] = maxs[1];
+
+ pts[3][0][0] = maxs[0];
+ pts[3][0][1] = mins[1];
+
+ for (i=0 ; i<4 ; i++)
+ {
+ pts[i][0][2] = mins[2];
+ pts[i][1][0] = pts[i][0][0];
+ pts[i][1][1] = pts[i][0][1];
+ pts[i][1][2] = maxs[2];
+ }
+
+ for (i=0 ; i<4 ; i++)
+ {
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+ j = (i+1)%4;
+
+ VectorCopy (pts[j][1], f->planepts[0]);
+ VectorCopy (pts[i][1], f->planepts[1]);
+ VectorCopy (pts[i][0], f->planepts[2]);
+ }
+
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ VectorCopy (pts[0][1], f->planepts[0]);
+ VectorCopy (pts[1][1], f->planepts[1]);
+ VectorCopy (pts[2][1], f->planepts[2]);
+
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ VectorCopy (pts[2][0], f->planepts[0]);
+ VectorCopy (pts[1][0], f->planepts[1]);
+ VectorCopy (pts[0][0], f->planepts[2]);
+
+ return b;
+}
+
+
+/*
+=============
+Brush_MakeSided
+
+Makes the current brushhave the given number of 2d sides
+=============
+*/
+void Brush_MakeSided (int sides)
+{
+ int i;
+ vec3_t mins, maxs;
+ brush_t *b;
+ texdef_t *texdef;
+ face_t *f;
+ vec3_t mid;
+ float width;
+ float sv, cv;
+
+ if (sides < 3)
+ {
+ Sys_Status ("Bad sides number", 0);
+ return;
+ }
+
+ if (!QE_SingleBrush ())
+ {
+ Sys_Status ("Must have a single brush selected", 0 );
+ return;
+ }
+
+ b = selected_brushes.next;
+ VectorCopy (b->mins, mins);
+ VectorCopy (b->maxs, maxs);
+ texdef = &g_qeglobals.d_texturewin.texdef;
+
+ Brush_Free (b);
+
+ // find center of brush
+ width = 8;
+ for (i=0 ; i<2 ; i++)
+ {
+ mid[i] = (maxs[i] + mins[i])*0.5;
+ if (maxs[i] - mins[i] > width)
+ width = maxs[i] - mins[i];
+ }
+ width /= 2;
+
+ b = qmalloc (sizeof(brush_t));
+
+ // create top face
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+f->planepts[2][0] = mins[0];f->planepts[2][1] = mins[1];f->planepts[2][2] = maxs[2];
+f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = maxs[2];
+f->planepts[0][0] = maxs[0];f->planepts[0][1] = maxs[1];f->planepts[0][2] = maxs[2];
+
+ // create bottom face
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+f->planepts[0][0] = mins[0];f->planepts[0][1] = mins[1];f->planepts[0][2] = mins[2];
+f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = mins[2];
+f->planepts[2][0] = maxs[0];f->planepts[2][1] = maxs[1];f->planepts[2][2] = mins[2];
+
+ for (i=0 ; i<sides ; i++)
+ {
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ sv = sin (i*3.14159265*2/sides);
+ cv = cos (i*3.14159265*2/sides);
+
+ f->planepts[0][0] = floor(mid[0]+width*cv+0.5);
+ f->planepts[0][1] = floor(mid[1]+width*sv+0.5);
+ f->planepts[0][2] = mins[2];
+
+ f->planepts[1][0] = f->planepts[0][0];
+ f->planepts[1][1] = f->planepts[0][1];
+ f->planepts[1][2] = maxs[2];
+
+ f->planepts[2][0] = floor(f->planepts[0][0] - width*sv + 0.5);
+ f->planepts[2][1] = floor(f->planepts[0][1] + width*cv + 0.5);
+ f->planepts[2][2] = maxs[2];
+
+ }
+
+ Brush_AddToList (b, &selected_brushes);
+
+ Entity_LinkBrush (world_entity, b);
+
+ Brush_Build( b );
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+=============
+Brush_Free
+
+Frees the brush with all of its faces and display list.
+Unlinks the brush from whichever chain it is in.
+Decrements the owner entity's brushcount.
+Removes owner entity if this was the last brush
+unless owner is the world.
+=============
+*/
+void Brush_Free (brush_t *b)
+{
+ face_t *f, *next;
+
+ // free faces
+ for (f=b->brush_faces ; f ; f=next)
+ {
+ next = f->next;
+ Face_Free( f );
+ }
+
+ /*
+ for ( i = 0; i < b->d_numwindings; i++ )
+ {
+ if ( b->d_windings[i] )
+ {
+ FreeWinding( b->d_windings[i] );
+ b->d_windings[i] = 0;
+ }
+ }
+ */
+
+ // unlink from active/selected list
+ if (b->next)
+ Brush_RemoveFromList (b);
+
+ // unlink from entity list
+ if (b->onext)
+ Entity_UnlinkBrush (b);
+
+ free (b);
+}
+
+/*
+============
+Brush_Move
+============
+*/
+void Brush_Move (brush_t *b, vec3_t move)
+{
+ int i;
+ face_t *f;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ for (i=0 ; i<3 ; i++)
+ VectorAdd (f->planepts[i], move, f->planepts[i]);
+ Brush_Build( b );
+}
+
+/*
+============
+Brush_Clone
+
+Does NOT add the new brush to any lists
+============
+*/
+brush_t *Brush_Clone (brush_t *b)
+{
+ brush_t *n;
+ face_t *f, *nf;
+
+ n = qmalloc(sizeof(brush_t));
+ n->owner = b->owner;
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ nf = Face_Clone( f );
+ nf->next = n->brush_faces;
+ n->brush_faces = nf;
+ }
+ return n;
+}
+
+/*
+==============
+Brush_Ray
+
+Itersects a ray with a brush
+Returns the face hit and the distance along the ray the intersection occured at
+Returns NULL and 0 if not hit at all
+==============
+*/
+face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist)
+{
+ face_t *f, *firstface;
+ vec3_t p1, p2;
+ float frac, d1, d2;
+ int i;
+
+ VectorCopy (origin, p1);
+ for (i=0 ; i<3 ; i++)
+ p2[i] = p1[i] + dir[i]*16384;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
+ d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
+ if (d1 >= 0 && d2 >= 0)
+ {
+ *dist = 0;
+ return NULL; // ray is on front side of face
+ }
+ if (d1 <=0 && d2 <= 0)
+ continue;
+ // clip the ray to the plane
+ frac = d1 / (d1 - d2);
+ if (d1 > 0)
+ {
+ firstface = f;
+ for (i=0 ; i<3 ; i++)
+ p1[i] = p1[i] + frac *(p2[i] - p1[i]);
+ }
+ else
+ {
+ for (i=0 ; i<3 ; i++)
+ p2[i] = p1[i] + frac *(p2[i] - p1[i]);
+ }
+ }
+
+ // find distance p1 is along dir
+ VectorSubtract (p1, origin, p1);
+ d1 = DotProduct (p1, dir);
+
+ *dist = d1;
+
+ return firstface;
+}
+
+void Brush_AddToList (brush_t *b, brush_t *list)
+{
+ if (b->next || b->prev)
+ Error ("Brush_RemoveFromList: allready linked");
+ b->next = list->next;
+ list->next->prev = b;
+ list->next = b;
+ b->prev = list;
+}
+
+void Brush_RemoveFromList (brush_t *b)
+{
+ if (!b->next || !b->prev)
+ Error ("Brush_RemoveFromList: not linked");
+ b->next->prev = b->prev;
+ b->prev->next = b->next;
+ b->next = b->prev = NULL;
+}
+
+void Brush_SetTexture (brush_t *b, texdef_t *texdef)
+{
+ face_t *f;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ f->texdef = *texdef;
+ Brush_Build( b );
+}
+
+
+qboolean ClipLineToFace (vec3_t p1, vec3_t p2, face_t *f)
+{
+ float d1, d2, fr;
+ int i;
+ float *v;
+
+ d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
+ d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
+
+ if (d1 >= 0 && d2 >= 0)
+ return false; // totally outside
+ if (d1 <= 0 && d2 <= 0)
+ return true; // totally inside
+
+ fr = d1 / (d1 - d2);
+
+ if (d1 > 0)
+ v = p1;
+ else
+ v = p2;
+
+ for (i=0 ; i<3 ; i++)
+ v[i] = p1[i] + fr*(p2[i] - p1[i]);
+
+ return true;
+}
+
+
+int AddPlanept (float *f)
+{
+ int i;
+
+ for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
+ if (g_qeglobals.d_move_points[i] == f)
+ return 0;
+ g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = f;
+ return 1;
+}
+
+/*
+==============
+Brush_SelectFaceForDragging
+
+Adds the faces planepts to move_points, and
+rotates and adds the planepts of adjacent face if shear is set
+==============
+*/
+void Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear)
+{
+ int i;
+ face_t *f2;
+ winding_t *w;
+ float d;
+ brush_t *b2;
+ int c;
+
+ if (b->owner->eclass->fixedsize)
+ return;
+
+ c = 0;
+ for (i=0 ; i<3 ; i++)
+ c += AddPlanept (f->planepts[i]);
+ if (c == 0)
+ return; // allready completely added
+
+ // select all points on this plane in all brushes the selection
+ for (b2=selected_brushes.next ; b2 != &selected_brushes ; b2 = b2->next)
+ {
+ if (b2 == b)
+ continue;
+ for (f2=b2->brush_faces ; f2 ; f2=f2->next)
+ {
+ for (i=0 ; i<3 ; i++)
+ if (fabs(DotProduct(f2->planepts[i], f->plane.normal)
+ -f->plane.dist) > ON_EPSILON)
+ break;
+ if (i==3)
+ { // move this face as well
+ Brush_SelectFaceForDragging (b2, f2, shear);
+ break;
+ }
+ }
+ }
+
+
+ // if shearing, take all the planes adjacent to
+ // selected faces and rotate their points so the
+ // edge clipped by a selcted face has two of the points
+ if (!shear)
+ return;
+
+ for (f2=b->brush_faces ; f2 ; f2=f2->next)
+ {
+ if (f2 == f)
+ continue;
+ w = MakeFaceWinding (b, f2);
+ if (!w)
+ continue;
+
+ // any points on f will become new control points
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ d = DotProduct (w->points[i], f->plane.normal)
+ - f->plane.dist;
+ if (d > -ON_EPSILON && d < ON_EPSILON)
+ break;
+ }
+
+ //
+ // if none of the points were on the plane,
+ // leave it alone
+ //
+ if (i != w->numpoints)
+ {
+ if (i == 0)
+ { // see if the first clockwise point was the
+ // last point on the winding
+ d = DotProduct (w->points[w->numpoints-1]
+ , f->plane.normal) - f->plane.dist;
+ if (d > -ON_EPSILON && d < ON_EPSILON)
+ i = w->numpoints - 1;
+ }
+
+ AddPlanept (f2->planepts[0]);
+
+ VectorCopy (w->points[i], f2->planepts[0]);
+ if (++i == w->numpoints)
+ i = 0;
+
+ // see if the next point is also on the plane
+ d = DotProduct (w->points[i]
+ , f->plane.normal) - f->plane.dist;
+ if (d > -ON_EPSILON && d < ON_EPSILON)
+ AddPlanept (f2->planepts[1]);
+
+ VectorCopy (w->points[i], f2->planepts[1]);
+ if (++i == w->numpoints)
+ i = 0;
+
+ // the third point is never on the plane
+
+ VectorCopy (w->points[i], f2->planepts[2]);
+ }
+
+ free(w);
+ }
+}
+
+/*
+==============
+Brush_SideSelect
+
+The mouse click did not hit the brush, so grab one or more side
+planes for dragging
+==============
+*/
+void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir
+ , qboolean shear)
+{
+ face_t *f, *f2;
+ vec3_t p1, p2;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ VectorCopy (origin, p1);
+ VectorMA (origin, 16384, dir, p2);
+
+ for (f2=b->brush_faces ; f2 ; f2=f2->next)
+ {
+ if (f2 == f)
+ continue;
+ ClipLineToFace (p1, p2, f2);
+ }
+
+ if (f2)
+ continue;
+
+ if (VectorCompare (p1, origin))
+ continue;
+ if (ClipLineToFace (p1, p2, f))
+ continue;
+
+ Brush_SelectFaceForDragging (b, f, shear);
+ }
+
+
+}
+
+void Brush_BuildWindings( brush_t *b )
+{
+ winding_t *w;
+ face_t *face;
+ vec_t v;
+
+ Brush_SnapPlanepts( b );
+
+ // clear the mins/maxs bounds
+ b->mins[0] = b->mins[1] = b->mins[2] = 99999;
+ b->maxs[0] = b->maxs[1] = b->maxs[2] = -99999;
+
+ Brush_MakeFacePlanes (b);
+
+ face = b->brush_faces;
+
+ for ( ; face ; face=face->next)
+ {
+ int i, j;
+
+ w = face->face_winding = MakeFaceWinding (b, face);
+ face->d_texture = Texture_ForName( face->texdef.name );
+
+ if (!w)
+ {
+ continue;
+ }
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ // add to bounding box
+ for (j=0 ; j<3 ; j++)
+ {
+ v = w->points[i][j];
+ if (v > b->maxs[j])
+ b->maxs[j] = v;
+ if (v < b->mins[j])
+ b->mins[j] = v;
+ }
+ }
+ // setup s and t vectors, and set color
+ BeginTexturingFace( b, face, face->d_texture);
+
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ EmitTextureCoordinates( w->points[i], face->d_texture, face);
+ }
+ }
+}
+
+/*
+==================
+Brush_RemoveEmptyFaces
+
+Frees any overconstraining faces
+==================
+*/
+void Brush_RemoveEmptyFaces ( brush_t *b )
+{
+ face_t *f, *next;
+
+ f = b->brush_faces;
+ b->brush_faces = NULL;
+
+ for ( ; f ; f=next)
+ {
+ next = f->next;
+ if (!f->face_winding)
+ Face_Free (f);
+ else
+ {
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+ }
+
+ }
+}
+
+void Brush_Draw( brush_t *b )
+{
+ face_t *face;
+ int i, order;
+ qtexture_t *prev = 0;
+ winding_t *w;
+
+ if (b->owner->eclass->fixedsize && camera.draw_mode == cd_texture)
+ glDisable (GL_TEXTURE_2D);
+
+ // guarantee the texture will be set first
+ prev = NULL;
+ for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
+ {
+ w = face->face_winding;
+ if (!w)
+ continue; // freed face
+
+ if ( face->d_texture != prev && camera.draw_mode == cd_texture)
+ {
+ // set the texture for this face
+ prev = face->d_texture;
+ glBindTexture( GL_TEXTURE_2D, face->d_texture->texture_number );
+ }
+
+ glColor3fv( face->d_color );
+
+ // draw the polygon
+ glBegin(GL_POLYGON);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ if (camera.draw_mode == cd_texture)
+ glTexCoord2fv( &w->points[i][3] );
+ glVertex3fv(w->points[i]);
+ }
+ glEnd();
+ }
+
+ if (b->owner->eclass->fixedsize && camera.draw_mode == cd_texture)
+ glEnable (GL_TEXTURE_2D);
+
+ glBindTexture( GL_TEXTURE_2D, 0 );
+}
+
+void Face_Draw( face_t *f )
+{
+ int i;
+
+ if ( f->face_winding == 0 )
+ return;
+ glBegin( GL_POLYGON );
+ for ( i = 0 ; i < f->face_winding->numpoints; i++)
+ glVertex3fv( f->face_winding->points[i] );
+ glEnd();
+}
+
+void Brush_DrawXY( brush_t *b )
+{
+ face_t *face;
+ int order;
+ winding_t *w;
+ int i;
+
+ for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
+ {
+ // only draw up facing polygons
+ if (face->plane.normal[2] <= 0)
+ continue;
+
+ w = face->face_winding;
+ if (!w)
+ continue;
+
+ // draw the polygon
+ glBegin(GL_LINE_LOOP);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3fv(w->points[i]);
+ glEnd();
+ }
+
+ // optionally add a text label
+ if ( g_qeglobals.d_savedinfo.show_names )
+ DrawBrushEntityName (b);
+}
+
+face_t *Face_Alloc( void )
+{
+ face_t *f = qmalloc( sizeof( *f ) );
+
+ return f;
+}
+
+void Face_Free( face_t *f )
+{
+ assert( f != 0 );
+
+ if ( f->face_winding )
+ free( f->face_winding ), f->face_winding = 0;
+ free( f );
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// brush.h
+
+
+typedef struct
+{
+ int numpoints;
+ int maxpoints;
+ float points[8][5]; // variable sized
+} winding_t;
+
+
+// the normals on planes point OUT of the brush
+#define MAXPOINTS 16
+typedef struct face_s
+{
+ struct face_s *next;
+ vec3_t planepts[3];
+ texdef_t texdef;
+
+ plane_t plane;
+
+ winding_t *face_winding;
+
+ vec3_t d_color;
+ qtexture_t *d_texture;
+
+// int d_numpoints;
+// vec3_t *d_points;
+} face_t;
+
+#define MAX_FACES 16
+typedef struct brush_s
+{
+ struct brush_s *prev, *next; // links in active/selected
+ struct brush_s *oprev, *onext; // links in entity
+ struct entity_s *owner;
+ vec3_t mins, maxs;
+
+ face_t *brush_faces;
+} brush_t;
+
+
+void Brush_AddToList (brush_t *b, brush_t *list);
+void Brush_Build(brush_t *b);
+void Brush_BuildWindings( brush_t *b );
+brush_t *Brush_Clone (brush_t *b);
+brush_t *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef);
+void Brush_Draw( brush_t *b );
+void Brush_DrawXY( brush_t *b );
+void Brush_Free (brush_t *b);
+void Brush_MakeSided (int sides);
+void Brush_Move (brush_t *b, vec3_t move);
+brush_t *Brush_Parse (void);
+face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist);
+void Brush_RemoveFromList (brush_t *b);
+void Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear);
+void Brush_SetTexture (brush_t *b, texdef_t *texdef);
+void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir, qboolean shear);
+void Brush_Write (brush_t *b, FILE *f);
+void Brush_RemoveEmptyFaces ( brush_t *b );
+
+int AddPlanept (float *f);
+face_t *Face_Clone (face_t *f);
+void Face_Draw( face_t *face );
+winding_t *MakeFaceWinding (brush_t *b, face_t *face);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// 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_PATHS 2048
+#define MAX_MAP_ENTSTRING 0x20000
+#define MAX_MAP_TEXTURES 1024
+#define MAX_MAP_TEXINFO 8192
+
+#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_MIPTEX 0x200000
+#define MAX_MAP_LIGHTING 0x200000
+#define MAX_MAP_VISIBILITY 0x100000
+
+// key / value pair sizes
+
+#define MAX_KEY 32
+#define MAX_VALUE 1024
+
+//=============================================================================
+
+#define BSPVERSION 34
+
+typedef struct
+{
+ int fileofs, filelen;
+} lump_t;
+
+#define LUMP_ENTITIES 0
+#define LUMP_PLANES 1
+#define LUMP_TEXTURES 2
+#define LUMP_VERTEXES 3
+#define LUMP_VISIBILITY 4
+#define LUMP_NODES 5
+#define LUMP_TEXINFO 6
+#define LUMP_FACES 7
+#define LUMP_LIGHTING 8
+#define LUMP_LEAFS 9
+#define LUMP_LEAFFACES 10
+#define LUMP_LEAFBRUSHES 11
+#define LUMP_EDGES 12
+#define LUMP_SURFEDGES 13
+#define LUMP_MODELS 14
+#define LUMP_PATHS 15
+#define LUMP_BRUSHES 16
+#define LUMP_BRUSHSIDES 17
+#define LUMP_POP 18
+
+#define HEADER_LUMPS 18
+
+typedef struct
+{
+ 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 visleafs; // not including the solid leaf 0
+ int firstface, numfaces;
+} dmodel_t;
+
+typedef struct
+{
+ int nummiptex;
+ int dataofs[4]; // [nummiptex]
+} dmiptexlump_t;
+
+#define MIPLEVELS 4
+typedef struct miptex_s
+{
+ char name[16];
+ unsigned width, height;
+ unsigned offsets[MIPLEVELS]; // four mip maps stored
+ int flags;
+ int value;
+} miptex_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 allways opposites
+
+typedef struct
+{
+ float normal[3];
+ float dist;
+ int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+// contents flags are seperate bits
+// a 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_LAVA 8
+#define CONTENTS_SLIME 16
+#define CONTENTS_WATER 32
+#define CONTENTS_THINWATER 64 // translucent faces
+
+#define LAST_VISIBLE_CONTENTS 64
+
+// remaining contents are non-visible, and don't eat brushes
+#define CONTENTS_MONSTER 128
+#define CONTENTS_PLAYERCLIP 256
+#define CONTENTS_MONSTERCLIP 512
+
+
+// currents can be added to any other contents, and may be mixed
+#define CONTENTS_CURRENT_0 1024
+#define CONTENTS_CURRENT_90 2048
+#define CONTENTS_CURRENT_180 4096
+#define CONTENTS_CURRENT_270 8192
+#define CONTENTS_CURRENT_UP 16384
+#define CONTENTS_CURRENT_DOWN 32768
+
+#define CONTENTS_ORIGIN 65536 // removed before processing
+
+
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+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 miptex;
+ int flags; // miptex flags + overrides
+ int value; // light emition, etc
+} texinfo_t;
+
+#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision
+#define SURF_LIGHT 2
+
+#define SURF_WATER 4
+#define SURF_SLIME 8
+#define SURF_LAVA 16
+#define SURF_WINDOW 32
+
+#define SURF_SKY 64
+#define SURF_MIRROR 128
+
+#define SURF_SLIPPERY 256
+
+// 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
+ int visofs; // -1 = no visibility info
+
+ 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;
+
+typedef struct
+{
+ float origin[3];
+ float angles[3];
+ int next, prev;
+ int flags;
+ float speed;
+} dpath_t;
+
+//============================================================================
+
+#ifndef QUAKE_GAME
+
+#define ANGLE_UP -1
+#define ANGLE_DOWN -2
+
+
+// the utilities get to be lazy and just use large static arrays
+
+extern int nummodels;
+extern dmodel_t dmodels[MAX_MAP_MODELS];
+
+extern int visdatasize;
+extern byte dvisdata[MAX_MAP_VISIBILITY];
+
+extern int lightdatasize;
+extern byte dlightdata[MAX_MAP_LIGHTING];
+
+extern int texdatasize;
+extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
+
+extern int entdatasize;
+extern char dentdata[MAX_MAP_ENTSTRING];
+
+extern int numleafs;
+extern dleaf_t dleafs[MAX_MAP_LEAFS];
+
+extern int numplanes;
+extern dplane_t dplanes[MAX_MAP_PLANES];
+
+extern int numvertexes;
+extern dvertex_t dvertexes[MAX_MAP_VERTS];
+
+extern int numnodes;
+extern dnode_t dnodes[MAX_MAP_NODES];
+
+extern int numtexinfo;
+extern texinfo_t texinfo[MAX_MAP_TEXINFO];
+
+extern int numfaces;
+extern dface_t dfaces[MAX_MAP_FACES];
+
+extern int numedges;
+extern dedge_t dedges[MAX_MAP_EDGES];
+
+extern int numleaffaces;
+extern unsigned short dleaffaces[MAX_MAP_LEAFFACES];
+
+extern int numleafbrushes;
+extern unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+extern int numsurfedges;
+extern int dsurfedges[MAX_MAP_SURFEDGES];
+
+extern int numpaths;
+extern dpath_t dpaths[MAX_MAP_PATHS];
+
+extern int numbrushes;
+extern dbrush_t dbrushes[MAX_MAP_BRUSHES];
+
+extern int numbrushsides;
+extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
+
+
+void DecompressVis (byte *in, byte *decompressed);
+int CompressVis (byte *vis, byte *dest);
+
+void LoadBSPFile (char *filename);
+void WriteBSPFile (char *filename);
+void PrintBSPFileSizes (void);
+
+//===============
+
+
+typedef struct epair_s
+{
+ struct epair_s *next;
+ char *key;
+ char *value;
+} epair_t;
+
+typedef struct
+{
+ vec3_t origin;
+ int firstbrush;
+ int numbrushes;
+ epair_t *epairs;
+} entity_t;
+
+extern int num_entities;
+extern entity_t entities[MAX_MAP_ENTITIES];
+
+void ParseEntities (void);
+void UnparseEntities (void);
+
+void SetKeyValue (entity_t *ent, char *key, char *value);
+char *ValueForKey (entity_t *ent, char *key);
+// will return "" if not present
+
+vec_t FloatForKey (entity_t *ent, char *key);
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+epair_t *ParseEpair (void);
+
+void PrintEntity (entity_t *ent);
+
+extern int r_leaftovis[MAX_MAP_LEAFS];
+extern int r_vistoleaf[MAX_MAP_LEAFS];
+extern int r_numvisleafs;
+
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+#define PAGEFLIPS 2
+
+void DrawPathLines (void);
+
+camera_t camera;
+
+/*
+============
+Cam_Init
+============
+*/
+void Cam_Init (void)
+{
+// camera.draw_mode = cd_texture;
+// camera.draw_mode = cd_solid;
+// camera.draw_mode = cd_wire;
+
+ camera.timing = false;
+
+ camera.origin[0] = 0;
+ camera.origin[1] = 20;
+ camera.origin[2] = 46;
+
+ camera.color[0] = 0.3;
+ camera.color[1] = 0.3;
+ camera.color[2] = 0.3;
+}
+
+
+//============================================================================
+
+void Cam_BuildMatrix (void)
+{
+ float xa, ya;
+ float matrix[4][4];
+ int i;
+
+ xa = camera.angles[0]/180*Q_PI;
+ ya = camera.angles[1]/180*Q_PI;
+
+ // the movement matrix is kept 2d
+
+ camera.forward[0] = cos(ya);
+ camera.forward[1] = sin(ya);
+ camera.right[0] = camera.forward[1];
+ camera.right[1] = -camera.forward[0];
+
+ glGetFloatv (GL_PROJECTION_MATRIX, &matrix[0][0]);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ camera.vright[i] = matrix[i][0];
+ camera.vup[i] = matrix[i][1];
+ camera.vpn[i] = matrix[i][2];
+ }
+
+ VectorNormalize (camera.vright);
+ VectorNormalize (camera.vup);
+ VectorNormalize (camera.vpn);
+}
+
+//===============================================
+
+/*
+===============
+Cam_ChangeFloor
+===============
+*/
+void Cam_ChangeFloor (qboolean up)
+{
+ brush_t *b;
+ float d, bestd, current;
+ vec3_t start, dir;
+
+ start[0] = camera.origin[0];
+ start[1] = camera.origin[1];
+ start[2] = 8192;
+ dir[0] = dir[1] = 0;
+ dir[2] = -1;
+
+ current = 8192 - (camera.origin[2] - 48);
+ if (up)
+ bestd = 0;
+ else
+ bestd = 16384;
+
+ for (b=active_brushes.next ; b != &active_brushes ; b=b->next)
+ {
+ if (!Brush_Ray (start, dir, b, &d))
+ continue;
+ if (up && d < current && d > bestd)
+ bestd = d;
+ if (!up && d > current && d < bestd)
+ bestd = d;
+ }
+
+ if (bestd == 0 || bestd == 16384)
+ return;
+
+ camera.origin[2] += current - bestd;
+ Sys_UpdateWindows (W_CAMERA|W_Z_OVERLAY);
+}
+
+
+//===============================================
+
+int cambuttonstate;
+static int buttonx, buttony;
+static int cursorx, cursory;
+
+face_t *side_select;
+
+#define ANGLE_SPEED 300
+#define MOVE_SPEED 400
+
+/*
+================
+Cam_PositionDrag
+================
+*/
+void Cam_PositionDrag (void)
+{
+ int x, y;
+
+ Sys_GetCursorPos (&x, &y);
+ if (x != cursorx || y != cursory)
+ {
+ x -= cursorx;
+ VectorMA (camera.origin, x, camera.vright, camera.origin);
+ y -= cursory;
+ camera.origin[2] -= y;
+
+ Sys_SetCursorPos (cursorx, cursory);
+ Sys_UpdateWindows (W_CAMERA | W_XY_OVERLAY);
+ }
+}
+
+/*
+===============
+Cam_MouseControl
+===============
+*/
+void Cam_MouseControl (float dtime)
+{
+ int xl, xh;
+ int yl, yh;
+ float xf, yf;
+
+ if (cambuttonstate != MK_RBUTTON)
+ return;
+
+ xf = (float)(buttonx - camera.width/2) / (camera.width/2);
+ yf = (float)(buttony - camera.height/2) / (camera.height/2);
+
+ xl = camera.width/3;
+ xh = xl*2;
+ yl = camera.height/3;
+ yh = yl*2;
+
+#if 0
+ // strafe
+ if (buttony < yl && (buttonx < xl || buttonx > xh))
+ VectorMA (camera.origin, xf*dtime*MOVE_SPEED, camera.right, camera.origin);
+ else
+#endif
+ {
+ xf *= 1.0 - fabs(yf);
+ if (xf < 0)
+ {
+ xf += 0.1;
+ if (xf > 0)
+ xf = 0;
+ }
+ else
+ {
+ xf -= 0.1;
+ if (xf < 0)
+ xf = 0;
+ }
+
+ VectorMA (camera.origin, yf*dtime*MOVE_SPEED, camera.forward, camera.origin);
+ camera.angles[YAW] += xf*-dtime*ANGLE_SPEED;
+ }
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+}
+
+
+
+
+/*
+==============
+Cam_MouseDown
+==============
+*/
+void Cam_MouseDown (int x, int y, int buttons)
+{
+ vec3_t dir;
+ float f, r, u;
+ int i;
+
+ //
+ // calc ray direction
+ //
+ u = (float)(y - camera.height/2) / (camera.width/2);
+ r = (float)(x - camera.width/2) / (camera.width/2);
+ f = 1;
+
+ for (i=0 ; i<3 ; i++)
+ dir[i] = camera.vpn[i] * f + camera.vright[i] * r + camera.vup[i] * u;
+ VectorNormalize (dir);
+
+ Sys_GetCursorPos (&cursorx, &cursory);
+
+ cambuttonstate = buttons;
+ buttonx = x;
+ buttony = y;
+
+ // LBUTTON = manipulate selection
+ // shift-LBUTTON = select
+ // middle button = grab texture
+ // ctrl-middle button = set entire brush to texture
+ // ctrl-shift-middle button = set single face to texture
+ if ( (buttons == MK_LBUTTON)
+ || (buttons == (MK_LBUTTON | MK_SHIFT))
+ || (buttons == (MK_LBUTTON | MK_CONTROL))
+ || (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT))
+ || (buttons == MK_MBUTTON)
+ || (buttons == (MK_MBUTTON|MK_CONTROL))
+ || (buttons == (MK_MBUTTON|MK_SHIFT|MK_CONTROL)) )
+ {
+ Drag_Begin (x, y, buttons,
+ camera.vright, camera.vup,
+ camera.origin, dir);
+ return;
+ }
+
+ if (buttons == MK_RBUTTON)
+ {
+ Cam_MouseControl (0.1);
+ return;
+ }
+}
+
+/*
+==============
+Cam_MouseUp
+==============
+*/
+void Cam_MouseUp (int x, int y, int buttons)
+{
+ cambuttonstate = 0;
+ Drag_MouseUp ();
+}
+
+
+/*
+==============
+Cam_MouseMoved
+==============
+*/
+void Cam_MouseMoved (int x, int y, int buttons)
+{
+ cambuttonstate = buttons;
+ if (!buttons)
+ return;
+ buttonx = x;
+ buttony = y;
+
+ if (buttons == (MK_RBUTTON|MK_CONTROL) )
+ {
+ Cam_PositionDrag ();
+ Sys_UpdateWindows (W_XY|W_CAMERA|W_Z);
+ return;
+ }
+
+ Sys_GetCursorPos (&cursorx, &cursory);
+
+ if (buttons & (MK_LBUTTON | MK_MBUTTON) )
+ {
+ Drag_MouseMoved (x, y, buttons);
+ Sys_UpdateWindows (W_XY|W_CAMERA|W_Z);
+ }
+}
+
+
+vec3_t cull1, cull2;
+int cullv1[3], cullv2[3];
+
+void InitCull (void)
+{
+ int i;
+
+ VectorSubtract (camera.vpn, camera.vright, cull1);
+ VectorAdd (camera.vpn, camera.vright, cull2);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (cull1[i] > 0)
+ cullv1[i] = 3+i;
+ else
+ cullv1[i] = i;
+ if (cull2[i] > 0)
+ cullv2[i] = 3+i;
+ else
+ cullv2[i] = i;
+ }
+}
+
+qboolean CullBrush (brush_t *b)
+{
+ int i;
+ vec3_t point;
+ float d;
+
+ for (i=0 ; i<3 ; i++)
+ point[i] = b->mins[cullv1[i]] - camera.origin[i];
+
+ d = DotProduct (point, cull1);
+ if (d < -1)
+ return true;
+
+ for (i=0 ; i<3 ; i++)
+ point[i] = b->mins[cullv2[i]] - camera.origin[i];
+
+ d = DotProduct (point, cull2);
+ if (d < -1)
+ return true;
+
+ return false;
+}
+
+
+/*
+==============
+Cam_Draw
+==============
+*/
+void Cam_Draw (void)
+{
+ brush_t *brush;
+ face_t *face;
+ float screenaspect;
+ float yfov;
+ double start, end;
+ int i;
+
+ if (!active_brushes.next)
+ return; // not valid yet
+
+ if (camera.timing)
+ start = Sys_DoubleTime ();
+
+ //
+ // clear
+ //
+ QE_CheckOpenGLForErrors();
+
+ glViewport(0, 0, camera.width, camera.height);
+ glScissor(0, 0, camera.width, camera.height);
+ glClearColor (
+ g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][0],
+ g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][1],
+ g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][2],
+ 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ //
+ // set up viewpoint
+ //
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity ();
+
+ screenaspect = (float)camera.width/camera.height;
+ yfov = 2*atan((float)camera.height/camera.width)*180/Q_PI;
+ gluPerspective (yfov, screenaspect, 2, 8192);
+
+ glRotatef (-90, 1, 0, 0); // put Z going up
+ glRotatef (90, 0, 0, 1); // put Z going up
+ glRotatef (camera.angles[0], 0, 1, 0);
+ glRotatef (-camera.angles[1], 0, 0, 1);
+ glTranslatef (-camera.origin[0], -camera.origin[1], -camera.origin[2]);
+
+ Cam_BuildMatrix ();
+
+ InitCull ();
+
+ //
+ // draw stuff
+ //
+
+ switch (camera.draw_mode)
+ {
+ case cd_wire:
+ glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glColor3f(1.0, 1.0, 1.0);
+// glEnable (GL_LINE_SMOOTH);
+ break;
+
+ case cd_solid:
+ glCullFace(GL_FRONT);
+ glEnable(GL_CULL_FACE);
+ glShadeModel (GL_FLAT);
+
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glDisable(GL_TEXTURE_2D);
+
+ glDisable(GL_BLEND);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc (GL_LEQUAL);
+ break;
+
+ case cd_texture:
+ glCullFace(GL_FRONT);
+ glEnable(GL_CULL_FACE);
+
+ glShadeModel (GL_FLAT);
+
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glEnable(GL_TEXTURE_2D);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glDisable(GL_BLEND);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc (GL_LEQUAL);
+
+#if 0
+
+ {
+ GLfloat fogColor[4] = {0.0, 1.0, 0.0, 0.25};
+
+ glFogi (GL_FOG_MODE, GL_LINEAR);
+ glHint (GL_FOG_HINT, GL_NICEST); /* per pixel */
+ glFogf (GL_FOG_START, -8192);
+ glFogf (GL_FOG_END, 65536);
+ glFogfv (GL_FOG_COLOR, fogColor);
+
+ }
+
+#endif
+ break;
+
+ case cd_blend:
+ glCullFace(GL_FRONT);
+ glEnable(GL_CULL_FACE);
+
+ glShadeModel (GL_FLAT);
+
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glEnable(GL_TEXTURE_2D);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glDisable(GL_DEPTH_TEST);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ }
+
+ glMatrixMode(GL_TEXTURE);
+ for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
+ {
+ if (CullBrush (brush))
+ continue;
+ if (FilterBrush (brush))
+ continue;
+
+ Brush_Draw( brush );
+ }
+ glMatrixMode(GL_PROJECTION);
+
+ //
+ // now draw selected brushes
+ //
+
+ glTranslatef (g_qeglobals.d_select_translate[0], g_qeglobals.d_select_translate[1], g_qeglobals.d_select_translate[2]);
+ glMatrixMode(GL_TEXTURE);
+
+ // draw normally
+ for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+ {
+ Brush_Draw( brush );
+ }
+
+ // blend on top
+ glMatrixMode(GL_PROJECTION);
+
+ glColor4f(1.0, 0.0, 0.0, 0.3);
+ glEnable (GL_BLEND);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable (GL_TEXTURE_2D);
+ for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+ for (face=brush->brush_faces ; face ; face=face->next)
+ Face_Draw( face );
+ if (selected_face)
+ Face_Draw(selected_face);
+
+ // non-zbuffered outline
+
+ glDisable (GL_BLEND);
+ glDisable (GL_DEPTH_TEST);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+ glColor3f (1, 1, 1);
+ for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+ for (face=brush->brush_faces ; face ; face=face->next)
+ Face_Draw( face );
+
+ // edge / vertex flags
+
+ if (g_qeglobals.d_select_mode == sel_vertex)
+ {
+ glPointSize (4);
+ glColor3f (0,1,0);
+ glBegin (GL_POINTS);
+ for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
+ glVertex3fv (g_qeglobals.d_points[i]);
+ glEnd ();
+ glPointSize (1);
+ }
+ else if (g_qeglobals.d_select_mode == sel_edge)
+ {
+ float *v1, *v2;
+
+ glPointSize (4);
+ glColor3f (0,0,1);
+ glBegin (GL_POINTS);
+ for (i=0 ; i<g_qeglobals.d_numedges ; i++)
+ {
+ v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1];
+ v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2];
+ glVertex3f ( (v1[0]+v2[0])*0.5,(v1[1]+v2[1])*0.5,(v1[2]+v2[2])*0.5);
+ }
+ glEnd ();
+ glPointSize (1);
+ }
+
+ //
+ // draw pointfile
+ //
+ glEnable(GL_DEPTH_TEST);
+
+ DrawPathLines ();
+
+ if (g_qeglobals.d_pointfile_display_list)
+ {
+ Pointfile_Draw();
+// glCallList (g_qeglobals.d_pointfile_display_list);
+ }
+
+ // bind back to the default texture so that we don't have problems
+ // elsewhere using/modifying texture maps between contexts
+ glBindTexture( GL_TEXTURE_2D, 0 );
+
+ glFinish();
+ QE_CheckOpenGLForErrors();
+// Sys_EndWait();
+ if (camera.timing)
+ {
+ end = Sys_DoubleTime ();
+ Sys_Printf ("Camera: %i ms\n", (int)(1000*(end-start)));
+ }
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// window system independent camera view code
+
+typedef enum
+{
+ cd_wire,
+ cd_solid,
+ cd_texture,
+ cd_blend
+} camera_draw_mode;
+
+typedef struct
+{
+ int width, height;
+
+ qboolean timing;
+
+ vec3_t origin;
+ vec3_t angles;
+
+ camera_draw_mode draw_mode;
+
+ vec3_t color; // background
+
+ vec3_t forward, right, up; // move matrix
+
+ vec3_t vup, vpn, vright; // view matrix
+} camera_t;
+
+extern camera_t camera;
+
+void Cam_Init ();
+void Cam_KeyDown (int key);
+void Cam_MouseDown (int x, int y, int buttons);
+void Cam_MouseUp (int x, int y, int buttons);
+void Cam_MouseMoved (int x, int y, int buttons);
+void Cam_MouseControl (float dtime);
+void Cam_Draw ();
+
+void Cam_HomeView ();
+void Cam_ChangeFloor (qboolean up);
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// cmdlib.c
+
+#include "cmdlib.h"
+
+#define PATHSEPERATOR '/'
+
+char com_token[1024];
+qboolean com_eof;
+
+/*
+================
+I_FloatTime
+================
+*/
+double I_FloatTime (void)
+{
+ time_t t;
+
+ time (&t);
+
+ return t;
+#if 0
+// more precise, less portable
+ struct timeval tp;
+ struct timezone tzp;
+ static int secbase;
+
+ gettimeofday(&tp, &tzp);
+
+ if (!secbase)
+ {
+ secbase = tp.tv_sec;
+ return tp.tv_usec/1000000.0;
+ }
+
+ return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
+#endif
+}
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+ int c;
+ int len;
+
+ len = 0;
+ com_token[0] = 0;
+
+ if (!data)
+ return NULL;
+
+// skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ {
+ com_eof = true;
+ return NULL; // end of file;
+ }
+ data++;
+ }
+
+// skip // comments
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+
+
+// handle quoted strings specially
+ if (c == '\"')
+ {
+ data++;
+ do
+ {
+ c = *data++;
+ if (c=='\"')
+ {
+ com_token[len] = 0;
+ return data;
+ }
+ com_token[len] = c;
+ len++;
+ } while (1);
+ }
+
+// parse single characters
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+ {
+ com_token[len] = c;
+ len++;
+ com_token[len] = 0;
+ return data+1;
+ }
+
+// parse a regular word
+ do
+ {
+ com_token[len] = c;
+ data++;
+ len++;
+ c = *data;
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+ break;
+ } while (c>32);
+
+ com_token[len] = 0;
+ return data;
+}
+
+
+int Q_strncasecmp (char *s1, char *s2, int n)
+{
+ int c1, c2;
+
+ while (1)
+ {
+ c1 = *s1++;
+ c2 = *s2++;
+
+ if (!n--)
+ return 0; // strings are equal until end point
+
+ if (c1 != c2)
+ {
+ if (c1 >= 'a' && c1 <= 'z')
+ c1 -= ('a' - 'A');
+ if (c2 >= 'a' && c2 <= 'z')
+ c2 -= ('a' - 'A');
+ if (c1 != c2)
+ return -1; // strings not equal
+ }
+ if (!c1)
+ return 0; // strings are equal
+ }
+
+ return -1;
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+ return Q_strncasecmp (s1, s2, 99999);
+}
+
+
+
+/*
+=============================================================================
+
+ MISC FUNCTIONS
+
+=============================================================================
+*/
+
+
+int argc;
+char *argv[MAX_NUM_ARGVS];
+
+/*
+============
+ParseCommandLine
+============
+*/
+void ParseCommandLine (char *lpCmdLine)
+{
+ argc = 1;
+ argv[0] = "programname";
+
+ while (*lpCmdLine && (argc < MAX_NUM_ARGVS))
+ {
+ while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
+ lpCmdLine++;
+
+ if (*lpCmdLine)
+ {
+ argv[argc] = lpCmdLine;
+ argc++;
+
+ while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
+ lpCmdLine++;
+
+ if (*lpCmdLine)
+ {
+ *lpCmdLine = 0;
+ lpCmdLine++;
+ }
+
+ }
+ }
+}
+
+
+
+/*
+=================
+CheckParm
+
+Checks for the given parameter in the program's command line arguments
+Returns the argument number (1 to argc-1) or 0 if not present
+=================
+*/
+int CheckParm (char *check)
+{
+ int i;
+
+ for (i = 1;i<argc;i++)
+ {
+ if ( !Q_strcasecmp(check, argv[i]) )
+ return i;
+ }
+
+ return 0;
+}
+
+
+
+/*
+================
+Q_filelength
+================
+*/
+int Q_filelength (FILE *f)
+{
+ int pos;
+ int end;
+
+ pos = ftell (f);
+ fseek (f, 0, SEEK_END);
+ end = ftell (f);
+ fseek (f, pos, SEEK_SET);
+
+ return end;
+}
+
+
+FILE *SafeOpenWrite (char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "wb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+FILE *SafeOpenRead (char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "rb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+
+void SafeRead (FILE *f, void *buffer, int count)
+{
+ if ( (int)fread (buffer, 1, count, f) != count)
+ Error ("File read failure");
+}
+
+
+void SafeWrite (FILE *f, void *buffer, int count)
+{
+ if ( (int)fwrite (buffer, 1, count, f) != count)
+ Error ("File read failure");
+}
+
+
+
+/*
+==============
+LoadFile
+==============
+*/
+int LoadFile (char *filename, void **bufferptr)
+{
+ FILE *f;
+ int length;
+ void *buffer;
+ extern void *qmalloc( size_t size );
+
+ f = fopen (filename, "rb");
+ if (!f)
+ {
+ *bufferptr = NULL;
+ return -1;
+ }
+ length = Q_filelength (f);
+ buffer = qmalloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+LoadFileNoCrash
+
+returns -1 length if not present
+==============
+*/
+int LoadFileNoCrash (char *filename, void **bufferptr)
+{
+ FILE *f;
+ int length;
+ void *buffer;
+
+ f = fopen (filename, "rb");
+ if (!f)
+ return -1;
+ length = Q_filelength (f);
+ buffer = qmalloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+SaveFile
+==============
+*/
+void SaveFile (char *filename, void *buffer, int count)
+{
+ FILE *f;
+
+ f = SafeOpenWrite (filename);
+ SafeWrite (f, buffer, count);
+ fclose (f);
+}
+
+
+
+void DefaultExtension (char *path, char *extension)
+{
+ char *src;
+//
+// if path doesn't have a .EXT, append extension
+// (extension should include the .)
+//
+ src = path + strlen(path) - 1;
+
+ while (*src != PATHSEPERATOR && src != path)
+ {
+ if (*src == '.')
+ return; // it has an extension
+ src--;
+ }
+
+ strcat (path, extension);
+}
+
+
+void DefaultPath (char *path, char *basepath)
+{
+ char temp[128];
+
+ if (path[0] == PATHSEPERATOR)
+ return; // absolute path location
+ strcpy (temp,path);
+ strcpy (path,basepath);
+ strcat (path,temp);
+}
+
+
+void StripFilename (char *path)
+{
+ int length;
+
+ length = strlen(path)-1;
+ while (length > 0 && path[length] != PATHSEPERATOR)
+ length--;
+ path[length] = 0;
+}
+
+void StripExtension (char *path)
+{
+ int length;
+
+ length = strlen(path)-1;
+ while (length > 0 && path[length] != '.')
+ {
+ length--;
+ if (path[length] == '/')
+ return; // no extension
+ }
+ if (length)
+ path[length] = 0;
+}
+
+
+/*
+====================
+Extract file parts
+====================
+*/
+void ExtractFilePath (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+ while (src != path && *(src-1) != PATHSEPERATOR)
+ src--;
+
+ memcpy (dest, path, src-path);
+ dest[src-path] = 0;
+}
+
+void ExtractFileName (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+ while (src != path && *(src-1) != '/'
+ && *(src-1) != '\\' )
+ src--;
+
+ while (*src)
+ {
+ *dest++ = *src++;
+ }
+ *dest = 0;
+}
+
+void ExtractFileBase (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+ while (src != path && *(src-1) != '/'
+ && *(src-1) != '\\' )
+ src--;
+
+ while (*src && *src != '.')
+ {
+ *dest++ = *src++;
+ }
+ *dest = 0;
+}
+
+void ExtractFileExtension (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a . or the start
+//
+ while (src != path && *(src-1) != '.')
+ src--;
+ if (src == path)
+ {
+ *dest = 0; // no extension
+ return;
+ }
+
+ strcpy (dest,src);
+}
+
+
+/*
+==============
+ParseNum / ParseHex
+==============
+*/
+int ParseHex (char *hex)
+{
+ char *str;
+ int num;
+
+ num = 0;
+ str = hex;
+
+ while (*str)
+ {
+ num <<= 4;
+ if (*str >= '0' && *str <= '9')
+ num += *str-'0';
+ else if (*str >= 'a' && *str <= 'f')
+ num += 10 + *str-'a';
+ else if (*str >= 'A' && *str <= 'F')
+ num += 10 + *str-'A';
+ else
+ Error ("Bad hex number: %s",hex);
+ str++;
+ }
+
+ return num;
+}
+
+
+int ParseNum (char *str)
+{
+ if (str[0] == '$')
+ return ParseHex (str+1);
+ if (str[0] == '0' && str[1] == 'x')
+ return ParseHex (str+2);
+ return atol (str);
+}
+
+
+
+/*
+============================================================================
+
+ BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+#ifdef _SGI_SOURCE
+#define __BIG_ENDIAN__
+#endif
+
+#ifdef __BIG_ENDIAN__
+
+short LittleShort (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short BigShort (short l)
+{
+ return l;
+}
+
+
+int LittleLong (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int BigLong (int l)
+{
+ return l;
+}
+
+
+float LittleFloat (float l)
+{
+ union {byte b[4]; float f;} in, out;
+
+ in.f = l;
+ out.b[0] = in.b[3];
+ out.b[1] = in.b[2];
+ out.b[2] = in.b[1];
+ out.b[3] = in.b[0];
+
+ return out.f;
+}
+
+float BigFloat (float l)
+{
+ return l;
+}
+
+
+#else
+
+
+short BigShort (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short LittleShort (short l)
+{
+ return l;
+}
+
+
+int BigLong (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int LittleLong (int l)
+{
+ return l;
+}
+
+float BigFloat (float l)
+{
+ union {byte b[4]; float f;} in, out;
+
+ in.f = l;
+ out.b[0] = in.b[3];
+ out.b[1] = in.b[2];
+ out.b[2] = in.b[1];
+ out.b[3] = in.b[0];
+
+ return out.f;
+}
+
+float LittleFloat (float l)
+{
+ return l;
+}
+
+
+
+#endif
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// cmdlib.h
+
+#ifndef __CMDLIB__
+#define __CMDLIB__
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+
+#ifndef __BYTEBOOL__
+#define __BYTEBOOL__
+typedef enum {false, true} qboolean;
+typedef unsigned char byte;
+#endif
+
+// the dec offsetof macro doesn't work very well...
+#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
+
+
+// set these before calling CheckParm
+extern int myargc;
+extern char **myargv;
+
+int Q_strncasecmp (char *s1, char *s2, int n);
+int Q_strcasecmp (char *s1, char *s2);
+
+int Q_filelength (FILE *f);
+
+double I_FloatTime (void);
+
+void Error (char *error, ...);
+int CheckParm (char *check);
+void ParseCommandLine (char *lpCmdLine);
+
+FILE *SafeOpenWrite (char *filename);
+FILE *SafeOpenRead (char *filename);
+void SafeRead (FILE *f, void *buffer, int count);
+void SafeWrite (FILE *f, void *buffer, int count);
+
+int LoadFile (char *filename, void **bufferptr);
+int LoadFileNoCrash (char *filename, void **bufferptr);
+void SaveFile (char *filename, void *buffer, int count);
+
+void DefaultExtension (char *path, char *extension);
+void DefaultPath (char *path, char *basepath);
+void StripFilename (char *path);
+void StripExtension (char *path);
+
+void ExtractFilePath (char *path, char *dest);
+void ExtractFileName (char *path, char *dest);
+void ExtractFileBase (char *path, char *dest);
+void ExtractFileExtension (char *path, char *dest);
+
+int ParseNum (char *str);
+
+short BigShort (short l);
+short LittleShort (short l);
+int BigLong (int l);
+int LittleLong (int l);
+float BigFloat (float l);
+float LittleFloat (float l);
+
+
+char *COM_Parse (char *data);
+
+extern char com_token[1024];
+extern qboolean com_eof;
+
+#define MAX_NUM_ARGVS 32
+extern int argc;
+extern char *argv[MAX_NUM_ARGVS];
+
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+/*
+==============
+CSG_SplitBrushByFace
+
+The incoming brush is NOT freed.
+The incoming face is NOT left referenced.
+==============
+*/
+void CSG_SplitBrushByFace (brush_t *in, face_t *f, brush_t **front, brush_t **back)
+{
+ brush_t *b;
+ face_t *nf;
+ vec3_t temp;
+
+ b = Brush_Clone (in);
+ nf = Face_Clone (f);
+
+ nf->texdef = b->brush_faces->texdef;
+ nf->next = b->brush_faces;
+ b->brush_faces = nf;
+
+ Brush_Build( b );
+ Brush_RemoveEmptyFaces ( b );
+ if ( !b->brush_faces )
+ { // completely clipped away
+ Brush_Free (b);
+ *back = NULL;
+ }
+ else
+ {
+ Entity_LinkBrush (in->owner, b);
+ *back = b;
+ }
+
+ b = Brush_Clone (in);
+ nf = Face_Clone (f);
+ // swap the plane winding
+ VectorCopy (nf->planepts[0], temp);
+ VectorCopy (nf->planepts[1], nf->planepts[0]);
+ VectorCopy (temp, nf->planepts[1]);
+
+ nf->texdef = b->brush_faces->texdef;
+ nf->next = b->brush_faces;
+ b->brush_faces = nf;
+
+ Brush_Build( b );
+ Brush_RemoveEmptyFaces ( b );
+ if ( !b->brush_faces )
+ { // completely clipped away
+ Brush_Free (b);
+ *front = NULL;
+ }
+ else
+ {
+ Entity_LinkBrush (in->owner, b);
+ *front = b;
+ }
+}
+
+/*
+=============
+CSG_MakeHollow
+=============
+*/
+void CSG_MakeHollow (void)
+{
+ brush_t *b, *front, *back, *next;
+ face_t *f;
+ face_t split;
+ vec3_t move;
+ int i;
+
+ for (b = selected_brushes.next ; b != &selected_brushes ; b=next)
+ {
+ next = b->next;
+ for (f = b->brush_faces ; f ; f=f->next)
+ {
+ split = *f;
+ VectorScale (f->plane.normal, g_qeglobals.d_gridsize, move);
+ for (i=0 ; i<3 ; i++)
+ VectorSubtract (split.planepts[i], move, split.planepts[i]);
+
+ CSG_SplitBrushByFace (b, &split, &front, &back);
+ if (back)
+ Brush_Free (back);
+ if (front)
+ Brush_AddToList (front, &selected_brushes);
+ }
+ Brush_Free (b);
+ }
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+=============
+CSG_Subtract
+=============
+*/
+void CSG_Subtract (void)
+{
+ brush_t *b, *s, *frag, *front, *back, *next, *snext;
+ face_t *f;
+ int i;
+
+ Sys_Printf ("Subtracting...\n");
+
+ for (b = selected_brushes.next ; b != &selected_brushes ; b=next)
+ {
+ next = b->next;
+
+ if (b->owner->eclass->fixedsize)
+ continue; // can't use texture from a fixed entity, so don't subtract
+
+ for (s=active_brushes.next ; s != &active_brushes ; s=snext)
+ {
+ snext = s->next;
+ if (s->owner->eclass->fixedsize)
+ continue;
+
+ for (i=0 ; i<3 ; i++)
+ if (b->mins[i] >= s->maxs[i] - ON_EPSILON
+ || b->maxs[i] <= s->mins[i] + ON_EPSILON)
+ break;
+ if (i != 3)
+ continue; // definately don't touch
+
+ frag = s;
+ for (f = b->brush_faces ; f && frag ; f=f->next)
+ {
+ CSG_SplitBrushByFace (frag, f, &front, &back);
+ Brush_Free (frag);
+ frag = back;
+ if (front)
+ Brush_AddToList (front, &active_brushes);
+ }
+ if (frag)
+ Brush_Free (frag);
+ }
+ }
+
+ Sys_Printf ("done.\n");
+ Sys_UpdateWindows (W_ALL);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+/*
+
+ drag either multiple brushes, or select plane points from
+ a single brush.
+
+*/
+
+qboolean drag_ok;
+vec3_t drag_xvec;
+vec3_t drag_yvec;
+
+static int buttonstate;
+static int pressx, pressy;
+static vec3_t pressdelta;
+static int buttonx, buttony;
+
+
+//int num_move_points;
+//float *move_points[1024];
+
+int lastx, lasty;
+
+qboolean drag_first;
+
+
+void AxializeVector (vec3_t v)
+{
+ vec3_t a;
+ float o;
+ int i;
+
+ if (!v[0] && !v[1])
+ return;
+ if (!v[1] && !v[2])
+ return;
+ if (!v[0] && !v[2])
+ return;
+
+ for (i=0 ; i<3 ; i++)
+ a[i] = fabs(v[i]);
+ if (a[0] > a[1] && a[0] > a[2])
+ i = 0;
+ else if (a[1] > a[0] && a[1] > a[2])
+ i = 1;
+ else
+ i = 2;
+
+ o = v[i];
+ VectorCopy (vec3_origin, v);
+ if (o<0)
+ v[i] = -1;
+ else
+ v[i] = 1;
+
+}
+
+
+/*
+===========
+Drag_Setup
+===========
+*/
+void Drag_Setup (int x, int y, int buttons,
+ vec3_t xaxis, vec3_t yaxis,
+ vec3_t origin, vec3_t dir)
+{
+ trace_t t;
+ face_t *f;
+
+ if (selected_brushes.next == &selected_brushes)
+ {
+ Sys_Status("No selection to drag\n", 0);
+ return;
+ }
+
+ drag_first = true;
+ g_qeglobals.d_num_move_points = 0;
+ VectorCopy (vec3_origin, pressdelta);
+ pressx = x;
+ pressy = y;
+
+ VectorCopy (xaxis, drag_xvec);
+ AxializeVector (drag_xvec);
+ VectorCopy (yaxis, drag_yvec);
+ AxializeVector (drag_yvec);
+
+ if (g_qeglobals.d_select_mode == sel_vertex)
+ {
+ SelectVertexByRay (origin, dir);
+ if (g_qeglobals.d_num_move_points)
+ {
+ drag_ok = true;
+ return;
+ }
+ }
+ if (g_qeglobals.d_select_mode == sel_edge)
+ {
+ SelectEdgeByRay (origin, dir);
+ if (g_qeglobals.d_num_move_points)
+ {
+ drag_ok = true;
+ return;
+ }
+ }
+
+
+ //
+ // check for direct hit first
+ //
+ t = Test_Ray (origin, dir, true);
+ if (t.selected)
+ {
+ drag_ok = true;
+
+ if (buttons == (MK_LBUTTON|MK_CONTROL) )
+ {
+ Sys_Printf ("Shear dragging face\n");
+ Brush_SelectFaceForDragging (t.brush, t.face, true);
+ }
+ else if (buttons == (MK_LBUTTON|MK_CONTROL|MK_SHIFT) )
+ {
+ Sys_Printf ("Sticky dragging brush\n");
+ for (f=t.brush->brush_faces ; f ; f=f->next)
+ Brush_SelectFaceForDragging (t.brush, f, false);
+ }
+ else
+ Sys_Printf ("Dragging entire selection\n");
+
+ return;
+ }
+
+ if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)
+ return;
+
+ //
+ // check for side hit
+ //
+ if (selected_brushes.next->next != &selected_brushes)
+ {
+ Sys_Printf ("Click isn't inside multiple selection\n");
+ return;
+ }
+
+ if (selected_brushes.next->owner->eclass->fixedsize)
+ {
+ Sys_Printf ("Can't stretch fixed size entities\n");
+ return;
+ }
+
+
+ if (buttons & MK_CONTROL)
+ Brush_SideSelect (selected_brushes.next, origin, dir, true);
+ else
+ Brush_SideSelect (selected_brushes.next, origin, dir, false);
+
+
+ Sys_Printf ("Side stretch\n");
+ drag_ok = true;
+}
+
+entity_t *peLink;
+
+void UpdateTarget(vec3_t origin, vec3_t dir)
+{
+ trace_t t;
+ entity_t *pe;
+ int i;
+ char sz[128];
+
+ t = Test_Ray (origin, dir, 0);
+
+ if (!t.brush)
+ return;
+
+ pe = t.brush->owner;
+
+ if (pe == NULL)
+ return;
+
+ // is this the first?
+ if (peLink != NULL)
+ {
+
+ // Get the target id from out current target
+ // if there is no id, make one
+
+ i = IntForKey(pe, "target");
+ if (i <= 0)
+ {
+ i = GetUniqueTargetId(1);
+ sprintf(sz, "%d", i);
+
+ SetKeyValue(pe, "target", sz);
+ }
+
+ // set the target # into our src
+
+ sprintf(sz, "%d", i);
+ SetKeyValue(peLink, "targetname", sz);
+
+ Sys_UpdateWindows(W_ENTITY);
+
+ }
+
+ // promote the target to the src
+
+ peLink = pe;
+
+}
+
+/*
+===========
+Drag_Begin
+===========
+*/
+void Drag_Begin (int x, int y, int buttons,
+ vec3_t xaxis, vec3_t yaxis,
+ vec3_t origin, vec3_t dir)
+{
+ trace_t t;
+
+ drag_ok = false;
+ VectorCopy (vec3_origin, pressdelta);
+
+ drag_first = true;
+ peLink = NULL;
+
+ // shift LBUTTON = select entire brush
+ if (buttons == (MK_LBUTTON | MK_SHIFT))
+ {
+ if (!dir[0] && !dir[1])
+ Select_Ray (origin, dir, SF_ENTITIES_FIRST); // hack for XY
+ else
+ Select_Ray (origin, dir, 0);
+ return;
+ }
+
+ // ctrl-shift LBUTTON = select single face
+ if (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT))
+ {
+ Select_Deselect ();
+ Select_Ray (origin, dir, SF_SINGLEFACE);
+ return;
+ }
+
+ // LBUTTON + all other modifiers = manipulate selection
+ if (buttons & MK_LBUTTON)
+ {
+ Drag_Setup (x, y, buttons, xaxis, yaxis, origin, dir);
+ return;
+ }
+
+ // middle button = grab texture
+ if (buttons == MK_MBUTTON)
+ {
+ t = Test_Ray (origin, dir, false);
+ if (t.face)
+ {
+ g_qeglobals.d_new_brush_bottom_z = t.brush->mins[2];
+ g_qeglobals.d_new_brush_top_z = t.brush->maxs[2];
+ Texture_SetTexture (&t.face->texdef);
+ }
+ else
+ Sys_Printf ("Did not select a texture\n");
+ return;
+ }
+
+ // ctrl-middle button = set entire brush to texture
+ if (buttons == (MK_MBUTTON|MK_CONTROL) )
+ {
+ t = Test_Ray (origin, dir, false);
+ if (t.brush)
+ {
+ if (t.brush->brush_faces->texdef.name[0] == '(')
+ Sys_Printf ("Can't change an entity texture\n");
+ else
+ {
+ Brush_SetTexture (t.brush, &g_qeglobals.d_texturewin.texdef);
+ Sys_UpdateWindows (W_ALL);
+ }
+ }
+ else
+ Sys_Printf ("Didn't hit a btrush\n");
+ return;
+ }
+
+ // ctrl-shift-middle button = set single face to texture
+ if (buttons == (MK_MBUTTON|MK_SHIFT|MK_CONTROL) )
+ {
+ t = Test_Ray (origin, dir, false);
+ if (t.brush)
+ {
+ if (t.brush->brush_faces->texdef.name[0] == '(')
+ Sys_Printf ("Can't change an entity texture\n");
+ else
+ {
+ t.face->texdef = g_qeglobals.d_texturewin.texdef;
+ Brush_Build( t.brush );
+ Sys_UpdateWindows (W_ALL);
+ }
+ }
+ else
+ Sys_Printf ("Didn't hit a btrush\n");
+ return;
+ }
+
+}
+
+
+/*
+===========
+MoveSelection
+===========
+*/
+void MoveSelection (vec3_t move)
+{
+ int i;
+ brush_t *b;
+
+ if (!move[0] && !move[1] && !move[2])
+ return;
+
+ Sys_UpdateWindows (W_XY|W_CAMERA);
+
+ //
+ // dragging only a part of the selection
+ //
+ if (g_qeglobals.d_num_move_points)
+ {
+ for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
+ VectorAdd (g_qeglobals.d_move_points[i], move, g_qeglobals.d_move_points[i]);
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ {
+ Brush_Build( b );
+ for (i=0 ; i<3 ; i++)
+ if (b->mins[i] > b->maxs[i]
+ || b->maxs[i] - b->mins[i] > 4096)
+ break; // dragged backwards or fucked up
+ if (i != 3)
+ break;
+ }
+
+ // if any of the brushes were crushed out of existance
+ // calcel the entire move
+ if (b != &selected_brushes)
+ {
+ Sys_Printf ("Brush dragged backwards, move canceled\n");
+ for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
+ VectorSubtract (g_qeglobals.d_move_points[i], move, g_qeglobals.d_move_points[i]);
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ Brush_Build( b );
+ }
+
+ }
+ else
+ {
+ //
+ // if there are lots of brushes selected, just translate instead
+ // of rebuilding the brushes
+ //
+ if (drag_yvec[2] == 0 && selected_brushes.next->next != &selected_brushes)
+ {
+ VectorAdd (g_qeglobals.d_select_translate, move, g_qeglobals.d_select_translate);
+ }
+ else
+ {
+ Select_Move (move);
+ }
+ }
+}
+
+/*
+===========
+Drag_MouseMoved
+===========
+*/
+void Drag_MouseMoved (int x, int y, int buttons)
+{
+ vec3_t move, delta;
+ int i;
+ char movestring[128];
+
+ if (!buttons)
+ {
+ drag_ok = false;
+ return;
+ }
+ if (!drag_ok)
+ return;
+
+ // clear along one axis
+ if (buttons & MK_SHIFT)
+ {
+ drag_first = false;
+ if (abs(x-pressx) > abs(y-pressy))
+ y = pressy;
+ else
+ x = pressx;
+ }
+
+
+ for (i=0 ; i<3 ; i++)
+ {
+ move[i] = drag_xvec[i]*(x - pressx)
+ + drag_yvec[i]*(y - pressy);
+ move[i] = floor(move[i]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+ }
+
+ sprintf (movestring, "drag (%i %i %i)", (int)move[0], (int)move[1], (int)move[2]);
+ Sys_Status (movestring, 0);
+
+ VectorSubtract (move, pressdelta, delta);
+ MoveSelection (delta);
+ VectorCopy (move, pressdelta);
+}
+
+/*
+===========
+Drag_MouseUp
+===========
+*/
+void Drag_MouseUp (void)
+{
+ Sys_Status ("drag completed.", 0);
+ if (g_qeglobals.d_select_translate[0] || g_qeglobals.d_select_translate[1] || g_qeglobals.d_select_translate[2])
+ {
+ Select_Move (g_qeglobals.d_select_translate);
+ VectorCopy (vec3_origin, g_qeglobals.d_select_translate);
+ Sys_UpdateWindows (W_CAMERA);
+ }
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include "io.h"
+
+eclass_t *eclass;
+eclass_t *eclass_bad;
+char eclass_directory[1024];
+
+/*
+
+the classname, color triple, and bounding box are parsed out of comments
+A ? size means take the exact brush size.
+
+/*QUAKED <classname> (0 0 0) ?
+/*QUAKED <classname> (0 0 0) (-8 -8 -8) (8 8 8)
+
+Flag names can follow the size description:
+
+/*QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY
+
+*/
+char *debugname;
+
+eclass_t *Eclass_InitFromText (char *text)
+{
+ char *t;
+ int len;
+ int r, i;
+ char parms[256], *p;
+ eclass_t *e;
+ char color[128];
+
+ e = qmalloc(sizeof(*e));
+ memset (e, 0, sizeof(*e));
+
+ text += strlen("/*QUAKED ");
+
+// grab the name
+ text = COM_Parse (text);
+ e->name = qmalloc (strlen(com_token)+1);
+ strcpy (e->name, com_token);
+ debugname = e->name;
+
+// grab the color, reformat as texture name
+ r = sscanf (text," (%f %f %f)", &e->color[0], &e->color[1], &e->color[2]);
+ if (r != 3)
+ return e;
+ sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);
+ strcpy (e->texdef.name, color);
+
+ while (*text != ')')
+ {
+ if (!*text)
+ return e;
+ text++;
+ }
+ text++;
+
+// get the size
+ text = COM_Parse (text);
+ if (com_token[0] == '(')
+ { // parse the size as two vectors
+ e->fixedsize = true;
+ r = sscanf (text,"%f %f %f) (%f %f %f)", &e->mins[0], &e->mins[1], &e->mins[2],
+ &e->maxs[0], &e->maxs[1], &e->maxs[2]);
+ if (r != 6)
+ return e;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ while (*text != ')')
+ {
+ if (!*text)
+ return e;
+ text++;
+ }
+ text++;
+ }
+ }
+ else
+ { // use the brushes
+ }
+
+// get the flags
+
+
+// copy to the first /n
+ p = parms;
+ while (*text && *text != '\n')
+ *p++ = *text++;
+ *p = 0;
+ text++;
+
+// any remaining words are parm flags
+ p = parms;
+ for (i=0 ; i<8 ; i++)
+ {
+ p = COM_Parse (p);
+ if (!p)
+ break;
+ strcpy (e->flagnames[i], com_token);
+ }
+
+// find the length until close comment
+ for (t=text ; t[0] && !(t[0]=='*' && t[1]=='/') ; t++)
+ ;
+
+// copy the comment block out
+ len = t-text;
+ e->comments = qmalloc (len+1);
+ memcpy (e->comments, text, len);
+#if 0
+ for (i=0 ; i<len ; i++)
+ if (text[i] == '\n')
+ e->comments[i] = '\r';
+ else
+ e->comments[i] = text[i];
+#endif
+ e->comments[len] = 0;
+
+ return e;
+}
+
+
+/*
+=================
+Eclass_InsertAlphabetized
+=================
+*/
+void Eclass_InsertAlphabetized (eclass_t *e)
+{
+ eclass_t *s;
+
+ if (!eclass)
+ {
+ eclass = e;
+ return;
+ }
+
+
+ s = eclass;
+ if (stricmp (e->name, s->name) < 0)
+ {
+ e->next = s;
+ eclass = e;
+ return;
+ }
+
+ do
+ {
+ if (!s->next || stricmp (e->name, s->next->name) < 0)
+ {
+ e->next = s->next;
+ s->next = e;
+ return;
+ }
+ s=s->next;
+ } while (1);
+}
+
+
+/*
+=================
+Eclass_ScanFile
+=================
+*/
+void Eclass_ScanFile (char *filename)
+{
+ int size;
+ char *data;
+ eclass_t *e;
+ int i;
+ char temp[1024];
+
+ QE_ConvertDOSToUnixName( temp, filename );
+
+ Sys_Printf ("ScanFile: %s\n", temp);
+
+ size = LoadFile (filename, (void *)&data);
+
+ for (i=0 ; i<size ; i++)
+ if (!strncmp(data+i, "/*QUAKED",8))
+ {
+ e = Eclass_InitFromText (data+i);
+ if (e)
+ Eclass_InsertAlphabetized (e);
+ else
+ printf ("Error parsing: %s in %s\n",debugname, filename);
+ }
+
+ free (data);
+}
+
+
+
+void Eclass_InitForSourceDirectory (char *path)
+{
+ struct _finddata_t fileinfo;
+ int handle;
+ char filename[1024];
+ char filebase[1024];
+ char temp[1024];
+ char *s;
+
+ QE_ConvertDOSToUnixName( temp, path );
+
+ Sys_Printf ("Eclass_InitForSourceDirectory: %s\n", temp );
+
+ strcpy (filebase, path);
+ s = filebase + strlen(filebase)-1;
+ while (*s != '\\' && *s != '/' && s!=filebase)
+ s--;
+ *s = 0;
+
+ eclass = NULL;
+
+ handle = _findfirst (path, &fileinfo);
+ if (handle != -1)
+ {
+ do
+ {
+ sprintf (filename, "%s\\%s", filebase, fileinfo.name);
+ Eclass_ScanFile (filename);
+ } while (_findnext( handle, &fileinfo ) != -1);
+
+ _findclose (handle);
+ }
+
+ eclass_bad = Eclass_InitFromText ("/*QUAKED UNKNOWN_CLASS (0 0.5 0) ?");
+}
+
+eclass_t *Eclass_ForName (char *name, qboolean has_brushes)
+{
+ eclass_t *e;
+ char init[1024];
+
+ if (!name)
+ return eclass_bad;
+
+ for (e=eclass ; e ; e=e->next)
+ if (!strcmp (name, e->name))
+ return e;
+
+ // create a new class for it
+ if (has_brushes)
+ {
+ sprintf (init, "/*QUAKED %s (0 0.5 0) ?\nNot found in source.\n", name);
+ e = Eclass_InitFromText (init);
+ }
+ else
+ {
+ sprintf (init, "/*QUAKED %s (0 0.5 0) (-8 -8 -8) (8 8 8)\nNot found in source.\n", name);
+ e = Eclass_InitFromText (init);
+ }
+
+ Eclass_InsertAlphabetized (e);
+
+ return e;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+char *ValueForKey (entity_t *ent, char *key)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ return ep->value;
+ return "";
+}
+
+void SetKeyValue (entity_t *ent, char *key, char *value)
+{
+ epair_t *ep;
+
+ if (ent == NULL)
+ return;
+
+ if (!key || !key[0])
+ return;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ {
+ free (ep->value);
+ ep->value = qmalloc(strlen(value)+1);
+ strcpy (ep->value, value);
+ return;
+ }
+ ep = qmalloc (sizeof(*ep));
+ ep->next = ent->epairs;
+ ent->epairs = ep;
+ ep->key = qmalloc(strlen(key)+1);
+ strcpy (ep->key, key);
+ ep->value = qmalloc(strlen(value)+1);
+ strcpy (ep->value, value);
+}
+
+void DeleteKey (entity_t *ent, char *key)
+{
+ epair_t **ep, *next;
+
+ ep = &ent->epairs;
+ while (*ep)
+ {
+ next = *ep;
+ if ( !strcmp (next->key, key) )
+ {
+ *ep = next->next;
+ free(next->key);
+ free(next->value);
+ free(next);
+ return;
+ }
+ ep = &next->next;
+ }
+}
+
+float FloatForKey (entity_t *ent, char *key)
+{
+ char *k;
+
+ k = ValueForKey (ent, key);
+ return atof(k);
+}
+
+int IntForKey (entity_t *ent, char *key)
+{
+ char *k;
+
+ k = ValueForKey (ent, key);
+ return atoi(k);
+}
+
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
+{
+ char *k;
+
+ k = ValueForKey (ent, key);
+ sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
+}
+
+
+/*
+===============
+Entity_Free
+
+Frees the entity and any brushes is has.
+The entity is removed from the global entities list.
+===============
+*/
+void Entity_Free (entity_t *e)
+{
+ epair_t *ep, *next;
+
+ while (e->brushes.onext != &e->brushes)
+ Brush_Free (e->brushes.onext);
+
+ if (e->next)
+ {
+ e->next->prev = e->prev;
+ e->prev->next = e->next;
+ }
+
+ for (ep = e->epairs ; ep ; ep=next)
+ {
+ next = ep->next;
+ free (ep);
+ }
+ free (e);
+}
+
+/*
+=================
+ParseEpair
+=================
+*/
+epair_t *ParseEpair (void)
+{
+ epair_t *e;
+
+ e = qmalloc (sizeof(*e));
+
+ e->key = qmalloc(strlen(token)+1);
+ strcpy (e->key, token);
+
+ GetToken (false);
+ e->value = qmalloc(strlen(token)+1);
+ strcpy (e->value, token);
+
+ return e;
+}
+
+/*
+================
+Entity_Parse
+
+If onlypairs is set, the classname info will not
+be looked up, and the entity will not be added
+to the global list. Used for parsing the project.
+================
+*/
+entity_t *Entity_Parse (qboolean onlypairs)
+{
+ entity_t *ent;
+ eclass_t *e;
+ brush_t *b;
+ vec3_t mins, maxs;
+ epair_t *ep;
+ qboolean has_brushes;
+
+ if (!GetToken (true))
+ return NULL;
+
+ if (strcmp (token, "{") )
+ Error ("ParseEntity: { not found");
+
+ ent = qmalloc (sizeof(*ent));
+ ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
+
+ do
+ {
+ if (!GetToken (true))
+ Error ("ParseEntity: EOF without closing brace");
+ if (!strcmp (token, "}") )
+ break;
+ if (!strcmp (token, "{") )
+ {
+ b = Brush_Parse ();
+ b->owner = ent;
+
+ // add to the end of the entity chain
+ b->onext = &ent->brushes;
+ b->oprev = ent->brushes.oprev;
+ ent->brushes.oprev->onext = b;
+ ent->brushes.oprev = b;
+ }
+ else
+ {
+ ep = ParseEpair ();
+ ep->next = ent->epairs;
+ ent->epairs = ep;
+ }
+ } while (1);
+
+ if (onlypairs)
+ return ent;
+
+ if (ent->brushes.onext == &ent->brushes)
+ has_brushes = false;
+ else
+ has_brushes = true;
+
+ GetVectorForKey (ent, "origin", ent->origin);
+
+ e = Eclass_ForName (ValueForKey (ent, "classname"), has_brushes);
+ ent->eclass = e;
+ if (e->fixedsize)
+ { // fixed size entity
+ if (ent->brushes.onext != &ent->brushes)
+ {
+ printf ("Warning: Fixed size entity with brushes\n");
+#if 0
+ while (ent->brushes.onext != &ent->brushes)
+ { // FIXME: this will free the entity and crash!
+ Brush_Free (b);
+ }
+#endif
+ent->brushes.next = ent->brushes.prev = &ent->brushes;
+ }
+ // create a custom brush
+ VectorAdd (e->mins, ent->origin, mins);
+ VectorAdd (e->maxs, ent->origin, maxs);
+ b = Brush_Create (mins, maxs, &e->texdef);
+ b->owner = ent;
+
+ b->onext = ent->brushes.onext;
+ b->oprev = &ent->brushes;
+ ent->brushes.onext->oprev = b;
+ ent->brushes.onext = b;
+ }
+ else
+ { // brush entity
+ if (ent->brushes.next == &ent->brushes)
+ printf ("Warning: Brush entity with no brushes\n");
+ }
+
+ // add all the brushes to the main list
+ for (b=ent->brushes.onext ; b != &ent->brushes ; b=b->onext)
+ {
+ b->next = active_brushes.next;
+ active_brushes.next->prev = b;
+ b->prev = &active_brushes;
+ active_brushes.next = b;
+ }
+
+ return ent;
+}
+
+/*
+============
+Entity_Write
+============
+*/
+void Entity_Write (entity_t *e, FILE *f, qboolean use_region)
+{
+ epair_t *ep;
+ brush_t *b;
+ vec3_t origin;
+ char text[128];
+ int count;
+
+ // if none of the entities brushes are in the region,
+ // don't write the entity at all
+ if (use_region)
+ {
+ // in region mode, save the camera position as playerstart
+ if ( !strcmp(ValueForKey (e, "classname"), "info_player_start") )
+ {
+ fprintf (f, "{\n");
+ fprintf (f, "\"classname\" \"info_player_start\"\n");
+ fprintf (f, "\"origin\" \"%i %i %i\"\n", (int)camera.origin[0],
+ (int)camera.origin[1], (int)camera.origin[2]);
+ fprintf (f, "\"angle\" \"%i\"\n", (int)camera.angles[YAW]);
+ fprintf (f, "}\n");
+ return;
+ }
+
+ for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
+ if (!Map_IsBrushFiltered(b))
+ break; // got one
+
+ if (b == &e->brushes)
+ return; // nothing visible
+ }
+
+ // if fixedsize, calculate a new origin based on the current
+ // brush position
+ if (e->eclass->fixedsize)
+ {
+ VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin);
+ sprintf (text, "%i %i %i", (int)origin[0],
+ (int)origin[1], (int)origin[2]);
+ SetKeyValue (e, "origin", text);
+ }
+
+ fprintf (f, "{\n");
+ for (ep = e->epairs ; ep ; ep=ep->next)
+ fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value);
+
+ if (!e->eclass->fixedsize)
+ {
+ count = 0;
+ for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
+ {
+ if (!use_region || !Map_IsBrushFiltered (b))
+ {
+ fprintf (f, "// brush %i\n", count);
+ count++;
+ Brush_Write (b, f);
+ }
+ }
+ }
+ fprintf (f, "}\n");
+}
+
+
+
+/*
+============
+Entity_Create
+
+Creates a new entity out of the selected_brushes list.
+If the entity class is fixed size, the brushes are only
+used to find a midpoint. Otherwise, the brushes have
+their ownershi[ transfered to the new entity.
+============
+*/
+entity_t *Entity_Create (eclass_t *c)
+{
+ entity_t *e;
+ brush_t *b;
+ vec3_t mins, maxs;
+ int i;
+
+ // check to make sure the brushes are ok
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ if (b->owner != world_entity)
+ {
+ Sys_Printf ("Entity NOT created, brushes not all from world\n");
+ Sys_Beep ();
+ return NULL;
+ }
+
+ // create it
+
+ e = qmalloc(sizeof(*e));
+ e->brushes.onext = e->brushes.oprev = &e->brushes;
+ e->eclass = c;
+ SetKeyValue (e, "classname", c->name);
+
+ // add the entity to the entity list
+ e->next = entities.next;
+ entities.next = e;
+ e->next->prev = e;
+ e->prev = &entities;
+
+ if (c->fixedsize)
+ {
+ //
+ // just use the selection for positioning
+ //
+ b = selected_brushes.next;
+ for (i=0 ; i<3 ; i++)
+ e->origin[i] = b->mins[i] - c->mins[i];
+
+ // create a custom brush
+ VectorAdd (c->mins, e->origin, mins);
+ VectorAdd (c->maxs, e->origin, maxs);
+ b = Brush_Create (mins, maxs, &c->texdef);
+
+ Entity_LinkBrush (e, b);
+
+ // delete the current selection
+ Select_Delete ();
+
+ // select the new brush
+ b->next = b->prev = &selected_brushes;
+ selected_brushes.next = selected_brushes.prev = b;
+
+ Brush_Build( b );
+ }
+ else
+ {
+ //
+ // change the selected brushes over to the new entity
+ //
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ {
+ Entity_UnlinkBrush (b);
+ Entity_LinkBrush (e, b);
+ Brush_Build( b ); // so the key brush gets a name
+ }
+ }
+
+ Sys_UpdateWindows (W_ALL);
+ return e;
+}
+
+
+/*
+===========
+Entity_LinkBrush
+===========
+*/
+void Entity_LinkBrush (entity_t *e, brush_t *b)
+{
+ if (b->oprev || b->onext)
+ Error ("Entity_LinkBrush: Allready linked");
+ b->owner = e;
+
+ b->onext = e->brushes.onext;
+ b->oprev = &e->brushes;
+ e->brushes.onext->oprev = b;
+ e->brushes.onext = b;
+}
+
+/*
+===========
+Entity_UnlinkBrush
+===========
+*/
+void Entity_UnlinkBrush (brush_t *b)
+{
+ if (!b->owner || !b->onext || !b->oprev)
+ Error ("Entity_UnlinkBrush: Not currently linked");
+ b->onext->oprev = b->oprev;
+ b->oprev->onext = b->onext;
+ b->onext = b->oprev = NULL;
+ b->owner = NULL;
+}
+
+
+
+/*
+===========
+Entity_Clone
+===========
+*/
+entity_t *Entity_Clone (entity_t *e)
+{
+ entity_t *n;
+ epair_t *ep, *np;
+
+ n = qmalloc(sizeof(*n));
+ n->brushes.onext = n->brushes.oprev = &n->brushes;
+ n->eclass = e->eclass;
+
+ // add the entity to the entity list
+ n->next = entities.next;
+ entities.next = n;
+ n->next->prev = n;
+ n->prev = &entities;
+
+ for (ep = e->epairs ; ep ; ep=ep->next)
+ {
+ np = qmalloc(sizeof(*np));
+ np->key = copystring(ep->key);
+ np->value = copystring(ep->value);
+ np->next = n->epairs;
+ n->epairs = np;
+ }
+ return n;
+}
+
+int GetUniqueTargetId(int iHint)
+{
+ int iMin, iMax, i;
+ BOOL fFound;
+ entity_t *pe;
+
+ fFound = FALSE;
+ pe = entities.next;
+ iMin = 0;
+ iMax = 0;
+
+ for (; pe != NULL && pe != &entities ; pe = pe->next)
+ {
+ i = IntForKey(pe, "target");
+ if (i)
+ {
+ iMin = min(i, iMin);
+ iMax = max(i, iMax);
+ if (i == iHint)
+ fFound = TRUE;
+ }
+ }
+
+ if (fFound)
+ return iMax + 1;
+ else
+ return iHint;
+}
+
+entity_t *FindEntity(char *pszKey, char *pszValue)
+{
+ entity_t *pe;
+
+ pe = entities.next;
+
+ for (; pe != NULL && pe != &entities ; pe = pe->next)
+ {
+ if (!strcmp(ValueForKey(pe, pszKey), pszValue))
+ return pe;
+ }
+
+ return NULL;
+}
+
+entity_t *FindEntityInt(char *pszKey, int iValue)
+{
+ entity_t *pe;
+
+ pe = entities.next;
+
+ for (; pe != NULL && pe != &entities ; pe = pe->next)
+ {
+ if (IntForKey(pe, pszKey) == iValue)
+ return pe;
+ }
+
+ return NULL;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// entity.h
+
+
+#define MAX_FLAGS 8
+
+typedef struct eclass_s
+{
+ struct eclass_s *next;
+ char *name;
+ qboolean fixedsize;
+ qboolean unknown; // wasn't found in source
+ vec3_t mins, maxs;
+ vec3_t color;
+ texdef_t texdef;
+ char *comments;
+ char flagnames[MAX_FLAGS][32];
+} eclass_t;
+
+extern eclass_t *eclass;
+
+void Eclass_InitForSourceDirectory (char *path);
+eclass_t *Eclass_ForName (char *name, qboolean has_brushes);
+
+//===================================================
+
+
+typedef struct epair_s
+{
+ struct epair_s *next;
+ char *key;
+ char *value;
+} epair_t;
+
+typedef struct entity_s
+{
+ struct entity_s *prev, *next;
+ brush_t brushes; // head/tail of list
+ vec3_t origin;
+ eclass_t *eclass;
+ epair_t *epairs;
+} entity_t;
+
+char *ValueForKey (entity_t *ent, char *key);
+void SetKeyValue (entity_t *ent, char *key, char *value);
+void DeleteKey (entity_t *ent, char *key);
+float FloatForKey (entity_t *ent, char *key);
+int IntForKey (entity_t *ent, char *key);
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+void Entity_Free (entity_t *e);
+entity_t *Entity_Parse (qboolean onlypairs);
+void Entity_Write (entity_t *e, FILE *f, qboolean use_region);
+entity_t *Entity_Create (eclass_t *c);
+entity_t *Entity_Clone (entity_t *e);
+
+void Entity_LinkBrush (entity_t *e, brush_t *b);
+void Entity_UnlinkBrush (brush_t *b);
+entity_t *FindEntity(char *pszKey, char *pszValue);
+entity_t *FindEntityInt(char *pszKey, int iValue);
+
+int GetUniqueTargetId(int iHint);
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// entity.h
+
+#define DlgXBorder 5
+#define DlgYBorder 5
+
+
+#define EntList 0
+#define EntComment 1
+#define EntCheck1 2
+#define EntCheck2 3
+#define EntCheck3 4
+#define EntCheck4 5
+#define EntCheck5 6
+#define EntCheck6 7
+#define EntCheck7 8
+#define EntCheck8 9
+#define EntCheck9 10
+#define EntCheck10 11
+#define EntCheck11 12
+#define EntCheck12 13
+#define EntProps 14
+#define EntDir0 15
+#define EntDir45 16
+#define EntDir90 17
+#define EntDir135 18
+#define EntDir180 19
+#define EntDir225 20
+#define EntDir270 21
+#define EntDir315 22
+#define EntDirUp 23
+#define EntDirDown 24
+#define EntDelProp 25
+#define EntKeyLabel 26
+#define EntKeyField 27
+#define EntValueLabel 28
+#define EntValueField 29
+#define EntColor 30
+
+#define EntLast 31
+
+extern HWND hwndEnt[EntLast];
+
+extern int rgIds[EntLast];
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// This .h file contains constants, typedefs, etc. for Intergraph
+// extensions to OpenGL. These extensions are:
+//
+// Multiple Palette Extension
+// Texture Object Extension
+
+#define GL_INGR_multiple_palette 1
+#define GL_EXT_texture_object 1
+
+
+// New constants and typedefs for the Multiple Palette Extension
+#define GL_PALETTE_INGR 0x80c0
+#define GL_MAX_PALETTES_INGR 0x80c1
+#define GL_MAX_PALETTE_ENTRIES_INGR 0x80c2
+#define GL_CURRENT_PALETTE_INGR 0x80c3
+#define GL_PALETTE_WRITEMASK_INGR 0x80c4
+#define GL_CURRENT_RASTER_PALETTE_INGR 0x80c5
+#define GL_PALETTE_CLEAR_VALUE_INGR 0x80c6
+
+// Function prototypes for the Multiple Palette Extension routines
+typedef void (APIENTRY *PALETTEFUNCPTR)(GLuint);
+typedef void (APIENTRY *PALETTEMASKFUNCPTR)(GLboolean);
+typedef void (APIENTRY *WGLLOADPALETTEFUNCPTR)(GLuint, GLsizei, GLuint *);
+typedef void (APIENTRY *CLEARPALETTEFUNCPTR)(GLuint);
+
+
+// New Constants and typedefs for the Texture Object Extension
+#define GL_TEXTURE_PRIORITY_EXT 0x8066
+#define GL_TEXTURE_RESIDENT_EXT 0x8067
+#define GL_TEXTURE_1D_BINDING_EXT 0x8068
+#define GL_TEXTURE_2D_BINDING_EXT 0x8069
+
+// Function prototypes for the Texture Object Extension routines
+typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *,
+ const GLboolean *);
+typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint);
+typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *);
+typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *);
+typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint);
+typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *,
+ const GLclampf *);
+
+
+/* OpenGL ExtEscape escape function constants */
+#ifndef OPENGL_GETINFO
+#define OPENGL_GETINFO 4353 /* for OpenGL ExtEscape */
+#endif
+
+// OPENGL_GETINFO ExtEscape sub-escape numbers. They are defined by
+// Microsoft.
+
+#ifndef OPENGL_GETINFO_DRVNAME
+
+#define OPENGL_GETINFO_DRVNAME 0
+
+
+// Input structure for OPENGL_GETINFO ExtEscape.
+
+typedef struct _OPENGLGETINFO
+{
+ ULONG ulSubEsc;
+} OPENGLGETINFO, *POPENGLGETINFO;
+
+
+// Output structure for OPENGL_GETINFO_DRVNAME ExtEscape.
+
+typedef struct _GLDRVNAMERET
+{
+ ULONG ulVersion; // must be 1 for this version
+ ULONG ulDriverVersion; // driver specific version number
+ WCHAR awch[MAX_PATH+1];
+} GLDRVNAMERET, *PGLDRVNAMERET;
+
+#endif
+
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// lbmlib.c
+
+#include "cmdlib.h"
+#include "lbmlib.h"
+
+
+
+/*
+============================================================================
+
+ LBM STUFF
+
+============================================================================
+*/
+
+
+typedef unsigned char UBYTE;
+//conflicts with windows typedef short WORD;
+typedef unsigned short UWORD;
+typedef long LONG;
+
+typedef enum
+{
+ ms_none,
+ ms_mask,
+ ms_transcolor,
+ ms_lasso
+} mask_t;
+
+typedef enum
+{
+ cm_none,
+ cm_rle1
+} compress_t;
+
+typedef struct
+{
+ UWORD w,h;
+ short x,y;
+ UBYTE nPlanes;
+ UBYTE masking;
+ UBYTE compression;
+ UBYTE pad1;
+ UWORD transparentColor;
+ UBYTE xAspect,yAspect;
+ short pageWidth,pageHeight;
+} bmhd_t;
+
+extern bmhd_t bmhd; // will be in native byte order
+
+
+
+#define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24))
+#define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24))
+#define PBMID ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24))
+#define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24))
+#define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24))
+#define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24))
+
+
+bmhd_t bmhd;
+
+int Align (int l)
+{
+ if (l&1)
+ return l+1;
+ return l;
+}
+
+
+
+/*
+================
+LBMRLEdecompress
+
+Source must be evenly aligned!
+================
+*/
+byte *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth)
+{
+ int count;
+ byte b,rept;
+
+ count = 0;
+
+ do
+ {
+ rept = *source++;
+
+ if (rept > 0x80)
+ {
+ rept = (rept^0xff)+2;
+ b = *source++;
+ memset(unpacked,b,rept);
+ unpacked += rept;
+ }
+ else if (rept < 0x80)
+ {
+ rept++;
+ memcpy(unpacked,source,rept);
+ unpacked += rept;
+ source += rept;
+ }
+ else
+ rept = 0; // rept of 0x80 is NOP
+
+ count += rept;
+
+ } while (count<bpwidth);
+
+ if (count>bpwidth)
+ Error ("Decompression exceeded width!\n");
+
+
+ return source;
+}
+
+
+/*
+=================
+LoadLBM
+=================
+*/
+void LoadLBM (char *filename, byte **picture, byte **palette)
+{
+ byte *LBMbuffer, *picbuffer, *cmapbuffer;
+ int y;
+ byte *LBM_P, *LBMEND_P;
+ byte *pic_p;
+ byte *body_p;
+
+ int formtype,formlength;
+ int chunktype,chunklength;
+
+// qiet compiler warnings
+ picbuffer = NULL;
+ cmapbuffer = NULL;
+
+//
+// load the LBM
+//
+ LoadFile (filename, (void **)&LBMbuffer);
+
+//
+// parse the LBM header
+//
+ LBM_P = LBMbuffer;
+ if ( *(int *)LBMbuffer != LittleLong(FORMID) )
+ Error ("No FORM ID at start of file!\n");
+
+ LBM_P += 4;
+ formlength = BigLong( *(int *)LBM_P );
+ LBM_P += 4;
+ LBMEND_P = LBM_P + Align(formlength);
+
+ formtype = LittleLong(*(int *)LBM_P);
+
+ if (formtype != ILBMID && formtype != PBMID)
+ Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff
+ ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff);
+
+ LBM_P += 4;
+
+//
+// parse chunks
+//
+
+ while (LBM_P < LBMEND_P)
+ {
+ chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24);
+ LBM_P += 4;
+ chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24);
+ LBM_P += 4;
+
+ switch ( chunktype )
+ {
+ case BMHDID:
+ memcpy (&bmhd,LBM_P,sizeof(bmhd));
+ bmhd.w = BigShort(bmhd.w);
+ bmhd.h = BigShort(bmhd.h);
+ bmhd.x = BigShort(bmhd.x);
+ bmhd.y = BigShort(bmhd.y);
+ bmhd.pageWidth = BigShort(bmhd.pageWidth);
+ bmhd.pageHeight = BigShort(bmhd.pageHeight);
+ break;
+
+ case CMAPID:
+ cmapbuffer = malloc (768);
+ memset (cmapbuffer, 0, 768);
+ memcpy (cmapbuffer, LBM_P, chunklength);
+ break;
+
+ case BODYID:
+ body_p = LBM_P;
+
+ pic_p = picbuffer = malloc (bmhd.w*bmhd.h);
+ if (formtype == PBMID)
+ {
+ //
+ // unpack PBM
+ //
+ for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w)
+ {
+ if (bmhd.compression == cm_rle1)
+ body_p = LBMRLEDecompress ((byte *)body_p
+ , pic_p , bmhd.w);
+ else if (bmhd.compression == cm_none)
+ {
+ memcpy (pic_p,body_p,bmhd.w);
+ body_p += Align(bmhd.w);
+ }
+ }
+
+ }
+ else
+ {
+ //
+ // unpack ILBM
+ //
+ Error ("%s is an interlaced LBM, not packed", filename);
+ }
+ break;
+ }
+
+ LBM_P += Align(chunklength);
+ }
+
+ free (LBMbuffer);
+
+ *picture = picbuffer;
+
+ if (palette)
+ *palette = cmapbuffer;
+}
+
+
+/*
+============================================================================
+
+ WRITE LBM
+
+============================================================================
+*/
+
+/*
+==============
+WriteLBMfile
+==============
+*/
+void WriteLBMfile (char *filename, byte *data,
+ int width, int height, byte *palette)
+{
+ byte *lbm, *lbmptr;
+ int *formlength, *bmhdlength, *cmaplength, *bodylength;
+ int length;
+ bmhd_t basebmhd;
+
+ lbm = lbmptr = malloc (width*height+1000);
+
+//
+// start FORM
+//
+ *lbmptr++ = 'F';
+ *lbmptr++ = 'O';
+ *lbmptr++ = 'R';
+ *lbmptr++ = 'M';
+
+ formlength = (int*)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ *lbmptr++ = 'P';
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'M';
+ *lbmptr++ = ' ';
+
+//
+// write BMHD
+//
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'M';
+ *lbmptr++ = 'H';
+ *lbmptr++ = 'D';
+
+ bmhdlength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memset (&basebmhd,0,sizeof(basebmhd));
+ basebmhd.w = BigShort((short)width);
+ basebmhd.h = BigShort((short)height);
+ basebmhd.nPlanes = 8;
+ basebmhd.xAspect = 5;
+ basebmhd.yAspect = 6;
+ basebmhd.pageWidth = BigShort((short)width);
+ basebmhd.pageHeight = BigShort((short)height);
+
+ memcpy (lbmptr,&basebmhd,sizeof(basebmhd));
+ lbmptr += sizeof(basebmhd);
+
+ length = lbmptr-(byte *)bmhdlength-4;
+ *bmhdlength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write CMAP
+//
+ *lbmptr++ = 'C';
+ *lbmptr++ = 'M';
+ *lbmptr++ = 'A';
+ *lbmptr++ = 'P';
+
+ cmaplength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memcpy (lbmptr,palette,768);
+ lbmptr += 768;
+
+ length = lbmptr-(byte *)cmaplength-4;
+ *cmaplength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write BODY
+//
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'O';
+ *lbmptr++ = 'D';
+ *lbmptr++ = 'Y';
+
+ bodylength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memcpy (lbmptr,data,width*height);
+ lbmptr += width*height;
+
+ length = lbmptr-(byte *)bodylength-4;
+ *bodylength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// done
+//
+ length = lbmptr-(byte *)formlength-4;
+ *formlength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write output file
+//
+ SaveFile (filename, lbm, lbmptr-lbm);
+ free (lbm);
+}
+
+
+/*
+============================================================================
+
+LOAD PCX
+
+============================================================================
+*/
+
+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;
+
+
+/*
+==============
+LoadPCX
+==============
+*/
+void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height)
+{
+ byte *raw;
+ pcx_t *pcx;
+ int x, y;
+ int len;
+ int dataByte, runLength;
+ byte *out, *pix;
+
+ if (pic)
+ *pic = NULL;
+ if (palette)
+ *palette = NULL;
+ if (width)
+ *width = 0;
+ if (height)
+ *height = 0;
+
+ //
+ // load the file
+ //
+ len = LoadFile (filename, (void **)&raw);
+ if (len == -1)
+ return;
+
+ //
+ // parse the PCX file
+ //
+ pcx = (pcx_t *)raw;
+ raw = &pcx->data;
+
+ 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);
+
+ if (pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8
+ || pcx->xmax >= 640
+ || pcx->ymax >= 480)
+ Error ("Bad pcx file %s", filename);
+
+ if (palette)
+ {
+ *palette = malloc(768);
+ memcpy (*palette, (byte *)pcx + len - 768, 768);
+ }
+
+ if (width)
+ *width = pcx->xmax+1;
+ if (height)
+ *height = pcx->ymax+1;
+
+ if (!pic)
+ return;
+
+ out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
+ if (!out)
+ Error ("Skin_Cache: couldn't allocate");
+
+ *pic = out;
+
+ pix = out;
+
+ for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
+ {
+ for (x=0 ; x<=pcx->xmax ; )
+ {
+ dataByte = *raw++;
+
+ if((dataByte & 0xC0) == 0xC0)
+ {
+ runLength = dataByte & 0x3F;
+ dataByte = *raw++;
+ }
+ else
+ runLength = 1;
+
+ while(runLength-- > 0)
+ pix[x++] = dataByte;
+ }
+
+ }
+
+ if ( raw - (byte *)pcx > len)
+ Error ("PCX file %s was malformed", filename);
+
+ free (pcx);
+}
+
+/*
+==============
+WritePCXfile
+==============
+*/
+void WritePCXfile (char *filename, byte *data,
+ int width, int height, byte *palette)
+{
+ int i, j, length;
+ pcx_t *pcx;
+ byte *pack;
+
+ pcx = malloc (width*height*2+1000);
+ memset (pcx, 0, sizeof(*pcx));
+
+ pcx->manufacturer = 0x0a; // PCX id
+ pcx->version = 5; // 256 color
+ pcx->encoding = 1; // uncompressed
+ pcx->bits_per_pixel = 8; // 256 color
+ pcx->xmin = 0;
+ pcx->ymin = 0;
+ pcx->xmax = LittleShort((short)(width-1));
+ pcx->ymax = LittleShort((short)(height-1));
+ pcx->hres = LittleShort((short)width);
+ pcx->vres = LittleShort((short)height);
+ pcx->color_planes = 1; // chunky image
+ pcx->bytes_per_line = LittleShort((short)width);
+ pcx->palette_type = LittleShort(2); // not a grey scale
+
+ // pack the image
+ pack = &pcx->data;
+
+ for (i=0 ; i<height ; i++)
+ {
+ for (j=0 ; j<width ; j++)
+ {
+ if ( (*data & 0xc0) != 0xc0)
+ *pack++ = *data++;
+ else
+ {
+ *pack++ = 0xc1;
+ *pack++ = *data++;
+ }
+ }
+ }
+
+ // write the palette
+ *pack++ = 0x0c; // palette ID byte
+ for (i=0 ; i<768 ; i++)
+ *pack++ = *palette++;
+
+// write output file
+ length = pack - (byte *)pcx;
+ SaveFile (filename, pcx, length);
+
+ free (pcx);
+}
+
+
+/*
+============================================================================
+
+LOAD IMAGE
+
+============================================================================
+*/
+
+/*
+==============
+Load256Image
+
+Will load either an lbm or pcx, depending on extension.
+Any of the return pointers can be NULL if you don't want them.
+==============
+*/
+void Load256Image (char *name, byte **pixels, byte **palette,
+ int *width, int *height)
+{
+ char ext[128];
+
+ ExtractFileExtension (name, ext);
+ if (!Q_strcasecmp (ext, "lbm"))
+ {
+ LoadLBM (name, pixels, palette);
+ if (width)
+ *width = bmhd.w;
+ if (height)
+ *height = bmhd.h;
+ }
+ else if (!Q_strcasecmp (ext, "pcx"))
+ {
+ LoadPCX (name, pixels, palette, width, height);
+ }
+ else
+ Error ("%s doesn't have a known image extension", name);
+}
+
+
+/*
+==============
+Save256Image
+
+Will save either an lbm or pcx, depending on extension.
+==============
+*/
+void Save256Image (char *name, byte *pixels, byte *palette,
+ int width, int height)
+{
+ char ext[128];
+
+ ExtractFileExtension (name, ext);
+ if (!Q_strcasecmp (ext, "lbm"))
+ {
+ WriteLBMfile (name, pixels, width, height, palette);
+ }
+ else if (!Q_strcasecmp (ext, "pcx"))
+ {
+ WritePCXfile (name, pixels, width, height, palette);
+ }
+ else
+ Error ("%s doesn't have a known image extension", name);
+}
+
+
+
+
+/*
+============================================================================
+
+TARGA IMAGE
+
+============================================================================
+*/
+
+typedef struct _TargaHeader {
+ unsigned char id_length, colormap_type, image_type;
+ unsigned short colormap_index, colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin, y_origin, width, height;
+ unsigned char pixel_size, attributes;
+} TargaHeader;
+
+int fgetLittleShort (FILE *f)
+{
+ byte b1, b2;
+
+ b1 = fgetc(f);
+ b2 = fgetc(f);
+
+ return (short)(b1 + b2*256);
+}
+
+int fgetLittleLong (FILE *f)
+{
+ byte b1, b2, b3, b4;
+
+ b1 = fgetc(f);
+ b2 = fgetc(f);
+ b3 = fgetc(f);
+ b4 = fgetc(f);
+
+ return b1 + (b2<<8) + (b3<<16) + (b4<<24);
+}
+
+
+/*
+=============
+LoadTGA
+=============
+*/
+void LoadTGA (char *name, byte **pixels, int *width, int *height)
+{
+ int columns, rows, numPixels;
+ byte *pixbuf;
+ int row, column;
+ FILE *fin;
+ byte *targa_rgba;
+ TargaHeader targa_header;
+
+ fin = fopen (name, "rb");
+ if (!fin)
+ Error ("Couldn't read %s", name);
+
+ targa_header.id_length = fgetc(fin);
+ targa_header.colormap_type = fgetc(fin);
+ targa_header.image_type = fgetc(fin);
+
+ targa_header.colormap_index = fgetLittleShort(fin);
+ targa_header.colormap_length = fgetLittleShort(fin);
+ targa_header.colormap_size = fgetc(fin);
+ targa_header.x_origin = fgetLittleShort(fin);
+ targa_header.y_origin = fgetLittleShort(fin);
+ targa_header.width = fgetLittleShort(fin);
+ targa_header.height = fgetLittleShort(fin);
+ targa_header.pixel_size = fgetc(fin);
+ targa_header.attributes = fgetc(fin);
+
+ if (targa_header.image_type!=2
+ && targa_header.image_type!=10)
+ Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
+
+ if (targa_header.colormap_type !=0
+ || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
+ Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+
+ columns = targa_header.width;
+ rows = targa_header.height;
+ numPixels = columns * rows;
+
+ if (width)
+ *width = columns;
+ if (height)
+ *height = rows;
+ targa_rgba = malloc(numPixels*4);
+ *pixels = targa_rgba;
+
+ if (targa_header.id_length != 0)
+ fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment
+
+ if (targa_header.image_type==2) { // Uncompressed, RGB images
+ for(row=rows-1; row>=0; row--) {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; column++) {
+ unsigned char red,green,blue,alphabyte;
+ switch (targa_header.pixel_size) {
+ case 24:
+
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ }
+ }
+ }
+ }
+ else if (targa_header.image_type==10) { // Runlength encoded RGB images
+ unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
+ for(row=rows-1; row>=0; row--) {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; ) {
+ packetHeader=getc(fin);
+ packetSize = 1 + (packetHeader & 0x7f);
+ if (packetHeader & 0x80) { // run-length packet
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ break;
+ }
+
+ for(j=0;j<packetSize;j++) {
+ *pixbuf++=red;
+ *pixbuf++=green;
+ *pixbuf++=blue;
+ *pixbuf++=alphabyte;
+ column++;
+ if (column==columns) { // run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ else { // non run-length packet
+ for(j=0;j<packetSize;j++) {
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ }
+ column++;
+ if (column==columns) { // pixel packet run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ }
+ breakOut:;
+ }
+ }
+
+ fclose(fin);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// piclib.h
+
+
+void LoadLBM (char *filename, byte **picture, byte **palette);
+void WriteLBMfile (char *filename, byte *data, int width, int height
+ , byte *palette);
+void LoadPCX (char *filename, byte **picture, byte **palette, int *width, int *height);
+void WritePCXfile (char *filename, byte *data, int width, int height
+ , byte *palette);
+
+// loads / saves either lbm or pcx, depending on extension
+void Load256Image (char *name, byte **pixels, byte **palette,
+ int *width, int *height);
+void Save256Image (char *name, byte *pixels, byte *palette,
+ int width, int height);
+
+
+void LoadTGA (char *filename, byte **pixels, int *width, int *height);
--- /dev/null
+
+TARGETOS=WINNT
+
+!include <ntwin32.mak>
+
+# This line allows NMAKE to work as well
+
+all: gengl.exe
+
+# Update the object file if necessary
+
+gengl.obj: gengl.c gengl.h
+ $(cc) $(cflags) $(cvars) $(cdebug) $(cf) gengl.c
+
+render.obj: render.c gengl.h
+ $(cc) $(cflags) $(cvars) $(cdebug) $(cf) render.c
+
+gengl.res: gengl.rc genglrc.h
+ rc -r gengl.rc
+
+gengl.exe: gengl.obj gengl.res render.obj
+ $(link) $(linkdebug) /NODEFAULTLIB $(guilflags) -out:gengl.exe \
+ gengl.obj render.obj gengl.res $(guilibsdll) opengl32.lib glu32.lib
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// map.c
+
+#include "qe3.h"
+
+qboolean modified; // for quit confirmation (0 = clean, 1 = unsaved,
+ // 2 = autosaved, but not regular saved)
+
+char currentmap[1024];
+
+brush_t active_brushes; // brushes currently being displayed
+brush_t selected_brushes; // highlighted
+face_t *selected_face;
+brush_t *selected_face_brush;
+brush_t filtered_brushes; // brushes that have been filtered or regioned
+
+entity_t entities; // head/tail of doubly linked list
+
+entity_t *world_entity;
+
+void AddRegionBrushes (void);
+void RemoveRegionBrushes (void);
+
+/*
+=============================================================
+
+ Cross map selection saving
+
+ this could fuck up if you have only part of a complex entity selected...
+=============================================================
+*/
+
+brush_t between_brushes;
+entity_t between_entities;
+
+
+void Map_SaveBetween (void)
+{
+ brush_t *b;
+ entity_t *e, *e2;
+
+ between_brushes.next = selected_brushes.next;
+ between_brushes.prev = selected_brushes.prev;
+ between_brushes.next->prev = &between_brushes;
+ between_brushes.prev->next = &between_brushes;
+
+ between_entities.next = between_entities.prev = &between_entities;
+ selected_brushes.next = selected_brushes.prev = &selected_brushes;
+
+ for (b=between_brushes.next ; b != &between_brushes ; b=b->next)
+ {
+ e = b->owner;
+ if (e == world_entity)
+ b->owner = NULL;
+ else
+ {
+ for (e2=between_entities.next ; e2 != &between_entities ; e2=e2->next)
+ if (e2 == e)
+ goto next; // allready got the entity
+ // move the entity over
+ e->prev->next = e->next;
+ e->next->prev = e->prev;
+ e->next = between_entities.next;
+ e->prev = &between_entities;
+ e->next->prev = e;
+ e->prev->next = e;
+ }
+next: ;
+ }
+}
+
+void Map_RestoreBetween (void)
+{
+ entity_t *head, *tail;
+ brush_t *b;
+
+ if (!between_brushes.next)
+ return;
+
+ for (b=between_brushes.next ; b != &between_brushes ; b=b->next)
+ {
+ if (!b->owner)
+ {
+ b->owner = world_entity;
+ b->onext = world_entity->brushes.onext;
+ b->oprev = &world_entity->brushes;
+ b->onext->oprev = b;
+ b->oprev->onext = b;
+ }
+ }
+
+ selected_brushes.next = between_brushes.next;
+ selected_brushes.prev = between_brushes.prev;
+ selected_brushes.next->prev = &selected_brushes;
+ selected_brushes.prev->next = &selected_brushes;
+
+ head = between_entities.next;
+ tail = between_entities.prev;
+
+ if (head != tail)
+ {
+ entities.prev->next = head;
+ head->prev = entities.prev;
+ tail->next = &entities;
+ entities.prev = tail;
+ }
+
+ between_brushes.next = NULL;
+ between_entities.next = NULL;
+}
+
+//============================================================================
+
+void Map_BuildBrushData(void)
+{
+ brush_t *b, *next;
+
+ if (active_brushes.next == NULL)
+ return;
+
+ Sys_BeginWait (); // this could take a while
+
+ for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=next)
+ {
+ next = b->next;
+ Brush_Build( b );
+ if (!b->brush_faces)
+ {
+ Brush_Free (b);
+ Sys_Printf ("Removed degenerate brush\n");
+ }
+ }
+
+ Sys_EndWait();
+}
+
+entity_t *Map_FindClass (char *cname)
+{
+ entity_t *ent;
+
+ for (ent = entities.next ; ent != &entities ; ent=ent->next)
+ {
+ if (!strcmp(cname, ValueForKey (ent, "classname")))
+ return ent;
+ }
+ return NULL;
+}
+
+/*
+================
+Map_Free
+================
+*/
+void Map_Free (void)
+{
+ if (selected_brushes.next &&
+ (selected_brushes.next != &selected_brushes) )
+ {
+ if (MessageBox(g_qeglobals.d_hwndMain, "Copy selection?", "", MB_YESNO) == IDYES)
+ Map_SaveBetween ();
+ }
+
+ Texture_ClearInuse ();
+ Pointfile_Clear ();
+ strcpy (currentmap, "unnamed.map");
+ Sys_SetTitle (currentmap);
+ g_qeglobals.d_num_entities = 0;
+
+ if (!active_brushes.next)
+ { // first map
+ active_brushes.prev = active_brushes.next = &active_brushes;
+ selected_brushes.prev = selected_brushes.next = &selected_brushes;
+ filtered_brushes.prev = filtered_brushes.next = &filtered_brushes;
+
+ entities.prev = entities.next = &entities;
+ }
+ else
+ {
+ while (active_brushes.next != &active_brushes)
+ Brush_Free (active_brushes.next);
+ while (selected_brushes.next != &selected_brushes)
+ Brush_Free (selected_brushes.next);
+ while (filtered_brushes.next != &filtered_brushes)
+ Brush_Free (filtered_brushes.next);
+
+ while (entities.next != &entities)
+ Entity_Free (entities.next);
+ }
+
+ world_entity = NULL;
+}
+
+/*
+================
+Map_LoadFile
+================
+*/
+void Map_LoadFile (char *filename)
+{
+ char *buf;
+ entity_t *ent;
+ char temp[1024];
+
+ Sys_BeginWait ();
+
+ SetInspectorMode(W_CONSOLE);
+
+ QE_ConvertDOSToUnixName( temp, filename );
+ Sys_Printf ("Map_LoadFile: %s\n", temp );
+
+ Map_Free ();
+
+ g_qeglobals.d_parsed_brushes = 0;
+ strcpy (currentmap, filename);
+ LoadFile (filename, (void **)&buf);
+
+ StartTokenParsing (buf);
+
+ g_qeglobals.d_num_entities = 0;
+
+ while (1)
+ {
+ ent = Entity_Parse (false);
+ if (!ent)
+ break;
+ if (!strcmp(ValueForKey (ent, "classname"), "worldspawn"))
+ {
+ if (world_entity)
+ Sys_Printf ("WARNING: multiple worldspawn\n");
+ world_entity = ent;
+ }
+ else
+ {
+ // add the entity to the end of the entity list
+ ent->next = &entities;
+ ent->prev = entities.prev;
+ entities.prev->next = ent;
+ entities.prev = ent;
+ g_qeglobals.d_num_entities++;
+ }
+ }
+
+ free (buf);
+
+ if (!world_entity)
+ {
+ Sys_Printf ("No worldspawn in map.\n");
+ Map_New ();
+ return;
+ }
+
+ Sys_Printf ("--- LoadMapFile ---\n");
+ Sys_Printf ("%s\n", temp );
+
+ Sys_Printf ("%5i brushes\n", g_qeglobals.d_parsed_brushes );
+ Sys_Printf ("%5i entities\n", g_qeglobals.d_num_entities);
+
+ Map_RestoreBetween ();
+
+ Sys_Printf ("Map_BuildAllDisplayLists\n");
+ Map_BuildBrushData();
+
+ //
+ // move the view to a start position
+ //
+ ent = Map_FindClass ("info_player_start");
+ if (!ent)
+ ent = Map_FindClass ("info_player_deathmatch");
+ camera.angles[PITCH] = 0;
+ if (ent)
+ {
+ GetVectorForKey (ent, "origin", camera.origin);
+ GetVectorForKey (ent, "origin", g_qeglobals.d_xy.origin);
+ camera.angles[YAW] = FloatForKey (ent, "angle");
+ }
+ else
+ {
+ camera.angles[YAW] = 0;
+ VectorCopy (vec3_origin, camera.origin);
+ VectorCopy (vec3_origin, g_qeglobals.d_xy.origin);
+ }
+
+ Sys_UpdateWindows (W_ALL);
+
+ Map_RegionOff ();
+
+ modified = false;
+ Sys_SetTitle (temp);
+
+ Texture_ShowInuse ();
+
+ Sys_EndWait();
+
+}
+
+/*
+===========
+Map_SaveFile
+===========
+*/
+void Map_SaveFile (char *filename, qboolean use_region )
+{
+ entity_t *e, *next;
+ FILE *f;
+ char temp[1024];
+ int count;
+
+ QE_ConvertDOSToUnixName( temp, filename );
+
+ if (!use_region)
+ {
+ char backup[1024];
+
+ // rename current to .bak
+ strcpy (backup, filename);
+ StripExtension (backup);
+ strcat (backup, ".bak");
+ _unlink (backup);
+ rename (filename, backup);
+ }
+
+ Sys_Printf ("Map_SaveFile: %s\n", filename);
+
+ f = fopen(filename, "w");
+ if (!f)
+ {
+ Sys_Printf ("ERROR!!!! Couldn't open %s\n", filename);
+ return;
+ }
+
+ if (use_region)
+ AddRegionBrushes ();
+
+ // write world entity first
+ Entity_Write (world_entity, f, use_region);
+
+ // then write all other ents
+ count = 1;
+ for (e=entities.next ; e != &entities ; e=next)
+ {
+ fprintf (f, "// entity %i\n", count);
+ count++;
+ next = e->next;
+ if (e->brushes.onext == &e->brushes)
+ Entity_Free (e); // no brushes left, so remove it
+ else
+ Entity_Write (e, f, use_region);
+ }
+
+ fclose (f);
+
+ if (use_region)
+ RemoveRegionBrushes ();
+
+ Sys_Printf ("Saved.\n");
+ modified = false;
+
+ if ( !strstr( temp, "autosave" ) )
+ Sys_SetTitle (temp);
+
+ if (!use_region)
+ {
+ time_t timer;
+ FILE *f;
+
+ time (&timer);
+ MessageBeep (MB_ICONEXCLAMATION);
+ f = fopen ("c:/tstamps.log", "a");
+ if (f)
+ {
+ fprintf (f, "%4i : %35s : %s", g_qeglobals.d_workcount, filename, ctime(&timer));
+ fclose (f);
+ g_qeglobals.d_workcount = 0;
+ }
+ fclose (f);
+ Sys_Status ("Saved.\n", 0);
+ }
+}
+
+/*
+===========
+Map_New
+===========
+*/
+void Map_New (void)
+{
+ Sys_Printf ("Map_New\n");
+ Map_Free ();
+ world_entity = qmalloc(sizeof(*world_entity));
+ world_entity->brushes.onext =
+ world_entity->brushes.oprev = &world_entity->brushes;
+ SetKeyValue (world_entity, "classname", "worldspawn");
+ world_entity->eclass = Eclass_ForName ("worldspawn", true);
+
+ camera.angles[YAW] = 0;
+ VectorCopy (vec3_origin, camera.origin);
+ camera.origin[2] = 48;
+ VectorCopy (vec3_origin, g_qeglobals.d_xy.origin);
+
+ Map_RestoreBetween ();
+
+ Sys_UpdateWindows (W_ALL);
+ modified = false;
+}
+
+
+/*
+===========================================================
+
+ REGION
+
+===========================================================
+*/
+
+qboolean region_active;
+vec3_t region_mins = {-4096, -4096, -4096};
+vec3_t region_maxs = {4096, 4096, 4096};
+
+brush_t *region_sides[4];
+
+/*
+===========
+AddRegionBrushes
+
+a regioned map will have temp walls put up at the region boundary
+===========
+*/
+void AddRegionBrushes (void)
+{
+ vec3_t mins, maxs;
+ int i;
+ texdef_t td;
+
+ if (!region_active)
+ return;
+
+ memset (&td, 0, sizeof(td));
+ strcpy (td.name, "REGION");
+
+ mins[0] = region_mins[0] - 16;
+ maxs[0] = region_mins[0] + 1;
+ mins[1] = region_mins[1] - 16;
+ maxs[1] = region_maxs[1] + 16;
+ mins[2] = -2048;
+ maxs[2] = 2048;
+ region_sides[0] = Brush_Create (mins, maxs, &td);
+
+ mins[0] = region_maxs[0] - 1;
+ maxs[0] = region_maxs[0] + 16;
+ region_sides[1] = Brush_Create (mins, maxs, &td);
+
+ mins[0] = region_mins[0] - 16;
+ maxs[0] = region_maxs[0] + 16;
+ mins[1] = region_mins[1] - 16;
+ maxs[1] = region_mins[1] + 1;
+ region_sides[2] = Brush_Create (mins, maxs, &td);
+
+ mins[1] = region_maxs[1] - 1;
+ maxs[1] = region_maxs[1] + 16;
+ region_sides[3] = Brush_Create (mins, maxs, &td);
+
+ for (i=0 ; i<4 ; i++)
+ {
+ Brush_AddToList (region_sides[i], &selected_brushes);
+ Entity_LinkBrush (world_entity, region_sides[i]);
+ Brush_Build( region_sides[i] );
+ }
+}
+
+void RemoveRegionBrushes (void)
+{
+ int i;
+
+ if (!region_active)
+ return;
+ for (i=0 ; i<4 ; i++)
+ Brush_Free (region_sides[i]);
+}
+
+
+qboolean Map_IsBrushFiltered (brush_t *b)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (b->mins[i] > region_maxs[i])
+ return true;
+ if (b->maxs[i] < region_mins[i])
+ return true;
+ }
+ return false;
+}
+
+/*
+===========
+Map_RegionOff
+
+Other filtering options may still be on
+===========
+*/
+void Map_RegionOff (void)
+{
+ brush_t *b, *next;
+ int i;
+
+ region_active = false;
+ for (i=0 ; i<3 ; i++)
+ {
+ region_maxs[i] = 4096;
+ region_mins[i] = -4096;
+ }
+
+ for (b=filtered_brushes.next ; b != &filtered_brushes ; b=next)
+ {
+ next = b->next;
+ if (Map_IsBrushFiltered (b))
+ continue; // still filtered
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &active_brushes);
+ }
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+void Map_ApplyRegion (void)
+{
+ brush_t *b, *next;
+
+ region_active = true;
+ for (b=active_brushes.next ; b != &active_brushes ; b=next)
+ {
+ next = b->next;
+ if (!Map_IsBrushFiltered (b))
+ continue; // still filtered
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &filtered_brushes);
+ }
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+========================
+Map_RegionSelectedBrushes
+========================
+*/
+void Map_RegionSelectedBrushes (void)
+{
+ Map_RegionOff ();
+
+ region_active = true;
+ Select_GetBounds (region_mins, region_maxs);
+
+ // move the entire active_brushes list to filtered_brushes
+ filtered_brushes.next = active_brushes.next;
+ filtered_brushes.prev = active_brushes.prev;
+ filtered_brushes.next->prev = &filtered_brushes;
+ filtered_brushes.prev->next = &filtered_brushes;
+
+ // move the entire selected_brushes list to active_brushes
+ active_brushes.next = selected_brushes.next;
+ active_brushes.prev = selected_brushes.prev;
+ active_brushes.next->prev = &active_brushes;
+ active_brushes.prev->next = &active_brushes;
+
+ // clear selected_brushes
+ selected_brushes.next = selected_brushes.prev = &selected_brushes;
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+===========
+Map_RegionXY
+===========
+*/
+void Map_RegionXY (void)
+{
+ Map_RegionOff ();
+
+ region_mins[0] = g_qeglobals.d_xy.origin[0] - 0.5*g_qeglobals.d_xy.width/g_qeglobals.d_xy.scale;
+ region_maxs[0] = g_qeglobals.d_xy.origin[0] + 0.5*g_qeglobals.d_xy.width/g_qeglobals.d_xy.scale;
+ region_mins[1] = g_qeglobals.d_xy.origin[1] - 0.5*g_qeglobals.d_xy.height/g_qeglobals.d_xy.scale;
+ region_maxs[1] = g_qeglobals.d_xy.origin[1] + 0.5*g_qeglobals.d_xy.height/g_qeglobals.d_xy.scale;
+ region_mins[2] = -4096;
+ region_maxs[2] = 4096;
+
+ Map_ApplyRegion ();
+}
+
+/*
+===========
+Map_RegionTallBrush
+===========
+*/
+void Map_RegionTallBrush (void)
+{
+ brush_t *b;
+
+ if (!QE_SingleBrush ())
+ return;
+
+ b = selected_brushes.next;
+
+ Map_RegionOff ();
+
+ VectorCopy (b->mins, region_mins);
+ VectorCopy (b->maxs, region_maxs);
+ region_mins[2] = -4096;
+ region_maxs[2] = 4096;
+
+ Select_Delete ();
+ Map_ApplyRegion ();
+}
+/*
+===========
+Map_RegionBrush
+===========
+*/
+void Map_RegionBrush (void)
+{
+ brush_t *b;
+
+ if (!QE_SingleBrush ())
+ return;
+
+ b = selected_brushes.next;
+
+ Map_RegionOff ();
+
+ VectorCopy (b->mins, region_mins);
+ VectorCopy (b->maxs, region_maxs);
+
+ Select_Delete ();
+ Map_ApplyRegion ();
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// map.h -- the state of the current world that all views are displaying
+
+extern char currentmap[1024];
+
+// head/tail of doubly linked lists
+extern brush_t active_brushes; // brushes currently being displayed
+extern brush_t selected_brushes; // highlighted
+extern face_t *selected_face;
+extern brush_t *selected_face_brush;
+extern brush_t filtered_brushes; // brushes that have been filtered or regioned
+
+extern entity_t entities;
+extern entity_t *world_entity; // the world entity is NOT included in
+ // the entities chain
+
+extern qboolean modified; // for quit confirmations
+
+extern vec3_t region_mins, region_maxs;
+extern qboolean region_active;
+
+void Map_LoadFile (char *filename);
+void Map_SaveFile (char *filename, qboolean use_region);
+void Map_New (void);
+void Map_BuildBrushData(void);
+
+void Map_RegionOff (void);
+void Map_RegionXY (void);
+void Map_RegionTallBrush (void);
+void Map_RegionBrush (void);
+void Map_RegionSelectedBrushes (void);
+qboolean Map_IsBrushFiltered (brush_t *b);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// mathlib.c -- math primitives
+
+#include "cmdlib.h"
+#include "mathlib.h"
+
+vec3_t vec3_origin = {0.0f,0.0f,0.0f};
+
+
+float VectorLength(vec3_t v)
+{
+ int i;
+ float length;
+
+ length = 0.0f;
+ for (i=0 ; i< 3 ; i++)
+ length += v[i]*v[i];
+ length = (float)sqrt (length);
+
+ return length;
+}
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
+ return false;
+
+ return true;
+}
+
+vec_t Q_rint (vec_t in)
+{
+ return (float)floor (in + 0.5);
+}
+
+void VectorMA (vec3_t va, float scale, vec3_t vb, vec3_t vc)
+{
+ vc[0] = va[0] + scale*vb[0];
+ vc[1] = va[1] + scale*vb[1];
+ vc[2] = va[2] + scale*vb[2];
+}
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
+{
+ cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out)
+{
+ out[0] = va[0]-vb[0];
+ out[1] = va[1]-vb[1];
+ out[2] = va[2]-vb[2];
+}
+
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out)
+{
+ out[0] = va[0]+vb[0];
+ out[1] = va[1]+vb[1];
+ out[2] = va[2]+vb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+vec_t VectorNormalize (vec3_t v)
+{
+ int i;
+ float length;
+
+ length = 0.0f;
+ for (i=0 ; i< 3 ; i++)
+ length += v[i]*v[i];
+ length = (float)sqrt (length);
+ if (length == 0)
+ return (vec_t)0;
+
+ for (i=0 ; i< 3 ; i++)
+ v[i] /= length;
+
+ return length;
+}
+
+void VectorInverse (vec3_t v)
+{
+ v[0] = -v[0];
+ v[1] = -v[1];
+ v[2] = -v[2];
+}
+
+void VectorScale (vec3_t v, vec_t scale, vec3_t out)
+{
+ out[0] = v[0] * scale;
+ out[1] = v[1] * scale;
+ out[2] = v[2] * scale;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#ifndef __MATHLIB__
+#define __MATHLIB__
+
+// mathlib.h
+
+#include <math.h>
+
+typedef float vec_t;
+typedef vec_t vec3_t[3];
+
+#define SIDE_FRONT 0
+#define SIDE_ON 2
+#define SIDE_BACK 1
+#define SIDE_CROSS -2
+
+#define Q_PI 3.14159265358979323846
+
+extern vec3_t vec3_origin;
+
+#define EQUAL_EPSILON 0.001
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2);
+
+#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
+#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
+
+vec_t Q_rint (vec_t in);
+vec_t _DotProduct (vec3_t v1, vec3_t v2);
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorCopy (vec3_t in, vec3_t out);
+
+float VectorLength(vec3_t v);
+
+void VectorMA (vec3_t va, float scale, vec3_t vb, vec3_t vc);
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
+vec_t VectorNormalize (vec3_t v);
+void VectorInverse (vec3_t v);
+void VectorScale (vec3_t v, vec_t scale, vec3_t out);
+
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//*************************************************************
+// File name: mru.c
+//
+// Description:
+//
+// Routines for MRU support
+//
+// Development Team:
+//
+// Gilles Vollant (100144.2636@compuserve.com)
+//
+//*************************************************************
+
+#include <windows.h>
+#include <windowsx.h>
+#include <string.h>
+
+#include "mru.h"
+// CreateMruMenu : MRUMENU constructor
+// wNbLruShowInit : nb of item showed in menu
+// wNbLruMenuInit : nb of item stored in memory
+// wMaxSizeLruItemInit : size max. of filename
+
+
+//*************************************************************
+//
+// CreateMruMenu()
+//
+// Purpose:
+//
+// Allocate and Initialize an MRU and return a pointer on it
+//
+//
+// Parameters:
+//
+// WORD wNbLruShowInit - Maximum number of item displayed on menu
+// WORD wNbLruMenuInit - Maximum number of item stored in memory
+// WORD wMaxSizeLruItemInit - Maximum size of an item (ie size of pathname)
+// WORD wIdMruInit - ID of the first item in the menu (default:IDMRU)
+//
+//
+// Return: (LPMRUMENU)
+//
+// Pointer on a MRUMENU structure, used by other function
+//
+//
+// Comments:
+// wNbLruShowInit <= wNbLruMenuInit
+//
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+
+LPMRUMENU CreateMruMenu (WORD wNbLruShowInit,
+ WORD wNbLruMenuInit,WORD wMaxSizeLruItemInit,WORD wIdMruInit)
+{
+LPMRUMENU lpMruMenu;
+ lpMruMenu = (LPMRUMENU)GlobalAllocPtr(GHND,sizeof(MRUMENU));
+
+ lpMruMenu->wNbItemFill = 0;
+ lpMruMenu->wNbLruMenu = wNbLruMenuInit;
+ lpMruMenu->wNbLruShow = wNbLruShowInit;
+ lpMruMenu->wIdMru = wIdMruInit;
+ lpMruMenu->wMaxSizeLruItem = wMaxSizeLruItemInit;
+ lpMruMenu->lpMRU = (LPSTR)GlobalAllocPtr(GHND,
+ lpMruMenu->wNbLruMenu*(UINT)lpMruMenu->wMaxSizeLruItem);
+ if (lpMruMenu->lpMRU == NULL)
+ {
+ GlobalFreePtr(lpMruMenu);
+ lpMruMenu = NULL;
+ }
+ return lpMruMenu;
+}
+
+//*************************************************************
+//
+// CreateMruMenuDefault()
+//
+// Purpose:
+//
+// Allocate and Initialize an MRU and return a pointer on it
+// Use default parameter
+//
+//
+// Parameters:
+//
+//
+// Return: (LPMRUMENU)
+//
+// Pointer on a MRUMENU structure, used by other function
+//
+//
+// Comments:
+//
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+
+LPMRUMENU CreateMruMenuDefault()
+{
+ return CreateMruMenu (NBMRUMENUSHOW,NBMRUMENU,MAXSIZEMRUITEM,IDMRU);
+}
+
+
+//*************************************************************
+//
+// DeleteMruMenu()
+//
+// Purpose:
+// Destructor :
+// Clean and free a MRUMENU structure
+//
+// Parameters:
+//
+// LPMRUMENU lpMruMenu - pointer on MRUMENU, allocated
+// by CreateMruMenu() or CreateMruMenuDefault()
+//
+//
+// Return: void
+//
+//
+// Comments:
+//
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+void DeleteMruMenu(LPMRUMENU lpMruMenu)
+{
+ GlobalFreePtr(lpMruMenu->lpMRU);
+ GlobalFreePtr(lpMruMenu);
+}
+
+//*************************************************************
+//
+// SetNbLruShow()
+//
+// Purpose:
+// Change the maximum number of item displayed on menu
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// WORD wNbLruShowInit - Maximum number of item displayed on menu
+//
+//
+// Return: void
+//
+//
+// Comments:
+//
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+void SetNbLruShow (LPMRUMENU lpMruMenu,WORD wNbLruShowInit)
+{
+ lpMruMenu->wNbLruShow = min(wNbLruShowInit,lpMruMenu->wNbLruMenu);
+}
+
+//*************************************************************
+//
+// SetMenuItem()
+//
+// Purpose:
+// Set the filename of an item
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// WORD wItem - Number of Item to set, zero based
+// LPSTR lpItem - String, contain the filename of the item
+//
+//
+// Return: (BOOL)
+// TRUE - Function run successfully
+// FALSE - Function don't run successfully
+//
+//
+// Comments:
+// used when load .INI or reg database
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+BOOL SetMenuItem (LPMRUMENU lpMruMenu,WORD wItem,LPSTR lpItem)
+{
+ if (wItem >= NBMRUMENU)
+ return FALSE;
+ _fstrncpy((lpMruMenu->lpMRU) +
+ ((lpMruMenu->wMaxSizeLruItem) * (UINT)wItem),
+ lpItem,lpMruMenu->wMaxSizeLruItem-1);
+ lpMruMenu->wNbItemFill = max(lpMruMenu->wNbItemFill,wItem+1);
+ return TRUE;
+}
+
+//*************************************************************
+//
+// GetMenuItem()
+//
+// Purpose:
+// Get the filename of an item
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// WORD wItem - Number of Item to set, zero based
+// BOOL fIDMBased - TRUE : wItem is based on ID menu item
+// FALSE : wItem is zero-based
+// LPSTR lpItem - String where the filename of the item will be
+// stored by GetMenuItem()
+// UINT uiSize - Size of the lpItem buffer
+//
+//
+// Return: (BOOL)
+// TRUE - Function run successfully
+// FALSE - Function don't run successfully
+//
+//
+// Comments:
+// Used for saving in .INI or reg database, or when user select
+// an MRU in File menu
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+BOOL GetMenuItem (LPMRUMENU lpMruMenu,WORD wItem,
+ BOOL fIDMBased,LPSTR lpItem,UINT uiSize)
+{
+ if (fIDMBased)
+ wItem -= (lpMruMenu->wIdMru + 1);
+ if (wItem >= lpMruMenu->wNbItemFill)
+ return FALSE;
+ _fstrncpy(lpItem,(lpMruMenu->lpMRU) +
+ ((lpMruMenu->wMaxSizeLruItem) * (UINT)(wItem)),uiSize);
+ *(lpItem+uiSize-1) = '\0';
+ return TRUE;
+}
+
+//*************************************************************
+//
+// AddNewItem()
+//
+// Purpose:
+// Add an item at the begin of the list
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// LPSTR lpItem - String contain the filename to add
+//
+// Return: (BOOL)
+// TRUE - Function run successfully
+// FALSE - Function don't run successfully
+//
+//
+// Comments:
+// Used when used open a file (using File Open common
+// dialog, Drag and drop or MRU)
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+void AddNewItem (LPMRUMENU lpMruMenu,LPSTR lpItem)
+{
+WORD i,j;
+ for (i=0;i<lpMruMenu->wNbItemFill;i++)
+ if (lstrcmpi(lpItem,(lpMruMenu->lpMRU) +
+ ((lpMruMenu->wMaxSizeLruItem) * (UINT)i)) == 0)
+ {
+ // Shift the other items
+ for (j=i;j>0;j--)
+ lstrcpy((lpMruMenu->lpMRU) + (lpMruMenu->wMaxSizeLruItem * (UINT)j),
+ (lpMruMenu->lpMRU) + (lpMruMenu->wMaxSizeLruItem * (UINT)(j-1)));
+ _fstrncpy(lpMruMenu->lpMRU,lpItem,lpMruMenu->wMaxSizeLruItem-1);
+ return ;
+ }
+ lpMruMenu->wNbItemFill = min(lpMruMenu->wNbItemFill+1,lpMruMenu->wNbLruMenu);
+ for (i=lpMruMenu->wNbItemFill-1;i>0;i--)
+ lstrcpy(lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)i),
+ lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)(i-1)));
+ _fstrncpy(lpMruMenu->lpMRU,lpItem,lpMruMenu->wMaxSizeLruItem-1);
+}
+
+//*************************************************************
+//
+// DelMenuItem()
+//
+// Purpose:
+// Delete an item
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// WORD wItem - Number of Item to set, zero based
+// BOOL fIDMBased - TRUE : wItem is based on ID menu item
+// FALSE : wItem is zero-based
+//
+// Return: (BOOL)
+// TRUE - Function run successfully
+// FALSE - Function don't run successfully
+//
+//
+// Comments:
+// Used when used open a file, using MRU, and when an error
+// occured (by example, when file was deleted)
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+BOOL DelMenuItem(LPMRUMENU lpMruMenu,WORD wItem,BOOL fIDMBased)
+{
+WORD i;
+ if (fIDMBased)
+ wItem -= (lpMruMenu->wIdMru + 1);
+ if (lpMruMenu->wNbItemFill <= wItem)
+ return FALSE;
+ lpMruMenu->wNbItemFill--;
+ for (i=wItem;i<lpMruMenu->wNbItemFill;i++)
+ lstrcpy(lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)i),
+ lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)(i+1)));
+ return TRUE;
+}
+
+//*************************************************************
+//
+// PlaceMenuMRUItem()
+//
+// Purpose:
+// Add MRU at the end of a menu
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// HMENU hMenu - Handle of menu where MRU must be added
+// UINT uiItem - Item of menu entry where MRU must be added
+//
+// Return: void
+//
+//
+// Comments:
+// Used MRU is modified, for refresh the File menu
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+void PlaceMenuMRUItem(LPMRUMENU lpMruMenu,HMENU hMenu,UINT uiItem)
+{
+int i;
+WORD wNbShow;
+ if (hMenu == NULL)
+ return;
+ // remove old MRU in menu
+ for (i=0;i<=(int)(lpMruMenu->wNbLruMenu);i++)
+ RemoveMenu(hMenu,i+lpMruMenu->wIdMru,MF_BYCOMMAND);
+
+ if (lpMruMenu->wNbItemFill == 0)
+ return;
+
+ // If they are item, insert a separator before the files
+ InsertMenu(hMenu,uiItem,MF_SEPARATOR,lpMruMenu->wIdMru,NULL);
+
+ wNbShow = min(lpMruMenu->wNbItemFill,lpMruMenu->wNbLruShow);
+ for (i=(int)wNbShow-1;i>=0;i--)
+ {
+ LPSTR lpTxt;
+ if (lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20))
+ {
+ wsprintf(lpTxt,"&%lu %s",
+ (DWORD)(i+1),lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem*(UINT)i));
+ InsertMenu(hMenu,(((WORD)i)!=(wNbShow-1)) ? (lpMruMenu->wIdMru+i+2) : lpMruMenu->wIdMru,
+ MF_STRING,lpMruMenu->wIdMru+i+1,lpTxt);
+ GlobalFreePtr(lpTxt);
+ }
+ }
+
+}
+
+///////////////////////////////////////////
+
+
+
+//*************************************************************
+//
+// SaveMruInIni()
+//
+// Purpose:
+// Save MRU in a private .INI
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// LPSTR lpszSection - Points to a null-terminated string containing
+// the name of the section
+// LPSTR lpszFile - Points to a null-terminated string that names
+// the initialization file.
+//
+// Return: (BOOL)
+// TRUE - Function run successfully
+// FALSE - Function don't run successfully
+//
+//
+// Comments:
+// See WritePrivateProfileString API for more info on lpszSection and lpszFile
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+BOOL SaveMruInIni(LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile)
+{
+LPSTR lpTxt;
+WORD i;
+
+ lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20);
+ if (lpTxt == NULL)
+ return FALSE;
+
+ for (i=0;i<lpMruMenu->wNbLruMenu;i++)
+ {
+ char szEntry[16];
+ wsprintf(szEntry,"File%lu",(DWORD)i+1);
+ if (!GetMenuItem(lpMruMenu,i,FALSE,lpTxt,lpMruMenu->wMaxSizeLruItem + 10))
+ *lpTxt = '\0';
+ WritePrivateProfileString(lpszSection,szEntry,lpTxt,lpszFile);
+ }
+ GlobalFreePtr(lpTxt);
+ WritePrivateProfileString(NULL,NULL,NULL,lpszFile); // flush cache
+ return TRUE;
+}
+
+
+//*************************************************************
+//
+// LoadMruInIni()
+//
+// Purpose:
+// Load MRU from a private .INI
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// LPSTR lpszSection - Points to a null-terminated string containing
+// the name of the section
+// LPSTR lpszFile - Points to a null-terminated string that names
+// the initialization file.
+//
+// Return: (BOOL)
+// TRUE - Function run successfully
+// FALSE - Function don't run successfully
+//
+//
+// Comments:
+// See GetPrivateProfileString API for more info on lpszSection and lpszFile
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+BOOL LoadMruInIni(LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile)
+{
+LPSTR lpTxt;
+WORD i;
+ lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20);
+ if (lpTxt == NULL)
+ return FALSE;
+
+ for (i=0;i<lpMruMenu->wNbLruMenu;i++)
+ {
+ char szEntry[16];
+
+ wsprintf(szEntry,"File%lu",(DWORD)i+1);
+ GetPrivateProfileString(lpszSection,szEntry,"",lpTxt,
+ lpMruMenu->wMaxSizeLruItem + 10,lpszFile);
+ if (*lpTxt == '\0')
+ break;
+ SetMenuItem(lpMruMenu,i,lpTxt);
+ }
+ GlobalFreePtr(lpTxt);
+ return TRUE;
+}
+
+#ifdef WIN32
+
+BOOL IsWin395OrHigher(void)
+{
+ WORD wVer;
+
+ wVer = LOWORD(GetVersion());
+ wVer = (((WORD)LOBYTE(wVer)) << 8) | (WORD)HIBYTE(wVer);
+
+ return (wVer >= 0x035F); // 5F = 95 dec
+}
+
+
+//*************************************************************
+//
+// SaveMruInReg()
+//
+// Purpose:
+// Save MRU in the registry
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// LPSTR lpszKey - Points to a null-terminated string
+// specifying the name of a key that
+// this function opens or creates.
+//
+// Return: (BOOL)
+// TRUE - Function run successfully
+// FALSE - Function don't run successfully
+//
+//
+// Comments:
+// Win32 function designed for Windows NT and Windows 95
+// See RegCreateKeyEx API for more info on lpszKey
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+BOOL SaveMruInReg(LPMRUMENU lpMruMenu,LPSTR lpszKey)
+{
+LPSTR lpTxt;
+WORD i;
+HKEY hCurKey;
+DWORD dwDisp;
+
+ lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20);
+ if (lpTxt == NULL)
+ return FALSE;
+
+ RegCreateKeyEx(HKEY_CURRENT_USER,lpszKey,0,NULL,
+ REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hCurKey,&dwDisp);
+
+ for (i=0;i<lpMruMenu->wNbLruMenu;i++)
+ {
+ char szEntry[16];
+ wsprintf(szEntry,"File%lu",(DWORD)i+1);
+ if (!GetMenuItem(lpMruMenu,i,FALSE,lpTxt,lpMruMenu->wMaxSizeLruItem + 10))
+ *lpTxt = '\0';
+ RegSetValueEx(hCurKey,szEntry,0,REG_SZ,lpTxt,lstrlen(lpTxt));
+ }
+ RegCloseKey(hCurKey);
+ GlobalFreePtr(lpTxt);
+ return TRUE;
+}
+
+//*************************************************************
+//
+// LoadMruInReg()
+//
+// Purpose:
+// Load MRU from the registry
+//
+// Parameters:
+// LPMRUMENU lpMruMenu - pointer on MRUMENU
+// LPSTR lpszKey - Points to a null-terminated string
+// specifying the name of a key that
+// this function opens or creates.
+//
+// Return: (BOOL)
+// TRUE - Function run successfully
+// FALSE - Function don't run successfully
+//
+//
+// Comments:
+// Win32 function designed for Windows NT and Windows 95
+// See RegOpenKeyEx API for more info on lpszKey
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+BOOL LoadMruInReg(LPMRUMENU lpMruMenu,LPSTR lpszKey)
+{
+LPSTR lpTxt;
+WORD i;
+HKEY hCurKey;
+DWORD dwType;
+ lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20);
+ if (lpTxt == NULL)
+ return FALSE;
+
+ RegOpenKeyEx(HKEY_CURRENT_USER,lpszKey,0,KEY_READ,&hCurKey);
+
+
+ for (i=0;i<lpMruMenu->wNbLruMenu;i++)
+ {
+ char szEntry[16];
+ DWORD dwSizeBuf;
+ wsprintf(szEntry,"File%lu",(DWORD)i+1);
+ *lpTxt = '\0';
+ dwSizeBuf = lpMruMenu->wMaxSizeLruItem + 10;
+ RegQueryValueEx(hCurKey,szEntry,NULL,&dwType,(LPBYTE)lpTxt,&dwSizeBuf);
+ *(lpTxt+dwSizeBuf)='\0';
+ if (*lpTxt == '\0')
+ break;
+ SetMenuItem(lpMruMenu,i,lpTxt);
+ }
+ RegCloseKey(hCurKey);
+ GlobalFreePtr(lpTxt);
+ return TRUE;
+}
+
+
+//*************************************************************
+//
+// GetWin32Kind()
+//
+// Purpose:
+// Get the Win32 platform
+//
+// Parameters:
+//
+// Return: (WIN32KIND)
+// WINNT - Run under Windows NT
+// WIN32S - Run under Windows 3.1x + Win32s
+// WIN95ORGREATHER - Run under Windows 95
+//
+//
+// Comments:
+// Win32 function designed for Windows NT and Windows 95
+// See RegOpenKeyEx API for more info on lpszKey
+//
+// History: Date Author Comment
+// 09/24/94 G. Vollant Created
+//
+//*************************************************************
+WIN32KIND GetWin32Kind()
+{
+BOOL IsWin395OrHigher(void);
+
+ WORD wVer;
+
+ if ((GetVersion() & 0x80000000) == 0)
+ return WINNT;
+ wVer = LOWORD(GetVersion());
+ wVer = (((WORD)LOBYTE(wVer)) << 8) | (WORD)HIBYTE(wVer);
+
+ if (wVer >= 0x035F)
+ return WIN95ORGREATHER;
+ else
+ return WIN32S;
+}
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//*************************************************************
+// File name: mru.h
+//
+// Description:
+//
+// Header for MRU support
+//
+// Development Team:
+//
+// Gilles Vollant (100144.2636@compuserve.com)
+//
+//*************************************************************
+
+#ifndef __MRU_H__
+#define __MRU_H__
+
+#define NBMRUMENUSHOW 6 // Default number of MRU showed in the menu File
+#define NBMRUMENU 9 // Default number of MRU stored
+#define IDMRU 8000 // Default First ID of MRU
+#ifdef OFS_MAXPATHNAME
+#define MAXSIZEMRUITEM OFS_MAXPATHNAME
+#else
+#define MAXSIZEMRUITEM 128 // Default max size of an entry
+#endif
+
+typedef struct
+{
+WORD wNbItemFill;
+WORD wNbLruShow;
+WORD wNbLruMenu;
+WORD wMaxSizeLruItem;
+WORD wIdMru;
+LPSTR lpMRU;
+} MRUMENU;
+
+typedef MRUMENU FAR * LPMRUMENU;
+
+#ifdef __cplusplus
+LPMRUMENU CreateMruMenu (WORD wNbLruShowInit=NBMRUMENUSHOW,
+ WORD wNbLruMenuInit=NBMRUMENU,
+ WORD wMaxSizeLruItemInit=MAXSIZEMRUITEM,
+ WORD wIdMruInit=IDMRU);
+#else
+LPMRUMENU CreateMruMenu (WORD wNbLruShowInit,
+ WORD wNbLruMenuInit,
+ WORD wMaxSizeLruItemInit,
+ WORD wIdMruInit);
+#endif
+
+LPMRUMENU CreateMruMenuDefault();
+void DeleteMruMenu (LPMRUMENU lpMruMenu);
+
+void SetNbLruShow (LPMRUMENU lpMruMenu,WORD wNbLruShowInit);
+BOOL SetMenuItem (LPMRUMENU lpMruMenu,WORD wItem,
+ LPSTR lpItem);
+BOOL GetMenuItem (LPMRUMENU lpMruMenu,WORD wItem,
+ BOOL fIDMBased,LPSTR lpItem,UINT uiSize);
+BOOL DelMenuItem (LPMRUMENU lpMruMenu,WORD wItem,BOOL fIDMBased);
+void AddNewItem (LPMRUMENU lpMruMenu,LPSTR lpItem);
+void PlaceMenuMRUItem(LPMRUMENU lpMruMenu,HMENU hMenu,UINT uiItem);
+
+BOOL SaveMruInIni (LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile);
+BOOL LoadMruInIni (LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile);
+#ifdef WIN32
+BOOL SaveMruInReg (LPMRUMENU lpMruMenu,LPSTR lpszKey);
+BOOL LoadMruInReg (LPMRUMENU lpMruMenu,LPSTR lpszKey);
+
+typedef enum
+{
+WIN32S,
+WINNT,
+WIN95ORGREATHER
+} WIN32KIND;
+WIN32KIND GetWin32Kind();
+#endif
+
+
+//////////////////////////////////////////////////////////////
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+char token[MAXTOKEN];
+qboolean unget;
+char *script_p;
+int scriptline;
+
+void StartTokenParsing (char *data)
+{
+ scriptline = 1;
+ script_p = data;
+ unget = false;
+}
+
+qboolean GetToken (qboolean crossline)
+{
+ char *token_p;
+
+ if (unget) // is a token allready waiting?
+ return true;
+
+//
+// skip space
+//
+skipspace:
+ while (*script_p <= 32)
+ {
+ if (!*script_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ return false;
+ }
+ if (*script_p++ == '\n')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ scriptline++;
+ }
+ }
+
+ if (script_p[0] == '/' && script_p[1] == '/') // comment field
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ while (*script_p++ != '\n')
+ if (!*script_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ return false;
+ }
+ goto skipspace;
+ }
+
+//
+// copy token
+//
+ token_p = token;
+
+ if (*script_p == '"')
+ {
+ script_p++;
+ while ( *script_p != '"' )
+ {
+ if (!*script_p)
+ Error ("EOF inside quoted token");
+ *token_p++ = *script_p++;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i",scriptline);
+ }
+ script_p++;
+ }
+ else while ( *script_p > 32 )
+ {
+ *token_p++ = *script_p++;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i",scriptline);
+ }
+
+ *token_p = 0;
+
+ return true;
+}
+
+void UngetToken (void)
+{
+ unget = true;
+}
+
+
+/*
+==============
+TokenAvailable
+
+Returns true if there is another token on the line
+==============
+*/
+qboolean TokenAvailable (void)
+{
+ char *search_p;
+
+ search_p = script_p;
+
+ while ( *search_p <= 32)
+ {
+ if (*search_p == '\n')
+ return false;
+ if (*search_p == 0)
+ return false;
+ search_p++;
+ }
+
+ if (*search_p == ';')
+ return false;
+
+ return true;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// parse.h -- text file parsing routines
+
+#define MAXTOKEN 1024
+
+extern char token[MAXTOKEN];
+extern int scriptline;
+
+void StartTokenParsing (char *data);
+qboolean GetToken (qboolean crossline);
+void UngetToken (void);
+qboolean TokenAvailable (void);
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+
+#define MAX_POINTFILE 8192
+static vec3_t s_pointvecs[MAX_POINTFILE];
+static int s_num_points, s_check_point;
+
+void Pointfile_Delete (void)
+{
+ char name[1024];
+
+ strcpy (name, currentmap);
+ StripExtension (name);
+ strcat (name, ".lin");
+
+ remove(name);
+}
+
+// advance camera to next point
+void Pointfile_Next (void)
+{
+ vec3_t dir;
+
+ if (s_check_point >= s_num_points-2)
+ {
+ Sys_Status ("End of pointfile", 0);
+ return;
+ }
+ s_check_point++;
+ VectorCopy (s_pointvecs[s_check_point], camera.origin);
+ VectorCopy (s_pointvecs[s_check_point], g_qeglobals.d_xy.origin);
+ VectorSubtract (s_pointvecs[s_check_point+1], camera.origin, dir);
+ VectorNormalize (dir);
+ camera.angles[1] = atan2 (dir[1], dir[0])*180/3.14159;
+ camera.angles[0] = asin (dir[2])*180/3.14159;
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+// advance camera to previous point
+void Pointfile_Prev (void)
+{
+ vec3_t dir;
+
+ if ( s_check_point == 0)
+ {
+ Sys_Status ("Start of pointfile", 0);
+ return;
+ }
+ s_check_point--;
+ VectorCopy (s_pointvecs[s_check_point], camera.origin);
+ VectorCopy (s_pointvecs[s_check_point], g_qeglobals.d_xy.origin);
+ VectorSubtract (s_pointvecs[s_check_point+1], camera.origin, dir);
+ VectorNormalize (dir);
+ camera.angles[1] = atan2 (dir[1], dir[0])*180/3.14159;
+ camera.angles[0] = asin (dir[2])*180/3.14159;
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+void Pointfile_Check (void)
+{
+ char name[1024];
+ FILE *f;
+ vec3_t v;
+
+ strcpy (name, currentmap);
+ StripExtension (name);
+ strcat (name, ".lin");
+
+ f = fopen (name, "r");
+ if (!f)
+ return;
+
+ Sys_Printf ("Reading pointfile %s\n", name);
+
+ if (!g_qeglobals.d_pointfile_display_list)
+ g_qeglobals.d_pointfile_display_list = glGenLists(1);
+
+ s_num_points = 0;
+ glNewList (g_qeglobals.d_pointfile_display_list, GL_COMPILE);
+ glColor3f (1, 0, 0);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glLineWidth (4);
+ glBegin(GL_LINE_STRIP);
+ do
+ {
+ if (fscanf (f, "%f %f %f\n", &v[0], &v[1], &v[2]) != 3)
+ break;
+ if (s_num_points < MAX_POINTFILE)
+ {
+ VectorCopy (v, s_pointvecs[s_num_points]);
+ s_num_points++;
+ }
+ glVertex3fv (v);
+ } while (1);
+ glEnd();
+ glLineWidth (1);
+ glEndList ();
+
+ s_check_point = 0;
+ fclose (f);
+ Pointfile_Next ();
+}
+
+void Pointfile_Draw( void )
+{
+ int i;
+
+ glColor3f( 1.0F, 0.0F, 0.0F );
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glLineWidth (4);
+ glBegin(GL_LINE_STRIP);
+ for ( i = 0; i < s_num_points; i++ )
+ {
+ glVertex3fv( s_pointvecs[i] );
+ }
+ glEnd();
+ glLineWidth( 1 );
+}
+
+void Pointfile_Clear (void)
+{
+ if (!g_qeglobals.d_pointfile_display_list)
+ return;
+
+ glDeleteLists (g_qeglobals.d_pointfile_display_list, 1);
+ g_qeglobals.d_pointfile_display_list = 0;
+ Sys_UpdateWindows (W_ALL);
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+QEGlobals_t g_qeglobals;
+
+void QE_CheckOpenGLForErrors(void)
+{
+ int i;
+
+ while ( ( i = glGetError() ) != GL_NO_ERROR )
+ {
+ char buffer[100];
+
+ sprintf( buffer, "OpenGL Error: %s", gluErrorString( i ) );
+
+ MessageBox( g_qeglobals.d_hwndMain, buffer , "QuakeEd Error", MB_OK | MB_ICONEXCLAMATION );
+ exit( 1 );
+ }
+}
+
+
+char *ExpandReletivePath (char *p)
+{
+ static char temp[1024];
+ char *base;
+
+ if (!p || !p[0])
+ return NULL;
+ if (p[0] == '/' || p[0] == '\\')
+ return p;
+
+ base = ValueForKey(g_qeglobals.d_project_entity, "basepath");
+ sprintf (temp, "%s/%s", base, p);
+ return temp;
+}
+
+
+
+void *qmalloc (int size)
+{
+ void *b;
+ b = malloc(size);
+ memset (b, 0, size);
+ return b;
+}
+
+char *copystring (char *s)
+{
+ char *b;
+ b = malloc(strlen(s)+1);
+ strcpy (b,s);
+ return b;
+}
+
+/*
+===============
+QE_CheckAutoSave
+
+If five minutes have passed since making a change
+and the map hasn't been saved, save it out.
+===============
+*/
+void QE_CheckAutoSave( void )
+{
+ static clock_t s_start;
+ clock_t now;
+
+ now = clock();
+
+ if ( modified != 1 || !s_start)
+ {
+ s_start = now;
+ return;
+ }
+
+ if ( now - s_start > ( CLOCKS_PER_SEC * 60 * QE_AUTOSAVE_INTERVAL ) )
+ {
+ Sys_Printf ("Autosaving...\n");
+ Sys_Status ("Autosaving...", 0 );
+
+ Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false);
+
+ Sys_Status ("Autosaving...Saved.", 0 );
+ modified = 2;
+ s_start = now;
+ }
+}
+
+
+
+/*
+===========
+QE_LoadProject
+===========
+*/
+qboolean QE_LoadProject (char *projectfile)
+{
+ char *data;
+
+ Sys_Printf ("QE_LoadProject (%s)\n", projectfile);
+
+ if ( LoadFileNoCrash (projectfile, (void *)&data) == -1)
+ return false;
+ StartTokenParsing (data);
+ g_qeglobals.d_project_entity = Entity_Parse (true);
+ if (!g_qeglobals.d_project_entity)
+ Error ("Couldn't parse %s", projectfile);
+ free (data);
+
+ Eclass_InitForSourceDirectory (ValueForKey (g_qeglobals.d_project_entity, "entitypath"));
+
+ FillClassList (); // list in entity window
+
+ Map_New ();
+
+ FillTextureMenu ();
+ FillBSPMenu ();
+
+ return true;
+}
+
+/*
+===========
+QE_KeyDown
+===========
+*/
+#define SPEED_MOVE 32
+#define SPEED_TURN 22.5
+
+qboolean QE_KeyDown (int key)
+{
+ switch (key)
+ {
+ case 'K':
+ PostMessage( g_qeglobals.d_hwndMain, WM_COMMAND, ID_MISC_SELECTENTITYCOLOR, 0 );
+ break;
+
+ case VK_UP:
+ VectorMA (camera.origin, SPEED_MOVE, camera.forward, camera.origin);
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+ case VK_DOWN:
+ VectorMA (camera.origin, -SPEED_MOVE, camera.forward, camera.origin);
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+ case VK_LEFT:
+ camera.angles[1] += SPEED_TURN;
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+ case VK_RIGHT:
+ camera.angles[1] -= SPEED_TURN;
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+ case 'D':
+ camera.origin[2] += SPEED_MOVE;
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z_OVERLAY);
+ break;
+ case 'C':
+ camera.origin[2] -= SPEED_MOVE;
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z_OVERLAY);
+ break;
+ case 'A':
+ camera.angles[0] += SPEED_TURN;
+ if (camera.angles[0] > 85)
+ camera.angles[0] = 85;
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+ case 'Z':
+ camera.angles[0] -= SPEED_TURN;
+ if (camera.angles[0] < -85)
+ camera.angles[0] = -85;
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+ case VK_COMMA:
+ VectorMA (camera.origin, -SPEED_MOVE, camera.right, camera.origin);
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+ case VK_PERIOD:
+ VectorMA (camera.origin, SPEED_MOVE, camera.right, camera.origin);
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+
+ case '0':
+ g_qeglobals.d_showgrid = !g_qeglobals.d_showgrid;
+ PostMessage( g_qeglobals.d_hwndXY, WM_PAINT, 0, 0 );
+ break;
+ case '1':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_1, 0);
+ break;
+ case '2':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_2, 0);
+ break;
+ case '3':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_4, 0);
+ break;
+ case '4':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_8, 0);
+ break;
+ case '5':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_16, 0);
+ break;
+ case '6':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_32, 0);
+ break;
+ case '7':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_64, 0);
+ break;
+
+ case 'E':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_DRAGEDGES, 0);
+ break;
+ case 'V':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_DRAGVERTECIES, 0);
+ break;
+
+ case 'N':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_ENTITY, 0);
+ break;
+ case 'O':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_CONSOLE, 0);
+ break;
+ case 'T':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_TEXTURE, 0);
+ break;
+ case 'S':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_TEXTURES_INSPECTOR, 0);
+ break;
+
+ case ' ':
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_CLONE, 0);
+ break;
+
+ case VK_BACK:
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_DELETE, 0);
+ break;
+ case VK_ESCAPE:
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_DESELECT, 0);
+ break;
+ case VK_END:
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_CENTER, 0);
+ break;
+
+ case VK_DELETE:
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_ZOOMIN, 0);
+ break;
+ case VK_INSERT:
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_ZOOMOUT, 0);
+ break;
+
+ case VK_NEXT:
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_DOWNFLOOR, 0);
+ break;
+ case VK_PRIOR:
+ PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_UPFLOOR, 0);
+ break;
+
+ default:
+ return false;
+
+ }
+
+ return true;
+}
+
+/*
+===============
+ConnectEntities
+
+Sets target / targetname on the two entities selected
+from the first selected to the secon
+===============
+*/
+void ConnectEntities (void)
+{
+ entity_t *e1, *e2, *e;
+ char *target, *tn;
+ int maxtarg, targetnum;
+ char newtarg[32];
+
+ if (g_qeglobals.d_select_count != 2)
+ {
+ Sys_Status ("Must have two brushes selected.", 0);
+ Sys_Beep ();
+ return;
+ }
+
+ e1 = g_qeglobals.d_select_order[0]->owner;
+ e2 = g_qeglobals.d_select_order[1]->owner;
+
+ if (e1 == world_entity || e2 == world_entity)
+ {
+ Sys_Status ("Can't connect to the world.", 0);
+ Sys_Beep ();
+ return;
+ }
+
+ if (e1 == e2)
+ {
+ Sys_Status ("Brushes are from same entity.", 0);
+ Sys_Beep ();
+ return;
+ }
+
+ target = ValueForKey (e1, "target");
+ if (target && target[0])
+ strcpy (newtarg, target);
+ else
+ {
+ target = ValueForKey (e2, "targetname");
+ if (target && target[0])
+ strcpy (newtarg, target);
+ else
+ {
+ // make a unique target value
+ maxtarg = 0;
+ for (e=entities.next ; e != &entities ; e=e->next)
+ {
+ tn = ValueForKey (e, "targetname");
+ if (tn && tn[0])
+ {
+ targetnum = atoi(tn+1);
+ if (targetnum > maxtarg)
+ maxtarg = targetnum;
+ }
+ }
+ sprintf (newtarg, "t%i", maxtarg+1);
+ }
+ }
+
+ SetKeyValue (e1, "target", newtarg);
+ SetKeyValue (e2, "targetname", newtarg);
+ Sys_UpdateWindows (W_XY | W_CAMERA);
+
+ Select_Deselect();
+ Select_Brush (g_qeglobals.d_select_order[1]);
+}
+
+qboolean QE_SingleBrush (void)
+{
+ if ( (selected_brushes.next == &selected_brushes)
+ || (selected_brushes.next->next != &selected_brushes) )
+ {
+ Sys_Printf ("Error: you must have a single brush selected\n");
+ return false;
+ }
+ if (selected_brushes.next->owner->eclass->fixedsize)
+ {
+ Sys_Printf ("Error: you cannot manipulate fixed size entities\n");
+ return false;
+ }
+
+ return true;
+}
+
+void QE_Init (void)
+{
+ /*
+ ** initialize variables
+ */
+ g_qeglobals.d_gridsize = 8;
+ g_qeglobals.d_showgrid = true;
+
+ /*
+ ** other stuff
+ */
+ Texture_Init ();
+ Cam_Init ();
+ XY_Init ();
+ Z_Init ();
+}
+
+void QE_ConvertDOSToUnixName( char *dst, const char *src )
+{
+ while ( *src )
+ {
+ if ( *src == '\\' )
+ *dst = '/';
+ else
+ *dst = *src;
+ dst++; src++;
+ }
+ *dst = 0;
+}
+
+int g_numbrushes, g_numentities;
+
+void QE_CountBrushesAndUpdateStatusBar( void )
+{
+ static int s_lastbrushcount, s_lastentitycount;
+ static qboolean s_didonce;
+
+ entity_t *e;
+ brush_t *b, *next;
+
+ g_numbrushes = 0;
+ g_numentities = 0;
+
+ if ( active_brushes.next != NULL )
+ {
+ for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next)
+ {
+ next = b->next;
+ if (b->brush_faces )
+ {
+ if ( !b->owner->eclass->fixedsize)
+ g_numbrushes++;
+ else
+ g_numentities++;
+ }
+ }
+ }
+
+ if ( entities.next != NULL )
+ {
+ for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next)
+ {
+ g_numentities++;
+ }
+ }
+
+ if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) )
+ {
+ Sys_UpdateStatusBar();
+
+ s_lastbrushcount = g_numbrushes;
+ s_lastentitycount = g_numentities;
+ s_didonce = true;
+ }
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#ifndef __QE3_H__
+#define __QE3_H__
+
+// disable data conversion warnings for gl
+#pragma warning(disable : 4244) // MIPS
+#pragma warning(disable : 4136) // X86
+#pragma warning(disable : 4051) // ALPHA
+
+#include <windows.h>
+
+#include <gl/gl.h>
+#include <gl/glu.h>
+#include <gl/glaux.h>
+#include "glingr.h"
+#include <math.h>
+#include <stdlib.h>
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "parse.h"
+#include "lbmlib.h"
+
+#include <commctrl.h>
+#include "afxres.h"
+#include "resource.h"
+
+#include "qedefs.h"
+
+typedef struct
+{
+ vec3_t normal;
+ double dist;
+ int type;
+} plane_t;
+
+#include "qfiles.h"
+
+#include "textures.h"
+#include "brush.h"
+#include "entity.h"
+#include "map.h"
+#include "select.h"
+
+#include "camera.h"
+#include "xy.h"
+#include "z.h"
+#include "mru.h"
+
+typedef struct
+{
+ int p1, p2;
+ face_t *f1, *f2;
+} pedge_t;
+
+typedef struct
+{
+ int iSize;
+ int iTexMenu; // nearest, linear, etc
+ float fGamma; // gamma for textures
+ char szProject[256]; // last project loaded
+ vec3_t colors[COLOR_LAST];
+ qboolean show_names,
+ show_coordinates;
+ int exclude;
+} SavedInfo_t;
+
+//
+// system functions
+//
+void Sys_UpdateStatusBar( void );
+void Sys_UpdateWindows (int bits);
+void Sys_Beep (void);
+void Sys_ClearPrintf (void);
+void Sys_Printf (char *text, ...);
+double Sys_DoubleTime (void);
+void Sys_GetCursorPos (int *x, int *y);
+void Sys_SetCursorPos (int x, int y);
+void Sys_SetTitle (char *text);
+void Sys_BeginWait (void);
+void Sys_EndWait (void);
+void Sys_Status(const char *psz, int part);
+
+/*
+** most of the QE globals are stored in this structure
+*/
+typedef struct
+{
+ qboolean d_showgrid;
+ int d_gridsize;
+
+ int d_num_entities;
+
+ entity_t *d_project_entity;
+
+ float d_new_brush_bottom_z,
+ d_new_brush_top_z;
+
+ HINSTANCE d_hInstance;
+
+ HGLRC d_hglrcBase;
+ HDC d_hdcBase;
+
+ HWND d_hwndMain;
+ HWND d_hwndCamera;
+ HWND d_hwndEdit;
+ HWND d_hwndEntity;
+ HWND d_hwndTexture;
+ HWND d_hwndXY;
+ HWND d_hwndZ;
+ HWND d_hwndStatus;
+
+ vec3_t d_points[MAX_POINTS];
+ int d_numpoints;
+ pedge_t d_edges[MAX_EDGES];
+ int d_numedges;
+
+ int d_num_move_points;
+ float *d_move_points[1024];
+
+ qtexture_t *d_qtextures;
+
+ texturewin_t d_texturewin;
+
+ int d_pointfile_display_list;
+
+ xy_t d_xy;
+
+ LPMRUMENU d_lpMruMenu;
+
+ SavedInfo_t d_savedinfo;
+
+ int d_workcount;
+
+ // connect entities uses the last two brushes selected
+ int d_select_count;
+ brush_t *d_select_order[2];
+ vec3_t d_select_translate; // for dragging w/o making new display lists
+ select_t d_select_mode;
+
+ int d_font_list;
+
+ int d_parsed_brushes;
+
+ qboolean show_blocks;
+} QEGlobals_t;
+
+void *qmalloc (int size);
+char *copystring (char *s);
+char *ExpandReletivePath (char *p);
+
+void Pointfile_Delete (void);
+void Pointfile_Check (void);
+void Pointfile_Next (void);
+void Pointfile_Prev (void);
+void Pointfile_Clear (void);
+void Pointfile_Draw( void );
+void Pointfile_Load( void );
+
+//
+// drag.c
+//
+void Drag_Begin (int x, int y, int buttons,
+ vec3_t xaxis, vec3_t yaxis,
+ vec3_t origin, vec3_t dir);
+void Drag_MouseMoved (int x, int y, int buttons);
+void Drag_MouseUp (void);
+
+//
+// csg.c
+//
+void CSG_MakeHollow (void);
+void CSG_Subtract (void);
+
+//
+// vertsel.c
+//
+
+void SetupVertexSelection (void);
+void SelectEdgeByRay (vec3_t org, vec3_t dir);
+void SelectVertexByRay (vec3_t org, vec3_t dir);
+
+void ConnectEntities (void);
+
+extern int update_bits;
+
+extern int screen_width;
+extern int screen_height;
+
+extern HANDLE bsp_process;
+
+char *TranslateString (char *buf);
+
+void ProjectDialog (void);
+
+void FillTextureMenu (void);
+void FillBSPMenu (void);
+
+BOOL CALLBACK Win_Dialog (
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+);
+
+
+//
+// win_cam.c
+//
+void WCam_Create (HINSTANCE hInstance);
+
+
+//
+// win_xy.c
+//
+void WXY_Create (HINSTANCE hInstance);
+
+//
+// win_z.c
+//
+void WZ_Create (HINSTANCE hInstance);
+
+//
+// win_ent.c
+//
+
+
+//
+// win_main.c
+//
+void Main_Create (HINSTANCE hInstance);
+extern BOOL SaveWindowState(HWND hWnd, const char *pszName);
+extern BOOL LoadWindowState(HWND hWnd, const char *pszName);
+
+extern BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize);
+extern BOOL loadRegistryInfo(const char *pszName, void *pvBuf, long *plSize);
+
+//
+// entityw.c
+//
+BOOL CreateEntityWindow(HINSTANCE hInstance);
+void FillClassList (void);
+BOOL UpdateEntitySel(eclass_t *pec);
+void SetInspectorMode(int iType);
+int DrawTexControls(HWND hWnd);
+void SetSpawnFlags(void);
+void GetSpawnFlags(void);
+void SetKeyValuePairs(void);
+extern void BuildGammaTable(float g);
+
+
+// win_dlg.c
+
+void DoGamma(void);
+void DoFind(void);
+void DoRotate(void);
+void DoSides(void);
+void DoAbout(void);
+void DoSurface(void);
+
+/*
+** QE function declarations
+*/
+void QE_CheckAutoSave( void );
+void QE_ConvertDOSToUnixName( char *dst, const char *src );
+void QE_CountBrushesAndUpdateStatusBar( void );
+void QE_CheckOpenGLForErrors(void);
+void QE_ExpandBspString (char *bspaction, char *out, char *mapname);
+void QE_Init (void);
+qboolean QE_KeyDown (int key);
+qboolean QE_LoadProject (char *projectfile);
+qboolean QE_SingleBrush (void);
+
+/*
+** QE Win32 function declarations
+*/
+int QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer );
+void QEW_StopGL( HWND hWnd, HGLRC hGLRC, HDC hDC );
+
+/*
+** extern declarations
+*/
+extern QEGlobals_t g_qeglobals;
+
+#endif
--- /dev/null
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+# TARGTYPE "Win32 (ALPHA) Application" 0x0601
+
+!IF "$(CFG)" == ""
+CFG=qe3 - Win32 (ALPHA) Debug
+!MESSAGE No configuration specified. Defaulting to qe3 - Win32 (ALPHA) Debug.
+!ENDIF
+
+!IF "$(CFG)" != "qe3 - Win32 Release" && "$(CFG)" != "qe3 - Win32 Debug" &&\
+ "$(CFG)" != "qe3 - Win32 (ALPHA) Debug" && "$(CFG)" !=\
+ "qe3 - Win32 (ALPHA) Release"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "qe4.mak" CFG="qe3 - Win32 (ALPHA) Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "qe3 - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "qe3 - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "qe3 - Win32 (ALPHA) Debug" (based on "Win32 (ALPHA) Application")
+!MESSAGE "qe3 - Win32 (ALPHA) Release" (based on "Win32 (ALPHA) Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "qe3 - Win32 Debug"
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\qe4.exe" "$(OUTDIR)\qe4.bsc"
+
+CLEAN :
+ -@erase "$(INTDIR)\brush.obj"
+ -@erase "$(INTDIR)\brush.sbr"
+ -@erase "$(INTDIR)\camera.obj"
+ -@erase "$(INTDIR)\camera.sbr"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\cmdlib.sbr"
+ -@erase "$(INTDIR)\csg.obj"
+ -@erase "$(INTDIR)\csg.sbr"
+ -@erase "$(INTDIR)\drag.obj"
+ -@erase "$(INTDIR)\drag.sbr"
+ -@erase "$(INTDIR)\eclass.obj"
+ -@erase "$(INTDIR)\eclass.sbr"
+ -@erase "$(INTDIR)\entity.obj"
+ -@erase "$(INTDIR)\entity.sbr"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\lbmlib.sbr"
+ -@erase "$(INTDIR)\map.obj"
+ -@erase "$(INTDIR)\map.sbr"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\mathlib.sbr"
+ -@erase "$(INTDIR)\mru.obj"
+ -@erase "$(INTDIR)\mru.sbr"
+ -@erase "$(INTDIR)\parse.obj"
+ -@erase "$(INTDIR)\parse.sbr"
+ -@erase "$(INTDIR)\points.obj"
+ -@erase "$(INTDIR)\points.sbr"
+ -@erase "$(INTDIR)\qe3.obj"
+ -@erase "$(INTDIR)\qe3.sbr"
+ -@erase "$(INTDIR)\select.obj"
+ -@erase "$(INTDIR)\select.sbr"
+ -@erase "$(INTDIR)\textures.obj"
+ -@erase "$(INTDIR)\textures.sbr"
+ -@erase "$(INTDIR)\vertsel.obj"
+ -@erase "$(INTDIR)\vertsel.sbr"
+ -@erase "$(INTDIR)\win_cam.obj"
+ -@erase "$(INTDIR)\win_cam.sbr"
+ -@erase "$(INTDIR)\win_dlg.obj"
+ -@erase "$(INTDIR)\win_dlg.sbr"
+ -@erase "$(INTDIR)\win_ent.obj"
+ -@erase "$(INTDIR)\win_ent.sbr"
+ -@erase "$(INTDIR)\win_main.obj"
+ -@erase "$(INTDIR)\win_main.sbr"
+ -@erase "$(INTDIR)\win_qe3.obj"
+ -@erase "$(INTDIR)\win_qe3.res"
+ -@erase "$(INTDIR)\win_qe3.sbr"
+ -@erase "$(INTDIR)\win_xy.obj"
+ -@erase "$(INTDIR)\win_xy.sbr"
+ -@erase "$(INTDIR)\win_z.obj"
+ -@erase "$(INTDIR)\win_z.sbr"
+ -@erase "$(INTDIR)\xy.obj"
+ -@erase "$(INTDIR)\xy.sbr"
+ -@erase "$(INTDIR)\z.obj"
+ -@erase "$(INTDIR)\z.sbr"
+ -@erase "$(OUTDIR)\qe4.bsc"
+ -@erase "$(OUTDIR)\qe4.exe"
+ -@erase "$(OUTDIR)\qe4.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /W3 /GX /Zd /O1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fr /YX /c
+CPP_PROJ=/nologo /ML /W3 /GX /Zd /O1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\
+ /Fr"$(INTDIR)/" /Fp"$(INTDIR)/qe4.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\Release/
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qe4.bsc"
+BSC32_SBRS= \
+ "$(INTDIR)\brush.sbr" \
+ "$(INTDIR)\camera.sbr" \
+ "$(INTDIR)\cmdlib.sbr" \
+ "$(INTDIR)\csg.sbr" \
+ "$(INTDIR)\drag.sbr" \
+ "$(INTDIR)\eclass.sbr" \
+ "$(INTDIR)\entity.sbr" \
+ "$(INTDIR)\lbmlib.sbr" \
+ "$(INTDIR)\map.sbr" \
+ "$(INTDIR)\mathlib.sbr" \
+ "$(INTDIR)\mru.sbr" \
+ "$(INTDIR)\parse.sbr" \
+ "$(INTDIR)\points.sbr" \
+ "$(INTDIR)\qe3.sbr" \
+ "$(INTDIR)\select.sbr" \
+ "$(INTDIR)\textures.sbr" \
+ "$(INTDIR)\vertsel.sbr" \
+ "$(INTDIR)\win_cam.sbr" \
+ "$(INTDIR)\win_dlg.sbr" \
+ "$(INTDIR)\win_ent.sbr" \
+ "$(INTDIR)\win_main.sbr" \
+ "$(INTDIR)\win_qe3.sbr" \
+ "$(INTDIR)\win_xy.sbr" \
+ "$(INTDIR)\win_z.sbr" \
+ "$(INTDIR)\xy.sbr" \
+ "$(INTDIR)\z.sbr"
+
+"$(OUTDIR)\qe4.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+LINK32_FLAGS=comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib\
+ gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib\
+ oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows\
+ /incremental:no /pdb:"$(OUTDIR)/qe4.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/qe4.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\brush.obj" \
+ "$(INTDIR)\camera.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\csg.obj" \
+ "$(INTDIR)\drag.obj" \
+ "$(INTDIR)\eclass.obj" \
+ "$(INTDIR)\entity.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\map.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\mru.obj" \
+ "$(INTDIR)\parse.obj" \
+ "$(INTDIR)\points.obj" \
+ "$(INTDIR)\qe3.obj" \
+ "$(INTDIR)\select.obj" \
+ "$(INTDIR)\textures.obj" \
+ "$(INTDIR)\vertsel.obj" \
+ "$(INTDIR)\win_cam.obj" \
+ "$(INTDIR)\win_dlg.obj" \
+ "$(INTDIR)\win_ent.obj" \
+ "$(INTDIR)\win_main.obj" \
+ "$(INTDIR)\win_qe3.obj" \
+ "$(INTDIR)\win_qe3.res" \
+ "$(INTDIR)\win_xy.obj" \
+ "$(INTDIR)\win_z.obj" \
+ "$(INTDIR)\xy.obj" \
+ "$(INTDIR)\z.obj"
+
+"$(OUTDIR)\qe4.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\qe4.exe" "$(OUTDIR)\qe4.bsc"
+
+CLEAN :
+ -@erase "$(INTDIR)\brush.obj"
+ -@erase "$(INTDIR)\brush.sbr"
+ -@erase "$(INTDIR)\camera.obj"
+ -@erase "$(INTDIR)\camera.sbr"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\cmdlib.sbr"
+ -@erase "$(INTDIR)\csg.obj"
+ -@erase "$(INTDIR)\csg.sbr"
+ -@erase "$(INTDIR)\drag.obj"
+ -@erase "$(INTDIR)\drag.sbr"
+ -@erase "$(INTDIR)\eclass.obj"
+ -@erase "$(INTDIR)\eclass.sbr"
+ -@erase "$(INTDIR)\entity.obj"
+ -@erase "$(INTDIR)\entity.sbr"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\lbmlib.sbr"
+ -@erase "$(INTDIR)\map.obj"
+ -@erase "$(INTDIR)\map.sbr"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\mathlib.sbr"
+ -@erase "$(INTDIR)\mru.obj"
+ -@erase "$(INTDIR)\mru.sbr"
+ -@erase "$(INTDIR)\parse.obj"
+ -@erase "$(INTDIR)\parse.sbr"
+ -@erase "$(INTDIR)\points.obj"
+ -@erase "$(INTDIR)\points.sbr"
+ -@erase "$(INTDIR)\qe3.obj"
+ -@erase "$(INTDIR)\qe3.sbr"
+ -@erase "$(INTDIR)\select.obj"
+ -@erase "$(INTDIR)\select.sbr"
+ -@erase "$(INTDIR)\textures.obj"
+ -@erase "$(INTDIR)\textures.sbr"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(INTDIR)\vertsel.obj"
+ -@erase "$(INTDIR)\vertsel.sbr"
+ -@erase "$(INTDIR)\win_cam.obj"
+ -@erase "$(INTDIR)\win_cam.sbr"
+ -@erase "$(INTDIR)\win_dlg.obj"
+ -@erase "$(INTDIR)\win_dlg.sbr"
+ -@erase "$(INTDIR)\win_ent.obj"
+ -@erase "$(INTDIR)\win_ent.sbr"
+ -@erase "$(INTDIR)\win_main.obj"
+ -@erase "$(INTDIR)\win_main.sbr"
+ -@erase "$(INTDIR)\win_qe3.obj"
+ -@erase "$(INTDIR)\win_qe3.res"
+ -@erase "$(INTDIR)\win_qe3.sbr"
+ -@erase "$(INTDIR)\win_xy.obj"
+ -@erase "$(INTDIR)\win_xy.sbr"
+ -@erase "$(INTDIR)\win_z.obj"
+ -@erase "$(INTDIR)\win_z.sbr"
+ -@erase "$(INTDIR)\xy.obj"
+ -@erase "$(INTDIR)\xy.sbr"
+ -@erase "$(INTDIR)\z.obj"
+ -@erase "$(INTDIR)\z.sbr"
+ -@erase "$(OUTDIR)\qe4.bsc"
+ -@erase "$(OUTDIR)\qe4.exe"
+ -@erase "$(OUTDIR)\qe4.map"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /c
+CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS"\
+ /FR"$(INTDIR)/" /Fp"$(INTDIR)/qe4.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\Debug/
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qe4.bsc"
+BSC32_SBRS= \
+ "$(INTDIR)\brush.sbr" \
+ "$(INTDIR)\camera.sbr" \
+ "$(INTDIR)\cmdlib.sbr" \
+ "$(INTDIR)\csg.sbr" \
+ "$(INTDIR)\drag.sbr" \
+ "$(INTDIR)\eclass.sbr" \
+ "$(INTDIR)\entity.sbr" \
+ "$(INTDIR)\lbmlib.sbr" \
+ "$(INTDIR)\map.sbr" \
+ "$(INTDIR)\mathlib.sbr" \
+ "$(INTDIR)\mru.sbr" \
+ "$(INTDIR)\parse.sbr" \
+ "$(INTDIR)\points.sbr" \
+ "$(INTDIR)\qe3.sbr" \
+ "$(INTDIR)\select.sbr" \
+ "$(INTDIR)\textures.sbr" \
+ "$(INTDIR)\vertsel.sbr" \
+ "$(INTDIR)\win_cam.sbr" \
+ "$(INTDIR)\win_dlg.sbr" \
+ "$(INTDIR)\win_ent.sbr" \
+ "$(INTDIR)\win_main.sbr" \
+ "$(INTDIR)\win_qe3.sbr" \
+ "$(INTDIR)\win_xy.sbr" \
+ "$(INTDIR)\win_z.sbr" \
+ "$(INTDIR)\xy.sbr" \
+ "$(INTDIR)\z.sbr"
+
+"$(OUTDIR)\qe4.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+# ADD LINK32 comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /profile /map /debug /machine:I386
+LINK32_FLAGS=comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib\
+ gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib\
+ oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows\
+ /profile /map:"$(INTDIR)/qe4.map" /debug /machine:I386 /out:"$(OUTDIR)/qe4.exe"\
+
+LINK32_OBJS= \
+ "$(INTDIR)\brush.obj" \
+ "$(INTDIR)\camera.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\csg.obj" \
+ "$(INTDIR)\drag.obj" \
+ "$(INTDIR)\eclass.obj" \
+ "$(INTDIR)\entity.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\map.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\mru.obj" \
+ "$(INTDIR)\parse.obj" \
+ "$(INTDIR)\points.obj" \
+ "$(INTDIR)\qe3.obj" \
+ "$(INTDIR)\select.obj" \
+ "$(INTDIR)\textures.obj" \
+ "$(INTDIR)\vertsel.obj" \
+ "$(INTDIR)\win_cam.obj" \
+ "$(INTDIR)\win_dlg.obj" \
+ "$(INTDIR)\win_ent.obj" \
+ "$(INTDIR)\win_main.obj" \
+ "$(INTDIR)\win_qe3.obj" \
+ "$(INTDIR)\win_qe3.res" \
+ "$(INTDIR)\win_xy.obj" \
+ "$(INTDIR)\win_z.obj" \
+ "$(INTDIR)\xy.obj" \
+ "$(INTDIR)\z.obj"
+
+"$(OUTDIR)\qe4.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "qe3___Wi"
+# PROP BASE Intermediate_Dir "qe3___Wi"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "debug_alpha"
+# PROP Intermediate_Dir "debug_alpha"
+# PROP Target_Dir ""
+OUTDIR=.\debug_alpha
+INTDIR=.\debug_alpha
+
+ALL : "$(OUTDIR)\qe3.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /Gt0 /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /Gt0 /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MLd /Gt0 /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS"\
+ /Fp"$(INTDIR)/qe3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\debug_alpha/
+CPP_SBRS=.\.
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /alpha
+# ADD MTL /nologo /D "_DEBUG" /alpha
+MTL_PROJ=/nologo /D "_DEBUG" /alpha
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qe3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:ALPHA
+# SUBTRACT BASE LINK32 /incremental:no
+# ADD LINK32 comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:ALPHA
+# SUBTRACT LINK32 /incremental:no
+LINK32_FLAGS=comctl32.lib opengl32.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:windows /incremental:yes /pdb:"$(OUTDIR)/qe3.pdb" /debug\
+ /machine:ALPHA /out:"$(OUTDIR)/qe3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\brush.obj" \
+ "$(INTDIR)\camera.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\csg.obj" \
+ "$(INTDIR)\drag.obj" \
+ "$(INTDIR)\eclass.obj" \
+ "$(INTDIR)\entity.obj" \
+ "$(INTDIR)\map.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\mru.obj" \
+ "$(INTDIR)\parse.obj" \
+ "$(INTDIR)\points.obj" \
+ "$(INTDIR)\qe3.obj" \
+ "$(INTDIR)\select.obj" \
+ "$(INTDIR)\textures.obj" \
+ "$(INTDIR)\vertsel.obj" \
+ "$(INTDIR)\win_cam.obj" \
+ "$(INTDIR)\win_dlg.obj" \
+ "$(INTDIR)\win_ent.obj" \
+ "$(INTDIR)\win_main.obj" \
+ "$(INTDIR)\win_qe3.obj" \
+ "$(INTDIR)\win_qe3.res" \
+ "$(INTDIR)\win_xy.obj" \
+ "$(INTDIR)\win_z.obj" \
+ "$(INTDIR)\xy.obj" \
+ "$(INTDIR)\z.obj"
+
+"$(OUTDIR)\qe3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "qe3___W0"
+# PROP BASE Intermediate_Dir "qe3___W0"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "release_alpha"
+# PROP Intermediate_Dir "release_alpha"
+# PROP Target_Dir ""
+OUTDIR=.\release_alpha
+INTDIR=.\release_alpha
+
+ALL : "$(OUTDIR)\qe3.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /Gt0 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /Gt0 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /ML /Gt0 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\
+ /Fp"$(INTDIR)/qe3.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\release_alpha/
+CPP_SBRS=.\.
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /alpha
+# ADD MTL /nologo /D "NDEBUG" /alpha
+MTL_PROJ=/nologo /D "NDEBUG" /alpha
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qe3.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:ALPHA
+# ADD LINK32 comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:ALPHA
+LINK32_FLAGS=comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib\
+ gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib\
+ oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows\
+ /incremental:no /pdb:"$(OUTDIR)/qe3.pdb" /machine:ALPHA\
+ /out:"$(OUTDIR)/qe3.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\brush.obj" \
+ "$(INTDIR)\camera.obj" \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\csg.obj" \
+ "$(INTDIR)\drag.obj" \
+ "$(INTDIR)\eclass.obj" \
+ "$(INTDIR)\entity.obj" \
+ "$(INTDIR)\map.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\mru.obj" \
+ "$(INTDIR)\parse.obj" \
+ "$(INTDIR)\points.obj" \
+ "$(INTDIR)\qe3.obj" \
+ "$(INTDIR)\select.obj" \
+ "$(INTDIR)\textures.obj" \
+ "$(INTDIR)\vertsel.obj" \
+ "$(INTDIR)\win_cam.obj" \
+ "$(INTDIR)\win_dlg.obj" \
+ "$(INTDIR)\win_ent.obj" \
+ "$(INTDIR)\win_main.obj" \
+ "$(INTDIR)\win_qe3.obj" \
+ "$(INTDIR)\win_qe3.res" \
+ "$(INTDIR)\win_xy.obj" \
+ "$(INTDIR)\win_z.obj" \
+ "$(INTDIR)\xy.obj" \
+ "$(INTDIR)\z.obj"
+
+"$(OUTDIR)\qe3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+################################################################################
+# Begin Target
+
+# Name "qe3 - Win32 Release"
+# Name "qe3 - Win32 Debug"
+# Name "qe3 - Win32 (ALPHA) Debug"
+# Name "qe3 - Win32 (ALPHA) Release"
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\textures.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\textures.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_TEXTU=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+"$(INTDIR)\textures.sbr" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_TEXTU=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+"$(INTDIR)\textures.sbr" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_TEXTU=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_TEXTU=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\resource.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mathlib.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mathlib.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_MATHL=\
+ ".\cmdlib.h"\
+ ".\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+"$(INTDIR)\mathlib.sbr" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_MATHL=\
+ ".\cmdlib.h"\
+ ".\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+"$(INTDIR)\mathlib.sbr" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_MATHL=\
+ ".\cmdlib.h"\
+ ".\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_MATHL=\
+ ".\cmdlib.h"\
+ ".\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\map.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\map.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_MAP_C=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+"$(INTDIR)\map.sbr" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_MAP_C=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+"$(INTDIR)\map.sbr" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_MAP_C=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_MAP_C=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\cmdlib.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\cmdlib.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_CMDLI=\
+ ".\cmdlib.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+"$(INTDIR)\cmdlib.sbr" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_CMDLI=\
+ ".\cmdlib.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+"$(INTDIR)\cmdlib.sbr" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_CMDLI=\
+ ".\cmdlib.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_CMDLI=\
+ ".\cmdlib.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\brush.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\brush.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_BRUSH=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+"$(INTDIR)\brush.sbr" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_BRUSH=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+"$(INTDIR)\brush.sbr" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_BRUSH=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_BRUSH=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\notes.txt
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_cam.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_C=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+"$(INTDIR)\win_cam.sbr" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_C=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+"$(INTDIR)\win_cam.sbr" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_C=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_C=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qe3.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\parse.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\parse.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_PARSE=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\parse.obj" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+"$(INTDIR)\parse.sbr" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_PARSE=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\parse.obj" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+"$(INTDIR)\parse.sbr" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_PARSE=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\parse.obj" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_PARSE=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\parse.obj" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\camera.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\camera.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_CAMER=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+"$(INTDIR)\camera.sbr" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_CAMER=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+"$(INTDIR)\camera.sbr" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_CAMER=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_CAMER=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_qe3.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_xy.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_X=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_xy.obj" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+"$(INTDIR)\win_xy.sbr" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_X=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_xy.obj" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+"$(INTDIR)\win_xy.sbr" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_X=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_xy.obj" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_X=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_xy.obj" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_qe3.rc
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_RSC_WIN_Q=\
+ ".\icon1.ico"\
+ ".\q.bmp"\
+ ".\toolbar1.bmp"\
+
+
+"$(INTDIR)\win_qe3.res" : $(SOURCE) $(DEP_RSC_WIN_Q) "$(INTDIR)"
+ $(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_RSC_WIN_Q=\
+ ".\icon1.ico"\
+ ".\q.bmp"\
+ ".\toolbar1.bmp"\
+
+
+"$(INTDIR)\win_qe3.res" : $(SOURCE) $(DEP_RSC_WIN_Q) "$(INTDIR)"
+ $(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_RSC_WIN_Q=\
+ ".\icon1.ico"\
+ ".\toolbar1.bmp"\
+
+
+"$(INTDIR)\win_qe3.res" : $(SOURCE) $(DEP_RSC_WIN_Q) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "_DEBUG" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_RSC_WIN_Q=\
+ ".\icon1.ico"\
+ ".\toolbar1.bmp"\
+
+
+"$(INTDIR)\win_qe3.res" : $(SOURCE) $(DEP_RSC_WIN_Q) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "NDEBUG" $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\xy.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_XY_C14=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\xy.obj" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+"$(INTDIR)\xy.sbr" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_XY_C14=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\xy.obj" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+"$(INTDIR)\xy.sbr" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_XY_C14=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\xy.obj" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_XY_C14=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\xy.obj" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\xy.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\select.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_SELEC=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\select.obj" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+"$(INTDIR)\select.sbr" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_SELEC=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\select.obj" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+"$(INTDIR)\select.sbr" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_SELEC=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\select.obj" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_SELEC=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\select.obj" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_qe3.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_QE=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_qe3.obj" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+"$(INTDIR)\win_qe3.sbr" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_QE=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_qe3.obj" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+"$(INTDIR)\win_qe3.sbr" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_QE=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_qe3.obj" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_QE=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_qe3.obj" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\select.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qe3.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_QE3_C=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\qe3.obj" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+"$(INTDIR)\qe3.sbr" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_QE3_C=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\qe3.obj" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+"$(INTDIR)\qe3.sbr" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_QE3_C=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\qe3.obj" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_QE3_C=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\qe3.obj" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\eclass.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_ECLAS=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\eclass.obj" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+"$(INTDIR)\eclass.sbr" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_ECLAS=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\eclass.obj" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+"$(INTDIR)\eclass.sbr" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_ECLAS=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\eclass.obj" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_ECLAS=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\eclass.obj" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\eclass.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\entity.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_ENTIT=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\entity.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+"$(INTDIR)\entity.sbr" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_ENTIT=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\entity.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+"$(INTDIR)\entity.sbr" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_ENTIT=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\entity.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_ENTIT=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\entity.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\entity.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_dlg.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_D=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_dlg.obj" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+"$(INTDIR)\win_dlg.sbr" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_D=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_dlg.obj" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+"$(INTDIR)\win_dlg.sbr" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_D=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_dlg.obj" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_D=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_dlg.obj" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\points.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_POINT=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\points.obj" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+"$(INTDIR)\points.sbr" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_POINT=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\points.obj" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+"$(INTDIR)\points.sbr" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_POINT=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\points.obj" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_POINT=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\points.obj" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_z.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_Z=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_z.obj" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+"$(INTDIR)\win_z.sbr" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_Z=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_z.obj" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+"$(INTDIR)\win_z.sbr" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_Z=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_z.obj" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_Z=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_z.obj" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\z.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_Z_C26=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\z.obj" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+"$(INTDIR)\z.sbr" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_Z_C26=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\z.obj" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+"$(INTDIR)\z.sbr" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_Z_C26=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\z.obj" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_Z_C26=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\z.obj" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\z.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\drag.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_DRAG_=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\drag.obj" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+"$(INTDIR)\drag.sbr" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_DRAG_=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\drag.obj" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+"$(INTDIR)\drag.sbr" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_DRAG_=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\drag.obj" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_DRAG_=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\drag.obj" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_main.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_M=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\entityw.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+"$(INTDIR)\win_main.sbr" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_M=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\entityw.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+"$(INTDIR)\win_main.sbr" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_M=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_M=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\csg.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_CSG_C=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+"$(INTDIR)\csg.sbr" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_CSG_C=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+"$(INTDIR)\csg.sbr" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_CSG_C=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_CSG_C=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\vertsel.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_VERTS=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\vertsel.obj" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+"$(INTDIR)\vertsel.sbr" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_VERTS=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\vertsel.obj" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+"$(INTDIR)\vertsel.sbr" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_VERTS=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\vertsel.obj" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_VERTS=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\vertsel.obj" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mru.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_MRU_C=\
+ ".\mru.h"\
+
+
+"$(INTDIR)\mru.obj" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+"$(INTDIR)\mru.sbr" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_MRU_C=\
+ ".\mru.h"\
+
+
+"$(INTDIR)\mru.obj" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+"$(INTDIR)\mru.sbr" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_MRU_C=\
+ ".\mru.h"\
+
+
+"$(INTDIR)\mru.obj" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_MRU_C=\
+ ".\mru.h"\
+
+
+"$(INTDIR)\mru.obj" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_ent.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_E=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\entityw.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_ent.obj" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+"$(INTDIR)\win_ent.sbr" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_E=\
+ ".\brush.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\entityw.h"\
+ ".\glingr.h"\
+ ".\lbmlib.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\mru.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\qedefs.h"\
+ ".\qfiles.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_ent.obj" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+"$(INTDIR)\win_ent.sbr" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_E=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\entityw.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_ent.obj" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_E=\
+ ".\brush.h"\
+ ".\bspfile.h"\
+ ".\camera.h"\
+ ".\cmdlib.h"\
+ ".\entity.h"\
+ ".\entityw.h"\
+ ".\gl\GL.H"\
+ ".\gl\GLAUX.H"\
+ ".\gl\GLU.H"\
+ ".\glingr.h"\
+ ".\map.h"\
+ ".\mathlib.h"\
+ ".\parse.h"\
+ ".\qe3.h"\
+ ".\select.h"\
+ ".\textures.h"\
+ ".\xy.h"\
+ ".\z.h"\
+
+
+"$(INTDIR)\win_ent.obj" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\lbmlib.c
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_LBMLI=\
+ ".\cmdlib.h"\
+ ".\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+
+"$(INTDIR)\lbmlib.sbr" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_LBMLI=\
+ ".\cmdlib.h"\
+ ".\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+
+"$(INTDIR)\lbmlib.sbr" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qfiles.h
+
+!IF "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
+################################################################################
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#ifndef __QEDEFS_H__
+#define __QEDEFS_H__
+
+#define QE_VERSION 0x0401
+
+#define QE3_STYLE (WS_OVERLAPPED| WS_CAPTION | WS_THICKFRAME | \
+ /* WS_MINIMIZEBOX | */ WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | \
+ WS_CLIPCHILDREN | WS_CHILD)
+
+#define QE_AUTOSAVE_INTERVAL 5 // number of minutes between autosaves
+
+#define CAMERA_WINDOW_CLASS "QCamera"
+#define XY_WINDOW_CLASS "QXY"
+#define Z_WINDOW_CLASS "QZ"
+#define ENT_WINDOW_CLASS "QENT"
+
+#define ZWIN_WIDTH 40
+#define CWIN_SIZE (0.4)
+
+#define MAX_EDGES 256
+#define MAX_POINTS 512
+
+#define CMD_TEXTUREWAD 60000
+#define CMD_BSPCOMMAND 61000
+
+#define PITCH 0
+#define YAW 1
+#define ROLL 2
+
+#define QE_TIMER0 1
+
+#define PLANE_X 0
+#define PLANE_Y 1
+#define PLANE_Z 2
+#define PLANE_ANYX 3
+#define PLANE_ANYY 4
+#define PLANE_ANYZ 5
+
+#define ON_EPSILON 0.01
+
+#define KEY_FORWARD 1
+#define KEY_BACK 2
+#define KEY_TURNLEFT 4
+#define KEY_TURNRIGHT 8
+#define KEY_LEFT 16
+#define KEY_RIGHT 32
+#define KEY_LOOKUP 64
+#define KEY_LOOKDOWN 128
+#define KEY_UP 256
+#define KEY_DOWN 512
+
+// xy.c
+#define EXCLUDE_LIGHTS 1
+#define EXCLUDE_ENT 2
+#define EXCLUDE_PATHS 4
+#define EXCLUDE_WATER 8
+#define EXCLUDE_WORLD 16
+#define EXCLUDE_CLIP 32
+#define EXCLUDE_DETAIL 64
+
+
+//
+// menu indexes for modifying menus
+//
+#define MENU_VIEW 2
+#define MENU_BSP 4
+#define MENU_TEXTURE 6
+
+
+// odd things not in windows header...
+#define VK_COMMA 188
+#define VK_PERIOD 190
+
+/*
+** window bits
+*/
+#define W_CAMERA 0x0001
+#define W_XY 0x0002
+#define W_XY_OVERLAY 0x0004
+#define W_Z 0x0008
+#define W_TEXTURE 0x0010
+#define W_Z_OVERLAY 0x0020
+#define W_CONSOLE 0x0040
+#define W_ENTITY 0x0080
+#define W_ALL 0xFFFFFFFF
+
+#define COLOR_TEXTUREBACK 0
+#define COLOR_GRIDBACK 1
+#define COLOR_GRIDMINOR 2
+#define COLOR_GRIDMAJOR 3
+#define COLOR_CAMERABACK 4
+#define COLOR_ENTITY 5
+#define COLOR_LAST 6
+
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//
+// qfiles.h: quake file formats
+// This file must be identical in the quake and utils directories
+//
+
+/*
+========================================================================
+
+.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;
+
+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;
+
+
+
+/*
+==============================================================================
+
+ .BSP file format
+
+==============================================================================
+*/
+
+#define IDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I')
+ // little-endian "IBSP"
+
+#define BSPVERSION 36
+
+
+// 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 0x20000
+#define MAX_MAP_TEXINFO 8192
+
+#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 HEADER_LUMPS 17
+
+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 allways opposites
+
+typedef struct
+{
+ float normal[3];
+ float dist;
+ int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+// contents flags are seperate bits
+// a 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_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
+
+
+
+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
+ int value; // light emission, etc
+ char texture[32]; // texture name (textures/*.wal)
+ int nexttexinfo; // for animations, -1 = end of chain
+} texinfo_t;
+
+
+#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
+
+
+// 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?)
+
+ int pvsofs; // -1 = no info
+ int phsofs; // -1 = no info
+
+ 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
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by win_qe3.rc
+//
+#define IDAPPLY 3
+#define IDR_MENU1 101
+#define IDR_ACCELERATOR1 104
+#define IDD_CREATE_ENTITY 107
+#define IDD_EDIT_ENTITY 108
+#define IDR_TOOLBAR1 109
+#define IDD_FINDTEXTURE 111
+#define IDD_ENTITY 115
+#define IDR_E_MENU 116
+#define IDD_EDITPROP 117
+#define IDD_GAMMA 118
+#define IDD_FINDBRUSH 119
+#define IDI_ICON1 120
+#define IDD_ROTATE 121
+#define IDD_SIDES 122
+#define IDD_ABOUT 123
+#define IDB_BITMAP1 127
+#define IDD_SURFACE 129
+#define IDC_ENTITY_COMMENT 1018
+#define IDC_VALUE 1021
+#define IDC_KEY 1022
+#define IDC_ENTITY_LIST 1023
+#define IDC_PAIRS 1024
+#define IDC_CHECK1 1026
+#define IDC_CHECK2 1027
+#define IDC_CHECK3 1028
+#define IDC_CHECK4 1029
+#define IDC_CHECK5 1030
+#define IDC_CHECK6 1031
+#define IDC_CHECK7 1032
+#define IDC_CHECK8 1033
+#define IDC_CHECK9 1034
+#define IDC_CHECK10 1035
+#define IDC_CHECK11 1036
+#define IDC_CHECK12 1037
+#define IDC_ANGLE90 1038
+#define IDC_CHECK13 1038
+#define IDC_ANGLE45 1039
+#define IDC_CHECK14 1039
+#define IDC_ANGLE135 1040
+#define IDC_CHECK15 1040
+#define IDC_ANGLE225 1041
+#define IDC_CHECK16 1041
+#define IDC_ANGLEUP 1042
+#define IDC_CHECK17 1042
+#define IDC_ANGLE180 1043
+#define IDC_CHECK18 1043
+#define IDC_ANGLE315 1044
+#define IDC_CHECK19 1044
+#define IDC_ANGLE0 1045
+#define IDC_CHECK20 1045
+#define IDC_ANGLE270 1046
+#define IDC_CHECK21 1046
+#define IDC_ANGLEDOWN 1047
+#define IDC_CHECK22 1047
+#define IDC_DELETEKEY 1048
+#define IDC_ADDPAIR 1048
+#define IDC_CHECK23 1048
+#define IDC_DELETEPAIR 1049
+#define IDC_CHECK24 1049
+#define IDC_CHECK25 1050
+#define IDC_SPAWN1 1051
+#define IDC_CHECK26 1051
+#define IDC_SPAWN2 1052
+#define IDC_CHECK27 1052
+#define IDC_SPAWN3 1053
+#define IDC_CHECK28 1053
+#define IDC_SPAWN4 1054
+#define IDC_CHECK29 1054
+#define IDC_SPAWN5 1055
+#define IDC_CHECK30 1055
+#define IDC_SPAWN6 1056
+#define IDC_CHECK31 1056
+#define IDC_SPAWN7 1057
+#define IDC_CHECK32 1057
+#define IDC_SPAWN8 1058
+#define IDC_CHECK33 1058
+#define IDC_EDIT1 1059
+#define IDC_CHECK34 1059
+#define IDC_ROTATE_BOX 1060
+#define IDC_ROTZ 1060
+#define IDC_CHECK35 1060
+#define IDC_SCALE_BOX 1061
+#define IDC_ROTY 1061
+#define IDC_CHECK36 1061
+#define IDC_E_VALUE_FIELD 1062
+#define IDC_CHECK37 1062
+#define IDC_CHECK38 1063
+#define IDC_E_LIST 1064
+#define IDC_CHECK39 1064
+#define IDC_E_COMMENT 1065
+#define IDC_CHECK40 1065
+#define IDC_CHECK41 1066
+#define IDC_E_PROPS 1067
+#define IDC_CHECK42 1067
+#define IDC_E_135 1068
+#define IDC_CHECK43 1068
+#define IDC_E_180 1069
+#define IDC_CHECK44 1069
+#define IDC_E_225 1070
+#define IDC_CHECK45 1070
+#define IDC_E_270 1071
+#define IDC_CHECK46 1071
+#define IDC_E_90 1072
+#define IDC_CHECK47 1072
+#define IDC_E_45 1073
+#define IDC_CHECK48 1073
+#define IDC_E_0 1074
+#define IDC_CHECK49 1074
+#define IDC_E_315 1075
+#define IDC_CHECK50 1075
+#define IDC_E_UP 1076
+#define IDC_CHECK51 1076
+#define IDC_E_DOWN 1077
+#define IDC_CHECK52 1077
+#define IDC_CHECK53 1078
+#define IDC_CHECK54 1079
+#define IDC_E_ADDPROP 1080
+#define IDC_CHECK55 1080
+#define IDC_E_DELPROP 1081
+#define IDC_CHECK56 1081
+#define IDC_E_CREATE 1082
+#define IDC_CHECK57 1082
+#define IDC_E_STATUS 1083
+#define IDC_CHECK58 1083
+#define IDC_CHECK59 1084
+#define IDC_CHECK60 1085
+#define IDC_CHECK61 1086
+#define IDC_CHECK62 1087
+#define IDC_CHECK63 1088
+#define IDC_CHECK64 1089
+#define IDC_SHIFT_BOX 1090
+#define IDC_HSHIFT 1090
+#define IDC_VSHIFT 1091
+#define IDC_ROTATEV 1092
+#define IDC_SCALEV 1093
+#define IDC_SCALEH 1094
+#define IDC_SHIFTV 1095
+#define IDC_HSHIFTA 1095
+#define IDC_SHIFTH 1096
+#define IDC_VSHIFTA 1097
+#define IDC_HSCALE 1098
+#define IDC_HSCALEA 1099
+#define IDC_VSCALE 1100
+#define IDC_VSCALEA 1101
+#define IDC_ROTATE 1102
+#define IDC_G_EDIT 1103
+#define IDC_ROTATEA 1103
+#define IDC_STATIC_KEY 1104
+#define IDC_FIND_BRUSH 1104
+#define IDC_STATIC_VALUE 1105
+#define IDC_E_KEY_FIELD 1106
+#define IDC_FIND_ENTITY 1107
+#define IDC_SIDES 1108
+#define IDC_ROTX 1109
+#define IDC_E_COLOR 1111
+#define IDC_ABOUT_GLVENDOR 1112
+#define IDC_ABOUT_GLVERSION 1113
+#define IDC_ABOUT_GLRENDERER 1114
+#define IDC_TEXTURE 1114
+#define IDC_ABOUT_GLEXTENSIONS 1115
+#define ID_FILE_EXIT 40002
+#define ID_FILE_SAVEAS 40004
+#define ID_VIEW_CENTER 40005
+#define ID_VIEW_UPFLOOR 40006
+#define ID_VIEW_DOWNFLOOR 40007
+#define ID_BRUSH_FLIPX 40008
+#define ID_BRUSH_FLIPY 40009
+#define ID_BRUSH_FLIPZ 40010
+#define ID_BRUSH_ROTATEX 40011
+#define ID_BRUSH_ROTATEY 40012
+#define ID_BRUSH_ROTATEZ 40013
+#define ID_BSP_FULLVIS 40016
+#define ID_BSP_FASTVIS 40017
+#define ID_BSP_NOVIS 40018
+#define ID_BSP_RELIGHT 40019
+#define ID_BSP_ENTITIES 40020
+#define ID_FILE_POINTFILE 40021
+#define ID_VIEW_100 40022
+#define ID_VIEW_75 40023
+#define ID_VIEW_50 40024
+#define ID_VIEW_25 40025
+#define ID_VIEW_12 40026
+#define ID_GRID_1 40028
+#define ID_GRID_2 40029
+#define ID_GRID_4 40030
+#define ID_GRID_8 40031
+#define ID_GRID_16 40032
+#define ID_TEXTURES_SHOWALL 40033
+#define ID_TEXTURES_SHOWINUSE 40034
+#define ID_TEXTURES_TOGGLEVIEW 40037
+#define ID_SELECTION_CREATEENTITY 40039
+#define ID_SELECTION_EDITENTITY 40040
+#define ID_MISC_BENCHMARK 40041
+#define ID_REGION_OFF 40043
+#define ID_REGION_SETXY 40044
+#define ID_REGION_SETBRUSH 40045
+#define ID_SELECTION_MAKEHOLLOW 40046
+#define ID_SELECTION_SELECTPARTIALTALL 40047
+#define ID_SELECTION_SELECTCOMPLETETALL 40048
+#define ID_SELECTION_CSGSUBTRACT 40049
+#define ID_SELECTION_SELECTTOUCHING 40050
+#define ID_VIEW_NEAREST 40052
+#define ID_VIEW_LINEAR 40053
+#define ID_VIEW_BILINEAR 40054
+#define ID_VIEW_TRILINEAR 40055
+#define ID_VIEW_NEARESTMIPMAP 40056
+#define ID_VIEW_BILINEARMIPMAP 40057
+#define ID_VIEW_SHOWNAMES 40058
+#define ID_VIEW_ZOOMIN 40059
+#define ID_VIEW_ZOOMOUT 40060
+#define ID_VIEW_SHOWCOORDINATES 40061
+#define ID_VIEW_Z100 40062
+#define ID_VIEW_ZZOOMIN 40063
+#define ID_VIEW_ZZOOMOUT 40064
+#define ID_SELECTION_CLONE 40065
+#define ID_SELECTION_DESELECT 40066
+#define ID_SELECTION_DELETE 40067
+#define ID_BUTTON40068 40068
+#define ID_TEXTURES_WIREFRAME 40072
+#define ID_TEXTURES_FLATSHADE 40073
+#define ID_SELECTION_DRAGVERTECIES 40074
+#define ID_SELECTION_DRAGEDGES 40075
+#define ID_REGION_SETTALLBRUSH 40076
+#define ID_SELECTION_SELECTINSIDE 40092
+#define ID_PROJECT_RELEAD 40094
+#define ID_PROJECT_CHANGE 40095
+#define ID_MISC_GAMMA 40097
+#define ID_VIEW_SHOWENT 40098
+#define ID_VIEW_SHOWPATH 40099
+#define ID_VIEW_SHOWLIGHTS 40100
+#define ID_VIEW_SHOWCLIP 40101
+#define ID_VIEW_SHOWWATER 40102
+#define ID_VIEW_SHOWWORLD 40103
+#define ID_MISC_TEXTUREBACKGROUN 40104
+#define ID_TEXTUREBK 40105
+#define ID_COLORS_XYBK 40106
+#define ID_FILE_ABOUT 40107
+#define ID_VIEW_CONSOLE 40108
+#define ID_VIEW_ENTITY 40109
+#define ID_VIEW_TEXTURE 40110
+#define ID_COLORS_MAJOR 40111
+#define ID_COLORS_MINOR 40113
+#define ID_SELECTION_CONNECT 40114
+#define ID_FILE_LOADPROJECT 40115
+#define ID_MISC_FINDBRUSH 40116
+#define ID_MISC_NEXTLEAKSPOT 40117
+#define ID_MISC_PREVIOUSLEAKSPOT 40118
+#define ID_BRUSH_3SIDED 40119
+#define ID_BRUSH_4SIDED 40120
+#define ID_BRUSH_5SIDED 40121
+#define ID_BRUSH_6SIDED 40122
+#define ID_BRUSH_7SIDED 40123
+#define ID_BRUSH_8SIDED 40124
+#define ID_BRUSH_9SIDED 40125
+#define ID_SELECTION_ARBITRARYROTATION 40126
+#define ID_BRUSH_ARBITRARYSIDED 40127
+#define ID_GRID_32 40128
+#define ID_GRID_64 40129
+#define ID_SELECTION_UNGROUPENTITY 40130
+#define ID_MISC_SELECTENTITYCOLOR 40131
+#define ID_MISC_PRINTXY 40132
+#define ID_HELP_ABOUT 40134
+#define ID_EDIT_COPYBRUSH 40135
+#define ID_EDIT_PASTEBRUSH 40136
+#define ID_TEXTURES_INSPECTOR 40137
+#define ID_VIEW_SHOWDETAIL 40138
+#define ID_SELECTION_MAKE_DETAIL 40139
+#define ID_SELECTION_MAKE_STRUCTURAL 40140
+#define ID_REGION_SETSELECTION 40142
+#define ID_VIEW_SHOWBLOCKS 40143
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 130
+#define _APS_NEXT_COMMAND_VALUE 40144
+#define _APS_NEXT_CONTROL_VALUE 1115
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// select.c
+
+#include "qe3.h"
+
+/*
+===========
+Test_Ray
+===========
+*/
+#define DIST_START 999999
+trace_t Test_Ray (vec3_t origin, vec3_t dir, int flags)
+{
+ brush_t *brush;
+ face_t *face;
+ float dist;
+ trace_t t;
+
+ memset (&t, 0, sizeof(t));
+ t.dist = DIST_START;
+
+ if (! (flags & SF_SELECTED_ONLY) )
+ for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
+ {
+ if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity)
+ continue;
+ if (FilterBrush (brush))
+ continue;
+ face = Brush_Ray (origin, dir, brush, &dist);
+ if (dist > 0 && dist < t.dist)
+ {
+ t.dist = dist;
+ t.brush = brush;
+ t.face = face;
+ t.selected = false;
+ }
+ }
+ for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+ {
+ if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity)
+ continue;
+ if (FilterBrush (brush))
+ continue;
+ face = Brush_Ray (origin, dir, brush, &dist);
+ if (dist > 0 && dist < t.dist)
+ {
+ t.dist = dist;
+ t.brush = brush;
+ t.face = face;
+ t.selected = true;
+ }
+ }
+
+ // if entites first, but didn't find any, check regular
+
+ if ( (flags & SF_ENTITIES_FIRST) && t.brush == NULL)
+ return Test_Ray (origin, dir, flags - SF_ENTITIES_FIRST);
+
+ return t;
+}
+
+
+/*
+============
+Select_Brush
+
+============
+*/
+void Select_Brush (brush_t *brush)
+{
+ brush_t *b;
+ entity_t *e;
+
+ selected_face = NULL;
+ if (g_qeglobals.d_select_count < 2)
+ g_qeglobals.d_select_order[g_qeglobals.d_select_count] = brush;
+ g_qeglobals.d_select_count++;
+
+ e = brush->owner;
+ if (e)
+ {
+ // select complete entity on first click
+ if (e != world_entity)
+ {
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ if (b->owner == e)
+ goto singleselect;
+ for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
+ {
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &selected_brushes);
+ }
+ }
+ else
+ {
+singleselect:
+ Brush_RemoveFromList (brush);
+ Brush_AddToList (brush, &selected_brushes);
+ }
+
+ if (e->eclass)
+ {
+ UpdateEntitySel(brush->owner->eclass);
+ }
+ }
+}
+
+/*
+============
+Select_Ray
+
+If the origin is inside a brush, that brush will be ignored.
+============
+*/
+void Select_Ray (vec3_t origin, vec3_t dir, int flags)
+{
+ trace_t t;
+
+ t = Test_Ray (origin, dir, flags);
+ if (!t.brush)
+ return;
+
+ if (flags == SF_SINGLEFACE)
+ {
+ selected_face = t.face;
+ selected_face_brush = t.brush;
+ Sys_UpdateWindows (W_ALL);
+ g_qeglobals.d_select_mode = sel_brush;
+ return;
+ }
+
+ // move the brush to the other list
+
+ g_qeglobals.d_select_mode = sel_brush;
+
+ if (t.selected)
+ {
+ Brush_RemoveFromList (t.brush);
+ Brush_AddToList (t.brush, &active_brushes);
+ } else
+ {
+ Select_Brush (t.brush);
+ }
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+void Select_Delete (void)
+{
+ brush_t *brush;
+
+ selected_face = NULL;
+ g_qeglobals.d_select_mode = sel_brush;
+
+ g_qeglobals.d_select_count = 0;
+ g_qeglobals.d_num_move_points = 0;
+ while (selected_brushes.next != &selected_brushes)
+ {
+ brush = selected_brushes.next;
+ Brush_Free (brush);
+ }
+
+ // FIXME: remove any entities with no brushes
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+void Select_Deselect (void)
+{
+ brush_t *b;
+
+ g_qeglobals.d_workcount++;
+ g_qeglobals.d_select_count = 0;
+ g_qeglobals.d_num_move_points = 0;
+ b = selected_brushes.next;
+
+ if (b == &selected_brushes)
+ {
+ if (selected_face)
+ {
+ selected_face = NULL;
+ Sys_UpdateWindows (W_ALL);
+ }
+ return;
+ }
+
+ selected_face = NULL;
+ g_qeglobals.d_select_mode = sel_brush;
+
+ // grab top / bottom height for new brushes
+ if (b->mins[2] < b->maxs[2])
+ {
+ g_qeglobals.d_new_brush_bottom_z = b->mins[2];
+ g_qeglobals.d_new_brush_top_z = b->maxs[2];
+ }
+
+ selected_brushes.next->prev = &active_brushes;
+ selected_brushes.prev->next = active_brushes.next;
+ active_brushes.next->prev = selected_brushes.prev;
+ active_brushes.next = selected_brushes.next;
+ selected_brushes.prev = selected_brushes.next = &selected_brushes;
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+/*
+============
+Select_Move
+============
+*/
+void Select_Move (vec3_t delta)
+{
+ brush_t *b;
+
+// actually move the selected brushes
+ for (b = selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ Brush_Move (b, delta);
+// Sys_UpdateWindows (W_ALL);
+}
+
+/*
+============
+Select_Clone
+
+Creates an exact duplicate of the selection in place, then moves
+the selected brushes off of their old positions
+============
+*/
+void Select_Clone (void)
+{
+ brush_t *b, *b2, *n, *next, *next2;
+ vec3_t delta;
+ entity_t *e;
+
+ g_qeglobals.d_workcount++;
+ g_qeglobals.d_select_mode = sel_brush;
+
+ delta[0] = g_qeglobals.d_gridsize;
+ delta[1] = g_qeglobals.d_gridsize;
+ delta[2] = 0;
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=next)
+ {
+ next = b->next;
+ // if the brush is a world brush, handle simply
+ if (b->owner == world_entity)
+ {
+ n = Brush_Clone (b);
+ Brush_AddToList (n, &active_brushes);
+ Entity_LinkBrush (world_entity, n);
+ Brush_Build( n );
+ Brush_Move (b, delta);
+ continue;
+ }
+
+ e = Entity_Clone (b->owner);
+ // clear the target / targetname
+ DeleteKey (e, "target");
+ DeleteKey (e, "targetname");
+
+ // if the brush is a fixed size entity, create a new entity
+ if (b->owner->eclass->fixedsize)
+ {
+ n = Brush_Clone (b);
+ Brush_AddToList (n, &active_brushes);
+ Entity_LinkBrush (e, n);
+ Brush_Build( n );
+ Brush_Move (b, delta);
+ continue;
+ }
+
+ // brush is a complex entity, grab all the other ones now
+
+ next = &selected_brushes;
+
+ for ( b2 = b ; b2 != &selected_brushes ; b2=next2)
+ {
+ next2 = b2->next;
+ if (b2->owner != b->owner)
+ {
+ if (next == &selected_brushes)
+ next = b2;
+ continue;
+ }
+
+ // move b2 to the start of selected_brushes,
+ // so it won't be hit again
+ Brush_RemoveFromList (b2);
+ Brush_AddToList (b2, &selected_brushes);
+
+ n = Brush_Clone (b2);
+ Brush_AddToList (n, &active_brushes);
+ Entity_LinkBrush (e, n);
+ Brush_Build( n );
+ Brush_Move (b2, delta);
+ }
+
+ }
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+
+/*
+============
+Select_SetTexture
+============
+*/
+void Select_SetTexture (texdef_t *texdef)
+{
+ brush_t *b;
+
+ if (selected_face)
+ {
+ selected_face->texdef = *texdef;
+ Brush_Build(selected_face_brush);
+ }
+ else
+ {
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ if (!b->owner->eclass->fixedsize)
+ Brush_SetTexture (b, texdef);
+ }
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+================================================================
+
+ TRANSFORMATIONS
+
+================================================================
+*/
+
+void Select_GetBounds (vec3_t mins, vec3_t maxs)
+{
+ brush_t *b;
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ mins[i] = 99999;
+ maxs[i] = -99999;
+ }
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ for (i=0 ; i<3 ; i++)
+ {
+ if (b->mins[i] < mins[i])
+ mins[i] = b->mins[i];
+ if (b->maxs[i] > maxs[i])
+ maxs[i] = b->maxs[i];
+ }
+}
+
+void Select_GetMid (vec3_t mid)
+{
+ vec3_t mins, maxs;
+ int i;
+
+ Select_GetBounds (mins, maxs);
+ for (i=0 ; i<3 ; i++)
+ mid[i] = g_qeglobals.d_gridsize*floor ( ( (mins[i] + maxs[i])*0.5 )/g_qeglobals.d_gridsize );
+}
+
+vec3_t select_origin;
+vec3_t select_matrix[3];
+qboolean select_fliporder;
+
+void Select_AplyMatrix (void)
+{
+ brush_t *b;
+ face_t *f;
+ int i, j;
+ vec3_t temp;
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ {
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ for (i=0 ; i<3 ; i++)
+ {
+ VectorSubtract (f->planepts[i], select_origin, temp);
+ for (j=0 ; j<3 ; j++)
+ f->planepts[i][j] = DotProduct(temp, select_matrix[j])
+ + select_origin[j];
+ }
+ if (select_fliporder)
+ {
+ VectorCopy (f->planepts[0], temp);
+ VectorCopy (f->planepts[2], f->planepts[0]);
+ VectorCopy (temp, f->planepts[2]);
+ }
+ }
+ Brush_Build( b );
+ }
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+void Select_FlipAxis (int axis)
+{
+ int i;
+
+ Select_GetMid (select_origin);
+ for (i=0 ; i<3 ; i++)
+ {
+ VectorCopy (vec3_origin, select_matrix[i]);
+ select_matrix[i][i] = 1;
+ }
+ select_matrix[axis][axis] = -1;
+
+ select_fliporder = true;
+ Select_AplyMatrix ();
+}
+
+void Select_RotateAxis (int axis, float deg)
+{
+ vec3_t temp;
+ int i, j;
+ vec_t c, s;
+
+ if (deg == 0)
+ return;
+
+ Select_GetMid (select_origin);
+ select_fliporder = false;
+
+ if (deg == 90)
+ {
+ for (i=0 ; i<3 ; i++)
+ {
+ VectorCopy (vec3_origin, select_matrix[i]);
+ select_matrix[i][i] = 1;
+ }
+ i = (axis+1)%3;
+ j = (axis+2)%3;
+ VectorCopy (select_matrix[i], temp);
+ VectorCopy (select_matrix[j], select_matrix[i]);
+ VectorSubtract (vec3_origin, temp, select_matrix[j]);
+ }
+ else
+ {
+ deg = -deg;
+ if (deg == -180)
+ {
+ c = -1;
+ s = 0;
+ }
+ else if (deg == -270)
+ {
+ c = 0;
+ s = -1;
+ }
+ else
+ {
+ c = cos(deg/180*3.14159);
+ s = sin (deg/180*3.14159);
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ VectorCopy (vec3_origin, select_matrix[i]);
+ select_matrix[i][i] = 1;
+ }
+
+ switch (axis)
+ {
+ case 0:
+ select_matrix[1][1] = c;
+ select_matrix[1][2] = -s;
+ select_matrix[2][1] = s;
+ select_matrix[2][2] = c;
+ break;
+ case 1:
+ select_matrix[0][0] = c;
+ select_matrix[0][2] = s;
+ select_matrix[2][0] = -s;
+ select_matrix[2][2] = c;
+ break;
+ case 2:
+ select_matrix[0][0] = c;
+ select_matrix[0][1] = -s;
+ select_matrix[1][0] = s;
+ select_matrix[1][1] = c;
+ break;
+ }
+ }
+
+ Select_AplyMatrix ();
+}
+
+/*
+================================================================
+
+GROUP SELECTIONS
+
+================================================================
+*/
+
+void Select_CompleteTall (void)
+{
+ brush_t *b, *next;
+ int i;
+ vec3_t mins, maxs;
+
+ if (!QE_SingleBrush ())
+ return;
+
+ g_qeglobals.d_select_mode = sel_brush;
+
+ VectorCopy (selected_brushes.next->mins, mins);
+ VectorCopy (selected_brushes.next->maxs, maxs);
+ Select_Delete ();
+
+ for (b=active_brushes.next ; b != &active_brushes ; b=next)
+ {
+ next = b->next;
+ for (i=0 ; i<2 ; i++)
+ if (b->maxs[i] > maxs[i] || b->mins[i] < mins[i])
+ break;
+ if (i == 2)
+ {
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &selected_brushes);
+ }
+ }
+ Sys_UpdateWindows (W_ALL);
+}
+
+void Select_PartialTall (void)
+{
+ brush_t *b, *next;
+ int i;
+ vec3_t mins, maxs;
+
+ if (!QE_SingleBrush ())
+ return;
+
+ g_qeglobals.d_select_mode = sel_brush;
+
+ VectorCopy (selected_brushes.next->mins, mins);
+ VectorCopy (selected_brushes.next->maxs, maxs);
+ Select_Delete ();
+
+ for (b=active_brushes.next ; b != &active_brushes ; b=next)
+ {
+ next = b->next;
+ for (i=0 ; i<2 ; i++)
+ if (b->mins[i] > maxs[i] || b->maxs[i] < mins[i])
+ break;
+ if (i == 2)
+ {
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &selected_brushes);
+ }
+ }
+ Sys_UpdateWindows (W_ALL);
+}
+
+void Select_Touching (void)
+{
+ brush_t *b, *next;
+ int i;
+ vec3_t mins, maxs;
+
+ if (!QE_SingleBrush ())
+ return;
+
+ g_qeglobals.d_select_mode = sel_brush;
+
+ VectorCopy (selected_brushes.next->mins, mins);
+ VectorCopy (selected_brushes.next->maxs, maxs);
+
+ for (b=active_brushes.next ; b != &active_brushes ; b=next)
+ {
+ next = b->next;
+ for (i=0 ; i<3 ; i++)
+ if (b->mins[i] > maxs[i]+1 || b->maxs[i] < mins[i]-1)
+ break;
+ if (i == 3)
+ {
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &selected_brushes);
+ }
+ }
+ Sys_UpdateWindows (W_ALL);
+}
+
+void Select_Inside (void)
+{
+ brush_t *b, *next;
+ int i;
+ vec3_t mins, maxs;
+
+ if (!QE_SingleBrush ())
+ return;
+
+ g_qeglobals.d_select_mode = sel_brush;
+
+ VectorCopy (selected_brushes.next->mins, mins);
+ VectorCopy (selected_brushes.next->maxs, maxs);
+ Select_Delete ();
+
+ for (b=active_brushes.next ; b != &active_brushes ; b=next)
+ {
+ next = b->next;
+ for (i=0 ; i<3 ; i++)
+ if (b->maxs[i] > maxs[i] || b->mins[i] < mins[i])
+ break;
+ if (i == 3)
+ {
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &selected_brushes);
+ }
+ }
+ Sys_UpdateWindows (W_ALL);
+}
+
+/*
+=============
+Select_Ungroup
+
+Turn the currently selected entity back into normal brushes
+=============
+*/
+void Select_Ungroup (void)
+{
+ entity_t *e;
+ brush_t *b;
+
+ e = selected_brushes.next->owner;
+
+ if (!e || e == world_entity || e->eclass->fixedsize)
+ {
+ Sys_Status ("Not a grouped entity.", 0);
+ return;
+ }
+
+ for (b=e->brushes.onext ; b != &e->brushes ; b=e->brushes.onext)
+ {
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &active_brushes);
+ Entity_UnlinkBrush (b);
+ Entity_LinkBrush (world_entity, b);
+ Brush_Build( b );
+ b->owner = world_entity;
+ }
+
+ Entity_Free (e);
+ Sys_UpdateWindows (W_ALL);
+}
+
+/*
+====================
+Select_MakeStructural
+====================
+*/
+void Select_MakeStructural (void)
+{
+ brush_t *b;
+ face_t *f;
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ for (f=b->brush_faces ; f ; f=f->next)
+ f->texdef.contents &= ~CONTENTS_DETAIL;
+ Select_Deselect ();
+ Sys_UpdateWindows (W_ALL);
+}
+
+void Select_MakeDetail (void)
+{
+ brush_t *b;
+ face_t *f;
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ for (f=b->brush_faces ; f ; f=f->next)
+ f->texdef.contents |= CONTENTS_DETAIL;
+ Select_Deselect ();
+ Sys_UpdateWindows (W_ALL);
+}
+
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+typedef enum
+{
+ sel_brush,
+ // sel_sticky_brush,
+ // sel_face,
+ sel_vertex,
+ sel_edge
+} select_t;
+
+typedef struct
+{
+ brush_t *brush;
+ face_t *face;
+ float dist;
+ qboolean selected;
+} trace_t;
+
+#define SF_SELECTED_ONLY 1
+#define SF_ENTITIES_FIRST 2
+#define SF_SINGLEFACE 4
+
+
+trace_t Test_Ray (vec3_t origin, vec3_t dir, int flags);
+
+void Select_GetBounds (vec3_t mins, vec3_t maxs);
+void Select_Brush (brush_t *b);
+void Select_Ray (vec3_t origin, vec3_t dir, int flags);
+void Select_Delete (void);
+void Select_Deselect (void);
+void Select_Clone (void);
+void Select_Move (vec3_t delta);
+void Select_SetTexture (texdef_t *texdef);
+void Select_FlipAxis (int axis);
+void Select_RotateAxis (int axis, float deg);
+void Select_CompleteTall (void);
+void Select_PartialTall (void);
+void Select_Touching (void);
+void Select_Inside (void);
+void Select_MakeStructural (void);
+void Select_MakeDetail (void);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include "io.h"
+
+#define TYP_MIPTEX 68
+static unsigned tex_palette[256];
+
+static qtexture_t *notexture;
+
+static qboolean nomips;
+
+#define FONT_HEIGHT 10
+
+static HGLRC s_hglrcTexture;
+static HDC s_hdcTexture;
+
+//int texture_mode = GL_NEAREST;
+//int texture_mode = GL_NEAREST_MIPMAP_NEAREST;
+//int texture_mode = GL_NEAREST_MIPMAP_LINEAR;
+//int texture_mode = GL_LINEAR;
+//int texture_mode = GL_LINEAR_MIPMAP_NEAREST;
+int texture_mode = GL_LINEAR_MIPMAP_LINEAR;
+
+int texture_extension_number = 1;
+
+// current active texture directory. if empty, show textures in use
+char texture_directory[32]; // use if texture_showinuse is false
+qboolean texture_showinuse;
+
+// texture layout functions
+qtexture_t *current_texture;
+int current_x, current_y, current_row;
+
+int texture_nummenus;
+#define MAX_TEXTUREDIRS 100
+char texture_menunames[MAX_TEXTUREDIRS][64];
+
+qboolean g_dontuse; // set to true to load the texture but not flag as used
+
+void SelectTexture (int mx, int my);
+
+void Texture_MouseDown (int x, int y, int buttons);
+void Texture_MouseUp (int x, int y, int buttons);
+void Texture_MouseMoved (int x, int y, int buttons);
+
+//=====================================================
+
+void SortTextures(void)
+{
+ qtexture_t *q, *qtemp, *qhead, *qcur, *qprev;
+
+ // standard insertion sort
+ // Take the first texture from the list and
+ // add it to our new list
+ if ( g_qeglobals.d_qtextures == NULL)
+ return;
+
+ qhead = g_qeglobals.d_qtextures;
+ q = g_qeglobals.d_qtextures->next;
+ qhead->next = NULL;
+
+ // while there are still things on the old
+ // list, keep adding them to the new list
+ while (q)
+ {
+ qtemp = q;
+ q = q->next;
+
+ qprev = NULL;
+ qcur = qhead;
+
+ while (qcur)
+ {
+ // Insert it here?
+ if (strcmp(qtemp->name, qcur->name) < 0)
+ {
+ qtemp->next = qcur;
+ if (qprev)
+ qprev->next = qtemp;
+ else
+ qhead = qtemp;
+ break;
+ }
+
+ // Move on
+
+ qprev = qcur;
+ qcur = qcur->next;
+
+
+ // is this one at the end?
+
+ if (qcur == NULL)
+ {
+ qprev->next = qtemp;
+ qtemp->next = NULL;
+ }
+ }
+
+
+ }
+
+ g_qeglobals.d_qtextures = qhead;
+}
+
+//=====================================================
+
+
+/*
+==============
+Texture_InitPalette
+==============
+*/
+void Texture_InitPalette (byte *pal)
+{
+ int r,g,b,v;
+ int i;
+ int inf;
+ byte gammatable[256];
+ float gamma;
+
+ gamma = g_qeglobals.d_savedinfo.fGamma;
+
+ if (gamma == 1.0)
+ {
+ for (i=0 ; i<256 ; i++)
+ gammatable[i] = i;
+ }
+ else
+ {
+ for (i=0 ; i<256 ; i++)
+ {
+ inf = 255 * pow ( (i+0.5)/255.5 , gamma ) + 0.5;
+ if (inf < 0)
+ inf = 0;
+ if (inf > 255)
+ inf = 255;
+ gammatable[i] = inf;
+ }
+ }
+
+ for (i=0 ; i<256 ; i++)
+ {
+ r = gammatable[pal[0]];
+ g = gammatable[pal[1]];
+ b = gammatable[pal[2]];
+ pal += 3;
+
+ v = (r<<24) + (g<<16) + (b<<8) + 255;
+ v = BigLong (v);
+
+ tex_palette[i] = v;
+ }
+}
+
+void SetTexParameters (void)
+{
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture_mode );
+
+ switch ( texture_mode )
+ {
+ case GL_NEAREST:
+ case GL_NEAREST_MIPMAP_NEAREST:
+ case GL_NEAREST_MIPMAP_LINEAR:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ break;
+ case GL_LINEAR:
+ case GL_LINEAR_MIPMAP_NEAREST:
+ case GL_LINEAR_MIPMAP_LINEAR:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ break;
+ }
+}
+
+/*
+============
+Texture_SetMode
+============
+*/
+void Texture_SetMode(int iMenu)
+{
+ int i, iMode;
+ HMENU hMenu;
+ qboolean texturing = true;
+
+ hMenu = GetMenu(g_qeglobals.d_hwndMain);
+
+ switch(iMenu) {
+ case ID_VIEW_NEAREST:
+ iMode = GL_NEAREST;
+ break;
+ case ID_VIEW_NEARESTMIPMAP:
+ iMode = GL_NEAREST_MIPMAP_NEAREST;
+ break;
+ case ID_VIEW_LINEAR:
+ iMode = GL_NEAREST_MIPMAP_LINEAR;
+ break;
+ case ID_VIEW_BILINEAR:
+ iMode = GL_LINEAR;
+ break;
+ case ID_VIEW_BILINEARMIPMAP:
+ iMode = GL_LINEAR_MIPMAP_NEAREST;
+ break;
+ case ID_VIEW_TRILINEAR:
+ iMode = GL_LINEAR_MIPMAP_LINEAR;
+ break;
+
+ case ID_TEXTURES_WIREFRAME:
+ iMode = 0;
+ texturing = false;
+ break;
+
+ case ID_TEXTURES_FLATSHADE:
+ iMode = 0;
+ texturing = false;
+ break;
+
+ }
+
+ CheckMenuItem(hMenu, ID_VIEW_NEAREST, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_VIEW_NEARESTMIPMAP, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_VIEW_LINEAR, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_VIEW_BILINEARMIPMAP, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_VIEW_BILINEAR, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_VIEW_TRILINEAR, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_TEXTURES_WIREFRAME, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_TEXTURES_FLATSHADE, MF_BYCOMMAND | MF_UNCHECKED);
+
+ CheckMenuItem(hMenu, iMenu, MF_BYCOMMAND | MF_CHECKED);
+
+ g_qeglobals.d_savedinfo.iTexMenu = iMenu;
+ texture_mode = iMode;
+ if ( texturing )
+ SetTexParameters ();
+
+ if ( !texturing && iMenu == ID_TEXTURES_WIREFRAME)
+ {
+ camera.draw_mode = cd_wire;
+ Map_BuildBrushData();
+ Sys_UpdateWindows (W_ALL);
+ return;
+
+ } else if ( !texturing && iMenu == ID_TEXTURES_FLATSHADE) {
+
+ camera.draw_mode = cd_solid;
+ Map_BuildBrushData();
+ Sys_UpdateWindows (W_ALL);
+ return;
+ }
+
+ for (i=1 ; i<texture_extension_number ; i++)
+ {
+ glBindTexture( GL_TEXTURE_2D, i );
+ SetTexParameters ();
+ }
+
+ // select the default texture
+ glBindTexture( GL_TEXTURE_2D, 0 );
+
+ glFinish();
+
+ if (camera.draw_mode != cd_texture)
+ {
+ camera.draw_mode = cd_texture;
+ Map_BuildBrushData();
+ }
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+=================
+Texture_LoadTexture
+=================
+*/
+qtexture_t *Texture_LoadTexture (miptex_t *qtex)
+{
+ byte *source;
+ unsigned *dest;
+ int width, height, i, count;
+ int total[3];
+ qtexture_t *q;
+
+ q = qmalloc(sizeof(*q));
+ width = LittleLong(qtex->width);
+ height = LittleLong(qtex->height);
+
+ q->width = width;
+ q->height = height;
+
+ q->flags = qtex->flags;
+ q->value = qtex->value;
+ q->contents = qtex->contents;
+
+ dest = qmalloc (width*height*4);
+
+ count = width*height;
+ source = (byte *)qtex + LittleLong(qtex->offsets[0]);
+
+ // The dib is upside down so we want to copy it into
+ // the buffer bottom up.
+
+ total[0] = total[1] = total[2] = 0;
+ for (i=0 ; i<count ; i++)
+ {
+ dest[i] = tex_palette[source[i]];
+
+ total[0] += ((byte *)(dest+i))[0];
+ total[1] += ((byte *)(dest+i))[1];
+ total[2] += ((byte *)(dest+i))[2];
+ }
+
+ q->color[0] = (float)total[0]/(count*255);
+ q->color[1] = (float)total[1]/(count*255);
+ q->color[2] = (float)total[2]/(count*255);
+
+ q->texture_number = texture_extension_number++;
+
+ glBindTexture( GL_TEXTURE_2D, q->texture_number );
+ SetTexParameters ();
+
+ if (nomips)
+ glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dest);
+ else
+ gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height,GL_RGBA, GL_UNSIGNED_BYTE, dest);
+
+ free (dest);
+
+ glBindTexture( GL_TEXTURE_2D, 0 );
+
+ return q;
+}
+
+/*
+===============
+Texture_CreateSolid
+
+Create a single pixel texture of the apropriate color
+===============
+*/
+qtexture_t *Texture_CreateSolid (char *name)
+{
+ byte data[4];
+ qtexture_t *q;
+
+ q = qmalloc(sizeof(*q));
+
+ sscanf (name, "(%f %f %f)", &q->color[0], &q->color[1], &q->color[2]);
+
+ data[0] = q->color[0]*255;
+ data[1] = q->color[1]*255;
+ data[2] = q->color[2]*255;
+ data[3] = 255;
+
+ q->width = q->height = 1;
+ q->texture_number = texture_extension_number++;
+ glBindTexture( GL_TEXTURE_2D, q->texture_number );
+ SetTexParameters ();
+
+ if (nomips)
+ glTexImage2D(GL_TEXTURE_2D, 0, 3, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ else
+ gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 1, 1,GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ glBindTexture( GL_TEXTURE_2D, 0 );
+
+ return q;
+}
+
+
+/*
+=================
+Texture_MakeNotexture
+=================
+*/
+void Texture_MakeNotexture (void)
+{
+ qtexture_t *q;
+ byte data[4][4];
+
+ notexture = q = qmalloc(sizeof(*q));
+ strcpy (q->name, "notexture");
+ q->width = q->height = 64;
+
+ memset (data, 0, sizeof(data));
+ data[0][2] = data[3][2] = 255;
+
+ q->color[0] = 0;
+ q->color[1] = 0;
+ q->color[2] = 0.5;
+
+ q->texture_number = texture_extension_number++;
+ glBindTexture( GL_TEXTURE_2D, q->texture_number );
+ SetTexParameters ();
+
+ if (nomips)
+ glTexImage2D(GL_TEXTURE_2D, 0, 3, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ else
+ gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 2, 2,GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ glBindTexture( GL_TEXTURE_2D, 0 );
+}
+
+
+
+/*
+===============
+Texture_ForName
+===============
+*/
+qtexture_t *Texture_ForName (char *name)
+{
+ byte *lump;
+ qtexture_t *q;
+ char filename[1024];
+
+//return notexture;
+ for (q=g_qeglobals.d_qtextures ; q ; q=q->next)
+ {
+ if (!strcmp(name, q->name))
+ {
+ if (!g_dontuse)
+ q->inuse = true;
+ return q;
+ }
+ }
+
+ if (name[0] == '(')
+ {
+ q = Texture_CreateSolid (name);
+ strncpy (q->name, name, sizeof(q->name)-1);
+ }
+ else
+ {
+ // load the file
+ sprintf (filename, "%s/%s.wal",
+ ValueForKey (g_qeglobals.d_project_entity, "texturepath"),
+ name);
+ Sys_Printf ("Loading %s\n", name);
+ if (LoadFile (filename, &lump) == -1)
+ {
+ Sys_Printf (" load failed!\n");
+ return notexture;
+ }
+ q = Texture_LoadTexture ((miptex_t *)lump);
+ free (lump);
+ strncpy (q->name, name, sizeof(q->name)-1);
+ StripExtension (q->name);
+ }
+
+ if (!g_dontuse)
+ q->inuse = true;
+ q->next = g_qeglobals.d_qtextures;
+ g_qeglobals.d_qtextures = q;
+
+ return q;
+}
+
+/*
+==================
+FillTextureMenu
+
+==================
+*/
+void FillTextureMenu (void)
+{
+ HMENU hmenu;
+ int i;
+ struct _finddata_t fileinfo;
+ int handle;
+ char dirstring[1024];
+ char *path;
+
+ hmenu = GetSubMenu (GetMenu(g_qeglobals.d_hwndMain), MENU_TEXTURE);
+
+ // delete everything
+ for (i=0 ; i<texture_nummenus ; i++)
+ DeleteMenu (hmenu, CMD_TEXTUREWAD+i, MF_BYCOMMAND);
+
+ // add everything
+ path = ValueForKey (g_qeglobals.d_project_entity, "texturepath");
+
+ sprintf (dirstring, "%s/*.*", path);
+
+ handle = _findfirst (dirstring, &fileinfo);
+ if (handle == -1)
+ return;
+
+ do
+ {
+ if (!(fileinfo.attrib & _A_SUBDIR))
+ continue;
+ if (fileinfo.name[0] == '.')
+ continue;
+ // add this directory to the menu
+ AppendMenu (hmenu, MF_ENABLED|MF_STRING,
+ CMD_TEXTUREWAD+texture_nummenus, (LPCTSTR)fileinfo.name);
+ strcpy (texture_menunames[texture_nummenus], fileinfo.name);
+ strcat (texture_menunames[texture_nummenus], "/");
+ if (++texture_nummenus == MAX_TEXTUREDIRS)
+ break;
+ } while (_findnext( handle, &fileinfo ) != -1);
+
+ _findclose (handle);
+}
+
+
+/*
+==================
+Texture_ClearInuse
+
+A new map is being loaded, so clear inuse markers
+==================
+*/
+void Texture_ClearInuse (void)
+{
+ qtexture_t *q;
+
+ for (q=g_qeglobals.d_qtextures ; q ; q=q->next)
+ {
+ q->inuse = false;
+ }
+}
+
+
+
+/*
+==============
+Texture_ShowDirectory
+==============
+*/
+void Texture_ShowDirectory (int menunum)
+{
+ struct _finddata_t fileinfo;
+ int handle;
+ char name[1024];
+ char dirstring[1024];
+
+ texture_showinuse = false;
+ strcpy (texture_directory, texture_menunames[menunum-CMD_TEXTUREWAD]);
+
+ g_qeglobals.d_texturewin.originy = 0;
+ Sys_Status("loading all textures\n", 0);
+
+ // load all .wal files
+ sprintf (dirstring, "%s/textures/%s*.wal",
+ ValueForKey (g_qeglobals.d_project_entity, "basepath"),
+ texture_menunames[menunum-CMD_TEXTUREWAD]);
+
+ Sys_Printf ("Scanning %s\n", dirstring);
+
+ handle = _findfirst (dirstring, &fileinfo);
+ if (handle == -1)
+ return;
+
+ g_dontuse = true;
+ do
+ {
+ sprintf (name, "%s%s", texture_directory, fileinfo.name);
+ StripExtension (name);
+ Texture_ForName (name);
+ } while (_findnext( handle, &fileinfo ) != -1);
+ g_dontuse = false;
+
+ _findclose (handle);
+
+ SortTextures();
+ SetInspectorMode(W_TEXTURE);
+ Sys_UpdateWindows(W_TEXTURE);
+
+ sprintf (name, "Textures: %s", texture_directory);
+ SetWindowText(g_qeglobals.d_hwndEntity, name);
+
+ // select the first texture in the list
+ if (!g_qeglobals.d_texturewin.texdef.name[0])
+ SelectTexture (16, g_qeglobals.d_texturewin.height -16);
+}
+
+/*
+==============
+Texture_ShowInuse
+==============
+*/
+void Texture_ShowInuse (void)
+{
+ char name[1024];
+ face_t *f;
+ brush_t *b;
+
+ texture_showinuse = true;
+
+ g_qeglobals.d_texturewin.originy = 0;
+ Sys_Status("Selecting active textures\n", 0);
+ Texture_ClearInuse ();
+
+ for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=b->next)
+ for (f=b->brush_faces ; f ; f=f->next)
+ Texture_ForName (f->texdef.name);
+
+ for (b=selected_brushes.next ; b != NULL && b != &selected_brushes ; b=b->next)
+ for (f=b->brush_faces ; f ; f=f->next)
+ Texture_ForName (f->texdef.name);
+
+ SortTextures();
+ SetInspectorMode(W_TEXTURE);
+ Sys_UpdateWindows (W_TEXTURE);
+
+ sprintf (name, "Textures: in use");
+ SetWindowText(g_qeglobals.d_hwndEntity, name);
+
+ // select the first texture in the list
+ if (!g_qeglobals.d_texturewin.texdef.name[0])
+ SelectTexture (16, g_qeglobals.d_texturewin.height -16);
+}
+
+/*
+============================================================================
+
+TEXTURE LAYOUT
+
+============================================================================
+*/
+
+void Texture_StartPos (void)
+{
+ current_texture = g_qeglobals.d_qtextures;
+ current_x = 8;
+ current_y = -8;
+ current_row = 0;
+}
+
+qtexture_t *Texture_NextPos (int *x, int *y)
+{
+ qtexture_t *q;
+
+ while (1)
+ {
+ q = current_texture;
+ if (!q)
+ return q;
+ current_texture = current_texture->next;
+ if (q->name[0] == '(') // fake color texture
+ continue;
+ if (q->inuse)
+ break; // allways show in use
+ if (!texture_showinuse && strncmp (q->name, texture_directory, strlen(texture_directory)))
+ continue;
+ break;
+ }
+
+ if (current_x + q->width > g_qeglobals.d_texturewin.width-8 && current_row)
+ { // go to the next row unless the texture is the first on the row
+ current_x = 8;
+ current_y -= current_row + FONT_HEIGHT + 4;
+ current_row = 0;
+ }
+
+ *x = current_x;
+ *y = current_y;
+
+ // Is our texture larger than the row? If so, grow the
+ // row height to match it
+
+ if (current_row < q->height)
+ current_row = q->height;
+
+ // never go less than 64, or the names get all crunched up
+ current_x += q->width < 64 ? 64 : q->width;
+ current_x += 8;
+
+ return q;
+}
+
+/*
+============================================================================
+
+ MOUSE ACTIONS
+
+============================================================================
+*/
+
+static int textures_cursorx, textures_cursory;
+
+
+/*
+============
+Texture_SetTexture
+
+============
+*/
+void Texture_SetTexture (texdef_t *texdef)
+{
+ qtexture_t *q;
+ int x,y;
+ char sz[256];
+
+ if (texdef->name[0] == '(')
+ {
+ Sys_Status("Can't select an entity texture\n", 0);
+ return;
+ }
+ g_qeglobals.d_texturewin.texdef = *texdef;
+
+ Sys_UpdateWindows (W_TEXTURE);
+ sprintf(sz, "Selected texture: %s\n", texdef->name);
+ Sys_Status(sz, 0);
+ Select_SetTexture(texdef);
+
+// scroll origin so the texture is completely on screen
+ Texture_StartPos ();
+ while (1)
+ {
+ q = Texture_NextPos (&x, &y);
+ if (!q)
+ break;
+ if (!strcmpi(texdef->name, q->name))
+ {
+ if (y > g_qeglobals.d_texturewin.originy)
+ {
+ g_qeglobals.d_texturewin.originy = y;
+ Sys_UpdateWindows (W_TEXTURE);
+ return;
+ }
+
+ if (y-q->height-2*FONT_HEIGHT < g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height)
+ {
+ g_qeglobals.d_texturewin.originy = y-q->height-2*FONT_HEIGHT+g_qeglobals.d_texturewin.height;
+ Sys_UpdateWindows (W_TEXTURE);
+ return;
+ }
+
+ return;
+ }
+ }
+}
+
+
+/*
+==============
+SelectTexture
+
+ By mouse click
+==============
+*/
+void SelectTexture (int mx, int my)
+{
+ int x, y;
+ qtexture_t *q;
+ texdef_t tex;
+
+ my += g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height;
+
+ Texture_StartPos ();
+ while (1)
+ {
+ q = Texture_NextPos (&x, &y);
+ if (!q)
+ break;
+ if (mx > x && mx - x < q->width
+ && my < y && y - my < q->height + FONT_HEIGHT)
+ {
+ memset (&tex, 0, sizeof(tex));
+ tex.scale[0] = 1;
+ tex.scale[1] = 1;
+ tex.flags = q->flags;
+ tex.value = q->value;
+ tex.contents = q->contents;
+ strcpy (tex.name, q->name);
+ Texture_SetTexture (&tex);
+ return;
+ }
+ }
+
+ Sys_Status("Did not select a texture\n", 0);
+}
+
+/*
+==============
+Texture_MouseDown
+==============
+*/
+void Texture_MouseDown (int x, int y, int buttons)
+{
+ Sys_GetCursorPos (&textures_cursorx, &textures_cursory);
+
+ // lbutton = select texture
+ if (buttons == MK_LBUTTON )
+ {
+ SelectTexture (x, g_qeglobals.d_texturewin.height - 1 - y);
+ return;
+ }
+
+}
+
+/*
+==============
+Texture_MouseUp
+==============
+*/
+void Texture_MouseUp (int x, int y, int buttons)
+{
+}
+
+/*
+==============
+Texture_MouseMoved
+==============
+*/
+void Texture_MouseMoved (int x, int y, int buttons)
+{
+ int scale = 1;
+
+ if ( buttons & MK_SHIFT )
+ scale = 4;
+
+ // rbutton = drag texture origin
+ if (buttons & MK_RBUTTON)
+ {
+ Sys_GetCursorPos (&x, &y);
+ if ( y != textures_cursory)
+ {
+ g_qeglobals.d_texturewin.originy += ( y-textures_cursory) * scale;
+ if (g_qeglobals.d_texturewin.originy > 0)
+ g_qeglobals.d_texturewin.originy = 0;
+ Sys_SetCursorPos (textures_cursorx, textures_cursory);
+ Sys_UpdateWindows (W_TEXTURE);
+ }
+ return;
+ }
+}
+
+
+/*
+============================================================================
+
+DRAWING
+
+============================================================================
+*/
+
+int imax(int iFloor, int i) { if (i>iFloor) return iFloor; return i; }
+HFONT ghFont = NULL;
+
+/*
+============
+Texture_Draw2
+============
+*/
+void Texture_Draw2 (int width, int height)
+{
+ qtexture_t *q;
+ int x, y;
+ char *name;
+
+ glClearColor (
+ g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][0],
+ g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][1],
+ g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][2],
+ 0);
+ glViewport (0,0,width,height);
+ glClear (GL_COLOR_BUFFER_BIT);
+ glDisable (GL_DEPTH_TEST);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0, width, g_qeglobals.d_texturewin.originy-height, g_qeglobals.d_texturewin.originy, -100, 100);
+ glEnable (GL_TEXTURE_2D);
+
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ g_qeglobals.d_texturewin.width = width;
+ g_qeglobals.d_texturewin.height = height;
+ Texture_StartPos ();
+
+ while (1)
+ {
+ q = Texture_NextPos (&x, &y);
+ if (!q)
+ break;
+
+ // Is this texture visible?
+ if ( (y-q->height-FONT_HEIGHT < g_qeglobals.d_texturewin.originy)
+ && (y > g_qeglobals.d_texturewin.originy - height) )
+ {
+
+ // if in use, draw a background
+ if (q->inuse && !texture_showinuse)
+ {
+ glLineWidth (1);
+ glColor3f (0.5,1,0.5);
+ glDisable (GL_TEXTURE_2D);
+
+ glBegin (GL_LINE_LOOP);
+ glVertex2f (x-1,y+1-FONT_HEIGHT);
+ glVertex2f (x-1,y-q->height-1-FONT_HEIGHT);
+ glVertex2f (x+1+q->width,y-q->height-1-FONT_HEIGHT);
+ glVertex2f (x+1+q->width,y+1-FONT_HEIGHT);
+ glEnd ();
+
+ glEnable (GL_TEXTURE_2D);
+ }
+
+ // Draw the texture
+ glColor3f (1,1,1);
+ glBindTexture( GL_TEXTURE_2D, q->texture_number );
+ glBegin (GL_QUADS);
+ glTexCoord2f (0,0);
+ glVertex2f (x,y-FONT_HEIGHT);
+ glTexCoord2f (1,0);
+ glVertex2f (x+q->width,y-FONT_HEIGHT);
+ glTexCoord2f (1,1);
+ glVertex2f (x+q->width,y-FONT_HEIGHT-q->height);
+ glTexCoord2f (0,1);
+ glVertex2f (x,y-FONT_HEIGHT-q->height);
+ glEnd ();
+
+ // draw the selection border
+ if (!strcmpi(g_qeglobals.d_texturewin.texdef.name, q->name))
+ {
+ glLineWidth (3);
+ glColor3f (1,0,0);
+ glDisable (GL_TEXTURE_2D);
+
+ glBegin (GL_LINE_LOOP);
+ glVertex2f (x-4,y-FONT_HEIGHT+4);
+ glVertex2f (x-4,y-FONT_HEIGHT-q->height-4);
+ glVertex2f (x+4+q->width,y-FONT_HEIGHT-q->height-4);
+ glVertex2f (x+4+q->width,y-FONT_HEIGHT+4);
+ glEnd ();
+
+ glEnable (GL_TEXTURE_2D);
+ glLineWidth (1);
+ }
+
+ // draw the texture name
+ glColor3f (0,0,0);
+ glRasterPos2f (x, y-FONT_HEIGHT+2);
+
+ // don't draw the directory name
+ for (name = q->name ; *name && *name != '/' && *name != '\\' ; name++)
+ ;
+ if (!*name)
+ name = q->name;
+ else
+ name++;
+ glCallLists (strlen(name), GL_UNSIGNED_BYTE, name);
+ }
+ }
+
+ // reset the current texture
+ glBindTexture( GL_TEXTURE_2D, 0 );
+ glFinish();
+}
+
+/*
+============
+WTexWndProc
+============
+*/
+LONG WINAPI WTex_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ int xPos, yPos;
+ RECT rect;
+
+ GetClientRect(hWnd, &rect);
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ s_hdcTexture = GetDC(hWnd);
+ QEW_SetupPixelFormat(s_hdcTexture, false);
+
+ if ( ( s_hglrcTexture = wglCreateContext( s_hdcTexture ) ) == 0 )
+ Error( "wglCreateContext in WTex_WndProc failed" );
+
+ if (!wglMakeCurrent( s_hdcTexture, s_hglrcTexture ))
+ Error ("wglMakeCurrent in WTex_WndProc failed");
+
+ if (!wglShareLists( g_qeglobals.d_hglrcBase, s_hglrcTexture ) )
+ Error( "wglShareLists in WTex_WndProc failed" );
+
+ return 0;
+
+ case WM_DESTROY:
+ wglMakeCurrent( NULL, NULL );
+ wglDeleteContext( s_hglrcTexture );
+ ReleaseDC( hWnd, s_hdcTexture );
+ return 0;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+
+ BeginPaint(hWnd, &ps);
+
+ if ( !wglMakeCurrent( s_hdcTexture, s_hglrcTexture ) )
+ Error ("wglMakeCurrent failed");
+ Texture_Draw2 (rect.right-rect.left, rect.bottom-rect.top);
+ SwapBuffers(s_hdcTexture);
+
+ EndPaint(hWnd, &ps);
+ }
+ return 0;
+
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONDOWN:
+ SetCapture( g_qeglobals.d_hwndTexture );
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+
+ Texture_MouseDown (xPos, yPos, wParam);
+ return 0;
+
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+
+ Texture_MouseUp (xPos, yPos, wParam);
+ if (! (wParam & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+ ReleaseCapture ();
+ return 0;
+
+ case WM_MOUSEMOVE:
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+
+ Texture_MouseMoved (xPos, yPos, wParam);
+ return 0;
+ }
+
+ return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+
+
+/*
+==================
+CreateTextureWindow
+
+We need to create a seperate window for the textures
+in the inspector window, because we can't share
+gl and gdi drawing in a single window
+==================
+*/
+#define TEXTURE_WINDOW_CLASS "QTEX"
+HWND CreateTextureWindow (void)
+{
+ WNDCLASS wc;
+ HWND hwnd;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WTex_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = g_qeglobals.d_hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = TEXTURE_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Error ("WCam_Register: failed");
+
+ hwnd = CreateWindow (TEXTURE_WINDOW_CLASS ,
+ "Texture View",
+ WS_BORDER|WS_CHILD|WS_VISIBLE,
+ 20,
+ 20,
+ 64,
+ 64, // size
+
+ g_qeglobals.d_hwndEntity, // parent window
+ 0, // no menu
+ g_qeglobals.d_hInstance,
+ 0);
+ if (!hwnd)
+ Error ("Couldn't create texturewindow");
+
+ return hwnd;
+}
+
+/*
+==================
+Texture_Flush
+==================
+*/
+void Texture_Flush (void)
+{
+}
+
+
+/*
+==================
+Texture_Init
+==================
+*/
+void Texture_Init (void)
+{
+ char name[1024];
+ byte *pal;
+
+ // load the palette
+ sprintf (name, "%s/pics/colormap.pcx",
+ ValueForKey (g_qeglobals.d_project_entity, "basepath"));
+ Load256Image (name, NULL, &pal, NULL, NULL);
+ if (!pal)
+ Error ("Couldn't load %s", name);
+ Texture_InitPalette (pal);
+ free (pal);
+
+ // create the fallback texture
+ Texture_MakeNotexture ();
+
+ g_qeglobals.d_qtextures = NULL;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+typedef struct
+{
+ char name[32];
+ float shift[2];
+ float rotate;
+ float scale[2];
+ int contents;
+ int flags;
+ int value;
+} texdef_t;
+
+
+typedef struct
+{
+ int width, height;
+ int originy;
+ texdef_t texdef;
+} texturewin_t;
+
+typedef struct qtexture_s
+{
+ struct qtexture_s *next;
+ char name[64]; // includes partial directory and extension
+ int width, height;
+ int contents;
+ int flags;
+ int value;
+ int texture_number; // gl bind number
+ vec3_t color; // for flat shade mode
+ qboolean inuse; // true = is present on the level
+} qtexture_t;
+
+
+// a texturename of the form (0 0 0) will
+// create a solid color texture
+
+void Texture_Init (void);
+void Texture_Flush (void);
+void Texture_ClearInuse (void);
+void Texture_ShowInuse (void);
+void Texture_ShowDirectory (int menunum);
+
+qtexture_t *Texture_ForName (char *name);
+
+void Texture_Init (void);
+void Texture_SetTexture (texdef_t *texdef);
+
+void Texture_SetMode(int iMenu); // GL_TEXTURE_NEAREST, etc..
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+int FindPoint (vec3_t point)
+{
+ int i, j;
+
+ for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ if (fabs(point[j] - g_qeglobals.d_points[i][j]) > 0.1)
+ break;
+ if (j == 3)
+ return i;
+ }
+
+ VectorCopy (point, g_qeglobals.d_points[g_qeglobals.d_numpoints]);
+ g_qeglobals.d_numpoints++;
+
+ return g_qeglobals.d_numpoints-1;
+}
+
+int FindEdge (int p1, int p2, face_t *f)
+{
+ int i;
+
+ for (i=0 ; i<g_qeglobals.d_numedges ; i++)
+ if (g_qeglobals.d_edges[i].p1 == p2 && g_qeglobals.d_edges[i].p2 == p1)
+ {
+ g_qeglobals.d_edges[i].f2 = f;
+ return i;
+ }
+
+ g_qeglobals.d_edges[g_qeglobals.d_numedges].p1 = p1;
+ g_qeglobals.d_edges[g_qeglobals.d_numedges].p2 = p2;
+ g_qeglobals.d_edges[g_qeglobals.d_numedges].f1 = f;
+ g_qeglobals.d_numedges++;
+
+ return g_qeglobals.d_numedges-1;
+}
+
+void MakeFace (face_t *f)
+{
+ winding_t *w;
+ int i;
+ int pnum[128];
+
+ w = MakeFaceWinding (selected_brushes.next, f);
+ if (!w)
+ return;
+ for (i=0 ; i<w->numpoints ; i++)
+ pnum[i] = FindPoint (w->points[i]);
+ for (i=0 ; i<w->numpoints ; i++)
+ FindEdge (pnum[i], pnum[(i+1)%w->numpoints], f);
+
+ free (w);
+}
+
+void SetupVertexSelection (void)
+{
+ face_t *f;
+ brush_t *b;
+
+ g_qeglobals.d_numpoints = 0;
+ g_qeglobals.d_numedges = 0;
+ if (!QE_SingleBrush())
+ return;
+ b = selected_brushes.next;
+ for (f=b->brush_faces ; f ; f=f->next)
+ MakeFace (f);
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+void SelectFaceEdge (face_t *f, int p1, int p2)
+{
+ winding_t *w;
+ int i, j, k;
+ int pnum[128];
+
+ w = MakeFaceWinding (selected_brushes.next, f);
+ if (!w)
+ return;
+ for (i=0 ; i<w->numpoints ; i++)
+ pnum[i] = FindPoint (w->points[i]);
+ for (i=0 ; i<w->numpoints ; i++)
+ if (pnum[i] == p1 && pnum[(i+1)%w->numpoints] == p2)
+ {
+ VectorCopy (g_qeglobals.d_points[pnum[i]], f->planepts[0]);
+ VectorCopy (g_qeglobals.d_points[pnum[(i+1)%w->numpoints]], f->planepts[1]);
+ VectorCopy (g_qeglobals.d_points[pnum[(i+2)%w->numpoints]], f->planepts[2]);
+ for (j=0 ; j<3 ; j++)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+ }
+ }
+
+ AddPlanept (f->planepts[0]);
+ AddPlanept (f->planepts[1]);
+ break;
+ }
+
+ if (i == w->numpoints)
+ Sys_Printf ("SelectFaceEdge: failed\n");
+ free (w);
+}
+
+void SelectVertex (int p1)
+{
+ brush_t *b;
+ winding_t *w;
+ int i, j, k;
+ face_t *f;
+
+ b = selected_brushes.next;
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ w = MakeFaceWinding (b, f);
+ if (!w)
+ continue;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ if (FindPoint (w->points[i]) == p1)
+ {
+ VectorCopy (w->points[(i+w->numpoints-1)%w->numpoints], f->planepts[0]);
+ VectorCopy (w->points[i], f->planepts[1]);
+ VectorCopy (w->points[(i+1)%w->numpoints], f->planepts[2]);
+ for (j=0 ; j<3 ; j++)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+ }
+ }
+
+ AddPlanept (f->planepts[1]);
+ break;
+ }
+ }
+ free (w);
+ }
+}
+
+void SelectEdgeByRay (vec3_t org, vec3_t dir)
+{
+ int i, j, besti;
+ float d, bestd;
+ vec3_t mid, temp;
+ pedge_t *e;
+
+ // find the edge closest to the ray
+ besti = -1;
+ bestd = 8;
+
+ for (i=0 ; i<g_qeglobals.d_numedges ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ mid[j] = 0.5*(g_qeglobals.d_points[g_qeglobals.d_edges[i].p1][j] + g_qeglobals.d_points[g_qeglobals.d_edges[i].p2][j]);
+
+ VectorSubtract (mid, org, temp);
+ d = DotProduct (temp, dir);
+ VectorMA (org, d, dir, temp);
+ VectorSubtract (mid, temp, temp);
+ d = VectorLength (temp);
+ if (d < bestd)
+ {
+ bestd = d;
+ besti = i;
+ }
+ }
+
+ if (besti == -1)
+ {
+ Sys_Printf ("Click didn't hit an edge\n");
+ return;
+ }
+ Sys_Printf ("hit edge\n");
+
+ // make the two faces that border the edge use the two edge points
+ // as primary drag points
+ g_qeglobals.d_num_move_points = 0;
+ e = &g_qeglobals.d_edges[besti];
+ SelectFaceEdge (e->f1, e->p1, e->p2);
+ SelectFaceEdge (e->f2, e->p2, e->p1);
+}
+
+void SelectVertexByRay (vec3_t org, vec3_t dir)
+{
+ int i, besti;
+ float d, bestd;
+ vec3_t temp;
+
+ // find the point closest to the ray
+ besti = -1;
+ bestd = 8;
+
+ for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
+ {
+ VectorSubtract (g_qeglobals.d_points[i], org, temp);
+ d = DotProduct (temp, dir);
+ VectorMA (org, d, dir, temp);
+ VectorSubtract (g_qeglobals.d_points[i], temp, temp);
+ d = VectorLength (temp);
+ if (d < bestd)
+ {
+ bestd = d;
+ besti = i;
+ }
+ }
+
+ if (besti == -1)
+ {
+ Sys_Printf ("Click didn't hit a vertex\n");
+ return;
+ }
+ Sys_Printf ("hit vertex\n");
+ SelectVertex (besti);
+}
+
+
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+typedef struct
+{
+ int x, y;
+ int w, h;
+
+ int redraws; // clear to 0 when need update
+
+ vec3_t color;
+
+ vec3_t origin;
+ vec3_t angles;
+ vec3_t forward, right, up;
+
+ int draw_filtered;
+ int draw_wire;
+ int draw_solid;
+ int draw_textured;
+ int draw_blend;
+} vieworg_t;
+
+extern vieworg_t *view_org;
+extern int keysdown;
+
+void View_Init (void);
+void View_Draw (void);
+void View_MouseDown (int x, int y, int buttons);
+void View_KeyUp (int key);
+void View_KeyDown (int key);
+void View_Reshape(int w, int h);
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// win_cam.c -- windows specific camera view code
+
+#include "qe3.h"
+
+/*
+============
+CameraWndProc
+============
+*/
+LONG WINAPI WCam_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ int fwKeys, xPos, yPos;
+ RECT rect;
+
+ GetClientRect(hWnd, &rect);
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ {
+ HFONT hfont;
+
+ g_qeglobals.d_hdcBase = GetDC(hWnd);
+ QEW_SetupPixelFormat(g_qeglobals.d_hdcBase, true);
+
+ if ( ( g_qeglobals.d_hglrcBase = wglCreateContext( g_qeglobals.d_hdcBase ) ) == 0 )
+ Error ("wglCreateContext failed");
+ if (!wglMakeCurrent( g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase ))
+ Error ("wglMakeCurrent failed");
+
+ Texture_SetMode(g_qeglobals.d_savedinfo.iTexMenu);
+
+ //
+ // create GL font
+ //
+ hfont = CreateFont(
+ 10, // logical height of font
+ 7, // logical average character width
+ 0, // angle of escapement
+ 0, // base-line orientation angle
+ 0, // font weight
+ 0, // italic attribute flag
+ 0, // underline attribute flag
+ 0, // strikeout attribute flag
+ 0, // character set identifier
+ 0, // output precision
+ 0, // clipping precision
+ 0, // output quality
+ 0, // pitch and family
+ 0 // pointer to typeface name string
+ );
+
+ if ( !hfont )
+ Error( "couldn't create font" );
+
+ SelectObject (g_qeglobals.d_hdcBase, hfont);
+
+ if ( ( g_qeglobals.d_font_list = glGenLists (256) ) == 0 )
+ Error( "couldn't create font dlists" );
+
+ // create the bitmap display lists
+ // we're making images of glyphs 0 thru 255
+ if ( !wglUseFontBitmaps (g_qeglobals.d_hdcBase, 1, 255, g_qeglobals.d_font_list) )
+ Error( "wglUseFontBitmaps faileD" );
+
+ // indicate start of glyph display lists
+ glListBase (g_qeglobals.d_font_list);
+
+ // report OpenGL information
+ Sys_Printf ("GL_VENDOR: %s\n", glGetString (GL_VENDOR));
+ Sys_Printf ("GL_RENDERER: %s\n", glGetString (GL_RENDERER));
+ Sys_Printf ("GL_VERSION: %s\n", glGetString (GL_VERSION));
+ Sys_Printf ("GL_EXTENSIONS: %s\n", glGetString (GL_EXTENSIONS));
+ }
+ return 0;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+
+ if (!wglMakeCurrent( g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase ))
+ Error ("wglMakeCurrent failed");
+
+ if ( BeginPaint(hWnd, &ps) )
+ {
+ QE_CheckOpenGLForErrors();
+ Cam_Draw ();
+ QE_CheckOpenGLForErrors();
+
+ EndPaint(hWnd, &ps);
+ SwapBuffers(g_qeglobals.d_hdcBase);
+ }
+ }
+ return 0;
+
+ case WM_USER+267: // benchmark
+ {
+ PAINTSTRUCT ps;
+ WINDOWPLACEMENT wp;
+ double start, end;
+ int i;
+
+ memset( &wp, 0, sizeof( wp ) );
+ wp.length = sizeof( wp );
+ GetWindowPlacement( g_qeglobals.d_hwndCamera, &wp );
+
+ MoveWindow( g_qeglobals.d_hwndCamera, 30, 30, 400, 400, TRUE );
+
+ BeginPaint(hWnd, &ps);
+ if (!wglMakeCurrent( g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase))
+ Error ("wglMakeCurrent failed");
+ glDrawBuffer (GL_FRONT);
+
+ start = Sys_DoubleTime ();
+ for (i=0 ; i<100 ; i++)
+ {
+ camera.angles[YAW] = i*4;
+ Cam_Draw ();
+ }
+ SwapBuffers(g_qeglobals.d_hdcBase);
+ glDrawBuffer (GL_BACK);
+ end = Sys_DoubleTime ();
+ EndPaint(hWnd, &ps);
+ Sys_Printf ("%5.2f seconds\n", end-start);
+
+ SetWindowPlacement( g_qeglobals.d_hwndCamera, &wp );
+ }
+ break;
+
+ case WM_KEYDOWN:
+ if ( QE_KeyDown (wParam) )
+ return 0;
+ else
+ return DefWindowProc( hWnd, uMsg, wParam, lParam );
+
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONDOWN:
+ if (GetTopWindow(g_qeglobals.d_hwndMain) != hWnd)
+ BringWindowToTop(hWnd);
+
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetCapture (g_qeglobals.d_hwndCamera);
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ Cam_MouseDown (xPos, yPos, fwKeys);
+ return 0;
+
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ Cam_MouseUp (xPos, yPos, fwKeys);
+ if (! (fwKeys & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+ ReleaseCapture ();
+ return 0;
+
+ case WM_MOUSEMOVE:
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ Cam_MouseMoved (xPos, yPos, fwKeys);
+ return 0;
+
+ case WM_SIZE:
+ camera.width = rect.right;
+ camera.height = rect.bottom;
+ InvalidateRect(g_qeglobals.d_hwndCamera, NULL, false);
+ return 0;
+
+ case WM_KILLFOCUS:
+ case WM_SETFOCUS:
+ SendMessage( hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0 );
+ return 0;
+
+ case WM_NCCALCSIZE:// don't let windows copy pixels
+ DefWindowProc (hWnd, uMsg, wParam, lParam);
+ return WVR_REDRAW;
+
+ case WM_CLOSE:
+ DestroyWindow (hWnd);
+ return 0;
+
+ case WM_DESTROY:
+ QEW_StopGL( hWnd, g_qeglobals.d_hglrcBase, g_qeglobals.d_hdcBase );
+ return 0;
+ }
+
+ return DefWindowProc( hWnd, uMsg, wParam, lParam );
+}
+
+
+/*
+==============
+WCam_Create
+==============
+*/
+void WCam_Create (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+ char *title;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WCam_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = CAMERA_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Error ("WCam_Register: failed");
+
+ if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_DETAIL )
+ title = "Camera View (DETAIL EXCLUDED)";
+ else
+ title = "Camera View";
+
+ g_qeglobals.d_hwndCamera = CreateWindow (CAMERA_WINDOW_CLASS ,
+ title,
+ QE3_STYLE,
+ ZWIN_WIDTH,
+ 20,
+ (int)(screen_width*CWIN_SIZE),
+ (int)(screen_height*CWIN_SIZE), // size
+
+ g_qeglobals.d_hwndMain, // parent window
+ 0, // no menu
+ hInstance,
+ 0);
+ if (!g_qeglobals.d_hwndCamera)
+ Error ("Couldn't create g_qeglobals.d_hwndCamera");
+
+ LoadWindowState(g_qeglobals.d_hwndCamera, "camerawindow");
+ ShowWindow (g_qeglobals.d_hwndCamera, SW_SHOWDEFAULT);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+BOOL CALLBACK GammaDlgProc (
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ )
+{
+ char sz[256];
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ sprintf(sz, "%1.1f", g_qeglobals.d_savedinfo.fGamma);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_G_EDIT), sz);
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+
+ case IDOK:
+ GetWindowText(GetDlgItem(hwndDlg, IDC_G_EDIT), sz, 255);
+ g_qeglobals.d_savedinfo.fGamma = atof(sz);
+ EndDialog(hwndDlg, 1);
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+
+void DoGamma(void)
+{
+ char *psz, sz[256];
+ if ( DialogBox(g_qeglobals.d_hInstance, (char *)IDD_GAMMA, g_qeglobals.d_hwndMain, GammaDlgProc))
+ {
+ psz = ValueForKey(world_entity, "_wad");
+ if (psz)
+ {
+ strcpy(sz, psz);
+ Texture_Flush();
+ Texture_ShowInuse();
+ }
+ }
+}
+
+//================================================
+
+
+void SelectBrush (int entitynum, int brushnum)
+{
+ entity_t *e;
+ brush_t *b;
+ int i;
+
+ if (entitynum == 0)
+ e = world_entity;
+ else
+ {
+ e = entities.next;
+ while (--entitynum)
+ {
+ e=e->next;
+ if (e == &entities)
+ {
+ Sys_Status ("No such entity.", 0);
+ return;
+ }
+ }
+ }
+
+ b = e->brushes.onext;
+ if (b == &e->brushes)
+ {
+ Sys_Status ("No such brush.", 0);
+ return;
+ }
+ while (brushnum--)
+ {
+ b=b->onext;
+ if (b == &e->brushes)
+ {
+ Sys_Status ("No such brush.", 0);
+ return;
+ }
+ }
+
+ Brush_RemoveFromList (b);
+ Brush_AddToList (b, &selected_brushes);
+
+
+ Sys_UpdateWindows (W_ALL);
+ for (i=0 ; i<3 ; i++)
+ g_qeglobals.d_xy.origin[i] = (b->mins[i] + b->maxs[i])/2;
+
+ Sys_Status ("Selected.", 0);
+}
+
+/*
+=================
+GetSelectionIndex
+=================
+*/
+void GetSelectionIndex (int *ent, int *brush)
+{
+ brush_t *b, *b2;
+ entity_t *entity;
+
+ *ent = *brush = 0;
+
+ b = selected_brushes.next;
+ if (b == &selected_brushes)
+ return;
+
+ // find entity
+ if (b->owner != world_entity)
+ {
+ (*ent)++;
+ for (entity = entities.next ; entity != &entities
+ ; entity=entity->next, (*ent)++)
+ ;
+ }
+
+ // find brush
+ for (b2=b->owner->brushes.onext
+ ; b2 != b && b2 != &b->owner->brushes
+ ; b2=b2->onext, (*brush)++)
+ ;
+}
+
+BOOL CALLBACK FindBrushDlgProc (
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ )
+{
+ char entstr[256];
+ char brushstr[256];
+ HWND h;
+ int ent, brush;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ // set entity and brush number
+ GetSelectionIndex (&ent, &brush);
+ sprintf (entstr, "%i", ent);
+ sprintf (brushstr, "%i", brush);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_FIND_ENTITY), entstr);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_FIND_BRUSH), brushstr);
+
+ h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY);
+ SetFocus (h);
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ GetWindowText(GetDlgItem(hwndDlg, IDC_FIND_ENTITY), entstr, 255);
+ GetWindowText(GetDlgItem(hwndDlg, IDC_FIND_BRUSH), brushstr, 255);
+ SelectBrush (atoi(entstr), atoi(brushstr));
+ EndDialog(hwndDlg, 1);
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+
+void DoFind(void)
+{
+ DialogBox(g_qeglobals.d_hInstance, (char *)IDD_FINDBRUSH, g_qeglobals.d_hwndMain, FindBrushDlgProc);
+}
+
+/*
+===================================================
+
+ ARBITRARY ROTATE
+
+===================================================
+*/
+
+
+BOOL CALLBACK RotateDlgProc (
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ )
+{
+ char str[256];
+ HWND h;
+ float v;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY);
+ SetFocus (h);
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+
+ case IDOK:
+ GetWindowText(GetDlgItem(hwndDlg, IDC_ROTX), str, 255);
+ v = atof(str);
+ if (v)
+ Select_RotateAxis (0, v);
+
+ GetWindowText(GetDlgItem(hwndDlg, IDC_ROTY), str, 255);
+ v = atof(str);
+ if (v)
+ Select_RotateAxis (1, v);
+
+ GetWindowText(GetDlgItem(hwndDlg, IDC_ROTZ), str, 255);
+ v = atof(str);
+ if (v)
+ Select_RotateAxis (2, v);
+
+ EndDialog(hwndDlg, 1);
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+void DoRotate(void)
+{
+ DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ROTATE, g_qeglobals.d_hwndMain, RotateDlgProc);
+}
+
+/*
+===================================================
+
+ ARBITRARY SIDES
+
+===================================================
+*/
+
+
+BOOL CALLBACK SidesDlgProc (
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ )
+{
+ char str[256];
+ HWND h;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY);
+ SetFocus (h);
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+
+ case IDOK:
+ GetWindowText(GetDlgItem(hwndDlg, IDC_SIDES), str, 255);
+ Brush_MakeSided (atoi(str));
+
+ EndDialog(hwndDlg, 1);
+ break;
+
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ break;
+ }
+ default:
+ return FALSE;
+ }
+}
+
+
+
+void DoSides(void)
+{
+ DialogBox(g_qeglobals.d_hInstance, (char *)IDD_SIDES, g_qeglobals.d_hwndMain, SidesDlgProc);
+}
+
+//======================================================================
+
+/*
+===================
+DoAbout
+===================
+*/
+BOOL CALLBACK AboutDlgProc( HWND hwndDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam )
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ char renderer[1024];
+ char version[1024];
+ char vendor[1024];
+ char extensions[4096];
+
+ sprintf( renderer, "Renderer:\t%s", glGetString( GL_RENDERER ) );
+ sprintf( version, "Version:\t\t%s", glGetString( GL_VERSION ) );
+ sprintf( vendor, "Vendor:\t\t%s", glGetString( GL_VENDOR ) );
+ sprintf( extensions, "\n%s", glGetString( GL_EXTENSIONS ) );
+
+ SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLRENDERER ), renderer );
+ SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLVERSION ), version );
+ SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLVENDOR ), vendor );
+ SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLEXTENSIONS ), extensions );
+ }
+ return TRUE;
+
+ case WM_CLOSE:
+ EndDialog( hwndDlg, 1 );
+ return TRUE;
+
+ case WM_COMMAND:
+ if ( LOWORD( wParam ) == IDOK )
+ EndDialog(hwndDlg, 1);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void DoAbout(void)
+{
+ DialogBox( g_qeglobals.d_hInstance, ( char * ) IDD_ABOUT, g_qeglobals.d_hwndMain, AboutDlgProc );
+}
+
+
+/*
+===================================================
+
+ SURFACE INSPECTOR
+
+===================================================
+*/
+
+texdef_t g_old_texdef;
+HWND g_surfwin;
+qboolean g_changed_surface;
+
+int g_checkboxes[64] = {
+ IDC_CHECK1, IDC_CHECK2, IDC_CHECK3, IDC_CHECK4,
+ IDC_CHECK5, IDC_CHECK6, IDC_CHECK7, IDC_CHECK8,
+ IDC_CHECK9, IDC_CHECK10, IDC_CHECK11, IDC_CHECK12,
+ IDC_CHECK13, IDC_CHECK14, IDC_CHECK15, IDC_CHECK16,
+ IDC_CHECK17, IDC_CHECK18, IDC_CHECK19, IDC_CHECK20,
+ IDC_CHECK21, IDC_CHECK22, IDC_CHECK23, IDC_CHECK24,
+ IDC_CHECK25, IDC_CHECK26, IDC_CHECK27, IDC_CHECK28,
+ IDC_CHECK29, IDC_CHECK30, IDC_CHECK31, IDC_CHECK32,
+
+ IDC_CHECK33, IDC_CHECK34, IDC_CHECK35, IDC_CHECK36,
+ IDC_CHECK37, IDC_CHECK38, IDC_CHECK39, IDC_CHECK40,
+ IDC_CHECK41, IDC_CHECK42, IDC_CHECK43, IDC_CHECK44,
+ IDC_CHECK45, IDC_CHECK46, IDC_CHECK47, IDC_CHECK48,
+ IDC_CHECK49, IDC_CHECK50, IDC_CHECK51, IDC_CHECK52,
+ IDC_CHECK53, IDC_CHECK54, IDC_CHECK55, IDC_CHECK56,
+ IDC_CHECK57, IDC_CHECK58, IDC_CHECK59, IDC_CHECK60,
+ IDC_CHECK61, IDC_CHECK62, IDC_CHECK63, IDC_CHECK64
+ };
+
+/*
+==============
+SetTexMods
+
+Set the fields to the current texdef
+===============
+*/
+void SetTexMods(void)
+{
+ char sz[128];
+ texdef_t *pt;
+ int i;
+
+ pt = &g_qeglobals.d_texturewin.texdef;
+
+ SendMessage (g_surfwin, WM_SETREDRAW, 0, 0);
+
+ SetWindowText(GetDlgItem(g_surfwin, IDC_TEXTURE), pt->name);
+
+ sprintf(sz, "%d", (int)pt->shift[0]);
+ SetWindowText(GetDlgItem(g_surfwin, IDC_HSHIFT), sz);
+
+ sprintf(sz, "%d", (int)pt->shift[1]);
+ SetWindowText(GetDlgItem(g_surfwin, IDC_VSHIFT), sz);
+
+ sprintf(sz, "%4.2f", pt->scale[0]);
+ SetWindowText(GetDlgItem(g_surfwin, IDC_HSCALE), sz);
+
+ sprintf(sz, "%4.2f", pt->scale[1]);
+ SetWindowText(GetDlgItem(g_surfwin, IDC_VSCALE), sz);
+
+ sprintf(sz, "%d", (int)pt->rotate);
+ SetWindowText(GetDlgItem(g_surfwin, IDC_ROTATE), sz);
+
+ sprintf(sz, "%d", (int)pt->value);
+ SetWindowText(GetDlgItem(g_surfwin, IDC_VALUE), sz);
+
+ for (i=0 ; i<32 ; i++)
+ SendMessage(GetDlgItem(g_surfwin, g_checkboxes[i]), BM_SETCHECK, !!(pt->flags&(1<<i)), 0 );
+ for (i=0 ; i<32 ; i++)
+ SendMessage(GetDlgItem(g_surfwin, g_checkboxes[32+i]), BM_SETCHECK, !!(pt->contents&(1<<i)), 0 );
+
+ SendMessage (g_surfwin, WM_SETREDRAW, 1, 0);
+ InvalidateRect (g_surfwin, NULL, true);
+}
+
+
+/*
+==============
+GetTexMods
+
+Reads the fields to get the current texdef
+===============
+*/
+void GetTexMods(void)
+{
+ char sz[128];
+ texdef_t *pt;
+ int b;
+ int i;
+
+ pt = &g_qeglobals.d_texturewin.texdef;
+
+ GetWindowText (GetDlgItem(g_surfwin, IDC_TEXTURE), sz, 127);
+ strncpy (pt->name, sz, sizeof(pt->name)-1);
+ if (pt->name[0] <= ' ')
+ {
+ strcpy (pt->name, "none");
+ SetWindowText(GetDlgItem(g_surfwin, IDC_TEXTURE), pt->name);
+ }
+
+ GetWindowText (GetDlgItem(g_surfwin, IDC_HSHIFT), sz, 127);
+ pt->shift[0] = atof(sz);
+
+ GetWindowText (GetDlgItem(g_surfwin, IDC_VSHIFT), sz, 127);
+ pt->shift[1] = atof(sz);
+
+ GetWindowText(GetDlgItem(g_surfwin, IDC_HSCALE), sz, 127);
+ pt->scale[0] = atof(sz);
+
+ GetWindowText(GetDlgItem(g_surfwin, IDC_VSCALE), sz, 127);
+ pt->scale[1] = atof(sz);
+
+ GetWindowText(GetDlgItem(g_surfwin, IDC_ROTATE), sz, 127);
+ pt->rotate = atof(sz);
+
+ GetWindowText(GetDlgItem(g_surfwin, IDC_VALUE), sz, 127);
+ pt->value = atof(sz);
+
+ pt->flags = 0;
+ for (i=0 ; i<32 ; i++)
+ {
+ b = SendMessage(GetDlgItem(g_surfwin, g_checkboxes[i]), BM_GETCHECK, 0, 0);
+ if (b != 1 && b != 0)
+ continue;
+ pt->flags |= b<<i;
+ }
+
+ pt->contents = 0;
+ for (i=0 ; i<32 ; i++)
+ {
+ b = SendMessage(GetDlgItem(g_surfwin, g_checkboxes[32+i]), BM_GETCHECK, 0, 0);
+ if (b != 1 && b != 0)
+ continue;
+ pt->contents |= b<<i;
+ }
+
+ g_changed_surface = true;
+ Select_SetTexture(pt);
+}
+
+/*
+=================
+UpdateSpinners
+=================
+*/
+void UpdateSpinners(unsigned uMsg, WPARAM wParam, LPARAM lParam)
+{
+ int nScrollCode;
+ HWND hwnd;
+ texdef_t *pt;
+
+ pt = &g_qeglobals.d_texturewin.texdef;
+
+ nScrollCode = (int) LOWORD(wParam); // scroll bar value
+ hwnd = (HWND) lParam; // handle of scroll bar
+
+ if ((nScrollCode != SB_LINEUP) && (nScrollCode != SB_LINEDOWN))
+ return;
+
+ if (hwnd == GetDlgItem(g_surfwin, IDC_ROTATEA))
+ {
+ if (nScrollCode == SB_LINEUP)
+ pt->rotate += 45;
+ else
+ pt->rotate -= 45;
+
+ if (pt->rotate < 0)
+ pt->rotate += 360;
+
+ if (pt->rotate >= 360)
+ pt->rotate -= 360;
+ }
+
+ else if (hwnd == GetDlgItem(g_surfwin, IDC_HSCALEA))
+ {
+ if (nScrollCode == SB_LINEDOWN)
+ pt->scale[0] -= 0.1;
+ else
+ pt->scale[0] += 0.1;
+ }
+
+ else if (hwnd == GetDlgItem(g_surfwin, IDC_VSCALEA))
+ {
+ if (nScrollCode == SB_LINEUP)
+ pt->scale[1] += 0.1;
+ else
+ pt->scale[1] -= 0.1;
+ }
+
+ else if (hwnd == GetDlgItem(g_surfwin, IDC_HSHIFTA))
+ {
+ if (nScrollCode == SB_LINEDOWN)
+ pt->shift[0] -= 8;
+ else
+ pt->shift[0] += 8;
+ }
+
+ else if (hwnd == GetDlgItem(g_surfwin, IDC_VSHIFTA))
+ {
+ if (nScrollCode == SB_LINEUP)
+ pt->shift[1] += 8;
+ else
+ pt->shift[1] -= 8;
+ }
+
+ SetTexMods();
+ g_changed_surface = true;
+ Select_SetTexture(pt);
+}
+
+
+
+BOOL CALLBACK SurfaceDlgProc (
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ )
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ g_surfwin = hwndDlg;
+ SetTexMods ();
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+
+ case IDOK:
+ GetTexMods ();
+ EndDialog(hwndDlg, 1);
+ break;
+
+ case IDAPPLY:
+ GetTexMods ();
+ InvalidateRect(g_qeglobals.d_hwndCamera, NULL, false);
+ UpdateWindow (g_qeglobals.d_hwndCamera);
+ break;
+
+ case IDCANCEL:
+ g_qeglobals.d_texturewin.texdef = g_old_texdef;
+ if (g_changed_surface)
+ Select_SetTexture(&g_qeglobals.d_texturewin.texdef);
+ EndDialog(hwndDlg, 0);
+ break;
+ }
+ break;
+
+ case WM_HSCROLL:
+ case WM_VSCROLL:
+ UpdateSpinners(uMsg, wParam, lParam);
+ InvalidateRect(g_qeglobals.d_hwndCamera, NULL, false);
+ UpdateWindow (g_qeglobals.d_hwndCamera);
+ return 0;
+
+ default:
+ return FALSE;
+ }
+}
+
+
+
+void DoSurface (void)
+{
+ // save current state for cancel
+ g_old_texdef = g_qeglobals.d_texturewin.texdef;
+ g_changed_surface = false;
+
+ DialogBox(g_qeglobals.d_hInstance, (char *)IDD_SURFACE, g_qeglobals.d_hwndMain, SurfaceDlgProc);
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include "entityw.h"
+
+int rgIds[EntLast] = {
+ IDC_E_LIST,
+ IDC_E_COMMENT,
+ IDC_CHECK1,
+ IDC_CHECK2,
+ IDC_CHECK3,
+ IDC_CHECK4,
+ IDC_CHECK5,
+ IDC_CHECK6,
+ IDC_CHECK7,
+ IDC_CHECK8,
+ IDC_CHECK9,
+ IDC_CHECK10,
+ IDC_CHECK11,
+ IDC_CHECK12,
+ IDC_E_PROPS,
+ IDC_E_0,
+ IDC_E_45,
+ IDC_E_90,
+ IDC_E_135,
+ IDC_E_180,
+ IDC_E_225,
+ IDC_E_270,
+ IDC_E_315,
+ IDC_E_UP,
+ IDC_E_DOWN,
+ IDC_E_DELPROP,
+
+ IDC_STATIC_KEY,
+ IDC_E_KEY_FIELD,
+ IDC_STATIC_VALUE,
+ IDC_E_VALUE_FIELD,
+
+ IDC_E_COLOR
+};
+
+HWND hwndEnt[EntLast];
+
+int inspector_mode; // W_TEXTURE, W_ENTITY, or W_CONSOLE
+
+qboolean multiple_entities;
+
+entity_t *edit_entity;
+
+HWND CreateTextureWindow (void);
+
+BOOL CALLBACK EntityWndProc(
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam); // second message parameter
+
+void SizeEntityDlg(int iWidth, int iHeight);
+void AddProp(void);
+void GetTexMods(void);
+
+
+LRESULT (CALLBACK* OldFieldWindowProc) (HWND, UINT, WPARAM, LPARAM);
+LRESULT (CALLBACK* OldEntityListWindowProc) (HWND, UINT, WPARAM, LPARAM);
+
+/*
+=========================
+FieldWndProc
+
+Just to handle tab and enter...
+=========================
+*/
+BOOL CALLBACK FieldWndProc(
+ HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_CHAR:
+ if (LOWORD(wParam) == VK_TAB)
+ return FALSE;
+ if (LOWORD(wParam) == VK_RETURN)
+ return FALSE;
+ if (LOWORD(wParam) == VK_ESCAPE)
+ {
+ SetFocus (g_qeglobals.d_hwndCamera);
+ return FALSE;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ if (LOWORD(wParam) == VK_TAB)
+ {
+ if (hwnd == hwndEnt[EntKeyField])
+ {
+ SendMessage (hwndEnt[EntValueField], WM_SETTEXT, 0, (long)"");
+ SetFocus (hwndEnt[EntValueField]);
+ }
+ else
+ SetFocus (hwndEnt[EntKeyField]);
+ }
+ if (LOWORD(wParam) == VK_RETURN)
+ {
+ if (hwnd == hwndEnt[EntKeyField])
+ {
+ SendMessage (hwndEnt[EntValueField], WM_SETTEXT, 0, (long)"");
+ SetFocus (hwndEnt[EntValueField]);
+ }
+ else
+ {
+ AddProp ();
+ SetFocus (g_qeglobals.d_hwndCamera);
+ }
+ }
+ break;
+// case WM_NCHITTEST:
+ case WM_LBUTTONDOWN:
+ SetFocus (hwnd);
+ break;
+ }
+ return CallWindowProc (OldFieldWindowProc, hwnd, uMsg, wParam, lParam);
+}
+
+
+/*
+=========================
+EntityListWndProc
+
+Just to handle enter...
+=========================
+*/
+BOOL CALLBACK EntityListWndProc(
+ HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_KEYDOWN:
+ if (LOWORD(wParam) == VK_RETURN)
+ {
+ SendMessage ( g_qeglobals.d_hwndEntity,
+ WM_COMMAND,
+ (LBN_DBLCLK<<16) + IDC_E_LIST,
+ 0 );
+ return 0;
+ }
+ break;
+ }
+ return CallWindowProc (OldEntityListWindowProc, hwnd, uMsg, wParam, lParam);
+}
+
+
+/*
+================
+GetEntityControls
+
+Finds the controls from the dialog and
+moves them to the window
+================
+*/
+void GetEntityControls(HWND ghwndEntity)
+{
+ int i;
+
+ for (i = 0; i < EntLast; i++)
+ {
+ if (i == EntList || i == EntProps || i == EntComment)
+ continue;
+ if (i == EntKeyField || i == EntValueField)
+ continue;
+ hwndEnt[i] = GetDlgItem(ghwndEntity, rgIds[i]);
+ if (hwndEnt[i])
+ SetParent (hwndEnt[i], g_qeglobals.d_hwndEntity );
+ }
+
+
+ // SetParent apears to not modify some internal state
+ // on listboxes, so create it from scratch...
+
+ hwndEnt[EntList] = CreateWindow ("listbox", NULL,
+ LBS_STANDARD | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT
+ | WS_VSCROLL | WS_CHILD | WS_VISIBLE,
+ 5, 5, 180, 99,
+ g_qeglobals.d_hwndEntity,
+ (void *)IDC_E_LIST,
+ g_qeglobals.d_hInstance,
+ NULL);
+ if (!hwndEnt[EntList])
+ Error ("CreateWindow failed");
+
+ hwndEnt[EntProps] = CreateWindow ("listbox", NULL,
+ LBS_STANDARD | LBS_NOINTEGRALHEIGHT | LBS_USETABSTOPS
+ | WS_VSCROLL | WS_CHILD | WS_VISIBLE,
+ 5, 100, 180, 99,
+ g_qeglobals.d_hwndEntity,
+ (void *)IDC_E_PROPS,
+ g_qeglobals.d_hInstance,
+ NULL);
+ if (!hwndEnt[EntProps])
+ Error ("CreateWindow failed");
+
+ hwndEnt[EntComment] = CreateWindow ("edit", NULL,
+ ES_MULTILINE | ES_READONLY | WS_VSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER,
+ 5, 100, 180, 99,
+ g_qeglobals.d_hwndEntity,
+ (void *)IDC_E_COMMENT,
+ g_qeglobals.d_hInstance,
+ NULL);
+ if (!hwndEnt[EntComment])
+ Error ("CreateWindow failed");
+
+ hwndEnt[EntKeyField] = CreateWindow ("edit", NULL,
+ WS_CHILD | WS_VISIBLE | WS_BORDER,
+ 5, 100, 180, 99,
+ g_qeglobals.d_hwndEntity,
+ (void *)IDC_E_KEY_FIELD,
+ g_qeglobals.d_hInstance,
+ NULL);
+ if (!hwndEnt[EntKeyField])
+ Error ("CreateWindow failed");
+
+ hwndEnt[EntValueField] = CreateWindow ("edit", NULL,
+ WS_CHILD | WS_VISIBLE | WS_BORDER,
+ 5, 100, 180, 99,
+ g_qeglobals.d_hwndEntity,
+ (void *)IDC_E_VALUE_FIELD,
+ g_qeglobals.d_hInstance,
+ NULL);
+ if (!hwndEnt[EntValueField])
+ Error ("CreateWindow failed");
+
+ g_qeglobals.d_hwndEdit = CreateWindow ("edit", NULL,
+ ES_MULTILINE | ES_READONLY | WS_VSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER,
+ 5, 100, 180, 99,
+ g_qeglobals.d_hwndEntity,
+ (void *)IDC_E_STATUS,
+ g_qeglobals.d_hInstance,
+ NULL);
+ if (!g_qeglobals.d_hwndEdit)
+ Error ("CreateWindow failed");
+
+ g_qeglobals.d_hwndTexture = CreateTextureWindow ();
+
+#if 0
+ for (i=0 ; i<12 ; i++)
+ {
+ hwndEnt[EntCheck1 + i] = CreateWindow ("button", NULL,
+ BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,
+ 5, 100, 180, 99,
+ entwindow,
+ (void *)IDC_E_STATUS,
+ main_instance,
+ NULL);
+ if (!hwndEnt[EntCheck1 + i])
+ Error ("CreateWindow failed");
+ }
+#endif
+}
+
+
+
+/*
+===============================================================
+
+ENTITY WINDOW
+
+===============================================================
+*/
+
+
+void FillClassList (void)
+{
+ eclass_t *pec;
+ int iIndex;
+
+ SendMessage(hwndEnt[EntList], LB_RESETCONTENT, 0 , 0);
+
+ for (pec = eclass ; pec ; pec = pec->next)
+ {
+ iIndex = SendMessage(hwndEnt[EntList], LB_ADDSTRING, 0 , (LPARAM)pec->name);
+ SendMessage(hwndEnt[EntList], LB_SETITEMDATA, iIndex, (LPARAM)pec);
+ }
+
+}
+
+
+/*
+==============
+WEnt_Create
+==============
+*/
+void WEnt_Create (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)EntityWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = GetStockObject (LTGRAY_BRUSH);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = ENT_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Error ("RegisterClass: failed");
+
+ g_qeglobals.d_hwndEntity = CreateWindow (ENT_WINDOW_CLASS ,
+ "Entity",
+ QE3_STYLE ,
+ 20,
+ 20,
+ 100,
+ 480, // size
+
+ g_qeglobals.d_hwndMain, // parent
+ 0, // no menu
+ hInstance,
+ NULL);
+
+ if (!g_qeglobals.d_hwndEntity )
+ Error ("Couldn't create Entity window");
+}
+
+/*
+==============
+CreateEntityWindow
+==============
+*/
+BOOL CreateEntityWindow(HINSTANCE hInstance)
+{
+ HWND hwndEntityPalette;
+
+ inspector_mode = W_ENTITY;
+
+ WEnt_Create (hInstance);
+
+ hwndEntityPalette = CreateDialog(hInstance, (char *)IDD_ENTITY, g_qeglobals.d_hwndMain, (DLGPROC)NULL);
+ if (!hwndEntityPalette)
+ Error ("CreateDialog failed");
+
+ GetEntityControls (hwndEntityPalette);
+ DestroyWindow (hwndEntityPalette);
+
+ OldFieldWindowProc = (void *)GetWindowLong (hwndEnt[EntKeyField], GWL_WNDPROC);
+ SetWindowLong (hwndEnt[EntKeyField], GWL_WNDPROC, (long)FieldWndProc);
+ SetWindowLong (hwndEnt[EntValueField], GWL_WNDPROC, (long)FieldWndProc);
+
+ OldEntityListWindowProc = (void *)GetWindowLong (hwndEnt[EntList], GWL_WNDPROC);
+ SetWindowLong (hwndEnt[EntList], GWL_WNDPROC, (long)EntityListWndProc);
+
+ FillClassList ();
+
+ LoadWindowState(g_qeglobals.d_hwndEntity, "EntityWindow");
+
+ ShowWindow (g_qeglobals.d_hwndEntity, SW_SHOW);
+ SetInspectorMode (W_CONSOLE);
+
+ return TRUE;
+}
+
+/*
+==============
+SetInspectorMode
+==============
+*/
+void SetInspectorMode(int iType)
+{
+ RECT rc;
+ HMENU hMenu = GetMenu( g_qeglobals.d_hwndMain );
+
+ // Is the caller asking us to cycle to the next window?
+
+ if (iType == -1)
+ {
+ if (inspector_mode == W_ENTITY)
+ iType = W_TEXTURE;
+ else if (inspector_mode == W_TEXTURE)
+ iType = W_CONSOLE;
+ else
+ iType = W_ENTITY;
+ }
+
+ inspector_mode = iType;
+ switch(iType)
+ {
+
+ case W_ENTITY:
+ SetWindowText(g_qeglobals.d_hwndEntity, "Entity");
+ EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_ENABLED | MF_BYCOMMAND );
+ break;
+
+ case W_TEXTURE:
+// title is set by textures.c SetWindowText(g_qeglobals.d_hwndEntity, "Textures");
+ EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND );
+ break;
+
+ case W_CONSOLE:
+ SetWindowText(g_qeglobals.d_hwndEntity, "Console");
+ EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND );
+ break;
+
+ default:
+ break;
+ }
+
+ GetWindowRect (g_qeglobals.d_hwndEntity, &rc);
+ SizeEntityDlg( rc.right - rc.left - 8, rc.bottom - rc.top - 32);
+
+ RedrawWindow (g_qeglobals.d_hwndEntity, NULL, NULL, RDW_ERASE | RDW_INVALIDATE
+ | RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+// InvalidateRect(entwindow, NULL, true);
+// ShowWindow (entwindow, SW_SHOW);
+// UpdateWindow (entwindow);
+
+ SetWindowPos( g_qeglobals.d_hwndEntity,
+ HWND_TOP,
+ rc.left, rc.top,
+ rc.right - rc.left, rc.bottom - rc.top,
+ SWP_NOSIZE | SWP_NOMOVE );
+}
+
+
+
+
+
+// SetKeyValuePairs
+//
+// Reset the key/value (aka property) listbox and fill it with the
+// k/v pairs from the entity being edited.
+//
+
+void SetKeyValuePairs (void)
+{
+ epair_t *pep;
+ RECT rc;
+ char sz[4096];
+
+ if (edit_entity == NULL)
+ return;
+
+ // set key/value pair list
+
+ GetWindowRect(hwndEnt[EntProps], &rc);
+ SendMessage(hwndEnt[EntProps], LB_SETCOLUMNWIDTH, (rc.right - rc.left)/2, 0);
+ SendMessage(hwndEnt[EntProps], LB_RESETCONTENT, 0, 0);
+
+ // Walk through list and add pairs
+
+ for (pep = edit_entity->epairs ; pep ; pep = pep->next)
+ {
+ // if the key is less than 8 chars, add a tab for alignment
+ if (strlen(pep->key) > 8)
+ sprintf (sz, "%s\t%s", pep->key, pep->value);
+ else
+ sprintf (sz, "%s\t\t%s", pep->key, pep->value);
+ SendMessage(hwndEnt[EntProps], LB_ADDSTRING, 0, (LPARAM)sz);
+ }
+
+}
+
+// SetSpawnFlags
+//
+// Update the checkboxes to reflect the flag state of the entity
+//
+void SetSpawnFlags(void)
+{
+ int f;
+ int i;
+ int v;
+
+ f = atoi(ValueForKey (edit_entity, "spawnflags"));
+ for (i=0 ; i<12 ; i++)
+ {
+ v = !!(f&(1<<i));
+ SendMessage(hwndEnt[EntCheck1+i], BM_SETCHECK, v, 0);
+ }
+}
+
+
+// GetSpawnFlags
+//
+// Update the entity flags to reflect the state of the checkboxes
+//
+void GetSpawnFlags(void)
+{
+ int f;
+ int i, v;
+ char sz[32];
+
+ f = 0;
+ for (i=0 ; i<12 ; i++)
+ {
+ v = SendMessage(hwndEnt[EntCheck1+i], BM_GETCHECK, 0, 0);
+ f |= v<<i;
+ }
+
+ sprintf (sz, "%i", f);
+
+ if (multiple_entities)
+ {
+ brush_t *b;
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ SetKeyValue(b->owner, "spawnflags", sz);
+ }
+ else
+ SetKeyValue (edit_entity, "spawnflags", sz);
+ SetKeyValuePairs ();
+}
+
+// UpdateSel
+//
+// Update the listbox, checkboxes and k/v pairs to reflect the new selection
+//
+
+BOOL UpdateSel(int iIndex, eclass_t *pec)
+{
+ int i;
+ brush_t *b;
+
+ if (selected_brushes.next == &selected_brushes)
+ {
+ edit_entity = world_entity;
+ multiple_entities = false;
+ }
+ else
+ {
+ edit_entity = selected_brushes.next->owner;
+ for (b=selected_brushes.next->next ; b != &selected_brushes ; b=b->next)
+ {
+ if (b->owner != edit_entity)
+ {
+ multiple_entities = true;
+ break;
+ }
+ }
+ }
+
+ if (iIndex != LB_ERR)
+ SendMessage(hwndEnt[EntList], LB_SETCURSEL, iIndex, 0);
+
+ if (pec == NULL)
+ return TRUE;
+
+ // Set up the description
+
+ SendMessage(hwndEnt[EntComment], WM_SETTEXT, 0,
+ (LPARAM)TranslateString(pec->comments));
+
+ for (i=0 ; i<8 ; i++)
+ {
+ HWND hwnd = hwndEnt[EntCheck1+i];
+ if (pec->flagnames[i] && pec->flagnames[i][0] != 0)
+ {
+ EnableWindow(hwnd, TRUE);
+ SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)pec->flagnames[i]);
+ } else {
+
+ // disable check box
+ SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)" ");
+ EnableWindow(hwnd, FALSE);
+ }
+ }
+
+ SetSpawnFlags();
+ SetKeyValuePairs();
+ return TRUE;
+}
+
+BOOL UpdateEntitySel(eclass_t *pec)
+{
+ int iIndex;
+
+ iIndex = (int)SendMessage(hwndEnt[EntList], LB_FINDSTRINGEXACT,
+ (WPARAM)-1, (LPARAM)pec->name);
+
+ return UpdateSel(iIndex, pec);
+}
+
+// CreateEntity
+//
+// Creates a new entity based on the currently selected brush and entity type.
+//
+
+void CreateEntity(void)
+{
+ eclass_t *pecNew;
+ entity_t *petNew;
+ int i;
+ HWND hwnd;
+ char sz[1024];
+
+ // check to make sure we have a brush
+
+ if (selected_brushes.next == &selected_brushes)
+ {
+ MessageBox(g_qeglobals.d_hwndMain, "You must have a selected brush to create an entity"
+ , "info", 0);
+ return;
+ }
+
+
+ // find out what type of entity we are trying to create
+
+ hwnd = hwndEnt[EntList];
+
+ i = SendMessage(hwndEnt[EntList], LB_GETCURSEL, 0, 0);
+
+ if (i < 0)
+ {
+ MessageBox(g_qeglobals.d_hwndMain, "You must have a selected class to create an entity"
+ , "info", 0);
+ return;
+ }
+
+ SendMessage(hwnd, LB_GETTEXT, i, (LPARAM)sz);
+
+ if (!stricmp(sz, "worldspawn"))
+ {
+ MessageBox(g_qeglobals.d_hwndMain, "Can't create an entity with worldspawn.", "info", 0);
+ return;
+ }
+
+ pecNew = Eclass_ForName(sz, false);
+
+ // create it
+
+ petNew = Entity_Create(pecNew);
+
+ if (petNew == NULL)
+ {
+ MessageBox(g_qeglobals.d_hwndMain, "Failed to create entity.", "info", 0);
+ return;
+ }
+
+ if (selected_brushes.next == &selected_brushes)
+ edit_entity = world_entity;
+ else
+ edit_entity = selected_brushes.next->owner;
+
+ SetKeyValuePairs();
+ Select_Deselect ();
+ Select_Brush (edit_entity->brushes.onext);
+}
+
+
+
+/*
+===============
+AddProp
+
+===============
+*/
+void AddProp(void)
+{
+ char key[4096];
+ char value[4096];
+
+ if (edit_entity == NULL)
+ return;
+
+ // Get current selection text
+
+ SendMessage(hwndEnt[EntKeyField], WM_GETTEXT, sizeof(key)-1, (LPARAM)key);
+ SendMessage(hwndEnt[EntValueField], WM_GETTEXT, sizeof(value)-1, (LPARAM)value);
+
+ if (multiple_entities)
+ {
+ brush_t *b;
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ SetKeyValue(b->owner, key, value);
+ }
+ else
+ SetKeyValue(edit_entity, key, value);
+
+ // refresh the prop listbox
+
+ SetKeyValuePairs();
+}
+
+/*
+===============
+DelProp
+
+===============
+*/
+void DelProp(void)
+{
+ char sz[4096];
+
+ if (edit_entity == NULL)
+ return;
+
+ // Get current selection text
+
+ SendMessage(hwndEnt[EntKeyField], WM_GETTEXT, sizeof(sz)-1, (LPARAM)sz);
+
+ if (multiple_entities)
+ {
+ brush_t *b;
+
+ for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+ DeleteKey(b->owner, sz);
+ }
+ else
+ DeleteKey(edit_entity, sz);
+
+ // refresh the prop listbox
+
+ SetKeyValuePairs();
+}
+
+/*
+===============
+EditProp
+
+===============
+*/
+void EditProp(void)
+{
+ int i;
+ HWND hwnd;
+ char sz[4096];
+ char *val;
+
+ if (edit_entity == NULL)
+ return;
+
+ hwnd = hwndEnt[EntProps];
+
+ // Get current selection text
+
+ i = SendMessage(hwnd, LB_GETCURSEL, 0, 0);
+
+ if (i < 0)
+ return;
+
+ SendMessage(hwnd, LB_GETTEXT, i, (LPARAM)sz);
+
+ // strip it down to the key name
+
+ for(i=0;sz[i] != '\t';i++)
+ ;
+
+ sz[i] = '\0';
+
+ val = sz + i + 1;
+ if (*val == '\t')
+ val++;
+
+ SendMessage(hwndEnt[EntKeyField], WM_SETTEXT, 0, (LPARAM)sz);
+ SendMessage(hwndEnt[EntValueField], WM_SETTEXT, 0, (LPARAM)val);
+}
+
+
+HDWP defer;
+int col;
+void MOVE(HWND e, int x, int y, int w, int h)
+{
+// defer=DeferWindowPos(defer,e,HWND_TOP,col+(x),y,w,h,SWP_SHOWWINDOW);
+// MoveWindow (e, col+x, y, w, h, FALSE);
+ SetWindowPos (e, HWND_TOP, col+x, y, w, h,
+ SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOZORDER);
+}
+
+
+/*
+===============
+SizeEnitityDlg
+
+Positions all controls so that the active inspector
+is displayed correctly and the inactive ones are
+off the side
+===============
+*/
+void SizeEntityDlg(int iWidth, int iHeight)
+{
+ int y, x, xCheck, yCheck;
+ int i, iRow;
+ int w, h;
+
+ if (iWidth < 32 || iHeight < 32)
+ return;
+
+ SendMessage( g_qeglobals.d_hwndEntity, WM_SETREDRAW, 0, 0);
+
+ //==========================================
+
+ //
+ // console
+ //
+
+ if (inspector_mode == W_CONSOLE)
+ col = 0;
+ else
+ col = iWidth;
+
+ MOVE(g_qeglobals.d_hwndEdit, DlgXBorder, DlgYBorder, iWidth - (2 * DlgXBorder), iHeight - (2 * DlgYBorder) );
+
+ //==========================================
+
+ //
+ // texture controls
+ //
+ if (inspector_mode == W_TEXTURE)
+ col = 0;
+ else
+ col = iWidth;
+
+ MOVE(g_qeglobals.d_hwndTexture, DlgXBorder, DlgYBorder, iWidth - (2 * DlgXBorder), iHeight - (2 * DlgYBorder) );
+
+ //==========================================
+
+ //
+ // entity controls
+ //
+ if (inspector_mode == W_ENTITY)
+ col = 0;
+ else
+ col = iWidth;
+
+
+ // top half includes the entity list (2/3) and the
+ // comments (1/3) - 2 gaps, above and below.
+
+ y = iHeight/2;
+ y -= 2 * DlgYBorder;
+ y = y / 3;
+ w = iWidth - (2 * DlgXBorder);
+ MOVE(hwndEnt[EntList], DlgXBorder, DlgYBorder, w, 2 * y);
+
+ MOVE(hwndEnt[EntComment],
+ DlgXBorder, 2 * DlgYBorder + 2 * y,
+ w, y - (2 * DlgYBorder));
+
+ // bottom half includes flags (fixed), k/v pairs,
+ // and buttons (fixed).
+
+ // xCheck = width of a single check box
+ // yCheck = distance from top of one check to the next
+
+ xCheck = (iWidth - (2 * DlgXBorder)) / 3;
+ yCheck = 20;
+
+ x = DlgXBorder;
+
+ for (iRow = 0; iRow <= 12; iRow += 4)
+ {
+ y = iHeight/2;
+
+ for (i = 0; i < 4; i++)
+ {
+ MOVE(hwndEnt[EntCheck1 + i + iRow],
+ x, y, xCheck, yCheck);
+ y += yCheck;
+ }
+
+ x += xCheck;
+ }
+
+ //
+ // properties scroll box
+ //
+ y = iHeight/2 + 4 * yCheck;
+
+ w = iWidth - (2 * DlgXBorder);
+ h = (iHeight - (yCheck * 5 + 2 * DlgYBorder) ) - y;
+
+ MOVE(hwndEnt[EntProps], DlgXBorder, y, w, h);
+
+ y += h + DlgYBorder;
+
+ //
+ // key / value fields
+ //
+ w = iWidth-(DlgXBorder+45);
+ MOVE(hwndEnt[EntKeyLabel], DlgXBorder, y, 40, yCheck);
+ MOVE(hwndEnt[EntKeyField], DlgXBorder+40, y, w, yCheck);
+ y += yCheck;
+
+ MOVE(hwndEnt[EntValueLabel], DlgXBorder, y, 40, yCheck);
+ MOVE(hwndEnt[EntValueField], DlgXBorder+40, y, w, yCheck);
+ y += yCheck;
+
+ //
+ // angle check boxes
+ //
+ i = y;
+ x = DlgXBorder;
+
+ xCheck = yCheck*2;
+
+ MOVE(hwndEnt[EntDir135], x, y, xCheck, yCheck);
+ y += yCheck;
+
+ MOVE(hwndEnt[EntDir180], x, y, xCheck, yCheck);
+ y += yCheck;
+
+ MOVE(hwndEnt[EntDir225], x, y, xCheck, yCheck);
+
+ y = i;
+ x += xCheck;
+
+
+ MOVE(hwndEnt[EntDir90], x, y, xCheck, yCheck);
+ y += yCheck;
+ y += yCheck;
+
+ MOVE(hwndEnt[EntDir270], x, y, xCheck, yCheck);
+
+ y = i;
+ x += xCheck;
+
+
+ MOVE(hwndEnt[EntDir45], x, y, xCheck, yCheck);
+ y += yCheck;
+
+ MOVE(hwndEnt[EntDir0], x, y, xCheck, yCheck);
+ y += yCheck;
+
+ MOVE(hwndEnt[EntDir315], x, y, xCheck, yCheck);
+
+ y = i + yCheck/2;
+ x += xCheck + xCheck/2;
+
+
+ MOVE(hwndEnt[EntDirUp], x, y, xCheck, yCheck);
+ y += yCheck;
+
+ MOVE(hwndEnt[EntDirDown], x, y, xCheck, yCheck);
+
+ y = i;
+ x += 1.5 * xCheck;
+
+ MOVE(hwndEnt[EntDelProp], x, y, xCheck*2, yCheck);
+ y += yCheck;
+
+ SendMessage( g_qeglobals.d_hwndEntity, WM_SETREDRAW, 1, 0);
+// InvalidateRect(entwindow, NULL, TRUE);
+}
+
+
+/*
+=========================
+EntityWndProc
+=========================
+*/
+BOOL CALLBACK EntityWndProc(
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam) // second message parameter
+{
+ RECT rc;
+
+ GetClientRect(hwndDlg, &rc);
+
+ switch (uMsg)
+ {
+ case WM_GETMINMAXINFO:
+ {
+ LPMINMAXINFO lpmmi;
+
+ lpmmi = (LPMINMAXINFO) lParam;
+ lpmmi->ptMinTrackSize.x = 320;
+ lpmmi->ptMinTrackSize.y = 500;
+ }
+ return 0;
+
+ case WM_WINDOWPOSCHANGING:
+ {
+ LPWINDOWPOS lpwp;
+ lpwp = (LPWINDOWPOS) lParam;
+
+ DefWindowProc (hwndDlg, uMsg, wParam, lParam);
+
+ lpwp->flags |= SWP_NOCOPYBITS;
+ SizeEntityDlg(lpwp->cx-8, lpwp->cy-32);
+ return 0;
+
+ }
+ return 0;
+
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_E_DELPROP:
+ DelProp();
+ SetFocus (g_qeglobals.d_hwndCamera);
+ break;
+
+ case IDC_E_0:
+ SetKeyValue (edit_entity, "angle", "0");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_45:
+ SetKeyValue (edit_entity, "angle", "45");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_90:
+ SetKeyValue (edit_entity, "angle", "90");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_135:
+ SetKeyValue (edit_entity, "angle", "135");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_180:
+ SetKeyValue (edit_entity, "angle", "180");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_225:
+ SetKeyValue (edit_entity, "angle", "225");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_270:
+ SetKeyValue (edit_entity, "angle", "270");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_315:
+ SetKeyValue (edit_entity, "angle", "315");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_UP:
+ SetKeyValue (edit_entity, "angle", "-1");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+ case IDC_E_DOWN:
+ SetKeyValue (edit_entity, "angle", "-2");
+ SetFocus (g_qeglobals.d_hwndCamera);
+ SetKeyValuePairs ();
+ break;
+
+ case IDC_CHECK1:
+ case IDC_CHECK2:
+ case IDC_CHECK3:
+ case IDC_CHECK4:
+ case IDC_CHECK5:
+ case IDC_CHECK6:
+ case IDC_CHECK7:
+ case IDC_CHECK8:
+ case IDC_CHECK9:
+ case IDC_CHECK10:
+ case IDC_CHECK11:
+ case IDC_CHECK12:
+ GetSpawnFlags();
+ SetFocus (g_qeglobals.d_hwndCamera);
+ break;
+
+
+ case IDC_E_PROPS:
+ switch (HIWORD(wParam))
+ {
+ case LBN_SELCHANGE:
+
+ EditProp();
+ return TRUE;
+ }
+ break;
+
+ case IDC_E_LIST:
+
+ switch (HIWORD(wParam)) {
+
+ case LBN_SELCHANGE:
+ {
+ int iIndex;
+ eclass_t *pec;
+
+ iIndex = SendMessage(hwndEnt[EntList], LB_GETCURSEL, 0, 0);
+ pec = (eclass_t *)SendMessage(hwndEnt[EntList], LB_GETITEMDATA,
+ iIndex, 0);
+
+ UpdateSel(iIndex, pec);
+
+ return TRUE;
+ break;
+ }
+
+ case LBN_DBLCLK:
+ CreateEntity ();
+ SetFocus (g_qeglobals.d_hwndCamera);
+ break;
+ }
+ break;
+
+
+ default:
+ return DefWindowProc( hwndDlg, uMsg, wParam, lParam );
+ }
+
+ return 0;
+ }
+
+ return DefWindowProc (hwndDlg, uMsg, wParam, lParam);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include <process.h>
+#include "mru.h"
+#include "entityw.h"
+
+static HWND s_hwndToolbar;
+
+BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize);
+BOOL LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize);
+
+static HWND CreateMyStatusWindow(HINSTANCE hInst);
+static HWND CreateToolBar(HINSTANCE hinst);
+
+extern int WXY_Print( void );
+
+/*
+==============================================================================
+
+ MENU
+
+==============================================================================
+*/
+
+void OpenDialog (void);
+void SaveAsDialog (void);
+qboolean ConfirmModified (void);
+void Select_Ungroup (void);
+
+void QE_ExpandBspString (char *bspaction, char *out, char *mapname)
+{
+ char *in;
+ char src[1024];
+ char rsh[1024];
+ char base[256];
+
+ ExtractFileName (mapname, base);
+ sprintf (src, "%s/maps/%s", ValueForKey(g_qeglobals.d_project_entity, "remotebasepath"), base);
+ strcpy (rsh, ValueForKey(g_qeglobals.d_project_entity, "rshcmd"));
+
+ in = ValueForKey( g_qeglobals.d_project_entity, bspaction );
+ while (*in)
+ {
+ if (in[0] == '!')
+ {
+ strcpy (out, rsh);
+ out += strlen(rsh);
+ in++;
+ continue;
+ }
+ if (in[0] == '$')
+ {
+ strcpy (out, src);
+ out += strlen(src);
+ in++;
+ continue;
+ }
+ if (in[0] == '@')
+ {
+ *out++ = '"';
+ in++;
+ continue;
+ }
+ *out++ = *in++;
+ }
+ *out = 0;
+}
+
+
+
+void RunBsp (char *command)
+{
+ char sys[1024];
+ char batpath[1024];
+ char outputpath[1024];
+ char temppath[512];
+ char name[1024];
+ FILE *hFile;
+ BOOL ret;
+ PROCESS_INFORMATION ProcessInformation;
+ STARTUPINFO startupinfo;
+
+ SetInspectorMode (W_CONSOLE);
+
+ if (bsp_process)
+ {
+ Sys_Printf ("BSP is still going...\n");
+ return;
+ }
+
+ GetTempPath(512, temppath);
+ sprintf (outputpath, "%sjunk.txt", temppath);
+
+ strcpy (name, currentmap);
+ if (region_active)
+ {
+ Map_SaveFile (name, false);
+ StripExtension (name);
+ strcat (name, ".reg");
+ }
+
+ Map_SaveFile (name, region_active);
+
+
+ QE_ExpandBspString (command, sys, name);
+
+ Sys_ClearPrintf ();
+ Sys_Printf ("======================================\nRunning bsp command...\n");
+ Sys_Printf ("\n%s\n", sys);
+
+ //
+ // write qe3bsp.bat
+ //
+ sprintf (batpath, "%sqe3bsp.bat", temppath);
+ hFile = fopen(batpath, "w");
+ if (!hFile)
+ Error ("Can't write to %s", batpath);
+ fprintf (hFile, sys);
+ fclose (hFile);
+
+ //
+ // write qe3bsp2.bat
+ //
+ sprintf (batpath, "%sqe3bsp2.bat", temppath);
+ hFile = fopen(batpath, "w");
+ if (!hFile)
+ Error ("Can't write to %s", batpath);
+ fprintf (hFile, "%sqe3bsp.bat > %s", temppath, outputpath);
+ fclose (hFile);
+
+ Pointfile_Delete ();
+
+ GetStartupInfo (&startupinfo);
+
+ ret = CreateProcess(
+ batpath, // pointer to name of executable module
+ NULL, // pointer to command line string
+ NULL, // pointer to process security attributes
+ NULL, // pointer to thread security attributes
+ FALSE, // handle inheritance flag
+ 0 /*DETACHED_PROCESS*/, // creation flags
+ NULL, // pointer to new environment block
+ NULL, // pointer to current directory name
+ &startupinfo, // pointer to STARTUPINFO
+ &ProcessInformation // pointer to PROCESS_INFORMATION
+ );
+
+ if (!ret)
+ Error ("CreateProcess failed");
+
+ bsp_process = ProcessInformation.hProcess;
+
+ Sleep (100); // give the new process a chance to open it's window
+
+ BringWindowToTop( g_qeglobals.d_hwndMain ); // pop us back on top
+ SetFocus (g_qeglobals.d_hwndCamera);
+}
+
+/*
+=============
+DoColor
+
+=============
+*/
+qboolean DoColor(int iIndex)
+{
+ CHOOSECOLOR cc;
+ static COLORREF custom[16];
+
+ cc.lStructSize = sizeof(cc);
+ cc.hwndOwner = g_qeglobals.d_hwndMain;
+ cc.hInstance = g_qeglobals.d_hInstance;
+ cc.rgbResult =
+ (int)(g_qeglobals.d_savedinfo.colors[iIndex][0]*255) +
+ (((int)(g_qeglobals.d_savedinfo.colors[iIndex][1]*255))<<8) +
+ (((int)(g_qeglobals.d_savedinfo.colors[iIndex][2]*255))<<16);
+ cc.lpCustColors = custom;
+ cc.Flags = CC_FULLOPEN|CC_RGBINIT;
+ //cc.lCustData;
+ //cc.lpfnHook;
+ //cc.lpTemplateName
+
+ if (!ChooseColor(&cc))
+ return false;
+
+ g_qeglobals.d_savedinfo.colors[iIndex][0] = (cc.rgbResult&255)/255.0;
+ g_qeglobals.d_savedinfo.colors[iIndex][1] = ((cc.rgbResult>>8)&255)/255.0;
+ g_qeglobals.d_savedinfo.colors[iIndex][2] = ((cc.rgbResult>>16)&255)/255.0;
+
+ /*
+ ** scale colors so that at least one component is at 1.0F
+ ** if this is meant to select an entity color
+ */
+ if ( iIndex == COLOR_ENTITY )
+ {
+ float largest = 0.0F;
+
+ if ( g_qeglobals.d_savedinfo.colors[iIndex][0] > largest )
+ largest = g_qeglobals.d_savedinfo.colors[iIndex][0];
+ if ( g_qeglobals.d_savedinfo.colors[iIndex][1] > largest )
+ largest = g_qeglobals.d_savedinfo.colors[iIndex][1];
+ if ( g_qeglobals.d_savedinfo.colors[iIndex][2] > largest )
+ largest = g_qeglobals.d_savedinfo.colors[iIndex][2];
+
+ if ( largest == 0.0F )
+ {
+ g_qeglobals.d_savedinfo.colors[iIndex][0] = 1.0F;
+ g_qeglobals.d_savedinfo.colors[iIndex][1] = 1.0F;
+ g_qeglobals.d_savedinfo.colors[iIndex][2] = 1.0F;
+ }
+ else
+ {
+ float scaler = 1.0F / largest;
+
+ g_qeglobals.d_savedinfo.colors[iIndex][0] *= scaler;
+ g_qeglobals.d_savedinfo.colors[iIndex][1] *= scaler;
+ g_qeglobals.d_savedinfo.colors[iIndex][2] *= scaler;
+ }
+ }
+
+ Sys_UpdateWindows (W_ALL);
+
+ return true;
+}
+
+
+/* Copied from MSDN */
+
+BOOL DoMru(HWND hWnd,WORD wId)
+{
+ char szFileName[128];
+ OFSTRUCT of;
+ BOOL fExist;
+
+ GetMenuItem(g_qeglobals.d_lpMruMenu, wId, TRUE, szFileName, sizeof(szFileName));
+
+ // Test if the file exists.
+
+ fExist = OpenFile(szFileName ,&of,OF_EXIST) != HFILE_ERROR;
+
+ if (fExist) {
+
+ // Place the file on the top of MRU.
+ AddNewItem(g_qeglobals.d_lpMruMenu,(LPSTR)szFileName);
+
+ // Now perform opening this file !!!
+ Map_LoadFile (szFileName);
+ }
+ else
+ // Remove the file on MRU.
+ DelMenuItem(g_qeglobals.d_lpMruMenu,wId,TRUE);
+
+ // Refresh the File menu.
+ PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(hWnd),0),
+ ID_FILE_EXIT);
+
+ return fExist;
+}
+
+
+/* handle all WM_COMMAND messages here */
+LONG WINAPI CommandHandler (
+ HWND hWnd,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ HMENU hMenu;
+
+ switch (LOWORD(wParam))
+ {
+//
+// file menu
+//
+ case ID_FILE_EXIT:
+ /* exit application */
+ if (!ConfirmModified())
+ return TRUE;
+
+ PostMessage (hWnd, WM_CLOSE, 0, 0L);
+ break;
+
+ case ID_FILE_OPEN:
+ if (!ConfirmModified())
+ return TRUE;
+ OpenDialog ();
+ break;
+
+ case ID_FILE_NEW:
+ if (!ConfirmModified())
+ return TRUE;
+ Map_New ();
+ break;
+ case ID_FILE_SAVE:
+ if (!strcmp(currentmap, "unnamed.map"))
+ SaveAsDialog ();
+ else
+ Map_SaveFile (currentmap, false); // ignore region
+ break;
+ case ID_FILE_SAVEAS:
+ SaveAsDialog ();
+ break;
+
+ case ID_FILE_LOADPROJECT:
+ if (!ConfirmModified())
+ return TRUE;
+ ProjectDialog ();
+ break;
+
+ case ID_FILE_POINTFILE:
+ if (g_qeglobals.d_pointfile_display_list)
+ Pointfile_Clear ();
+ else
+ Pointfile_Check ();
+ break;
+
+//
+// view menu
+//
+ case ID_VIEW_ENTITY:
+ SetInspectorMode(W_ENTITY);
+ break;
+ case ID_VIEW_CONSOLE:
+ SetInspectorMode(W_CONSOLE);
+ break;
+ case ID_VIEW_TEXTURE:
+ SetInspectorMode(W_TEXTURE);
+ break;
+
+ case ID_VIEW_100:
+ g_qeglobals.d_xy.scale = 1;
+ Sys_UpdateWindows (W_XY|W_XY_OVERLAY);
+ break;
+ case ID_VIEW_ZOOMIN:
+ g_qeglobals.d_xy.scale *= 5.0/4;
+ if (g_qeglobals.d_xy.scale > 16)
+ g_qeglobals.d_xy.scale = 16;
+ Sys_UpdateWindows (W_XY|W_XY_OVERLAY);
+ break;
+ case ID_VIEW_ZOOMOUT:
+ g_qeglobals.d_xy.scale *= 4.0/5;
+ if (g_qeglobals.d_xy.scale < 0.1)
+ g_qeglobals.d_xy.scale = 0.1;
+ Sys_UpdateWindows (W_XY|W_XY_OVERLAY);
+ break;
+
+ case ID_VIEW_Z100:
+ z.scale = 1;
+ Sys_UpdateWindows (W_Z|W_Z_OVERLAY);
+ break;
+ case ID_VIEW_ZZOOMIN:
+ z.scale *= 5.0/4;
+ if (z.scale > 4)
+ z.scale = 4;
+ Sys_UpdateWindows (W_Z|W_Z_OVERLAY);
+ break;
+ case ID_VIEW_ZZOOMOUT:
+ z.scale *= 4.0/5;
+ if (z.scale < 0.125)
+ z.scale = 0.125;
+ Sys_UpdateWindows (W_Z|W_Z_OVERLAY);
+ break;
+
+ case ID_VIEW_CENTER:
+ camera.angles[ROLL] = camera.angles[PITCH] = 0;
+ camera.angles[YAW] = 22.5 *
+ floor( (camera.angles[YAW]+11)/22.5 );
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ break;
+
+ case ID_VIEW_UPFLOOR:
+ Cam_ChangeFloor (true);
+ break;
+ case ID_VIEW_DOWNFLOOR:
+ Cam_ChangeFloor (false);
+ break;
+
+ case ID_VIEW_SHOWNAMES:
+ g_qeglobals.d_savedinfo.show_names = !g_qeglobals.d_savedinfo.show_names;
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWNAMES, MF_BYCOMMAND | (g_qeglobals.d_savedinfo.show_names ? MF_CHECKED : MF_UNCHECKED) );
+ Map_BuildBrushData();
+ Sys_UpdateWindows (W_XY);
+ break;
+
+ case ID_VIEW_SHOWCOORDINATES:
+ g_qeglobals.d_savedinfo.show_coordinates ^= 1;
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | (g_qeglobals.d_savedinfo.show_coordinates ? MF_CHECKED : MF_UNCHECKED) );
+ Sys_UpdateWindows (W_XY);
+ break;
+
+ case ID_VIEW_SHOWBLOCKS:
+ g_qeglobals.show_blocks ^= 1;
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWBLOCKS, MF_BYCOMMAND | (g_qeglobals.show_blocks ? MF_CHECKED : MF_UNCHECKED) );
+ Sys_UpdateWindows (W_XY);
+ break;
+
+ case ID_VIEW_SHOWLIGHTS:
+ if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_LIGHTS ) & EXCLUDE_LIGHTS )
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED );
+ else
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_CHECKED );
+ Sys_UpdateWindows (W_XY|W_CAMERA);
+ break;
+
+ case ID_VIEW_SHOWPATH:
+ if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_PATHS ) & EXCLUDE_PATHS )
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED );
+ else
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_CHECKED );
+ Sys_UpdateWindows (W_XY|W_CAMERA);
+ break;
+
+ case ID_VIEW_SHOWENT:
+ if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_ENT ) & EXCLUDE_ENT )
+ CheckMenuItem( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_UNCHECKED);
+ else
+ CheckMenuItem( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_CHECKED);
+ Sys_UpdateWindows (W_XY|W_CAMERA);
+ break;
+
+ case ID_VIEW_SHOWWATER:
+ if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_WATER ) & EXCLUDE_WATER )
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED );
+ else
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_CHECKED );
+ Sys_UpdateWindows (W_XY|W_CAMERA);
+ break;
+
+ case ID_VIEW_SHOWCLIP:
+ if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_CLIP ) & EXCLUDE_CLIP )
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED );
+ else
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_CHECKED );
+ Sys_UpdateWindows (W_XY|W_CAMERA);
+ break;
+
+ case ID_VIEW_SHOWDETAIL:
+ if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_DETAIL ) & EXCLUDE_DETAIL )
+ {
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWDETAIL, MF_BYCOMMAND | MF_UNCHECKED );
+ SetWindowText (g_qeglobals.d_hwndCamera, "Camera View (DETAIL EXCLUDED)");
+ }
+ else
+ {
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWDETAIL, MF_BYCOMMAND | MF_CHECKED );
+ SetWindowText (g_qeglobals.d_hwndCamera, "Camera View");
+ }
+ Sys_UpdateWindows (W_XY|W_CAMERA);
+ break;
+
+ case ID_VIEW_SHOWWORLD:
+ if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_WORLD ) & EXCLUDE_WORLD )
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED );
+ else
+ CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_CHECKED );
+ Sys_UpdateWindows (W_XY|W_CAMERA);
+ break;
+
+
+//
+// grid menu
+//
+ case ID_GRID_1:
+ case ID_GRID_2:
+ case ID_GRID_4:
+ case ID_GRID_8:
+ case ID_GRID_16:
+ case ID_GRID_32:
+ case ID_GRID_64:
+ {
+ hMenu = GetMenu(hWnd);
+
+ CheckMenuItem(hMenu, ID_GRID_1, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_GRID_2, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_GRID_4, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_GRID_8, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_GRID_16, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_GRID_32, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_GRID_64, MF_BYCOMMAND | MF_UNCHECKED);
+
+ switch (LOWORD(wParam))
+ {
+ case ID_GRID_1: g_qeglobals.d_gridsize = 0; break;
+ case ID_GRID_2: g_qeglobals.d_gridsize = 1; break;
+ case ID_GRID_4: g_qeglobals.d_gridsize = 2; break;
+ case ID_GRID_8: g_qeglobals.d_gridsize = 3; break;
+ case ID_GRID_16: g_qeglobals.d_gridsize = 4; break;
+ case ID_GRID_32: g_qeglobals.d_gridsize = 5; break;
+ case ID_GRID_64: g_qeglobals.d_gridsize = 6; break;
+ }
+ g_qeglobals.d_gridsize = 1 << g_qeglobals.d_gridsize;
+
+ CheckMenuItem(hMenu, LOWORD(wParam), MF_BYCOMMAND | MF_CHECKED);
+ Sys_UpdateWindows (W_XY|W_Z);
+ break;
+ }
+
+//
+// texture menu
+//
+ case ID_VIEW_NEAREST:
+ case ID_VIEW_NEARESTMIPMAP:
+ case ID_VIEW_LINEAR:
+ case ID_VIEW_BILINEAR:
+ case ID_VIEW_BILINEARMIPMAP:
+ case ID_VIEW_TRILINEAR:
+ case ID_TEXTURES_WIREFRAME:
+ case ID_TEXTURES_FLATSHADE:
+ Texture_SetMode (LOWORD(wParam));
+ break;
+
+ case ID_TEXTURES_SHOWINUSE:
+ Sys_BeginWait ();
+ Texture_ShowInuse ();
+ SetInspectorMode(W_TEXTURE);
+ break;
+
+ case ID_TEXTURES_INSPECTOR:
+ DoSurface ();
+ break;
+
+ case CMD_TEXTUREWAD:
+ case CMD_TEXTUREWAD+1:
+ case CMD_TEXTUREWAD+2:
+ case CMD_TEXTUREWAD+3:
+ case CMD_TEXTUREWAD+4:
+ case CMD_TEXTUREWAD+5:
+ case CMD_TEXTUREWAD+6:
+ case CMD_TEXTUREWAD+7:
+ case CMD_TEXTUREWAD+8:
+ case CMD_TEXTUREWAD+9:
+ case CMD_TEXTUREWAD+10:
+ case CMD_TEXTUREWAD+11:
+ case CMD_TEXTUREWAD+12:
+ case CMD_TEXTUREWAD+13:
+ case CMD_TEXTUREWAD+14:
+ case CMD_TEXTUREWAD+15:
+ case CMD_TEXTUREWAD+16:
+ case CMD_TEXTUREWAD+17:
+ case CMD_TEXTUREWAD+18:
+ case CMD_TEXTUREWAD+19:
+ case CMD_TEXTUREWAD+20:
+ case CMD_TEXTUREWAD+21:
+ case CMD_TEXTUREWAD+22:
+ case CMD_TEXTUREWAD+23:
+ case CMD_TEXTUREWAD+24:
+ case CMD_TEXTUREWAD+25:
+ case CMD_TEXTUREWAD+26:
+ case CMD_TEXTUREWAD+27:
+ case CMD_TEXTUREWAD+28:
+ case CMD_TEXTUREWAD+29:
+ case CMD_TEXTUREWAD+30:
+ case CMD_TEXTUREWAD+31:
+ Sys_BeginWait ();
+ Texture_ShowDirectory (LOWORD(wParam));
+ SetInspectorMode(W_TEXTURE);
+ break;
+
+//
+// bsp menu
+//
+ case CMD_BSPCOMMAND:
+ case CMD_BSPCOMMAND+1:
+ case CMD_BSPCOMMAND+2:
+ case CMD_BSPCOMMAND+3:
+ case CMD_BSPCOMMAND+4:
+ case CMD_BSPCOMMAND+5:
+ case CMD_BSPCOMMAND+6:
+ case CMD_BSPCOMMAND+7:
+ case CMD_BSPCOMMAND+8:
+ case CMD_BSPCOMMAND+9:
+ case CMD_BSPCOMMAND+10:
+ case CMD_BSPCOMMAND+11:
+ case CMD_BSPCOMMAND+12:
+ case CMD_BSPCOMMAND+13:
+ case CMD_BSPCOMMAND+14:
+ case CMD_BSPCOMMAND+15:
+ case CMD_BSPCOMMAND+16:
+ case CMD_BSPCOMMAND+17:
+ case CMD_BSPCOMMAND+18:
+ case CMD_BSPCOMMAND+19:
+ case CMD_BSPCOMMAND+20:
+ case CMD_BSPCOMMAND+21:
+ case CMD_BSPCOMMAND+22:
+ case CMD_BSPCOMMAND+23:
+ case CMD_BSPCOMMAND+24:
+ case CMD_BSPCOMMAND+25:
+ case CMD_BSPCOMMAND+26:
+ case CMD_BSPCOMMAND+27:
+ case CMD_BSPCOMMAND+28:
+ case CMD_BSPCOMMAND+29:
+ case CMD_BSPCOMMAND+30:
+ case CMD_BSPCOMMAND+31:
+ {
+ extern char *bsp_commands[256];
+
+ RunBsp (bsp_commands[LOWORD(wParam-CMD_BSPCOMMAND)]);
+ }
+ break;
+
+//
+// misc menu
+//
+ case ID_MISC_BENCHMARK:
+ SendMessage ( g_qeglobals.d_hwndCamera,
+ WM_USER+267, 0, 0);
+ break;
+
+ case ID_TEXTUREBK:
+ DoColor(COLOR_TEXTUREBACK);
+ Sys_UpdateWindows (W_ALL);
+ break;
+
+ case ID_MISC_SELECTENTITYCOLOR:
+ {
+ extern int inspector_mode;
+
+ if ( ( inspector_mode == W_ENTITY ) && DoColor(COLOR_ENTITY) == true )
+ {
+ extern void AddProp( void );
+
+ char buffer[100];
+
+ sprintf( buffer, "%f %f %f", g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][0],
+ g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][1],
+ g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][2] );
+
+ SetWindowText( hwndEnt[EntValueField], buffer );
+ SetWindowText( hwndEnt[EntKeyField], "_color" );
+ AddProp();
+ }
+ Sys_UpdateWindows( W_ALL );
+ }
+ break;
+
+ case ID_MISC_PRINTXY:
+ WXY_Print();
+ break;
+
+ case ID_COLORS_XYBK:
+ DoColor(COLOR_GRIDBACK);
+ Sys_UpdateWindows (W_ALL);
+ break;
+
+ case ID_COLORS_MAJOR:
+ DoColor(COLOR_GRIDMAJOR);
+ Sys_UpdateWindows (W_ALL);
+ break;
+
+ case ID_COLORS_MINOR:
+ DoColor(COLOR_GRIDMINOR);
+ Sys_UpdateWindows (W_ALL);
+ break;
+
+ case ID_MISC_GAMMA:
+ DoGamma();
+ break;
+
+ case ID_MISC_FINDBRUSH:
+ DoFind();
+ break;
+
+ case ID_MISC_NEXTLEAKSPOT:
+ Pointfile_Next();
+ break;
+ case ID_MISC_PREVIOUSLEAKSPOT:
+ Pointfile_Prev();
+ break;
+
+//
+// brush menu
+//
+ case ID_BRUSH_3SIDED:
+ Brush_MakeSided (3);
+ break;
+ case ID_BRUSH_4SIDED:
+ Brush_MakeSided (4);
+ break;
+ case ID_BRUSH_5SIDED:
+ Brush_MakeSided (5);
+ break;
+ case ID_BRUSH_6SIDED:
+ Brush_MakeSided (6);
+ break;
+ case ID_BRUSH_7SIDED:
+ Brush_MakeSided (7);
+ break;
+ case ID_BRUSH_8SIDED:
+ Brush_MakeSided (8);
+ break;
+ case ID_BRUSH_9SIDED:
+ Brush_MakeSided (9);
+ break;
+ case ID_BRUSH_ARBITRARYSIDED:
+ DoSides ();
+ break;
+
+//
+// select menu
+//
+ case ID_BRUSH_FLIPX:
+ Select_FlipAxis (0);
+ break;
+ case ID_BRUSH_FLIPY:
+ Select_FlipAxis (1);
+ break;
+ case ID_BRUSH_FLIPZ:
+ Select_FlipAxis (2);
+ break;
+ case ID_BRUSH_ROTATEX:
+ Select_RotateAxis (0, 90);
+ break;
+ case ID_BRUSH_ROTATEY:
+ Select_RotateAxis (1, 90);
+ break;
+ case ID_BRUSH_ROTATEZ:
+ Select_RotateAxis (2, 90);
+ break;
+
+ case ID_SELECTION_ARBITRARYROTATION:
+ DoRotate ();
+ break;
+
+ case ID_SELECTION_UNGROUPENTITY:
+ Select_Ungroup ();
+ break;
+
+ case ID_SELECTION_CONNECT:
+ ConnectEntities ();
+ break;
+
+ case ID_SELECTION_DRAGVERTECIES:
+ if (g_qeglobals.d_select_mode == sel_vertex)
+ {
+ g_qeglobals.d_select_mode = sel_brush;
+ Sys_UpdateWindows (W_ALL);
+ }
+ else
+ {
+ SetupVertexSelection ();
+ if (g_qeglobals.d_numpoints)
+ g_qeglobals.d_select_mode = sel_vertex;
+ }
+ break;
+ case ID_SELECTION_DRAGEDGES:
+ if (g_qeglobals.d_select_mode == sel_edge)
+ {
+ g_qeglobals.d_select_mode = sel_brush;
+ Sys_UpdateWindows (W_ALL);
+ }
+ else
+ {
+ SetupVertexSelection ();
+ if (g_qeglobals.d_numpoints)
+ g_qeglobals.d_select_mode = sel_edge;
+ }
+ break;
+
+ case ID_SELECTION_SELECTPARTIALTALL:
+ Select_PartialTall ();
+ break;
+ case ID_SELECTION_SELECTCOMPLETETALL:
+ Select_CompleteTall ();
+ break;
+ case ID_SELECTION_SELECTTOUCHING:
+ Select_Touching ();
+ break;
+ case ID_SELECTION_SELECTINSIDE:
+ Select_Inside ();
+ break;
+ case ID_SELECTION_CSGSUBTRACT:
+ CSG_Subtract ();
+ break;
+ case ID_SELECTION_MAKEHOLLOW:
+ CSG_MakeHollow ();
+ break;
+
+ case ID_SELECTION_CLONE:
+ Select_Clone ();
+ break;
+ case ID_SELECTION_DELETE:
+ Select_Delete ();
+ break;
+ case ID_SELECTION_DESELECT:
+ Select_Deselect ();
+ break;
+
+ case ID_SELECTION_MAKE_DETAIL:
+ Select_MakeDetail ();
+ break;
+ case ID_SELECTION_MAKE_STRUCTURAL:
+ Select_MakeStructural ();
+ break;
+
+
+//
+// region menu
+//
+ case ID_REGION_OFF:
+ Map_RegionOff ();
+ break;
+ case ID_REGION_SETXY:
+ Map_RegionXY ();
+ break;
+ case ID_REGION_SETTALLBRUSH:
+ Map_RegionTallBrush ();
+ break;
+ case ID_REGION_SETBRUSH:
+ Map_RegionBrush ();
+ break;
+ case ID_REGION_SETSELECTION:
+ Map_RegionSelectedBrushes ();
+ break;
+
+ case IDMRU+1:
+ case IDMRU+2:
+ case IDMRU+3:
+ case IDMRU+4:
+ case IDMRU+5:
+ case IDMRU+6:
+ case IDMRU+7:
+ case IDMRU+8:
+ case IDMRU+9:
+ DoMru(hWnd,LOWORD(wParam));
+ break;
+
+//
+// help menu
+//
+
+ case ID_HELP_ABOUT:
+ DoAbout();
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+============
+WMAIN_WndProc
+============
+*/
+LONG WINAPI WMAIN_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ LONG lRet = 1;
+ RECT rect;
+ HDC maindc;
+
+ GetClientRect(hWnd, &rect);
+
+ switch (uMsg)
+ {
+ case WM_TIMER:
+ QE_CountBrushesAndUpdateStatusBar();
+ QE_CheckAutoSave();
+ return 0;
+
+ case WM_DESTROY:
+ SaveMruInReg(g_qeglobals.d_lpMruMenu,"Software\\id\\QuakeEd4\\MRU");
+ DeleteMruMenu(g_qeglobals.d_lpMruMenu);
+ PostQuitMessage(0);
+ KillTimer( hWnd, QE_TIMER0 );
+ return 0;
+
+ case WM_CREATE:
+ maindc = GetDC(hWnd);
+// QEW_SetupPixelFormat(maindc, false);
+ g_qeglobals.d_lpMruMenu = CreateMruMenuDefault();
+ LoadMruInReg(g_qeglobals.d_lpMruMenu,"Software\\id\\QuakeEd4\\MRU");
+
+ // Refresh the File menu.
+ PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(hWnd),0),
+ ID_FILE_EXIT);
+
+ return 0;
+
+ case WM_SIZE:
+ // resize the status window
+ MoveWindow( g_qeglobals.d_hwndStatus, -100, 100, 10, 10, true);
+ return 0;
+
+ case WM_KEYDOWN:
+ return QE_KeyDown (wParam);
+
+ case WM_CLOSE:
+ /* call destroy window to cleanup and go away */
+ SaveWindowState(g_qeglobals.d_hwndXY, "xywindow");
+ SaveWindowState(g_qeglobals.d_hwndCamera, "camerawindow");
+ SaveWindowState(g_qeglobals.d_hwndZ, "zwindow");
+ SaveWindowState(g_qeglobals.d_hwndEntity, "EntityWindow");
+ SaveWindowState(g_qeglobals.d_hwndMain, "mainwindow");
+
+ // FIXME: is this right?
+ SaveRegistryInfo("SavedInfo", &g_qeglobals.d_savedinfo, sizeof(g_qeglobals.d_savedinfo));
+ DestroyWindow (hWnd);
+ return 0;
+
+ case WM_COMMAND:
+ return CommandHandler (hWnd, wParam, lParam);
+ return 0;
+ }
+
+ return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+
+
+
+/*
+==============
+Main_Create
+==============
+*/
+void Main_Create (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+ int i;
+ HMENU hMenu;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WMAIN_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
+ wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
+ wc.lpszClassName = "QUAKE_MAIN";
+
+ if (!RegisterClass (&wc) )
+ Error ("WCam_Register: failed");
+
+
+ g_qeglobals.d_hwndMain = CreateWindow ("QUAKE_MAIN" ,
+ "QuakeEd 3",
+ WS_OVERLAPPEDWINDOW |
+ WS_CLIPSIBLINGS |
+ WS_CLIPCHILDREN,
+ 0,0,screen_width,screen_height+GetSystemMetrics(SM_CYSIZE), // size
+ 0,
+ 0, // no menu
+ hInstance,
+ NULL);
+ if (!g_qeglobals.d_hwndMain)
+ Error ("Couldn't create main window");
+
+ /* create a timer so that we can count brushes */
+ SetTimer( g_qeglobals.d_hwndMain,
+ QE_TIMER0,
+ 1000,
+ NULL );
+
+ LoadWindowState(g_qeglobals.d_hwndMain, "mainwindow");
+
+ s_hwndToolbar = CreateToolBar(hInstance);
+
+ g_qeglobals.d_hwndStatus = CreateMyStatusWindow(hInstance);
+
+ //
+ // load misc info from registry
+ //
+ i = sizeof(g_qeglobals.d_savedinfo);
+ LoadRegistryInfo("SavedInfo", &g_qeglobals.d_savedinfo, &i);
+
+ if (g_qeglobals.d_savedinfo.iSize != sizeof(g_qeglobals.d_savedinfo))
+ {
+ // fill in new defaults
+
+ g_qeglobals.d_savedinfo.iSize = sizeof(g_qeglobals.d_savedinfo);
+ g_qeglobals.d_savedinfo.fGamma = 1.0;
+ g_qeglobals.d_savedinfo.iTexMenu = ID_VIEW_NEAREST;
+
+ g_qeglobals.d_savedinfo.exclude = 0;
+ g_qeglobals.d_savedinfo.show_coordinates = true;
+ g_qeglobals.d_savedinfo.show_names = true;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.25;
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 1.0;
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.75;
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.5;
+ g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25;
+ }
+ }
+
+ if ( ( hMenu = GetMenu( g_qeglobals.d_hwndMain ) ) != 0 )
+ {
+ /*
+ ** by default all of these are checked because that's how they're defined in the menu editor
+ */
+ if ( !g_qeglobals.d_savedinfo.show_names )
+ CheckMenuItem( hMenu, ID_VIEW_SHOWNAMES, MF_BYCOMMAND | MF_UNCHECKED );
+ if ( !g_qeglobals.d_savedinfo.show_coordinates )
+ CheckMenuItem( hMenu, ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | MF_UNCHECKED );
+
+ if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS )
+ CheckMenuItem( hMenu, ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED );
+ if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT )
+ CheckMenuItem( hMenu, ID_VIEW_ENTITY, MF_BYCOMMAND | MF_UNCHECKED );
+ if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS )
+ CheckMenuItem( hMenu, ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED );
+ if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WATER )
+ CheckMenuItem( hMenu, ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED );
+ if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD )
+ CheckMenuItem( hMenu, ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED );
+ if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP )
+ CheckMenuItem( hMenu, ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED );
+ }
+
+ ShowWindow (g_qeglobals.d_hwndMain, SW_SHOWDEFAULT);
+}
+
+
+/*
+=============================================================
+
+REGISTRY INFO
+
+=============================================================
+*/
+
+BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize)
+{
+ LONG lres;
+ DWORD dwDisp;
+ HKEY hKeyId;
+
+ lres = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\id\\QuakeEd4", 0, NULL,
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyId, &dwDisp);
+
+ if (lres != ERROR_SUCCESS)
+ return FALSE;
+
+ lres = RegSetValueEx(hKeyId, pszName, 0, REG_BINARY, pvBuf, lSize);
+
+ RegCloseKey(hKeyId);
+
+ if (lres != ERROR_SUCCESS)
+ return FALSE;
+
+ return TRUE;
+}
+
+BOOL LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize)
+{
+ HKEY hKey;
+ long lres, lType, lSize;
+
+ if (plSize == NULL)
+ plSize = &lSize;
+
+ lres = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\id\\QuakeEd4", 0, KEY_READ, &hKey);
+
+ if (lres != ERROR_SUCCESS)
+ return FALSE;
+
+ lres = RegQueryValueEx(hKey, pszName, NULL, &lType, pvBuf, plSize);
+
+ RegCloseKey(hKey);
+
+ if (lres != ERROR_SUCCESS)
+ return FALSE;
+
+ return TRUE;
+}
+
+BOOL SaveWindowState(HWND hWnd, const char *pszName)
+{
+ RECT rc;
+
+ GetWindowRect(hWnd, &rc);
+ if (hWnd != g_qeglobals.d_hwndMain)
+ MapWindowPoints(NULL, g_qeglobals.d_hwndMain, (POINT *)&rc, 2);
+ return SaveRegistryInfo(pszName, &rc, sizeof(rc));
+}
+
+
+BOOL LoadWindowState(HWND hWnd, const char *pszName)
+{
+ RECT rc;
+ LONG lSize = sizeof(rc);
+
+ if (LoadRegistryInfo(pszName, &rc, &lSize))
+ {
+ if (rc.left < 0)
+ rc.left = 0;
+ if (rc.top < 0)
+ rc.top = 0;
+ if (rc.right < rc.left + 16)
+ rc.right = rc.left + 16;
+ if (rc.bottom < rc.top + 16)
+ rc.bottom = rc.top + 16;
+
+ MoveWindow(hWnd, rc.left, rc.top, rc.right - rc.left,
+ rc.bottom - rc.top, FALSE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+===============================================================
+
+ STATUS WINDOW
+
+===============================================================
+*/
+
+void Sys_UpdateStatusBar( void )
+{
+ extern int g_numbrushes, g_numentities;
+
+ char numbrushbuffer[100]="";
+
+ sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities );
+
+ Sys_Status( numbrushbuffer, 2 );
+}
+
+void Sys_Status(const char *psz, int part )
+{
+ SendMessage(g_qeglobals.d_hwndStatus, SB_SETTEXT, part, (LPARAM)psz);
+}
+
+static HWND CreateMyStatusWindow(HINSTANCE hInst)
+{
+ HWND hWnd;
+ int partsize[3] = { 300, 1100, -1 };
+
+ hWnd = CreateWindowEx( WS_EX_TOPMOST, // no extended styles
+ STATUSCLASSNAME, // status bar
+ "", // no text
+ WS_CHILD | WS_BORDER | WS_VISIBLE, // styles
+ -100, -100, 10, 10, // x, y, cx, cy
+ g_qeglobals.d_hwndMain, // parent window
+ (HMENU)100, // window ID
+ hInst, // instance
+ NULL); // window data
+
+ SendMessage( hWnd, SB_SETPARTS, 3, ( long ) partsize );
+
+ return hWnd;
+}
+
+//==============================================================
+
+#define NUMBUTTONS 15
+HWND CreateToolBar(HINSTANCE hinst)
+{
+ HWND hwndTB;
+ TBADDBITMAP tbab;
+ TBBUTTON tbb[NUMBUTTONS];
+
+ // Ensure that the common control DLL is loaded.
+
+ InitCommonControls();
+
+ // Create a toolbar that the user can customize and that has a
+ // tooltip associated with it.
+
+ hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL,
+ WS_CHILD | TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE | WS_BORDER,
+ 0, 0, 0, 0, g_qeglobals.d_hwndMain, (HMENU) IDR_TOOLBAR1, hinst, NULL);
+
+ // Send the TB_BUTTONSTRUCTSIZE message, which is required for
+ // backward compatibility.
+
+ SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
+
+ // Add the bitmap containing button images to the toolbar.
+
+ tbab.hInst = hinst;
+ tbab.nID = IDR_TOOLBAR1;
+ SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM)NUMBUTTONS, (WPARAM) &tbab);
+
+ // Fill the TBBUTTON array with button information, and add the
+ // buttons to the toolbar.
+
+ tbb[0].iBitmap = 0;
+ tbb[0].idCommand = ID_BRUSH_FLIPX;
+ tbb[0].fsState = TBSTATE_ENABLED;
+ tbb[0].fsStyle = TBSTYLE_BUTTON;
+ tbb[0].dwData = 0;
+ tbb[0].iString = 0;
+
+ tbb[1].iBitmap = 2;
+ tbb[1].idCommand = ID_BRUSH_FLIPY;
+ tbb[1].fsState = TBSTATE_ENABLED;
+ tbb[1].fsStyle = TBSTYLE_BUTTON;
+ tbb[1].dwData = 0;
+ tbb[1].iString = 0;
+
+ tbb[2].iBitmap = 4;
+ tbb[2].idCommand = ID_BRUSH_FLIPZ;
+ tbb[2].fsState = TBSTATE_ENABLED;
+ tbb[2].fsStyle = TBSTYLE_BUTTON;
+ tbb[2].dwData = 0;
+ tbb[2].iString = 0;
+
+ tbb[3].iBitmap = 1;
+ tbb[3].idCommand = ID_BRUSH_ROTATEX;
+ tbb[3].fsState = TBSTATE_ENABLED;
+ tbb[3].fsStyle = TBSTYLE_BUTTON;
+ tbb[3].dwData = 0;
+ tbb[3].iString = 0;
+
+ tbb[4].iBitmap = 3;
+ tbb[4].idCommand = ID_BRUSH_ROTATEY;
+ tbb[4].fsState = TBSTATE_ENABLED;
+ tbb[4].fsStyle = TBSTYLE_BUTTON;
+ tbb[4].dwData = 0;
+ tbb[4].iString = 0;
+
+ tbb[5].iBitmap = 5;
+ tbb[5].idCommand = ID_BRUSH_ROTATEZ;
+ tbb[5].fsState = TBSTATE_ENABLED;
+ tbb[5].fsStyle = TBSTYLE_BUTTON;
+ tbb[5].dwData = 0;
+ tbb[5].iString = 0;
+
+ tbb[6].iBitmap = 6;
+ tbb[6].idCommand = ID_SELECTION_SELECTCOMPLETETALL;
+ tbb[6].fsState = TBSTATE_ENABLED;
+ tbb[6].fsStyle = TBSTYLE_BUTTON;
+ tbb[6].dwData = 0;
+ tbb[6].iString = 0;
+
+ tbb[7].iBitmap = 7;
+ tbb[7].idCommand = ID_SELECTION_SELECTTOUCHING;
+ tbb[7].fsState = TBSTATE_ENABLED;
+ tbb[7].fsStyle = TBSTYLE_BUTTON;
+ tbb[7].dwData = 0;
+ tbb[7].iString = 0;
+
+ tbb[8].iBitmap = 8;
+ tbb[8].idCommand = ID_SELECTION_SELECTPARTIALTALL;
+ tbb[8].fsState = TBSTATE_ENABLED;
+ tbb[8].fsStyle = TBSTYLE_BUTTON;
+ tbb[8].dwData = 0;
+ tbb[8].iString = 0;
+
+
+ tbb[9].iBitmap = 9;
+ tbb[9].idCommand = ID_SELECTION_SELECTINSIDE;
+ tbb[9].fsState = TBSTATE_ENABLED;
+ tbb[9].fsStyle = TBSTYLE_BUTTON;
+ tbb[9].dwData = 0;
+ tbb[9].iString = 0;
+
+ tbb[10].iBitmap = 10;
+ tbb[10].idCommand = ID_SELECTION_CSGSUBTRACT;
+ tbb[10].fsState = TBSTATE_ENABLED;
+ tbb[10].fsStyle = TBSTYLE_BUTTON;
+ tbb[10].dwData = 0;
+ tbb[10].iString = 0;
+
+
+ tbb[11].iBitmap = 11;
+ tbb[11].idCommand = ID_SELECTION_MAKEHOLLOW;
+ tbb[11].fsState = TBSTATE_ENABLED;
+ tbb[11].fsStyle = TBSTYLE_BUTTON;
+ tbb[11].dwData = 0;
+ tbb[11].iString = 0;
+
+ tbb[12].iBitmap = 12;
+ tbb[12].idCommand = ID_TEXTURES_WIREFRAME;
+ tbb[12].fsState = TBSTATE_ENABLED;
+ tbb[12].fsStyle = TBSTYLE_BUTTON;
+ tbb[12].dwData = 0;
+ tbb[12].iString = 0;
+
+ tbb[13].iBitmap = 13;
+ tbb[13].idCommand = ID_TEXTURES_FLATSHADE;
+ tbb[13].fsState = TBSTATE_ENABLED;
+ tbb[13].fsStyle = TBSTYLE_BUTTON;
+ tbb[13].dwData = 0;
+ tbb[13].iString = 0;
+
+ tbb[14].iBitmap = 14;
+ tbb[14].idCommand = ID_VIEW_TRILINEAR;
+ tbb[14].fsState = TBSTATE_ENABLED;
+ tbb[14].fsStyle = TBSTYLE_BUTTON;
+ tbb[14].dwData = 0;
+ tbb[14].iString = 0;
+
+ SendMessage(hwndTB, TB_ADDBUTTONS, (WPARAM)NUMBUTTONS,
+ (LPARAM) (LPTBBUTTON) &tbb);
+
+ ShowWindow(hwndTB, SW_SHOW);
+
+ return hwndTB;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include "mru.h"
+
+int screen_width;
+int screen_height;
+qboolean have_quit;
+
+int update_bits;
+
+HANDLE bsp_process;
+
+//===========================================
+
+void Sys_SetTitle (char *text)
+{
+ SetWindowText (g_qeglobals.d_hwndMain, text);
+}
+
+HCURSOR waitcursor;
+
+void Sys_BeginWait (void)
+{
+ waitcursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
+}
+
+void Sys_EndWait (void)
+{
+ if (waitcursor)
+ {
+ SetCursor (waitcursor);
+ waitcursor = NULL;
+ }
+}
+
+
+void Sys_GetCursorPos (int *x, int *y)
+{
+ POINT lpPoint;
+
+ GetCursorPos (&lpPoint);
+ *x = lpPoint.x;
+ *y = lpPoint.y;
+}
+
+void Sys_SetCursorPos (int x, int y)
+{
+ SetCursorPos (x, y);
+}
+
+void Sys_UpdateWindows (int bits)
+{
+// Sys_Printf("updating 0x%X\n", bits);
+ update_bits |= bits;
+//update_bits = -1;
+}
+
+
+void Sys_Beep (void)
+{
+ MessageBeep (MB_ICONASTERISK);
+}
+
+char *TranslateString (char *buf)
+{
+ static char buf2[32768];
+ int i, l;
+ char *out;
+
+ l = strlen(buf);
+ out = buf2;
+ for (i=0 ; i<l ; i++)
+ {
+ if (buf[i] == '\n')
+ {
+ *out++ = '\r';
+ *out++ = '\n';
+ }
+ else
+ *out++ = buf[i];
+ }
+ *out++ = 0;
+
+ return buf2;
+}
+
+void Sys_ClearPrintf (void)
+{
+ char text[4];
+
+ text[0] = 0;
+
+ SendMessage (g_qeglobals.d_hwndEdit,
+ WM_SETTEXT,
+ 0,
+ (LPARAM)text);
+}
+
+void Sys_Printf (char *text, ...)
+{
+ va_list argptr;
+ char buf[32768];
+ char *out;
+
+ va_start (argptr,text);
+ vsprintf (buf, text,argptr);
+ va_end (argptr);
+
+ out = TranslateString (buf);
+
+#ifdef LATER
+ Sys_Status(out);
+#else
+ SendMessage (g_qeglobals.d_hwndEdit,
+ EM_REPLACESEL,
+ 0,
+ (LPARAM)out);
+#endif
+
+}
+
+double Sys_DoubleTime (void)
+{
+ return clock()/ 1000.0;
+}
+
+void PrintPixels (HDC hDC)
+{
+ int i;
+ PIXELFORMATDESCRIPTOR p[64];
+
+ printf ("### flags color layer\n");
+ for (i=1 ; i<64 ; i++)
+ {
+ if (!DescribePixelFormat ( hDC, i, sizeof(p[0]), &p[i]))
+ break;
+ printf ("%3i %5i %5i %5i\n", i,
+ p[i].dwFlags,
+ p[i].cColorBits,
+ p[i].bReserved);
+ }
+ printf ("%i modes\n", i-1);
+}
+
+
+
+//==========================================================================
+
+void QEW_StopGL( HWND hWnd, HGLRC hGLRC, HDC hDC )
+{
+ wglMakeCurrent( NULL, NULL );
+ wglDeleteContext( hGLRC );
+ ReleaseDC( hWnd, hDC );
+}
+
+int QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer )
+{
+ static PIXELFORMATDESCRIPTOR pfd = {
+ sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
+ 1, // version number
+ PFD_DRAW_TO_WINDOW | // support window
+ PFD_SUPPORT_OPENGL | // support OpenGL
+ PFD_DOUBLEBUFFER, // double buffered
+ PFD_TYPE_RGBA, // RGBA type
+ 24, // 24-bit color depth
+ 0, 0, 0, 0, 0, 0, // color bits ignored
+ 0, // no alpha buffer
+ 0, // shift bit ignored
+ 0, // no accumulation buffer
+ 0, 0, 0, 0, // accum bits ignored
+ 32, // depth bits
+ 0, // no stencil buffer
+ 0, // no auxiliary buffer
+ PFD_MAIN_PLANE, // main layer
+ 0, // reserved
+ 0, 0, 0 // layer masks ignored
+ };
+ int pixelformat = 0;
+
+ zbuffer = true;
+ if ( !zbuffer )
+ pfd.cDepthBits = 0;
+
+ if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
+ {
+ printf("%d",GetLastError());
+ Error ("ChoosePixelFormat failed");
+ }
+
+ if (!SetPixelFormat(hDC, pixelformat, &pfd))
+ Error ("SetPixelFormat failed");
+
+ return pixelformat;
+}
+
+/*
+=================
+Error
+
+For abnormal program terminations
+=================
+*/
+void Error (char *error, ...)
+{
+ va_list argptr;
+ char text[1024];
+ char text2[1024];
+ int err;
+
+ err = GetLastError ();
+
+ va_start (argptr,error);
+ vsprintf (text, error,argptr);
+ va_end (argptr);
+
+ sprintf (text2, "%s\nGetLastError() = %i", text, err);
+ MessageBox(g_qeglobals.d_hwndMain, text2, "Error", 0 /* MB_OK */ );
+
+ exit (1);
+}
+
+/*
+======================================================================
+
+FILE DIALOGS
+
+======================================================================
+*/
+
+qboolean ConfirmModified (void)
+{
+ if (!modified)
+ return true;
+
+ if (MessageBox (g_qeglobals.d_hwndMain, "This will lose changes to the map"
+ , "warning", MB_OKCANCEL) == IDCANCEL)
+ return false;
+ return true;
+}
+
+static OPENFILENAME ofn; /* common dialog box structure */
+static char szDirName[MAX_PATH]; /* directory string */
+static char szFile[260]; /* filename string */
+static char szFileTitle[260]; /* file title string */
+static char szFilter[260] = /* filter string */
+ "QuakeEd file (*.map)\0*.map\0\0";
+static char szProjectFilter[260] = /* filter string */
+ "QuakeEd project (*.qe4)\0*.qe4\0\0";
+static char chReplace; /* string separator for szFilter */
+static int i, cbString; /* integer count variables */
+static HANDLE hf; /* file handle */
+
+void OpenDialog (void)
+{
+ /*
+ * Obtain the system directory name and
+ * store it in szDirName.
+ */
+
+ strcpy (szDirName, ValueForKey (g_qeglobals.d_project_entity, "basepath") );
+ strcat (szDirName, "\\maps");
+
+ /* Place the terminating null character in the szFile. */
+
+ szFile[0] = '\0';
+
+ /* Set the members of the OPENFILENAME structure. */
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = g_qeglobals.d_hwndCamera;
+ ofn.lpstrFilter = szFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+ OFN_FILEMUSTEXIST;
+
+ /* Display the Open dialog box. */
+
+ if (!GetOpenFileName(&ofn))
+ return; // canceled
+
+ // Add the file in MRU.
+ AddNewItem( g_qeglobals.d_lpMruMenu, ofn.lpstrFile);
+
+ // Refresh the File menu.
+ PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0),
+ ID_FILE_EXIT);
+
+ /* Open the file. */
+
+ Map_LoadFile (ofn.lpstrFile);
+}
+
+void ProjectDialog (void)
+{
+ /*
+ * Obtain the system directory name and
+ * store it in szDirName.
+ */
+
+ strcpy (szDirName, ValueForKey(g_qeglobals.d_project_entity, "basepath") );
+ strcat (szDirName, "\\scripts");
+
+ /* Place the terminating null character in the szFile. */
+
+ szFile[0] = '\0';
+
+ /* Set the members of the OPENFILENAME structure. */
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = g_qeglobals.d_hwndCamera;
+ ofn.lpstrFilter = szProjectFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+ OFN_FILEMUSTEXIST;
+
+ /* Display the Open dialog box. */
+
+ if (!GetOpenFileName(&ofn))
+ return; // canceled
+
+ // Refresh the File menu.
+ PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0),
+ ID_FILE_EXIT);
+
+ /* Open the file. */
+ if (!QE_LoadProject(ofn.lpstrFile))
+ Error ("Couldn't load project file");
+}
+
+
+void SaveAsDialog (void)
+{
+ strcpy (szDirName, ValueForKey (g_qeglobals.d_project_entity, "basepath") );
+ strcat (szDirName, "\\maps");
+
+ /* Place the terminating null character in the szFile. */
+
+ szFile[0] = '\0';
+
+ /* Set the members of the OPENFILENAME structure. */
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = g_qeglobals.d_hwndCamera;
+ ofn.lpstrFilter = szFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+ OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT;
+
+ /* Display the Open dialog box. */
+
+ if (!GetSaveFileName(&ofn))
+ return; // canceled
+
+ DefaultExtension (ofn.lpstrFile, ".map");
+ strcpy (currentmap, ofn.lpstrFile);
+
+ // Add the file in MRU.
+ AddNewItem(g_qeglobals.d_lpMruMenu, ofn.lpstrFile);
+
+ // Refresh the File menu.
+ PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0),
+ ID_FILE_EXIT);
+
+ Map_SaveFile (ofn.lpstrFile, false); // ignore region
+}
+
+/*
+=======================================================
+
+Menu modifications
+
+=======================================================
+*/
+
+/*
+==================
+FillBSPMenu
+
+==================
+*/
+char *bsp_commands[256];
+
+void FillBSPMenu (void)
+{
+ HMENU hmenu;
+ epair_t *ep;
+ int i;
+ static int count;
+
+ hmenu = GetSubMenu (GetMenu(g_qeglobals.d_hwndMain), MENU_BSP);
+
+ for (i=0 ; i<count ; i++)
+ DeleteMenu (hmenu, CMD_BSPCOMMAND+i, MF_BYCOMMAND);
+ count = 0;
+
+ i = 0;
+ for (ep = g_qeglobals.d_project_entity->epairs ; ep ; ep=ep->next)
+ {
+ if (ep->key[0] == 'b' && ep->key[1] == 's' && ep->key[2] == 'p')
+ {
+ bsp_commands[i] = ep->key;
+ AppendMenu (hmenu, MF_ENABLED|MF_STRING,
+ CMD_BSPCOMMAND+i, (LPCTSTR)ep->key);
+ i++;
+ }
+ }
+ count = i;
+}
+
+//==============================================
+
+/*
+===============
+CheckBspProcess
+
+See if the BSP is done yet
+===============
+*/
+void CheckBspProcess (void)
+{
+ char outputpath[1024];
+ char temppath[512];
+ DWORD exitcode;
+ char *out;
+ BOOL ret;
+
+ if (!bsp_process)
+ return;
+
+ ret = GetExitCodeProcess (bsp_process, &exitcode);
+ if (!ret)
+ Error ("GetExitCodeProcess failed");
+ if (exitcode == STILL_ACTIVE)
+ return;
+
+ bsp_process = 0;
+
+ GetTempPath(512, temppath);
+ sprintf (outputpath, "%sjunk.txt", temppath);
+
+ LoadFile (outputpath, (void *)&out);
+ Sys_Printf ("%s", out);
+ Sys_Printf ("\ncompleted.\n");
+ free (out);
+ Sys_Beep ();
+
+ Pointfile_Check ();
+}
+
+extern int cambuttonstate;
+
+/*
+==================
+WinMain
+
+==================
+*/
+int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance
+ ,LPSTR lpCmdLine, int nCmdShow)
+{
+ MSG msg;
+ double time, oldtime, delta;
+ HACCEL accelerators;
+
+ g_qeglobals.d_hInstance = hInstance;
+
+ InitCommonControls ();
+
+ screen_width = GetSystemMetrics (SM_CXFULLSCREEN);
+ screen_height = GetSystemMetrics (SM_CYFULLSCREEN);
+
+ // hack for broken NT 4.0 dual screen
+ if (screen_width > 2*screen_height)
+ screen_width /= 2;
+
+ accelerators = LoadAccelerators (hInstance
+ , MAKEINTRESOURCE(IDR_ACCELERATOR1));
+ if (!accelerators)
+ Error ("LoadAccelerators failed");
+
+ Main_Create (hInstance);
+
+ WCam_Create (hInstance);
+ WXY_Create (hInstance);
+ WZ_Create (hInstance);
+ CreateEntityWindow(hInstance);
+
+ // the project file can be specified on the command line,
+ // or implicitly found in the scripts directory
+ if (lpCmdLine && strlen(lpCmdLine))
+ {
+ ParseCommandLine (lpCmdLine);
+ if (!QE_LoadProject(argv[1]))
+ Error ("Couldn't load %s project file", argv[1]);
+ }
+ else if (!QE_LoadProject("scripts/quake.qe4"))
+ Error ("Couldn't load scripts/quake.qe4 project file");
+
+ QE_Init ();
+
+ Sys_Printf ("Entering message loop\n");
+
+ oldtime = Sys_DoubleTime ();
+
+ while (!have_quit)
+ {
+ Sys_EndWait (); // remove wait cursor if active
+
+ while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if (!TranslateAccelerator(g_qeglobals.d_hwndMain, accelerators, &msg) )
+ {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ if (msg.message == WM_QUIT)
+ have_quit = true;
+ }
+
+
+ CheckBspProcess ();
+
+ time = Sys_DoubleTime ();
+ delta = time - oldtime;
+ oldtime = time;
+ if (delta > 0.2)
+ delta = 0.2;
+
+ // run time dependant behavior
+ Cam_MouseControl (delta);
+
+ // update any windows now
+ if (update_bits & W_CAMERA)
+ {
+ InvalidateRect(g_qeglobals.d_hwndCamera, NULL, false);
+ UpdateWindow (g_qeglobals.d_hwndCamera);
+ }
+ if (update_bits & (W_Z | W_Z_OVERLAY) )
+ {
+ InvalidateRect(g_qeglobals.d_hwndZ, NULL, false);
+ UpdateWindow (g_qeglobals.d_hwndZ);
+ }
+
+ if ( update_bits & W_TEXTURE )
+ {
+ InvalidateRect(g_qeglobals.d_hwndTexture, NULL, false);
+ UpdateWindow (g_qeglobals.d_hwndEntity);
+ }
+
+ if (update_bits & (W_XY | W_XY_OVERLAY))
+ {
+ InvalidateRect(g_qeglobals.d_hwndXY, NULL, false);
+ UpdateWindow (g_qeglobals.d_hwndXY);
+ }
+
+ update_bits = 0;
+
+ if (!cambuttonstate && !have_quit)
+ { // if not driving in the camera view, block
+ WaitMessage ();
+ }
+
+ }
+
+ /* return success of application */
+ return TRUE;
+
+}
+
--- /dev/null
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENU1 MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&New", ID_FILE_NEW
+ MENUITEM "&Open", ID_FILE_OPEN
+ MENUITEM "&Save", ID_FILE_SAVE
+ MENUITEM "Save &as...", ID_FILE_SAVEAS
+ MENUITEM "&Pointfile", ID_FILE_POINTFILE
+ MENUITEM "Load &project", ID_FILE_LOADPROJECT
+ MENUITEM "E&xit", ID_FILE_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "&Copy brush", ID_EDIT_COPYBRUSH, GRAYED
+ MENUITEM "&Paste brush", ID_EDIT_PASTEBRUSH, GRAYED
+ END
+ POPUP "&View"
+ BEGIN
+ MENUITEM "Texture View\tT", ID_VIEW_TEXTURE
+ MENUITEM "Console View\tO", ID_VIEW_CONSOLE
+ MENUITEM "Entity View\tN", ID_VIEW_ENTITY
+ MENUITEM SEPARATOR
+ MENUITEM "&Center\tEnd", ID_VIEW_CENTER
+ MENUITEM "&Up Floor\tPage Up", ID_VIEW_UPFLOOR
+ MENUITEM "&Down Floor\tPage Down", ID_VIEW_DOWNFLOOR
+ MENUITEM SEPARATOR
+ MENUITEM "&XY 100%", ID_VIEW_100
+ MENUITEM "XY Zoom &In\tDelete", ID_VIEW_ZOOMIN
+ MENUITEM "XY Zoom &Out\tInsert", ID_VIEW_ZOOMOUT
+ MENUITEM SEPARATOR
+ MENUITEM "Show &Names", ID_VIEW_SHOWNAMES, CHECKED
+ MENUITEM "Show Blocks", ID_VIEW_SHOWBLOCKS
+ MENUITEM "Show C&oordinates", ID_VIEW_SHOWCOORDINATES
+ , CHECKED
+ MENUITEM "Show &Entities", ID_VIEW_SHOWENT, CHECKED
+ MENUITEM "Show &Path", ID_VIEW_SHOWPATH, CHECKED
+ MENUITEM "Show &Lights", ID_VIEW_SHOWLIGHTS, CHECKED
+ MENUITEM "Show &Water", ID_VIEW_SHOWWATER, CHECKED
+ MENUITEM "Show Clip &Brush", ID_VIEW_SHOWCLIP, CHECKED
+ MENUITEM "Show Wor&ld", ID_VIEW_SHOWWORLD, CHECKED
+ MENUITEM "Show Detail\tctrl-D", ID_VIEW_SHOWDETAIL, CHECKED
+ MENUITEM SEPARATOR
+ MENUITEM "&Z 100%", ID_VIEW_Z100
+ MENUITEM "Z Zoo&m In\tctrl-Delete", ID_VIEW_ZZOOMIN
+ MENUITEM "Z Zoom O&ut\tctrl-Insert", ID_VIEW_ZZOOMOUT
+ END
+ POPUP "&Selection"
+ BEGIN
+ MENUITEM "Drag &Edges\tE", ID_SELECTION_DRAGEDGES
+ MENUITEM "Drag &Vertecies\tV", ID_SELECTION_DRAGVERTECIES
+ MENUITEM "&Clone\tspace", ID_SELECTION_CLONE
+ MENUITEM "Deselect\tEsc", ID_SELECTION_DESELECT
+ MENUITEM "&Delete\tBackspace", ID_SELECTION_DELETE
+ MENUITEM "Flip &X", ID_BRUSH_FLIPX
+ MENUITEM "Flip &Y", ID_BRUSH_FLIPY
+ MENUITEM "Flip &Z", ID_BRUSH_FLIPZ
+ MENUITEM "Rotate X", ID_BRUSH_ROTATEX
+ MENUITEM "Rotate Y", ID_BRUSH_ROTATEY
+ MENUITEM "Rotate Z", ID_BRUSH_ROTATEZ
+ MENUITEM "Arbitrary rotation", ID_SELECTION_ARBITRARYROTATION
+
+ MENUITEM "Make &Hollow", ID_SELECTION_MAKEHOLLOW
+ MENUITEM "CSG &Subtract", ID_SELECTION_CSGSUBTRACT
+ MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL
+
+ MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING
+ MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL
+
+ MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE
+ MENUITEM "Connect entities\tCtrl-k", ID_SELECTION_CONNECT
+ MENUITEM "Ungroup entity", ID_SELECTION_UNGROUPENTITY
+ MENUITEM "Make detail\tCtrl-m", ID_SELECTION_MAKE_DETAIL
+ MENUITEM "Make structural", ID_SELECTION_MAKE_STRUCTURAL
+ END
+ POPUP "&Bsp"
+ BEGIN
+ MENUITEM SEPARATOR
+ END
+ POPUP "&Grid"
+ BEGIN
+ MENUITEM "Grid1\t&1", ID_GRID_1
+ MENUITEM "Grid2\t&2", ID_GRID_2
+ MENUITEM "Grid4\t&3", ID_GRID_4
+ MENUITEM "Grid8\t&4", ID_GRID_8, CHECKED
+ MENUITEM "Grid16\t&5", ID_GRID_16
+ MENUITEM "Grid32\t&6", ID_GRID_32
+ MENUITEM "Grid64\t&7", ID_GRID_64
+ END
+ POPUP "&Textures"
+ BEGIN
+ MENUITEM "Show In &Use\tU", ID_TEXTURES_SHOWINUSE
+ MENUITEM "&Surface inspector\tS", ID_TEXTURES_INSPECTOR
+ MENUITEM SEPARATOR
+ MENUITEM "&Wireframe", ID_TEXTURES_WIREFRAME
+ MENUITEM "&Flat shade", ID_TEXTURES_FLATSHADE
+ MENUITEM "&Nearest", ID_VIEW_NEAREST
+ MENUITEM "Nearest &Mipmap", ID_VIEW_NEARESTMIPMAP
+ MENUITEM "&Linear", ID_VIEW_LINEAR
+ MENUITEM "&Bilinear", ID_VIEW_BILINEAR
+ MENUITEM "B&ilinear Mipmap", ID_VIEW_BILINEARMIPMAP
+ MENUITEM "T&rilinear", ID_VIEW_TRILINEAR
+ MENUITEM SEPARATOR
+ END
+ POPUP "&Misc"
+ BEGIN
+ MENUITEM "&Benchmark", ID_MISC_BENCHMARK
+ POPUP "&Colors"
+ BEGIN
+ MENUITEM "&Texture Background", ID_TEXTUREBK
+ MENUITEM "Grid Background", ID_COLORS_XYBK
+ MENUITEM "Grid Major", ID_COLORS_MAJOR
+ MENUITEM "Grid Minor", ID_COLORS_MINOR
+ END
+ MENUITEM "&Gamma", ID_MISC_GAMMA
+ MENUITEM "Find brush", ID_MISC_FINDBRUSH
+ MENUITEM "Next leak spot\tctrl-l", ID_MISC_NEXTLEAKSPOT
+ MENUITEM "Previous leak spot\tctrl-p", ID_MISC_PREVIOUSLEAKSPOT
+ MENUITEM "&Print XY View", ID_MISC_PRINTXY
+ MENUITEM "&Select Entity Color\tK", ID_MISC_SELECTENTITYCOLOR
+ END
+ POPUP "&Region"
+ BEGIN
+ MENUITEM "&Off", ID_REGION_OFF
+ MENUITEM "&Set XY", ID_REGION_SETXY
+ MENUITEM "Set &Tall Brush", ID_REGION_SETTALLBRUSH
+ MENUITEM "Set &Brush", ID_REGION_SETBRUSH
+ MENUITEM "Set Se&lected Brushes", ID_REGION_SETSELECTION
+ END
+ POPUP "&Brush"
+ BEGIN
+ MENUITEM "3 sided\tctrl-3", ID_BRUSH_3SIDED
+ MENUITEM "4 sided\tctrl-4", ID_BRUSH_4SIDED
+ MENUITEM "5 sided\tctrl-5", ID_BRUSH_5SIDED
+ MENUITEM "6 sided\tctrl-6", ID_BRUSH_6SIDED
+ MENUITEM "7 sided\tctrl-7", ID_BRUSH_7SIDED
+ MENUITEM "8 sided\tctrl-8", ID_BRUSH_8SIDED
+ MENUITEM "9 sided\tctrl-9", ID_BRUSH_9SIDED
+ MENUITEM "Arbitrary sided", ID_BRUSH_ARBITRARYSIDED
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About", ID_HELP_ABOUT
+ END
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_FINDTEXTURE DIALOG DISCARDABLE 0, 0, 129, 53
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Find Texture"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,10,30,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,70,30,50,14
+ EDITTEXT IDC_EDIT1,10,10,110,14,ES_AUTOHSCROLL
+END
+
+IDD_ENTITY DIALOGEX 0, 0, 234, 389
+STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_THICKFRAME
+EXSTYLE WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE
+CAPTION "Entity"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ LISTBOX IDC_E_LIST,5,5,180,99,LBS_SORT | LBS_NOINTEGRALHEIGHT |
+ LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP,
+ WS_EX_CLIENTEDGE
+ EDITTEXT IDC_E_COMMENT,5,106,180,50,ES_MULTILINE | ES_READONLY |
+ WS_VSCROLL,WS_EX_CLIENTEDGE
+ PUSHBUTTON "135",IDC_E_135,5,290,15,15
+ PUSHBUTTON "180",IDC_E_180,5,305,15,15
+ PUSHBUTTON "225",IDC_E_225,5,320,15,15
+ PUSHBUTTON "270",IDC_E_270,21,320,15,15
+ PUSHBUTTON "90",IDC_E_90,21,290,15,15
+ PUSHBUTTON "45",IDC_E_45,35,290,15,15
+ PUSHBUTTON "0",IDC_E_0,35,305,15,15
+ PUSHBUTTON "315",IDC_E_315,35,320,15,15
+ PUSHBUTTON "Up",IDC_E_UP,60,295,15,15
+ PUSHBUTTON "Dn",IDC_E_DOWN,60,310,15,15
+ CONTROL "",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_DISABLED |
+ WS_TABSTOP,5,160,50,8
+ CONTROL "",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_DISABLED |
+ WS_TABSTOP,5,170,50,8
+ CONTROL "",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_DISABLED |
+ WS_TABSTOP,5,180,50,8
+ CONTROL "",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_DISABLED |
+ WS_TABSTOP,5,190,50,8
+ CONTROL "",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_DISABLED |
+ WS_TABSTOP,65,160,50,8
+ CONTROL "",IDC_CHECK6,"Button",BS_AUTOCHECKBOX | WS_DISABLED |
+ WS_TABSTOP,65,170,50,8
+ CONTROL "",IDC_CHECK7,"Button",BS_AUTOCHECKBOX | WS_DISABLED |
+ WS_TABSTOP,65,180,50,8
+ CONTROL "",IDC_CHECK8,"Button",BS_AUTOCHECKBOX | WS_DISABLED |
+ WS_TABSTOP,65,190,50,8
+ CONTROL "!Easy",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 125,160,50,8
+ CONTROL "!Medium",IDC_CHECK10,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,125,170,50,8
+ CONTROL "!Hard",IDC_CHECK11,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,125,180,50,10
+ CONTROL "!DeathMatch",IDC_CHECK12,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,125,190,55,10
+ LISTBOX IDC_E_PROPS,5,205,180,50,LBS_SORT | LBS_USETABSTOPS |
+ LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT |
+ WS_VSCROLL | WS_TABSTOP,WS_EX_CLIENTEDGE
+ PUSHBUTTON "Del Key/Pair",IDC_E_DELPROP,105,295,45,15
+ EDITTEXT IDC_E_STATUS,83,312,95,30,ES_MULTILINE | ES_AUTOVSCROLL |
+ ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL
+ LTEXT "Key",IDC_STATIC_KEY,5,260,25,10
+ LTEXT "Value",IDC_STATIC_VALUE,5,275,25,10
+ EDITTEXT IDC_E_KEY_FIELD,40,260,135,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_E_VALUE_FIELD,40,275,135,14,ES_AUTOHSCROLL
+END
+
+IDD_GAMMA DIALOGEX 0, 0, 127, 76
+STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+CAPTION "Gamma"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,10,40,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,65,40,50,14
+ EDITTEXT IDC_G_EDIT,30,15,66,13,ES_AUTOHSCROLL,WS_EX_CLIENTEDGE
+END
+
+IDD_FINDBRUSH DIALOGEX 0, 0, 127, 76
+STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+CAPTION "Find brush"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,5,55,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,65,55,50,14
+ EDITTEXT IDC_FIND_ENTITY,80,15,46,13,ES_AUTOHSCROLL,
+ WS_EX_CLIENTEDGE
+ EDITTEXT IDC_FIND_BRUSH,80,30,46,13,ES_AUTOHSCROLL,
+ WS_EX_CLIENTEDGE
+ LTEXT "Entity number",IDC_STATIC,10,15,60,8
+ LTEXT "Brush number",IDC_STATIC,10,30,65,8
+END
+
+IDD_ROTATE DIALOG DISCARDABLE 0, 0, 186, 71
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Arbitrary rotation"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,129,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14
+ EDITTEXT IDC_ROTX,30,5,40,14,ES_AUTOHSCROLL
+ LTEXT "x",IDC_STATIC,5,10,8,8
+ EDITTEXT IDC_ROTZ,30,45,40,14,ES_AUTOHSCROLL
+ LTEXT "y",IDC_STATIC,5,25,8,8
+ EDITTEXT IDC_ROTY,30,25,40,14,ES_AUTOHSCROLL
+ LTEXT "z",IDC_STATIC,5,45,8,8
+END
+
+IDD_SIDES DIALOG DISCARDABLE 0, 0, 186, 55
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Arbitrrary sides"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,129,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14
+ EDITTEXT IDC_SIDES,50,15,40,14,ES_AUTOHSCROLL
+ LTEXT "Sides",IDC_STATIC,15,15,18,8
+END
+
+IDD_ABOUT DIALOG DISCARDABLE 0, 0, 274, 212
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About QuakeEd"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,217,7,50,14
+ CONTROL 127,IDC_STATIC,"Static",SS_BITMAP,7,7,83,58
+ CONTROL "QuakeEd 4.0(beta)\nCopyright (C) 1997 id Software, Inc.",
+ IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,100,10,
+ 110,23
+ GROUPBOX "OpenGL Properties",IDC_STATIC,5,75,265,50
+ LTEXT "Vendor:\t\tWHOEVER",IDC_ABOUT_GLVENDOR,10,90,125,10
+ LTEXT "Version:\t\t1.1",IDC_ABOUT_GLVERSION,10,100,125,10
+ LTEXT "Renderer:\tWHATEVER",IDC_ABOUT_GLRENDERER,10,110,125,10
+ LTEXT "WHATEVER",IDC_ABOUT_GLEXTENSIONS,10,140,255,60
+ GROUPBOX "OpenGL Extensions",IDC_STATIC,5,130,265,80
+END
+
+IDD_SURFACE DIALOG DISCARDABLE 400, 100, 392, 181
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Surface inspector"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,5,155,40,14
+ PUSHBUTTON "Cancel",IDCANCEL,105,155,40,14
+ EDITTEXT IDC_HSHIFT,85,45,35,15,ES_AUTOHSCROLL
+ SCROLLBAR IDC_HSHIFTA,120,45,10,15,SBS_VERT
+ LTEXT "Horizontal shift",IDC_STATIC,10,45,65,8
+ LTEXT "Vertical shift",IDC_STATIC,10,60,65,8
+ LTEXT "Horizontal stretch",IDC_STATIC,10,75,65,8
+ LTEXT "Vertical stretch",IDC_STATIC,10,90,65,8
+ LTEXT "Rotate",IDC_STATIC,10,105,65,8
+ LTEXT "value",IDC_STATIC,10,120,65,8
+ EDITTEXT IDC_VSHIFT,85,60,35,15,ES_AUTOHSCROLL
+ SCROLLBAR IDC_VSHIFTA,120,60,10,15,SBS_VERT
+ EDITTEXT IDC_HSCALE,85,75,35,15,ES_AUTOHSCROLL
+ SCROLLBAR IDC_HSCALEA,120,75,10,15,SBS_VERT
+ EDITTEXT IDC_VSCALE,85,90,35,15,ES_AUTOHSCROLL
+ SCROLLBAR IDC_VSCALEA,120,90,10,15,SBS_VERT
+ EDITTEXT IDC_ROTATE,85,105,35,15,ES_AUTOHSCROLL
+ SCROLLBAR IDC_ROTATEA,120,105,10,15,SBS_VERT
+ EDITTEXT IDC_VALUE,85,120,35,15,ES_AUTOHSCROLL
+ EDITTEXT IDC_TEXTURE,50,15,80,14,ES_AUTOHSCROLL
+ CONTROL "light",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,10,41,8
+ CONTROL "slick",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,20,41,8
+ CONTROL "sky",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,30,41,8
+ CONTROL "warp",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,40,41,8
+ CONTROL "trans33",IDC_CHECK5,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,160,50,41,8
+ CONTROL "trans66",IDC_CHECK6,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,160,60,41,8
+ CONTROL "flowing",IDC_CHECK7,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,160,70,41,8
+ CONTROL "nodraw",IDC_CHECK8,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,160,80,41,8
+ CONTROL "100",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,90,41,8
+ CONTROL "200",IDC_CHECK10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,100,41,8
+ CONTROL "400",IDC_CHECK11,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,110,41,8
+ CONTROL "800",IDC_CHECK12,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,120,41,8
+ CONTROL "1000",IDC_CHECK13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,130,41,8
+ CONTROL "2000",IDC_CHECK14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,140,41,8
+ CONTROL "4000",IDC_CHECK15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,150,41,8
+ CONTROL "8000",IDC_CHECK16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 160,160,41,8
+ LTEXT "Texture",IDC_STATIC,10,18,30,8
+ PUSHBUTTON "Apply",IDAPPLY,55,155,40,14
+ CONTROL "10000",IDC_CHECK17,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,10,41,8
+ CONTROL "20000",IDC_CHECK18,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,20,41,8
+ CONTROL "40000",IDC_CHECK19,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,30,41,8
+ CONTROL "80000",IDC_CHECK20,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,40,41,8
+ CONTROL "100000",IDC_CHECK21,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,50,41,8
+ CONTROL "200000",IDC_CHECK22,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,60,41,8
+ CONTROL "400000",IDC_CHECK23,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,70,41,8
+ CONTROL "800000",IDC_CHECK24,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,80,41,8
+ CONTROL "1000000",IDC_CHECK25,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,90,41,8
+ CONTROL "2000000",IDC_CHECK26,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,100,41,8
+ CONTROL "4000000",IDC_CHECK27,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,110,41,8
+ CONTROL "8000000",IDC_CHECK28,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,120,41,8
+ CONTROL "10000000",IDC_CHECK29,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,130,40,8
+ CONTROL "20000000",IDC_CHECK30,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,140,45,8
+ CONTROL "40000000",IDC_CHECK31,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,150,45,8
+ CONTROL "80000000",IDC_CHECK32,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,210,160,45,8
+ CONTROL "solid",IDC_CHECK33,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,280,10,41,8
+ CONTROL "window",IDC_CHECK34,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,280,20,41,8
+ CONTROL "aux",IDC_CHECK35,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,31,41,8
+ CONTROL "lava",IDC_CHECK36,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,41,41,8
+ CONTROL "slime",IDC_CHECK37,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,280,50,41,8
+ CONTROL "water",IDC_CHECK38,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,280,60,41,8
+ CONTROL "mist",IDC_CHECK39,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,71,41,8
+ CONTROL "80",IDC_CHECK40,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,81,41,8
+ CONTROL "100",IDC_CHECK41,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,90,41,8
+ CONTROL "200",IDC_CHECK42,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,100,41,8
+ CONTROL "400",IDC_CHECK43,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,111,41,8
+ CONTROL "800",IDC_CHECK44,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,121,41,8
+ CONTROL "1000",IDC_CHECK45,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,130,41,8
+ CONTROL "2000",IDC_CHECK46,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,140,41,8
+ CONTROL "4000",IDC_CHECK47,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,151,41,8
+ CONTROL "8000",IDC_CHECK48,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 280,161,41,8
+ CONTROL "playerclip",IDC_CHECK49,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,10,41,8
+ CONTROL "monsterclip",IDC_CHECK50,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,20,50,8
+ CONTROL "current_0",IDC_CHECK51,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,31,50,8
+ CONTROL "current_90",IDC_CHECK52,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,41,50,8
+ CONTROL "current_180",IDC_CHECK53,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,50,50,8
+ CONTROL "current_270",IDC_CHECK54,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,60,50,8
+ CONTROL "current_up",IDC_CHECK55,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,71,50,8
+ CONTROL "current_dn",IDC_CHECK56,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,81,50,8
+ CONTROL "origin",IDC_CHECK57,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,90,41,8
+ CONTROL "monster",IDC_CHECK58,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,100,41,8
+ CONTROL "corpse",IDC_CHECK59,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,111,41,8
+ CONTROL "detail",IDC_CHECK60,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,121,41,8
+ CONTROL "translucent",IDC_CHECK61,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,130,50,8
+ CONTROL "ladder",IDC_CHECK62,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,140,45,8
+ CONTROL "40000000",IDC_CHECK63,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,151,45,8
+ CONTROL "80000000",IDC_CHECK64,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,330,161,45,8
+ GROUPBOX "Surf flags",IDC_STATIC,150,0,115,175
+ GROUPBOX "Content flags",IDC_STATIC,270,0,115,175
+END
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Id Software\0"
+ VALUE "FileDescription", "qe3\0"
+ VALUE "FileVersion", "1, 0, 0, 1\0"
+ VALUE "InternalName", "qe3\0"
+ VALUE "LegalCopyright", "Copyright © 1996\0"
+ VALUE "OriginalFilename", "qe3.exe\0"
+ VALUE "ProductName", "Id Software qe3\0"
+ VALUE "ProductVersion", "1, 0, 0, 1\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE
+BEGIN
+ "3", ID_BRUSH_3SIDED, VIRTKEY, CONTROL, NOINVERT
+ "4", ID_BRUSH_4SIDED, VIRTKEY, CONTROL, NOINVERT
+ "5", ID_BRUSH_5SIDED, VIRTKEY, CONTROL, NOINVERT
+ "6", ID_BRUSH_6SIDED, VIRTKEY, CONTROL, NOINVERT
+ "7", ID_BRUSH_7SIDED, VIRTKEY, CONTROL, NOINVERT
+ "8", ID_BRUSH_8SIDED, VIRTKEY, CONTROL, NOINVERT
+ "9", ID_BRUSH_9SIDED, VIRTKEY, CONTROL, NOINVERT
+ "D", ID_VIEW_SHOWDETAIL, VIRTKEY, CONTROL, NOINVERT
+ "K", ID_SELECTION_CONNECT, VIRTKEY, CONTROL, NOINVERT
+ "L", ID_MISC_NEXTLEAKSPOT, VIRTKEY, CONTROL, NOINVERT
+ "M", ID_SELECTION_MAKE_DETAIL, VIRTKEY, CONTROL, NOINVERT
+ "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
+ "P", ID_MISC_PREVIOUSLEAKSPOT, VIRTKEY, CONTROL, NOINVERT
+ "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT
+ VK_DELETE, ID_VIEW_ZZOOMIN, VIRTKEY, CONTROL, NOINVERT
+ VK_INSERT, ID_VIEW_ZZOOMOUT, VIRTKEY, CONTROL, NOINVERT
+ "X", ID_FILE_EXIT, VIRTKEY, CONTROL, NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Toolbar
+//
+
+IDR_TOOLBAR1 TOOLBAR DISCARDABLE 16, 15
+BEGIN
+ BUTTON ID_BRUSH_FLIPX
+ BUTTON ID_BRUSH_ROTATEX
+ BUTTON ID_BRUSH_FLIPY
+ BUTTON ID_BRUSH_ROTATEY
+ BUTTON ID_BRUSH_FLIPZ
+ BUTTON ID_BRUSH_ROTATEZ
+ BUTTON ID_SELECTION_SELECTCOMPLETETALL
+ BUTTON ID_SELECTION_SELECTTOUCHING
+ BUTTON ID_SELECTION_SELECTPARTIALTALL
+ BUTTON ID_SELECTION_SELECTINSIDE
+ BUTTON ID_SELECTION_CSGSUBTRACT
+ BUTTON ID_SELECTION_MAKEHOLLOW
+ BUTTON ID_TEXTURES_WIREFRAME
+ BUTTON ID_TEXTURES_FLATSHADE
+ BUTTON ID_VIEW_TRILINEAR
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDR_TOOLBAR1 BITMAP DISCARDABLE "toolbar1.bmp"
+IDB_BITMAP1 BITMAP DISCARDABLE "q.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_ENTITY, DIALOG
+ BEGIN
+ RIGHTMARGIN, 227
+ BOTTOMMARGIN, 387
+ END
+
+ IDD_GAMMA, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 120
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 68
+ END
+
+ IDD_FINDBRUSH, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 120
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 68
+ END
+
+ IDD_ROTATE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 64
+ END
+
+ IDD_SIDES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 48
+ END
+
+ IDD_ABOUT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 267
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 205
+ END
+
+ IDD_SURFACE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 385
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 174
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1 ICON DISCARDABLE "icon1.ico"
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// win_xy.c -- windows specific xy view code
+
+#include "qe3.h"
+
+static HDC s_hdcXY;
+static HGLRC s_hglrcXY;
+
+static unsigned s_stipple[32] =
+{
+ 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+ 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+ 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+ 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+ 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+ 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+ 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+ 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+};
+
+/*
+============
+WXY_WndProc
+============
+*/
+LONG WINAPI WXY_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ int fwKeys, xPos, yPos;
+ RECT rect;
+
+
+ GetClientRect(hWnd, &rect);
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+
+ s_hdcXY = GetDC(hWnd);
+
+ QEW_SetupPixelFormat(s_hdcXY, false);
+
+ if ( ( s_hglrcXY = wglCreateContext( s_hdcXY ) ) == 0 )
+ Error( "wglCreateContext in WXY_WndProc failed" );
+
+ if (!wglMakeCurrent( s_hdcXY, s_hglrcXY ))
+ Error ("wglMakeCurrent failed");
+
+ if (!wglShareLists( g_qeglobals.d_hglrcBase, s_hglrcXY ) )
+ Error( "wglShareLists in WXY_WndProc failed" );
+
+ glPolygonStipple ((char *)s_stipple);
+ glLineStipple (3, 0xaaaa);
+
+ return 0;
+
+ case WM_DESTROY:
+ QEW_StopGL( hWnd, s_hglrcXY, s_hdcXY );
+ return 0;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+
+ BeginPaint(hWnd, &ps);
+
+ if (!wglMakeCurrent( s_hdcXY, s_hglrcXY ))
+ Error ("wglMakeCurrent failed");
+
+ QE_CheckOpenGLForErrors();
+ XY_Draw ();
+ QE_CheckOpenGLForErrors();
+
+ SwapBuffers(s_hdcXY);
+
+ EndPaint(hWnd, &ps);
+ }
+ return 0;
+
+ case WM_KEYDOWN:
+ return QE_KeyDown (wParam);
+
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONDOWN:
+ if ( GetTopWindow( g_qeglobals.d_hwndMain ) != hWnd)
+ BringWindowToTop(hWnd);
+ SetFocus( g_qeglobals.d_hwndXY );
+ SetCapture( g_qeglobals.d_hwndXY );
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ XY_MouseDown (xPos, yPos, fwKeys);
+ return 0;
+
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ XY_MouseUp (xPos, yPos, fwKeys);
+ if (! (fwKeys & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+ ReleaseCapture ();
+ return 0;
+
+ case WM_MOUSEMOVE:
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ XY_MouseMoved (xPos, yPos, fwKeys);
+ return 0;
+
+ case WM_SIZE:
+ g_qeglobals.d_xy.width = rect.right;
+ g_qeglobals.d_xy.height = rect.bottom;
+ InvalidateRect( g_qeglobals.d_hwndXY, NULL, false);
+ return 0;
+
+ case WM_NCCALCSIZE:// don't let windows copy pixels
+ DefWindowProc (hWnd, uMsg, wParam, lParam);
+ return WVR_REDRAW;
+
+ case WM_KILLFOCUS:
+ case WM_SETFOCUS:
+ SendMessage( hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0 );
+ return 0;
+
+ case WM_CLOSE:
+ DestroyWindow (hWnd);
+ return 0;
+ }
+
+ return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+
+/*
+==============
+WXY_Create
+==============
+*/
+void WXY_Create (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WXY_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL; //(HBRUSH)(COLOR_WINDOW+1);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = XY_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Error ("RegisterClass: failed");
+
+ g_qeglobals.d_hwndXY = CreateWindow (XY_WINDOW_CLASS ,
+ "XY View",
+ QE3_STYLE ,
+ ZWIN_WIDTH,
+ (int)(screen_height*CWIN_SIZE)-20,
+ screen_width-ZWIN_WIDTH,
+ (int)(screen_height*(1.0-CWIN_SIZE)-38), // size
+
+ g_qeglobals.d_hwndMain, // parent
+ 0, // no menu
+ hInstance,
+ NULL);
+
+ if (!g_qeglobals.d_hwndXY )
+ Error ("Couldn't create XY View");
+
+ LoadWindowState(g_qeglobals.d_hwndXY, "xywindow");
+ ShowWindow(g_qeglobals.d_hwndXY, SW_SHOWDEFAULT);
+}
+
+static void WXY_InitPixelFormat( PIXELFORMATDESCRIPTOR *pPFD )
+{
+ memset( pPFD, 0, sizeof( *pPFD ) );
+
+ pPFD->nSize = sizeof( PIXELFORMATDESCRIPTOR );
+ pPFD->nVersion = 1;
+ pPFD->dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
+ pPFD->iPixelType = PFD_TYPE_RGBA;
+ pPFD->cColorBits = 24;
+ pPFD->cDepthBits = 32;
+ pPFD->iLayerType = PFD_MAIN_PLANE;
+
+}
+
+void WXY_Print( void )
+{
+ DOCINFO di;
+
+ PRINTDLG pd;
+
+ /*
+ ** initialize the PRINTDLG struct and execute it
+ */
+ memset( &pd, 0, sizeof( pd ) );
+ pd.lStructSize = sizeof( pd );
+ pd.hwndOwner = g_qeglobals.d_hwndXY;
+ pd.Flags = PD_RETURNDC;
+ pd.hInstance = 0;
+ if ( !PrintDlg( &pd ) || !pd.hDC )
+ {
+ MessageBox( g_qeglobals.d_hwndMain, "Could not PrintDlg()", "QE4 Print Error", MB_OK | MB_ICONERROR );
+ return;
+ }
+
+ /*
+ ** StartDoc
+ */
+ memset( &di, 0, sizeof( di ) );
+ di.cbSize = sizeof( di );
+ di.lpszDocName = "QE4";
+ if ( StartDoc( pd.hDC, &di ) <= 0 )
+ {
+ MessageBox( g_qeglobals.d_hwndMain, "Could not StartDoc()", "QE4 Print Error", MB_OK | MB_ICONERROR );
+ return;
+ }
+
+ /*
+ ** StartPage
+ */
+ if ( StartPage( pd.hDC ) <= 0 )
+ {
+ MessageBox( g_qeglobals.d_hwndMain, "Could not StartPage()", "QE4 Print Error", MB_OK | MB_ICONERROR );
+ return;
+ }
+
+ /*
+ ** read pixels from the XY window
+ */
+ {
+ int bmwidth = 320, bmheight = 320;
+ int pwidth, pheight;
+
+ RECT r;
+
+ GetWindowRect( g_qeglobals.d_hwndXY, &r );
+
+ bmwidth = r.right - r.left;
+ bmheight = r.bottom - r.top;
+
+ pwidth = GetDeviceCaps( pd.hDC, PHYSICALWIDTH ) - GetDeviceCaps( pd.hDC, PHYSICALOFFSETX );
+ pheight = GetDeviceCaps( pd.hDC, PHYSICALHEIGHT ) - GetDeviceCaps( pd.hDC, PHYSICALOFFSETY );
+
+ StretchBlt( pd.hDC,
+ 0, 0,
+ pwidth, pheight,
+ s_hdcXY,
+ 0, 0,
+ bmwidth, bmheight,
+ SRCCOPY );
+ }
+
+ /*
+ ** EndPage and EndDoc
+ */
+ if ( EndPage( pd.hDC ) <= 0 )
+ {
+ MessageBox( g_qeglobals.d_hwndMain, "QE4 Print Error", "Could not EndPage()", MB_OK | MB_ICONERROR );
+ return;
+ }
+
+ if ( EndDoc( pd.hDC ) <= 0 )
+ {
+ MessageBox( g_qeglobals.d_hwndMain, "QE4 Print Error", "Could not EndDoc()", MB_OK | MB_ICONERROR );
+ return;
+ }
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// win_cam.c -- windows specific camera view code
+
+#include "qe3.h"
+
+static HDC s_hdcZ;
+static HGLRC s_hglrcZ;
+
+/*
+============
+WZ_WndProc
+============
+*/
+LONG WINAPI WZ_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ int fwKeys, xPos, yPos;
+ RECT rect;
+
+ GetClientRect(hWnd, &rect);
+
+ switch (uMsg)
+ {
+
+ case WM_DESTROY:
+ QEW_StopGL( hWnd, s_hglrcZ, s_hdcZ );
+ return 0;
+
+ case WM_CREATE:
+ s_hdcZ = GetDC(hWnd);
+ QEW_SetupPixelFormat( s_hdcZ, false);
+ if ( ( s_hglrcZ = wglCreateContext( s_hdcZ ) ) == 0 )
+ Error( "wglCreateContext in WZ_WndProc failed" );
+
+ if (!wglMakeCurrent( s_hdcZ, s_hglrcZ ))
+ Error ("wglMakeCurrent in WZ_WndProc failed");
+
+ if (!wglShareLists( g_qeglobals.d_hglrcBase, s_hglrcZ ) )
+ Error( "wglShareLists in WZ_WndProc failed" );
+ return 0;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+
+ BeginPaint(hWnd, &ps);
+
+ if ( !wglMakeCurrent( s_hdcZ, s_hglrcZ ) )
+ Error ("wglMakeCurrent failed");
+ QE_CheckOpenGLForErrors();
+
+ Z_Draw ();
+ SwapBuffers(s_hdcZ);
+
+ EndPaint(hWnd, &ps);
+ }
+ return 0;
+
+
+ case WM_KEYDOWN:
+ QE_KeyDown (wParam);
+ return 0;
+
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONDOWN:
+ if (GetTopWindow(g_qeglobals.d_hwndMain) != hWnd)
+ BringWindowToTop(hWnd);
+
+ SetFocus( g_qeglobals.d_hwndZ );
+ SetCapture( g_qeglobals.d_hwndZ );
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ Z_MouseDown (xPos, yPos, fwKeys);
+ return 0;
+
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ Z_MouseUp (xPos, yPos, fwKeys);
+ if (! (fwKeys & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+ ReleaseCapture ();
+ return 0;
+
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *pmmi = (LPMINMAXINFO) lParam;
+
+ pmmi->ptMinTrackSize.x = ZWIN_WIDTH;
+ return 0;
+ }
+
+ case WM_MOUSEMOVE:
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ Z_MouseMoved (xPos, yPos, fwKeys);
+ return 0;
+
+ case WM_SIZE:
+ z.width = rect.right;
+ z.height = rect.bottom;
+ InvalidateRect( g_qeglobals.d_hwndZ, NULL, false);
+ return 0;
+
+ case WM_NCCALCSIZE:// don't let windows copy pixels
+ DefWindowProc (hWnd, uMsg, wParam, lParam);
+ return WVR_REDRAW;
+
+ case WM_KILLFOCUS:
+ case WM_SETFOCUS:
+ SendMessage( hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0 );
+ return 0;
+
+ case WM_CLOSE:
+ /* call destroy window to cleanup and go away */
+ DestroyWindow (hWnd);
+ return 0;
+ }
+
+ return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+
+/*
+==============
+WZ_Create
+==============
+*/
+void WZ_Create (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WZ_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = Z_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Error ("WCam_Register: failed");
+
+ g_qeglobals.d_hwndZ = CreateWindow (Z_WINDOW_CLASS ,
+ "Z",
+ QE3_STYLE,
+ 0,20,ZWIN_WIDTH,screen_height-38, // size
+ g_qeglobals.d_hwndMain, // parent
+ 0, // no menu
+ hInstance,
+ NULL);
+ if (!g_qeglobals.d_hwndZ)
+ Error ("Couldn't create zwindow");
+
+ LoadWindowState(g_qeglobals.d_hwndZ, "zwindow");
+ ShowWindow (g_qeglobals.d_hwndZ, SW_SHOWDEFAULT);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+#define PAGEFLIPS 2
+
+/*
+============
+XY_Init
+============
+*/
+void XY_Init (void)
+{
+ g_qeglobals.d_xy.origin[0] = 0;
+ g_qeglobals.d_xy.origin[1] = 20;
+ g_qeglobals.d_xy.origin[2] = 46;
+
+ g_qeglobals.d_xy.scale = 1;
+}
+
+
+/*
+============================================================================
+
+ MOUSE ACTIONS
+
+============================================================================
+*/
+
+static int cursorx, cursory;
+static int buttonstate;
+static int pressx, pressy;
+static vec3_t pressdelta;
+static qboolean press_selection;
+
+void XY_ToPoint (int x, int y, vec3_t point)
+{
+ point[0] = g_qeglobals.d_xy.origin[0] + (x - g_qeglobals.d_xy.width/2)/g_qeglobals.d_xy.scale;
+ point[1] = g_qeglobals.d_xy.origin[1] + (y - g_qeglobals.d_xy.height/2)/g_qeglobals.d_xy.scale;
+ point[2] = 0;
+}
+
+void XY_ToGridPoint (int x, int y, vec3_t point)
+{
+ point[0] = g_qeglobals.d_xy.origin[0] + (x - g_qeglobals.d_xy.width/2)/g_qeglobals.d_xy.scale;
+ point[1] = g_qeglobals.d_xy.origin[1] + (y - g_qeglobals.d_xy.height/2)/g_qeglobals.d_xy.scale;
+ point[2] = 0;
+ point[0] = floor(point[0]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+ point[1] = floor(point[1]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+}
+
+/*
+==============
+XY_MouseDown
+==============
+*/
+void XY_MouseDown (int x, int y, int buttons)
+{
+ vec3_t point;
+ vec3_t origin, dir, right, up;
+
+ buttonstate = buttons;
+ pressx = x;
+ pressy = y;
+ VectorCopy (vec3_origin, pressdelta);
+
+ XY_ToPoint (x, y, point);
+
+ VectorCopy (point, origin);
+ origin[2] = 8192;
+
+ dir[0] = 0; dir[1] = 0; dir[2] = -1;
+ right[0] = 1/g_qeglobals.d_xy.scale; right[1] = 0; right[2] = 0;
+ up[0] = 0; up[1] = 1/g_qeglobals.d_xy.scale; up[2] = 0;
+
+ press_selection = (selected_brushes.next != &selected_brushes);
+
+ Sys_GetCursorPos (&cursorx, &cursory);
+
+ // lbutton = manipulate selection
+ // shift-LBUTTON = select
+ if ( (buttons == MK_LBUTTON)
+ || (buttons == (MK_LBUTTON | MK_SHIFT))
+ || (buttons == (MK_LBUTTON | MK_CONTROL))
+ || (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) )
+ {
+ Drag_Begin (x, y, buttons,
+ right, up,
+ origin, dir);
+ return;
+ }
+
+ // control mbutton = move camera
+ if (buttonstate == (MK_CONTROL|MK_MBUTTON) )
+ {
+ camera.origin[0] = point[0];
+ camera.origin[1] = point[1];
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ }
+
+ // mbutton = angle camera
+ if (buttonstate == MK_MBUTTON)
+ {
+ VectorSubtract (point, camera.origin, point);
+ if (point[1] || point[0])
+ {
+ camera.angles[YAW] = 180/Q_PI*atan2 (point[1], point[0]);
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+ }
+ }
+
+ // shift mbutton = move z checker
+ if (buttonstate == (MK_SHIFT|MK_MBUTTON) )
+ {
+ XY_ToPoint (x, y, point);
+ z.origin[0] = point[0];
+ z.origin[1] = point[1];
+ Sys_UpdateWindows (W_XY_OVERLAY|W_Z);
+ return;
+ }
+
+}
+
+/*
+==============
+XY_MouseUp
+==============
+*/
+void XY_MouseUp (int x, int y, int buttons)
+{
+ Drag_MouseUp ();
+
+ if (!press_selection)
+ Sys_UpdateWindows (W_ALL);
+
+ buttonstate = 0;
+}
+
+qboolean DragDelta (int x, int y, vec3_t move)
+{
+ vec3_t xvec, yvec, delta;
+ int i;
+
+ xvec[0] = 1/g_qeglobals.d_xy.scale;
+ xvec[1] = xvec[2] = 0;
+ yvec[1] = 1/g_qeglobals.d_xy.scale;
+ yvec[0] = yvec[2] = 0;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ delta[i] = xvec[i]*(x - pressx) + yvec[i]*(y - pressy);
+ delta[i] = floor(delta[i]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+ }
+ VectorSubtract (delta, pressdelta, move);
+ VectorCopy (delta, pressdelta);
+
+ if (move[0] || move[1] || move[2])
+ return true;
+ return false;
+}
+
+/*
+==============
+NewBrushDrag
+==============
+*/
+void NewBrushDrag (int x, int y)
+{
+ vec3_t mins, maxs, junk;
+ int i;
+ float temp;
+ brush_t *n;
+
+ if (!DragDelta (x,y, junk))
+ return;
+ // delete the current selection
+ if (selected_brushes.next != &selected_brushes)
+ Brush_Free (selected_brushes.next);
+ XY_ToGridPoint (pressx, pressy, mins);
+ mins[2] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_bottom_z/g_qeglobals.d_gridsize));
+ XY_ToGridPoint (x, y, maxs);
+ maxs[2] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_top_z/g_qeglobals.d_gridsize));
+ if (maxs[2] <= mins[2])
+ maxs[2] = mins[2] + g_qeglobals.d_gridsize;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (mins[i] == maxs[i])
+ return; // don't create a degenerate brush
+ if (mins[i] > maxs[i])
+ {
+ temp = mins[i];
+ mins[i] = maxs[i];
+ maxs[i] = temp;
+ }
+ }
+
+ n = Brush_Create (mins, maxs, &g_qeglobals.d_texturewin.texdef);
+ if (!n)
+ return;
+
+ Brush_AddToList (n, &selected_brushes);
+
+ Entity_LinkBrush (world_entity, n);
+
+ Brush_Build( n );
+
+// Sys_UpdateWindows (W_ALL);
+ Sys_UpdateWindows (W_XY| W_CAMERA);
+}
+
+/*
+==============
+XY_MouseMoved
+==============
+*/
+void XY_MouseMoved (int x, int y, int buttons)
+{
+ vec3_t point;
+
+ if (!buttonstate)
+ return;
+
+ // lbutton without selection = drag new brush
+ if (buttonstate == MK_LBUTTON && !press_selection)
+ {
+ NewBrushDrag (x, y);
+ return;
+ }
+
+ // lbutton (possibly with control and or shift)
+ // with selection = drag selection
+ if (buttonstate & MK_LBUTTON)
+ {
+ Drag_MouseMoved (x, y, buttons);
+ Sys_UpdateWindows (W_XY_OVERLAY | W_CAMERA);
+ return;
+ }
+
+ // control mbutton = move camera
+ if (buttonstate == (MK_CONTROL|MK_MBUTTON) )
+ {
+ XY_ToPoint (x, y, point);
+ camera.origin[0] = point[0];
+ camera.origin[1] = point[1];
+ Sys_UpdateWindows (W_XY_OVERLAY | W_CAMERA);
+ return;
+ }
+
+ // shift mbutton = move z checker
+ if (buttonstate == (MK_SHIFT|MK_MBUTTON) )
+ {
+ XY_ToPoint (x, y, point);
+ z.origin[0] = point[0];
+ z.origin[1] = point[1];
+ Sys_UpdateWindows (W_XY_OVERLAY|W_Z);
+ return;
+ }
+
+ // mbutton = angle camera
+ if (buttonstate == MK_MBUTTON )
+ {
+ XY_ToPoint (x, y, point);
+ VectorSubtract (point, camera.origin, point);
+ if (point[1] || point[0])
+ {
+ camera.angles[YAW] = 180/Q_PI*atan2 (point[1], point[0]);
+ Sys_UpdateWindows (W_XY_OVERLAY | W_CAMERA);
+ }
+ return;
+ }
+
+ // rbutton = drag xy origin
+ if (buttonstate == MK_RBUTTON)
+ {
+ Sys_GetCursorPos (&x, &y);
+ if (x != cursorx || y != cursory)
+ {
+ g_qeglobals.d_xy.origin[0] -= (x-cursorx)/g_qeglobals.d_xy.scale;
+ g_qeglobals.d_xy.origin[1] += (y-cursory)/g_qeglobals.d_xy.scale;
+ Sys_SetCursorPos (cursorx, cursory);
+ Sys_UpdateWindows (W_XY | W_XY_OVERLAY);
+ }
+ return;
+ }
+}
+
+
+/*
+============================================================================
+
+DRAWING
+
+============================================================================
+*/
+
+
+/*
+==============
+XY_DrawGrid
+==============
+*/
+void XY_DrawGrid (void)
+{
+ float x, y, xb, xe, yb, ye;
+ int w, h;
+ char text[32];
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+
+ w = g_qeglobals.d_xy.width/2 / g_qeglobals.d_xy.scale;
+ h = g_qeglobals.d_xy.height/2 / g_qeglobals.d_xy.scale;
+
+ xb = g_qeglobals.d_xy.origin[0] - w;
+ if (xb < region_mins[0])
+ xb = region_mins[0];
+ xb = 64 * floor (xb/64);
+
+ xe = g_qeglobals.d_xy.origin[0] + w;
+ if (xe > region_maxs[0])
+ xe = region_maxs[0];
+ xe = 64 * ceil (xe/64);
+
+ yb = g_qeglobals.d_xy.origin[1] - h;
+ if (yb < region_mins[1])
+ yb = region_mins[1];
+ yb = 64 * floor (yb/64);
+
+ ye = g_qeglobals.d_xy.origin[1] + h;
+ if (ye > region_maxs[1])
+ ye = region_maxs[1];
+ ye = 64 * ceil (ye/64);
+
+ // draw major blocks
+
+ glColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR]);
+
+ if ( g_qeglobals.d_showgrid )
+ {
+
+ glBegin (GL_LINES);
+
+ for (x=xb ; x<=xe ; x+=64)
+ {
+ glVertex2f (x, yb);
+ glVertex2f (x, ye);
+ }
+ for (y=yb ; y<=ye ; y+=64)
+ {
+ glVertex2f (xb, y);
+ glVertex2f (xe, y);
+ }
+
+ glEnd ();
+
+ }
+
+ // draw minor blocks
+ if ( g_qeglobals.d_showgrid && g_qeglobals.d_gridsize*g_qeglobals.d_xy.scale >= 4)
+ {
+ glColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR]);
+
+ glBegin (GL_LINES);
+ for (x=xb ; x<xe ; x += g_qeglobals.d_gridsize)
+ {
+ if ( ! ((int)x & 63) )
+ continue;
+ glVertex2f (x, yb);
+ glVertex2f (x, ye);
+ }
+ for (y=yb ; y<ye ; y+=g_qeglobals.d_gridsize)
+ {
+ if ( ! ((int)y & 63) )
+ continue;
+ glVertex2f (xb, y);
+ glVertex2f (xe, y);
+ }
+ glEnd ();
+ }
+
+ // draw coordinate text if needed
+
+ if ( g_qeglobals.d_savedinfo.show_coordinates)
+ {
+ glColor4f(0, 0, 0, 0);
+
+ for (x=xb ; x<xe ; x+=64)
+ {
+ glRasterPos2f (x, g_qeglobals.d_xy.origin[1] + h - 6/g_qeglobals.d_xy.scale);
+ sprintf (text, "%i",(int)x);
+ glCallLists (strlen(text), GL_UNSIGNED_BYTE, text);
+ }
+ for (y=yb ; y<ye ; y+=64)
+ {
+ glRasterPos2f (g_qeglobals.d_xy.origin[0] - w + 1, y);
+ sprintf (text, "%i",(int)y);
+ glCallLists (strlen(text), GL_UNSIGNED_BYTE, text);
+ }
+ }
+}
+
+/*
+==============
+XY_DrawBlockGrid
+==============
+*/
+void XY_DrawBlockGrid (void)
+{
+ float x, y, xb, xe, yb, ye;
+ int w, h;
+ char text[32];
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+
+ w = g_qeglobals.d_xy.width/2 / g_qeglobals.d_xy.scale;
+ h = g_qeglobals.d_xy.height/2 / g_qeglobals.d_xy.scale;
+
+ xb = g_qeglobals.d_xy.origin[0] - w;
+ if (xb < region_mins[0])
+ xb = region_mins[0];
+ xb = 1024 * floor (xb/1024);
+
+ xe = g_qeglobals.d_xy.origin[0] + w;
+ if (xe > region_maxs[0])
+ xe = region_maxs[0];
+ xe = 1024 * ceil (xe/1024);
+
+ yb = g_qeglobals.d_xy.origin[1] - h;
+ if (yb < region_mins[1])
+ yb = region_mins[1];
+ yb = 1024 * floor (yb/1024);
+
+ ye = g_qeglobals.d_xy.origin[1] + h;
+ if (ye > region_maxs[1])
+ ye = region_maxs[1];
+ ye = 1024 * ceil (ye/1024);
+
+ // draw major blocks
+
+ glColor3f(0,0,1);
+ glLineWidth (2);
+
+ glBegin (GL_LINES);
+
+ for (x=xb ; x<=xe ; x+=1024)
+ {
+ glVertex2f (x, yb);
+ glVertex2f (x, ye);
+ }
+ for (y=yb ; y<=ye ; y+=1024)
+ {
+ glVertex2f (xb, y);
+ glVertex2f (xe, y);
+ }
+
+ glEnd ();
+ glLineWidth (1);
+
+ // draw coordinate text if needed
+
+ for (x=xb ; x<xe ; x+=1024)
+ for (y=yb ; y<ye ; y+=1024)
+ {
+ glRasterPos2f (x+512, y+512);
+ sprintf (text, "%i,%i",(int)floor(x/1024), (int)floor(y/1024) );
+ glCallLists (strlen(text), GL_UNSIGNED_BYTE, text);
+ }
+
+ glColor4f(0, 0, 0, 0);
+}
+
+
+void DrawCameraIcon (void)
+{
+ float x, y, a;
+
+ x = camera.origin[0];
+ y = camera.origin[1];
+ a = camera.angles[YAW]/180*Q_PI;
+
+ glColor3f (0.0, 0.0, 1.0);
+ glBegin(GL_LINE_STRIP);
+ glVertex3f (x-16,y,0);
+ glVertex3f (x,y+8,0);
+ glVertex3f (x+16,y,0);
+ glVertex3f (x,y-8,0);
+ glVertex3f (x-16,y,0);
+ glVertex3f (x+16,y,0);
+ glEnd ();
+
+ glBegin(GL_LINE_STRIP);
+ glVertex3f (x+48*cos(a+Q_PI/4), y+48*sin(a+Q_PI/4), 0);
+ glVertex3f (x, y, 0);
+ glVertex3f (x+48*cos(a-Q_PI/4), y+48*sin(a-Q_PI/4), 0);
+ glEnd ();
+
+}
+
+void DrawZIcon (void)
+{
+ float x, y;
+
+ x = z.origin[0];
+ y = z.origin[1];
+
+ glEnable (GL_BLEND);
+ glDisable (GL_TEXTURE_2D);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glDisable (GL_CULL_FACE);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4f (0.0, 0.0, 1.0, 0.25);
+ glBegin(GL_QUADS);
+ glVertex3f (x-8,y-8,0);
+ glVertex3f (x+8,y-8,0);
+ glVertex3f (x+8,y+8,0);
+ glVertex3f (x-8,y+8,0);
+ glEnd ();
+ glDisable (GL_BLEND);
+
+ glColor4f (0.0, 0.0, 1.0, 1);
+
+ glBegin(GL_LINE_LOOP);
+ glVertex3f (x-8,y-8,0);
+ glVertex3f (x+8,y-8,0);
+ glVertex3f (x+8,y+8,0);
+ glVertex3f (x-8,y+8,0);
+ glEnd ();
+
+ glBegin(GL_LINE_STRIP);
+ glVertex3f (x-4,y+4,0);
+ glVertex3f (x+4,y+4,0);
+ glVertex3f (x-4,y-4,0);
+ glVertex3f (x+4,y-4,0);
+ glEnd ();
+}
+
+
+/*
+==================
+FilterBrush
+==================
+*/
+BOOL FilterBrush(brush_t *pb)
+{
+ if (!pb->owner)
+ return FALSE; // during construction
+
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP)
+ {
+ if (!strncmp(pb->brush_faces->texdef.name, "clip", 4))
+ return TRUE;
+ }
+
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WATER)
+ {
+ if (pb->brush_faces->texdef.name[0] == '*')
+ return TRUE;
+ }
+
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_DETAIL)
+ {
+ if (pb->brush_faces->texdef.contents & CONTENTS_DETAIL)
+ return TRUE;
+ }
+
+ if (pb->owner == world_entity)
+ {
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD)
+ return TRUE;
+ return FALSE;
+ }
+ else if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT)
+ return TRUE;
+
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS)
+ {
+ if (!strncmp(pb->owner->eclass->name, "light", 5))
+ return TRUE;
+ }
+
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS)
+ {
+ if (!strncmp(pb->owner->eclass->name, "path", 4))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+=============================================================
+
+ PATH LINES
+
+=============================================================
+*/
+
+/*
+==================
+DrawPathLines
+
+Draws connections between entities.
+Needs to consider all entities, not just ones on screen,
+because the lines can be visible when neither end is.
+Called for both camera view and xy view.
+==================
+*/
+void DrawPathLines (void)
+{
+ int i, j, k;
+ vec3_t mid, mid1;
+ entity_t *se, *te;
+ brush_t *sb, *tb;
+ char *psz;
+ vec3_t dir, s1, s2;
+ vec_t len, f;
+ int arrows;
+ int num_entities;
+ char *ent_target[MAX_MAP_ENTITIES];
+ entity_t *ent_entity[MAX_MAP_ENTITIES];
+
+
+ num_entities = 0;
+ for (te = entities.next ; te != &entities && num_entities != MAX_MAP_ENTITIES ; te = te->next)
+ {
+ ent_target[num_entities] = ValueForKey (te, "target");
+ if (ent_target[num_entities][0])
+ {
+ ent_entity[num_entities] = te;
+ num_entities++;
+ }
+ }
+
+ for (se = entities.next ; se != &entities ; se = se->next)
+ {
+ psz = ValueForKey(se, "targetname");
+
+ if (psz == NULL || psz[0] == '\0')
+ continue;
+
+ sb = se->brushes.onext;
+ if (sb == &se->brushes)
+ continue;
+
+ for (k=0 ; k<num_entities ; k++)
+ {
+ if (strcmp (ent_target[k], psz))
+ continue;
+
+ te = ent_entity[k];
+ tb = te->brushes.onext;
+ if (tb == &te->brushes)
+ continue;
+
+ for (i=0 ; i<3 ; i++)
+ mid[i] = (sb->mins[i] + sb->maxs[i])*0.5;
+
+ for (i=0 ; i<3 ; i++)
+ mid1[i] = (tb->mins[i] + tb->maxs[i])*0.5;
+
+ VectorSubtract (mid1, mid, dir);
+ len = VectorNormalize (dir);
+ s1[0] = -dir[1]*8 + dir[0]*8;
+ s2[0] = dir[1]*8 + dir[0]*8;
+ s1[1] = dir[0]*8 + dir[1]*8;
+ s2[1] = -dir[0]*8 + dir[1]*8;
+
+ glColor3f (se->eclass->color[0], se->eclass->color[1], se->eclass->color[2]);
+
+ glBegin(GL_LINES);
+ glVertex3fv(mid);
+ glVertex3fv(mid1);
+
+ arrows = (int)(len / 256) + 1;
+
+ for (i=0 ; i<arrows ; i++)
+ {
+ f = len * (i + 0.5) / arrows;
+
+ for (j=0 ; j<3 ; j++)
+ mid1[j] = mid[j] + f*dir[j];
+ glVertex3fv (mid1);
+ glVertex3f (mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]);
+ glVertex3fv (mid1);
+ glVertex3f (mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]);
+ }
+
+ glEnd();
+ }
+ }
+
+ return;
+}
+
+//=============================================================
+
+
+/*
+==============
+XY_Draw
+==============
+*/
+void XY_Draw (void)
+{
+ brush_t *brush;
+ float w, h;
+ entity_t *e;
+ double start, end;
+ vec3_t mins, maxs;
+ int drawn, culled;
+ int i;
+
+ if (!active_brushes.next)
+ return; // not valid yet
+
+ if (g_qeglobals.d_xy.timing)
+ start = Sys_DoubleTime ();
+
+ //
+ // clear
+ //
+ g_qeglobals.d_xy.d_dirty = false;
+
+ glViewport(0, 0, g_qeglobals.d_xy.width, g_qeglobals.d_xy.height);
+ glClearColor (
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0],
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1],
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2],
+ 0);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ //
+ // set up viewpoint
+ //
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity ();
+
+ w = g_qeglobals.d_xy.width/2 / g_qeglobals.d_xy.scale;
+ h = g_qeglobals.d_xy.height/2 / g_qeglobals.d_xy.scale;
+ mins[0] = g_qeglobals.d_xy.origin[0] - w;
+ maxs[0] = g_qeglobals.d_xy.origin[0] + w;
+ mins[1] = g_qeglobals.d_xy.origin[1] - h;
+ maxs[1] = g_qeglobals.d_xy.origin[1] + h;
+
+ glOrtho (mins[0], maxs[0], mins[1], maxs[1], -8000, 8000);
+
+ //
+ // now draw the grid
+ //
+ XY_DrawGrid ();
+
+ //
+ // draw stuff
+ //
+ glShadeModel (GL_FLAT);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+ glColor3f(0, 0, 0);
+// glEnable (GL_LINE_SMOOTH);
+
+ drawn = culled = 0;
+
+ e = NULL;
+ for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
+ {
+ if (brush->mins[0] > maxs[0]
+ || brush->mins[1] > maxs[1]
+ || brush->maxs[0] < mins[0]
+ || brush->maxs[1] < mins[1] )
+ {
+ culled++;
+ continue; // off screen
+ }
+
+ if (FilterBrush (brush))
+ continue;
+ drawn++;
+ if (brush->owner != e)
+ {
+ e = brush->owner;
+ glColor3fv(e->eclass->color);
+ }
+ Brush_DrawXY( brush );
+ }
+
+ DrawPathLines ();
+
+ //
+ // draw pointfile
+ //
+ if ( g_qeglobals.d_pointfile_display_list)
+ glCallList (g_qeglobals.d_pointfile_display_list);
+
+ //
+ // draw block grid
+ //
+ if ( g_qeglobals.show_blocks)
+ XY_DrawBlockGrid ();
+
+ //
+ // now draw selected brushes
+ //
+ glTranslatef( g_qeglobals.d_select_translate[0], g_qeglobals.d_select_translate[1], g_qeglobals.d_select_translate[2]);
+
+ glColor3f(1.0, 0.0, 0.0);
+ glEnable (GL_LINE_STIPPLE);
+ glLineStipple (3, 0xaaaa);
+ glLineWidth (2);
+
+ for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+ {
+ drawn++;
+ Brush_DrawXY( brush );
+ }
+
+ glDisable (GL_LINE_STIPPLE);
+ glLineWidth (1);
+
+ // edge / vertex flags
+
+ if (g_qeglobals.d_select_mode == sel_vertex)
+ {
+ glPointSize (4);
+ glColor3f (0,1,0);
+ glBegin (GL_POINTS);
+ for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
+ glVertex3fv (g_qeglobals.d_points[i]);
+ glEnd ();
+ glPointSize (1);
+ }
+ else if (g_qeglobals.d_select_mode == sel_edge)
+ {
+ float *v1, *v2;
+
+ glPointSize (4);
+ glColor3f (0,0,1);
+ glBegin (GL_POINTS);
+ for (i=0 ; i<g_qeglobals.d_numedges ; i++)
+ {
+ v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1];
+ v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2];
+ glVertex3f ( (v1[0]+v2[0])*0.5,(v1[1]+v2[1])*0.5,(v1[2]+v2[2])*0.5);
+ }
+ glEnd ();
+ glPointSize (1);
+ }
+ glTranslatef (-g_qeglobals.d_select_translate[0], -g_qeglobals.d_select_translate[1], -g_qeglobals.d_select_translate[2]);
+
+ //
+ // now draw camera point
+ //
+ DrawCameraIcon ();
+ DrawZIcon ();
+
+ glFinish();
+ QE_CheckOpenGLForErrors();
+
+ if (g_qeglobals.d_xy.timing)
+ {
+ end = Sys_DoubleTime ();
+ Sys_Printf ("xy: %i ms\n", (int)(1000*(end-start)));
+ }
+}
+
+/*
+==============
+XY_Overlay
+==============
+*/
+void XY_Overlay (void)
+{
+ int w, h;
+ int r[4];
+ static vec3_t lastz;
+ static vec3_t lastcamera;
+
+
+ glViewport(0, 0, g_qeglobals.d_xy.width, g_qeglobals.d_xy.height);
+
+ //
+ // set up viewpoint
+ //
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity ();
+
+ w = g_qeglobals.d_xy.width/2 / g_qeglobals.d_xy.scale;
+ h = g_qeglobals.d_xy.height/2 / g_qeglobals.d_xy.scale;
+ glOrtho (g_qeglobals.d_xy.origin[0] - w, g_qeglobals.d_xy.origin[0] + w
+ , g_qeglobals.d_xy.origin[1] - h, g_qeglobals.d_xy.origin[1] + h, -8000, 8000);
+ //
+ // erase the old camera and z checker positions
+ // if the entire xy hasn't been redrawn
+ //
+ if (g_qeglobals.d_xy.d_dirty)
+ {
+ glReadBuffer (GL_BACK);
+ glDrawBuffer (GL_FRONT);
+
+ glRasterPos2f (lastz[0]-9, lastz[1]-9);
+ glGetIntegerv (GL_CURRENT_RASTER_POSITION,r);
+ glCopyPixels(r[0], r[1], 18,18, GL_COLOR);
+
+ glRasterPos2f (lastcamera[0]-50, lastcamera[1]-50);
+ glGetIntegerv (GL_CURRENT_RASTER_POSITION,r);
+ glCopyPixels(r[0], r[1], 100,100, GL_COLOR);
+ }
+ g_qeglobals.d_xy.d_dirty = true;
+
+ //
+ // save off underneath where we are about to draw
+ //
+ VectorCopy (z.origin, lastz);
+ VectorCopy (camera.origin, lastcamera);
+
+ glReadBuffer (GL_FRONT);
+ glDrawBuffer (GL_BACK);
+
+ glRasterPos2f (lastz[0]-9, lastz[1]-9);
+ glGetIntegerv (GL_CURRENT_RASTER_POSITION,r);
+ glCopyPixels(r[0], r[1], 18,18, GL_COLOR);
+
+ glRasterPos2f (lastcamera[0]-50, lastcamera[1]-50);
+ glGetIntegerv (GL_CURRENT_RASTER_POSITION,r);
+ glCopyPixels(r[0], r[1], 100,100, GL_COLOR);
+
+ //
+ // draw the new icons
+ //
+ glDrawBuffer (GL_FRONT);
+
+ glShadeModel (GL_FLAT);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+ glColor3f(0, 0, 0);
+
+ DrawCameraIcon ();
+ DrawZIcon ();
+
+ glDrawBuffer (GL_BACK);
+ glFinish();
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// window system independent camera view code
+
+typedef struct
+{
+ int width, height;
+
+ qboolean timing;
+
+ vec3_t origin; // at center of window
+ float scale;
+
+ float topclip, bottomclip;
+
+ qboolean d_dirty;
+} xy_t;
+
+BOOL XYExcludeBrush(brush_t *pb);
+
+void XY_Init (void);
+void XY_MouseDown (int x, int y, int buttons);
+void XY_MouseUp (int x, int y, int buttons);
+void XY_MouseMoved (int x, int y, int buttons);
+void XY_Draw (void);
+void XY_Overlay (void);
+
+BOOL FilterBrush(brush_t *pb);
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+#define PAGEFLIPS 2
+
+z_t z;
+
+/*
+============
+Z_Init
+============
+*/
+void Z_Init (void)
+{
+ z.origin[0] = 0;
+ z.origin[1] = 20;
+ z.origin[2] = 46;
+
+ z.scale = 1;
+}
+
+
+
+/*
+============================================================================
+
+ MOUSE ACTIONS
+
+============================================================================
+*/
+
+static int cursorx, cursory;
+
+/*
+==============
+Z_MouseDown
+==============
+*/
+void Z_MouseDown (int x, int y, int buttons)
+{
+ vec3_t org, dir, vup, vright;
+ brush_t *b;
+
+ Sys_GetCursorPos (&cursorx, &cursory);
+
+ vup[0] = 0; vup[1] = 0; vup[2] = 1/z.scale;
+
+ VectorCopy (z.origin, org);
+ org[2] += (y - (z.height/2))/z.scale;
+ org[1] = -8192;
+
+ b = selected_brushes.next;
+ if (b != &selected_brushes)
+ {
+ org[0] = (b->mins[0] + b->maxs[0])/2;
+ }
+
+ dir[0] = 0; dir[1] = 1; dir[2] = 0;
+
+ vright[0] = 0; vright[1] = 0; vright[2] = 0;
+
+ // LBUTTON = manipulate selection
+ // shift-LBUTTON = select
+ // middle button = grab texture
+ // ctrl-middle button = set entire brush to texture
+ // ctrl-shift-middle button = set single face to texture
+ if ( (buttons == MK_LBUTTON)
+ || (buttons == (MK_LBUTTON | MK_SHIFT))
+ || (buttons == MK_MBUTTON)
+// || (buttons == (MK_MBUTTON|MK_CONTROL))
+ || (buttons == (MK_MBUTTON|MK_SHIFT|MK_CONTROL)) )
+ {
+ Drag_Begin (x, y, buttons,
+ vright, vup,
+ org, dir);
+ return;
+ }
+
+ // control mbutton = move camera
+ if ((buttons == (MK_CONTROL|MK_MBUTTON) ) || (buttons == (MK_CONTROL|MK_LBUTTON)))
+ {
+ camera.origin[2] = org[2] ;
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z);
+ }
+
+
+}
+
+/*
+==============
+Z_MouseUp
+==============
+*/
+void Z_MouseUp (int x, int y, int buttons)
+{
+ Drag_MouseUp ();
+}
+
+/*
+==============
+Z_MouseMoved
+==============
+*/
+void Z_MouseMoved (int x, int y, int buttons)
+{
+ if (!buttons)
+ return;
+ if (buttons == MK_LBUTTON)
+ {
+ Drag_MouseMoved (x, y, buttons);
+ Sys_UpdateWindows (W_Z|W_CAMERA);
+ return;
+ }
+ // rbutton = drag z origin
+ if (buttons == MK_RBUTTON)
+ {
+ Sys_GetCursorPos (&x, &y);
+ if ( y != cursory)
+ {
+ z.origin[2] += y-cursory;
+ Sys_SetCursorPos (cursorx, cursory);
+ Sys_UpdateWindows (W_Z);
+ }
+ return;
+ }
+ // control mbutton = move camera
+ if ((buttons == (MK_CONTROL|MK_MBUTTON) ) || (buttons == (MK_CONTROL|MK_LBUTTON)))
+ {
+ camera.origin[2] = (y - (z.height/2))/z.scale;
+ Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z);
+ }
+
+}
+
+
+/*
+============================================================================
+
+DRAWING
+
+============================================================================
+*/
+
+
+/*
+==============
+Z_DrawGrid
+==============
+*/
+void Z_DrawGrid (void)
+{
+ float zz, zb, ze;
+ int w, h;
+ char text[32];
+
+ w = z.width/2 / z.scale;
+ h = z.height/2 / z.scale;
+
+ zb = z.origin[2] - h;
+ if (zb < region_mins[2])
+ zb = region_mins[2];
+ zb = 64 * floor (zb/64);
+
+ ze = z.origin[2] + h;
+ if (ze > region_maxs[2])
+ ze = region_maxs[2];
+ ze = 64 * ceil (ze/64);
+
+ // draw major blocks
+
+ glColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR]);
+
+ glBegin (GL_LINES);
+
+ glVertex2f (0, zb);
+ glVertex2f (0, ze);
+
+ for (zz=zb ; zz<ze ; zz+=64)
+ {
+ glVertex2f (-w, zz);
+ glVertex2f (w, zz);
+ }
+
+ glEnd ();
+
+ // draw minor blocks
+ if (g_qeglobals.d_showgrid && g_qeglobals.d_gridsize*z.scale >= 4)
+ {
+ glColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR]);
+
+ glBegin (GL_LINES);
+ for (zz=zb ; zz<ze ; zz+=g_qeglobals.d_gridsize)
+ {
+ if ( ! ((int)zz & 63) )
+ continue;
+ glVertex2f (-w, zz);
+ glVertex2f (w, zz);
+ }
+ glEnd ();
+ }
+
+ // draw coordinate text if needed
+
+ glColor4f(0, 0, 0, 0);
+
+ for (zz=zb ; zz<ze ; zz+=64)
+ {
+ glRasterPos2f (-w+1, zz);
+ sprintf (text, "%i",(int)zz);
+ glCallLists (strlen(text), GL_UNSIGNED_BYTE, text);
+ }
+}
+
+#define CAM_HEIGHT 48 // height of main part
+#define CAM_GIZMO 8 // height of the gizmo
+
+void ZDrawCameraIcon (void)
+{
+ float x, y;
+ int xCam = z.width/4;
+
+ x = 0;
+ y = camera.origin[2];
+
+ glColor3f (0.0, 0.0, 1.0);
+ glBegin(GL_LINE_STRIP);
+ glVertex3f (x-xCam,y,0);
+ glVertex3f (x,y+CAM_GIZMO,0);
+ glVertex3f (x+xCam,y,0);
+ glVertex3f (x,y-CAM_GIZMO,0);
+ glVertex3f (x-xCam,y,0);
+ glVertex3f (x+xCam,y,0);
+ glVertex3f (x+xCam,y-CAM_HEIGHT,0);
+ glVertex3f (x-xCam,y-CAM_HEIGHT,0);
+ glVertex3f (x-xCam,y,0);
+ glEnd ();
+
+}
+
+GLbitfield glbitClear = GL_COLOR_BUFFER_BIT; //HACK
+
+/*
+==============
+Z_Draw
+==============
+*/
+void Z_Draw (void)
+{
+ brush_t *brush;
+ float w, h;
+ double start, end;
+ qtexture_t *q;
+ float top, bottom;
+ vec3_t org_top, org_bottom, dir_up, dir_down;
+ int xCam = z.width/3;
+
+ if (!active_brushes.next)
+ return; // not valid yet
+
+ if (z.timing)
+ start = Sys_DoubleTime ();
+
+ //
+ // clear
+ //
+ glViewport(0, 0, z.width, z.height);
+
+ glClearColor (
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0],
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1],
+ g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2],
+ 0);
+
+ /* GL Bug */
+ /* When not using hw acceleration, gl will fault if we clear the depth
+ buffer bit on the first pass. The hack fix is to set the GL_DEPTH_BUFFER_BIT
+ only after Z_Draw() has been called once. Yeah, right. */
+ glClear(glbitClear);
+ glbitClear |= GL_DEPTH_BUFFER_BIT;
+
+ glMatrixMode(GL_PROJECTION);
+
+ glLoadIdentity ();
+ w = z.width/2 / z.scale;
+ h = z.height/2 / z.scale;
+ glOrtho (-w, w, z.origin[2]-h, z.origin[2]+h, -8, 8);
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+
+
+ //
+ // now draw the grid
+ //
+ Z_DrawGrid ();
+
+ //
+ // draw stuff
+ //
+
+ glDisable(GL_CULL_FACE);
+
+ glShadeModel (GL_FLAT);
+
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+
+
+ // draw filled interiors and edges
+ dir_up[0] = 0 ; dir_up[1] = 0; dir_up[2] = 1;
+ dir_down[0] = 0 ; dir_down[1] = 0; dir_down[2] = -1;
+ VectorCopy (z.origin, org_top);
+ org_top[2] = 4096;
+ VectorCopy (z.origin, org_bottom);
+ org_bottom[2] = -4096;
+
+ for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
+ {
+ if (brush->mins[0] >= z.origin[0]
+ || brush->maxs[0] <= z.origin[0]
+ || brush->mins[1] >= z.origin[1]
+ || brush->maxs[1] <= z.origin[1])
+ continue;
+
+ if (!Brush_Ray (org_top, dir_down, brush, &top))
+ continue;
+ top = org_top[2] - top;
+ if (!Brush_Ray (org_bottom, dir_up, brush, &bottom))
+ continue;
+ bottom = org_bottom[2] + bottom;
+
+ q = Texture_ForName (brush->brush_faces->texdef.name);
+ glColor3f (q->color[0], q->color[1], q->color[2]);
+ glBegin (GL_QUADS);
+ glVertex2f (-xCam, bottom);
+ glVertex2f (xCam, bottom);
+ glVertex2f (xCam, top);
+ glVertex2f (-xCam, top);
+ glEnd ();
+
+ glColor3f (1,1,1);
+ glBegin (GL_LINE_LOOP);
+ glVertex2f (-xCam, bottom);
+ glVertex2f (xCam, bottom);
+ glVertex2f (xCam, top);
+ glVertex2f (-xCam, top);
+ glEnd ();
+ }
+
+ //
+ // now draw selected brushes
+ //
+ for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+ {
+ if ( !(brush->mins[0] >= z.origin[0]
+ || brush->maxs[0] <= z.origin[0]
+ || brush->mins[1] >= z.origin[1]
+ || brush->maxs[1] <= z.origin[1]) )
+ {
+ if (Brush_Ray (org_top, dir_down, brush, &top))
+ {
+ top = org_top[2] - top;
+ if (Brush_Ray (org_bottom, dir_up, brush, &bottom))
+ {
+ bottom = org_bottom[2] + bottom;
+
+ q = Texture_ForName (brush->brush_faces->texdef.name);
+ glColor3f (q->color[0], q->color[1], q->color[2]);
+ glBegin (GL_QUADS);
+ glVertex2f (-xCam, bottom);
+ glVertex2f (xCam, bottom);
+ glVertex2f (xCam, top);
+ glVertex2f (-xCam, top);
+ glEnd ();
+ }
+ }
+ }
+
+ glColor3f (1,0,0);
+ glBegin (GL_LINE_LOOP);
+ glVertex2f (-xCam, brush->mins[2]);
+ glVertex2f (xCam, brush->mins[2]);
+ glVertex2f (xCam, brush->maxs[2]);
+ glVertex2f (-xCam, brush->maxs[2]);
+ glEnd ();
+ }
+
+
+ ZDrawCameraIcon ();
+
+ glFinish();
+ QE_CheckOpenGLForErrors();
+
+ if (z.timing)
+ {
+ end = Sys_DoubleTime ();
+ Sys_Printf ("z: %i ms\n", (int)(1000*(end-start)));
+ }
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// window system independent camera view code
+
+typedef struct
+{
+ int width, height;
+
+ qboolean timing;
+
+ vec3_t origin; // at center of window
+ float scale;
+} z_t;
+
+extern z_t z;
+
+void Z_Init (void);
+void Z_MouseDown (int x, int y, int buttons);
+void Z_MouseUp (int x, int y, int buttons);
+void Z_MouseMoved (int x, int y, int buttons);
+void Z_Draw (void);
+
--- /dev/null
+
+Texpaint works with three data types:
+
+Skin textures
+Model frames
+S/T mappings
+
+
+Skin textures can be either lbm or pcx files, and they will allways be
+saved out the same size as loaded in, even if it is larger than the
+active texture area.
+
+Model frames are alias .tri files. Adding support for 3ds would not
+be difficult, but it is likely that there would be coordinate problems
+unless the entire model was done completely in 3ds.
+
+S/T mappings allow a skin to be mapped onto any model frame. A mapping is
+generated from a base frame and a texture size. If a coords.txt file is
+not present, texpaint will generate a default mapping that is compatable
+with the output of the old texmake. New skin or remap to skin will
+generate a new coords.txt file.
+
+
+Usage
+-----
+A three button mouse is required.
+
+The left button paints with the current color in either the skin window
+or the camera window, or selects colors in the palette view. Ctrl-left
+click picks up the color clicked on in any view, making it current.
+
+Right button dragging slides the object or skin around. Ctrl-right drag
+to move in or out on the object.
+
+Middle button dragging rotates the object in the camera view. Ctrl-middle
+drag to change the roll angle.
+
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by texmake.rc
+//
+#define IDR_MENU1 101
+#define IDR_MENU2 102
+#define IDR_ACCELERATOR1 104
+#define IDD_NEWSKIN 105
+#define IDC_WIDTH 1000
+#define IDC_HEIGHT 1001
+#define ID_FILE_SAVEAS 4003
+#define ID_VIEW_MODELLINES 4006
+#define ID_VIEW_MODELLIGHTING 4008
+#define ID_FILE_OPENSKIN 4009
+#define ID_FILE_SAVESKIN 4010
+#define ID_FILE_SAVESKINAS 4011
+#define ID_FILE_EXIT 4012
+#define ID_FILE_OPENMODEL 4013
+#define ID_FILE_RELOADSKIN 4014
+#define ID_FILE_NEWSKIN 4016
+#define ID_FILE_OPENFRAME 4017
+#define ID_VIEW_SKINLINES 4018
+#define ID_VIEW_MODELINES 4019
+#define ID_VIEW_TEXTURELINES 4020
+#define ID_FILE_RESAMPLESKIN 4024
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 106
+#define _APS_NEXT_COMMAND_VALUE 4025
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
--- /dev/null
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENU2 MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&New skin\tCtrl-n", ID_FILE_NEWSKIN
+ MENUITEM "&Open skin\tCtrl-o", ID_FILE_OPENSKIN
+ MENUITEM "&Reload skin\tCtrl-r", ID_FILE_RELOADSKIN
+ MENUITEM "&Save skin\tCtrl-s", ID_FILE_SAVESKIN
+ MENUITEM "Save skin &as\tCtrl-a", ID_FILE_SAVESKINAS
+ MENUITEM "Resample skin", ID_FILE_RESAMPLESKIN
+ MENUITEM SEPARATOR
+ MENUITEM "Open &frame\tCtrl-f", ID_FILE_OPENFRAME
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", ID_FILE_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "&Undo\tCtrl-z", ID_EDIT_UNDO
+ MENUITEM "&Redo\tCtrl-y", ID_EDIT_REDO
+ END
+ POPUP "&View"
+ BEGIN
+ MENUITEM "&Model lines\tCtrl-m", ID_VIEW_MODELLINES
+ MENUITEM "&Texture lines\tCtrl-t", ID_VIEW_TEXTURELINES
+ MENUITEM "Model &lighting\tCtrl-l", ID_VIEW_MODELLIGHTING
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE
+BEGIN
+ "A", ID_FILE_SAVESKINAS, VIRTKEY, CONTROL, NOINVERT
+ "F", ID_FILE_OPENFRAME, VIRTKEY, CONTROL, NOINVERT
+ "L", ID_VIEW_MODELLIGHTING, VIRTKEY, CONTROL, NOINVERT
+ "M", ID_VIEW_MODELLINES, VIRTKEY, CONTROL, NOINVERT
+ "N", ID_FILE_NEWSKIN, VIRTKEY, CONTROL, NOINVERT
+ "O", ID_FILE_OPENSKIN, VIRTKEY, CONTROL, NOINVERT
+ "R", ID_FILE_RELOADSKIN, VIRTKEY, CONTROL, NOINVERT
+ "S", ID_FILE_SAVESKIN, VIRTKEY, CONTROL, NOINVERT
+ "T", ID_VIEW_TEXTURELINES, VIRTKEY, CONTROL, NOINVERT
+ "Y", ID_EDIT_REDO, VIRTKEY, CONTROL, NOINVERT
+ "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_NEWSKIN DIALOG DISCARDABLE 0, 0, 186, 95
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "New Skin"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,129,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14
+ EDITTEXT IDC_WIDTH,51,14,40,14,ES_AUTOHSCROLL
+ LTEXT "Width",IDC_STATIC,20,17,20,8
+ EDITTEXT IDC_HEIGHT,51,37,40,14,ES_AUTOHSCROLL
+ LTEXT "height",IDC_STATIC,19,40,20,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_NEWSKIN, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 88
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+triangle_t *faces;
+int numfaces;
+
+int skinwidth, skinheight;
+int picwidth, picheight;
+int width, height;
+int iwidth, iheight;
+int width2, height2; // padded to ^2
+
+float tmcoords[10000][3][2];
+
+byte pic[1024*512];
+unsigned rgb[1024*512];
+
+float scale;
+float s_scale, t_scale;
+
+char filename[1024];
+char picfilename[1024];
+
+
+/*
+================
+BoundFaces
+================
+*/
+vec3_t mins, maxs;
+
+void BoundFaces (void)
+{
+ int i,j,k;
+ triangle_t *pol;
+ float v;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ mins[i] = 9999;
+ maxs[i] = -9999;
+ }
+
+ for (i=0 ; i<numfaces ; i++)
+ {
+ pol = &faces[i];
+ for (j=0 ; j<3 ; j++)
+ for (k=0 ; k<3 ; k++)
+ {
+ v = pol->verts[j][k];
+ if (v<mins[k])
+ mins[k] = v;
+ if (v>maxs[k])
+ maxs[k] = v;
+ }
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ mins[i] = floor(mins[i]);
+ maxs[i] = ceil(maxs[i]);
+ }
+
+ width = maxs[0] - mins[0];
+ height = maxs[2] - mins[2];
+
+ printf ("width: %i height: %i\n",width, height);
+
+ if (!skinwidth)
+ { // old way
+ scale = 8;
+ if (width*scale >= 150)
+ scale = 150.0 / width;
+ if (height*scale >= 190)
+ scale = 190.0 / height;
+ s_scale = t_scale = scale;
+ iwidth = ceil(width*scale) + 4;
+ iheight = ceil(height*scale) + 4;
+ }
+ else
+ { // new way
+ s_scale = (skinwidth/2-4)/(float)width;
+ t_scale = (skinheight-4)/(float)height;
+ iwidth = skinwidth/2;
+ iheight = skinheight;
+ }
+
+ printf ("scale: %f\n",scale);
+ printf ("iwidth: %i iheight: %i\n",iwidth, iheight);
+}
+
+
+
+/*
+============
+AddFace
+============
+*/
+void AddFace (int facenum, triangle_t *f)
+{
+ vec3_t v1, v2, normal;
+ int basex, basey;
+ int i, j;
+ int coords[3][2];
+
+//
+// determine which side to map the teture to
+//
+ VectorSubtract (f->verts[0], f->verts[1], v1);
+ VectorSubtract (f->verts[2], f->verts[1], v2);
+ CrossProduct (v1, v2, normal);
+
+ if (normal[1] > 0)
+ basex = iwidth + 2;
+ else
+ basex = 2;
+ basey = 2;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ coords[i][0] = Q_rint((f->verts[i][0] - mins[0])*s_scale + basex);
+ coords[i][1] = Q_rint( (maxs[2] - f->verts[i][2])*t_scale + basey);
+tmcoords[facenum][i][0] = coords[i][0]/(float)width2;
+tmcoords[facenum][i][1] = coords[i][1]/(float)height2;
+ }
+
+}
+
+
+void CalcTmCoords (void)
+{
+ int j;
+
+ BoundFaces ();
+
+ for (j=0 ; j<numfaces ; j++)
+ AddFace (j, &faces[j]);
+
+ printf ("numfaces: %i\n",numfaces);
+}
+
+//===============================================================================
+
+
+
+#define MAX_NUM_ARGVS 32
+int argc;
+char *argv[MAX_NUM_ARGVS];
+
+/*
+============
+ParseCommandLine
+============
+*/
+void ParseCommandLine (char *lpCmdLine)
+{
+ argc = 1;
+ argv[0] = "programname";
+
+ while (*lpCmdLine && (argc < MAX_NUM_ARGVS))
+ {
+ while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
+ lpCmdLine++;
+
+ if (*lpCmdLine)
+ {
+ argv[argc] = lpCmdLine;
+ argc++;
+
+ while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
+ lpCmdLine++;
+
+ if (*lpCmdLine)
+ {
+ *lpCmdLine = 0;
+ lpCmdLine++;
+ }
+
+ }
+ }
+}
+
+/*
+=================
+LoadTriFile
+=================
+*/
+void LoadTriFile (char *name)
+{
+ strcpy (tri_filename, name);
+ SetWindowText (camerawindow, tri_filename);
+
+ LoadTriangleList (tri_filename, &faces, &numfaces);
+ InvalidateRect (camerawindow, NULL, false);
+}
+
+/*
+==================
+TimerProc
+
+==================
+*/
+int CALLBACK TimerProc(
+ HWND hwnd, // handle of window for timer messages
+ UINT uMsg, // WM_TIMER message
+ UINT idEvent, // timer identifier
+ DWORD dwTime // current system time
+ )
+{
+ static int counter;
+ char name[1024];
+
+ if (!skin_filename[0])
+ return 0;
+
+ if (!modified_past_autosave)
+ {
+ counter = 0;
+ return 0;
+ }
+
+ counter++;
+
+ if (counter < 3*5)
+ return 0; // save every five minutes
+
+ strcpy (name, skin_filename);
+ StripExtension (name);
+ strcat (name, "_autosave.lbm");
+ Skin_SaveFile (name);
+
+ modified_past_autosave = false;
+ counter = 0;
+
+ return 0;
+}
+
+/*
+==================
+WinMain
+
+==================
+*/
+int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance
+ ,LPSTR lpCmdLine, int nCmdShow)
+{
+ MSG msg;
+ HACCEL accelerators;
+
+ main_instance = hInstance;
+
+ ParseCommandLine (lpCmdLine);
+
+ screen_width = GetSystemMetrics (SM_CXFULLSCREEN);
+ screen_height = GetSystemMetrics (SM_CYFULLSCREEN);
+
+ // hack for broken NT 4.0 dual screen
+ if (screen_width > 2*screen_height)
+ screen_width /= 2;
+
+ accelerators = LoadAccelerators (hInstance
+ , MAKEINTRESOURCE(IDR_ACCELERATOR1));
+ if (!accelerators)
+ Sys_Error ("LoadAccelerators failed");
+
+ Main_Create (hInstance);
+ WCam_Create (hInstance);
+ WPal_Create (hInstance);
+ WSkin_Create (hInstance);
+
+ if (argc == 2)
+ Skin_LoadFile (argv[1]);
+
+ SetTimer ( mainwindow, 1, 1000*20, TimerProc );
+
+ while (1)
+ {
+ if (!GetMessage (&msg, mainwindow, 0, 0))
+ break;
+ if (!TranslateAccelerator(mainwindow, accelerators, &msg) )
+ {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ }
+
+ /* return success of application */
+ return TRUE;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "lbmlib.h"
+#include "trilib.h"
+#include "l3dslib.h"
+
+#include <windows.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glaux.h>
+#include "resource.h"
+#include "afxres.h"
+
+extern HINSTANCE main_instance;
+
+extern HGLRC baseRC;
+
+extern HWND mainwindow;
+extern HWND camerawindow;
+extern HWND palettewindow;
+extern HWND skinwindow;
+
+extern int screen_width, screen_height;
+
+#define QE3_STYLE (WS_OVERLAPPED| WS_CAPTION | WS_THICKFRAME | \
+ /* WS_MINIMIZEBOX | */ WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | \
+ WS_CLIPCHILDREN | WS_CHILD)
+
+extern byte pic[1024*512];
+extern unsigned rgb[1024*512];
+extern unsigned index_texture[1024*512];
+extern byte palette[768];
+
+extern triangle_t *faces;
+extern int numfaces;
+extern float tmcoords[10000][3][2];
+
+extern int skinwidth, skinheight;
+extern int picwidth, picheight;
+extern int width, height;
+extern int iwidth, iheight;
+extern int width2, height2; // padded to ^2
+
+extern char tri_filename[1024];
+extern char skin_filename[1024];
+
+extern int selected_index;
+extern unsigned selected_rgb;
+
+extern qboolean model_lines;
+extern qboolean skin_lines;
+
+extern qboolean modified;
+extern qboolean modified_past_autosave;
+
+#define TEXTURE_SKIN 1
+#define TEXTURE_INDEX 2
+
+#define MENU_VIEW 2
+
+#define MODEL_DISPLAYLIST 1
+
+typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint);
+
+extern BINDTEXFUNCPTR BindTextureEXT;
+
--- /dev/null
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+!IF "$(CFG)" == ""
+CFG=texpaint - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to texpaint - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "texpaint - Win32 Release" && "$(CFG)" !=\
+ "texpaint - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "texpaint.mak" CFG="texpaint - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "texpaint - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "texpaint - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "texpaint - Win32 Debug"
+MTL=mktyplib.exe
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "texpaint - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\texpaint.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\l3dslib.obj"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\texmake.res"
+ -@erase "$(INTDIR)\texpaint.obj"
+ -@erase "$(INTDIR)\trilib.obj"
+ -@erase "$(INTDIR)\win_cam.obj"
+ -@erase "$(INTDIR)\win_main.obj"
+ -@erase "$(INTDIR)\win_pal.obj"
+ -@erase "$(INTDIR)\win_skin.obj"
+ -@erase "$(OUTDIR)\texpaint.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_WINDOWS" /Fp"$(INTDIR)/texpaint.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/texmake.res" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/texpaint.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 opengl32.lib glu32.lib glaux.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+LINK32_FLAGS=opengl32.lib glu32.lib glaux.lib kernel32.lib user32.lib gdi32.lib\
+ winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\
+ uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no\
+ /pdb:"$(OUTDIR)/texpaint.pdb" /machine:I386 /out:"$(OUTDIR)/texpaint.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\l3dslib.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\texmake.res" \
+ "$(INTDIR)\texpaint.obj" \
+ "$(INTDIR)\trilib.obj" \
+ "$(INTDIR)\win_cam.obj" \
+ "$(INTDIR)\win_main.obj" \
+ "$(INTDIR)\win_pal.obj" \
+ "$(INTDIR)\win_skin.obj"
+
+"$(OUTDIR)\texpaint.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "texpaint - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "texpaint"
+# PROP BASE Intermediate_Dir "texpaint"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\texpaint.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\l3dslib.obj"
+ -@erase "$(INTDIR)\lbmlib.obj"
+ -@erase "$(INTDIR)\mathlib.obj"
+ -@erase "$(INTDIR)\scriplib.obj"
+ -@erase "$(INTDIR)\texmake.res"
+ -@erase "$(INTDIR)\texpaint.obj"
+ -@erase "$(INTDIR)\trilib.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(INTDIR)\win_cam.obj"
+ -@erase "$(INTDIR)\win_main.obj"
+ -@erase "$(INTDIR)\win_pal.obj"
+ -@erase "$(INTDIR)\win_skin.obj"
+ -@erase "$(OUTDIR)\texpaint.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "WIN_ERROR" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_WINDOWS" /D "WIN_ERROR" /Fp"$(INTDIR)/texpaint.pch" /YX /Fo"$(INTDIR)/"\
+ /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/texmake.res" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/texpaint.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+# ADD LINK32 opengl32.lib glu32.lib glaux.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386
+LINK32_FLAGS=opengl32.lib glu32.lib glaux.lib kernel32.lib user32.lib gdi32.lib\
+ winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\
+ uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /pdb:none /debug\
+ /machine:I386 /out:"$(OUTDIR)/texpaint.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\cmdlib.obj" \
+ "$(INTDIR)\l3dslib.obj" \
+ "$(INTDIR)\lbmlib.obj" \
+ "$(INTDIR)\mathlib.obj" \
+ "$(INTDIR)\scriplib.obj" \
+ "$(INTDIR)\texmake.res" \
+ "$(INTDIR)\texpaint.obj" \
+ "$(INTDIR)\trilib.obj" \
+ "$(INTDIR)\win_cam.obj" \
+ "$(INTDIR)\win_main.obj" \
+ "$(INTDIR)\win_pal.obj" \
+ "$(INTDIR)\win_skin.obj"
+
+"$(OUTDIR)\texpaint.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "texpaint - Win32 Release"
+# Name "texpaint - Win32 Debug"
+
+!IF "$(CFG)" == "texpaint - Win32 Release"
+
+!ELSEIF "$(CFG)" == "texpaint - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\texpaint.c
+DEP_CPP_TEXPA=\
+ "..\common\cmdlib.h"\
+ "..\common\l3dslib.h"\
+ "..\common\lbmlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\trilib.h"\
+ ".\texpaint.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\texpaint.obj" : $(SOURCE) $(DEP_CPP_TEXPA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\mathlib.c
+DEP_CPP_MATHL=\
+ "..\..\..\quake\utils2\common\cmdlib.h"\
+ "..\..\..\quake\utils2\common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\l3dslib.c
+DEP_CPP_L3DSL=\
+ "..\..\..\quake\utils2\common\cmdlib.h"\
+ "..\..\..\quake\utils2\common\l3dslib.h"\
+ "..\..\..\quake\utils2\common\mathlib.h"\
+ "..\..\..\quake\utils2\common\trilib.h"\
+
+
+"$(INTDIR)\l3dslib.obj" : $(SOURCE) $(DEP_CPP_L3DSL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\lbmlib.c
+DEP_CPP_LBMLI=\
+ "..\..\..\quake\utils2\common\cmdlib.h"\
+ "..\..\..\quake\utils2\common\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\scriplib.c
+DEP_CPP_SCRIP=\
+ "..\..\..\quake\utils2\common\cmdlib.h"\
+ "..\..\..\quake\utils2\common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\trilib.c
+DEP_CPP_TRILI=\
+ "..\..\..\quake\utils2\common\cmdlib.h"\
+ "..\..\..\quake\utils2\common\mathlib.h"\
+ "..\..\..\quake\utils2\common\trilib.h"\
+
+
+"$(INTDIR)\trilib.obj" : $(SOURCE) $(DEP_CPP_TRILI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_skin.c
+DEP_CPP_WIN_S=\
+ "..\common\cmdlib.h"\
+ "..\common\l3dslib.h"\
+ "..\common\lbmlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\trilib.h"\
+ ".\texpaint.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_skin.obj" : $(SOURCE) $(DEP_CPP_WIN_S) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_main.c
+DEP_CPP_WIN_M=\
+ "..\common\cmdlib.h"\
+ "..\common\l3dslib.h"\
+ "..\common\lbmlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\trilib.h"\
+ ".\texpaint.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_pal.c
+DEP_CPP_WIN_P=\
+ "..\common\cmdlib.h"\
+ "..\common\l3dslib.h"\
+ "..\common\lbmlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\trilib.h"\
+ ".\texpaint.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_pal.obj" : $(SOURCE) $(DEP_CPP_WIN_P) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_cam.c
+DEP_CPP_WIN_C=\
+ "..\common\cmdlib.h"\
+ "..\common\l3dslib.h"\
+ "..\common\lbmlib.h"\
+ "..\common\mathlib.h"\
+ "..\common\trilib.h"\
+ ".\texpaint.h"\
+ {$(INCLUDE)}"\gl\GL.H"\
+ {$(INCLUDE)}"\gl\GLAUX.H"\
+ {$(INCLUDE)}"\gl\GLU.H"\
+
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\texmake.rc
+
+"$(INTDIR)\texmake.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\texpaint.h
+
+!IF "$(CFG)" == "texpaint - Win32 Release"
+
+!ELSEIF "$(CFG)" == "texpaint - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\lbmlib.h
+
+!IF "$(CFG)" == "texpaint - Win32 Release"
+
+!ELSEIF "$(CFG)" == "texpaint - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.h
+
+!IF "$(CFG)" == "texpaint - Win32 Release"
+
+!ELSEIF "$(CFG)" == "texpaint - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ "..\..\..\quake\utils2\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\stat.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+#define CAMERA_WINDOW_CLASS "TPCamera"
+
+HDC camdc;
+HGLRC baseRC;
+
+float pitch, yaw, roll;
+qboolean model_lines = false;
+
+float cam_x, cam_y=-64, cam_z=32;
+
+int cam_width, cam_height;
+
+BINDTEXFUNCPTR BindTextureEXT;
+
+void InitIndexTexture (void)
+{
+ int i;
+
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_INDEX);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ for (i=0 ; i<sizeof(index_texture)/4 ; i++)
+ index_texture[i] = i+1;
+
+ glTexImage2D (GL_TEXTURE_2D, 0, 3, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, index_texture);
+
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+}
+
+void CreateDisplaylist (void)
+{
+}
+
+void DrawModel (void)
+{
+ int i, j;
+
+ glColor4f (1,1,1,1);
+
+ glBegin (GL_TRIANGLES);
+ for (i=0 ; i<numfaces ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ glTexCoord2f (tmcoords[i][j][0], tmcoords[i][j][1]);
+ glVertex3fv (faces[i].verts[j]);
+ }
+ }
+ glEnd ();
+}
+
+/*
+=============
+Cam_Click
+=============
+*/
+int cam_last_index;
+void Cam_Click (int x, int y, qboolean shift)
+{
+ int index;
+ index = 0;
+ glReadBuffer (GL_BACK);
+ glReadPixels (x, y, 1,1, GL_RGB, GL_UNSIGNED_BYTE, &index);
+
+ index--;
+ if (index == -1)
+ return;
+ if (index >= width2*height2)
+ return;
+
+ if (index == cam_last_index)
+ return; // in same pixel
+ cam_last_index = index;
+ if (shift)
+ {
+ Pal_SetIndex (pic[index]);
+ return;
+ }
+
+ SetSkin (index, selected_rgb);
+ UpdateWindow (camerawindow);
+}
+
+
+void Cam_DrawSetup (void)
+{
+ glViewport (0,0,cam_width, cam_height);
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ gluPerspective (90, (float)cam_width/cam_height, 2, 1024);
+ gluLookAt (cam_x, cam_y, cam_z, cam_x, cam_y+1, cam_z, 0, 0, 1);
+
+ glRotated (-roll*0.3, 0, 1, 0);
+ glRotated (-pitch*0.3, 1, 0, 0);
+ glRotated (yaw*0.3, 0, 0, 1);
+
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ glEnable (GL_DEPTH_TEST);
+ glEnable (GL_CULL_FACE);
+ glEnable (GL_TEXTURE_2D);
+ glCullFace (GL_FRONT);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+void Cam_Draw (void)
+{
+ if (!cam_width || !cam_height)
+ return;
+
+ glClearColor (0.3,0.3,0.3,1);
+ Cam_DrawSetup ();
+
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+
+ DrawModel ();
+
+ if (model_lines)
+ {
+ glDisable (GL_TEXTURE_2D);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+ glDepthFunc (GL_LEQUAL);
+ glDepthRange (0, 0.999); // nudge depth to avoid dropouts
+ DrawModel ();
+ glDepthRange (0, 1);
+
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glEnable (GL_TEXTURE_2D);
+ }
+
+ SwapBuffers(camdc);
+
+ // now fill the back buffer with the index texture
+ glClearColor (0,0,0,0);
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_INDEX);
+ DrawModel ();
+
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+}
+
+
+
+/*
+============
+CameraWndProc
+============
+*/
+LONG WINAPI WCam_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ LONG lRet = 1;
+ int fwKeys, xPos, yPos;
+ RECT rect;
+ static int oldx, oldy;
+ POINT pt;
+
+ GetClientRect(hWnd, &rect);
+ cam_width = rect.right-rect.left;
+ cam_height = rect.bottom-rect.top;
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ camdc = GetDC(hWnd);
+ bSetupPixelFormat(camdc);
+
+ baseRC = wglCreateContext( camdc );
+ if (!baseRC)
+ Sys_Error ("wglCreateContext failed");
+ if (!wglMakeCurrent( camdc, baseRC ))
+ Sys_Error ("wglMakeCurrent failed");
+ BindTextureEXT = (void *)wglGetProcAddress((LPCSTR) "glBindTextureEXT");
+ if (!BindTextureEXT)
+ Sys_Error ("GetProcAddress for BindTextureEXT failed");
+
+ break;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+
+ BeginPaint(hWnd, &ps);
+ if (!wglMakeCurrent( camdc, baseRC ))
+ Sys_Error ("wglMakeCurrent failed");
+ Cam_Draw ();
+ EndPaint(hWnd, &ps);
+ }
+ break;
+
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ if (GetTopWindow(mainwindow) != hWnd)
+ BringWindowToTop(hWnd);
+
+ SetFocus (camerawindow);
+ SetCapture (camerawindow);
+ GetCursorPos (&pt);
+ xPos = pt.x;
+ yPos = pt.y;
+ oldx = xPos;
+ oldy = yPos;
+ break;
+
+ case WM_LBUTTONDOWN:
+ cam_last_index = -1;
+draw:
+ if (GetTopWindow(mainwindow) != hWnd)
+ BringWindowToTop(hWnd);
+
+ SetFocus (camerawindow);
+ SetCapture (camerawindow);
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ if (!wglMakeCurrent( camdc, baseRC ))
+ Sys_Error ("wglMakeCurrent failed");
+
+ Cam_Click (xPos, yPos, !!(wParam&(MK_SHIFT|MK_CONTROL)) );
+
+// Cam_MouseDown (xPos, yPos, fwKeys);
+ break;
+
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ if (! (wParam & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+ ReleaseCapture ();
+ break;
+
+ case WM_MOUSEMOVE:
+ {
+ int dx, dy;
+
+ if (wParam & MK_LBUTTON)
+ goto draw;
+
+ GetCursorPos (&pt);
+ xPos = pt.x;
+ yPos = pt.y;
+ if (!(wParam & (MK_RBUTTON|MK_MBUTTON)))
+ {
+ oldx = xPos;
+ oldy = yPos;
+ break;
+ }
+ dx = xPos-oldx;
+ dy = oldy-yPos;
+ if (!dx && !dy)
+ break;
+ SetCursorPos (oldx, oldy);
+
+ if (wParam == (MK_RBUTTON|MK_CONTROL) )
+ {
+ if (abs(dx) > abs(dy))
+ cam_y -= 0.1*dx;
+ else
+ cam_y -= 0.1*dy;
+ InvalidateRect (camerawindow, NULL, false);
+ }
+ if (wParam == MK_RBUTTON)
+ {
+ cam_x -= 0.1*dx;
+ cam_z -= 0.1*dy;
+ InvalidateRect (camerawindow, NULL, false);
+ }
+ if (wParam == (MK_MBUTTON|MK_CONTROL) )
+ {
+ if (abs(dx) > abs(dy))
+ roll -= dx;
+ else
+ roll -= dy;
+ InvalidateRect (camerawindow, NULL, false);
+ }
+ if (wParam == MK_MBUTTON)
+ {
+ yaw += dx;
+ pitch += dy;
+ InvalidateRect (camerawindow, NULL, false);
+ }
+ }
+ break;
+
+
+
+ case WM_SIZE:
+// camera.width = rect.right;
+// camera.height = rect.bottom;
+ InvalidateRect(camerawindow, NULL, false);
+ break;
+ case WM_NCCALCSIZE:// don't let windows copy pixels
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ return WVR_REDRAW;
+ case WM_CLOSE:
+ /* call destroy window to cleanup and go away */
+ DestroyWindow (hWnd);
+ break;
+
+ case WM_DESTROY:
+ {
+ HGLRC hRC;
+ HDC hDC;
+
+ /* release and free the device context and rendering context */
+ hRC = wglGetCurrentContext();
+ hDC = wglGetCurrentDC();
+
+ wglMakeCurrent(NULL, NULL);
+
+ if (hRC)
+ wglDeleteContext(hRC);
+ if (hDC)
+ ReleaseDC(hWnd, hDC);
+ }
+ break;
+
+ default:
+ /* pass all unhandled messages to DefWindowProc */
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ /* return 1 if handled message, 0 if not */
+ return lRet;
+}
+
+
+/*
+==============
+WCam_Register
+==============
+*/
+void WCam_Register (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WCam_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = CAMERA_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Sys_Error ("WCam_Register: failed");
+}
+
+
+void WCam_Create (HINSTANCE hInstance)
+{
+ WCam_Register (hInstance);
+
+ camerawindow = CreateWindow (CAMERA_WINDOW_CLASS ,
+ "Camera View",
+ QE3_STYLE,
+ 0,
+ 0,
+ (int)(screen_width*0.5),
+ (int)(screen_height-20), // size
+
+ mainwindow, // parent window
+ 0, // no menu
+ hInstance,
+ 0);
+ if (!camerawindow)
+ Sys_Error ("Couldn't create camerawindow");
+
+ RestoreWindowState(camerawindow, "camerawindow");
+ ShowWindow (camerawindow, SW_SHOWDEFAULT);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+HINSTANCE main_instance;
+
+int screen_width, screen_height;
+
+HWND mainwindow;
+HWND camerawindow;
+HWND palettewindow;
+HWND skinwindow;
+
+/*
+=================
+Sys_Error
+
+For abnormal program terminations
+=================
+*/
+void Sys_Error (char *error, ...)
+{
+ va_list argptr;
+ char text[1024];
+ char text2[1024];
+ int err;
+
+ err = GetLastError ();
+
+ va_start (argptr,error);
+ vsprintf (text, error,argptr);
+ va_end (argptr);
+
+ sprintf (text2, "%s\nGetLastError() = %i", text, err);
+ MessageBox(mainwindow, text2, "Error", 0 /* MB_OK */ );
+
+ exit (1);
+}
+
+
+/*
+======================================================================
+
+FILE DIALOGS
+
+======================================================================
+*/
+
+qboolean modified;
+qboolean modified_past_autosave;
+
+qboolean ConfirmModified (void)
+{
+ if (!modified)
+ return true;
+
+ if (MessageBox (mainwindow, "This will lose changes to the skin"
+ , "warning", MB_OKCANCEL) == IDCANCEL)
+ return false;
+ return true;
+}
+
+OPENFILENAME ofn; /* common dialog box structure */
+char szDirName[MAX_PATH]; /* directory string */
+char szFile[260]; /* filename string */
+char szFileTitle[260]; /* file title string */
+char szSkinFilter[260] = /* filter string */
+ "Skin texture (*.lbm *.pcx)\0*.lbm;*.pcx\0\0";
+char szFrameFilter[260] = /* filter string */
+ "Model frame (*.tri)\0*.tri\0\0";
+char chReplace; /* string separator for szFilter */
+int i, cbString; /* integer count variables */
+HANDLE hf; /* file handle */
+
+void OpenSkinDialog (void)
+{
+// strcpy (szDirName, ValueForKey (project_entity, "basepath") );
+// strcat (szDirName, "\\maps");
+
+ /* Place the terminating null character in the szFile. */
+
+ szFile[0] = '\0';
+
+ /* Set the members of the OPENFILENAME structure. */
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = mainwindow;
+ ofn.lpstrFilter = szSkinFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+ OFN_FILEMUSTEXIST;
+
+ /* Display the Open dialog box. */
+
+ if (!GetOpenFileName(&ofn))
+ return; // canceled
+
+ Skin_LoadFile (ofn.lpstrFile);
+}
+
+void OpenFrameDialog (void)
+{
+// strcpy (szDirName, ValueForKey (project_entity, "basepath") );
+// strcat (szDirName, "\\maps");
+
+ /* Place the terminating null character in the szFile. */
+
+ szFile[0] = '\0';
+
+ /* Set the members of the OPENFILENAME structure. */
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = mainwindow;
+ ofn.lpstrFilter = szFrameFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+ OFN_FILEMUSTEXIST;
+
+ /* Display the Open dialog box. */
+
+ if (!GetOpenFileName(&ofn))
+ return; // canceled
+
+ LoadTriFile (ofn.lpstrFile);
+}
+
+void SaveSkinDialog (void)
+{
+// strcpy (szDirName, ValueForKey (project_entity, "basepath") );
+// strcat (szDirName, "\\maps");
+
+ /* Place the terminating null character in the szFile. */
+
+ szFile[0] = '\0';
+
+ /* Set the members of the OPENFILENAME structure. */
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = mainwindow;
+ ofn.lpstrFilter = szSkinFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+ OFN_FILEMUSTEXIST;
+
+ /* Display the Open dialog box. */
+
+ if (!GetSaveFileName(&ofn))
+ return; // canceled
+
+ DefaultExtension (ofn.lpstrFile, ".lbm");
+ Skin_SaveFile (ofn.lpstrFile);
+ strcpy (skin_filename, ofn.lpstrFile);
+}
+
+//==========================================================================
+
+BOOL bSetupPixelFormat(HDC hDC)
+{
+ static PIXELFORMATDESCRIPTOR pfd = {
+ sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
+ 1, // version number
+ PFD_DRAW_TO_WINDOW | // support window
+ PFD_SUPPORT_OPENGL | // support OpenGL
+ PFD_DOUBLEBUFFER, // double buffered
+ PFD_TYPE_RGBA, // RGBA type
+ 24, // 24-bit color depth
+ 0, 0, 0, 0, 0, 0, // color bits ignored
+ 0, // no alpha buffer
+ 0, // shift bit ignored
+ 0, // no accumulation buffer
+ 0, 0, 0, 0, // accum bits ignored
+ 32, // 32-bit z-buffer
+ 0, // no stencil buffer
+ 0, // no auxiliary buffer
+ PFD_MAIN_PLANE, // main layer
+ 0, // reserved
+ 0, 0, 0 // layer masks ignored
+ };
+ int pixelformat = 0;
+ PIXELFORMATDESCRIPTOR newp;
+
+ if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
+ {
+ printf("%d",GetLastError());
+ Error ("ChoosePixelFormat failed");
+ }
+ if (!SetPixelFormat(hDC, pixelformat, &pfd))
+ Error ("SetPixelFormat failed");
+
+ return TRUE;
+}
+
+
+/*
+==============================================================================
+
+ MENU
+
+==============================================================================
+*/
+
+
+/* handle all WM_COMMAND messages here */
+LONG WINAPI CommandHandler (
+ HWND hWnd,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ unsigned short cmd;
+
+ cmd = LOWORD(wParam);
+
+ switch (cmd)
+ {
+ //
+ // file menu
+ //
+ case ID_FILE_RESAMPLESKIN:
+ ResampleSkin ();
+ break;
+
+ case ID_FILE_NEWSKIN:
+ NewSkin ();
+ break;
+
+ case ID_FILE_OPENFRAME:
+ OpenFrameDialog ();
+ break;
+
+ case ID_FILE_OPENSKIN:
+ if (!ConfirmModified())
+ break;
+ OpenSkinDialog ();
+ break;
+
+ case ID_FILE_RELOADSKIN:
+ if (!ConfirmModified())
+ break;
+ Skin_LoadFile (skin_filename);
+ break;
+
+ case ID_FILE_SAVESKIN:
+ Skin_SaveFile (skin_filename);
+ break;
+
+ case ID_FILE_SAVESKINAS:
+ SaveSkinDialog ();
+ break;
+ case ID_FILE_EXIT:
+ if (!ConfirmModified())
+ break;
+ PostQuitMessage (0);
+ break;
+
+ //
+ // edit menu
+ //
+ case ID_EDIT_UNDO:
+ Undo();
+ break;
+ case ID_EDIT_REDO:
+ Redo();
+ break;
+
+ //
+ // view menu
+ //
+ case ID_VIEW_MODELLINES:
+ model_lines ^= 1;
+ CheckMenuItem ( GetSubMenu (GetMenu(mainwindow), MENU_VIEW)
+ , ID_VIEW_MODELLINES
+ , MF_BYCOMMAND | (model_lines ? MF_CHECKED : MF_UNCHECKED) );
+ InvalidateRect (camerawindow, NULL, false);
+ break;
+ case ID_VIEW_TEXTURELINES:
+ skin_lines ^= 1;
+ CheckMenuItem ( GetSubMenu (GetMenu(mainwindow), MENU_VIEW)
+ , ID_VIEW_TEXTURELINES
+ , MF_BYCOMMAND | (skin_lines ? MF_CHECKED : MF_UNCHECKED) );
+ InvalidateRect (skinwindow, NULL, false);
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+============
+WMAIN_WndProc
+============
+*/
+LONG WINAPI WMAIN_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ LONG lRet = 1;
+ RECT rect;
+ HDC maindc;
+
+ GetClientRect(hWnd, &rect);
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ maindc = GetDC(hWnd);
+ bSetupPixelFormat(maindc);
+ break;
+ case WM_COMMAND:
+ lRet = CommandHandler (hWnd, wParam, lParam);
+ break;
+
+ case WM_CLOSE:
+ if (!ConfirmModified())
+ break;
+ PostQuitMessage (0);
+ break;
+ default:
+ /* pass all unhandled messages to DefWindowProc */
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ /* return 1 if handled message, 0 if not */
+ return lRet;
+}
+
+
+
+
+/*
+==============
+Main_Create
+==============
+*/
+void Main_Create (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+
+ /* Register the class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WMAIN_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
+ wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU2);
+ wc.lpszClassName = "TEXPAINT_MAIN";
+
+ if (!RegisterClass (&wc) )
+ Error ("WCam_Register: failed");
+
+
+ mainwindow = CreateWindow ("TEXPAINT_MAIN" ,
+ "Texpaint",
+ WS_OVERLAPPEDWINDOW |
+ WS_CLIPSIBLINGS |
+ WS_CLIPCHILDREN,
+ 0,0,screen_width,screen_height, // size
+ 0,
+ NULL, // no menu
+ hInstance,
+ NULL);
+ if (!mainwindow)
+ Error ("Couldn't create main window");
+
+// GetWindowInfo("mainwindow", &SavedInfo, NULL);
+ ShowWindow (mainwindow, SW_SHOWDEFAULT);
+}
+
+
+
+
+BOOL SaveWindowInfo(const char *pszName, void *pvBuf, long lSize)
+{
+ LONG lres;
+ DWORD dwDisp;
+ HKEY hKeyId;
+
+ lres = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\id\\Texpaint", 0, NULL,
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyId, &dwDisp);
+
+ if (lres != ERROR_SUCCESS)
+ return FALSE;
+
+ lres = RegSetValueEx(hKeyId, pszName, 0, REG_BINARY, pvBuf, lSize);
+
+ RegCloseKey(hKeyId);
+
+ if (lres != ERROR_SUCCESS)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+BOOL GetWindowInfo(const char *pszName, void *pvBuf, long *plSize)
+{
+ HKEY hKey;
+ long lres, lType, lSize;
+
+ if (plSize == NULL)
+ plSize = &lSize;
+
+ lres = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\id\\Texpaint", 0, KEY_READ, &hKey);
+
+ if (lres != ERROR_SUCCESS)
+ return FALSE;
+
+ lres = RegQueryValueEx(hKey, pszName, NULL, &lType, pvBuf, plSize);
+
+ RegCloseKey(hKey);
+
+ if (lres != ERROR_SUCCESS)
+ return FALSE;
+
+ return TRUE;
+
+}
+
+BOOL SaveWindowState(HWND hWnd, const char *pszName)
+{
+ RECT rc;
+
+ GetWindowRect(hWnd, &rc);
+ MapWindowPoints(NULL, mainwindow, (POINT *)&rc, 2);
+ return SaveWindowInfo(pszName, &rc, sizeof(rc));
+}
+
+
+BOOL RestoreWindowState(HWND hWnd, const char *pszName)
+{
+ RECT rc;
+ LONG lSize = sizeof(rc);
+
+ if (GetWindowInfo(pszName, &rc, &lSize))
+ {
+ if (rc.left < 0)
+ rc.left = 0;
+ if (rc.top < 0)
+ rc.top = 0;
+ if (rc.right < rc.left + 16)
+ rc.right = rc.left + 16;
+ if (rc.bottom < rc.top + 16)
+ rc.bottom = rc.top + 16;
+
+ MoveWindow(hWnd, rc.left, rc.top, rc.right - rc.left,
+ rc.bottom - rc.top, FALSE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+#define PALETTE_WINDOW_CLASS "TPPalette"
+
+HDC paldc;
+int pal_width, pal_height;
+int blocks_x, blocks_y;
+
+int selected_index;
+unsigned selected_rgb;
+
+byte palette[768];
+
+float SnapAspect (float aspect)
+{
+ if (aspect > 128)
+ return 256;
+ if (aspect > 32)
+ return 128;
+ if (aspect > 8)
+ return 64;
+ if (aspect > 2)
+ return 32;
+ return 16;
+}
+
+void Pal_SetIndex (int index)
+{
+ selected_index = index;
+ selected_rgb = palette[index*3] + (palette[index*3+1]<<8) + (palette[index*3+2]<<16);
+ InvalidateRect (palettewindow, NULL, false);
+}
+
+void Pal_Draw (void)
+{
+ int x, y;
+ float aspect;
+ float xs, ys;
+ int c;
+
+ if (pal_width < 1 || pal_height < 1)
+ return;
+
+ //
+ // determine the block arrangement
+ //
+ if (pal_width > pal_height)
+ {
+ aspect = SnapAspect (pal_width / pal_height);
+ blocks_x = aspect;
+ blocks_y = 256/blocks_x;
+ }
+ else
+ {
+ aspect = SnapAspect (pal_height / pal_width);
+ blocks_y = aspect;
+ blocks_x = 256/blocks_y;
+ }
+
+ //
+ // draw it
+ //
+ glViewport (0,0,pal_width, pal_height);
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0,1,0,1,-100,100);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+
+ glClear (GL_COLOR_BUFFER_BIT);
+ glDisable (GL_DEPTH_TEST);
+ glDisable (GL_CULL_FACE);
+ glDisable (GL_TEXTURE_2D);
+
+ xs = 1.0/blocks_x;
+ ys = 1.0/blocks_y;
+
+ for (x=0 ; x<blocks_x ; x++)
+ {
+ for (y=0 ; y<blocks_y ; y++)
+ {
+ c = x*blocks_y+(blocks_y-1-y);
+ glColor3ubv (palette+c*3);
+ glRectf (x*xs, y*ys, (x+1)*xs, (y+1)*ys);
+ }
+ }
+
+ // highlight the selected texture
+ y = selected_index % blocks_y;
+ x = selected_index / blocks_y;
+ y = blocks_y-1-y;
+
+ glColor3f (0,0,0);
+ glRectf ( (x+0.4)*xs, (y+0.4)*ys, (x+0.6)*xs, (y+0.6)*ys);
+}
+
+void Pal_Click (int x, int y)
+{
+ int index;
+
+ x = x*blocks_x/pal_width;
+ y = y*blocks_y/pal_height;
+ y = blocks_y-1-y;
+
+ index = x*blocks_y + y;
+ Pal_SetIndex (index);
+}
+
+/*
+============
+Palette_WndProc
+============
+*/
+LONG WINAPI Palette_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ LONG lRet = 1;
+ int fwKeys, xPos, yPos;
+ RECT rect;
+
+ GetClientRect(hWnd, &rect);
+ pal_width = rect.right-rect.left;
+ pal_height = rect.bottom-rect.top;
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ paldc = GetDC(hWnd);
+ bSetupPixelFormat(paldc);
+ break;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+
+ BeginPaint(hWnd, &ps);
+ if (!wglMakeCurrent( paldc, baseRC ))
+ Error ("wglMakeCurrent failed");
+ Pal_Draw ();
+ EndPaint(hWnd, &ps);
+ SwapBuffers(paldc);
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (wParam != MK_LBUTTON)
+ break;
+ case WM_LBUTTONDOWN:
+ if (GetTopWindow(mainwindow) != hWnd)
+ BringWindowToTop(hWnd);
+
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+
+ Pal_Click (xPos, yPos);
+ break;
+
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ ReleaseCapture ();
+ break;
+
+ case WM_SIZE:
+ InvalidateRect(skinwindow, NULL, false);
+ break;
+ case WM_NCCALCSIZE:// don't let windows copy pixels
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ return WVR_REDRAW;
+ case WM_CLOSE:
+ /* call destroy window to cleanup and go away */
+ DestroyWindow (hWnd);
+ break;
+
+ default:
+ /* pass all unhandled messages to DefWindowProc */
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ /* return 1 if handled message, 0 if not */
+ return lRet;
+}
+
+
+/*
+==============
+WPal_Create
+==============
+*/
+void WPal_Create (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+
+ /* Register the skin class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)Palette_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = PALETTE_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Error ("RegisterClass failed");
+
+ palettewindow = CreateWindow (PALETTE_WINDOW_CLASS ,
+ "Palette View",
+ QE3_STYLE,
+ (int)(screen_width*0.5),
+ 0,
+ (int)(screen_width*0.5),
+ (int)(screen_height*.2), // size
+ mainwindow, // parent window
+ 0, // no menu
+ hInstance,
+ 0);
+ if (!palettewindow)
+ Error ("Couldn't create palettewindow");
+
+// RestoreWindowState(palettewindow, "palettewindow");
+ ShowWindow (palettewindow, SW_SHOWDEFAULT);
+}
--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code 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.
+
+Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+#define SKIN_WINDOW_CLASS "TPSkin"
+
+HDC skindc;
+int skinw_width, skinw_height; // size of the window
+
+float skin_x = 128, skin_y = 128, skin_z = 100;
+
+qboolean skin_lines = false;
+
+char tri_filename[1024];
+char skin_filename[1024];
+int skin_width, skin_height; // size of the .lbm image
+
+unsigned index_texture[1024*512];
+
+
+void UpdateTexture (int offset)
+{
+ int x, y;
+
+ y = offset / width2;
+ x = offset % width2;
+
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+// glTexImage2D (GL_TEXTURE_2D, 0, 3, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgb);
+
+ glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgb+offset);
+}
+
+/*
+===================================================================
+
+ TEXEL MODIFICATION
+
+===================================================================
+*/
+
+#define MAX_MODIFY 8192
+
+typedef struct
+{
+ int offset;
+ int oldvalue;
+} modify_t;
+
+int modify_index;
+int undo_index;
+modify_t modify[MAX_MODIFY];
+
+void SetSkinModified (void)
+{
+ char text[1024];
+
+ if (modified && modified_past_autosave)
+ return;
+
+ modified = true;
+ modified_past_autosave = true;
+
+ sprintf (text, "%s *", skin_filename);
+ SetWindowText (skinwindow, text);
+}
+
+void SetSkin (int index, int pixel)
+{
+ modify_t *m;
+
+ if (!modified)
+ SetSkinModified ();
+
+ // save undo info
+ m = &modify[undo_index];
+ m->offset = index;
+ m->oldvalue = pic[index];
+
+ modify_index = (++undo_index)&(MAX_MODIFY-1);
+
+ // modify it
+ rgb[index] = selected_rgb;
+ pic[index] = selected_index;
+ UpdateTexture (index);
+ InvalidateRect (skinwindow, NULL, false);
+ InvalidateRect (camerawindow, NULL, false);
+}
+
+void Undo (void)
+{
+ modify_t *m;
+ int temp;
+
+ if (!undo_index)
+ return;
+
+ if (!--undo_index)
+ { // back to unmodified state
+ modified = false;
+ SetWindowText (skinwindow, skin_filename);
+ }
+ m = &modify[undo_index];
+
+ // modify it
+ temp = pic[m->offset];
+ pic[m->offset] = m->oldvalue;
+ rgb[m->offset] = palette[m->oldvalue*3] +
+ (palette[m->oldvalue*3+1]<<8) + (palette[m->oldvalue*3+2]<<16);
+ m->oldvalue = temp;
+ UpdateTexture (m->offset);
+ InvalidateRect (skinwindow, NULL, false);
+ InvalidateRect (camerawindow, NULL, false);
+
+}
+
+void Redo (void)
+{
+ modify_t *m;
+ int temp;
+
+ if (undo_index == modify_index)
+ return;
+
+ m = &modify[undo_index];
+
+ // modify it
+ temp = pic[m->offset];
+ pic[m->offset] = m->oldvalue;
+ rgb[m->offset] = palette[m->oldvalue*3] +
+ (palette[m->oldvalue*3+1]<<8) + (palette[m->oldvalue*3+2]<<16);
+ m->oldvalue = temp;
+ UpdateTexture (m->offset);
+ InvalidateRect (skinwindow, NULL, false);
+ InvalidateRect (camerawindow, NULL, false);
+
+ if (!undo_index++)
+ { // modified again
+ char text[1024];
+
+ modified = true;
+ sprintf (text, "%s *", skin_filename);
+ SetWindowText (skinwindow, text);
+ }
+}
+
+//===================================================================
+
+/*
+=============
+Skin_SaveFile
+
+Load a skin texture and the base.tri from the same directory
+=============
+*/
+void Skin_SaveFile (char *name)
+{
+ byte *data;
+ int i, j;
+ char backup[1024];
+
+ // back up the current file if it exists
+ sprintf (backup, "%s.bak", name);
+ remove (backup);
+ rename (name, backup);
+
+ modified = false;
+ modified_past_autosave = false;
+ modify_index = undo_index = 0;
+ SetWindowText (skinwindow, skin_filename);
+
+ data = malloc(skin_width*skin_height);
+ for (i=0 ; i<skin_height ; i++)
+ memcpy (data + i*skin_width, pic + i*width2, skin_width);
+ Save256Image (name, data, palette, skin_width, skin_height);
+
+ free(data);
+}
+
+/*
+=============
+Expand256Texture
+
+=============
+*/
+void Expand256Texture (void)
+{
+ int i, j;
+ int p;
+
+ memset (rgb, 0, sizeof(rgb));
+ for (i=0 ; i<skin_height ; i++)
+ {
+ for (j=0 ; j<skin_width ; j++)
+ {
+ p = pic[i*width2+j];
+ rgb[i*width2+j] = (palette[p*3+0]<<0) + (palette[p*3+1]<<8) + (palette[p*3+2]<<16);
+ }
+ }
+
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+ glTexImage2D (GL_TEXTURE_2D, 0, 3, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgb);
+}
+
+void SetSizes (int width, int height)
+{
+ int i;
+
+ if (width < 32)
+ width = 16;
+ if (height < 32)
+ height = 16;
+
+ skin_width = width;
+ skin_height = height;
+
+ if (skin_width > 1024 || skin_height > 512)
+ Sys_Error ("Skin file is too large");
+
+ width2 = 1;
+ height2 = 1;
+ for (i=0 ; i<12 ; i++)
+ {
+ if (width2 < skin_width)
+ width2<<=1;
+ if (height2 < skin_height)
+ height2<<=1;
+ }
+
+ // compatability shit for auto sizing of old skins
+ if (skin_width != 320 || skin_height != 200)
+ {
+ skinwidth = skin_width;
+ skinheight = skin_height;
+ }
+ else
+ {
+ skinwidth = 0;
+ skinheight = 0;
+ }
+}
+
+/*
+=============
+Skin_LoadFile
+
+Load a skin texture and the base.tri from the same directory
+=============
+*/
+void Skin_LoadFile (char *name)
+{
+ int i, j, p;
+ byte *lbmpic;
+ byte *lbmpal;
+ char trifile[1024];
+ int width, height;
+
+ modified = false;
+ modified_past_autosave = false;
+ modify_index = undo_index = 0;
+ strcpy (skin_filename, name);
+ SetWindowText (skinwindow, skin_filename);
+
+ //
+ // read the texture
+ //
+ Load256Image (skin_filename, &lbmpic, &lbmpal, &width, &height);
+ memcpy (palette, lbmpal, sizeof(palette));
+ free (lbmpal);
+
+ SetSizes (width, height);
+
+ memset (pic, 0, sizeof(pic));
+ for (i=0 ; i<skin_height ; i++)
+ {
+ for (j=0 ; j<skin_width ; j++)
+ {
+ p = lbmpic[i*skin_width + j];
+ pic[i*width2+j] = p;
+ }
+ }
+ free (lbmpic);
+
+ Expand256Texture ();
+
+ InitIndexTexture ();
+
+ Pal_SetIndex (selected_index);
+
+ //
+ // read the polfile and
+ // generate the texture coordinates
+ //
+ strcpy (trifile, skin_filename);
+ StripExtension (trifile);
+ strcat (trifile, ".tri");
+ if (FileExists (trifile))
+ {
+ LoadTriFile (trifile);
+ CalcTmCoords ();
+ }
+ else
+ {
+ ExtractFilePath (name, trifile);
+ strcat (trifile, "base.tri");
+ if (FileExists (trifile))
+ {
+ LoadTriFile (trifile);
+ CalcTmCoords ();
+ }
+ }
+
+ InvalidateRect (palettewindow, NULL, false);
+ InvalidateRect (skinwindow, NULL, false);
+ InvalidateRect (camerawindow, NULL, false);
+
+}
+
+
+/*
+=============
+Skin_Click
+=============
+*/
+int skin_last_index;
+void Skin_Click (int x, int y, qboolean shift)
+{
+ int index;
+
+ index = 0;
+ glReadBuffer (GL_BACK);
+ glReadPixels (x, y, 1,1, GL_RGB, GL_UNSIGNED_BYTE, &index);
+
+ index--;
+ if (index == -1)
+ return;
+ if (index >= width2*height2)
+ return;
+
+ if (index == skin_last_index)
+ return; // in same pixel
+ skin_last_index = index;
+
+ if (shift)
+ {
+ Pal_SetIndex (pic[index]);
+ return;
+ }
+
+ SetSkin (index, selected_index);
+ UpdateWindow (skinwindow);
+}
+
+
+void DrawModelST (void)
+{
+ int i, j;
+
+ glColor4f (1,1,1,1);
+
+ glBegin (GL_TRIANGLES);
+ for (i=0 ; i<numfaces ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ glVertex2f (tmcoords[i][j][0]*width2, (1-tmcoords[i][j][1])*height2);
+ }
+ }
+ glEnd ();
+}
+
+void DrawSkin (void)
+{
+ glBegin (GL_POLYGON);
+ glTexCoord2f (0,1);
+ glVertex2f (0,0);
+
+ glTexCoord2f (0,0);
+ glVertex2f (0,height2);
+
+ glTexCoord2f (1,0);
+ glVertex2f (width2,height2);
+
+ glTexCoord2f (1,1);
+ glVertex2f (width2,0);
+ glEnd ();
+
+}
+
+void Skin_Draw (void)
+{
+ int x, y;
+ float aspect;
+ float xs, ys;
+ int c;
+
+ //
+ // draw it
+ //
+ if (skin_z < 20)
+ skin_z = 20;
+
+ glViewport (0,0,skinw_width, skinw_height);
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ gluPerspective (90, (float)skinw_width/skinw_height, 2, 16384);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ gluLookAt (skin_x, skin_y, skin_z, skin_x, skin_y, skin_z-1, 0, 1, 0);
+
+ glClearColor (0.3,0.3,0.3,1);
+ glClear (GL_COLOR_BUFFER_BIT);
+ glDisable (GL_DEPTH_TEST);
+ glDisable (GL_CULL_FACE);
+ glEnable (GL_TEXTURE_2D);
+
+ glColor4f (1,1,1,1);
+
+ DrawSkin ();
+
+ if (skin_lines)
+ {
+ glDisable (GL_TEXTURE_2D);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+
+ DrawModelST ();
+
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glEnable (GL_TEXTURE_2D);
+ }
+
+ SwapBuffers(skindc);
+
+
+ // now fill the back buffer with the index texture
+ glClearColor (0,0,0,0);
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_INDEX);
+ DrawSkin ();
+
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+}
+
+/*
+============
+Skin_WndProc
+============
+*/
+LONG WINAPI Skin_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ LONG lRet = 1;
+ int fwKeys, xPos, yPos;
+ RECT rect;
+
+ GetClientRect(hWnd, &rect);
+ skinw_width = rect.right-rect.left;
+ skinw_height = rect.bottom-rect.top;
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ skindc = GetDC(hWnd);
+ bSetupPixelFormat(skindc);
+ break;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+
+ BeginPaint(hWnd, &ps);
+ if (!wglMakeCurrent( skindc, baseRC ))
+ Sys_Error ("wglMakeCurrent failed");
+ Skin_Draw ();
+ EndPaint(hWnd, &ps);
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ skin_last_index = -1;
+draw:
+ if (GetTopWindow(mainwindow) != hWnd)
+ BringWindowToTop(hWnd);
+
+ SetFocus (skinwindow);
+ SetCapture (skinwindow);
+ fwKeys = wParam; // key flags
+ xPos = (short)LOWORD(lParam); // horizontal position of cursor
+ yPos = (short)HIWORD(lParam); // vertical position of cursor
+ yPos = (int)rect.bottom - 1 - yPos;
+ if (!wglMakeCurrent( skindc, baseRC ))
+ Sys_Error ("wglMakeCurrent failed");
+ Skin_Click (xPos, yPos, !!(wParam&(MK_SHIFT|MK_CONTROL)) );
+ break;
+
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ fwKeys = wParam; // key flags
+ if (! (fwKeys & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+ ReleaseCapture ();
+ break;
+
+ case WM_MOUSEMOVE:
+ {
+ static int oldx, oldy;
+ int dx, dy;
+ POINT pt;
+
+ if (wParam & MK_LBUTTON)
+ goto draw;
+
+ GetCursorPos (&pt);
+ xPos = pt.x;
+ yPos = pt.y;
+ if (!(wParam & (MK_RBUTTON|MK_MBUTTON)))
+ {
+ oldx = xPos;
+ oldy = yPos;
+ break;
+ }
+ dx = xPos-oldx;
+ dy = oldy-yPos;
+ if (!dx && !dy)
+ break;
+ SetCursorPos (oldx, oldy);
+
+ if (wParam == (MK_RBUTTON|MK_CONTROL) )
+ {
+ if (abs(dx) > abs(dy))
+ skin_z += 0.25*dx;
+ else
+ skin_z += 0.25*dy;
+ InvalidateRect (skinwindow, NULL, false);
+ }
+ if (wParam == MK_RBUTTON)
+ {
+ skin_x -= 0.25*dx;
+ skin_y -= 0.25*dy;
+ InvalidateRect (skinwindow, NULL, false);
+ }
+ }
+ break;
+
+ case WM_SIZE:
+ InvalidateRect(camerawindow, NULL, false);
+ break;
+ case WM_NCCALCSIZE:// don't let windows copy pixels
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ return WVR_REDRAW;
+ case WM_CLOSE:
+ DestroyWindow (hWnd);
+ break;
+
+ default:
+ /* pass all unhandled messages to DefWindowProc */
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ /* return 1 if handled message, 0 if not */
+ return lRet;
+}
+
+
+/*
+==============
+WSkin_Create
+==============
+*/
+void WSkin_Create (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)Skin_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = SKIN_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Sys_Error ("RegisterClass failed");
+
+ skinwindow = CreateWindow (SKIN_WINDOW_CLASS ,
+ "Skin View",
+ QE3_STYLE,
+ (int)(screen_width*0.5),
+ (int)(screen_height*0.2),
+ (int)(screen_width*0.5),
+ (int)(screen_height*0.8), // size
+ mainwindow, // parent window
+ 0, // no menu
+ hInstance,
+ 0);
+ if (!skinwindow)
+ Error ("Couldn't create skinwindow");
+
+// RestoreWindowState(palettewindow, "palettewindow");
+ ShowWindow (skinwindow, SW_SHOWDEFAULT);
+}
+
+
+/*
+===================================================================
+
+ SKIN RESAMPLING
+
+===================================================================
+*/
+
+HWND resamplewindow;
+HDC resampledc;
+
+#define RESAMPLE_WINDOW_CLASS "TPResample"
+
+/*
+============
+Resample_WndProc
+============
+*/
+LONG WINAPI Resample_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ resampledc = GetDC(hWnd);
+ bSetupPixelFormat(resampledc);
+ break;
+ }
+
+ return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+/*
+==============
+ResampleWindow
+==============
+*/
+void ResampleWindow (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+ static qboolean registered;
+
+ if (!registered)
+ {
+ registered = true;
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)Resample_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = RESAMPLE_WINDOW_CLASS;
+
+ if (!RegisterClass (&wc) )
+ Sys_Error ("RegisterClass failed");
+ }
+
+ resamplewindow = CreateWindow (RESAMPLE_WINDOW_CLASS ,
+ "ResampleWindow",
+ WS_OVERLAPPED,
+ 0, 0, width2+32, height2+32, // size
+ NULL, // parent window
+ 0, // no menu
+ hInstance,
+ 0);
+ if (!resamplewindow)
+ Error ("Couldn't create skinwindow");
+
+ ShowWindow (resamplewindow, SW_SHOWDEFAULT);
+}
+
+
+void OutlineTexture (byte *pic)
+{
+ int i, j;
+ int x, y;
+ int empty;
+ byte oldpic[1024*512];
+
+ memcpy (oldpic, pic, width2*height2);
+
+ empty = oldpic[0];
+
+ for (i=0 ; i<height2 ; i++)
+ {
+ for (j=0 ; j<width2 ; j++)
+ {
+ if (oldpic[i*width2+j] != empty)
+ continue;
+ for (x=-1 ; x<=1 ; x++)
+ {
+ for (y=-1 ; y<=1 ; y++)
+ {
+ if (i+y < 0 || i+y >= height2)
+ continue;
+ if (j+x < 0 || j+x >= width2)
+ continue;
+ if (oldpic[(i+y)*width2 + j+x] != empty)
+ {
+ pic[i*width2+j] = oldpic[(i+y)*width2 + j+x];
+ goto done;
+ }
+ }
+ }
+done: ;
+ }
+ }
+}
+
+void ResampleSkin (void)
+{
+ int i, j;
+ static float oldtmcoords[10000][3][2];
+ static int newindex[1024*512];
+ static byte oldpic[1024*512];
+
+ // open a window of the texture size
+ ResampleWindow (main_instance);
+
+ // get new S/T from current frame
+ memcpy (oldtmcoords, tmcoords, numfaces*3*2*4);
+ CalcTmCoords ();
+
+ // draw all the triangles with the index texture
+ if (!wglMakeCurrent( resampledc, baseRC ))
+ Sys_Error ("wglMakeCurrent failed");
+
+ glViewport (0,0,width2, height2);
+ glClearColor (0,0,0,0);
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0, width2, 0, height2, -100, 100);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+
+ glColor4f (1,1,1,1);
+ glDisable (GL_DEPTH_TEST);
+ glDisable (GL_CULL_FACE);
+ BindTextureEXT (GL_TEXTURE_2D, TEXTURE_INDEX);
+#if 0
+ glDisable(GL_TEXTURE_2D);
+ glBegin (GL_LINE_LOOP);
+ glVertex3f (1,1,10);
+ glVertex3f (skin_width-1,0,10);
+ glVertex3f (skin_width-1,skin_height-1,10);
+ glVertex3f (1,skin_height-1,10);
+ glEnd ();
+ glEnable(GL_TEXTURE_2D);
+#endif
+ glBegin (GL_TRIANGLES);
+ for (i=0 ; i<numfaces ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ glTexCoord2f (oldtmcoords[i][j][0], oldtmcoords[i][j][1]);
+ glVertex3f (tmcoords[i][j][0]*width2, tmcoords[i][j][1]*height2, 10);
+ }
+ }
+ glEnd ();
+ SwapBuffers (resampledc);
+
+ // build the new color texture
+ memcpy (oldpic, pic, width2*height2);
+ glReadBuffer (GL_FRONT);
+ glReadPixels (0,0,width2,height2,GL_RGBA,GL_UNSIGNED_BYTE, &newindex);
+ for (i=0 ; i<height2 ; i++)
+ for (j=0 ; j<width2 ; j++)
+ pic[i*width2+j] = oldpic[newindex[i*width2+j]&0xffffff];
+
+ // outline it
+ OutlineTexture (pic);
+ Expand256Texture ();
+
+ InvalidateRect (skinwindow, NULL, false);
+ InvalidateRect (camerawindow, NULL, false);
+
+ // change name
+ strcpy (skin_filename, tri_filename);
+ StripExtension (skin_filename);
+ strcat (skin_filename, ".lbm");
+
+ SetSkinModified ();
+
+ wglMakeCurrent (NULL, NULL);
+ DestroyWindow (resamplewindow);
+}
+
+/*
+===================================================================
+
+ NEW SKIN
+
+===================================================================
+*/
+
+BOOL CALLBACK NewSkinDlgProc (
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ )
+{
+ char sz[256];
+ int width, height;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ SetWindowText(GetDlgItem(hwndDlg, IDC_WIDTH), "320");
+ SetWindowText(GetDlgItem(hwndDlg, IDC_HEIGHT), "200");
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+
+ case IDOK:
+ GetWindowText(GetDlgItem(hwndDlg, IDC_WIDTH), sz, 255);
+ width = atoi(sz);
+ GetWindowText(GetDlgItem(hwndDlg, IDC_HEIGHT), sz, 255);
+ height = atoi(sz);
+ SetSizes (width, height);
+ EndDialog(hwndDlg, 1);
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+void NewSkin (void)
+{
+ int i, j;
+ byte *buf;
+
+ if (!DialogBox(main_instance, (char *)IDD_NEWSKIN, mainwindow, NewSkinDlgProc))
+ return;
+
+ // open a window of the texture size
+ ResampleWindow (main_instance);
+
+ // get new S/T from current frame
+ CalcTmCoords ();
+
+ // draw all the triangles
+ if (!wglMakeCurrent( resampledc, baseRC ))
+ Sys_Error ("wglMakeCurrent failed");
+
+ glViewport (0,0,width2, height2);
+ glClearColor (0,0,0,0);
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0, width2, 0, height2, -100, 100);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+
+ glColor4f (1,1,1,1);
+ glDisable (GL_DEPTH_TEST);
+ glDisable (GL_CULL_FACE);
+ glDisable (GL_TEXTURE_2D);
+
+ for (i=0 ; i<numfaces ; i++)
+ {
+ glColor3f ((i&255)/255.0, (i&255)/255.0, (i&255)/255.0);
+ glBegin (GL_TRIANGLES);
+ for (j=0 ; j<3 ; j++)
+ glVertex3f (tmcoords[i][j][0]*width2, tmcoords[i][j][1]*height2, 10);
+ glEnd ();
+ }
+
+ SwapBuffers (resampledc);
+
+ // build the new color texture
+ glReadBuffer (GL_FRONT);
+ buf = malloc(width2*height2*4);
+ glReadPixels (0,0,width2,height2,GL_RGBA,GL_UNSIGNED_BYTE, buf);
+ for (i=0 ; i<width2*height2 ; i++)
+ pic[i] = buf[i*4];
+ free (buf);
+
+ // outline it
+ OutlineTexture (pic);
+ Expand256Texture ();
+ InitIndexTexture ();
+
+ InvalidateRect (skinwindow, NULL, false);
+ InvalidateRect (camerawindow, NULL, false);
+
+ // change name
+ strcpy (skin_filename, tri_filename);
+ StripExtension (skin_filename);
+ strcat (skin_filename, ".lbm");
+
+ SetSkinModified ();
+
+ wglMakeCurrent (NULL, NULL);
+ DestroyWindow (resamplewindow);
+}