]> git.xonotic.org Git - xonotic/netradiant.git/commitdiff
Merge branch 'crunch-submodule' into 'master'
authorMario <zacjardine@y7mail.com>
Sun, 28 Oct 2018 21:33:15 +0000 (21:33 +0000)
committerMario <zacjardine@y7mail.com>
Sun, 28 Oct 2018 21:33:15 +0000 (21:33 +0000)
Add crunch support to radiant and q3map2, the submodule way

See merge request xonotic/netradiant!104

18 files changed:
.gitmodules [new file with mode: 0644]
CMakeLists.txt
README.md
libs/CMakeLists.txt
libs/crnrgba/CMakeLists.txt [new file with mode: 0644]
libs/crnrgba/crn_rgba.cpp [new file with mode: 0644]
libs/crnrgba/crn_rgba.h [new file with mode: 0644]
libs/crunch [new submodule]
plugins/CMakeLists.txt
plugins/image/CMakeLists.txt
plugins/imagecrn/CMakeLists.txt [new file with mode: 0644]
plugins/imagecrn/crn.cpp [new file with mode: 0644]
plugins/imagecrn/crn.h [new file with mode: 0644]
plugins/imagecrn/imagecrn.cpp [new file with mode: 0644]
plugins/imagecrn/imagecrn.def [new file with mode: 0644]
tools/quake3/CMakeLists.txt
tools/quake3/q3map2/image.c
tools/quake3/q3map2/q3map2.h

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..d20f6e3
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "libs/crunch"]
+       path = libs/crunch
+       url = https://github.com/DaemonEngine/crunch.git
index aa1028d64d869b1efb61341377dbf72a37e96cd9..fb4a34c1c54f655e38fd2006161772129de45327 100644 (file)
@@ -3,6 +3,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
 project(NetRadiant C CXX)
 
 option(BUILD_RADIANT "Build the GUI" ON)
+option(BUILD_CRUNCH "Build Crunch image support" OFF)
 
 if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
     set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/install" CACHE PATH "..." FORCE)
index 59e356fa8f13a94bc1f435ed2353e2b76376a3fe..eb6bf9756e4256557e13621653db93363296293b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ http://git-scm.org
 
 To get a copy of the source using the commandline git client:
 ```
-git clone https://gitlab.com/xonotic/netradiant.git
+git clone --recursive https://gitlab.com/xonotic/netradiant.git
 cd netradiant
 ```
 
