--- /dev/null
+[submodule "libs/crunch"]
+ path = libs/crunch
+ url = https://github.com/DaemonEngine/crunch.git
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)
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
```
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:
* `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.
add_subdirectory(cmdlib)
add_subdirectory(container)
+if (BUILD_CRUNCH)
+ add_subdirectory(crnrgba)
+endif ()
add_subdirectory(ddslib)
add_subdirectory(debugging)
add_subdirectory(etclib)
--- /dev/null
+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)
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+Subproject commit 85bab3d798a54abe32a22d5275e625ec06df6917
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)
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)
--- /dev/null
+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)
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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
+
--- /dev/null
+/*
+ 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();
+}
--- /dev/null
+; imagepng.def : Declares the module parameters for the DLL.
+
+LIBRARY "IMAGECRN"
+
+EXPORTS
+ ; Explicit exports can go here
+ Radiant_RegisterModules @1
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
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}
${LIBXML2_LIBRARIES}
${Minizip_LIBRARIES}
${ZLIB_LIBRARIES}
+ ${OPTIONAL_IMAGE_LIBRARIES}
ddslib
etclib
filematch
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
/*
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
}
}
}
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;
#include "mathlib.h"
#include "md5lib.h"
#include "ddslib.h"
+#include "crn_rgba.h"
#include "picomodel.h"