feat: modules moved and engine moved to submodule

This commit is contained in:
Jan van der Weide 2025-04-12 18:40:44 +02:00
parent dfb5e645cd
commit c33d2130cc
5136 changed files with 225275 additions and 64485 deletions

View file

@ -1,3 +0,0 @@
Import('env')
env.add_source_files(env.modules_sources, "*.cpp")

View file

@ -1,5 +0,0 @@
def can_build(env, platform):
return True;
def configure(env):
pass;

View file

@ -1,16 +0,0 @@
#ifndef GODOT_EXTRA_MACROS_H
#define GODOT_EXTRA_MACROS_H
#define BIND_GET_SET(m_property)\
ClassDB::bind_method(D_METHOD("set_" #m_property, #m_property), &self_type::set_##m_property);\
ClassDB::bind_method(D_METHOD("get_" #m_property), &self_type::get_##m_property)
#define BIND_HPROPERTY(m_type, m_property, ...)\
BIND_GET_SET(m_property);\
ADD_PROPERTY(PropertyInfo(m_type, #m_property, __VA_ARGS__), "set_" #m_property, "get_" #m_property)
#define BIND_PROPERTY(m_type, m_property)\
BIND_GET_SET(m_property);\
ADD_PROPERTY(PropertyInfo(m_type, #m_property), "set_" #m_property, "get_" #m_property)
#endif // !GODOT_EXTRA_MACROS_H

View file

@ -1,15 +0,0 @@
#include "register_types.h"
#include "core/object/class_db.h"
void initialize_PROJECT_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
void uninitialize_PROJECT_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}

View file

@ -1,9 +0,0 @@
#ifndef PROJECT_REGISTER_TYPES_H
#define PROJECT_REGISTER_TYPES_H
#include "modules/register_module_types.h"
void initialize_PROJECT_module(ModuleInitializationLevel p_level);
void uninitialize_PROJECT_module(ModuleInitializationLevel p_level);
#endif // !PROJECT_REGISTER_TYPES_H

View file

@ -17,8 +17,9 @@ Export("env_modules")
# Header with MODULE_*_ENABLED defines.
def modules_enabled_builder(target, source, env):
with methods.generated_wrapper(target) as file:
for module in source[0].read():
modules = sorted(source[0].read())
with methods.generated_wrapper(str(target[0])) as file:
for module in modules:
file.write(f"#define MODULE_{module.upper()}_ENABLED\n")
@ -29,14 +30,26 @@ modules_enabled = env.CommandNoCache(
def register_module_types_builder(target, source, env):
modules = source[0].read()
mod_inc = "\n".join([f'#include "{p}/register_types.h"' for p in modules.values()])
mod_inc = "\n".join([f'#include "{value}/register_types.h"' for value in modules.values()])
mod_init = "\n".join(
[f"#ifdef MODULE_{n.upper()}_ENABLED\n\tinitialize_{n}_module(p_level);\n#endif" for n in modules.keys()]
[
f"""\
#ifdef MODULE_{key.upper()}_ENABLED
initialize_{key}_module(p_level);
#endif"""
for key in modules.keys()
]
)
mod_uninit = "\n".join(
[f"#ifdef MODULE_{n.upper()}_ENABLED\n\tuninitialize_{n}_module(p_level);\n#endif" for n in modules.keys()]
[
f"""\
#ifdef MODULE_{key.upper()}_ENABLED
uninitialize_{key}_module(p_level);
#endif"""
for key in modules.keys()
]
)
with methods.generated_wrapper(target) as file:
with methods.generated_wrapper(str(target[0])) as file:
file.write(
f"""\
#include "register_module_types.h"
@ -88,9 +101,10 @@ for name, path in env.module_list.items():
if env["tests"]:
def modules_tests_builder(target, source, env):
with methods.generated_wrapper(target) as file:
for header in source:
file.write('#include "{}"\n'.format(os.path.normpath(header.path).replace("\\", "/")))
headers = sorted([os.path.relpath(src.path, methods.base_folder).replace("\\", "/") for src in source])
with methods.generated_wrapper(str(target[0])) as file:
for header in headers:
file.write(f'#include "{header}"\n')
env.CommandNoCache("modules_tests.gen.h", test_headers, env.Run(modules_tests_builder))

View file

@ -37,7 +37,7 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_astcenc.Prepend(CPPPATH=[thirdparty_dir])
env_astcenc.Prepend(CPPEXTPATH=[thirdparty_dir])
env_thirdparty = env_astcenc.Clone()
env_thirdparty.disable_warnings()

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef IMAGE_COMPRESS_ASTCENC_H
#define IMAGE_COMPRESS_ASTCENC_H
#pragma once
#include "core/io/image.h"
@ -38,5 +37,3 @@ void _compress_astc(Image *r_img, Image::ASTCFormat p_format);
#endif
void _decompress_astc(Image *r_img);
#endif // IMAGE_COMPRESS_ASTCENC_H

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ASTCENC_REGISTER_TYPES_H
#define ASTCENC_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_astcenc_module(ModuleInitializationLevel p_level);
void uninitialize_astcenc_module(ModuleInitializationLevel p_level);
#endif // ASTCENC_REGISTER_TYPES_H

View file

@ -42,18 +42,14 @@ if basisu_encoder:
transcoder_sources = [thirdparty_dir + "transcoder/basisu_transcoder.cpp"]
# Treat Basis headers as system headers to avoid raising warnings. Not supported on MSVC.
if not env.msvc:
env_basisu.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
else:
env_basisu.Prepend(CPPPATH=[thirdparty_dir])
env_basisu.Prepend(CPPEXTPATH=[thirdparty_dir])
if basisu_encoder:
env_basisu.Prepend(CPPPATH=["#thirdparty/jpeg-compressor"])
env_basisu.Prepend(CPPPATH=["#thirdparty/tinyexr"])
env_basisu.Prepend(CPPEXTPATH=["#thirdparty/jpeg-compressor"])
env_basisu.Prepend(CPPEXTPATH=["#thirdparty/tinyexr"])
if env["builtin_zstd"]:
env_basisu.Prepend(CPPPATH=["#thirdparty/zstd"])
env_basisu.Prepend(CPPEXTPATH=["#thirdparty/zstd"])
env_thirdparty = env_basisu.Clone()
env_thirdparty.disable_warnings()

View file

@ -275,6 +275,7 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
bool rgtc_supported = RS::get_singleton()->has_os_feature("rgtc");
bool s3tc_supported = RS::get_singleton()->has_os_feature("s3tc");
bool etc2_supported = RS::get_singleton()->has_os_feature("etc2");
bool astc_hdr_supported = RS::get_singleton()->has_os_feature("astc_hdr");
bool needs_ra_rg_swap = false;
bool needs_rg_trim = false;
@ -379,7 +380,7 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
if (bptc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC6H;
image_format = Image::FORMAT_BPTC_RGBFU;
} else if (astc_supported) {
} else if (astc_hdr_supported) {
basisu_format = basist::transcoder_texture_format::cTFASTC_HDR_4x4_RGBA;
image_format = Image::FORMAT_ASTC_4x4_HDR;
} else {

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef IMAGE_COMPRESS_BASISU_H
#define IMAGE_COMPRESS_BASISU_H
#pragma once
#include "core/io/image.h"
@ -58,5 +57,3 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size);
Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer);
#endif // IMAGE_COMPRESS_BASISU_H

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef BASIS_UNIVERSAL_REGISTER_TYPES_H
#define BASIS_UNIVERSAL_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_basis_universal_module(ModuleInitializationLevel p_level);
void uninitialize_basis_universal_module(ModuleInitializationLevel p_level);
#endif // BASIS_UNIVERSAL_REGISTER_TYPES_H

View file

@ -48,14 +48,14 @@ static void decompress_image(BCdecFormat format, const void *src, void *dst, con
const uint8_t *src_blocks = reinterpret_cast<const uint8_t *>(src);
uint8_t *dec_blocks = reinterpret_cast<uint8_t *>(dst);
#define DECOMPRESS_LOOP(func, block_size, color_bytesize, color_components) \
for (uint64_t y = 0; y < height; y += 4) { \
for (uint64_t x = 0; x < width; x += 4) { \
func(&src_blocks[src_pos], &dec_blocks[dst_pos], width *color_components); \
src_pos += block_size; \
dst_pos += 4 * color_bytesize; \
} \
dst_pos += 3 * width * color_bytesize; \
#define DECOMPRESS_LOOP(func, block_size, color_bytesize, color_components) \
for (uint64_t y = 0; y < height; y += 4) { \
for (uint64_t x = 0; x < width; x += 4) { \
func(&src_blocks[src_pos], &dec_blocks[dst_pos], width * color_components); \
src_pos += block_size; \
dst_pos += 4 * color_bytesize; \
} \
dst_pos += 3 * width * color_bytesize; \
}
#define DECOMPRESS_LOOP_SAFE(func, block_size, color_bytesize, color_components, output) \

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef IMAGE_DECOMPRESS_BCDEC_H
#define IMAGE_DECOMPRESS_BCDEC_H
#pragma once
#include "core/io/image.h"
@ -45,5 +44,3 @@ enum BCdecFormat {
};
void image_decompress_bcdec(Image *p_image);
#endif // IMAGE_DECOMPRESS_BCDEC_H

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef BCDEC_REGISTER_TYPES_H
#define BCDEC_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_bcdec_module(ModuleInitializationLevel p_level);
void uninitialize_bcdec_module(ModuleInitializationLevel p_level);
#endif // BCDEC_REGISTER_TYPES_H

View file

@ -89,12 +89,36 @@ float CalcMSLE(float3 a, float3 b) {
return result;
}
// Adapt the log function to make sense when a < 0
float3 customLog2(float3 a) {
return float3(
a.x >= 0 ? log2(a.x + 1.0f) : -log2(-a.x + 1.0f),
a.y >= 0 ? log2(a.y + 1.0f) : -log2(-a.y + 1.0f),
a.z >= 0 ? log2(a.z + 1.0f) : -log2(-a.z + 1.0f));
}
// Inverse of customLog2()
float3 customExp2(float3 a) {
return float3(
a.x >= 0 ? exp2(a.x) - 1.0f : -(exp2(-a.x) - 1.0f),
a.y >= 0 ? exp2(a.y) - 1.0f : -(exp2(-a.y) - 1.0f),
a.z >= 0 ? exp2(a.z) - 1.0f : -(exp2(-a.z) - 1.0f));
}
#else
float CalcMSLE(float3 a, float3 b) {
float3 err = log2((b + 1.0f) / (a + 1.0f));
err = err * err;
return err.x + err.y + err.z;
}
float3 customLog2(float3 a) {
return log2(a + 1.0f);
}
float3 customExp2(float3 a) {
return exp2(a) - 1.0f;
}
#endif
uint PatternFixupID(uint i) {
@ -272,15 +296,15 @@ void EncodeP1(inout uint4 block, inout float blockMSLE, float3 texels[16]) {
refinedBlockMax = max(refinedBlockMax, texels[i] == blockMax ? refinedBlockMax : texels[i]);
}
float3 logBlockMax = log2(blockMax + 1.0f);
float3 logBlockMin = log2(blockMin + 1.0f);
float3 logRefinedBlockMax = log2(refinedBlockMax + 1.0f);
float3 logRefinedBlockMin = log2(refinedBlockMin + 1.0f);
float3 logBlockMax = customLog2(blockMax);
float3 logBlockMin = customLog2(blockMin);
float3 logRefinedBlockMax = customLog2(refinedBlockMax);
float3 logRefinedBlockMin = customLog2(refinedBlockMin);
float3 logBlockMaxExt = (logBlockMax - logBlockMin) * (1.0f / 32.0f);
logBlockMin += min(logRefinedBlockMin - logBlockMin, logBlockMaxExt);
logBlockMax -= min(logBlockMax - logRefinedBlockMax, logBlockMaxExt);
blockMin = exp2(logBlockMin) - 1.0f;
blockMax = exp2(logBlockMax) - 1.0f;
blockMin = customExp2(logBlockMin);
blockMax = customExp2(logBlockMax);
float3 blockDir = blockMax - blockMin;
blockDir = blockDir / (blockDir.x + blockDir.y + blockDir.z);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef BETSY_BC1_H
#define BETSY_BC1_H
#pragma once
constexpr const float dxt1_encoding_table[1024] = {
0,
@ -1057,5 +1056,3 @@ constexpr const float dxt1_encoding_table[1024] = {
63,
63,
};
#endif // BETSY_BC1_H

View file

@ -259,6 +259,14 @@ void BetsyCompressor::_thread_exit() {
compress_rd->free(cached_shaders[i].compiled);
}
}
// Free the RD (and RCD if necessary).
memdelete(compress_rd);
compress_rd = nullptr;
if (compress_rcd != nullptr) {
memdelete(compress_rcd);
compress_rcd = nullptr;
}
}
}
@ -268,16 +276,6 @@ void BetsyCompressor::finish() {
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id);
task_id = WorkerThreadPool::INVALID_TASK_ID;
}
if (compress_rd != nullptr) {
// Free the RD (and RCD if necessary).
memdelete(compress_rd);
compress_rd = nullptr;
if (compress_rcd != nullptr) {
memdelete(compress_rcd);
compress_rcd = nullptr;
}
}
}
// Helper functions.
@ -373,6 +371,13 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
return ERR_INVALID_DATA;
}
int img_width = r_img->get_width();
int img_height = r_img->get_height();
if (img_width % 4 != 0 || img_height % 4 != 0) {
img_width = img_width <= 2 ? img_width : (img_width + 3) & ~3;
img_height = img_height <= 2 ? img_height : (img_height + 3) & ~3;
}
Error err = OK;
// Destination format.
@ -443,7 +448,7 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
// Container for the compressed data.
Vector<uint8_t> dst_data;
dst_data.resize(Image::get_image_data_size(r_img->get_width(), r_img->get_height(), dest_format, r_img->has_mipmaps()));
dst_data.resize(Image::get_image_data_size(img_width, img_height, dest_format, r_img->has_mipmaps()));
uint8_t *dst_data_ptr = dst_data.ptrw();
Vector<Vector<uint8_t>> src_images;
@ -452,9 +457,12 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
// Compress each mipmap.
for (int i = 0; i < mip_count; i++) {
int64_t ofs, size;
int width, height;
r_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, width, height);
Image::get_image_mipmap_offset_and_dimensions(img_width, img_height, dest_format, i, width, height);
int64_t src_mip_ofs, src_mip_size;
int src_mip_w, src_mip_h;
r_img->get_mipmap_offset_size_and_dimensions(i, src_mip_ofs, src_mip_size, src_mip_w, src_mip_h);
// Set the source texture width and size.
src_texture_format.height = height;
@ -464,9 +472,38 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
dst_texture_format.height = (height + 3) >> 2;
dst_texture_format.width = (width + 3) >> 2;
// Create a buffer filled with the source mip layer data.
src_image_ptr[0].resize(size);
memcpy(src_image_ptr[0].ptrw(), r_img->ptr() + ofs, size);
// Pad textures to nearest block by smearing.
if (width != src_mip_w || height != src_mip_h) {
const uint8_t *src_mip_read = r_img->ptr() + src_mip_ofs;
// Reserve the buffer for padded image data.
int px_size = Image::get_format_pixel_size(r_img->get_format());
src_image_ptr[0].resize(width * height * px_size);
uint8_t *ptrw = src_image_ptr[0].ptrw();
int x = 0, y = 0;
for (y = 0; y < src_mip_h; y++) {
for (x = 0; x < src_mip_w; x++) {
memcpy(ptrw + (width * y + x) * px_size, src_mip_read + (src_mip_w * y + x) * px_size, px_size);
}
// First, smear in x.
for (; x < width; x++) {
memcpy(ptrw + (width * y + x) * px_size, ptrw + (width * y + x - 1) * px_size, px_size);
}
}
// Then, smear in y.
for (; y < height; y++) {
for (x = 0; x < width; x++) {
memcpy(ptrw + (width * y + x) * px_size, ptrw + (width * y + x - width) * px_size, px_size);
}
}
} else {
// Create a buffer filled with the source mip layer data.
src_image_ptr[0].resize(src_mip_size);
memcpy(src_image_ptr[0].ptrw(), r_img->ptr() + src_mip_ofs, src_mip_size);
}
// Create the textures on the GPU.
RID src_texture = compress_rd->texture_create(src_texture_format, RD::TextureView(), src_images);
@ -646,7 +683,7 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
// Copy data from the GPU to the buffer.
const Vector<uint8_t> texture_data = compress_rd->texture_get_data(dst_texture_rid, 0);
int64_t dst_ofs = Image::get_image_mipmap_offset(r_img->get_width(), r_img->get_height(), dest_format, i);
int64_t dst_ofs = Image::get_image_mipmap_offset(img_width, img_height, dest_format, i);
memcpy(dst_data_ptr + dst_ofs, texture_data.ptr(), texture_data.size());
@ -658,12 +695,12 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
src_images.clear();
// Set the compressed data to the image.
r_img->set_data(r_img->get_width(), r_img->get_height(), r_img->has_mipmaps(), dest_format, dst_data);
r_img->set_data(img_width, img_height, r_img->has_mipmaps(), dest_format, dst_data);
print_verbose(
vformat("Betsy: Encoding a %dx%d image with %d mipmaps as %s took %d ms.",
r_img->get_width(),
r_img->get_height(),
img_width,
img_height,
r_img->get_mipmap_count(),
Image::get_format_name(dest_format),
OS::get_singleton()->get_ticks_msec() - start_time));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef IMAGE_COMPRESS_BETSY_H
#define IMAGE_COMPRESS_BETSY_H
#pragma once
#include "core/io/image.h"
#include "core/object/worker_thread_pool.h"
@ -128,5 +127,3 @@ public:
return err;
}
};
#endif // IMAGE_COMPRESS_BETSY_H

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef BETSY_REGISTER_TYPES_H
#define BETSY_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_betsy_module(ModuleInitializationLevel p_level);
void uninitialize_betsy_module(ModuleInitializationLevel p_level);
#endif // BETSY_REGISTER_TYPES_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef IMAGE_LOADER_BMP_H
#define IMAGE_LOADER_BMP_H
#pragma once
#include "core/io/image_loader.h"
@ -106,5 +105,3 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderBMP();
};
#endif // IMAGE_LOADER_BMP_H

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef BMP_REGISTER_TYPES_H
#define BMP_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_bmp_module(ModuleInitializationLevel p_level);
void uninitialize_bmp_module(ModuleInitializationLevel p_level);
#endif // BMP_REGISTER_TYPES_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef BUFFER_DECODER_H
#define BUFFER_DECODER_H
#pragma once
#include "core/io/image.h"
#include "core/templates/vector.h"
@ -112,5 +111,3 @@ public:
JpegBufferDecoder(CameraFeed *p_camera_feed);
virtual void decode(StreamingBuffer p_buffer) override;
};
#endif // BUFFER_DECODER_H

View file

@ -52,7 +52,7 @@ void CameraFeedLinux::_update_buffer() {
}
void CameraFeedLinux::_query_device(const String &p_device_name) {
file_descriptor = open(p_device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
file_descriptor = open(p_device_name.ascii().get_data(), O_RDWR | O_NONBLOCK, 0);
ERR_FAIL_COND_MSG(file_descriptor == -1, vformat("Cannot open file descriptor for %s. Error: %d.", p_device_name, errno));
struct v4l2_capability capability;
@ -235,7 +235,7 @@ String CameraFeedLinux::get_device_name() const {
bool CameraFeedLinux::activate_feed() {
ERR_FAIL_COND_V_MSG(selected_format == -1, false, "CameraFeed format needs to be set before activating.");
file_descriptor = open(device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
file_descriptor = open(device_name.ascii().get_data(), O_RDWR | O_NONBLOCK, 0);
if (_request_buffers() && _start_capturing()) {
buffer_decoder = _create_buffer_decoder();
_start_thread();
@ -315,7 +315,7 @@ bool CameraFeedLinux::set_format(int p_index, const Dictionary &p_parameters) {
FeedFormat feed_format = formats[p_index];
file_descriptor = open(device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
file_descriptor = open(device_name.ascii().get_data(), O_RDWR | O_NONBLOCK, 0);
struct v4l2_format format;
memset(&format, 0, sizeof(format));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CAMERA_FEED_LINUX_H
#define CAMERA_FEED_LINUX_H
#pragma once
#include "buffer_decoder.h"
@ -41,6 +40,8 @@
struct StreamingBuffer;
class CameraFeedLinux : public CameraFeed {
GDSOFTCLASS(CameraFeedLinux, CameraFeed);
private:
SafeFlag exit_flag;
Thread *thread = nullptr;
@ -65,14 +66,12 @@ private:
public:
String get_device_name() const;
bool activate_feed();
void deactivate_feed();
bool set_format(int p_index, const Dictionary &p_parameters);
Array get_formats() const;
FeedFormat get_format() const;
bool activate_feed() override;
void deactivate_feed() override;
bool set_format(int p_index, const Dictionary &p_parameters) override;
Array get_formats() const override;
FeedFormat get_format() const override;
CameraFeedLinux(const String &p_device_name);
virtual ~CameraFeedLinux();
~CameraFeedLinux() override;
};
#endif // CAMERA_FEED_LINUX_H

View file

@ -113,7 +113,7 @@ void CameraLinux::_add_device(const String &p_device_name) {
int CameraLinux::_open_device(const String &p_device_name) {
struct stat s;
if (stat(p_device_name.ascii(), &s) == -1) {
if (stat(p_device_name.ascii().get_data(), &s) == -1) {
return -1;
}
@ -121,7 +121,7 @@ int CameraLinux::_open_device(const String &p_device_name) {
return -1;
}
return open(p_device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
return open(p_device_name.ascii().get_data(), O_RDWR | O_NONBLOCK, 0);
}
// TODO any cheaper/cleaner way to check if file descriptor is invalid?
@ -162,11 +162,25 @@ bool CameraLinux::_can_query_format(int p_file_descriptor, int p_type) {
return ioctl(p_file_descriptor, VIDIOC_G_FMT, &format) != -1;
}
CameraLinux::CameraLinux() {
camera_thread.start(CameraLinux::camera_thread_func, this);
inline void CameraLinux::set_monitoring_feeds(bool p_monitoring_feeds) {
if (p_monitoring_feeds == monitoring_feeds) {
return;
}
CameraServer::set_monitoring_feeds(p_monitoring_feeds);
if (p_monitoring_feeds) {
camera_thread.start(CameraLinux::camera_thread_func, this);
} else {
exit_flag.set();
if (camera_thread.is_started()) {
camera_thread.wait_to_finish();
}
}
}
CameraLinux::~CameraLinux() {
exit_flag.set();
camera_thread.wait_to_finish();
if (camera_thread.is_started()) {
camera_thread.wait_to_finish();
}
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CAMERA_LINUX_H
#define CAMERA_LINUX_H
#pragma once
#include "core/os/mutex.h"
#include "core/os/thread.h"
@ -53,8 +52,8 @@ private:
bool _can_query_format(int p_file_descriptor, int p_type);
public:
CameraLinux();
CameraLinux() = default;
~CameraLinux();
};
#endif // CAMERA_LINUX_H
void set_monitoring_feeds(bool p_monitoring_feeds) override;
};

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CAMERA_MACOS_H
#define CAMERA_MACOS_H
#pragma once
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
// If you fix something here, make sure you fix it there as well!
@ -37,10 +36,11 @@
#include "servers/camera_server.h"
class CameraMacOS : public CameraServer {
GDSOFTCLASS(CameraMacOS, CameraServer);
public:
CameraMacOS();
CameraMacOS() = default;
void update_feeds();
void set_monitoring_feeds(bool p_monitoring_feeds) override;
};
#endif // CAMERA_MACOS_H

View file

@ -31,7 +31,7 @@
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
// If you fix something here, make sure you fix it there as well!
#include "camera_macos.h"
#import "camera_macos.h"
#include "servers/camera/camera_feed.h"
@ -195,6 +195,8 @@
// CameraFeedMacOS - Subclass for camera feeds in macOS
class CameraFeedMacOS : public CameraFeed {
GDSOFTCLASS(CameraFeedMacOS, CameraFeed);
private:
AVCaptureDevice *device;
MyCaptureSession *capture_session;
@ -206,8 +208,8 @@ public:
void set_device(AVCaptureDevice *p_device);
bool activate_feed();
void deactivate_feed();
bool activate_feed() override;
void deactivate_feed() override;
};
AVCaptureDevice *CameraFeedMacOS::get_device() const {
@ -359,10 +361,20 @@ void CameraMacOS::update_feeds() {
};
}
CameraMacOS::CameraMacOS() {
// Find available cameras we have at this time
update_feeds();
void CameraMacOS::set_monitoring_feeds(bool p_monitoring_feeds) {
if (p_monitoring_feeds == monitoring_feeds) {
return;
}
// should only have one of these....
device_notifications = [[MyDeviceNotifications alloc] initForServer:this];
CameraServer::set_monitoring_feeds(p_monitoring_feeds);
if (p_monitoring_feeds) {
// Find available cameras we have at this time.
update_feeds();
// Get notified on feed changes.
device_notifications = [[MyDeviceNotifications alloc] initForServer:this];
} else {
// Stop monitoring feed changes.
device_notifications = nil;
}
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CAMERA_WIN_H
#define CAMERA_WIN_H
#pragma once
#include "servers/camera/camera_feed.h"
#include "servers/camera_server.h"
@ -42,5 +41,3 @@ public:
CameraWindows();
~CameraWindows() {}
};
#endif // CAMERA_WIN_H

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CAMERA_REGISTER_TYPES_H
#define CAMERA_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_camera_module(ModuleInitializationLevel p_level);
void uninitialize_camera_module(ModuleInitializationLevel p_level);
#endif // CAMERA_REGISTER_TYPES_H

View file

@ -31,7 +31,7 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_csg.Prepend(CPPPATH=[thirdparty_dir + "include"])
env_csg.Prepend(CPPEXTPATH=[thirdparty_dir + "include"])
env_thirdparty = env_csg.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CSG_H
#define CSG_H
#pragma once
#include "core/math/aabb.h"
#include "core/math/transform_3d.h"
@ -65,5 +64,3 @@ struct CSGBrush {
void build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials, const Vector<bool> &p_invert_faces);
void copy_from(const CSGBrush &p_brush, const Transform3D &p_xform);
};
#endif // CSG_H

View file

@ -36,10 +36,13 @@
#include "core/math/geometry_2d.h"
#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/navigation_mesh.h"
#ifndef NAVIGATION_3D_DISABLED
#include "servers/navigation_server_3d.h"
#endif // NAVIGATION_3D_DISABLED
#include <manifold/manifold.h>
#ifndef NAVIGATION_3D_DISABLED
Callable CSGShape3D::_navmesh_source_geometry_parsing_callback;
RID CSGShape3D::_navmesh_source_geometry_parser;
@ -60,9 +63,13 @@ void CSGShape3D::navmesh_parse_source_geometry(const Ref<NavigationMesh> &p_navi
}
NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask();
if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS && csgshape3d->is_using_collision() && (csgshape3d->get_collision_layer() & parsed_collision_mask)) || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
#ifndef PHYSICS_3D_DISABLED
bool nav_collision = (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS && csgshape3d->is_using_collision() && (csgshape3d->get_collision_layer() & p_navigation_mesh->get_collision_mask()));
#else
bool nav_collision = false;
#endif // PHYSICS_3D_DISABLED
if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || nav_collision || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
Array meshes = csgshape3d->get_meshes();
if (!meshes.is_empty()) {
Ref<Mesh> mesh = meshes[1];
@ -72,7 +79,9 @@ void CSGShape3D::navmesh_parse_source_geometry(const Ref<NavigationMesh> &p_navi
}
}
}
#endif // NAVIGATION_3D_DISABLED
#ifndef PHYSICS_3D_DISABLED
void CSGShape3D::set_use_collision(bool p_enable) {
if (use_collision == p_enable) {
return;
@ -186,6 +195,7 @@ void CSGShape3D::set_collision_priority(real_t p_priority) {
real_t CSGShape3D::get_collision_priority() const {
return collision_priority;
}
#endif // PHYSICS_3D_DISABLED
bool CSGShape3D::is_root_shape() const {
return !parent_shape;
@ -207,15 +217,20 @@ float CSGShape3D::get_snap() const {
#endif // DISABLE_DEPRECATED
void CSGShape3D::_make_dirty(bool p_parent_removing) {
#ifndef PHYSICS_3D_DISABLED
if ((p_parent_removing || is_root_shape()) && !dirty) {
callable_mp(this, &CSGShape3D::_update_shape).call_deferred(); // Must be deferred; otherwise, is_root_shape() will use the previous parent.
callable_mp(this, &CSGShape3D::update_shape).call_deferred(); // Must be deferred; otherwise, is_root_shape() will use the previous parent.
}
#endif // PHYSICS_3D_DISABLED
if (!is_root_shape()) {
parent_shape->_make_dirty();
} else if (!dirty) {
callable_mp(this, &CSGShape3D::_update_shape).call_deferred();
}
#ifndef PHYSICS_3D_DISABLED
else if (!dirty) {
callable_mp(this, &CSGShape3D::update_shape).call_deferred();
}
#endif // PHYSICS_3D_DISABLED
dirty = true;
}
@ -550,7 +565,7 @@ void CSGShape3D::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const
surface.tansw[i++] = d < 0 ? -1 : 1;
}
void CSGShape3D::_update_shape() {
void CSGShape3D::update_shape() {
if (!is_root_shape()) {
return;
}
@ -711,9 +726,20 @@ void CSGShape3D::_update_shape() {
set_base(root_mesh->get_rid());
#ifndef PHYSICS_3D_DISABLED
_update_collision_faces();
#endif // PHYSICS_3D_DISABLED
}
Ref<ArrayMesh> CSGShape3D::bake_static_mesh() {
Ref<ArrayMesh> baked_mesh;
if (is_root_shape() && root_mesh.is_valid()) {
baked_mesh = root_mesh;
}
return baked_mesh;
}
#ifndef PHYSICS_3D_DISABLED
Vector<Vector3> CSGShape3D::_get_brush_collision_faces() {
Vector<Vector3> collision_faces;
CSGBrush *n = _get_brush();
@ -746,14 +772,6 @@ void CSGShape3D::_update_collision_faces() {
}
}
Ref<ArrayMesh> CSGShape3D::bake_static_mesh() {
Ref<ArrayMesh> baked_mesh;
if (is_root_shape() && root_mesh.is_valid()) {
baked_mesh = root_mesh;
}
return baked_mesh;
}
Ref<ConcavePolygonShape3D> CSGShape3D::bake_collision_shape() {
Ref<ConcavePolygonShape3D> baked_collision_shape;
if (is_root_shape() && root_collision_shape.is_valid()) {
@ -800,6 +818,7 @@ void CSGShape3D::_on_transform_changed() {
RS::get_singleton()->instance_set_transform(root_collision_debug_instance, debug_shape_old_transform);
}
}
#endif // PHYSICS_3D_DISABLED
AABB CSGShape3D::get_aabb() const {
return node_aabb;
@ -872,6 +891,7 @@ void CSGShape3D::_notification(int p_what) {
}
} break;
#ifndef PHYSICS_3D_DISABLED
case NOTIFICATION_ENTER_TREE: {
if (use_collision && is_root_shape()) {
root_collision_shape.instantiate();
@ -904,6 +924,7 @@ void CSGShape3D::_notification(int p_what) {
}
_on_transform_changed();
} break;
#endif // PHYSICS_3D_DISABLED
}
}
@ -969,17 +990,18 @@ Ref<TriangleMesh> CSGShape3D::generate_triangle_mesh() const {
}
void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape3D::_update_shape);
ClassDB::bind_method(D_METHOD("is_root_shape"), &CSGShape3D::is_root_shape);
ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape3D::set_operation);
ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape3D::get_operation);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape3D::update_shape);
ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape3D::set_snap);
ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape3D::get_snap);
#endif // DISABLE_DEPRECATED
#ifndef PHYSICS_3D_DISABLED
ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape3D::set_use_collision);
ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape3D::is_using_collision);
@ -1000,13 +1022,15 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_priority", "priority"), &CSGShape3D::set_collision_priority);
ClassDB::bind_method(D_METHOD("get_collision_priority"), &CSGShape3D::get_collision_priority);
ClassDB::bind_method(D_METHOD("bake_collision_shape"), &CSGShape3D::bake_collision_shape);
#endif // PHYSICS_3D_DISABLED
ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape3D::set_calculate_tangents);
ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape3D::is_calculating_tangents);
ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape3D::get_meshes);
ClassDB::bind_method(D_METHOD("bake_static_mesh"), &CSGShape3D::bake_static_mesh);
ClassDB::bind_method(D_METHOD("bake_collision_shape"), &CSGShape3D::bake_collision_shape);
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
#ifndef DISABLE_DEPRECATED
@ -1014,11 +1038,13 @@ void CSGShape3D::_bind_methods() {
#endif // DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
#ifndef PHYSICS_3D_DISABLED
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_priority"), "set_collision_priority", "get_collision_priority");
#endif // PHYSICS_3D_DISABLED
BIND_ENUM_CONSTANT(OPERATION_UNION);
BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
@ -1109,13 +1135,13 @@ CSGBrush *CSGMesh3D::_build_brush() {
Array arrays = mesh->surface_get_arrays(i);
if (arrays.size() == 0) {
if (arrays.is_empty()) {
_make_dirty();
ERR_FAIL_COND_V(arrays.is_empty(), memnew(CSGBrush));
}
Vector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX];
if (avertices.size() == 0) {
if (avertices.is_empty()) {
continue;
}
@ -1231,7 +1257,7 @@ CSGBrush *CSGMesh3D::_build_brush() {
}
}
if (vertices.size() == 0) {
if (vertices.is_empty()) {
return memnew(CSGBrush);
}
@ -1322,14 +1348,14 @@ CSGBrush *CSGSphere3D::_build_brush() {
// We want to follow an order that's convenient for UVs.
// For latitude step we start at the top and move down like in an image.
const double latitude_step = -Math_PI / rings;
const double longitude_step = Math_TAU / radial_segments;
const double latitude_step = -Math::PI / rings;
const double longitude_step = Math::TAU / radial_segments;
int face = 0;
for (int i = 0; i < rings; i++) {
double cos0 = 0;
double sin0 = 1;
if (i > 0) {
double latitude0 = latitude_step * i + Math_TAU / 4;
double latitude0 = latitude_step * i + Math::TAU / 4;
cos0 = Math::cos(latitude0);
sin0 = Math::sin(latitude0);
}
@ -1338,7 +1364,7 @@ CSGBrush *CSGSphere3D::_build_brush() {
double cos1 = 0;
double sin1 = -1;
if (i < rings - 1) {
double latitude1 = latitude_step * (i + 1) + Math_TAU / 4;
double latitude1 = latitude_step * (i + 1) + Math::TAU / 4;
cos1 = Math::cos(latitude1);
sin1 = Math::sin(latitude1);
}
@ -1701,8 +1727,8 @@ CSGBrush *CSGCylinder3D::_build_brush() {
inc_n = 0;
}
float ang = inc * Math_TAU;
float ang_n = inc_n * Math_TAU;
float ang = inc * Math::TAU;
float ang_n = inc_n * Math::TAU;
Vector3 face_base(Math::cos(ang), 0, Math::sin(ang));
Vector3 face_base_n(Math::cos(ang_n), 0, Math::sin(ang_n));
@ -1944,8 +1970,8 @@ CSGBrush *CSGTorus3D::_build_brush() {
inci_n = 0;
}
float angi = inci * Math_TAU;
float angi_n = inci_n * Math_TAU;
float angi = inci * Math::TAU;
float angi_n = inci_n * Math::TAU;
Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi));
Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n));
@ -1957,8 +1983,8 @@ CSGBrush *CSGTorus3D::_build_brush() {
incj_n = 0;
}
float angj = incj * Math_TAU;
float angj_n = incj_n * Math_TAU;
float angj = incj * Math::TAU;
float angj_n = incj_n * Math::TAU;
Vector2 normalj = Vector2(Math::cos(angj), Math::sin(angj)) * radius + Vector2(min_radius + radius, 0);
Vector2 normalj_n = Vector2(Math::cos(angj_n), Math::sin(angj_n)) * radius + Vector2(min_radius + radius, 0);

View file

@ -28,14 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CSG_SHAPE_H
#define CSG_SHAPE_H
#pragma once
#include "csg.h"
#include "scene/3d/path_3d.h"
#include "scene/3d/visual_instance_3d.h"
#ifndef PHYSICS_3D_DISABLED
#include "scene/resources/3d/concave_polygon_shape_3d.h"
#endif // PHYSICS_3D_DISABLED
#include "thirdparty/misc/mikktspace.h"
@ -65,6 +67,7 @@ private:
bool last_visible = false;
float snap = 0.001;
#ifndef PHYSICS_3D_DISABLED
bool use_collision = false;
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
@ -73,6 +76,7 @@ private:
RID root_collision_instance;
RID root_collision_debug_instance;
Transform3D debug_shape_old_transform;
#endif // PHYSICS_3D_DISABLED
bool calculate_tangents = true;
@ -110,13 +114,14 @@ private:
static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
void _update_shape();
#ifndef PHYSICS_3D_DISABLED
void _update_collision_faces();
bool _is_debug_collision_shape_visible();
void _update_debug_collision_shape();
void _clear_debug_collision_shape();
void _on_transform_changed();
Vector<Vector3> _get_brush_collision_faces();
#endif // PHYSICS_3D_DISABLED
protected:
void _notification(int p_what);
@ -133,6 +138,7 @@ protected:
public:
Array get_meshes() const;
void update_shape();
void set_operation(Operation p_operation);
Operation get_operation() const;
@ -172,10 +178,13 @@ public:
bool is_root_shape() const;
Ref<ArrayMesh> bake_static_mesh();
#ifndef PHYSICS_3D_DISABLED
Ref<ConcavePolygonShape3D> bake_collision_shape();
#endif // PHYSICS_3D_DISABLED
virtual Ref<TriangleMesh> generate_triangle_mesh() const override;
#ifndef NAVIGATION_3D_DISABLED
private:
static Callable _navmesh_source_geometry_parsing_callback;
static RID _navmesh_source_geometry_parser;
@ -183,6 +192,7 @@ private:
public:
static void navmesh_parse_init();
static void navmesh_parse_source_geometry(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
#endif // NAVIGATION_3D_DISABLED
CSGShape3D();
~CSGShape3D();
@ -482,5 +492,3 @@ public:
VARIANT_ENUM_CAST(CSGPolygon3D::Mode)
VARIANT_ENUM_CAST(CSGPolygon3D::PathRotation)
VARIANT_ENUM_CAST(CSGPolygon3D::PathIntervalType)
#endif // CSG_SHAPE_H

View file

@ -5,11 +5,10 @@
</brief_description>
<description>
This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot.
[b]Performance:[/b] CSG nodes are only intended for prototyping as they have a significant CPU performance cost.
Consider baking final CSG operation results into static geometry that replaces the CSG nodes.
[b]Performance:[/b] CSG nodes are only intended for prototyping as they have a significant CPU performance cost. Consider baking final CSG operation results into static geometry that replaces the CSG nodes.
Individual CSG root node results can be baked to nodes with static resources with the editor menu that appears when a CSG root node is selected.
Individual CSG root nodes can also be baked to static resources with scripts by calling [method bake_static_mesh] for the visual mesh or [method bake_collision_shape] for the physics collision.
Entire scenes of CSG nodes can be baked to static geometry and exported with the editor gltf scene exporter.
Entire scenes of CSG nodes can be baked to static geometry and exported with the editor glTF scene exporter: [b]Scene &gt; Export As... &gt; glTF 2.0 Scene...[/b]
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
@ -20,12 +19,14 @@
<description>
Returns a baked physics [ConcavePolygonShape3D] of this node's CSG operation result. Returns an empty shape if the node is not a CSG root node or has no valid geometry.
[b]Performance:[/b] If the CSG operation results in a very detailed geometry with many faces physics performance will be very slow. Concave shapes should in general only be used for static level geometry and not with dynamic objects that are moving.
[b]Note:[/b] CSG mesh data updates are deferred, which means they are updated with a delay of one rendered frame. To avoid getting an empty shape or outdated mesh data, make sure to call [code]await get_tree().process_frame[/code] before using [method bake_collision_shape] in [method Node._ready] or after changing properties on the [CSGShape3D].
</description>
</method>
<method name="bake_static_mesh">
<return type="ArrayMesh" />
<description>
Returns a baked static [ArrayMesh] of this node's CSG operation result. Materials from involved CSG nodes are added as extra mesh surfaces. Returns an empty mesh if the node is not a CSG root node or has no valid geometry.
[b]Note:[/b] CSG mesh data updates are deferred, which means they are updated with a delay of one rendered frame. To avoid getting an empty mesh or outdated mesh data, make sure to call [code]await get_tree().process_frame[/code] before using [method bake_static_mesh] in [method Node._ready] or after changing properties on the [CSGShape3D].
</description>
</method>
<method name="get_collision_layer_value" qualifiers="const">
@ -46,6 +47,7 @@
<return type="Array" />
<description>
Returns an [Array] with two elements, the first is the [Transform3D] of this node and the second is the root [Mesh] of this node. Only works when this node is the root shape.
[b]Note:[/b] CSG mesh data updates are deferred, which means they are updated with a delay of one rendered frame. To avoid getting an empty shape or outdated mesh data, make sure to call [code]await get_tree().process_frame[/code] before using [method get_meshes] in [method Node._ready] or after changing properties on the [CSGShape3D].
</description>
</method>
<method name="is_root_shape" qualifiers="const">
@ -73,7 +75,7 @@
</methods>
<members>
<member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents" default="true">
Calculate tangents for the CSG shape which allows the use of normal maps. This is only applied on the root shape, this setting is ignored on any child.
Calculate tangents for the CSG shape which allows the use of normal and height maps. This is only applied on the root shape, this setting is ignored on any child. Setting this to [code]false[/code] can speed up shape generation slightly.
</member>
<member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1">
The physics layers this area is in.

View file

@ -30,8 +30,6 @@
#include "csg_gizmos.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
@ -156,6 +154,8 @@ CSGShapeEditor::CSGShapeEditor() {
options->hide();
options->set_text(TTR("CSG"));
options->set_switch_on_hover(true);
options->set_flat(false);
options->set_theme_type_variation("FlatMenuButton");
Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
options->get_popup()->add_item(TTR("Bake Mesh Instance"), MENU_OPTION_BAKE_MESH_INSTANCE);
@ -187,9 +187,6 @@ CSGShape3DGizmoPlugin::CSGShape3DGizmoPlugin() {
create_handle_material("handles");
}
CSGShape3DGizmoPlugin::~CSGShape3DGizmoPlugin() {
}
String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
@ -227,7 +224,7 @@ Variant CSGShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
return p_id == 0 ? s->get_radius() : s->get_height();
return Vector2(s->get_radius(), s->get_height());
}
if (Object::cast_to<CSGTorus3D>(cs)) {
@ -384,7 +381,7 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector<Vector3> faces = cs->get_brush_faces();
if (faces.size() == 0) {
if (faces.is_empty()) {
return;
}
@ -508,5 +505,3 @@ EditorPluginCSG::EditorPluginCSG() {
csg_shape_editor = memnew(CSGShapeEditor);
EditorNode::get_singleton()->get_gui_base()->add_child(csg_shape_editor);
}
#endif // TOOLS_ENABLED

View file

@ -28,10 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CSG_GIZMOS_H
#define CSG_GIZMOS_H
#ifdef TOOLS_ENABLED
#pragma once
#include "../csg_shape.h"
@ -62,7 +59,6 @@ public:
virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) override;
CSGShape3DGizmoPlugin();
~CSGShape3DGizmoPlugin();
};
class CSGShapeEditor : public Control {
@ -104,7 +100,3 @@ public:
EditorPluginCSG();
};
#endif // TOOLS_ENABLED
#endif // CSG_GIZMOS_H

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="m8 2 6 3v6l-6 3-6-3V5zm0 12V8l6-3M8 8 2 5" mask="url(#a)"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="m8 2 6 3v6l-6 3-6-3V5zm0 12V8l6-3M8 8 2 5" mask="url(#a)"/></svg>

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 451 B

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M4 6a4 4 0 0 1 8 0v4a4 4 0 0 1-8 0zm0 1.25a2.5 1 0 0 0 8 0m-4-5v12" mask="url(#a)"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M4 6a4 4 0 0 1 8 0v4a4 4 0 0 1-8 0zm0 1.25a2.5 1 0 0 0 8 0m-4-5v12" mask="url(#a)"/></svg>

Before

Width:  |  Height:  |  Size: 475 B

After

Width:  |  Height:  |  Size: 476 B

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="#fc7f7f" d="M3 1a2 2 0 0 0-2 2h2zm2 0v2h2V1zm4 0v2h2V1zm4 0v2h2a2 2 0 0 0-2-2zM1 5v2h2V5zm12 0v2h2V5zM1 9v2h2V9zm0 4a2 2 0 0 0 2 2v-2zm4 0v2h2v-2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="#fc7f7f" d="M3 1a2 2 0 0 0-2 2h2zm2 0v2h2V1zm4 0v2h2V1zm4 0v2h2a2 2 0 0 0-2-2zM1 5v2h2V5zm12 0v2h2V5zM1 9v2h2V9zm0 4a2 2 0 0 0 2 2v-2zm4 0v2h2v-2z"/></svg>

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 377 B

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M2 4v8a6 2 0 0 0 12 0V4A6 2 0 0 0 2 4a6 2 0 0 0 12 0" mask="url(#a)"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M2 4v8a6 2 0 0 0 12 0V4A6 2 0 0 0 2 4a6 2 0 0 0 12 0" mask="url(#a)"/></svg>

Before

Width:  |  Height:  |  Size: 461 B

After

Width:  |  Height:  |  Size: 462 B

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fc7f7f" d="M4.73 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.729 14H8v-2H4.729A2 2 0 0 0 4 11.271V5.415l4.914 4.916A2 2 0 0 1 9.998 10a2 2 0 0 1 .33-1.084L5.414 4h5.856a2 2 0 0 0 .73.729V8h2V4.729A2 2 0 1 0 11.27 2z"/><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fc7f7f" d="M4.73 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.729 14H8v-2H4.729A2 2 0 0 0 4 11.271V5.415l4.914 4.916A2 2 0 0 1 9.998 10a2 2 0 0 1 .33-1.084L5.414 4h5.856a2 2 0 0 0 .73.729V8h2V4.729A2 2 0 1 0 11.27 2z"/><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/></svg>

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 440 B

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-linejoin="round" stroke-width="2" d="m8 2 6 3.5v5L8 14l-6-3.5v-5h6zm6 3.5L8 9 2 5.5M8 9v5" mask="url(#a)"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-linejoin="round" stroke-width="2" d="m8 2 6 3.5v5L8 14l-6-3.5v-5h6zm6 3.5L8 9 2 5.5M8 9v5" mask="url(#a)"/></svg>

Before

Width:  |  Height:  |  Size: 485 B

After

Width:  |  Height:  |  Size: 486 B

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M8 2a6 6 0 0 0 0 12A6 6 0 0 0 8 2v12M2.05 7.4a6 2 0 0 0 11.9 0" mask="url(#a)"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M8 2a6 6 0 0 0 0 12A6 6 0 0 0 8 2v12M2.05 7.4a6 2 0 0 0 11.9 0" mask="url(#a)"/></svg>

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 472 B

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><g fill="none" stroke="#fc7f7f" mask="url(#a)"><path stroke-width="2" d="M2.5 10a6 4 0 0 0 11 0 4 4 0 0 0 0-4 6 4 0 0 0-11 0 4 4 0 0 0 0 4z"/><path stroke-linecap="round" stroke-width="1.75" d="M6.2 7.2a2 1 0 1 0 3.6 0"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><g fill="none" stroke="#fc7f7f" mask="url(#a)"><path stroke-width="2" d="M2.5 10a6 4 0 0 0 11 0 4 4 0 0 0 0-4 6 4 0 0 0-11 0 4 4 0 0 0 0 4z"/><path stroke-linecap="round" stroke-width="1.75" d="M6.2 7.2a2 1 0 1 0 3.6 0"/></g></svg>

Before

Width:  |  Height:  |  Size: 561 B

After

Width:  |  Height:  |  Size: 562 B

Before After
Before After

View file

@ -47,7 +47,9 @@ void initialize_csg_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(CSGTorus3D);
GDREGISTER_CLASS(CSGPolygon3D);
GDREGISTER_CLASS(CSGCombiner3D);
#ifndef NAVIGATION_3D_DISABLED
CSGShape3D::navmesh_parse_init();
#endif // NAVIGATION_3D_DISABLED
}
#ifdef TOOLS_ENABLED
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CSG_REGISTER_TYPES_H
#define CSG_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_csg_module(ModuleInitializationLevel p_level);
void uninitialize_csg_module(ModuleInitializationLevel p_level);
#endif // CSG_REGISTER_TYPES_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TEST_CSG_H
#define TEST_CSG_H
#pragma once
#include "../csg.h"
#include "../csg_shape.h"
@ -110,5 +109,3 @@ TEST_CASE("[SceneTree][CSG] CSGPolygon3D") {
}
} // namespace TestCSG
#endif // TEST_CSG_H

View file

@ -26,7 +26,7 @@ thirdparty_sources = [
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_cvtt.Prepend(CPPPATH=[thirdparty_dir])
env_cvtt.Prepend(CPPEXTPATH=[thirdparty_dir])
env_thirdparty = env_cvtt.Clone()
env_thirdparty.disable_warnings()

View file

@ -46,7 +46,7 @@ struct CVTTCompressionJobParams {
};
struct CVTTCompressionRowTask {
const uint8_t *in_mm_bytes = nullptr;
Vector<uint8_t> in_mm;
uint8_t *out_mm_bytes = nullptr;
int y_start = 0;
int width = 0;
@ -61,7 +61,7 @@ struct CVTTCompressionJobQueue {
};
static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const CVTTCompressionRowTask &p_row_task) {
const uint8_t *in_bytes = p_row_task.in_mm_bytes;
const uint8_t *in_bytes = p_row_task.in_mm.ptr();
uint8_t *out_bytes = p_row_task.out_mm_bytes;
int w = p_row_task.width;
int h = p_row_task.height;
@ -151,6 +151,11 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) {
int w = p_image->get_width();
int h = p_image->get_height();
if (w % 4 != 0 || h % 4 != 0) {
w = w <= 2 ? w : (w + 3) & ~3;
h = h <= 2 ? h : (h + 3) & ~3;
}
bool is_ldr = (p_image->get_format() <= Image::FORMAT_RGBA8);
bool is_hdr = (p_image->get_format() >= Image::FORMAT_RF) && (p_image->get_format() <= Image::FORMAT_RGBE9995);
@ -180,8 +185,6 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) {
p_image->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert
}
const uint8_t *rb = p_image->get_data().ptr();
Vector<uint8_t> data;
int64_t target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0;
@ -209,20 +212,59 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) {
Vector<CVTTCompressionRowTask> tasks;
for (int i = 0; i <= mm_count; i++) {
int bw = w % 4 != 0 ? w + (4 - w % 4) : w;
int bh = h % 4 != 0 ? h + (4 - h % 4) : h;
Vector<uint8_t> in_data;
int width, height;
Image::get_image_mipmap_offset_and_dimensions(w, h, target_format, i, width, height);
int64_t src_ofs = p_image->get_mipmap_offset(i);
int bw = width % 4 != 0 ? width + (4 - width % 4) : width;
int bh = height % 4 != 0 ? height + (4 - height % 4) : height;
const uint8_t *in_bytes = &rb[src_ofs];
int64_t src_mip_ofs, src_mip_size;
int src_mip_w, src_mip_h;
p_image->get_mipmap_offset_size_and_dimensions(i, src_mip_ofs, src_mip_size, src_mip_w, src_mip_h);
// Pad textures to nearest block by smearing.
if (width != src_mip_w || height != src_mip_h) {
const uint8_t *src_mip_read = p_image->ptr() + src_mip_ofs;
// Reserve the buffer for padded image data.
int px_size = Image::get_format_pixel_size(p_image->get_format());
in_data.resize(width * height * px_size);
uint8_t *ptrw = in_data.ptrw();
int x = 0, y = 0;
for (y = 0; y < src_mip_h; y++) {
for (x = 0; x < src_mip_w; x++) {
memcpy(ptrw + (width * y + x) * px_size, src_mip_read + (src_mip_w * y + x) * px_size, px_size);
}
// First, smear in x.
for (; x < width; x++) {
memcpy(ptrw + (width * y + x) * px_size, ptrw + (width * y + x - 1) * px_size, px_size);
}
}
// Then, smear in y.
for (; y < height; y++) {
for (x = 0; x < width; x++) {
memcpy(ptrw + (width * y + x) * px_size, ptrw + (width * y + x - width) * px_size, px_size);
}
}
} else {
// Create a buffer filled with the source mip layer data.
in_data.resize(src_mip_size);
memcpy(in_data.ptrw(), p_image->ptr() + src_mip_ofs, src_mip_size);
}
//const uint8_t *in_bytes = &rb[src_ofs];
uint8_t *out_bytes = &wb[dst_ofs];
for (int y_start = 0; y_start < h; y_start += 4) {
for (int y_start = 0; y_start < height; y_start += 4) {
CVTTCompressionRowTask row_task;
row_task.width = w;
row_task.height = h;
row_task.width = width;
row_task.height = height;
row_task.y_start = y_start;
row_task.in_mm_bytes = in_bytes;
row_task.in_mm = in_data;
row_task.out_mm_bytes = out_bytes;
tasks.push_back(row_task);
@ -231,8 +273,6 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) {
}
dst_ofs += (MAX(4, bw) * MAX(4, bh)) >> shift;
w = MAX(w / 2, 1);
h = MAX(h / 2, 1);
}
const CVTTCompressionRowTask *tasks_rb = tasks.ptr();
@ -242,7 +282,7 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) {
WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&_digest_job_queue, &job_queue, WorkerThreadPool::get_singleton()->get_thread_count(), -1, true, SNAME("CVTT Compress"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
p_image->set_data(w, h, p_image->has_mipmaps(), target_format, data);
print_verbose(vformat("CVTT: Encoding took %d ms.", OS::get_singleton()->get_ticks_msec() - start_time));
}

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef IMAGE_COMPRESS_CVTT_H
#define IMAGE_COMPRESS_CVTT_H
#pragma once
#include "core/io/image.h"
void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels);
void image_decompress_cvtt(Image *p_image);
#endif // IMAGE_COMPRESS_CVTT_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CVTT_REGISTER_TYPES_H
#define CVTT_REGISTER_TYPES_H
#pragma once
#ifdef TOOLS_ENABLED
@ -39,5 +38,3 @@ void initialize_cvtt_module(ModuleInitializationLevel p_level);
void uninitialize_cvtt_module(ModuleInitializationLevel p_level);
#endif // TOOLS_ENABLED
#endif // CVTT_REGISTER_TYPES_H

View file

@ -0,0 +1,203 @@
/**************************************************************************/
/* dds_enums.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/io/image.h"
#define PF_FOURCC(m_s) ((uint32_t)(((m_s)[3] << 24U) | ((m_s)[2] << 16U) | ((m_s)[1] << 8U) | ((m_s)[0])))
// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
enum {
DDS_MAGIC = 0x20534444,
DDS_HEADER_SIZE = 124,
DDS_PIXELFORMAT_SIZE = 32,
DDSD_PITCH = 0x00000008,
DDSD_LINEARSIZE = 0x00080000,
DDSD_MIPMAPCOUNT = 0x00020000,
DDSD_CAPS = 0x1,
DDSD_HEIGHT = 0x2,
DDSD_WIDTH = 0x4,
DDSD_PIXELFORMAT = 0x1000,
DDPF_ALPHAPIXELS = 0x00000001,
DDPF_ALPHAONLY = 0x00000002,
DDPF_FOURCC = 0x00000004,
DDPF_RGB = 0x00000040,
DDPF_RG_SNORM = 0x00080000,
DDSC2_CUBEMAP = 0x200,
DDSC2_VOLUME = 0x200000,
DX10D_1D = 2,
DX10D_2D = 3,
DX10D_3D = 4,
};
enum DDSFourCC {
DDFCC_DXT1 = PF_FOURCC("DXT1"),
DDFCC_DXT2 = PF_FOURCC("DXT2"),
DDFCC_DXT3 = PF_FOURCC("DXT3"),
DDFCC_DXT4 = PF_FOURCC("DXT4"),
DDFCC_DXT5 = PF_FOURCC("DXT5"),
DDFCC_ATI1 = PF_FOURCC("ATI1"),
DDFCC_BC4U = PF_FOURCC("BC4U"),
DDFCC_ATI2 = PF_FOURCC("ATI2"),
DDFCC_BC5U = PF_FOURCC("BC5U"),
DDFCC_A2XY = PF_FOURCC("A2XY"),
DDFCC_DX10 = PF_FOURCC("DX10"),
DDFCC_R16F = 111,
DDFCC_RG16F = 112,
DDFCC_RGBA16F = 113,
DDFCC_R32F = 114,
DDFCC_RG32F = 115,
DDFCC_RGBA32F = 116,
};
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
enum DXGIFormat {
DXGI_R32G32B32A32_FLOAT = 2,
DXGI_R32G32B32_FLOAT = 6,
DXGI_R16G16B16A16_FLOAT = 10,
DXGI_R32G32_FLOAT = 16,
DXGI_R10G10B10A2_UNORM = 24,
DXGI_R8G8B8A8_UNORM = 28,
DXGI_R8G8B8A8_UNORM_SRGB = 29,
DXGI_R16G16_FLOAT = 34,
DXGI_R32_FLOAT = 41,
DXGI_R8G8_UNORM = 49,
DXGI_R16_FLOAT = 54,
DXGI_R8_UNORM = 61,
DXGI_A8_UNORM = 65,
DXGI_R9G9B9E5 = 67,
DXGI_BC1_UNORM = 71,
DXGI_BC1_UNORM_SRGB = 72,
DXGI_BC2_UNORM = 74,
DXGI_BC2_UNORM_SRGB = 75,
DXGI_BC3_UNORM = 77,
DXGI_BC3_UNORM_SRGB = 78,
DXGI_BC4_UNORM = 80,
DXGI_BC5_UNORM = 83,
DXGI_B5G6R5_UNORM = 85,
DXGI_B5G5R5A1_UNORM = 86,
DXGI_B8G8R8A8_UNORM = 87,
DXGI_BC6H_UF16 = 95,
DXGI_BC6H_SF16 = 96,
DXGI_BC7_UNORM = 98,
DXGI_BC7_UNORM_SRGB = 99,
DXGI_B4G4R4A4_UNORM = 115,
};
// The legacy bitmasked format names here represent the actual data layout in the files,
// while their official names are flipped (e.g. RGBA8 layout is officially called ABGR8).
enum DDSFormat {
DDS_DXT1,
DDS_DXT3,
DDS_DXT5,
DDS_ATI1,
DDS_ATI2,
DDS_BC6U,
DDS_BC6S,
DDS_BC7,
DDS_R16F,
DDS_RG16F,
DDS_RGBA16F,
DDS_R32F,
DDS_RG32F,
DDS_RGB32F,
DDS_RGBA32F,
DDS_RGB9E5,
DDS_RGB8,
DDS_RGBA8,
DDS_BGR8,
DDS_BGRA8,
DDS_BGR5A1,
DDS_BGR565,
DDS_B2GR3,
DDS_B2GR3A8,
DDS_BGR10A2,
DDS_RGB10A2,
DDS_BGRA4,
DDS_LUMINANCE,
DDS_LUMINANCE_ALPHA,
DDS_LUMINANCE_ALPHA_4,
DDS_MAX
};
enum DDSType {
DDST_2D = 1,
DDST_CUBEMAP,
DDST_3D,
DDST_TYPE_MASK = 0x7F,
DDST_ARRAY = 0x80,
};
struct DDSFormatInfo {
const char *name = nullptr;
bool compressed = false;
uint32_t divisor = 0;
uint32_t block_size = 0;
Image::Format format = Image::Format::FORMAT_BPTC_RGBA;
};
static const DDSFormatInfo dds_format_info[DDS_MAX] = {
{ "DXT1/BC1", true, 4, 8, Image::FORMAT_DXT1 },
{ "DXT2/DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 },
{ "DXT4/DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 },
{ "ATI1/BC4", true, 4, 8, Image::FORMAT_RGTC_R },
{ "ATI2/A2XY/BC5", true, 4, 16, Image::FORMAT_RGTC_RG },
{ "BC6UF", true, 4, 16, Image::FORMAT_BPTC_RGBFU },
{ "BC6SF", true, 4, 16, Image::FORMAT_BPTC_RGBF },
{ "BC7", true, 4, 16, Image::FORMAT_BPTC_RGBA },
{ "R16F", false, 1, 2, Image::FORMAT_RH },
{ "RG16F", false, 1, 4, Image::FORMAT_RGH },
{ "RGBA16F", false, 1, 8, Image::FORMAT_RGBAH },
{ "R32F", false, 1, 4, Image::FORMAT_RF },
{ "RG32F", false, 1, 8, Image::FORMAT_RGF },
{ "RGB32F", false, 1, 12, Image::FORMAT_RGBF },
{ "RGBA32F", false, 1, 16, Image::FORMAT_RGBAF },
{ "RGB9E5", false, 1, 4, Image::FORMAT_RGBE9995 },
{ "RGB8", false, 1, 3, Image::FORMAT_RGB8 },
{ "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGR8", false, 1, 3, Image::FORMAT_RGB8 },
{ "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGR5A1", false, 1, 2, Image::FORMAT_RGBA8 },
{ "BGR565", false, 1, 2, Image::FORMAT_RGB8 },
{ "B2GR3", false, 1, 1, Image::FORMAT_RGB8 },
{ "B2GR3A8", false, 1, 2, Image::FORMAT_RGBA8 },
{ "BGR10A2", false, 1, 4, Image::FORMAT_RGBA8 },
{ "RGB10A2", false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGRA4", false, 1, 2, Image::FORMAT_RGBA8 },
{ "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 },
{ "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 },
{ "GRAYSCALE_ALPHA_4", false, 1, 1, Image::FORMAT_LA8 },
};

View file

@ -0,0 +1,487 @@
/**************************************************************************/
/* image_saver_dds.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "image_saver_dds.h"
#include "dds_enums.h"
#include "core/io/file_access.h"
#include "core/io/stream_peer.h"
Error save_dds(const String &p_path, const Ref<Image> &p_img) {
Vector<uint8_t> buffer = save_dds_buffer(p_img);
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE);
if (file.is_null()) {
return ERR_CANT_CREATE;
}
file->store_buffer(buffer.ptr(), buffer.size());
return OK;
}
enum DDSFormatType {
DDFT_BITMASK,
DDFT_FOURCC,
DDFT_DXGI,
};
DDSFormatType _dds_format_get_type(DDSFormat p_format) {
switch (p_format) {
case DDS_DXT1:
case DDS_DXT3:
case DDS_DXT5:
case DDS_ATI1:
case DDS_ATI2:
case DDS_R16F:
case DDS_RG16F:
case DDS_RGBA16F:
case DDS_R32F:
case DDS_RG32F:
case DDS_RGBA32F:
return DDFT_FOURCC;
case DDS_BC6S:
case DDS_BC6U:
case DDS_BC7:
case DDS_RGB9E5:
case DDS_RGB32F:
return DDFT_DXGI;
default:
return DDFT_BITMASK;
}
}
DDSFormat _image_format_to_dds_format(Image::Format p_image_format) {
switch (p_image_format) {
case Image::FORMAT_RGBAF: {
return DDS_RGBA32F;
}
case Image::FORMAT_RGBF: {
return DDS_RGB32F;
}
case Image::FORMAT_RGBAH: {
return DDS_RGBA16F;
}
case Image::FORMAT_RGF: {
return DDS_RG32F;
}
case Image::FORMAT_RGBA8: {
return DDS_RGBA8;
}
case Image::FORMAT_RGH: {
return DDS_RG16F;
}
case Image::FORMAT_RF: {
return DDS_R32F;
}
case Image::FORMAT_L8:
case Image::FORMAT_R8: {
return DDS_LUMINANCE;
}
case Image::FORMAT_RH: {
return DDS_R16F;
}
case Image::FORMAT_LA8:
case Image::FORMAT_RG8: {
return DDS_LUMINANCE_ALPHA;
}
case Image::FORMAT_RGBA4444: {
return DDS_BGRA4;
}
case Image::FORMAT_RGB565: {
return DDS_BGR565;
}
case Image::FORMAT_RGBE9995: {
return DDS_RGB9E5;
}
case Image::FORMAT_DXT1: {
return DDS_DXT1;
}
case Image::FORMAT_DXT3: {
return DDS_DXT3;
}
case Image::FORMAT_DXT5: {
return DDS_DXT5;
}
case Image::FORMAT_RGTC_R: {
return DDS_ATI1;
}
case Image::FORMAT_RGTC_RG: {
return DDS_ATI2;
}
case Image::FORMAT_RGB8: {
return DDS_RGB8;
}
case Image::FORMAT_BPTC_RGBFU: {
return DDS_BC6U;
}
case Image::FORMAT_BPTC_RGBF: {
return DDS_BC6S;
}
case Image::FORMAT_BPTC_RGBA: {
return DDS_BC7;
}
default: {
return DDS_MAX;
}
}
}
uint32_t _image_format_to_fourcc_format(Image::Format p_format) {
switch (p_format) {
case Image::FORMAT_DXT1:
return DDFCC_DXT1;
case Image::FORMAT_DXT3:
return DDFCC_DXT3;
case Image::FORMAT_DXT5:
return DDFCC_DXT5;
case Image::FORMAT_RGTC_R:
return DDFCC_ATI1;
case Image::FORMAT_RGTC_RG:
return DDFCC_ATI2;
case Image::FORMAT_RF:
return DDFCC_R32F;
case Image::FORMAT_RGF:
return DDFCC_RG32F;
case Image::FORMAT_RGBAF:
return DDFCC_RGBA32F;
case Image::FORMAT_RH:
return DDFCC_R16F;
case Image::FORMAT_RGH:
return DDFCC_RG16F;
case Image::FORMAT_RGBAH:
return DDFCC_RGBA16F;
default:
return 0;
}
}
uint32_t _image_format_to_dxgi_format(Image::Format p_format) {
switch (p_format) {
case Image::FORMAT_DXT1:
return DXGI_BC1_UNORM;
case Image::FORMAT_DXT3:
return DXGI_BC2_UNORM;
case Image::FORMAT_DXT5:
return DXGI_BC3_UNORM;
case Image::FORMAT_RGTC_R:
return DXGI_BC4_UNORM;
case Image::FORMAT_RGTC_RG:
return DXGI_BC5_UNORM;
case Image::FORMAT_BPTC_RGBFU:
return DXGI_BC6H_UF16;
case Image::FORMAT_BPTC_RGBF:
return DXGI_BC6H_SF16;
case Image::FORMAT_BPTC_RGBA:
return DXGI_BC7_UNORM;
case Image::FORMAT_RF:
return DXGI_R32_FLOAT;
case Image::FORMAT_RGF:
return DXGI_R32G32_FLOAT;
case Image::FORMAT_RGBF:
return DXGI_R32G32B32_FLOAT;
case Image::FORMAT_RGBAF:
return DXGI_R32G32B32A32_FLOAT;
case Image::FORMAT_RH:
return DXGI_R16_FLOAT;
case Image::FORMAT_RGH:
return DXGI_R16G16_FLOAT;
case Image::FORMAT_RGBAH:
return DXGI_R16G16B16A16_FLOAT;
case Image::FORMAT_RGBE9995:
return DXGI_R9G9B9E5;
default:
return 0;
}
}
void _get_dds_pixel_bitmask(Image::Format p_format, uint32_t &r_bit_count, uint32_t &r_red_mask, uint32_t &r_green_mask, uint32_t &r_blue_mask, uint32_t &r_alpha_mask) {
switch (p_format) {
case Image::FORMAT_R8:
case Image::FORMAT_L8: {
r_bit_count = 8;
r_red_mask = 0xff;
r_green_mask = 0;
r_blue_mask = 0;
r_alpha_mask = 0;
} break;
case Image::FORMAT_RG8:
case Image::FORMAT_LA8: {
r_bit_count = 16;
r_red_mask = 0xff;
r_green_mask = 0;
r_blue_mask = 0;
r_alpha_mask = 0xff00;
} break;
case Image::FORMAT_RGB8: {
// BGR8
r_bit_count = 24;
r_red_mask = 0xff0000;
r_green_mask = 0xff00;
r_blue_mask = 0xff;
r_alpha_mask = 0;
} break;
case Image::FORMAT_RGBA8: {
r_bit_count = 32;
r_red_mask = 0xff;
r_green_mask = 0xff00;
r_blue_mask = 0xff0000;
r_alpha_mask = 0xff000000;
} break;
case Image::FORMAT_RGBA4444: {
// BGRA4444
r_bit_count = 16;
r_red_mask = 0xf00;
r_green_mask = 0xf0;
r_blue_mask = 0xf;
r_alpha_mask = 0xf000;
} break;
case Image::FORMAT_RGB565: {
// BGR565
r_bit_count = 16;
r_red_mask = 0xf800;
r_green_mask = 0x7e0;
r_blue_mask = 0x1f;
r_alpha_mask = 0;
} break;
default: {
r_bit_count = 0;
r_red_mask = 0;
r_green_mask = 0;
r_blue_mask = 0;
r_alpha_mask = 0;
} break;
}
}
Vector<uint8_t> save_dds_buffer(const Ref<Image> &p_img) {
Ref<StreamPeerBuffer> stream_buffer;
stream_buffer.instantiate();
Ref<Image> image = p_img;
stream_buffer->put_32(DDS_MAGIC);
stream_buffer->put_32(DDS_HEADER_SIZE);
uint32_t flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_PITCH | DDSD_LINEARSIZE;
if (image->has_mipmaps()) {
flags |= DDSD_MIPMAPCOUNT;
}
stream_buffer->put_32(flags);
uint32_t height = image->get_height();
stream_buffer->put_32(height);
uint32_t width = image->get_width();
stream_buffer->put_32(width);
DDSFormat dds_format = _image_format_to_dds_format(image->get_format());
const DDSFormatInfo &info = dds_format_info[dds_format];
uint32_t depth = 1; // Default depth for 2D textures
uint32_t pitch;
if (info.compressed) {
pitch = ((MAX(info.divisor, width) + info.divisor - 1) / info.divisor) * ((MAX(info.divisor, height) + info.divisor - 1) / info.divisor) * info.block_size;
} else {
pitch = width * info.block_size;
}
stream_buffer->put_32(pitch);
stream_buffer->put_32(depth);
uint32_t mipmaps = image->get_mipmap_count() + 1;
stream_buffer->put_32(mipmaps);
uint32_t reserved = 0;
for (int i = 0; i < 11; i++) {
stream_buffer->put_32(reserved);
}
stream_buffer->put_32(DDS_PIXELFORMAT_SIZE);
uint32_t pf_flags = 0;
DDSFormatType format_type = _dds_format_get_type(dds_format);
if (format_type == DDFT_BITMASK) {
pf_flags = DDPF_RGB;
if (image->get_format() == Image::FORMAT_LA8 || image->get_format() == Image::FORMAT_RG8 || image->get_format() == Image::FORMAT_RGBA8 || image->get_format() == Image::FORMAT_RGBA4444) {
pf_flags |= DDPF_ALPHAPIXELS;
}
} else {
pf_flags = DDPF_FOURCC;
}
stream_buffer->put_32(pf_flags);
bool needs_pixeldata_swap = false;
if (format_type == DDFT_BITMASK) {
// Uncompressed bitmasked.
stream_buffer->put_32(0); // FourCC
uint32_t bit_count, r_mask, g_mask, b_mask, a_mask;
_get_dds_pixel_bitmask(image->get_format(), bit_count, r_mask, g_mask, b_mask, a_mask);
stream_buffer->put_32(bit_count);
stream_buffer->put_32(r_mask);
stream_buffer->put_32(g_mask);
stream_buffer->put_32(b_mask);
stream_buffer->put_32(a_mask);
if (image->get_format() == Image::FORMAT_RGBA4444 || image->get_format() == Image::FORMAT_RGB565 || image->get_format() == Image::FORMAT_RGB8) {
needs_pixeldata_swap = true;
}
} else if (format_type == DDFT_FOURCC) {
// FourCC.
uint32_t fourcc = _image_format_to_fourcc_format(image->get_format());
stream_buffer->put_32(fourcc);
stream_buffer->put_32(0); // Bit count
stream_buffer->put_32(0); // R Bitmask
stream_buffer->put_32(0); // G Bitmask
stream_buffer->put_32(0); // B Bitmask
stream_buffer->put_32(0); // A Bitmask
} else {
// DXGI format and DX10 header.
stream_buffer->put_32(DDFCC_DX10);
stream_buffer->put_32(0); // Bit count
stream_buffer->put_32(0); // R Bitmask
stream_buffer->put_32(0); // G Bitmask
stream_buffer->put_32(0); // B Bitmask
stream_buffer->put_32(0); // A Bitmask
}
uint32_t caps1 = info.compressed ? DDSD_LINEARSIZE : DDSD_PITCH;
stream_buffer->put_32(caps1);
stream_buffer->put_32(0); // Caps2
stream_buffer->put_32(0); // Caps3
stream_buffer->put_32(0); // Caps4
stream_buffer->put_32(0); // Reserved 2
if (format_type == DDFT_DXGI) {
// DX10 header.
uint32_t dxgi_format = _image_format_to_dxgi_format(image->get_format());
stream_buffer->put_32(dxgi_format);
stream_buffer->put_32(DX10D_2D);
stream_buffer->put_32(0); // Misc flags 1
stream_buffer->put_32(1); // Array size
stream_buffer->put_32(0); // Misc flags 2
}
for (uint32_t mip_i = 0; mip_i < mipmaps; mip_i++) {
uint32_t mip_width = MAX(1u, width >> mip_i);
uint32_t mip_height = MAX(1u, height >> mip_i);
uint32_t expected_size = 0;
if (info.compressed) {
uint32_t blocks_x = (mip_width + info.divisor - 1) / info.divisor;
uint32_t blocks_y = (mip_height + info.divisor - 1) / info.divisor;
expected_size = blocks_x * blocks_y * info.block_size;
} else {
expected_size = mip_width * mip_height * info.block_size;
}
if (needs_pixeldata_swap) {
// The image's channels need to be swapped.
Ref<Image> mip_image = image->get_image_from_mipmap(mip_i);
Vector<uint8_t> data = mip_image->get_data();
ERR_FAIL_COND_V_MSG(data.size() != expected_size, Vector<uint8_t>(),
"Image data size mismatch for mipmap level " + itos(mip_i) +
". Expected size: " + itos(expected_size) + ", actual size: " + itos(data.size()) + ".");
if (mip_image->get_format() == Image::FORMAT_RGBA4444) {
// RGBA4 to BGRA4
const int64_t data_size = data.size();
uint8_t *wb = data.ptrw();
for (int64_t data_i = 0; data_i < data_size; data_i += 2) {
uint8_t ar = wb[data_i + 0];
uint8_t gb = wb[data_i + 1];
wb[data_i + 1] = ((ar & 0x0F) << 4) | ((gb & 0xF0) >> 4);
wb[data_i + 0] = ((ar & 0xF0) >> 4) | ((gb & 0x0F) << 4);
}
} else if (mip_image->get_format() == Image::FORMAT_RGB565) {
// RGB565 to BGR565
const int64_t data_size = data.size();
uint8_t *wb = data.ptrw();
for (int64_t data_i = 0; data_i < data_size; data_i += 2) {
uint16_t px = wb[data_i] | (wb[data_i + 1] << 8);
uint8_t r = (px >> 11) & 0x1F;
uint8_t g = (px >> 5) & 0x3F;
uint8_t b = px & 0x1F;
uint16_t out_px = (b << 11) | (g << 5) | r;
wb[data_i + 0] = out_px & 0xFF;
wb[data_i + 1] = (out_px >> 8) & 0xFF;
}
} else if (mip_image->get_format() == Image::FORMAT_RGB8) {
// RGB8 to BGR8
const int64_t data_size = data.size();
uint8_t *wb = data.ptrw();
for (int64_t data_i = 0; data_i < data_size; data_i += 3) {
SWAP(wb[data_i], wb[data_i + 2]);
}
}
stream_buffer->put_data(data.ptr(), data.size());
} else {
int64_t ofs, size;
image->get_mipmap_offset_and_size(mip_i, ofs, size);
ERR_FAIL_COND_V_MSG(size != expected_size, Vector<uint8_t>(),
"Image data size mismatch for mipmap level " + itos(mip_i) +
". Expected size: " + itos(expected_size) + ", actual size: " + itos(size) + ".");
stream_buffer->put_data(image->ptr() + ofs, size);
}
}
return stream_buffer->get_data_array();
}

View file

@ -0,0 +1,36 @@
/**************************************************************************/
/* image_saver_dds.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/io/image.h"
Error save_dds(const String &p_path, const Ref<Image> &p_img);
Vector<uint8_t> save_dds_buffer(const Ref<Image> &p_img);

View file

@ -30,8 +30,11 @@
#include "register_types.h"
#include "image_saver_dds.h"
#include "texture_loader_dds.h"
#include "scene/resources/texture.h"
static Ref<ResourceFormatDDS> resource_loader_dds;
void initialize_dds_module(ModuleInitializationLevel p_level) {
@ -39,8 +42,13 @@ void initialize_dds_module(ModuleInitializationLevel p_level) {
return;
}
resource_loader_dds.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_dds);
Image::save_dds_func = save_dds;
Image::save_dds_buffer_func = save_dds_buffer;
if (GD_IS_CLASS_ENABLED(Texture)) {
resource_loader_dds.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_dds);
}
}
void uninitialize_dds_module(ModuleInitializationLevel p_level) {
@ -48,6 +56,11 @@ void uninitialize_dds_module(ModuleInitializationLevel p_level) {
return;
}
ResourceLoader::remove_resource_format_loader(resource_loader_dds);
resource_loader_dds.unref();
if (GD_IS_CLASS_ENABLED(Texture)) {
ResourceLoader::remove_resource_format_loader(resource_loader_dds);
resource_loader_dds.unref();
}
Image::save_dds_func = nullptr;
Image::save_dds_buffer_func = nullptr;
}

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DDS_REGISTER_TYPES_H
#define DDS_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_dds_module(ModuleInitializationLevel p_level);
void uninitialize_dds_module(ModuleInitializationLevel p_level);
#endif // DDS_REGISTER_TYPES_H

View file

@ -0,0 +1,161 @@
/**************************************************************************/
/* test_dds.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../image_saver_dds.h"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/image.h"
#include "tests/core/config/test_project_settings.h"
#include "tests/test_macros.h"
#include "tests/test_utils.h"
namespace TestDDS {
String init(const String &p_test, const String &p_copy_target = String()) {
String old_resource_path = TestProjectSettingsInternalsAccessor::resource_path();
Error err;
// Setup project settings since it's needed for the import process.
String project_folder = TestUtils::get_temp_path(p_test.get_file().get_basename());
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
da->make_dir_recursive(project_folder.path_join(".godot").path_join("imported"));
// Initialize res:// to `project_folder`.
TestProjectSettingsInternalsAccessor::resource_path() = project_folder;
err = ProjectSettings::get_singleton()->setup(project_folder, String(), true);
if (p_copy_target.is_empty()) {
return old_resource_path;
}
// Copy all the necessary test data files to the res:// directory.
da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String test_data = String("tests/data").path_join(p_test);
da = DirAccess::open(test_data);
CHECK_MESSAGE(da.is_valid(), "Unable to open folder.");
da->list_dir_begin();
for (String item = da->get_next(); !item.is_empty(); item = da->get_next()) {
if (!FileAccess::exists(test_data.path_join(item))) {
continue;
}
Ref<FileAccess> output = FileAccess::open(p_copy_target.path_join(item), FileAccess::WRITE, &err);
CHECK_MESSAGE(err == OK, "Unable to open output file.");
output->store_buffer(FileAccess::get_file_as_bytes(test_data.path_join(item)));
output->close();
}
da->list_dir_end();
return old_resource_path;
}
TEST_CASE("[SceneTree][DDSSaver] Save DDS - Save valid image with mipmap" * doctest::skip(true)) {
String old_resource_path = init("save_dds_valid_image_with_mipmap");
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 0)); // Fill with red color
image->generate_mipmaps();
image->compress_from_channels(Image::COMPRESS_S3TC, Image::USED_CHANNELS_RGBA);
Error err = save_dds("res://valid_image_with_mipmap.dds", image);
CHECK(err == OK);
Ref<Image> loaded_image;
loaded_image.instantiate();
Vector<uint8_t> buffer = FileAccess::get_file_as_bytes("res://valid_image_with_mipmap.dds", &err);
CHECK(err == OK);
err = loaded_image->load_dds_from_buffer(buffer);
CHECK(err == OK);
Dictionary metrics = image->compute_image_metrics(loaded_image, false);
CHECK(metrics.size() > 0);
CHECK_MESSAGE(metrics.has("root_mean_squared"), "Metrics dictionary contains 'root_mean_squared'.");
float rms = metrics["root_mean_squared"];
CHECK(rms == 0.0f);
TestProjectSettingsInternalsAccessor::resource_path() = old_resource_path;
}
TEST_CASE("[SceneTree][DDSSaver] Save DDS - Save valid image with BPTC and S3TC compression" * doctest::skip(true)) {
String old_resource_path = init("save_dds_valid_image_bptc_s3tc");
Ref<Image> image_bptc = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image_bptc->fill(Color(0, 0, 1)); // Fill with blue color
image_bptc->compress_from_channels(Image::COMPRESS_BPTC, Image::USED_CHANNELS_RGBA);
Error err_bptc = image_bptc->save_dds("res://valid_image_bptc.dds");
CHECK(err_bptc == OK);
Ref<Image> image_s3tc = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image_s3tc->fill(Color(1, 1, 1)); // Fill with white color
image_s3tc->compress_from_channels(Image::COMPRESS_S3TC, Image::USED_CHANNELS_RGBA);
Error err_s3tc = image_s3tc->save_dds("res://valid_image_s3tc_combined.dds");
CHECK(err_s3tc == OK);
// Validate BPTC image
Ref<Image> loaded_image_bptc;
loaded_image_bptc.instantiate();
Vector<uint8_t> buffer_bptc = FileAccess::get_file_as_bytes("res://valid_image_bptc.dds", &err_bptc);
CHECK(err_bptc == OK);
err_bptc = loaded_image_bptc->load_dds_from_buffer(buffer_bptc);
CHECK(err_bptc == OK);
Dictionary metrics_bptc = image_bptc->compute_image_metrics(loaded_image_bptc, false);
CHECK(metrics_bptc.size() > 0);
CHECK_MESSAGE(metrics_bptc.has("root_mean_squared"), "Metrics dictionary contains 'root_mean_squared' for BPTC.");
float rms_bptc = metrics_bptc["root_mean_squared"];
CHECK(rms_bptc == 0.0f);
// Validate S3TC image
Ref<Image> loaded_image_s3tc;
loaded_image_s3tc.instantiate();
Vector<uint8_t> buffer_s3tc = FileAccess::get_file_as_bytes("res://valid_image_s3tc_combined.dds", &err_s3tc);
CHECK(err_s3tc == OK);
err_s3tc = loaded_image_s3tc->load_dds_from_buffer(buffer_s3tc);
CHECK(err_s3tc == OK);
Dictionary metrics_s3tc = image_s3tc->compute_image_metrics(loaded_image_s3tc, false);
CHECK(metrics_s3tc.size() > 0);
CHECK_MESSAGE(metrics_s3tc.has("root_mean_squared"), "Metrics dictionary contains 'root_mean_squared' for S3TC.");
float rms_s3tc = metrics_s3tc["root_mean_squared"];
CHECK(rms_s3tc == 0.0f);
TestProjectSettingsInternalsAccessor::resource_path() = old_resource_path;
}
TEST_CASE("[SceneTree][DDSSaver] Save DDS - Save valid uncompressed image") {
String old_resource_path = init("save_dds_valid_uncompressed");
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(0, 0, 1)); // Fill with blue color
Error err = image->save_dds("res://valid_image_uncompressed.dds");
CHECK(err == OK);
Vector<uint8_t> buffer = FileAccess::get_file_as_bytes("res://valid_image_uncompressed.dds", &err);
CHECK(err == OK);
Ref<Image> loaded_image;
loaded_image.instantiate();
err = loaded_image->load_dds_from_buffer(buffer);
CHECK(err == OK);
Dictionary metrics = image->compute_image_metrics(loaded_image, false);
CHECK(metrics.size() > 0);
CHECK_MESSAGE(metrics.has("root_mean_squared"), "Metrics dictionary contains 'root_mean_squared' for uncompressed.");
float rms = metrics["root_mean_squared"];
CHECK(rms == 0.0f);
TestProjectSettingsInternalsAccessor::resource_path() = old_resource_path;
}
} //namespace TestDDS

View file

@ -30,171 +30,13 @@
#include "texture_loader_dds.h"
#include "dds_enums.h"
#include "core/io/file_access.h"
#include "core/io/file_access_memory.h"
#include "scene/resources/image_texture.h"
#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
enum {
DDS_MAGIC = 0x20534444,
DDSD_PITCH = 0x00000008,
DDSD_LINEARSIZE = 0x00080000,
DDSD_MIPMAPCOUNT = 0x00020000,
DDPF_ALPHAPIXELS = 0x00000001,
DDPF_ALPHAONLY = 0x00000002,
DDPF_FOURCC = 0x00000004,
DDPF_RGB = 0x00000040,
DDPF_RG_SNORM = 0x00080000,
DDSC2_CUBEMAP = 0x200,
DDSC2_VOLUME = 0x200000,
DX10D_1D = 2,
DX10D_2D = 3,
DX10D_3D = 4,
};
enum DDSFourCC {
DDFCC_DXT1 = PF_FOURCC("DXT1"),
DDFCC_DXT2 = PF_FOURCC("DXT2"),
DDFCC_DXT3 = PF_FOURCC("DXT3"),
DDFCC_DXT4 = PF_FOURCC("DXT4"),
DDFCC_DXT5 = PF_FOURCC("DXT5"),
DDFCC_ATI1 = PF_FOURCC("ATI1"),
DDFCC_BC4U = PF_FOURCC("BC4U"),
DDFCC_ATI2 = PF_FOURCC("ATI2"),
DDFCC_BC5U = PF_FOURCC("BC5U"),
DDFCC_A2XY = PF_FOURCC("A2XY"),
DDFCC_DX10 = PF_FOURCC("DX10"),
DDFCC_R16F = 111,
DDFCC_RG16F = 112,
DDFCC_RGBA16F = 113,
DDFCC_R32F = 114,
DDFCC_RG32F = 115,
DDFCC_RGBA32F = 116
};
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
enum DXGIFormat {
DXGI_R32G32B32A32_FLOAT = 2,
DXGI_R32G32B32_FLOAT = 6,
DXGI_R16G16B16A16_FLOAT = 10,
DXGI_R32G32_FLOAT = 16,
DXGI_R10G10B10A2_UNORM = 24,
DXGI_R8G8B8A8_UNORM = 28,
DXGI_R8G8B8A8_UNORM_SRGB = 29,
DXGI_R16G16_FLOAT = 34,
DXGI_R32_FLOAT = 41,
DXGI_R8G8_UNORM = 49,
DXGI_R16_FLOAT = 54,
DXGI_R8_UNORM = 61,
DXGI_A8_UNORM = 65,
DXGI_R9G9B9E5 = 67,
DXGI_BC1_UNORM = 71,
DXGI_BC1_UNORM_SRGB = 72,
DXGI_BC2_UNORM = 74,
DXGI_BC2_UNORM_SRGB = 75,
DXGI_BC3_UNORM = 77,
DXGI_BC3_UNORM_SRGB = 78,
DXGI_BC4_UNORM = 80,
DXGI_BC5_UNORM = 83,
DXGI_B5G6R5_UNORM = 85,
DXGI_B5G5R5A1_UNORM = 86,
DXGI_B8G8R8A8_UNORM = 87,
DXGI_BC6H_UF16 = 95,
DXGI_BC6H_SF16 = 96,
DXGI_BC7_UNORM = 98,
DXGI_BC7_UNORM_SRGB = 99,
DXGI_B4G4R4A4_UNORM = 115
};
// The legacy bitmasked format names here represent the actual data layout in the files,
// while their official names are flipped (e.g. RGBA8 layout is officially called ABGR8).
enum DDSFormat {
DDS_DXT1,
DDS_DXT3,
DDS_DXT5,
DDS_ATI1,
DDS_ATI2,
DDS_BC6U,
DDS_BC6S,
DDS_BC7,
DDS_R16F,
DDS_RG16F,
DDS_RGBA16F,
DDS_R32F,
DDS_RG32F,
DDS_RGB32F,
DDS_RGBA32F,
DDS_RGB9E5,
DDS_RGB8,
DDS_RGBA8,
DDS_BGR8,
DDS_BGRA8,
DDS_BGR5A1,
DDS_BGR565,
DDS_B2GR3,
DDS_B2GR3A8,
DDS_BGR10A2,
DDS_RGB10A2,
DDS_BGRA4,
DDS_LUMINANCE,
DDS_LUMINANCE_ALPHA,
DDS_LUMINANCE_ALPHA_4,
DDS_MAX
};
enum DDSType {
DDST_2D = 1,
DDST_CUBEMAP,
DDST_3D,
DDST_TYPE_MASK = 0x7F,
DDST_ARRAY = 0x80,
};
struct DDSFormatInfo {
const char *name = nullptr;
bool compressed = false;
uint32_t divisor = 0;
uint32_t block_size = 0;
Image::Format format = Image::Format::FORMAT_BPTC_RGBA;
};
static const DDSFormatInfo dds_format_info[DDS_MAX] = {
{ "DXT1/BC1", true, 4, 8, Image::FORMAT_DXT1 },
{ "DXT2/DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 },
{ "DXT4/DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 },
{ "ATI1/BC4", true, 4, 8, Image::FORMAT_RGTC_R },
{ "ATI2/A2XY/BC5", true, 4, 16, Image::FORMAT_RGTC_RG },
{ "BC6UF", true, 4, 16, Image::FORMAT_BPTC_RGBFU },
{ "BC6SF", true, 4, 16, Image::FORMAT_BPTC_RGBF },
{ "BC7", true, 4, 16, Image::FORMAT_BPTC_RGBA },
{ "R16F", false, 1, 2, Image::FORMAT_RH },
{ "RG16F", false, 1, 4, Image::FORMAT_RGH },
{ "RGBA16F", false, 1, 8, Image::FORMAT_RGBAH },
{ "R32F", false, 1, 4, Image::FORMAT_RF },
{ "RG32F", false, 1, 8, Image::FORMAT_RGF },
{ "RGB32F", false, 1, 12, Image::FORMAT_RGBF },
{ "RGBA32F", false, 1, 16, Image::FORMAT_RGBAF },
{ "RGB9E5", false, 1, 4, Image::FORMAT_RGBE9995 },
{ "RGB8", false, 1, 3, Image::FORMAT_RGB8 },
{ "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGR8", false, 1, 3, Image::FORMAT_RGB8 },
{ "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGR5A1", false, 1, 2, Image::FORMAT_RGBA8 },
{ "BGR565", false, 1, 2, Image::FORMAT_RGB8 },
{ "B2GR3", false, 1, 1, Image::FORMAT_RGB8 },
{ "B2GR3A8", false, 1, 2, Image::FORMAT_RGBA8 },
{ "BGR10A2", false, 1, 4, Image::FORMAT_RGBA8 },
{ "RGB10A2", false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGRA4", false, 1, 2, Image::FORMAT_RGBA8 },
{ "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 },
{ "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 },
{ "GRAYSCALE_ALPHA_4", false, 1, 1, Image::FORMAT_LA8 }
};
inline DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) {
DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) {
switch (p_dxgi_format) {
case DXGI_R32G32B32A32_FLOAT: {
return DDS_RGBA32F;
@ -323,8 +165,8 @@ static Ref<Image> _dds_load_layer(Ref<FileAccess> p_file, DDSFormat p_dds_format
uint32_t size = p_width * p_height * info.block_size;
for (uint32_t i = 1; i < p_mipmaps; i++) {
w = (w + 1) >> 1;
h = (h + 1) >> 1;
w = MAX(1u, w >> 1);
h = MAX(1u, h >> 1);
size += w * h * info.block_size;
}
@ -543,7 +385,295 @@ static Ref<Image> _dds_load_layer(Ref<FileAccess> p_file, DDSFormat p_dds_format
return memnew(Image(p_width, p_height, p_mipmaps > 1, info.format, r_src_data));
}
Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
static Vector<Ref<Image>> _dds_load_images(Ref<FileAccess> p_f, DDSFormat p_dds_format, uint32_t p_width, uint32_t p_height, uint32_t p_mipmaps, uint32_t p_pitch, uint32_t p_flags, uint32_t p_layer_count) {
Vector<uint8_t> src_data;
Vector<Ref<Image>> images;
images.resize(p_layer_count);
for (uint32_t i = 0; i < p_layer_count; i++) {
images.write[i] = _dds_load_layer(p_f, p_dds_format, p_width, p_height, p_mipmaps, p_pitch, p_flags, src_data);
}
return images;
}
static Ref<Resource> _dds_create_texture(const Vector<Ref<Image>> &p_images, uint32_t p_dds_type, uint32_t p_width, uint32_t p_height, uint32_t p_layer_count, uint32_t p_mipmaps, Error *r_error) {
if ((p_dds_type & DDST_TYPE_MASK) == DDST_2D) {
if (p_dds_type & DDST_ARRAY) {
Ref<Texture2DArray> texture;
texture.instantiate();
texture->create_from_images(p_images);
if (r_error) {
*r_error = OK;
}
return texture;
} else {
if (r_error) {
*r_error = OK;
}
return ImageTexture::create_from_image(p_images[0]);
}
} else if ((p_layer_count & DDST_TYPE_MASK) == DDST_CUBEMAP) {
ERR_FAIL_COND_V(p_layer_count % 6 != 0, Ref<Resource>());
if (p_dds_type & DDST_ARRAY) {
Ref<CubemapArray> texture;
texture.instantiate();
texture->create_from_images(p_images);
if (r_error) {
*r_error = OK;
}
return texture;
} else {
Ref<Cubemap> texture;
texture.instantiate();
texture->create_from_images(p_images);
if (r_error) {
*r_error = OK;
}
return texture;
}
} else if ((p_dds_type & DDST_TYPE_MASK) == DDST_3D) {
Ref<ImageTexture3D> texture;
texture.instantiate();
texture->create(p_images[0]->get_format(), p_width, p_height, p_layer_count, p_mipmaps > 1, p_images);
if (r_error) {
*r_error = OK;
}
return texture;
}
return Ref<Resource>();
}
static Ref<Resource> _dds_create_texture_from_images(const Vector<Ref<Image>> &p_images, DDSFormat p_dds_format, uint32_t p_width, uint32_t p_height, uint32_t p_mipmaps, uint32_t p_pitch, uint32_t p_flags, uint32_t p_layer_count, uint32_t p_dds_type, Error *r_error) {
return _dds_create_texture(p_images, p_dds_type, p_width, p_height, p_layer_count, p_mipmaps, r_error);
}
static Vector<Ref<Image>> _dds_load_images_from_buffer(Ref<FileAccess> p_f, DDSFormat &r_dds_format, uint32_t &r_width, uint32_t &r_height, uint32_t &r_mipmaps, uint32_t &r_pitch, uint32_t &r_flags, uint32_t &r_layer_count, uint32_t &r_dds_type, const String &p_path = "") {
ERR_FAIL_COND_V_MSG(p_f.is_null(), Vector<Ref<Image>>(), vformat("Empty DDS texture file."));
ERR_FAIL_COND_V_MSG(!p_f->get_length(), Vector<Ref<Image>>(), vformat("Empty DDS texture file."));
uint32_t magic = p_f->get_32();
uint32_t hsize = p_f->get_32();
r_flags = p_f->get_32();
r_height = p_f->get_32();
r_width = p_f->get_32();
r_pitch = p_f->get_32();
uint32_t depth = p_f->get_32();
r_mipmaps = p_f->get_32();
// Skip reserved.
for (int i = 0; i < 11; i++) {
p_f->get_32();
}
// Validate.
// We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
// but non-mandatory when reading (as some writers don't set them).
if (magic != DDS_MAGIC || hsize != 124) {
ERR_FAIL_V_MSG(Vector<Ref<Image>>(), vformat("Invalid or unsupported DDS texture file '%s'.", p_path));
}
/* uint32_t format_size = */ p_f->get_32();
uint32_t format_flags = p_f->get_32();
uint32_t format_fourcc = p_f->get_32();
uint32_t format_rgb_bits = p_f->get_32();
uint32_t format_red_mask = p_f->get_32();
uint32_t format_green_mask = p_f->get_32();
uint32_t format_blue_mask = p_f->get_32();
uint32_t format_alpha_mask = p_f->get_32();
/* uint32_t caps_1 = */ p_f->get_32();
uint32_t caps_2 = p_f->get_32();
/* uint32_t caps_3 = */ p_f->get_32();
/* uint32_t caps_4 = */ p_f->get_32();
// Skip reserved.
p_f->get_32();
if (p_f->get_position() < 128) {
p_f->seek(128);
}
r_layer_count = 1;
r_dds_type = DDST_2D;
if (caps_2 & DDSC2_CUBEMAP) {
r_dds_type = DDST_CUBEMAP;
r_layer_count *= 6;
} else if (caps_2 & DDSC2_VOLUME) {
r_dds_type = DDST_3D;
r_layer_count = depth;
}
r_dds_format = DDS_MAX;
if (format_flags & DDPF_FOURCC) {
// FourCC formats.
switch (format_fourcc) {
case DDFCC_DXT1: {
r_dds_format = DDS_DXT1;
} break;
case DDFCC_DXT2:
case DDFCC_DXT3: {
r_dds_format = DDS_DXT3;
} break;
case DDFCC_DXT4:
case DDFCC_DXT5: {
r_dds_format = DDS_DXT5;
} break;
case DDFCC_ATI1:
case DDFCC_BC4U: {
r_dds_format = DDS_ATI1;
} break;
case DDFCC_ATI2:
case DDFCC_BC5U:
case DDFCC_A2XY: {
r_dds_format = DDS_ATI2;
} break;
case DDFCC_R16F: {
r_dds_format = DDS_R16F;
} break;
case DDFCC_RG16F: {
r_dds_format = DDS_RG16F;
} break;
case DDFCC_RGBA16F: {
r_dds_format = DDS_RGBA16F;
} break;
case DDFCC_R32F: {
r_dds_format = DDS_R32F;
} break;
case DDFCC_RG32F: {
r_dds_format = DDS_RG32F;
} break;
case DDFCC_RGBA32F: {
r_dds_format = DDS_RGBA32F;
} break;
case DDFCC_DX10: {
uint32_t dxgi_format = p_f->get_32();
uint32_t dimension = p_f->get_32();
/* uint32_t misc_flags_1 = */ p_f->get_32();
uint32_t array_size = p_f->get_32();
/* uint32_t misc_flags_2 = */ p_f->get_32();
if (dimension == DX10D_3D) {
r_dds_type = DDST_3D;
r_layer_count = depth;
}
if (array_size > 1) {
r_layer_count *= array_size;
r_dds_type |= DDST_ARRAY;
}
r_dds_format = _dxgi_to_dds_format(dxgi_format);
} break;
default: {
ERR_FAIL_V_MSG(Vector<Ref<Image>>(), vformat("Unrecognized or unsupported FourCC in DDS '%s'.", p_path));
}
}
} else if (format_flags & DDPF_RGB) {
// Channel-bitmasked formats.
if (format_flags & DDPF_ALPHAPIXELS) {
// With alpha.
if (format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) {
r_dds_format = DDS_BGRA8;
} else if (format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) {
r_dds_format = DDS_RGBA8;
} else if (format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) {
r_dds_format = DDS_BGR5A1;
} else if (format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) {
r_dds_format = DDS_BGR10A2;
} else if (format_rgb_bits == 32 && format_red_mask == 0x3ff && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff00000 && format_alpha_mask == 0xc0000000) {
r_dds_format = DDS_RGB10A2;
} else if (format_rgb_bits == 16 && format_red_mask == 0xf00 && format_green_mask == 0xf0 && format_blue_mask == 0xf && format_alpha_mask == 0xf000) {
r_dds_format = DDS_BGRA4;
} else if (format_rgb_bits == 16 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3 && format_alpha_mask == 0xff00) {
r_dds_format = DDS_B2GR3A8;
}
} else {
// Without alpha.
if (format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) {
r_dds_format = DDS_BGR8;
} else if (format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) {
r_dds_format = DDS_RGB8;
} else if (format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) {
r_dds_format = DDS_BGR565;
} else if (format_rgb_bits == 8 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3) {
r_dds_format = DDS_B2GR3;
}
}
} else {
// Other formats.
if (format_flags & DDPF_ALPHAONLY && format_rgb_bits == 8 && format_alpha_mask == 0xff) {
// Alpha only.
r_dds_format = DDS_LUMINANCE;
}
}
// Depending on the writer, luminance formats may or may not have the DDPF_RGB or DDPF_LUMINANCE flags defined,
// so we check for these formats after everything else failed.
if (r_dds_format == DDS_MAX) {
if (format_flags & DDPF_ALPHAPIXELS) {
// With alpha.
if (format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) {
r_dds_format = DDS_LUMINANCE_ALPHA;
} else if (format_rgb_bits == 8 && format_red_mask == 0xf && format_alpha_mask == 0xf0) {
r_dds_format = DDS_LUMINANCE_ALPHA_4;
}
} else {
// Without alpha.
if (format_rgb_bits == 8 && format_red_mask == 0xff) {
r_dds_format = DDS_LUMINANCE;
}
}
}
// No format detected, error.
if (r_dds_format == DDS_MAX) {
ERR_FAIL_V_MSG(Vector<Ref<Image>>(), vformat("Unrecognized or unsupported color layout in DDS '%s'.", p_path));
}
if (!(r_flags & DDSD_MIPMAPCOUNT)) {
r_mipmaps = 1;
}
return _dds_load_images(p_f, r_dds_format, r_width, r_height, r_mipmaps, r_pitch, r_flags, r_layer_count);
}
static Ref<Resource> _dds_load_from_buffer(Ref<FileAccess> p_f, Error *r_error, const String &p_path = "") {
if (r_error) {
*r_error = ERR_FILE_CORRUPT;
}
DDSFormat dds_format;
uint32_t width = 0, height = 0, mipmaps = 0, pitch = 0, flags = 0, layer_count = 0, dds_type = 0;
Vector<Ref<Image>> images = _dds_load_images_from_buffer(p_f, dds_format, width, height, mipmaps, pitch, flags, layer_count, dds_type, p_path);
return _dds_create_texture_from_images(images, dds_format, width, height, mipmaps, pitch, flags, layer_count, dds_type, r_error);
}
static Ref<Resource> _dds_load_from_file(const String &p_path, Error *r_error) {
if (r_error) {
*r_error = ERR_CANT_OPEN;
}
@ -554,268 +684,11 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
return Ref<Resource>();
}
Ref<FileAccess> fref(f);
if (r_error) {
*r_error = ERR_FILE_CORRUPT;
}
return _dds_load_from_buffer(f, r_error, p_path);
}
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), vformat("Unable to open DDS texture file '%s'.", p_path));
uint32_t magic = f->get_32();
uint32_t hsize = f->get_32();
uint32_t flags = f->get_32();
uint32_t height = f->get_32();
uint32_t width = f->get_32();
uint32_t pitch = f->get_32();
uint32_t depth = f->get_32();
uint32_t mipmaps = f->get_32();
// Skip reserved.
for (int i = 0; i < 11; i++) {
f->get_32();
}
// Validate.
// We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
// but non-mandatory when reading (as some writers don't set them).
if (magic != DDS_MAGIC || hsize != 124) {
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Invalid or unsupported DDS texture file '%s'.", p_path));
}
/* uint32_t format_size = */ f->get_32();
uint32_t format_flags = f->get_32();
uint32_t format_fourcc = f->get_32();
uint32_t format_rgb_bits = f->get_32();
uint32_t format_red_mask = f->get_32();
uint32_t format_green_mask = f->get_32();
uint32_t format_blue_mask = f->get_32();
uint32_t format_alpha_mask = f->get_32();
/* uint32_t caps_1 = */ f->get_32();
uint32_t caps_2 = f->get_32();
/* uint32_t caps_3 = */ f->get_32();
/* uint32_t caps_4 = */ f->get_32();
// Skip reserved.
f->get_32();
if (f->get_position() < 128) {
f->seek(128);
}
uint32_t layer_count = 1;
uint32_t dds_type = DDST_2D;
if (caps_2 & DDSC2_CUBEMAP) {
dds_type = DDST_CUBEMAP;
layer_count *= 6;
} else if (caps_2 & DDSC2_VOLUME) {
dds_type = DDST_3D;
layer_count = depth;
}
DDSFormat dds_format = DDS_MAX;
if (format_flags & DDPF_FOURCC) {
// FourCC formats.
switch (format_fourcc) {
case DDFCC_DXT1: {
dds_format = DDS_DXT1;
} break;
case DDFCC_DXT2:
case DDFCC_DXT3: {
dds_format = DDS_DXT3;
} break;
case DDFCC_DXT4:
case DDFCC_DXT5: {
dds_format = DDS_DXT5;
} break;
case DDFCC_ATI1:
case DDFCC_BC4U: {
dds_format = DDS_ATI1;
} break;
case DDFCC_ATI2:
case DDFCC_BC5U:
case DDFCC_A2XY: {
dds_format = DDS_ATI2;
} break;
case DDFCC_R16F: {
dds_format = DDS_R16F;
} break;
case DDFCC_RG16F: {
dds_format = DDS_RG16F;
} break;
case DDFCC_RGBA16F: {
dds_format = DDS_RGBA16F;
} break;
case DDFCC_R32F: {
dds_format = DDS_R32F;
} break;
case DDFCC_RG32F: {
dds_format = DDS_RG32F;
} break;
case DDFCC_RGBA32F: {
dds_format = DDS_RGBA32F;
} break;
case DDFCC_DX10: {
uint32_t dxgi_format = f->get_32();
uint32_t dimension = f->get_32();
/* uint32_t misc_flags_1 = */ f->get_32();
uint32_t array_size = f->get_32();
/* uint32_t misc_flags_2 = */ f->get_32();
if (dimension == DX10D_3D) {
dds_type = DDST_3D;
layer_count = depth;
}
if (array_size > 1) {
layer_count *= array_size;
dds_type |= DDST_ARRAY;
}
dds_format = _dxgi_to_dds_format(dxgi_format);
} break;
default: {
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Unrecognized or unsupported FourCC in DDS '%s'.", p_path));
}
}
} else if (format_flags & DDPF_RGB) {
// Channel-bitmasked formats.
if (format_flags & DDPF_ALPHAPIXELS) {
// With alpha.
if (format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) {
dds_format = DDS_BGRA8;
} else if (format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) {
dds_format = DDS_RGBA8;
} else if (format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) {
dds_format = DDS_BGR5A1;
} else if (format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) {
dds_format = DDS_BGR10A2;
} else if (format_rgb_bits == 32 && format_red_mask == 0x3ff && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff00000 && format_alpha_mask == 0xc0000000) {
dds_format = DDS_RGB10A2;
} else if (format_rgb_bits == 16 && format_red_mask == 0xf00 && format_green_mask == 0xf0 && format_blue_mask == 0xf && format_alpha_mask == 0xf000) {
dds_format = DDS_BGRA4;
} else if (format_rgb_bits == 16 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3 && format_alpha_mask == 0xff00) {
dds_format = DDS_B2GR3A8;
}
} else {
// Without alpha.
if (format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) {
dds_format = DDS_BGR8;
} else if (format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) {
dds_format = DDS_RGB8;
} else if (format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) {
dds_format = DDS_BGR565;
} else if (format_rgb_bits == 8 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3) {
dds_format = DDS_B2GR3;
}
}
} else {
// Other formats.
if (format_flags & DDPF_ALPHAONLY && format_rgb_bits == 8 && format_alpha_mask == 0xff) {
// Alpha only.
dds_format = DDS_LUMINANCE;
}
}
// Depending on the writer, luminance formats may or may not have the DDPF_RGB or DDPF_LUMINANCE flags defined,
// so we check for these formats after everything else failed.
if (dds_format == DDS_MAX) {
if (format_flags & DDPF_ALPHAPIXELS) {
// With alpha.
if (format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) {
dds_format = DDS_LUMINANCE_ALPHA;
} else if (format_rgb_bits == 8 && format_red_mask == 0xf && format_alpha_mask == 0xf0) {
dds_format = DDS_LUMINANCE_ALPHA_4;
}
} else {
// Without alpha.
if (format_rgb_bits == 8 && format_red_mask == 0xff) {
dds_format = DDS_LUMINANCE;
}
}
}
// No format detected, error.
if (dds_format == DDS_MAX) {
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Unrecognized or unsupported color layout in DDS '%s'.", p_path));
}
if (!(flags & DDSD_MIPMAPCOUNT)) {
mipmaps = 1;
}
Vector<uint8_t> src_data;
Vector<Ref<Image>> images;
images.resize(layer_count);
for (uint32_t i = 0; i < layer_count; i++) {
images.write[i] = _dds_load_layer(f, dds_format, width, height, mipmaps, pitch, flags, src_data);
}
if ((dds_type & DDST_TYPE_MASK) == DDST_2D) {
if (dds_type & DDST_ARRAY) {
Ref<Texture2DArray> texture = memnew(Texture2DArray());
texture->create_from_images(images);
if (r_error) {
*r_error = OK;
}
return texture;
} else {
if (r_error) {
*r_error = OK;
}
return ImageTexture::create_from_image(images[0]);
}
} else if ((dds_type & DDST_TYPE_MASK) == DDST_CUBEMAP) {
ERR_FAIL_COND_V(layer_count % 6 != 0, Ref<Resource>());
if (dds_type & DDST_ARRAY) {
Ref<CubemapArray> texture = memnew(CubemapArray());
texture->create_from_images(images);
if (r_error) {
*r_error = OK;
}
return texture;
} else {
Ref<Cubemap> texture = memnew(Cubemap());
texture->create_from_images(images);
if (r_error) {
*r_error = OK;
}
return texture;
}
} else if ((dds_type & DDST_TYPE_MASK) == DDST_3D) {
Ref<ImageTexture3D> texture = memnew(ImageTexture3D());
texture->create(images[0]->get_format(), width, height, layer_count, mipmaps > 1, images);
if (r_error) {
*r_error = OK;
}
return texture;
}
return Ref<Resource>();
Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
return _dds_load_from_file(p_path, r_error);
}
void ResourceFormatDDS::get_recognized_extensions(List<String> *p_extensions) const {
@ -832,3 +705,24 @@ String ResourceFormatDDS::get_resource_type(const String &p_path) const {
}
return "";
}
Ref<Image> load_mem_dds(const uint8_t *p_dds, int p_size) {
ERR_FAIL_NULL_V(p_dds, Ref<Image>());
ERR_FAIL_COND_V(!p_size, Ref<Image>());
Ref<FileAccessMemory> memfile;
memfile.instantiate();
Error open_memfile_error = memfile->open_custom(p_dds, p_size);
ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for DDS image buffer.");
DDSFormat dds_format;
uint32_t width, height, mipmaps, pitch, flags, layer_count, dds_type;
Vector<Ref<Image>> images = _dds_load_images_from_buffer(memfile, dds_format, width, height, mipmaps, pitch, flags, layer_count, dds_type);
ERR_FAIL_COND_V_MSG(images.is_empty(), Ref<Image>(), "Failed to load DDS image.");
return images[0];
}
ResourceFormatDDS::ResourceFormatDDS() {
Image::_dds_mem_loader_func = load_mem_dds;
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TEXTURE_LOADER_DDS_H
#define TEXTURE_LOADER_DDS_H
#pragma once
#include "core/io/resource_loader.h"
@ -40,7 +39,6 @@ public:
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
ResourceFormatDDS();
virtual ~ResourceFormatDDS() {}
};
#endif // TEXTURE_LOADER_DDS_H

View file

@ -24,7 +24,7 @@ if env["builtin_enet"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_enet.Prepend(CPPPATH=[thirdparty_dir])
env_enet.Prepend(CPPEXTPATH=[thirdparty_dir])
env_enet.Append(CPPDEFINES=["GODOT_ENET"])
env_thirdparty = env_enet.Clone()

View file

@ -72,8 +72,8 @@ Error ENetConnection::create_host(int p_max_peers, int p_max_channels, int p_in_
void ENetConnection::destroy() {
ERR_FAIL_NULL_MSG(host, "Host already destroyed.");
for (List<Ref<ENetPacketPeer>>::Element *E = peers.front(); E; E = E->next()) {
E->get()->_on_disconnect();
for (const Ref<ENetPacketPeer> &peer : peers) {
peer->_on_disconnect();
}
peers.clear();
enet_host_destroy(host);
@ -320,14 +320,10 @@ Error ENetConnection::_create(ENetAddress *p_address, int p_max_peers, int p_max
}
Array ENetConnection::_service(int p_timeout) {
Array out;
Event event;
Ref<ENetPacketPeer> peer;
EventType ret = service(p_timeout, event);
out.push_back(ret);
out.push_back(event.peer);
out.push_back(event.data);
out.push_back(event.channel_id);
Array out = { ret, event.peer, event.data, event.channel_id };
if (event.packet && event.peer.is_valid()) {
event.peer->_queue_packet(event.packet);
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ENET_CONNECTION_H
#define ENET_CONNECTION_H
#pragma once
#include "enet_packet_peer.h"
@ -140,5 +139,3 @@ public:
VARIANT_ENUM_CAST(ENetConnection::CompressionMode);
VARIANT_ENUM_CAST(ENetConnection::EventType);
VARIANT_ENUM_CAST(ENetConnection::HostStatistic);
#endif // ENET_CONNECTION_H

View file

@ -334,7 +334,7 @@ Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_si
Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V_MSG(!_is_active(), ERR_UNCONFIGURED, "The multiplayer instance isn't currently active.");
ERR_FAIL_COND_V_MSG(connection_status != CONNECTION_CONNECTED, ERR_UNCONFIGURED, "The multiplayer instance isn't currently connected to any server or client.");
ERR_FAIL_COND_V_MSG(target_peer != 0 && !peers.has(ABS(target_peer)), ERR_INVALID_PARAMETER, vformat("Invalid target peer: %d", target_peer));
ERR_FAIL_COND_V_MSG(target_peer != 0 && !peers.has(Math::abs(target_peer)), ERR_INVALID_PARAMETER, vformat("Invalid target peer: %d", target_peer));
ERR_FAIL_COND_V(active_mode == MODE_CLIENT && !peers.has(1), ERR_BUG);
int packet_flags = 0;
@ -394,7 +394,7 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
} else {
if (target_peer <= 0) {
int exclude = ABS(target_peer);
int exclude = Math::abs(target_peer);
for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
if (E.key == exclude) {
continue;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ENET_MULTIPLAYER_PEER_H
#define ENET_MULTIPLAYER_PEER_H
#pragma once
#include "enet_connection.h"
@ -132,5 +131,3 @@ public:
ENetMultiplayerPeer();
~ENetMultiplayerPeer();
};
#endif // ENET_MULTIPLAYER_PEER_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ENET_PACKET_PEER_H
#define ENET_PACKET_PEER_H
#pragma once
#include "core/io/packet_peer.h"
@ -128,5 +127,3 @@ public:
VARIANT_ENUM_CAST(ENetPacketPeer::PeerState);
VARIANT_ENUM_CAST(ENetPacketPeer::PeerStatistic);
#endif // ENET_PACKET_PEER_H

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ENET_REGISTER_TYPES_H
#define ENET_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_enet_module(ModuleInitializationLevel p_level);
void uninitialize_enet_module(ModuleInitializationLevel p_level);
#endif // ENET_REGISTER_TYPES_H

View file

@ -20,7 +20,7 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_etcpak.Prepend(CPPPATH=[thirdparty_dir])
env_etcpak.Prepend(CPPEXTPATH=[thirdparty_dir])
env_thirdparty = env_etcpak.Clone()
env_thirdparty.disable_warnings()

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef IMAGE_COMPRESS_ETCPAK_H
#define IMAGE_COMPRESS_ETCPAK_H
#pragma once
#ifdef TOOLS_ENABLED
@ -56,5 +55,3 @@ void _compress_bc(Image *r_img, Image::UsedChannels p_channels);
void _compress_etcpak(EtcpakType p_compress_type, Image *r_img);
#endif // TOOLS_ENABLED
#endif // IMAGE_COMPRESS_ETCPAK_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef IMAGE_DECOMPRESS_ETCPAK_H
#define IMAGE_DECOMPRESS_ETCPAK_H
#pragma once
#include "core/io/image.h"
@ -41,5 +40,3 @@ enum EtcpakFormat {
};
void _decompress_etc(Image *p_image);
#endif // IMAGE_DECOMPRESS_ETCPAK_H

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ETCPAK_REGISTER_TYPES_H
#define ETCPAK_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_etcpak_module(ModuleInitializationLevel p_level);
void uninitialize_etcpak_module(ModuleInitializationLevel p_level);
#endif // ETCPAK_REGISTER_TYPES_H

View file

@ -13,7 +13,7 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/ufbx/"
thirdparty_sources = [thirdparty_dir + "ufbx.c"]
env_fbx.Prepend(CPPPATH=[thirdparty_dir])
env_fbx.Prepend(CPPEXTPATH=[thirdparty_dir])
env_thirdparty = env_fbx.Clone()
env_thirdparty.disable_warnings()

View file

@ -30,8 +30,6 @@
#include "editor_scene_importer_fbx2gltf.h"
#ifdef TOOLS_ENABLED
#include "core/config/project_settings.h"
#include "editor/editor_settings.h"
#include "editor_scene_importer_ufbx.h"
@ -142,5 +140,3 @@ void EditorSceneFormatImporterFBX2GLTF::handle_compatibility_options(HashMap<Str
p_import_params["fbx/importer"] = EditorSceneFormatImporterUFBX::FBX_IMPORTER_UFBX;
}
}
#endif // TOOLS_ENABLED

View file

@ -28,10 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_SCENE_IMPORTER_FBX2GLTF_H
#define EDITOR_SCENE_IMPORTER_FBX2GLTF_H
#ifdef TOOLS_ENABLED
#pragma once
#include "editor/import/3d/resource_importer_scene.h"
@ -52,7 +49,3 @@ public:
const HashMap<StringName, Variant> &p_options) override;
virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const override;
};
#endif // TOOLS_ENABLED
#endif // EDITOR_SCENE_IMPORTER_FBX2GLTF_H

View file

@ -30,8 +30,6 @@
#include "editor_scene_importer_ufbx.h"
#ifdef TOOLS_ENABLED
#include "../fbx_document.h"
#include "editor_scene_importer_fbx2gltf.h"
@ -104,5 +102,3 @@ void EditorSceneFormatImporterUFBX::handle_compatibility_options(HashMap<StringN
p_import_params["fbx/importer"] = EditorSceneFormatImporterUFBX::FBX_IMPORTER_FBX2GLTF;
}
}
#endif // TOOLS_ENABLED

View file

@ -28,10 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_SCENE_IMPORTER_UFBX_H
#define EDITOR_SCENE_IMPORTER_UFBX_H
#ifdef TOOLS_ENABLED
#pragma once
#include "editor/import/3d/resource_importer_scene.h"
@ -56,6 +53,3 @@ public:
const HashMap<StringName, Variant> &p_options) override;
virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const override;
};
#endif // TOOLS_ENABLED
#endif // EDITOR_SCENE_IMPORTER_UFBX_H

View file

@ -251,18 +251,16 @@ static bool _thread_pool_init_fn(void *user, ufbx_thread_pool_context ctx, const
return true;
}
static bool _thread_pool_run_fn(void *user, ufbx_thread_pool_context ctx, uint32_t group, uint32_t start_index, uint32_t count) {
static void _thread_pool_run_fn(void *user, ufbx_thread_pool_context ctx, uint32_t group, uint32_t start_index, uint32_t count) {
ThreadPoolFBX *pool = (ThreadPoolFBX *)user;
ThreadPoolFBX::Group &pool_group = pool->groups[group];
pool_group.start_index = start_index;
pool_group.task_id = pool->pool->add_native_group_task(_thread_pool_task, &pool_group, (int)count, -1, true, "ufbx");
return true;
}
static bool _thread_pool_wait_fn(void *user, ufbx_thread_pool_context ctx, uint32_t group, uint32_t max_index) {
static void _thread_pool_wait_fn(void *user, ufbx_thread_pool_context ctx, uint32_t group, uint32_t max_index) {
ThreadPoolFBX *pool = (ThreadPoolFBX *)user;
pool->pool->wait_for_group_task_completion(pool->groups[group].task_id);
return true;
}
String FBXDocument::_gen_unique_name(HashSet<String> &unique_names, const String &p_name) {
@ -1082,7 +1080,7 @@ Error FBXDocument::_parse_images(Ref<FBXState> p_state, const String &p_base_pat
}
// Fallback to loading as byte array.
data = FileAccess::get_file_as_bytes(path);
if (data.size() == 0) {
if (data.is_empty()) {
WARN_PRINT(vformat("FBX: Image index '%d' couldn't be loaded from path: %s because there was no data to load. Skipping it.", texture_i, path));
p_state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
p_state->source_images.push_back(Ref<Image>());
@ -1658,8 +1656,7 @@ void FBXDocument::_generate_scene_node(Ref<FBXState> p_state, const GLTFNodeInde
// Add the node we generated and set the owner to the scene root.
p_scene_parent->add_child(current_node, true);
if (current_node != p_scene_root) {
Array args;
args.append(p_scene_root);
Array args = { p_scene_root };
current_node->propagate_call(StringName("set_owner"), args);
}
current_node->set_transform(fbx_node->transform);
@ -1746,8 +1743,7 @@ void FBXDocument::_generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTF
// Add the node we generated and set the owner to the scene root.
p_scene_parent->add_child(current_node, true);
if (current_node != p_scene_root) {
Array args;
args.append(p_scene_root);
Array args = { p_scene_root };
current_node->propagate_call(StringName("set_owner"), args);
}
// Do not set transform here. Transform is already applied to our bone.

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef FBX_DOCUMENT_H
#define FBX_DOCUMENT_H
#pragma once
#include "fbx_state.h"
@ -99,5 +98,3 @@ public:
const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks);
Error _parse(Ref<FBXState> p_state, String p_path, Ref<FileAccess> p_file);
};
#endif // FBX_DOCUMENT_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef FBX_STATE_H
#define FBX_STATE_H
#pragma once
#include "modules/gltf/gltf_defines.h"
#include "modules/gltf/gltf_state.h"
@ -65,5 +64,3 @@ public:
bool get_allow_geometry_helper_nodes();
void set_allow_geometry_helper_nodes(bool p_allow_geometry_helper_nodes);
};
#endif // FBX_STATE_H

View file

@ -61,10 +61,6 @@ void initialize_fbx_module(ModuleInitializationLevel p_level) {
#ifdef TOOLS_ENABLED
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
// Editor-specific API.
ClassDB::APIType prev_api = ClassDB::get_current_api();
ClassDB::set_current_api(ClassDB::API_EDITOR);
GDREGISTER_CLASS(EditorSceneFormatImporterUFBX);
GLOBAL_DEF_RST_BASIC("filesystem/import/fbx2gltf/enabled", true);
@ -72,7 +68,6 @@ void initialize_fbx_module(ModuleInitializationLevel p_level) {
GLOBAL_DEF_RST("filesystem/import/fbx2gltf/enabled.android", false);
GLOBAL_DEF_RST("filesystem/import/fbx2gltf/enabled.web", false);
ClassDB::set_current_api(prev_api);
EditorNode::add_init_callback(_editor_init);
}
#endif // TOOLS_ENABLED

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef FBX_REGISTER_TYPES_H
#define FBX_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_fbx_module(ModuleInitializationLevel p_level);
void uninitialize_fbx_module(ModuleInitializationLevel p_level);
#endif // FBX_REGISTER_TYPES_H

View file

@ -62,15 +62,15 @@ if env["builtin_freetype"]:
if env["brotli"]:
env_freetype.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
env_freetype.Prepend(CPPPATH=[thirdparty_dir + "/include"])
env_freetype.Prepend(CPPEXTPATH=[thirdparty_dir + "/include"])
# Also needed in main env for scene/
env.Prepend(CPPPATH=[thirdparty_dir + "/include"])
env.Prepend(CPPEXTPATH=[thirdparty_dir + "/include"])
env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", "FT_CONFIG_OPTION_SYSTEM_ZLIB"])
# Also requires libpng headers
if env["builtin_libpng"]:
env_freetype.Prepend(CPPPATH=["#thirdparty/libpng"])
env_freetype.Prepend(CPPEXTPATH=["#thirdparty/libpng"])
sfnt = thirdparty_dir + "src/sfnt/sfnt.c"
# Must be done after all CPPDEFINES are being set so we can copy them.

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef FREETYPE_REGISTER_TYPES_H
#define FREETYPE_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_freetype_module(ModuleInitializationLevel p_level);
void uninitialize_freetype_module(ModuleInitializationLevel p_level);
#endif // FREETYPE_REGISTER_TYPES_H

View file

@ -107,7 +107,7 @@
<return type="Dictionary" />
<param index="0" name="instance" type="Object" />
<description>
Returns the passed [param instance] converted to a Dictionary. Can be useful for serializing.
Returns the passed [param instance] converted to a [Dictionary]. Can be useful for serializing.
[codeblock]
var foo = "bar"
func _ready():
@ -133,7 +133,7 @@
- A constant from the [enum Variant.Type] enumeration, for example [constant TYPE_INT].
- An [Object]-derived class which exists in [ClassDB], for example [Node].
- A [Script] (you can use any class, including inner one).
Unlike the right operand of the [code]is[/code] operator, [param type] can be a non-constant value. The [code]is[/code] operator supports more features (such as typed arrays). Use the operator instead of this method if you do not need dynamic type checking.
Unlike the right operand of the [code]is[/code] operator, [param type] can be a non-constant value. The [code]is[/code] operator supports more features (such as typed arrays). Use the operator instead of this method if you do not need to check the type dynamically.
[b]Examples:[/b]
[codeblock]
print(is_instance_of(a, TYPE_INT))

View file

@ -105,7 +105,7 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type
return;
case GDType::SCRIPT:
if (p_gdtype.is_meta_type) {
r_type = p_gdtype.script_type.is_valid() ? p_gdtype.script_type->get_class() : Script::get_class_static();
r_type = p_gdtype.script_type.is_valid() ? p_gdtype.script_type->get_class_name() : Script::get_class_static();
return;
}
if (p_gdtype.script_type.is_valid()) {
@ -216,15 +216,15 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re
} else {
result += "{";
List<Variant> keys;
dict.get_key_list(&keys);
LocalVector<Variant> keys = dict.get_key_list();
keys.sort_custom<StringLikeVariantOrder>();
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
if (E->prev()) {
for (uint32_t i = 0; i < keys.size(); i++) {
const Variant &key = keys[i];
if (i > 0) {
result += ", ";
}
result += _docvalue_from_variant(E->get(), p_recursion_level + 1) + ": " + _docvalue_from_variant(dict[E->get()], p_recursion_level + 1);
result += _docvalue_from_variant(key, p_recursion_level + 1) + ": " + _docvalue_from_variant(dict[key], p_recursion_level + 1);
}
result += "}";

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDSCRIPT_DOCGEN_H
#define GDSCRIPT_DOCGEN_H
#pragma once
#include "../gdscript_parser.h"
@ -52,5 +51,3 @@ public:
static void doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false);
static String docvalue_from_expression(const GDP::ExpressionNode *p_expression);
};
#endif // GDSCRIPT_DOCGEN_H

View file

@ -150,6 +150,15 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
break;
}
}
// "#region" and "#endregion" only highlighted if they're the first region on the line.
if (color_regions[c].type == ColorRegion::TYPE_CODE_REGION) {
Vector<String> str_stripped_split = str.strip_edges().split_spaces(1);
if (!str_stripped_split.is_empty() &&
str_stripped_split[0] != "#region" &&
str_stripped_split[0] != "#endregion") {
match = false;
}
}
if (!match) {
continue;
}
@ -777,8 +786,8 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<String> comments;
gdscript->get_comment_delimiters(&comments);
for (const String &comment : comments) {
String beg = comment.get_slice(" ", 0);
String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
String beg = comment.get_slicec(' ', 0);
String end = comment.get_slice_count(" ") > 1 ? comment.get_slicec(' ', 1) : String();
add_color_region(ColorRegion::TYPE_COMMENT, beg, end, comment_color, end.is_empty());
}
@ -787,8 +796,8 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<String> doc_comments;
gdscript->get_doc_comment_delimiters(&doc_comments);
for (const String &doc_comment : doc_comments) {
String beg = doc_comment.get_slice(" ", 0);
String end = doc_comment.get_slice_count(" ") > 1 ? doc_comment.get_slice(" ", 1) : String();
String beg = doc_comment.get_slicec(' ', 0);
String end = doc_comment.get_slice_count(" ") > 1 ? doc_comment.get_slicec(' ', 1) : String();
add_color_region(ColorRegion::TYPE_COMMENT, beg, end, doc_comment_color, end.is_empty());
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDSCRIPT_HIGHLIGHTER_H
#define GDSCRIPT_HIGHLIGHTER_H
#pragma once
#include "editor/plugins/script_editor_plugin.h"
@ -116,5 +115,3 @@ public:
virtual Ref<EditorSyntaxHighlighter> _create() const override;
};
#endif // GDSCRIPT_HIGHLIGHTER_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
#pragma once
#include "../gdscript_parser.h"
#include "../gdscript_tokenizer.h"
@ -82,5 +81,3 @@ public:
GDScriptEditorTranslationParserPlugin();
};
#endif // GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H

View file

@ -4,6 +4,16 @@
extends _BASE_
func _enable_plugin() -> void:
# Add autoloads here.
pass
func _disable_plugin() -> void:
# Remove autoloads here.
pass
func _enter_tree() -> void:
# Initialization of the plugin goes here.
pass

View file

@ -59,8 +59,6 @@
#include "editor/editor_paths.h"
#endif
#include <stdint.h>
///////////////////////////
GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) {
@ -1123,7 +1121,7 @@ Error GDScript::load_source_code(const String &p_path) {
w[len] = 0;
String s;
if (s.parse_utf8((const char *)w, len) != OK) {
if (s.append_utf8((const char *)w, len) != OK) {
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
}
@ -1625,6 +1623,27 @@ void GDScript::clear(ClearData *p_clear_data) {
}
}
void GDScript::cancel_pending_functions(bool warn) {
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
// Order matters since clearing the stack may already cause
// the GDScriptFunctionState to be destroyed and thus removed from the list.
pending_func_states.remove(E);
GDScriptFunctionState *state = E->self();
#ifdef DEBUG_ENABLED
if (warn) {
WARN_PRINT("Canceling suspended execution of \"" + state->get_readable_function() + "\" due to a script reload.");
}
#endif
ObjectID state_id = state->get_instance_id();
state->_clear_connections();
if (ObjectDB::get_instance(state_id)) {
state->_clear_stack();
}
}
}
GDScript::~GDScript() {
if (destructing) {
return;
@ -1640,21 +1659,7 @@ GDScript::~GDScript() {
clear();
{
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
// Order matters since clearing the stack may already cause
// the GDScriptFunctionState to be destroyed and thus removed from the list.
pending_func_states.remove(E);
GDScriptFunctionState *state = E->self();
ObjectID state_id = state->get_instance_id();
state->_clear_connections();
if (ObjectDB::get_instance(state_id)) {
state->_clear_stack();
}
}
}
cancel_pending_functions(false);
{
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
@ -2249,10 +2254,10 @@ void GDScriptLanguage::init() {
_add_global(StaticCString::create(CoreConstants::get_global_constant_name(i)), CoreConstants::get_global_constant_value(i));
}
_add_global(StaticCString::create("PI"), Math_PI);
_add_global(StaticCString::create("TAU"), Math_TAU);
_add_global(StaticCString::create("INF"), INFINITY);
_add_global(StaticCString::create("NAN"), NAN);
_add_global(StaticCString::create("PI"), Math::PI);
_add_global(StaticCString::create("TAU"), Math::TAU);
_add_global(StaticCString::create("INF"), Math::INF);
_add_global(StaticCString::create("NAN"), Math::NaN);
//populate native classes
@ -2699,8 +2704,7 @@ void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload
}
void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
Array scripts;
scripts.push_back(p_script);
Array scripts = { p_script };
reload_scripts(scripts, p_soft_reload);
}
@ -2849,7 +2853,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
while (subclass) {
if (subclass->extends_used) {
if (!subclass->extends_path.is_empty()) {
if (subclass->extends.size() == 0) {
if (subclass->extends.is_empty()) {
get_global_class_name(subclass->extends_path, r_base_type);
subclass = nullptr;
break;
@ -3066,6 +3070,62 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
}
}
void ResourceFormatLoaderGDScript::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
Ref<GDScript> scr = ResourceLoader::load(p_path);
if (scr.is_null()) {
return;
}
const String source = scr->get_source_code();
GDScriptTokenizerText tokenizer;
tokenizer.set_source_code(source);
GDScriptTokenizer::Token current = tokenizer.scan();
while (current.type != GDScriptTokenizer::Token::TK_EOF) {
if (!current.is_identifier()) {
current = tokenizer.scan();
continue;
}
int insert_idx = 0;
for (int i = 0; i < current.start_line - 1; i++) {
insert_idx = source.find("\n", insert_idx) + 1;
}
// Insert the "cursor" character, needed for the lookup to work.
const String source_with_cursor = source.insert(insert_idx + current.start_column, String::chr(0xFFFF));
ScriptLanguage::LookupResult result;
if (scr->get_language()->lookup_code(source_with_cursor, current.get_identifier(), p_path, nullptr, result) == OK) {
if (!result.class_name.is_empty() && ClassDB::class_exists(result.class_name)) {
r_classes->insert(result.class_name);
}
if (result.type == ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY) {
PropertyInfo prop;
if (ClassDB::get_property_info(result.class_name, result.class_member, &prop)) {
if (!prop.class_name.is_empty() && ClassDB::class_exists(prop.class_name)) {
r_classes->insert(prop.class_name);
}
if (!prop.hint_string.is_empty() && ClassDB::class_exists(prop.hint_string)) {
r_classes->insert(prop.hint_string);
}
}
} else if (result.type == ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD) {
MethodInfo met;
if (ClassDB::get_method_info(result.class_name, result.class_member, &met)) {
if (!met.return_val.class_name.is_empty() && ClassDB::class_exists(met.return_val.class_name)) {
r_classes->insert(met.return_val.class_name);
}
if (!met.return_val.hint_string.is_empty() && ClassDB::class_exists(met.return_val.hint_string)) {
r_classes->insert(met.return_val.hint_string);
}
}
}
}
current = tokenizer.scan();
}
}
Error ResourceFormatSaverGDScript::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<GDScript> sqscr = p_resource;
ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDSCRIPT_H
#define GDSCRIPT_H
#pragma once
#include "gdscript_function.h"
@ -244,6 +243,9 @@ public:
void clear(GDScript::ClearData *p_clear_data = nullptr);
// Cancels all functions of the script that are are waiting to be resumed after using await.
void cancel_pending_functions(bool warn);
virtual bool is_valid() const override { return valid; }
virtual bool is_abstract() const override { return false; } // GDScript does not support abstract classes.
@ -655,6 +657,7 @@ public:
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
};
class ResourceFormatSaverGDScript : public ResourceFormatSaver {
@ -663,5 +666,3 @@ public:
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override;
virtual bool recognize(const Ref<Resource> &p_resource) const override;
};
#endif // GDSCRIPT_H

View file

@ -1640,13 +1640,12 @@ void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_anno
const MethodInfo &annotation_info = parser->valid_annotations[p_annotation->name].info;
const List<PropertyInfo>::Element *E = annotation_info.arguments.front();
for (int i = 0; i < p_annotation->arguments.size(); i++) {
for (int64_t i = 0, j = 0; i < p_annotation->arguments.size(); i++) {
GDScriptParser::ExpressionNode *argument = p_annotation->arguments[i];
const PropertyInfo &argument_info = E->get();
const PropertyInfo &argument_info = annotation_info.arguments[j];
if (E->next() != nullptr) {
E = E->next();
if (j + 1 < annotation_info.arguments.size()) {
++j;
}
reduce_expression(argument);
@ -1715,6 +1714,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
static_context = p_function->is_static;
}
MethodInfo method_info;
method_info.name = function_name;
if (p_function->is_static) {
method_info.flags |= MethodFlags::METHOD_FLAG_STATIC;
}
GDScriptParser::DataType prev_datatype = p_function->get_datatype();
GDScriptParser::DataType resolving_datatype;
@ -1734,6 +1739,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
for (int i = 0; i < p_function->parameters.size(); i++) {
resolve_parameter(p_function->parameters[i]);
method_info.arguments.push_back(p_function->parameters[i]->get_datatype().to_property_info(p_function->parameters[i]->identifier->name));
#ifdef DEBUG_ENABLED
if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) {
parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, function_visible_name, p_function->parameters[i]->identifier->name);
@ -1797,7 +1803,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
GDScriptParser::DataType parent_return_type;
List<GDScriptParser::DataType> parameters_types;
int default_par_count = 0;
BitField<MethodFlags> method_flags;
BitField<MethodFlags> method_flags = {};
StringName native_base;
if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, method_flags, &native_base)) {
bool valid = p_function->is_static == method_flags.has_flag(METHOD_FLAG_STATIC);
@ -1884,6 +1890,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
#endif
method_info.default_arguments.append_array(p_function->default_arg_values);
method_info.return_val = p_function->get_datatype().to_property_info("");
p_function->info = method_info;
if (p_function->get_datatype().is_resolving()) {
p_function->set_datatype(prev_datatype);
}
@ -2158,7 +2168,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
GDScriptParser::IdentifierNode *callee = static_cast<GDScriptParser::IdentifierNode *>(call->callee);
if (callee->name == "range") {
list_resolved = true;
if (call->arguments.size() < 1) {
if (call->arguments.is_empty()) {
push_error(R"*(Invalid call for "range()" function. Expected at least 1 argument, none given.)*", call->callee);
} else if (call->arguments.size() > 3) {
push_error(vformat(R"*(Invalid call for "range()" function. Expected at most 3 arguments, %d given.)*", call->arguments.size()), call->callee);
@ -2266,7 +2276,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
GDScriptParser::DataType return_type;
List<GDScriptParser::DataType> par_types;
int default_arg_count = 0;
BitField<MethodFlags> method_flags;
BitField<MethodFlags> method_flags = {};
if (get_function_signature(p_for->list, false, list_type, CoreStringName(_iter_get), return_type, par_types, default_arg_count, method_flags)) {
variable_type = return_type;
variable_type.type_source = list_type.type_source;
@ -3323,28 +3333,24 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
bool types_match = true;
{
List<PropertyInfo>::ConstIterator arg_itr = info.arguments.begin();
for (int i = 0; i < p_call->arguments.size(); ++arg_itr, ++i) {
GDScriptParser::DataType par_type = type_from_property(*arg_itr, true);
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
if (!is_type_compatible(par_type, arg_type, true)) {
types_match = false;
break;
for (int64_t i = 0; i < p_call->arguments.size(); ++i) {
GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
if (!is_type_compatible(par_type, arg_type, true)) {
types_match = false;
break;
#ifdef DEBUG_ENABLED
} else {
if (par_type.builtin_type == Variant::INT && arg_type.builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, function_name);
}
#endif
} else {
if (par_type.builtin_type == Variant::INT && arg_type.builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, function_name);
}
#endif
}
}
if (types_match) {
List<PropertyInfo>::ConstIterator arg_itr = info.arguments.begin();
for (int i = 0; i < p_call->arguments.size(); ++arg_itr, ++i) {
GDScriptParser::DataType par_type = type_from_property(*arg_itr, true);
for (int64_t i = 0; i < p_call->arguments.size(); ++i) {
GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
if (p_call->arguments[i]->is_constant) {
update_const_expression_builtin_type(p_call->arguments[i], par_type, "pass");
}
@ -3561,7 +3567,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
int default_arg_count = 0;
BitField<MethodFlags> method_flags;
BitField<MethodFlags> method_flags = {};
GDScriptParser::DataType return_type;
List<GDScriptParser::DataType> par_types;
@ -5574,7 +5580,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
result.set_container_element_type(0, elem_type);
} else if (p_property.type == Variant::DICTIONARY && p_property.hint == PROPERTY_HINT_DICTIONARY_TYPE) {
// Check element type.
StringName key_elem_type_name = p_property.hint_string.get_slice(";", 0);
StringName key_elem_type_name = p_property.hint_string.get_slicec(';', 0);
GDScriptParser::DataType key_elem_type;
key_elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@ -5599,7 +5605,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
}
key_elem_type.is_constant = false;
StringName value_elem_type_name = p_property.hint_string.get_slice(";", 1);
StringName value_elem_type_name = p_property.hint_string.get_slicec(';', 1);
GDScriptParser::DataType value_elem_type;
value_elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDSCRIPT_ANALYZER_H
#define GDSCRIPT_ANALYZER_H
#pragma once
#include "gdscript_cache.h"
#include "gdscript_parser.h"
@ -167,5 +166,3 @@ public:
GDScriptAnalyzer(GDScriptParser *p_parser);
};
#endif // GDSCRIPT_ANALYZER_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDSCRIPT_BYTE_CODEGEN_H
#define GDSCRIPT_BYTE_CODEGEN_H
#pragma once
#include "gdscript_codegen.h"
#include "gdscript_function.h"
@ -552,5 +551,3 @@ public:
virtual ~GDScriptByteCodeGenerator();
};
#endif // GDSCRIPT_BYTE_CODEGEN_H

View file

@ -189,7 +189,7 @@ void GDScriptCache::remove_script(const String &p_path) {
if (HashMap<String, Vector<ObjectID>>::Iterator E = singleton->abandoned_parser_map.find(p_path)) {
for (ObjectID parser_ref_id : E->value) {
Ref<GDScriptParserRef> parser_ref{ ObjectDB::get_instance(parser_ref_id) };
Ref<GDScriptParserRef> parser_ref = { ObjectDB::get_instance(parser_ref_id) };
if (parser_ref.is_valid()) {
parser_ref->clear();
}
@ -275,7 +275,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
source_file.write[len] = 0;
String source;
if (source.parse_utf8((const char *)source_file.ptr(), len) != OK) {
if (source.append_utf8((const char *)source_file.ptr(), len) != OK) {
ERR_FAIL_V_MSG("", "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
}
return source;
@ -460,7 +460,7 @@ void GDScriptCache::clear() {
for (const KeyValue<String, Vector<ObjectID>> &KV : singleton->abandoned_parser_map) {
for (ObjectID parser_ref_id : KV.value) {
Ref<GDScriptParserRef> parser_ref{ ObjectDB::get_instance(parser_ref_id) };
Ref<GDScriptParserRef> parser_ref = { ObjectDB::get_instance(parser_ref_id) };
if (parser_ref.is_valid()) {
parser_ref->clear();
}
@ -485,6 +485,7 @@ void GDScriptCache::clear() {
parser_map_refs.clear();
singleton->shallow_gdscript_cache.clear();
singleton->full_gdscript_cache.clear();
singleton->static_gdscript_cache.clear();
}
GDScriptCache::GDScriptCache() {

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDSCRIPT_CACHE_H
#define GDSCRIPT_CACHE_H
#pragma once
#include "gdscript.h"
@ -42,6 +41,8 @@ class GDScriptAnalyzer;
class GDScriptParser;
class GDScriptParserRef : public RefCounted {
GDSOFTCLASS(GDScriptParserRef, RefCounted);
public:
enum Status {
EMPTY,
@ -122,5 +123,3 @@ public:
GDScriptCache();
~GDScriptCache();
};
#endif // GDSCRIPT_CACHE_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDSCRIPT_CODEGEN_H
#define GDSCRIPT_CODEGEN_H
#pragma once
#include "gdscript_function.h"
#include "gdscript_utility_functions.h"
@ -165,5 +164,3 @@ public:
virtual ~GDScriptCodeGenerator() {}
};
#endif // GDSCRIPT_CODEGEN_H

Some files were not shown because too many files have changed in this diff Show more