@@ -54,6 +54,17 @@ brew install Caskroom/cask/xquartz
 brew link --force gettext
 ```
 
+# Submodules
+
+ * Crunch
+
+If you forgot to add `--recursive` option at `git clone` time, fetch it this way:
+
+
+```
+git submodule update --init --recursive
+```
+
 # Compiling
 
 This project uses the usual CMake workflow:
@@ -86,3 +97,7 @@ targets:
  * `quake3`     Compiles all the Quake3 tools
    - `q3map2`     Quake3 map compiler
    - `q3data`
+
+## Note about Crunch
+
+The crnlib used to decode `.crn` files is the one from [Dæmon](http://github.com/DaemonEngine/Daemon) which is just the one by [Unity](https://github.com/Unity-Technologies/crunch/tree/unity) made cross-platform. Since Unity brokes compatibility with [BinomialLLC's legacy tree](https://github.com/BinomialLLC/crunch) it's required to use either crunch from Dæmon or the one from Unity to compress textures that have to be read by radiant or q3map2.
index 9f5f834c9bb2dcfcad442c03712b8f7077667d3d..633b965f2b1c253fde4858182300040f57c7816f 100644 (file)
@@ -1,5 +1,8 @@
 add_subdirectory(cmdlib)
 add_subdirectory(container)
+if (BUILD_CRUNCH)
+    add_subdirectory(crnrgba)
+endif ()
 add_subdirectory(ddslib)
 add_subdirectory(debugging)
 add_subdirectory(etclib)
diff --git a/libs/crnrgba/CMakeLists.txt b/libs/crnrgba/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6e8463d
--- /dev/null
@@ -0,0 +1,10 @@
+add_library(crnrgba
+        crn_rgba.h
+        crn_rgba.cpp
+        ../crunch/inc/crn_decomp.h
+        ../crunch/inc/crnlib.h
+        )
+
+set_target_properties(crnrgba PROPERTIES LINKER_LANGUAGE CXX)
+target_link_libraries(crnrgba PRIVATE ddslib)
+target_compile_options(crnrgba PRIVATE -fexceptions)
diff --git a/libs/crnrgba/crn_rgba.cpp b/libs/crnrgba/crn_rgba.cpp
new file mode 100644 (file)
index 0000000..8dec126
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+   Copyright (C) 2018, Unvanquished Developers
+   All Rights Reserved.
+
+   This file is part of NetRadiant.
+
+   NetRadiant 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.
+
+   NetRadiant 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 NetRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#include "crn_rgba.h"
+
+#include <string.h>
+
+#include <memory>
+
+
+#include "ddslib.h"
+#include "../crunch/inc/crn_decomp.h"
+
+int LittleLong(int l) {
+#if GDEF_ARCH_ENDIAN_BIG
+    std::reverse(reinterpret_cast<unsigned char *>( &l ), reinterpret_cast<unsigned char *>( &l ) + sizeof(int));
+#endif
+    return l;
+}
+
+// Sets `x` and `y` to the width and height of the input crn image. Returns false if there is an
+// error reading the image.
+extern "C" int GetCRNImageSize(const void *buffer, int length, int *x, int *y) {
+    crnd::crn_texture_info ti;
+    if(!crnd::crnd_get_texture_info(buffer, length, &ti) ||
+      // Ensure we are not trying to load a cubemap (which has 6 faces...)
+      (ti.m_faces != 1) ) {
+        return false;
+    }
+    if (x) *x = ti.m_width;
+    if (y) *y = ti.m_height;
+    return true;
+}
+
+// Converts a .crn file to RGBA. Stores the pixels in outBuf. Use GetCRNImageSize to get the image
+// size to determine how big outBuf should be. The function will return false if the image does not
+// fit inside outBuf.
+extern "C" int ConvertCRNtoRGBA(const void *buffer, int length, int outBufLen, void* outBuf) {
+    crnd::crn_texture_info ti;
+    if(!crnd::crnd_get_texture_info(buffer, length, &ti) ||
+      // Ensure we are not trying to load a cubemap (which has 6 faces...)
+      (ti.m_faces != 1) ) {
+        return false;
+    }
+
+    // Sanity check mipmaps.
+    if (ti.m_levels <= 0) {
+        return false;
+    }
+
+    // The largest layer is always layer 0, so load that one.
+    crnd::crn_level_info li;
+    if (!crnd::crnd_get_level_info( buffer, length, 0, &li)) {
+        return false;
+    }
+
+    // Ensure we can fit the final image in outBuf.
+    if (outBufLen < ti.m_width * ti.m_height) {
+        return false;
+    }
+
+    crnd::crnd_unpack_context ctx = crnd::crnd_unpack_begin(buffer, length);
+    if (!ctx) {
+        return false;
+    }
+
+    // Since the texture is compressed and the crunch library doesn't provide the code to convert the code
+    // to RGBAImage, we'll need to convert it to DDS first and use the DDS decompression routines to get
+    // the raw pixels (theoretically, we could refactor the DDS functions to be generalized, but for now,
+    // this seems much more maintainable...). This code is cribbed from the example code in
+    // the crunch repo: https://github.com/DaemonEngine/crunch/blob/master/example2/example2.cpp
+    // Compute the face's width, height, number of DXT blocks per row/col, etc.
+    // This is not a proper DDS conversion; it's only enough to get the ddslib decompressor to be happy.
+    const crn_uint32 blocks_x = std::max(1U, (ti.m_width + 3) >> 2);
+    const crn_uint32 blocks_y = std::max(1U, (ti.m_height + 3) >> 2);
+    const crn_uint32 row_pitch = blocks_x * crnd::crnd_get_bytes_per_dxt_block(ti.m_format);
+    const crn_uint32 total_face_size = row_pitch * blocks_y;
+    const crn_uint32 ddsSize = sizeof(ddsBuffer_t) + total_face_size;
+    std::unique_ptr<char> ddsBuffer(new char[ddsSize]);
+    memset(ddsBuffer.get(), 0, ddsSize);
+
+
+    ddsBuffer_t* dds = reinterpret_cast<ddsBuffer_t*>(ddsBuffer.get());
+
+    memcpy(&dds->magic, "DDS ", sizeof(dds->magic));
+    dds->size = LittleLong(124);  // Size of the DDS header.
+    dds->height = LittleLong(ti.m_height);
+    dds->width = LittleLong(ti.m_width);
+    dds->mipMapCount = LittleLong(1);
+
+    dds->pixelFormat.size = LittleLong(sizeof(ddsPixelFormat_t));
+
+    crn_format fundamental_fmt = crnd::crnd_get_fundamental_dxt_format(ti.m_format);
+    dds->pixelFormat.fourCC = LittleLong(crnd::crnd_crn_format_to_fourcc(fundamental_fmt));
+    if (fundamental_fmt != ti.m_format) {
+        // It's a funky swizzled DXTn format - write its FOURCC to RGBBitCount.
+        dds->pixelFormat.rgbBitCount = LittleLong(crnd::crnd_crn_format_to_fourcc(ti.m_format));
+    }
+    char* imageArray[1];
+    imageArray[0] = reinterpret_cast<char*>(&dds->data);
+    if (!crnd::crnd_unpack_level(ctx, reinterpret_cast<void**>(&imageArray), total_face_size, row_pitch, 0)) {
+        return false;
+    }
+
+    if (DDSDecompress(dds, reinterpret_cast<unsigned char*>(outBuf)) == -1) {
+        return false;
+    }
+    return true;
+}
diff --git a/libs/crnrgba/crn_rgba.h b/libs/crnrgba/crn_rgba.h
new file mode 100644 (file)
index 0000000..b674957
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+   Copyright (C) 2018, Unvanquished Developers
+   All Rights Reserved.
+
+   This file is part of NetRadiant.
+
+   NetRadiant 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.
+
+   NetRadiant 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 NetRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+// Sets `x` and `y` to the width and height of the input crn image. Returns false if there is an
+// error reading the image.
+int GetCRNImageSize(const void *buffer, int length, int *x, int *y);
+
+// Converts a .crn file to RGBA. Stores the pixels in outBuf. Use GetCRNImageSize to get the image
+// size to determine how big outBuf should be. The function will return false if the image does not
+// fit inside outBuf.
+int ConvertCRNtoRGBA(const void *buffer, int length, int outBufLen, void* outBuf);
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
diff --git a/libs/crunch b/libs/crunch
new file mode 160000 (submodule)
index 0000000..85bab3d
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 85bab3d798a54abe32a22d5275e625ec06df6917
index a41ed78042ae5ec2a771bb0b1f33ee783707bf5f..d6c7f075b474e957de5f9f4060c44aac9d88ddee 100644 (file)
@@ -17,6 +17,11 @@ add_subdirectory(archivewad)
 add_subdirectory(archivezip)
 add_subdirectory(entity)
 add_subdirectory(image)
+
+if(BUILD_CRUNCH)
+       add_subdirectory(imagecrn)
+endif()
+
 add_subdirectory(imagehl)
 add_subdirectory(imagepng)
 add_subdirectory(imageq2)
index e1f40cc51747fbf6aaaa992af1b0470b21388fd0..bea548ee600ee08efa667a8a762c1048376b1e00 100644 (file)
@@ -11,3 +11,4 @@ radiant_plugin(image
 find_package(JPEG REQUIRED)
 target_include_directories(image PRIVATE ${JPEG_INCLUDE_DIR})
 target_link_libraries(image PRIVATE ddslib etclib ${JPEG_LIBRARIES})
+target_compile_options(image PRIVATE -fexceptions)
diff --git a/plugins/imagecrn/CMakeLists.txt b/plugins/imagecrn/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fd45552
--- /dev/null
@@ -0,0 +1,7 @@
+radiant_plugin(imagecrn
+        imagecrn.cpp
+        crn.cpp crn.h
+        )
+
+target_include_directories(imagecrn PRIVATE ${CMAKE_SOURCE_DIR}/libs/crnrgba)
+target_link_libraries(imagecrn PRIVATE crnrgba)
diff --git a/plugins/imagecrn/crn.cpp b/plugins/imagecrn/crn.cpp
new file mode 100644 (file)
index 0000000..85e1e5c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+   Copyright (C) 2018, Unvanquished Developers
+   All Rights Reserved.
+
+   This file is part of NetRadiant.
+
+   NetRadiant 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.
+
+   NetRadiant 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 NetRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#include "crn.h"
+
+#include <stdlib.h>
+
+#include "ifilesystem.h"
+#include "iarchive.h"
+#include "idatastream.h"
+
+#include "crn_rgba.h"
+#include "ddslib.h"
+#include "imagelib.h"
+
+Image *LoadCRNBuff(const byte *buffer, int length)
+{
+    int width, height;
+    if (!GetCRNImageSize(buffer, length, &width, &height)) {
+        globalErrorStream() << "ERROR: Error getting crn imag dimensions.\n";
+        return nullptr;
+    }
+    RGBAImage *image = new RGBAImage(width, height);
+    if (!ConvertCRNtoRGBA(buffer, length, width * height, image->getRGBAPixels())) {
+        globalErrorStream() << "ERROR: Error decoding crn image.\n";
+        image->release();
+        return nullptr;
+    }
+    return image;
+}
+
+Image *LoadCRN(ArchiveFile &file)
+{
+    ScopedArchiveBuffer buffer(file);
+    return LoadCRNBuff(buffer.buffer, buffer.length);
+}
diff --git a/plugins/imagecrn/crn.h b/plugins/imagecrn/crn.h
new file mode 100644 (file)
index 0000000..4e97556
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+   Copyright (C) 2018, Unvanquished Developers
+   All Rights Reserved.
+
+   This file is part of NetRadiant.
+
+   NetRadiant 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.
+
+   NetRadiant 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 NetRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined( INCLUDED_CRN_H )
+#define INCLUDED_CRN_H
+
+class Image;
+
+class ArchiveFile;
+
+Image *LoadCRN(ArchiveFile &file);
+
+#endif
+
diff --git a/plugins/imagecrn/imagecrn.cpp b/plugins/imagecrn/imagecrn.cpp
new file mode 100644 (file)
index 0000000..4516f06
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+   Copyright (C) 2001-2006, William Joseph.
+   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 "ifilesystem.h"
+#include "iimage.h"
+
+#include "crn.h"
+
+
+#include "modulesystem/singletonmodule.h"
+
+class ImageDependencies : public GlobalFileSystemModuleRef {
+};
+
+class ImageCRNAPI {
+    _QERPlugImageTable m_imagecrn;
+public:
+    typedef _QERPlugImageTable Type;
+
+    STRING_CONSTANT(Name, "crn");
+
+    ImageCRNAPI()
+    {
+        m_imagecrn.loadImage = LoadCRN;
+    }
+
+    _QERPlugImageTable *getTable()
+    {
+        return &m_imagecrn;
+    }
+};
+
+typedef SingletonModule<ImageCRNAPI, ImageDependencies> ImageCRNModule;
+
+ImageCRNModule g_ImageCRNModule;
+
+
+extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer &server)
+{
+    initialiseModule(server);
+
+    g_ImageCRNModule.selfRegister();
+}
diff --git a/plugins/imagecrn/imagecrn.def b/plugins/imagecrn/imagecrn.def
new file mode 100644 (file)
index 0000000..df815fa
--- /dev/null
@@ -0,0 +1,7 @@
+; imagepng.def : Declares the module parameters for the DLL.
+
+LIBRARY      "IMAGECRN"
+
+EXPORTS
+    ; Explicit exports can go here
+       Radiant_RegisterModules @1
index 1a852983f78f68a57919d0f1d8a6072ac186170b..2dfc9ef98c5c99abbf4b8526e9423e0e941186cd 100644 (file)
@@ -31,6 +31,8 @@ include_directories(${ZLIB_INCLUDE_DIRS})
 find_package(Minizip REQUIRED)
 include_directories(${Minizip_INCLUDE_DIRS})
 
+include_directories(${CMAKE_SOURCE_DIR}/libs/crnrgba)
+
 set(q3map2_games
         q3map2/game_darkplaces.h
         q3map2/game_dq.h
@@ -119,6 +121,16 @@ radiant_tool(q3map2
         q3map2/writebsp.c
         )
 
+if (BUILD_CRUNCH)
+        set(OPTIONAL_IMAGE_LIBRARIES crnrgba)
+        set(OPTIONAL_IMAGE_DEFINES BUILD_CRUNCH=1)
+endif ()
+
+target_compile_definitions(q3map2
+        PRIVATE
+        ${OPTIONAL_IMAGE_DEFINES}
+        )
+
 target_link_libraries(q3map2
         ${GLIB_LIBRARIES}
         ${JPEG_LIBRARIES}
@@ -126,6 +138,7 @@ target_link_libraries(q3map2
         ${LIBXML2_LIBRARIES}
         ${Minizip_LIBRARIES}
         ${ZLIB_LIBRARIES}
+        ${OPTIONAL_IMAGE_LIBRARIES}
         ddslib
         etclib
         filematch
index 6ae88ca78fbe6cd467f809d63f632c3d47617c4a..1732867b6ca0aac13d825406cf7e66fe2346c44c 100644 (file)
@@ -86,6 +86,28 @@ static void LoadDDSBuffer( byte *buffer, int size, byte **pixels, int *width, in
        DDSDecompress( (ddsBuffer_t*) buffer, *pixels );
 }
 
+#ifdef BUILD_CRUNCH
+/*
+    LoadCRNBuffer
+    loads a crn image into a valid rgba image
+*/
+void LoadCRNBuffer( byte *buffer, int size, byte **pixels, int *width, int *height) {
+       /* dummy check */
+       if ( buffer == NULL || size <= 0 || pixels == NULL || width == NULL || height == NULL ) {
+               return;
+       }
+       if ( !GetCRNImageSize( buffer, size, width, height ) ) {
+               Sys_FPrintf( SYS_WRN, "WARNING: Error getting crn imag dimensions.\n");;
+               return;
+       }
+       int outBufSize = *width * *height * 4;
+       *pixels = safe_malloc( outBufSize );
+       if ( !ConvertCRNtoRGBA( buffer, size, outBufSize, *pixels) ) {
+               Sys_FPrintf( SYS_WRN, "WARNING: Error decoding crn image.\n", 0 );
+               return;
+       }
+}
+#endif // BUILD_CRUNCH
 
 
 /*
@@ -437,6 +459,18 @@ image_t *ImageLoad( const char *filename ){
                                        if ( size > 0 ) {
                                                LoadKTXBufferFirstImage( buffer, size, &image->pixels, &image->width, &image->height );
                                        }
+                                       #ifdef BUILD_CRUNCH
+                                       else
+                                       {
+                                               /* attempt to load crn */
+                                               StripExtension( name );
+                                               strcat( name, ".crn" );
+                                               size = vfsLoadFile( ( const char* ) name, ( void** ) &buffer, 0 );
+                                               if ( size > 0 ) {
+                                                       LoadCRNBuffer( buffer, size, &image->pixels, &image->width, &image->height );
+                                               }
+                                       }
+                                       #endif // BUILD_CRUNCH
                                }
                        }
                }
@@ -473,7 +507,7 @@ image_t *ImageLoad( const char *filename ){
                                if (pixels) {
                                        // On error, LoadJPGBuff might store a pointer to the error message in pixels
                                        Sys_FPrintf( SYS_WRN, "WARNING: LoadJPGBuff %s %s\n", name, (unsigned char*) pixels );
-                               }                               
+                               }
                        } else {
                                if ( width == image->width && height == image->height ) {
                                        int i;
index 46fec5eef2bed378f918ff73fe8b8a609ec34ee2..698e57c1bab73b5c9d280fec73f9b7212b0061ea 100644 (file)
@@ -70,6 +70,7 @@
 #include "mathlib.h"
 #include "md5lib.h"
 #include "ddslib.h"
+#include "crn_rgba.h"
 
 #include "picomodel.h"