]> de.git.xonotic.org Git - xonotic/netradiant.git/commitdiff
Merge branch 'ingar/png_icons' into 'master'
authorMario <zacjardine@y7mail.com>
Sun, 2 Aug 2015 02:08:23 +0000 (02:08 +0000)
committerMario <zacjardine@y7mail.com>
Sun, 2 Aug 2015 02:08:23 +0000 (02:08 +0000)
Ingar/png icons

Replaces BMP icon images with PNG images, this allows for better icon transparency.

See merge request !8

29 files changed:
.gitignore
Makefile
contrib/bobtoolz/misc.cpp
download-gamepacks.sh
include/iundo.h
libs/bytestreamutils.h
libs/etclib.c [new file with mode: 0644]
libs/etclib.h [new file with mode: 0644]
libs/gtkutil/cursor.h
libs/scenelib.h
libs/xml/xmlparser.h
plugins/image/image.cpp
plugins/image/ktx.cpp [new file with mode: 0644]
plugins/image/ktx.h [new file with mode: 0644]
radiant/gtkdlgs.cpp
radiant/mainframe.cpp
radiant/patchdialog.cpp
radiant/patchmanip.cpp
radiant/patchmanip.h
radiant/select.cpp
radiant/surfacedialog.cpp
radiant/texwindow.cpp
tools/quake3/common/imagelib.c
tools/quake3/common/imagelib.h
tools/quake3/common/threads.c
tools/quake3/q3map2/image.c
tools/quake3/q3map2/light.c
tools/quake3/q3map2/light_bounce.c
tools/quake3/q3map2/main.c

index b1c030fcafa5f5152f74f2366e176d76f7d64af9..1bab8616f5eb3d19c3f966e6bfc0fed8c4fae490 100644 (file)
@@ -1,2 +1,3 @@
 *.[oda]
 install
+games
index b4174339e8e6ca3d2a4539cfcd533ba669bb2124..51b3fa9fa77d063d2cd679018cb321d26281c87a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -37,6 +37,8 @@ CAT                ?= cat
 MKDIR              ?= mkdir -p
 CP                 ?= cp
 CP_R               ?= $(CP) -r
+LN                 ?= ln
+LN_SNF             ?= $(LN) -snf
 RM                 ?= rm
 RM_R               ?= $(RM) -r
 TEE_STDERR         ?= | tee /dev/stderr
@@ -153,26 +155,39 @@ ifeq ($(BUILD),release)
 ifeq ($(findstring $(CFLAGS),-O),)
        CFLAGS_COMMON += -O3
        # only add -O3 if no -O flag is in $(CFLAGS)
-       # to allow overriding the optimizations
 endif
        CPPFLAGS_COMMON +=
        LDFLAGS_COMMON += -s
 else
 
+ifeq ($(BUILD),native)
+ifeq ($(findstring $(CFLAGS),-O),)
+       CFLAGS_COMMON += -O3
+       # only add -O3 if no -O flag is in $(CFLAGS)
+endif
+       CFLAGS_COMMON += -march=native -mcpu=native
+       CPPFLAGS_COMMON +=
+       LDFLAGS_COMMON += -s
+else
+
 $(error Unsupported build type: $(BUILD))
 endif
 endif
 endif
 endif
+endif
 
 INSTALLDIR_BASE := $(INSTALLDIR)
 
+MAKE_EXE_SYMLINK = false
+
 ifeq ($(OS),Linux)
        CPPFLAGS_COMMON += -DPOSIX -DXWINDOWS
        CFLAGS_COMMON += -fPIC
        LDFLAGS_DLL = -fPIC -ldl
        LIBS_COMMON = -lpthread
-       EXE ?= x86
+       EXE ?= $(shell uname -m)
+       MAKE_EXE_SYMLINK = true
        A = a
        DLL = so
        MWINDOWS =
@@ -204,7 +219,8 @@ ifeq ($(OS),Darwin)
        CPPFLAGS_COMMON += -I$(MACLIBDIR)/../include -I/usr/X11R6/include
        LDFLAGS_COMMON += -L$(MACLIBDIR) -L/usr/X11R6/lib
        LDFLAGS_DLL += -dynamiclib -ldl
-       EXE ?= ppc
+       EXE ?= $(shell uname -m)
+       MAKE_EXE_SYMLINK = true
        A = a
        DLL = dylib
        MWINDOWS =
@@ -385,6 +401,7 @@ binaries-radiant-plugins: \
 .PHONY: binaries-radiant
 binaries-radiant-core: \
        $(INSTALLDIR)/radiant.$(EXE) \
+       $(INSTALLDIR)/radiant \
 
 .PHONY: binaries-tools
 binaries-tools: \
@@ -400,14 +417,17 @@ binaries-tools-quake2: \
 .PHONY: binaries-q2map
 binaries-q2map: \
        $(INSTALLDIR)/q2map.$(EXE) \
+       $(INSTALLDIR)/q2map \
 
 .PHONY: binaries-qdata3
 binaries-qdata3: \
        $(INSTALLDIR)/qdata3.$(EXE) \
+       $(INSTALLDIR)/qdata3 \
 
 .PHONY: binaries-h2data
 binaries-h2data: \
-       $(INSTALLDIR)/heretic2/h2data.$(EXE)
+       $(INSTALLDIR)/heretic2/h2data.$(EXE) \
+       $(INSTALLDIR)/heretic2/h2data \
 
 .PHONY: binaries-tools-quake3
 binaries-tools-quake3: \
@@ -417,10 +437,12 @@ binaries-tools-quake3: \
 .PHONY: binaries-q3data
 binaries-q3data: \
        $(INSTALLDIR)/q3data.$(EXE) \
+       $(INSTALLDIR)/q3data \
 
 .PHONY: binaries-q3map2
 binaries-q3map2: \
        $(INSTALLDIR)/q3map2.$(EXE) \
+       $(INSTALLDIR)/q3map2 \
 
 
 .PHONY: clean
@@ -434,6 +456,9 @@ clean:
        $(CXX) $^ $(LDFLAGS) $(LDFLAGS_COMMON) $(LDFLAGS_EXTRA) $(LIBS_EXTRA) $(LIBS_COMMON) $(LIBS) -o $@
        [ -z "$(LDD)" ] || [ -z "`$(LDD) -r $@ $(STDERR_TO_STDOUT) $(STDOUT_TO_DEVNULL) $(TEE_STDERR)`" ] || { $(RM) $@; exit 1; }
 
+$(INSTALLDIR)/%: $(INSTALLDIR)/%.$(EXE)
+       if $(MAKE_EXE_SYMLINK); then o=$<; $(LN_SNF) $${o##*/} $@; else true; fi
+
 %.$(A):
        $(AR) rc $@ $^
        $(RANLIB) $@
@@ -511,6 +536,7 @@ $(INSTALLDIR)/q3map2.$(EXE): \
        tools/quake3/q3map2/vis.o \
        tools/quake3/q3map2/writebsp.o \
        libddslib.$(A) \
+       libetclib.$(A) \
        libfilematch.$(A) \
        libl_net.$(A) \
        libmathlib.$(A) \
@@ -560,6 +586,10 @@ libddslib.$(A): CPPFLAGS_EXTRA := -Ilibs
 libddslib.$(A): \
        libs/ddslib/ddslib.o \
 
+libetclib.$(A): CPPFLAGS_EXTRA := -Ilibs
+libetclib.$(A): \
+       libs/etclib.o \
+
 $(INSTALLDIR)/q3data.$(EXE): LIBS_EXTRA := $(LIBS_XML) $(LIBS_GLIB) $(LIBS_ZLIB)
 $(INSTALLDIR)/q3data.$(EXE): CPPFLAGS_EXTRA := $(CPPFLAGS_XML) $(CPPFLAGS_GLIB) $(CPPFLAGS_ZLIB) -Itools/quake3/common -Ilibs -Iinclude
 $(INSTALLDIR)/q3data.$(EXE): \
@@ -584,6 +614,7 @@ $(INSTALLDIR)/q3data.$(EXE): \
        tools/quake3/q3data/stripper.o \
        tools/quake3/q3data/video.o \
        libfilematch.$(A) \
+       libetclib.$(A) \
        libl_net.$(A) \
        libmathlib.$(A) \
        $(if $(findstring $(OS),Win32),icons/q3data.o,) \
@@ -778,9 +809,11 @@ $(INSTALLDIR)/modules/image.$(DLL): \
        plugins/image/dds.o \
        plugins/image/image.o \
        plugins/image/jpeg.o \
+       plugins/image/ktx.o \
        plugins/image/pcx.o \
        plugins/image/tga.o \
        libddslib.$(A) \
+       libetclib.$(A) \
 
 $(INSTALLDIR)/modules/imageq2.$(DLL): CPPFLAGS_EXTRA := -Ilibs -Iinclude
 $(INSTALLDIR)/modules/imageq2.$(DLL): \
index aa22640a1cccb5b46d65dd2094465c3020a6414a..977879f59e4b0fe4f239356eddef76aa623e87f3 100644 (file)
@@ -31,7 +31,7 @@
 
 #include "funchandlers.h"
 
-#ifdef __linux__
+#if defined ( POSIX )
 #include <sys/types.h>
 #include <unistd.h>
 #endif
index 7717cd2dcb961845d3c78bec9e98939f9caa8e08..ff5b9840cd14a9e7320702fe360e72504b3cebb5 100755 (executable)
@@ -158,5 +158,6 @@ pack QuakePack       GPL         zip1   http://ingar.satgnu.net/files/gtkradiant
 pack TremulousPack   proprietary zip1   http://ingar.satgnu.net/files/gtkradiant/gamepacks/TremulousPack.zip
 pack UFOAIPack       proprietary svn    svn://svn.icculus.org/gtkradiant-gamepacks/UFOAIPack/branches/1.5/
 #pack WarsowPack     GPL         svn    https://svn.bountysource.com/wswpack/trunk/netradiant/games/WarsowPack/
-pack WarsowPack      GPL         zip1   http://ingar.satgnu.net/files/gtkradiant/gamepacks/WarsowPack.zip
+#pack WarsowPack     GPL         zip1   http://ingar.satgnu.net/files/gtkradiant/gamepacks/WarsowPack.zip
+pack WarsowPack      GPL         git    https://github.com/Warsow/NetRadiantPack.git
 pack XonoticPack     GPL         git    http://git.xonotic.org/xonotic/netradiant-xonoticpack.git
index 58f72d4ba460984d263304b4133306f07be64e0f..eb1e8681c9c16a7882d240306dc20bff43daafb0 100644 (file)
@@ -33,6 +33,8 @@ class UndoMemento
 {
 public:
 virtual void release() = 0;
+virtual ~UndoMemento() {
+}
 };
 
 class Undoable
@@ -40,12 +42,16 @@ class Undoable
 public:
 virtual UndoMemento* exportState() const = 0;
 virtual void importState( const UndoMemento* state ) = 0;
+virtual ~Undoable() {
+}
 };
 
 class UndoObserver
 {
 public:
 virtual void save( Undoable* undoable ) = 0;
+virtual ~UndoObserver() {
+}
 };
 
 class UndoTracker
@@ -55,6 +61,8 @@ virtual void clear() = 0;
 virtual void begin() = 0;
 virtual void undo() = 0;
 virtual void redo() = 0;
+virtual ~UndoTracker() {
+}
 };
 
 class UndoSystem
@@ -75,6 +83,9 @@ virtual void clear() = 0;
 
 virtual void trackerAttach( UndoTracker& tracker ) = 0;
 virtual void trackerDetach( UndoTracker& tracker ) = 0;
+
+virtual ~UndoSystem() {
+}
 };
 
 #include "modulesystem.h"
index 156333a17cd6d599f7eff919250005977da8d6dd..90a9048a61cd16b30a52ecdc3827b8567913686f 100644 (file)
@@ -82,6 +82,13 @@ inline int16_t istream_read_int16_le( InputStreamType& istream ){
        return value;
 }
 
+template<typename InputStreamType>
+inline int16_t istream_read_int16_be( InputStreamType& istream ){
+       int16_t value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
 template<typename InputStreamType>
 inline uint16_t istream_read_uint16_le( InputStreamType& istream ){
        uint16_t value;
@@ -89,6 +96,13 @@ inline uint16_t istream_read_uint16_le( InputStreamType& istream ){
        return value;
 }
 
+template<typename InputStreamType>
+inline uint16_t istream_read_uint16_be( InputStreamType& istream ){
+       uint16_t value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
 template<typename InputStreamType>
 inline int32_t istream_read_int32_le( InputStreamType& istream ){
        int32_t value;
@@ -96,6 +110,13 @@ inline int32_t istream_read_int32_le( InputStreamType& istream ){
        return value;
 }
 
+template<typename InputStreamType>
+inline int32_t istream_read_int32_be( InputStreamType& istream ){
+       int32_t value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
 template<typename InputStreamType>
 inline uint32_t istream_read_uint32_le( InputStreamType& istream ){
        uint32_t value;
@@ -103,6 +124,13 @@ inline uint32_t istream_read_uint32_le( InputStreamType& istream ){
        return value;
 }
 
+template<typename InputStreamType>
+inline uint32_t istream_read_uint32_be( InputStreamType& istream ){
+       uint32_t value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
 template<typename InputStreamType>
 inline float istream_read_float32_le( InputStreamType& istream ){
        float value;
@@ -110,6 +138,13 @@ inline float istream_read_float32_le( InputStreamType& istream ){
        return value;
 }
 
+template<typename InputStreamType>
+inline float istream_read_float32_be( InputStreamType& istream ){
+       float value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
 template<typename InputStreamType>
 inline typename InputStreamType::byte_type istream_read_byte( InputStreamType& istream ){
        typename InputStreamType::byte_type b;
diff --git a/libs/etclib.c b/libs/etclib.c
new file mode 100644 (file)
index 0000000..09a149e
--- /dev/null
@@ -0,0 +1,114 @@
+// Copyright 2009 Google Inc.
+//
+// Based on the code from Android ETC1Util.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "etclib.h"
+
+static void ETC_DecodeETC1SubBlock( byte *out, qboolean outRGBA, int r, int g, int b, int tableIndex, unsigned int low, qboolean second, qboolean flipped ){
+       int baseX = 0, baseY = 0;
+       const int modifierTable[] = {
+               2, 8, -2, -8,
+               5, 17, -5, -17,
+               9, 29, -9, -29,
+               13, 42, -13, -42,
+               18, 60, -18, -60,
+               24, 80, -24, -80,
+               33, 106, -33, -106,
+               47, 183, -47, -183
+       };
+       const int *table = modifierTable + tableIndex * 4;
+       int i;  
+
+       if ( second ) {
+               if ( flipped ) {
+                       baseY = 2;
+               }
+               else {
+                       baseX = 2;
+               }
+       }
+
+       for ( i = 0; i < 8; i++ )
+       {
+               int x, y, k, delta;
+               int qr, qg, qb;
+               byte *q;
+
+               if ( flipped ) {
+                       x = baseX + ( i >> 1 );
+                       y = baseY + ( i & 1 );
+               }
+               else {
+                       x = baseX + ( i >> 2 );
+                       y = baseY + ( i & 3 );
+               }
+               k = y + ( x * 4 );
+               delta = table[( ( low >> k ) & 1 ) | ( ( low >> ( k + 15 ) ) & 2 )];
+
+               qr = r + delta;
+               qg = g + delta;
+               qb = b + delta;
+               if ( outRGBA ) {
+                       q = out + 4 * ( x + 4 * y );
+               }
+               else {
+                       q = out + 3 * ( x + 4 * y );
+               }
+               *( q++ ) = ( ( qr > 0 ) ? ( ( qr < 255 ) ? qr : 255 ) : 0 );
+               *( q++ ) = ( ( qg > 0 ) ? ( ( qg < 255 ) ? qg : 255 ) : 0 );
+               *( q++ ) = ( ( qb > 0 ) ? ( ( qb < 255 ) ? qb : 255 ) : 0 );
+               if ( outRGBA ) {
+                       *( q++ ) = 255;
+               }
+       }
+}
+
+void ETC_DecodeETC1Block( const byte* in, byte* out, qboolean outRGBA ){
+       unsigned int high = ( in[0] << 24 ) | ( in[1] << 16 ) | ( in[2] << 8 ) | in[3];
+       unsigned int low = ( in[4] << 24 ) | ( in[5] << 16 ) | ( in[6] << 8 ) | in[7];
+       int r1, r2, g1, g2, b1, b2;
+       qboolean flipped = ( ( high & 1 ) != 0 );
+
+       if ( high & 2 ) {
+               int rBase, gBase, bBase;
+               const int lookup[] = { 0, 1, 2, 3, -4, -3, -2, -1 };
+
+               rBase = ( high >> 27 ) & 31;
+               r1 = ( rBase << 3 ) | ( rBase >> 2 );
+               rBase = ( rBase + ( lookup[( high >> 24 ) & 7] ) ) & 31;
+               r2 = ( rBase << 3 ) | ( rBase >> 2 );
+
+               gBase = ( high >> 19 ) & 31;
+               g1 = ( gBase << 3 ) | ( gBase >> 2 );
+               gBase = ( gBase + ( lookup[( high >> 16 ) & 7] ) ) & 31;
+               g2 = ( gBase << 3 ) | ( gBase >> 2 );
+
+               bBase = ( high >> 11 ) & 31;
+               b1 = ( bBase << 3 ) | ( bBase >> 2 );
+               bBase = ( bBase + ( lookup[( high >> 8 ) & 7] ) ) & 31;
+               b2 = ( bBase << 3 ) | ( bBase >> 2 );
+       }
+       else {
+               r1 = ( ( high >> 24 ) & 0xf0 ) | ( ( high >> 28 ) & 0xf );
+               r2 = ( ( high >> 20 ) & 0xf0 ) | ( ( high >> 24 ) & 0xf );
+               g1 = ( ( high >> 16 ) & 0xf0 ) | ( ( high >> 20 ) & 0xf );
+               g2 = ( ( high >> 12 ) & 0xf0 ) | ( ( high >> 16 ) & 0xf );
+               b1 = ( ( high >> 8 ) & 0xf0 ) | ( ( high >> 12 ) & 0xf );
+               b2 = ( ( high >> 4 ) & 0xf0 ) | ( ( high >> 8 ) & 0xf );
+       }
+
+       ETC_DecodeETC1SubBlock( out, outRGBA, r1, g1, b1, ( high >> 5 ) & 7, low, qfalse, flipped );
+       ETC_DecodeETC1SubBlock( out, outRGBA, r2, g2, b2, ( high >> 2 ) & 7, low, qtrue, flipped );
+}
diff --git a/libs/etclib.h b/libs/etclib.h
new file mode 100644 (file)
index 0000000..7d24074
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2009 Google Inc.
+//
+// Based on the code from Android ETC1Util.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDED_ETCLIB_H
+#define INCLUDED_ETCLIB_H
+
+#include "bytebool.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void ETC_DecodeETC1Block( const byte* in, byte* out, qboolean outRGBA );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index b3cfe080f8676c10ea8010ce60fb27fa01fa992f..dfcc962c1d1d8ff1a02b3a9500c921b2023222d3 100644 (file)
@@ -114,7 +114,7 @@ void motion_delta( int x, int y, unsigned int state ){
 class FreezePointer
 {
 unsigned int handle_motion;
-int recorded_x, recorded_y;
+int recorded_x, recorded_y, last_x, last_y;
 typedef void ( *MotionDeltaFunction )( int x, int y, unsigned int state, void* data );
 MotionDeltaFunction m_function;
 void* m_data;
@@ -124,11 +124,19 @@ FreezePointer() : handle_motion( 0 ), m_function( 0 ), m_data( 0 ){
 static gboolean motion_delta( GtkWidget *widget, GdkEventMotion *event, FreezePointer* self ){
        int current_x, current_y;
        Sys_GetCursorPos( GTK_WINDOW( widget ), &current_x, &current_y );
-       int dx = current_x - self->recorded_x;
-       int dy = current_y - self->recorded_y;
+       int dx = current_x - self->last_x;
+       int dy = current_y - self->last_y;
+       int ddx = current_x - self->recorded_x;
+       int ddy = current_y - self->recorded_y;
+       self->last_x = current_x;
+       self->last_y = current_y;
        if ( dx != 0 || dy != 0 ) {
                //globalOutputStream() << "motion x: " << dx << ", y: " << dy << "\n";
-               Sys_SetCursorPos( GTK_WINDOW( widget ), self->recorded_x, self->recorded_y );
+               if (ddx < -32 || ddx > 32 || ddy < -32 || ddy > 32) {
+                       Sys_SetCursorPos( GTK_WINDOW( widget ), self->recorded_x, self->recorded_y );
+                       self->last_x = self->recorded_x;
+                       self->last_y = self->recorded_y;
+               }
                self->m_function( dx, dy, event->state, self->m_data );
        }
        return FALSE;
@@ -156,6 +164,9 @@ void freeze_pointer( GtkWindow* window, MotionDeltaFunction function, void* data
 
        Sys_SetCursorPos( window, recorded_x, recorded_y );
 
+       last_x = recorded_x;
+       last_y = recorded_y;
+
        m_function = function;
        m_data = data;
 
index 2b4b6007bb45fd69aa6bcc37ca5e9afc53a2e9ae..29402065252cf7c8f2cb4d040d244e48d2e67427 100644 (file)
@@ -166,6 +166,8 @@ class Symbiot
 {
 public:
 virtual void release() = 0;
+virtual ~Symbiot(){
+}
 };
 
 private:
@@ -223,8 +225,18 @@ bool visible(){
 bool excluded(){
        return ( m_state & eExcluded ) != 0;
 }
+bool operator<( const scene::Node& other ){
+       return this < &other;
+}
+bool operator==( const scene::Node& other ){
+       return this == &other;
+}
+bool operator!=( const scene::Node& other ){
+       return this != &other;
+}
 };
 
+
 class NullNode : public Node::Symbiot
 {
 NodeTypeCastTable m_casts;
@@ -276,16 +288,6 @@ inline TransformNode* Node_getTransformNode( scene::Node& node ){
        return NodeTypeCast<TransformNode>::cast( node );
 }
 
-inline bool operator<( scene::Node& node, scene::Node& other ){
-       return &node < &other;
-}
-inline bool operator==( scene::Node& node, scene::Node& other ){
-       return &node == &other;
-}
-inline bool operator!=( scene::Node& node, scene::Node& other ){
-       return !::operator==( node, other );
-}
-
 
 inline scene::Node& NewNullNode(){
        return ( new scene::NullNode )->node();
index ecc7a0437a6bd867ce82ca7c27c3aaffbd821648..b11d355bce45c1a42df4f4df3644b1071747c13a 100644 (file)
@@ -79,7 +79,7 @@ class Formatted
 {
 public:
 const char* m_format;
-va_list m_arguments;
+mutable va_list m_arguments;
 Formatted( const char* format, ... )
        : m_format( format ){
        va_start( m_arguments, format );
index fcfdd01875510561f8db08617b2cb2aec269d05a..4f3e07a8790d45716441d958796601ad7b95564e 100644 (file)
@@ -29,6 +29,7 @@
 #include "bmp.h"
 #include "pcx.h"
 #include "dds.h"
+#include "ktx.h"
 
 
 #include "modulesystem/singletonmodule.h"
@@ -137,6 +138,26 @@ typedef SingletonModule<ImageDDSAPI, ImageDependencies> ImageDDSModule;
 ImageDDSModule g_ImageDDSModule;
 
 
+class ImageKTXAPI
+{
+_QERPlugImageTable m_imagektx;
+public:
+typedef _QERPlugImageTable Type;
+STRING_CONSTANT( Name, "ktx" );
+
+ImageKTXAPI(){
+       m_imagektx.loadImage = LoadKTX;
+}
+_QERPlugImageTable* getTable(){
+       return &m_imagektx;
+}
+};
+
+typedef SingletonModule<ImageKTXAPI, ImageDependencies> ImageKTXModule;
+
+ImageKTXModule g_ImageKTXModule;
+
+
 extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server ){
        initialiseModule( server );
 
@@ -145,4 +166,5 @@ extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server
        g_ImageBMPModule.selfRegister();
        g_ImagePCXModule.selfRegister();
        g_ImageDDSModule.selfRegister();
+       g_ImageKTXModule.selfRegister();
 }
diff --git a/plugins/image/ktx.cpp b/plugins/image/ktx.cpp
new file mode 100644 (file)
index 0000000..582d0b8
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+   Copyright (C) 2015, SiPlus, Chasseur de bots.
+   All Rights Reserved.
+
+   This file is part of GtkRadiant.
+
+   GtkRadiant 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.
+
+   GtkRadiant is distributed in the hope that 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 GtkRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "ktx.h"
+
+#include <string.h>
+
+#include "bytestreamutils.h"
+#include "etclib.h"
+#include "ifilesystem.h"
+#include "imagelib.h"
+
+
+#define KTX_TYPE_UNSIGNED_BYTE                         0x1401
+#define KTX_TYPE_UNSIGNED_SHORT_4_4_4_4                0x8033
+#define KTX_TYPE_UNSIGNED_SHORT_5_5_5_1                0x8034
+#define KTX_TYPE_UNSIGNED_SHORT_5_6_5          0x8363
+
+#define KTX_FORMAT_ALPHA                                       0x1906
+#define KTX_FORMAT_RGB                                         0x1907
+#define KTX_FORMAT_RGBA                                                0x1908
+#define KTX_FORMAT_LUMINANCE                           0x1909
+#define KTX_FORMAT_LUMINANCE_ALPHA                     0x190A
+#define KTX_FORMAT_BGR                                         0x80E0
+#define KTX_FORMAT_BGRA                                                0x80E1
+
+#define KTX_FORMAT_ETC1_RGB8                           0x8D64
+
+class KTX_Decoder
+{
+public:
+       virtual void Decode( PointerInputStream& istream, byte* out ) = 0;
+       virtual unsigned int GetPixelSize() = 0;
+};
+
+class KTX_Decoder_A8 : public KTX_Decoder
+{
+public:
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               out[0] = out[1] = out[2] = 0;
+               out[3] = istream_read_byte( istream );
+       }
+       virtual unsigned int GetPixelSize(){
+               return 1;
+       }
+};
+
+class KTX_Decoder_RGB8 : public KTX_Decoder
+{
+public:
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               istream.read( out, 3 );
+               out[3] = 255;
+       }
+       virtual unsigned int GetPixelSize(){
+               return 3;
+       }
+};
+
+class KTX_Decoder_RGBA8 : public KTX_Decoder
+{
+public:
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               istream.read( out, 4 );
+       }
+       virtual unsigned int GetPixelSize(){
+               return 4;
+       }
+};
+
+class KTX_Decoder_L8 : public KTX_Decoder
+{
+public:
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               byte l = istream_read_byte( istream );
+               out[0] = out[1] = out[2] = l;
+               out[3] = 255;
+       }
+       virtual unsigned int GetPixelSize(){
+               return 1;
+       }
+};
+
+class KTX_Decoder_LA8 : public KTX_Decoder
+{
+public:
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               byte la[2];
+               istream.read( la, 2 );
+               out[0] = out[1] = out[2] = la[0];
+               out[3] = la[1];
+       }
+       virtual unsigned int GetPixelSize(){
+               return 2;
+       }
+};
+
+class KTX_Decoder_BGR8 : public KTX_Decoder
+{
+public:
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               byte bgr[3];
+               istream.read( bgr, 3 );
+               out[0] = bgr[2];
+               out[1] = bgr[1];
+               out[2] = bgr[0];
+               out[3] = 255;
+       }
+       virtual unsigned int GetPixelSize(){
+               return 3;
+       }
+};
+
+class KTX_Decoder_BGRA8 : public KTX_Decoder
+{
+public:
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               byte bgra[4];
+               istream.read( bgra, 4 );
+               out[0] = bgra[2];
+               out[1] = bgra[1];
+               out[2] = bgra[0];
+               out[3] = bgra[3];
+       }
+       virtual unsigned int GetPixelSize(){
+               return 4;
+       }
+};
+
+class KTX_Decoder_RGBA4 : public KTX_Decoder
+{
+protected:
+       bool m_bigEndian;
+public:
+       KTX_Decoder_RGBA4( bool bigEndian ) : m_bigEndian( bigEndian ){}
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               uint16_t rgba;
+               if ( m_bigEndian ) {
+                       rgba = istream_read_uint16_be( istream );
+               }
+               else {
+                       rgba = istream_read_uint16_le( istream );
+               }
+               int r = ( rgba >> 12 ) & 0xf;
+               int g = ( rgba >> 8 ) & 0xf;
+               int b = ( rgba >> 4 ) & 0xf;
+               int a = rgba & 0xf;
+               out[0] = ( r << 4 ) | r;
+               out[1] = ( g << 4 ) | g;
+               out[2] = ( b << 4 ) | b;
+               out[3] = ( a << 4 ) | a;
+       }
+       virtual unsigned int GetPixelSize(){
+               return 2;
+       }
+};
+
+class KTX_Decoder_RGBA5 : public KTX_Decoder
+{
+protected:
+       bool m_bigEndian;
+public:
+       KTX_Decoder_RGBA5( bool bigEndian ) : m_bigEndian( bigEndian ){}
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               uint16_t rgba;
+               if ( m_bigEndian ) {
+                       rgba = istream_read_uint16_be( istream );
+               }
+               else {
+                       rgba = istream_read_uint16_le( istream );
+               }
+               int r = ( rgba >> 11 ) & 0x1f;
+               int g = ( rgba >> 6 ) & 0x1f;
+               int b = ( rgba >> 1 ) & 0x1f;
+               out[0] = ( r << 3 ) | ( r >> 2 );
+               out[1] = ( g << 3 ) | ( g >> 2 );
+               out[2] = ( b << 3 ) | ( b >> 2 );
+               out[3] = ( rgba & 1 ) * 255;
+       }
+       virtual unsigned int GetPixelSize(){
+               return 2;
+       }
+};
+
+class KTX_Decoder_RGB5 : public KTX_Decoder
+{
+protected:
+       bool m_bigEndian;
+public:
+       KTX_Decoder_RGB5( bool bigEndian ) : m_bigEndian( bigEndian ){}
+       virtual void Decode( PointerInputStream& istream, byte* out ){
+               uint16_t rgb;
+               if ( m_bigEndian ) {
+                       rgb = istream_read_uint16_be( istream );
+               }
+               else {
+                       rgb = istream_read_uint16_le( istream );
+               }
+               int r = ( rgb >> 11 ) & 0x1f;
+               int g = ( rgb >> 5 ) & 0x3f;
+               int b = rgb & 0x1f;
+               out[0] = ( r << 3 ) | ( r >> 2 );
+               out[1] = ( g << 2 ) | ( g >> 4 );
+               out[2] = ( b << 3 ) | ( b >> 2 );
+               out[3] = 255;
+       }
+       virtual unsigned int GetPixelSize(){
+               return 2;
+       }
+};
+
+static void KTX_DecodeETC1( PointerInputStream& istream, Image& image ){
+       unsigned int width = image.getWidth(), height = image.getHeight();
+       unsigned int stride = width * 4;
+       byte* pixbuf = image.getRGBAPixels();
+       byte etc[8], rgba[64];
+
+       for ( unsigned int y = 0; y < height; y += 4, pixbuf += stride * 4 )
+       {
+               unsigned int blockrows = height - y;
+               if ( blockrows > 4 ) {
+                       blockrows = 4;
+               }
+
+               byte* p = pixbuf;
+               for ( unsigned int x = 0; x < width; x += 4, p += 16 )
+               {
+                       istream.read( etc, 8 );
+                       ETC_DecodeETC1Block( etc, rgba, qtrue );
+
+                       unsigned int blockrowsize = width - x;
+                       if ( blockrowsize > 4 ) {
+                               blockrowsize = 4;
+                       }
+                       blockrowsize *= 4;
+                       for ( unsigned int blockrow = 0; blockrow < blockrows; blockrow++ )
+                       {
+                               memcpy( p + blockrow * stride, rgba + blockrow * 16, blockrowsize );
+                       }
+               }
+       }
+}
+
+Image* LoadKTXBuff( PointerInputStream& istream ){
+       byte identifier[12];
+       istream.read( identifier, 12 );
+       if ( memcmp( identifier, "\xABKTX 11\xBB\r\n\x1A\n", 12 ) ) {
+               globalErrorStream() << "LoadKTX: Image has the wrong identifier\n";
+               return 0;
+       }
+
+       bool bigEndian = ( istream_read_uint32_le( istream ) == 0x01020304 );
+
+       unsigned int type;
+       if ( bigEndian ) {
+               type = istream_read_uint32_be( istream );
+       }
+       else {
+               type = istream_read_uint32_le( istream );
+       }
+
+       // For compressed textures, the format is in glInternalFormat.
+       // For uncompressed textures, it's in glBaseInternalFormat.
+       istream.seek( ( type ? 3 : 2 ) * sizeof( uint32_t ) );
+       unsigned int format;
+       if ( bigEndian ) {
+               format = istream_read_uint32_be( istream );
+       }
+       else {
+               format = istream_read_uint32_le( istream );
+       }
+       if ( !type ) {
+               istream.seek( sizeof( uint32_t ) );
+       }
+
+       unsigned int width, height;
+       if ( bigEndian ) {
+               width = istream_read_uint32_be( istream );
+               height = istream_read_uint32_be( istream );
+       }
+       else {
+               width = istream_read_uint32_le( istream );
+               height = istream_read_uint32_le( istream );
+       }
+       if ( !width ) {
+               globalErrorStream() << "LoadKTX: Image has zero width\n";
+               return 0;
+       }
+       if ( !height ) {
+               height = 1;
+       }
+
+       // Skip the key/values and load the first 2D image in the texture.
+       // Since KTXorientation is only a hint and has no effect on the texture data and coordinates, it must be ignored.
+       istream.seek( 4 * sizeof( uint32_t ) );
+       unsigned int bytesOfKeyValueData;
+       if ( bigEndian ) {
+               bytesOfKeyValueData = istream_read_uint32_be( istream );
+       }
+       else {
+               bytesOfKeyValueData = istream_read_uint32_le( istream );
+       }
+       istream.seek( bytesOfKeyValueData + sizeof( uint32_t ) );
+
+       RGBAImage* image = new RGBAImage( width, height );
+
+       if ( type ) {
+               KTX_Decoder* decoder = NULL;
+               switch ( type )
+               {
+               case KTX_TYPE_UNSIGNED_BYTE:
+                       switch ( format )
+                       {
+                       case KTX_FORMAT_ALPHA:
+                               decoder = new KTX_Decoder_A8();
+                               break;
+                       case KTX_FORMAT_RGB:
+                               decoder = new KTX_Decoder_RGB8();
+                               break;
+                       case KTX_FORMAT_RGBA:
+                               decoder = new KTX_Decoder_RGBA8();
+                               break;
+                       case KTX_FORMAT_LUMINANCE:
+                               decoder = new KTX_Decoder_L8();
+                               break;
+                       case KTX_FORMAT_LUMINANCE_ALPHA:
+                               decoder = new KTX_Decoder_LA8();
+                               break;
+                       case KTX_FORMAT_BGR:
+                               decoder = new KTX_Decoder_BGR8();
+                               break;
+                       case KTX_FORMAT_BGRA:
+                               decoder = new KTX_Decoder_BGRA8();
+                               break;
+                       }
+                       break;
+               case KTX_TYPE_UNSIGNED_SHORT_4_4_4_4:
+                       if ( format == KTX_FORMAT_RGBA ) {
+                               decoder = new KTX_Decoder_RGBA4( bigEndian );
+                       }
+                       break;
+               case KTX_TYPE_UNSIGNED_SHORT_5_5_5_1:
+                       if ( format == KTX_FORMAT_RGBA ) {
+                               decoder = new KTX_Decoder_RGBA5( bigEndian );
+                       }
+                       break;
+               case KTX_TYPE_UNSIGNED_SHORT_5_6_5:
+                       if ( format == KTX_FORMAT_RGB ) {
+                               decoder = new KTX_Decoder_RGB5( bigEndian );
+                       }
+                       break;
+               }
+
+               if ( !decoder ) {
+                       globalErrorStream() << "LoadKTX: Image has an unsupported pixel type " << type << " or format " << format << "\n";
+                       image->release();
+                       return 0;
+               }
+
+               unsigned int inRowLength = width * decoder->GetPixelSize();
+               unsigned int inPadding = ( ( inRowLength + 3 ) & ~3 ) - inRowLength;
+               byte* out = image->getRGBAPixels();
+               for ( unsigned int y = 0; y < height; y++ )
+               {
+                       for ( unsigned int x = 0; x < width; x++, out += 4 )
+                       {
+                               decoder->Decode( istream, out );
+                       }
+
+                       if ( inPadding ) {
+                               istream.seek( inPadding );
+                       }
+               }
+
+               delete decoder;
+       }
+       else {
+               switch ( format )
+               {
+               case KTX_FORMAT_ETC1_RGB8:
+                       KTX_DecodeETC1( istream, *image );
+                       break;
+               default:
+                       globalErrorStream() << "LoadKTX: Image has an unsupported compressed format " << format << "\n";
+                       image->release();
+                       return 0;
+               }
+       }
+
+       return image;
+}
+
+Image* LoadKTX( ArchiveFile& file ){
+       ScopedArchiveBuffer buffer( file );
+       PointerInputStream istream( buffer.buffer );
+       return LoadKTXBuff( istream );
+}
diff --git a/plugins/image/ktx.h b/plugins/image/ktx.h
new file mode 100644 (file)
index 0000000..6070077
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+   Copyright (C) 2015, SiPlus, Chasseur de bots.
+   All Rights Reserved.
+
+   This file is part of GtkRadiant.
+
+   GtkRadiant 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.
+
+   GtkRadiant is distributed in the hope that 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 GtkRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined( INCLUDED_KTX_H )
+#define INCLUDED_KTX_H
+
+class Image;
+class ArchiveFile;
+
+Image* LoadKTX( ArchiveFile& file );
+
+#endif
index 696758d41f566465e7e872e439137f70d9bc427a..daa0f31c5f1be2b3e3d7f65740df5dd8febe6e23 100644 (file)
@@ -590,6 +590,10 @@ void DoAbout(){
 // =============================================================================
 // TextureLayout dialog
 
+// Last used texture scale values
+static float last_used_texture_layout_scale_x = 4.0;
+static float last_used_texture_layout_scale_y = 4.0;
+
 EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
        ModalDialog dialog;
        ModalDialogButton ok_button( dialog, eIDOK );
@@ -644,8 +648,6 @@ EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
                                                                          (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
                                                                          (GtkAttachOptions) ( 0 ), 0, 0 );
 
-                                       gtk_widget_grab_focus( GTK_WIDGET( entry ) );
-
                                        x = entry;
                                }
                                {
@@ -675,16 +677,27 @@ EMessageBoxReturn DoTextureLayout( float *fx, float *fy ){
                        }
                }
        }
+       
+       // Initialize with last used values
+       char buf[16];
+       
+       sprintf( buf, "%f", last_used_texture_layout_scale_x );
+       gtk_entry_set_text( x, buf );
+       
+       sprintf( buf, "%f", last_used_texture_layout_scale_y );
+       gtk_entry_set_text( y, buf );
 
-       // Initialize
-       gtk_entry_set_text( x, "4.0" );
-       gtk_entry_set_text( y, "4.0" );
-
+       // Set focus after intializing the values
+       gtk_widget_grab_focus( GTK_WIDGET( x ) );
 
        EMessageBoxReturn ret = modal_dialog_show( window, dialog );
        if ( ret == eIDOK ) {
                *fx = static_cast<float>( atof( gtk_entry_get_text( x ) ) );
                *fy = static_cast<float>( atof( gtk_entry_get_text( y ) ) );
+       
+               // Remember last used values
+               last_used_texture_layout_scale_x = *fx;
+               last_used_texture_layout_scale_y = *fy;
        }
 
        gtk_widget_destroy( GTK_WIDGET( window ) );
index e2739a1106eff243da8d8e90b2a7acb233091e8c..dd91a26868c6d05e68610fb9f7cb067bf77b61f4 100644 (file)
@@ -2205,10 +2205,10 @@ void PatchInspector_registerShortcuts(){
 void Patch_registerShortcuts(){
        command_connect_accelerator( "InvertCurveTextureX" );
        command_connect_accelerator( "InvertCurveTextureY" );
-       command_connect_accelerator( "IncPatchColumn" );
-       command_connect_accelerator( "IncPatchRow" );
-       command_connect_accelerator( "DecPatchColumn" );
-       command_connect_accelerator( "DecPatchRow" );
+       command_connect_accelerator( "PatchInsertInsertColumn" );
+       command_connect_accelerator( "PatchInsertInsertRow" );
+       command_connect_accelerator( "PatchDeleteLastColumn" );
+       command_connect_accelerator( "PatchDeleteLastRow" );
        command_connect_accelerator( "NaturalizePatch" );
        //command_connect_accelerator("CapCurrentCurve");
 }
index daa79f65e5337bb8ec6850345f8777ae44a53867..73639e5463756bf4b3686d29b0b48d7662d5b71f 100644 (file)
@@ -376,29 +376,27 @@ void Scene_PatchTileTexture_Selected( scene::Graph& graph, float s, float t ){
 }
 
 static void OnBtnPatchdetails( GtkWidget *widget, gpointer data ){
-       UndoableCommand command( "patchCapTexture" );
-
-       Scene_PatchCapTexture_Selected( GlobalSceneGraph() );
+       Patch_CapTexture();
 }
 
 static void OnBtnPatchfit( GtkWidget *widget, gpointer data ){
-       UndoableCommand command( "patchFitTexture" );
-
-       Scene_PatchTileTexture_Selected( GlobalSceneGraph(), 1, 1 );
+       Patch_FitTexture();
 }
 
 static void OnBtnPatchnatural( GtkWidget *widget, gpointer data ){
-       UndoableCommand command( "patchNaturalTexture" );
-
-       Scene_PatchNaturalTexture_Selected( GlobalSceneGraph() );
+       Patch_NaturalTexture();
 }
 
 static void OnBtnPatchreset( GtkWidget *widget, gpointer data ){
-       float fx, fy;
-       if ( DoTextureLayout( &fx, &fy ) == eIDOK ) {
-               UndoableCommand command( "patchTileTexture" );
-               Scene_PatchTileTexture_Selected( GlobalSceneGraph(), fx, fy );
-       }
+       Patch_ResetTexture();
+}
+
+static void OnBtnPatchFlipX( GtkWidget *widget, gpointer data ){
+       Patch_FlipTextureX();
+}
+
+static void OnBtnPatchFlipY( GtkWidget *widget, gpointer data ){
+       Patch_FlipTextureY();
 }
 
 struct PatchRotateTexture
@@ -457,6 +455,10 @@ void Scene_PatchTranslateTexture_Selected( scene::Graph& graph, float s, float t
        Scene_forEachVisibleSelectedPatch( PatchTranslateTexture( s, t ) );
 }
 
+static void OnBtnPatchAutoCap( GtkWidget *widget, gpointer data ){
+       Patch_AutoCapTexture();
+}
+
 static void OnSpinChanged( GtkAdjustment *adj, gpointer data ){
        texdef_t td;
 
@@ -825,7 +827,7 @@ GtkWindow* PatchInspector::BuildDialog(){
                                                g_signal_connect( G_OBJECT( entry ), "key_press_event", G_CALLBACK( OnDialogKey ), 0 );
                                        }
                                        {
-                                               GtkTable* table = GTK_TABLE( gtk_table_new( 5, 3, FALSE ) );
+                                               GtkTable* table = GTK_TABLE( gtk_table_new( 5, 4, FALSE ) );
                                                gtk_widget_show( GTK_WIDGET( table ) );
                                                gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
                                                gtk_table_set_row_spacings( table, 5 );
@@ -833,16 +835,16 @@ GtkWindow* PatchInspector::BuildDialog(){
                                                {
                                                        GtkLabel* label = GTK_LABEL( gtk_label_new( "Horizontal Shift Step" ) );
                                                        gtk_widget_show( GTK_WIDGET( label ) );
-                                                       gtk_table_attach( table, GTK_WIDGET( label ), 2, 3, 0, 1,
-                                                                                         (GtkAttachOptions)( GTK_FILL ),
+                                                       gtk_table_attach( table, GTK_WIDGET( label ), 2, 4, 0, 1,
+                                                                                         (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
                                                                                          (GtkAttachOptions)( 0 ), 0, 0 );
                                                        gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
                                                }
                                                {
                                                        GtkLabel* label = GTK_LABEL( gtk_label_new( "Vertical Shift Step" ) );
                                                        gtk_widget_show( GTK_WIDGET( label ) );
-                                                       gtk_table_attach( table, GTK_WIDGET( label ), 2, 3, 1, 2,
-                                                                                         (GtkAttachOptions)( GTK_FILL ),
+                                                       gtk_table_attach( table, GTK_WIDGET( label ), 2, 4, 1, 2,
+                                                                                         (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
                                                                                          (GtkAttachOptions)( 0 ), 0, 0 );
                                                        gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
                                                }
@@ -850,23 +852,41 @@ GtkWindow* PatchInspector::BuildDialog(){
                                                        GtkLabel* label = GTK_LABEL( gtk_label_new( "Horizontal Stretch Step" ) );
                                                        gtk_widget_show( GTK_WIDGET( label ) );
                                                        gtk_table_attach( table, GTK_WIDGET( label ), 2, 3, 2, 3,
-                                                                                         (GtkAttachOptions)( GTK_FILL ),
+                                                                                         (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
                                                                                          (GtkAttachOptions)( 0 ), 0, 0 );
                                                        gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
                                                }
+                                               {
+                                                       GtkButton* button = GTK_BUTTON( gtk_button_new_with_label( "Flip" ) );
+                                                       gtk_widget_show( GTK_WIDGET( button ) );
+                                                       gtk_table_attach( table, GTK_WIDGET( button ), 3, 4, 2, 3,
+                                                                                         (GtkAttachOptions)( GTK_FILL ),
+                                                                                         (GtkAttachOptions)( 0 ), 0, 0 );
+                                                       g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchFlipX ), 0 );
+                                                       gtk_widget_set_usize( GTK_WIDGET( button ), 60, -1 );
+                                               }
                                                {
                                                        GtkLabel* label = GTK_LABEL( gtk_label_new( "Vertical Stretch Step" ) );
                                                        gtk_widget_show( GTK_WIDGET( label ) );
                                                        gtk_table_attach( table, GTK_WIDGET( label ), 2, 3, 3, 4,
-                                                                                         (GtkAttachOptions)( GTK_FILL ),
+                                                                                         (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
                                                                                          (GtkAttachOptions)( 0 ), 0, 0 );
                                                        gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
                                                }
+                                               {
+                                                       GtkButton* button = GTK_BUTTON( gtk_button_new_with_label( "Flip" ) );
+                                                       gtk_widget_show( GTK_WIDGET( button ) );
+                                                       gtk_table_attach( table, GTK_WIDGET( button ), 3, 4, 3, 4,
+                                                                                         (GtkAttachOptions)( GTK_FILL ),
+                                                                                         (GtkAttachOptions)( 0 ), 0, 0 );
+                                                       g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchFlipY ), 0 );
+                                                       gtk_widget_set_usize( GTK_WIDGET( button ), 60, -1 );
+                                               }
                                                {
                                                        GtkLabel* label = GTK_LABEL( gtk_label_new( "Rotate Step" ) );
                                                        gtk_widget_show( GTK_WIDGET( label ) );
-                                                       gtk_table_attach( table, GTK_WIDGET( label ), 2, 3, 4, 5,
-                                                                                         (GtkAttachOptions)( GTK_FILL ),
+                                                       gtk_table_attach( table, GTK_WIDGET( label ), 2, 4, 4, 5,
+                                                                                         (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
                                                                                          (GtkAttachOptions)( 0 ), 0, 0 );
                                                        gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
                                                }
@@ -982,6 +1002,13 @@ GtkWindow* PatchInspector::BuildDialog(){
                                        GtkHBox* hbox2 = GTK_HBOX( gtk_hbox_new( TRUE, 5 ) );
                                        gtk_widget_show( GTK_WIDGET( hbox2 ) );
                                        gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( hbox2 ), TRUE, FALSE, 0 );
+                                       {
+                                               GtkButton* button = GTK_BUTTON( gtk_button_new_with_label( "Auto Cap" ) );
+                                               gtk_widget_show( GTK_WIDGET( button ) );
+                                               gtk_box_pack_end( GTK_BOX( hbox2 ), GTK_WIDGET( button ), TRUE, FALSE, 0 );
+                                               g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchAutoCap ), 0 );
+                                               gtk_widget_set_usize( GTK_WIDGET( button ), 60, -1 );
+                                       }
                                        {
                                                GtkButton* button = GTK_BUTTON( gtk_button_new_with_label( "CAP" ) );
                                                gtk_widget_show( GTK_WIDGET( button ) );
index b7318b28e7b555505fbf6b0da1ad0106b421f6b3..909e3466311f966e23e11e9af66ddea22f221764 100644 (file)
@@ -67,6 +67,32 @@ void Scene_PatchConstructPrefab( scene::Graph& graph, const AABB aabb, const cha
        }
 }
 
+void PatchAutoCapTexture( Patch& patch ) {
+
+       AABB box = patch.localAABB();
+       float x = box.extents.x();
+       float y = box.extents.y();
+       float z = box.extents.z();
+
+       int cap_direction = -1;
+       if ( x < y  && x < z )
+               cap_direction = 0;
+       else if ( y < x  && y < z )
+               cap_direction = 1;
+       else if ( z < x  && z < x )
+               cap_direction = 2;
+
+       if ( cap_direction >= 0 )
+               patch.ProjectTexture(cap_direction);
+       else
+               patch.NaturalTexture();
+}
+
+void Patch_AutoCapTexture(){
+       UndoableCommand command( "patchAutoCapTexture" );
+       Scene_forEachVisibleSelectedPatch( &PatchAutoCapTexture );
+       SceneChangeNotify();
+}
 
 void Patch_makeCaps( Patch& patch, scene::Instance& instance, EPatchCap type, const char* shader ){
        if ( ( type == eCapEndCap || type == eCapIEndCap )
@@ -89,8 +115,10 @@ void Patch_makeCaps( Patch& patch, scene::Instance& instance, EPatchCap type, co
                NodeSmartReference cap( g_patchCreator->createPatch() );
                Node_getTraversable( instance.path().parent() )->insert( cap );
 
-               patch.MakeCap( Node_getPatch( cap ), type, ROW, true );
-               Node_getPatch( cap )->SetShader( shader );
+               Patch* cap_patch = Node_getPatch( cap );
+               patch.MakeCap( cap_patch, type, ROW, true );
+               cap_patch->SetShader( shader );
+               PatchAutoCapTexture(*cap_patch);
 
                scene::Path path( instance.path() );
                path.pop();
@@ -102,8 +130,10 @@ void Patch_makeCaps( Patch& patch, scene::Instance& instance, EPatchCap type, co
                NodeSmartReference cap( g_patchCreator->createPatch() );
                Node_getTraversable( instance.path().parent() )->insert( cap );
 
-               patch.MakeCap( Node_getPatch( cap ), type, ROW, false );
-               Node_getPatch( cap )->SetShader( shader );
+               Patch* cap_patch = Node_getPatch( cap );
+               patch.MakeCap( cap_patch, type, ROW, false );
+               cap_patch->SetShader( shader );
+               PatchAutoCapTexture(*cap_patch);
 
                scene::Path path( instance.path() );
                path.pop();
@@ -585,8 +615,25 @@ void Patch_NaturalTexture(){
        Scene_PatchNaturalTexture_Selected( GlobalSceneGraph() );
 }
 
+void Patch_CapTexture(){
+       UndoableCommand command( "patchCapTexture" );
 
+       Scene_PatchCapTexture_Selected( GlobalSceneGraph() );
+}
 
+void Patch_ResetTexture(){
+       float fx, fy;
+       if ( DoTextureLayout( &fx, &fy ) == eIDOK ) {
+               UndoableCommand command( "patchTileTexture" );
+               Scene_PatchTileTexture_Selected( GlobalSceneGraph(), fx, fy );
+       }
+}
+
+void Patch_FitTexture(){
+       UndoableCommand command( "patchFitTexture" );
+
+       Scene_PatchTileTexture_Selected( GlobalSceneGraph(), 1, 1 );
+}
 
 #include "ifilter.h"
 
@@ -661,10 +708,6 @@ void PatchPreferences_construct(){
 void Patch_registerCommands(){
        GlobalCommands_insert( "InvertCurveTextureX", FreeCaller<Patch_FlipTextureX>(), Accelerator( 'I', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
        GlobalCommands_insert( "InvertCurveTextureY", FreeCaller<Patch_FlipTextureY>(), Accelerator( 'I', (GdkModifierType)GDK_SHIFT_MASK ) );
-       GlobalCommands_insert( "IncPatchColumn", FreeCaller<Patch_InsertInsertColumn>(), Accelerator( GDK_KP_Add, (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
-       GlobalCommands_insert( "IncPatchRow", FreeCaller<Patch_InsertInsertRow>(), Accelerator( GDK_KP_Add, (GdkModifierType)GDK_CONTROL_MASK ) );
-       GlobalCommands_insert( "DecPatchColumn", FreeCaller<Patch_DeleteLastColumn>(), Accelerator( GDK_KP_Subtract, (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
-       GlobalCommands_insert( "DecPatchRow", FreeCaller<Patch_DeleteLastRow>(), Accelerator( GDK_KP_Subtract, (GdkModifierType)GDK_CONTROL_MASK ) );
        GlobalCommands_insert( "NaturalizePatch", FreeCaller<Patch_NaturalTexture>(), Accelerator( 'N', (GdkModifierType)GDK_CONTROL_MASK ) );
        GlobalCommands_insert( "PatchCylinder", FreeCaller<Patch_Cylinder>() );
        GlobalCommands_insert( "PatchDenseCylinder", FreeCaller<Patch_DenseCylinder>() );
@@ -680,13 +723,13 @@ void Patch_registerCommands(){
        GlobalCommands_insert( "PatchCone", FreeCaller<Patch_Cone>() );
        GlobalCommands_insert( "PatchSphere", FreeCaller<Patch_Sphere>() );
        GlobalCommands_insert( "SimplePatchMesh", FreeCaller<Patch_Plane>(), Accelerator( 'P', (GdkModifierType)GDK_SHIFT_MASK ) );
-       GlobalCommands_insert( "PatchInsertInsertColumn", FreeCaller<Patch_InsertInsertColumn>() );
+       GlobalCommands_insert( "PatchInsertInsertColumn", FreeCaller<Patch_InsertInsertColumn>(), Accelerator( GDK_KP_Add, (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
        GlobalCommands_insert( "PatchInsertAddColumn", FreeCaller<Patch_InsertAddColumn>() );
-       GlobalCommands_insert( "PatchInsertInsertRow", FreeCaller<Patch_InsertInsertRow>() );
+       GlobalCommands_insert( "PatchInsertInsertRow", FreeCaller<Patch_InsertInsertRow>(), Accelerator( GDK_KP_Add, (GdkModifierType)GDK_CONTROL_MASK ) );
        GlobalCommands_insert( "PatchInsertAddRow", FreeCaller<Patch_InsertAddRow>() );
        GlobalCommands_insert( "PatchDeleteFirstColumn", FreeCaller<Patch_DeleteFirstColumn>() );
-       GlobalCommands_insert( "PatchDeleteLastColumn", FreeCaller<Patch_DeleteLastColumn>() );
-       GlobalCommands_insert( "PatchDeleteFirstRow", FreeCaller<Patch_DeleteFirstRow>() );
+       GlobalCommands_insert( "PatchDeleteLastColumn", FreeCaller<Patch_DeleteLastColumn>(), Accelerator( GDK_KP_Subtract, (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
+       GlobalCommands_insert( "PatchDeleteFirstRow", FreeCaller<Patch_DeleteFirstRow>(), Accelerator( GDK_KP_Subtract, (GdkModifierType)GDK_CONTROL_MASK ) );
        GlobalCommands_insert( "PatchDeleteLastRow", FreeCaller<Patch_DeleteLastRow>() );
        GlobalCommands_insert( "InvertCurve", FreeCaller<Patch_Invert>(), Accelerator( 'I', (GdkModifierType)GDK_CONTROL_MASK ) );
        GlobalCommands_insert( "RedisperseRows", FreeCaller<Patch_RedisperseRows>(), Accelerator( 'E', (GdkModifierType)GDK_CONTROL_MASK ) );
index 4666ae73494a585c802afa8568360fa6e3cffeed..47f7f2248e201e26aeb70d7cdb8eabcd3ad406fc 100644 (file)
@@ -51,6 +51,14 @@ void PatchPreferences_construct();
 
 void Patch_registerPreferencesPage();
 
+void Patch_NaturalTexture();
+void Patch_CapTexture();
+void Patch_ResetTexture();
+void Patch_FitTexture();
+void Patch_FlipTextureX();
+void Patch_FlipTextureY();
+void Patch_AutoCapTexture();
+
 class PatchCreator;
 extern PatchCreator* g_patchCreator;
 
index 4246ab428d0eee68eb287baaa080d5dd0e57f37d..5d34645a8e4dccf267213cdaffe6b6687d386b8b 100644 (file)
@@ -900,7 +900,7 @@ static gboolean rotatedlg_cancel( GtkWidget *widget, RotateDialog* rotateDialog
 
 static gboolean rotatedlg_ok( GtkWidget *widget, RotateDialog* rotateDialog ){
        rotatedlg_apply( widget, rotateDialog );
-       rotatedlg_cancel( widget, rotateDialog );
+       gtk_widget_hide( GTK_WIDGET( rotateDialog->window ) );
        return TRUE;
 }
 
@@ -1052,7 +1052,7 @@ static gboolean scaledlg_cancel( GtkWidget *widget, ScaleDialog* scaleDialog ){
 
 static gboolean scaledlg_ok( GtkWidget *widget, ScaleDialog* scaleDialog ){
        scaledlg_apply( widget, scaleDialog );
-       scaledlg_cancel( widget, scaleDialog );
+       gtk_widget_hide( GTK_WIDGET( scaleDialog->window ) );
        return TRUE;
 }
 
index ac02d7fd89136301e56f7afaccf41319eed11cd6..73312a078fba5435b28649a9680e03ea31efe045 100644 (file)
@@ -442,23 +442,19 @@ void SurfaceInspector_FitTexture(){
 }
 
 static void OnBtnPatchdetails( GtkWidget *widget, gpointer data ){
-       Scene_PatchCapTexture_Selected( GlobalSceneGraph() );
+       Patch_CapTexture();
 }
 
 static void OnBtnPatchnatural( GtkWidget *widget, gpointer data ){
-       Scene_PatchNaturalTexture_Selected( GlobalSceneGraph() );
+       Patch_NaturalTexture();
 }
 
 static void OnBtnPatchreset( GtkWidget *widget, gpointer data ){
-       float fx, fy;
-
-       if ( DoTextureLayout( &fx, &fy ) == eIDOK ) {
-               Scene_PatchTileTexture_Selected( GlobalSceneGraph(), fx, fy );
-       }
+       Patch_ResetTexture();
 }
 
 static void OnBtnPatchFit( GtkWidget *widget, gpointer data ){
-       Scene_PatchTileTexture_Selected( GlobalSceneGraph(), 1, 1 );
+       Patch_FitTexture();
 }
 
 static void OnBtnAxial( GtkWidget *widget, gpointer data ){
index 5621aaa1712be3c900385600b7568b5e8cb0f8ea..33e7e5c257d8ef227a6e13f75fffa9cf2868953a 100644 (file)
@@ -132,8 +132,9 @@ typedef ReferenceCaller1<TextureGroups, const char*, TextureGroups_addDirectory>
 namespace
 {
 bool g_TextureBrowser_shaderlistOnly = false;
-bool g_TextureBrowser_fixedSize = false;
+bool g_TextureBrowser_fixedSize = true;
 bool g_TextureBrowser_filterNotex = false;
+bool g_TextureBrowser_enableAlpha = true;
 }
 
 class DeferredAdjustment
@@ -203,6 +204,9 @@ typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_fixedSize> Texture
 void TextureBrowser_filterNotex( const BoolImportCallback& importer );
 typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_filterNotex> TextureBrowserFilterNotexExport;
 
+void TextureBrowser_enableAlpha( const BoolImportCallback& importer );
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_enableAlpha> TextureBrowserEnableAlphaExport;
+
 class TextureBrowser
 {
 public:
@@ -238,6 +242,7 @@ ToggleItem m_showshaders_item;
 ToggleItem m_showshaderlistonly_item;
 ToggleItem m_fixedsize_item;
 ToggleItem m_filternotex_item;
+ToggleItem m_enablealpha_item;
 
 guint m_sizeHandler;
 guint m_exposeHandler;
@@ -307,6 +312,7 @@ TextureBrowser() :
        m_showshaderlistonly_item( TextureBrowserShowShaderlistOnlyExport() ),
        m_fixedsize_item( TextureBrowserFixedSizeExport() ),
        m_filternotex_item( TextureBrowserFilterNotexExport() ),
+       m_enablealpha_item( TextureBrowserEnableAlphaExport() ),
        m_heightChanged( true ),
        m_originInvalid( true ),
        m_scrollAdjustment( TextureBrowser_scrollChanged, this ),
@@ -320,7 +326,7 @@ TextureBrowser() :
        m_rmbSelected( false ),
        m_searchedTags( false ),
        m_tags( false ),
-       m_uniformTextureSize( 128 ){
+       m_uniformTextureSize( 96 ){
 }
 };
 
@@ -828,6 +834,11 @@ void TextureBrowser_filterNotex( const BoolImportCallback& importer ){
 }
 typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_filterNotex> TextureBrowser_filterNotexExport;
 
+void TextureBrowser_enableAlpha( const BoolImportCallback& importer ){
+       importer( g_TextureBrowser_enableAlpha );
+}
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_enableAlpha> TextureBrowser_enableAlphaExport;
+
 void TextureBrowser_SetHideUnused( TextureBrowser& textureBrowser, bool hideUnused ){
        if ( hideUnused ) {
                textureBrowser.m_hideUnused = true;
@@ -1021,7 +1032,13 @@ void Texture_Draw( TextureBrowser& textureBrowser ){
 
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        glDisable( GL_DEPTH_TEST );
-       glDisable( GL_BLEND );
+       if ( g_TextureBrowser_enableAlpha ) {
+               glEnable( GL_BLEND );
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       }
+       else {
+               glDisable( GL_BLEND );
+       }
        glOrtho( 0, textureBrowser.width, originy - textureBrowser.height, originy, -100, 100 );
        glEnable( GL_TEXTURE_2D );
 
@@ -1114,6 +1131,30 @@ void Texture_Draw( TextureBrowser& textureBrowser ){
                                }
                        }
 
+                       // draw checkerboard for transparent textures
+                       if ( g_TextureBrowser_enableAlpha )
+                       {
+                               glDisable( GL_TEXTURE_2D );
+                               glBegin( GL_QUADS );
+                               int font_height = TextureBrowser_fontHeight( textureBrowser );
+                               for ( int i = 0; i < nHeight; i += 8 )
+                                       for ( int j = 0; j < nWidth; j += 8 )
+                                       {
+                                               unsigned char color = (i + j) / 8 % 2 ? 0x66 : 0x99;
+                                               glColor3ub( color, color, color );
+                                               int left = j;
+                                               int right = std::min(j+8, nWidth);
+                                               int top = i;
+                                               int bottom = std::min(i+8, nHeight);
+                                               glVertex2i(x + right, y - nHeight - font_height + top);
+                                               glVertex2i(x + left,  y - nHeight - font_height + top);
+                                               glVertex2i(x + left,  y - nHeight - font_height + bottom);
+                                               glVertex2i(x + right, y - nHeight - font_height + bottom);
+                                       }
+                               glEnd();
+                               glEnable( GL_TEXTURE_2D );
+                       }
+
                        // Draw the texture
                        glBindTexture( GL_TEXTURE_2D, q->texture_number );
                        GlobalOpenGL_debugAssertNoErrors();
@@ -1167,6 +1208,12 @@ void TextureBrowser_setScale( TextureBrowser& textureBrowser, std::size_t scale
        TextureBrowser_queueDraw( textureBrowser );
 }
 
+void TextureBrowser_setUniformSize( TextureBrowser& textureBrowser, std::size_t scale ){
+       textureBrowser.m_uniformTextureSize = scale;
+
+       TextureBrowser_queueDraw( textureBrowser );
+}
+
 
 void TextureBrowser_MouseWheel( TextureBrowser& textureBrowser, bool bUp ){
        int originy = TextureBrowser_getOriginY( textureBrowser );
@@ -1583,7 +1630,9 @@ GtkMenuItem* TextureBrowser_constructViewMenu( GtkMenu* menu ){
                create_menu_item_with_mnemonic( menu, "Show Untagged", "ShowUntagged" );
        }
 
+       menu_separator( menu );
        create_check_menu_item_with_mnemonic( menu, "Fixed Size", "FixedSize" );
+       create_check_menu_item_with_mnemonic( menu, "Transparency", "EnableAlpha" );
 
        if ( string_empty( g_pGameDescription->getKeyValue( "show_wads" ) ) ) {
                menu_separator( menu );
@@ -1591,6 +1640,7 @@ GtkMenuItem* TextureBrowser_constructViewMenu( GtkMenu* menu ){
                gtk_widget_set_sensitive( g_TextureBrowser.m_shader_info_item, FALSE );
        }
 
+
        return textures_menu_item;
 }
 
@@ -2310,6 +2360,23 @@ void RefreshShaders(){
        ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Shaders" );
        GlobalShaderSystem().refresh();
        UpdateAllWindows();
+       GtkTreeSelection* selection = gtk_tree_view_get_selection((GtkTreeView*)GlobalTextureBrowser().m_treeViewTree);
+       GtkTreeModel* model = NULL;
+       GtkTreeIter iter;
+       if ( gtk_tree_selection_get_selected (selection, &model, &iter) )
+       {
+               gchar dirName[1024];
+
+               gchar* buffer;
+               gtk_tree_model_get( model, &iter, 0, &buffer, -1 );
+               strcpy( dirName, buffer );
+               g_free( buffer );
+               if ( !TextureBrowser_showWads() ) {
+                       strcat( dirName, "/" );
+               }
+               TextureBrowser_ShowDirectory( GlobalTextureBrowser(), dirName );
+               TextureBrowser_queueDraw( GlobalTextureBrowser() );
+       }
 }
 
 void TextureBrowser_ToggleShowShaders(){
@@ -2371,6 +2438,12 @@ void TextureBrowser_FilterNotex(){
        TextureBrowser_activeShadersChanged( GlobalTextureBrowser() );
 }
 
+void TextureBrowser_EnableAlpha(){
+       g_TextureBrowser_enableAlpha ^= 1;
+       GlobalTextureBrowser().m_enablealpha_item.update();
+       TextureBrowser_activeShadersChanged( GlobalTextureBrowser() );
+}
+
 void TextureBrowser_exportTitle( const StringImportCallback& importer ){
        StringOutputStream buffer( 64 );
        buffer << "Textures: ";
@@ -2429,6 +2502,14 @@ void TextureScaleExport( TextureBrowser& textureBrowser, const IntImportCallback
 }
 typedef ReferenceCaller1<TextureBrowser, const IntImportCallback&, TextureScaleExport> TextureScaleExportCaller;
 
+
+void UniformTextureSizeImport( TextureBrowser& textureBrowser, int value ){
+
+       if ( value > 16 )
+               TextureBrowser_setUniformSize( textureBrowser, value );
+}
+typedef ReferenceCaller1<TextureBrowser, int, UniformTextureSizeImport> UniformTextureSizeImportCaller;
+
 void TextureBrowser_constructPreferences( PreferencesPage& page ){
        page.appendCheckBox(
                "", "Texture scrollbar",
@@ -2444,6 +2525,12 @@ void TextureBrowser_constructPreferences( PreferencesPage& page ){
                        IntExportCallback( TextureScaleExportCaller( GlobalTextureBrowser() ) )
                        );
        }
+       page.appendSpinner(
+               "Texture Thumbnail Size",
+               GlobalTextureBrowser().m_uniformTextureSize,
+               GlobalTextureBrowser().m_uniformTextureSize,
+               16, 8192
+       );
        page.appendEntry( "Mousewheel Increment", GlobalTextureBrowser().m_mouseWheelScrollIncrement );
        {
                const char* startup_shaders[] = { "None", TextureBrowser_getComonShadersName() };
@@ -2484,11 +2571,15 @@ void TextureBrowser_Construct(){
        GlobalToggles_insert( "ToggleShowShaderlistOnly", FreeCaller<TextureBrowser_ToggleShowShaderListOnly>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaderlistonly_item ) );
        GlobalToggles_insert( "FixedSize", FreeCaller<TextureBrowser_FixedSize>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_fixedsize_item ) );
        GlobalToggles_insert( "FilterNotex", FreeCaller<TextureBrowser_FilterNotex>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_filternotex_item ) );
+       GlobalToggles_insert( "EnableAlpha", FreeCaller<TextureBrowser_EnableAlpha>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_enablealpha_item ) );
 
        GlobalPreferenceSystem().registerPreference( "TextureScale",
                                                                                                 makeSizeStringImportCallback( TextureBrowserSetScaleCaller( g_TextureBrowser ) ),
                                                                                                 SizeExportStringCaller( g_TextureBrowser.m_textureScale )
                                                                                                 );
+       GlobalPreferenceSystem().registerPreference( "UniformTextureSize",
+                                                                                                makeIntStringImportCallback(UniformTextureSizeImportCaller(g_TextureBrowser)),
+                                                                                                IntExportStringCaller(g_TextureBrowser.m_uniformTextureSize) );
        GlobalPreferenceSystem().registerPreference( "TextureScrollbar",
                                                                                                 makeBoolStringImportCallback( TextureBrowserImportShowScrollbarCaller( g_TextureBrowser ) ),
                                                                                                 BoolExportStringCaller( GlobalTextureBrowser().m_showTextureScrollbar )
@@ -2497,6 +2588,7 @@ void TextureBrowser_Construct(){
        GlobalPreferenceSystem().registerPreference( "ShowShaderlistOnly", BoolImportStringCaller( g_TextureBrowser_shaderlistOnly ), BoolExportStringCaller( g_TextureBrowser_shaderlistOnly ) );
        GlobalPreferenceSystem().registerPreference( "FixedSize", BoolImportStringCaller( g_TextureBrowser_fixedSize ), BoolExportStringCaller( g_TextureBrowser_fixedSize ) );
        GlobalPreferenceSystem().registerPreference( "FilterNotex", BoolImportStringCaller( g_TextureBrowser_filterNotex ), BoolExportStringCaller( g_TextureBrowser_filterNotex ) );
+       GlobalPreferenceSystem().registerPreference( "EnableAlpha", BoolImportStringCaller( g_TextureBrowser_enableAlpha ), BoolExportStringCaller( g_TextureBrowser_enableAlpha ) );
        GlobalPreferenceSystem().registerPreference( "LoadShaders", IntImportStringCaller( reinterpret_cast<int&>( GlobalTextureBrowser().m_startupShaders ) ), IntExportStringCaller( reinterpret_cast<int&>( GlobalTextureBrowser().m_startupShaders ) ) );
        GlobalPreferenceSystem().registerPreference( "WheelMouseInc", SizeImportStringCaller( GlobalTextureBrowser().m_mouseWheelScrollIncrement ), SizeExportStringCaller( GlobalTextureBrowser().m_mouseWheelScrollIncrement ) );
        GlobalPreferenceSystem().registerPreference( "SI_Colors0", Vector3ImportStringCaller( GlobalTextureBrowser().color_textureback ), Vector3ExportStringCaller( GlobalTextureBrowser().color_textureback ) );
index c349b498c3b4003cd2bdd2355453527343fb5d93..3a5902924879880f863cb0fed11408bfbeb17ccf 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "inout.h"
 #include "cmdlib.h"
+#include "etclib.h"
 #include "imagelib.h"
 #include "vfs.h"
 
@@ -1232,3 +1233,302 @@ void Load32BitImage( const char *name, unsigned **pixels,  int *width, int *heig
                }
        }
 }
+
+
+/*
+   ============================================================================
+
+   KHRONOS TEXTURE
+
+   ============================================================================
+ */
+
+
+#define KTX_UINT32_LE( buf ) ( ( unsigned int )( (buf)[0] | ( (buf)[1] << 8 ) | ( (buf)[2] << 16 ) | ( (buf)[3] << 24 ) ) )
+#define KTX_UINT32_BE( buf ) ( ( unsigned int )( (buf)[3] | ( (buf)[2] << 8 ) | ( (buf)[1] << 16 ) | ( (buf)[0] << 24 ) ) )
+
+#define KTX_TYPE_UNSIGNED_BYTE                         0x1401
+#define KTX_TYPE_UNSIGNED_SHORT_4_4_4_4                0x8033
+#define KTX_TYPE_UNSIGNED_SHORT_5_5_5_1                0x8034
+#define KTX_TYPE_UNSIGNED_SHORT_5_6_5          0x8363
+
+#define KTX_FORMAT_ALPHA                                       0x1906
+#define KTX_FORMAT_RGB                                         0x1907
+#define KTX_FORMAT_RGBA                                                0x1908
+#define KTX_FORMAT_LUMINANCE                           0x1909
+#define KTX_FORMAT_LUMINANCE_ALPHA                     0x190A
+#define KTX_FORMAT_BGR                                         0x80E0
+#define KTX_FORMAT_BGRA                                                0x80E1
+
+#define KTX_FORMAT_ETC1_RGB8                           0x8D64
+
+static void KTX_DecodeA8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = out[1] = out[2] = 0;
+       out[3] = in[0];
+}
+
+static void KTX_DecodeRGB8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = in[0];
+       out[1] = in[1];
+       out[2] = in[2];
+       out[3] = 255;
+}
+
+static void KTX_DecodeRGBA8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = in[0];
+       out[1] = in[1];
+       out[2] = in[2];
+       out[3] = in[3];
+}
+
+static void KTX_DecodeL8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = out[1] = out[2] = in[0];
+       out[3] = 255;
+}
+
+static void KTX_DecodeLA8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = out[1] = out[2] = in[0];
+       out[3] = in[1];
+}
+
+static void KTX_DecodeBGR8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = in[2];
+       out[1] = in[1];
+       out[2] = in[0];
+       out[3] = 255;
+}
+
+static void KTX_DecodeBGRA8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = in[2];
+       out[1] = in[1];
+       out[2] = in[0];
+       out[3] = in[3];
+}
+
+static void KTX_DecodeRGBA4( const byte *in, qboolean bigEndian, byte *out ){
+       unsigned short rgba;
+       int r, g, b, a;
+
+       if ( bigEndian ) {
+               rgba = ( in[0] << 8 ) | in[1];
+       }
+       else {
+               rgba = ( in[1] << 8 ) | in[0];
+       }
+
+       r = ( rgba >> 12 ) & 0xf;
+       g = ( rgba >> 8 ) & 0xf;
+       b = ( rgba >> 4 ) & 0xf;
+       a = rgba & 0xf;
+       out[0] = ( r << 4 ) | r;
+       out[1] = ( g << 4 ) | g;
+       out[2] = ( b << 4 ) | b;
+       out[3] = ( a << 4 ) | a;
+}
+
+static void KTX_DecodeRGBA5( const byte *in, qboolean bigEndian, byte *out ){
+       unsigned short rgba;
+       int r, g, b;
+
+       if ( bigEndian ) {
+               rgba = ( in[0] << 8 ) | in[1];
+       }
+       else {
+               rgba = ( in[1] << 8 ) | in[0];
+       }
+
+       r = ( rgba >> 11 ) & 0x1f;
+       g = ( rgba >> 6 ) & 0x1f;
+       b = ( rgba >> 1 ) & 0x1f;
+       out[0] = ( r << 3 ) | ( r >> 2 );
+       out[1] = ( g << 3 ) | ( g >> 2 );
+       out[2] = ( b << 3 ) | ( b >> 2 );
+       out[3] = ( rgba & 1 ) * 255;
+}
+
+static void KTX_DecodeRGB5( const byte *in, qboolean bigEndian, byte *out ){
+       unsigned short rgba;
+       int r, g, b;
+
+       if ( bigEndian ) {
+               rgba = ( in[0] << 8 ) | in[1];
+       }
+       else {
+               rgba = ( in[1] << 8 ) | in[0];
+       }
+
+       r = ( rgba >> 11 ) & 0x1f;
+       g = ( rgba >> 5 ) & 0x3f;
+       b = rgba & 0x1f;
+       out[0] = ( r << 3 ) | ( r >> 2 );
+       out[1] = ( g << 2 ) | ( g >> 4 );
+       out[2] = ( b << 3 ) | ( b >> 2 );
+       out[3] = 255;
+}
+
+typedef struct
+{
+       unsigned int type;
+       unsigned int format;
+       unsigned int pixelSize;
+       void ( *decode )( const byte *in, qboolean bigEndian, byte *out );
+} KTX_UncompressedFormat_t;
+
+static const KTX_UncompressedFormat_t KTX_UncompressedFormats[] =
+{
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_ALPHA, 1, KTX_DecodeA8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_RGB, 3, KTX_DecodeRGB8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_RGBA, 4, KTX_DecodeRGBA8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_LUMINANCE, 1, KTX_DecodeL8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_LUMINANCE_ALPHA, 2, KTX_DecodeLA8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_BGR, 3, KTX_DecodeBGR8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_BGRA, 4, KTX_DecodeBGRA8 },
+       { KTX_TYPE_UNSIGNED_SHORT_4_4_4_4, KTX_FORMAT_RGBA, 2, KTX_DecodeRGBA4 },
+       { KTX_TYPE_UNSIGNED_SHORT_5_5_5_1, KTX_FORMAT_RGBA, 2, KTX_DecodeRGBA5 },
+       { KTX_TYPE_UNSIGNED_SHORT_5_6_5, KTX_FORMAT_RGB, 2, KTX_DecodeRGB5 },
+       { 0, 0, 0, NULL }
+};
+
+static qboolean KTX_DecodeETC1( const byte* in, size_t inSize, unsigned int width, unsigned int height, byte* out ){
+       unsigned int y, stride = width * 4;
+       byte rgba[64];
+
+       if ( inSize < ( ( ( ( width + 3 ) & ~3 ) * ( ( height + 3 ) & ~3 ) ) >> 1 ) ) {
+               return qfalse;
+       }
+
+       for ( y = 0; y < height; y += 4, out += stride * 4 )
+       {
+               byte *p;
+               unsigned int x, blockrows;
+
+               blockrows = height - y;
+               if ( blockrows > 4 ) {
+                       blockrows = 4;
+               }
+
+               p = out;
+               for ( x = 0; x < width; x += 4, p += 16 )
+               {
+                       unsigned int blockrowsize, blockrow;
+
+                       ETC_DecodeETC1Block( in, rgba, qtrue );
+                       in += 8;
+
+                       blockrowsize = width - x;
+                       if ( blockrowsize > 4 ) {
+                               blockrowsize = 4;
+                       }
+                       blockrowsize *= 4;
+                       for ( blockrow = 0; blockrow < blockrows; blockrow++ )
+                       {
+                               memcpy( p + blockrow * stride, rgba + blockrow * 16, blockrowsize );
+                       }
+               }
+       }
+
+       return qtrue;
+}
+
+#define KTX_HEADER_UINT32( buf ) ( bigEndian ? KTX_UINT32_BE( buf ) : KTX_UINT32_LE( buf ) )
+
+void LoadKTXBufferFirstImage( const byte *buffer, size_t bufSize, byte **pic, int *picWidth, int *picHeight ){
+       unsigned int type, format, width, height, imageOffset;
+       byte *pixels;
+
+       if ( bufSize < 64 ) {
+               Error( "LoadKTX: Image doesn't have a header" );
+       }
+
+       if ( memcmp( buffer, "\xABKTX 11\xBB\r\n\x1A\n", 12 ) ) {
+               Error( "LoadKTX: Image has the wrong identifier" );
+       }
+
+       qboolean bigEndian = ( buffer[4] == 4 );
+
+       type = KTX_HEADER_UINT32( buffer + 16 );
+       if ( type ) {
+               format = KTX_HEADER_UINT32( buffer + 32 );
+       }
+       else {
+               format = KTX_HEADER_UINT32( buffer + 28 );
+       }
+
+       width = KTX_HEADER_UINT32( buffer + 36 );
+       height = KTX_HEADER_UINT32( buffer + 40 );
+       if ( !width ) {
+               Error( "LoadKTX: Image has zero width" );
+       }
+       if ( !height ) {
+               height = 1;
+       }
+       if ( picWidth ) {
+               *picWidth = width;
+       }
+       if ( picHeight ) {
+               *picHeight = height;
+       }
+
+       imageOffset = 64 + KTX_HEADER_UINT32( buffer + 60 ) + 4;
+       if ( bufSize < imageOffset ) {
+               Error( "LoadKTX: No image in the file" );
+       }
+       buffer += imageOffset;
+       bufSize -= imageOffset;
+
+       pixels = safe_malloc( width * height * 4 );
+       *pic = pixels;
+
+       if ( type ) {
+               const KTX_UncompressedFormat_t *ktxFormat = KTX_UncompressedFormats;
+               unsigned int pixelSize;
+               unsigned int inRowLength, inPadding;
+               unsigned int y;
+
+               while ( ktxFormat->type )
+               {
+                       if ( ktxFormat->type == type && ktxFormat->format == format ) {
+                               break;
+                       }
+                       ktxFormat++;
+               }
+               if ( !ktxFormat->type ) {
+                       Error( "LoadKTX: Image has an unsupported pixel type 0x%X or format 0x%X", type, format );
+               }
+
+               pixelSize = ktxFormat->pixelSize;
+               inRowLength = width * pixelSize;
+               inPadding = ( ( inRowLength + 3 ) & ~3 ) - inRowLength;
+
+               if ( bufSize < height * ( inRowLength + inPadding ) ) {
+                       Error( "LoadKTX: Image is truncated" );
+               }
+
+               for ( y = 0; y < height; y++ )
+               {
+                       unsigned int x;
+                       for ( x = 0; x < width; x++, buffer += pixelSize, pixels += 4 )
+                       {
+                               ktxFormat->decode( buffer, bigEndian, pixels );
+                       }
+                       buffer += inPadding;
+               }
+       }
+       else {
+               qboolean decoded = qfalse;
+
+               switch ( format )
+               {
+               case KTX_FORMAT_ETC1_RGB8:
+                       decoded = KTX_DecodeETC1( buffer, bufSize, width, height, pixels );
+                       break;
+               default:
+                       Error( "LoadKTX: Image has an unsupported compressed format format 0x%X", format );
+                       break;
+               }
+
+               if ( !decoded ) {
+                       Error( "LoadKTX: Image is truncated" );
+               }
+       }
+}
index 194ac5b13e2edea15dfb660b37d188d95b969cde..8c00f7ce15891718cc4d61cc6d9c61c88f10a878 100644 (file)
@@ -43,3 +43,5 @@ void WriteTGAGray( const char *filename, byte *data, int width, int height );
 int LoadJPGBuff( void *src_buffer, int src_size, unsigned char **pic, int *width, int *height );
 
 void Load32BitImage( const char *name, unsigned **pixels, int *width, int *height );
+
+void LoadKTXBufferFirstImage( const byte *buffer, size_t bufSize, byte **pic, int *picWidth, int *picHeight );
index 4d44bf36c8743ac9dd5a58f6c661f80a15dcee88..04946dd2f72edfc87566a954fee655d57becd854 100644 (file)
@@ -538,7 +538,7 @@ void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )( int ) ){
        size_t stacksize;
 
        int start, end;
-       int i = 0, status = 0;
+       int i = 0;
 
        start     = I_FloatTime();
        pacifier  = showpacifier;
@@ -582,7 +582,7 @@ void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )( int ) ){
                }
                for ( i = 0 ; i < numthreads ; i++ )
                {
-                       if ( pthread_join( work_threads[i], (void **)&status ) != 0 ) {
+                       if ( pthread_join( work_threads[i], NULL ) != 0 ) {
                                Error( "pthread_join failed" );
                        }
                }
index 5ab1d48b1c1a4799b295564851401d751a93a6e0..c1c737f51b3282b74d827a80fd9fe06110ec7346 100644 (file)
@@ -402,7 +402,8 @@ image_t *ImageLoad( const char *filename ){
                        size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );
                        if ( size > 0 ) {
                                if ( LoadJPGBuff( buffer, size, &image->pixels, &image->width, &image->height ) == -1 && image->pixels != NULL ) {
-                                       Sys_Printf( "WARNING: LoadJPGBuff: %s\n", (unsigned char*) image->pixels );
+                                       // On error, LoadJPGBuff might store a pointer to the error message in image->pixels
+                                       Sys_Printf( "WARNING: LoadJPGBuff %s %s\n", name, (unsigned char*) image->pixels );
                                }
                                alphaHack = qtrue;
                        }
@@ -429,6 +430,16 @@ image_t *ImageLoad( const char *filename ){
                                        }
                                        #endif
                                }
+                               else
+                               {
+                                       /* attempt to load ktx */
+                                       StripExtension( name );
+                                       strcat( name, ".ktx" );
+                                       size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );
+                                       if ( size > 0 ) {
+                                               LoadKTXBufferFirstImage( buffer, size, &image->pixels, &image->width, &image->height );
+                                       }
+                               }
                        }
                }
        }
@@ -460,15 +471,19 @@ image_t *ImageLoad( const char *filename ){
                if ( size > 0 ) {
                        unsigned char *pixels;
                        int width, height;
-                       if ( LoadJPGBuff( buffer, size, &pixels, &width, &height ) == -1 && pixels != NULL ) {
-                               Sys_Printf( "WARNING: LoadJPGBuff: %s\n", (unsigned char*) image->pixels );
-                       }
-                       if ( pixels && width == image->width && height == image->height ) {
-                               int i;
-                               for ( i = 0; i < width * height; ++i )
-                                       image->pixels[4 * i + 3] = pixels[4 * i + 2];  // copy alpha from blue channel
+                       if ( LoadJPGBuff( buffer, size, &pixels, &width, &height ) == -1 ) {
+                               if (pixels) {
+                                       // On error, LoadJPGBuff might store a pointer to the error message in pixels
+                                       Sys_Printf( "WARNING: LoadJPGBuff %s %s\n", name, (unsigned char*) pixels );
+                               }                               
+                       } else {
+                               if ( width == image->width && height == image->height ) {
+                                       int i;
+                                       for ( i = 0; i < width * height; ++i )
+                                               image->pixels[4 * i + 3] = pixels[4 * i + 2];  // copy alpha from blue channel
+                               }
+                               free( pixels );
                        }
-                       free( pixels );
                        free( buffer );
                }
        }
index ffcd832a5105f8f5648dc2381706d1c6ab2a506c..a09659795a527a326e7628697a36d20c8881d1fc 100644 (file)
@@ -2343,12 +2343,21 @@ int LightMain( int argc, char **argv ){
                        Sys_Printf( "Colors are linear\n" );
                }
 
-               else if ( !strcmp( argv[ i ], "-nosRGB" ) ) {
+               else if ( !strcmp( argv[ i ], "-sRGB" ) ) {
                        lightmapsRGB = qtrue;
-                       Sys_Printf( "Lighting is linear\n" );
+                       Sys_Printf( "Lighting is in sRGB\n" );
                        texturesRGB = qtrue;
-                       Sys_Printf( "Textures are linear\n" );
+                       Sys_Printf( "Textures are in sRGB\n" );
                        colorsRGB = qtrue;
+                       Sys_Printf( "Colors are in sRGB\n" );
+               }
+
+               else if ( !strcmp( argv[ i ], "-nosRGB" ) ) {
+                       lightmapsRGB = qfalse;
+                       Sys_Printf( "Lighting is linear\n" );
+                       texturesRGB = qfalse;
+                       Sys_Printf( "Textures are linear\n" );
+                       colorsRGB = qfalse;
                        Sys_Printf( "Colors are linear\n" );
                }
 
index f72653ab88fb21f849a820b341e1a6808a6e7931..37d8c4e57ca4f6ed141cc37f2f8e24dd01a47d6a 100644 (file)
@@ -264,6 +264,8 @@ static void RadSample( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm,
        float st[ 2 ], lightmap[ 2 ], *radLuxel;
        radVert_t   *rv[ 3 ];
 
+       if (!bouncing)
+               Sys_Printf( "BUG: RadSample: !bouncing shouldn't happen\n" );
 
        /* initial setup */
        ClearBounds( mins, maxs );
@@ -551,6 +553,9 @@ static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, raw
 
        /* bouncing light? */
        if ( !bouncing ) {
+               /* This is weird. This actually handles surfacelight and not
+                * bounces. */
+
                /* handle first-pass lights in normal q3a style */
                value = si->value;
                light->photons = value * area * areaScale;
@@ -616,6 +621,9 @@ static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, raw
                light->dist = DotProduct( light->origin, normal );
        }
 
+       if (light->photons < 0 || light->add < 0 || light->color[0] < 0 || light->color[1] < 0 || light->color[2] < 0)
+               Sys_Printf( "BUG: RadSubdivideDiffuseLight created a darkbulb\n" );
+
        /* emit light from both sides? */
        if ( si->compileFlags & C_FOG || si->twoSided ) {
                light->flags |= LIGHT_TWOSIDED;
index 76591a2ad29391bfd6b54cf43b17b912ace87f9a..8517b0bc6f113618b4489a568e05b29041de70e5 100644 (file)
@@ -1705,6 +1705,364 @@ int ConvertBSPMain( int argc, char **argv ){
 }
 
 
+struct HelpOption
+{
+       const char* name;
+       const char* description;
+};
+
+void HelpOptions(const char* group_name, int indentation, int width, struct HelpOption* options, int count)
+{
+       indentation *= 2;
+       char* indent = malloc(indentation+1);
+       memset(indent, ' ', indentation);
+       indent[indentation] = 0;
+       printf("%s%s:\n", indent, group_name);
+       indentation += 2;
+       indent = realloc(indent, indentation+1);
+       memset(indent, ' ', indentation);
+       indent[indentation] = 0;
+
+       int i;
+       for ( i = 0; i < count; i++ )
+       {
+               int printed = printf("%s%-24s  ", indent, options[i].name);
+               int descsz = strlen(options[i].description);
+               int j = 0;
+               while ( j < descsz && descsz-j > width - printed )
+               {
+                       if ( j != 0 )
+                               printf("%s%26c",indent,' ');
+                       int fragment = width - printed;
+                       while ( fragment > 0 && options[i].description[j+fragment-1] != ' ')
+                                       fragment--;
+                       j += fwrite(options[i].description+j, sizeof(char), fragment, stdout);
+                       putchar('\n');
+                       printed = indentation+26;
+               }
+               if ( j == 0 )
+               {
+                       printf("%s\n",options[i].description+j);
+               }
+               else if ( j < descsz )
+               {
+                       printf("%s%26c%s\n",indent,' ',options[i].description+j);
+               }
+       }
+
+       putchar('\n');
+
+       free(indent);
+}
+
+void HelpBsp()
+{
+       struct HelpOption bsp[] = {
+               {"-bsp <filename.map>", "Switch that enters this stage"},
+               {"-altsplit", "Alternate BSP tree splitting weights (should give more fps)"},
+               {"-celshader <shadername>", "Sets a global cel shader name"},
+               {"-custinfoparms", "Read scripts/custinfoparms.txt"},
+               {"-debuginset", "Push all triangle vertexes towards the triangle center"},
+               {"-debugportals", "Make BSP portals visible in the map"},
+               {"-debugsurfaces", "Color the vertexes according to the index of the surface"},
+               {"-deep", "Use detail brushes in the BSP tree, but at lowest priority (should give more fps)"},
+               {"-de <F>", "Distance epsilon for plane snapping etc."},
+               {"-fakemap", "Write fakemap.map containing all world brushes"},
+               {"-flares", "Turn on support for flares (TEST?)"},
+               {"-flat", "Enable flat shading (good for combining with -celshader)"},
+               {"-fulldetail", "Treat detail brushes as structural ones"},
+               {"-leaktest", "Abort if a leak was found"},
+               {"-meta", "Combine adjacent triangles of the same texture to surfaces (ALWAYS USE THIS)"},
+               {"-minsamplesize <N>", "Sets minimum lightmap resolution in luxels/qu"},
+               {"-mi <N>", "Sets the maximum number of indexes per surface"},
+               {"-mv <N>", "Sets the maximum number of vertices of a lightmapped surface"},
+               {"-ne <F>", "Normal epsilon for plane snapping etc."},
+               {"-nocurves", "Turn off support for patches"},
+               {"-nodetail", "Leave out detail brushes"},
+               {"-noflares", "Turn off support for flares"},
+               {"-nofog", "Turn off support for fog volumes"},
+               {"-nohint", "Turn off support for hint brushes"},
+               {"-nosubdivide", "Turn off support for `q3map_tessSize` (breaks water vertex deforms)"},
+               {"-notjunc", "Do not fix T-junctions (causes cracks between triangles, do not use)"},
+               {"-nowater", "Turn off support for water, slime or lava (Stef, this is for you)"},
+               {"-np <A>", "Force all surfaces to be nonplanar with a given shade angle"},
+               {"-onlyents", "Only update entities in the BSP"},
+               {"-patchmeta", "Turn patches into triangle meshes for display"},
+               {"-rename", "Append â€œbspâ€\9d suffix to miscmodel shaders (needed for SoF2)"},
+               {"-samplesize <N>", "Sets default lightmap resolution in luxels/qu"},
+               {"-skyfix", "Turn sky box into six surfaces to work around ATI problems"},
+               {"-snap <N>", "Snap brush bevel planes to the given number of units"},
+               {"-tempname <filename.map>", "Read the MAP file from the given file name"},
+               {"-texrange <N>", "Limit per-surface texture range to the given number of units, and subdivide surfaces like with `q3map_tessSize` if this is not met"},
+               {"-tmpout", "Write the BSP file to /tmp"},
+               {"-verboseentities", "Enable `-v` only for map entities, not for the world"},
+       };
+       HelpOptions("BSP Stage", 0, 80, bsp, sizeof(bsp)/sizeof(struct HelpOption));
+}
+void HelpVis()
+{
+       struct HelpOption vis[] = {
+               {"-vis <filename.map>", "Switch that enters this stage"},
+               {"-fast", "Very fast and crude vis calculation"},
+               {"-mergeportals", "The less crude half of `-merge`, makes vis sometimes much faster but doesn't hurt fps usually"},
+               {"-merge", "Faster but still okay vis calculation"},
+               {"-nopassage", "Just use PortalFlow vis (usually less fps)"},
+               {"-nosort", "Do not sort the portals before calculating vis (usually slower)"},
+               {"-passageOnly", "Just use PassageFlow vis (usually less fps)"},
+               {"-saveprt", "Keep the PRT file after running vis (so you can run vis again)"},
+               {"-tmpin", "Use /tmp folder for input"},
+               {"-tmpout", "Use /tmp folder for output"},
+       };
+       HelpOptions("VIS Stage", 0, 80, vis, sizeof(vis)/sizeof(struct HelpOption));
+}
+void HelpLight()
+{
+       struct HelpOption light[] = {
+               {"-light <filename.map>", "Switch that enters this stage"},
+               {"-vlight <filename.map>", "Deprecated alias for `-light -fast` ... filename.map"},
+               {"-approx <N>", "Vertex light approximation tolerance (never use in conjunction with deluxemapping)"},
+               {"-areascale <F, `-area` F>", "Scaling factor for area lights (surfacelight)"},
+               {"-border", "Add a red border to lightmaps for debugging"},
+               {"-bouncegrid", "Also compute radiosity on the light grid"},
+               {"-bounceonly", "Only compute radiosity"},
+               {"-bouncescale <F>", "Scaling factor for radiosity"},
+               {"-bounce <N>", "Number of bounces for radiosity"},
+               {"-cheapgrid", "Use `-cheap` style lighting for radiosity"},
+               {"-cheap", "Abort vertex light calculations when white is reached"},
+               {"-compensate <F>", "Lightmap compensate (darkening factor applied after everything else)"},
+               {"-cpma", "CPMA vertex lighting mode"},
+               {"-custinfoparms", "Read scripts/custinfoparms.txt"},
+               {"-dark", "Darken lightmap seams"},
+               {"-debugaxis", "Color the lightmaps according to the lightmap axis"},
+               {"-debugcluster", "Color the lightmaps according to the index of the cluster"},
+               {"-debugdeluxe", "Show deluxemaps on the lightmap"},
+               {"-debugnormals", "Color the lightmaps according to the direction of the surface normal"},
+               {"-debugorigin", "Color the lightmaps according to the origin of the luxels"},
+               {"-debugsurfaces, -debugsurface", "Color the lightmaps according to the index of the surface"},
+               {"-debugunused", "This option does nothing"},
+               {"-debug", "Mark the lightmaps according to the cluster: unmapped clusters get yellow, occluded ones get pink, flooded ones get blue overlay color, otherwise red"},
+               {"-deluxemode 0", "Use modelspace deluxemaps (DarkPlaces)"},
+               {"-deluxemode 1", "Use tangentspace deluxemaps"},
+               {"-deluxe, -deluxemap", "Enable deluxemapping (light direction maps)"},
+               {"-dirtdebug, -debugdirt", "Store the dirtmaps as lightmaps for debugging"},
+               {"-dirtdepth", "Dirtmapping depth"},
+               {"-dirtgain", "Dirtmapping exponent"},
+               {"-dirtmode 0", "Ordered direction dirtmapping"},
+               {"-dirtmode 1", "Randomized direction dirtmapping"},
+               {"-dirtscale", "Dirtmapping scaling factor"},
+               {"-dirty", "Enable dirtmapping"},
+               {"-dump", "Dump radiosity from `-bounce` into numbered MAP file prefabs"},
+               {"-export", "Export lightmaps when compile finished (like `-export` mode)"},
+               {"-exposure <F>", "Lightmap exposure to better support overbright spots"},
+               {"-external", "Force external lightmaps even if at size of internal lightmaps"},
+               {"-extravisnudge", "Broken feature to nudge the luxel origin to a better vis cluster"},
+               {"-extrawide", "Deprecated alias for `-super 2 -filter`"},
+               {"-extra", "Deprecated alias for `-super 2`"},
+               {"-fastbounce", "Use `-fast` style lighting for radiosity"},
+               {"-faster", "Use a faster falloff curve for lighting; also implies `-fast`"},
+               {"-fastgrid", "Use `-fast` style lighting for the light grid"},
+               {"-fast", "Ignore tiny light contributions"},
+               {"-filter", "Lightmap filtering"},
+               {"-floodlight", "Enable floodlight (zero-effort somewhat decent lighting)"},
+               {"-gamma <F>", "Lightmap gamma"},
+               {"-gridambientscale <F>", "Scaling factor for the light grid ambient components only"},
+               {"-gridscale <F>", "Scaling factor for the light grid only"},
+               {"-keeplights", "Keep light entities in the BSP file after compile"},
+               {"-lightmapdir <directory>", "Directory to store external lightmaps (default: same as map name without extension)"},
+               {"-lightmapsize <N>", "Size of lightmaps to generate (must be a power of two)"},
+               {"-lomem", "Low memory but slower lighting mode"},
+               {"-lowquality", "Low quality floodlight (appears to currently break floodlight)"},
+               {"-minsamplesize <N>", "Sets minimum lightmap resolution in luxels/qu"},
+               {"-nocollapse", "Do not collapse identical lightmaps"},
+               {"-nodeluxe, -nodeluxemap", "Disable deluxemapping"},
+               {"-nogrid", "Disable grid light calculation (makes all entities fullbright)"},
+               {"-nolightmapsearch", "Do not optimize lightmap packing for GPU memory usage (as doing so costs fps)"},
+               {"-normalmap", "Color the lightmaps according to the direction of the surface normal (TODO is this identical to `-debugnormals`?)"},
+               {"-nostyle, -nostyles", "Disable support for light styles"},
+               {"-nosurf", "Disable tracing against surfaces (only uses BSP nodes then)"},
+               {"-notrace", "Disable shadow occlusion"},
+               {"-novertex", "Disable vertex lighting"},
+               {"-patchshadows", "Cast shadows from patches"},
+               {"-pointscale <F, `-point` F>", "Scaling factor for point lights (light entities)"},
+               {"-q3", "Use nonlinear falloff curve by default (like Q3A)"},
+               {"-samplescale <F>", "Scales all lightmap resolutions"},
+               {"-samplesize <N>", "Sets default lightmap resolution in luxels/qu"},
+               {"-samples <N>", "Adaptive supersampling quality"},
+               {"-scale <F>", "Scaling factor for all light types"},
+               {"-shadeangle <A>", "Angle for phong shading"},
+               {"-shade", "Enable phong shading at default shade angle"},
+               {"-skyscale <F, `-sky` F>", "Scaling factor for sky and sun light"},
+               {"-smooth", "Deprecated alias for `-samples 2`"},
+               {"-style, -styles", "Enable support for light styles"},
+               {"-sunonly", "Only compute sun light"},
+               {"-super <N, `-supersample` N>", "Ordered grid supersampling quality"},
+               {"-thresh <F>", "Triangle subdivision threshold"},
+               {"-trianglecheck", "Broken check that should ensure luxels apply to the right triangle"},
+               {"-trisoup", "Convert brush faces to triangle soup"},
+               {"-wolf", "Use linear falloff curve by default (like W:ET)"},
+       };
+
+       HelpOptions("Light Stage", 0, 80, light, sizeof(light)/sizeof(struct HelpOption));
+}
+
+void HelpAnalize()
+{
+       struct HelpOption analize[] = {
+               {"-analyze <filename.bsp>", "Switch that enters this mode"},
+               {"-lumpswap", "Swap byte order in the lumps"},
+       };
+
+       HelpOptions("Analyzing BSP-like file structure", 0, 80, analize, sizeof(analize)/sizeof(struct HelpOption));
+}
+void HelpScale()
+{
+       struct HelpOption scale[] = {
+               {"-scale <S filename.bsp>", "Scale uniformly"},
+               {"-scale <SX SY SZ filename.bsp>", "Scale non-uniformly"},
+               {"-scale -tex <S filename.bsp>", "Scale uniformly without texture lock"},
+               {"-scale -tex <SX SY SZ filename.bsp>", "Scale non-uniformly without texture lock"},
+       };
+       HelpOptions("Scaling", 0, 80, scale, sizeof(scale)/sizeof(struct HelpOption));
+}
+void HelpConvert()
+{
+       struct HelpOption convert[] = {
+               {"-convert <filename.bsp>", "Switch that enters this mode"},
+               {"-de <number>", "Distance epsilon for the conversion"},
+               {"-format <converter>", "Select the converter (available: map, ase, or game names)"},
+               {"-ne <F>", "Normal epsilon for the conversion"},
+               {"-shadersasbitmap", "(only for ase) use the shader names as \\*BITMAP key so they work as prefabs"},
+       };
+
+       HelpOptions("Converting & Decompiling", 0, 80, convert, sizeof(convert)/sizeof(struct HelpOption));
+}
+
+void HelpExport()
+{
+       struct HelpOption exportl[] = {
+               {"-export <filename.bsp>", "Copies lightmaps from the BSP to `filename/lightmap_0000.tga` ff"}
+       };
+
+       HelpOptions("Exporting lightmaps", 0, 80, exportl, sizeof(exportl)/sizeof(struct HelpOption));
+}
+
+void HelpFixaas()
+{
+       struct HelpOption fixaas[] = {
+               {"-fixaas <filename.bsp>", "Switch that enters this mode"},
+       };
+
+       HelpOptions("Fixing AAS checksum", 0, 80, fixaas, sizeof(fixaas)/sizeof(struct HelpOption));
+}
+
+void HelpInfo()
+{
+       struct HelpOption info[] = {
+               {"-info <filename.bsp>", "Switch that enters this mode"},
+       };
+
+       HelpOptions("Get info about BSP file", 0, 80, info, sizeof(info)/sizeof(struct HelpOption));
+}
+
+void HelpImport()
+{
+       struct HelpOption import[] = {
+               {"-import <filename.bsp>", "Copies lightmaps from `filename/lightmap_0000.tga` ff into the BSP"},
+       };
+
+       HelpOptions("Importing lightmaps", 0, 80, import, sizeof(import)/sizeof(struct HelpOption));
+}
+
+void HelpMinimap()
+{
+       struct HelpOption minimap[] = {
+               {"-minimap <filename.bsp>", "Creates a minimap of the BSP, by default writes to `../gfx/filename_mini.tga`"},
+               {"-black", "Write the minimap as a black-on-transparency RGBA32 image"},
+               {"-boost <F>", "Sets the contrast boost value (higher values make a brighter image); contrast boost is somewhat similar to gamma, but continuous even at zero"},
+               {"-border <F>", "Sets the amount of border pixels relative to the total image size"},
+               {"-gray", "Write the minimap as a white-on-black GRAY8 image"},
+               {"-keepaspect", "Ensure the aspect ratio is kept (the minimap is then letterboxed to keep aspect)"},
+               {"-minmax <xmin ymin zmin xmax ymax zmax>", "Forces specific map dimensions (note: the minimap actually uses these dimensions, scaled to the target size while keeping aspect with centering, and 1/64 of border appended to all sides)"},
+               {"-nokeepaspect", "Do not ensure the aspect ratio is kept (makes it easier to use the image in your code, but looks bad together with sharpening)"},
+               {"-o <filename.tga>", "Sets the output file name"},
+               {"-random <N>", "Sets the randomized supersampling count (cannot be combined with `-samples`)"},
+               {"-samples <N>", "Sets the ordered supersampling count (cannot be combined with `-random`)"},
+               {"-sharpen <F>", "Sets the sharpening coefficient"},
+               {"-size <N>", "Sets the width and height of the output image"},
+               {"-white", "Write the minimap as a white-on-transparency RGBA32 image"},
+       };
+
+       HelpOptions("MiniMap", 0, 80, minimap, sizeof(minimap)/sizeof(struct HelpOption));
+}
+
+void HelpCommon()
+{
+       struct HelpOption common[] = {
+               {"-connect <address>", "Talk to a NetRadiant instance using a specific XML based protocol"},
+               {"-force", "Allow reading some broken/unsupported BSP files e.g. when decompiling, may also crash"},
+               {"-fs_basepath <path>", "Sets the given path as main directory of the game (can be used more than once to look in multiple paths)"},
+               {"-fs_game <gamename>", "Sets a different game directory name (default for Q3A: baseq3)"},
+               {"-fs_homebase <dir>", "Specifies where the user home directory name is on Linux (default for Q3A: .q3a)"},
+               {"-game <gamename>", "Load settings for the given game (default: quake3)"},
+               {"-subdivisions <F>", "multiplier for patch subdivisions quality"},
+               {"-threads <N>", "number of threads to use"},
+               {"-v", "Verbose mode"}
+       };
+
+       HelpOptions("Common Options", 0, 80, common, sizeof(common)/sizeof(struct HelpOption));
+
+}
+
+void Help(const char* arg)
+{
+       printf("Usage: q3map2 [stage] [common options...] [stage options...] [stage source file]\n");
+       printf("       q3map2 -help [stage]\n\n");
+
+       HelpCommon();
+
+       struct HelpOption stages[] = {
+               {"-bsp", "BSP Stage"},
+               {"-vis", "VIS Stage"},
+               {"-light", "Light Stage"},
+               {"-analize", "Analyzing BSP-like file structure"},
+               {"-scale", "Scaling"},
+               {"-convert", "Converting & Decompiling"},
+               {"-export", "Exporting lightmaps"},
+               {"-fixaas", "Fixing AAS checksum"},
+               {"-info", "Get info about BSP file"},
+               {"-import", "Importing lightmaps"},
+               {"-minimap", "MiniMap"},
+       };
+       void(*help_funcs[])() = {
+               HelpBsp,
+               HelpVis,
+               HelpLight,
+               HelpAnalize,
+               HelpScale,
+               HelpConvert,
+               HelpExport,
+               HelpFixaas,
+               HelpInfo,
+               HelpImport,
+               HelpMinimap
+       };
+
+       if ( arg && strlen(arg) > 0 )
+       {
+               if ( arg[0] == '-' )
+                       arg++;
+
+               unsigned i;
+               for ( i = 0; i < sizeof(stages)/sizeof(struct HelpOption); i++ )
+                       if ( strcmp(arg, stages[i].name+1) == 0 )
+                       {
+                               help_funcs[i]();
+                               return;
+                       }
+       }
+
+       HelpOptions("Stages", 0, 80, stages, sizeof(stages)/sizeof(struct HelpOption));
+}
 
 /*
    main()
@@ -1731,6 +2089,13 @@ int main( int argc, char **argv ){
        /* read general options first */
        for ( i = 1; i < argc; i++ )
        {
+               /* -help */
+               if ( !strcmp( argv[ i ], "-h" ) || !strcmp( argv[ i ], "--help" )
+                       || !strcmp( argv[ i ], "-help" ) ) {
+                       Help(argv[i+1]);
+                       return 0;
+               }
+
                /* -connect */
                if ( !strcmp( argv[ i ], "-connect" ) ) {
                        argv[ i ] = NULL;