mirror of https://github.com/AxioDL/metaforce.git
Merge submodule contents for hecl/master
This commit is contained in:
commit
5efaf5b7db
|
@ -46,3 +46,19 @@
|
||||||
path = Editor/locale
|
path = Editor/locale
|
||||||
url = ../urde-translations.git
|
url = ../urde-translations.git
|
||||||
branch = master
|
branch = master
|
||||||
|
[submodule "extern/libSquish"]
|
||||||
|
path = hecl/extern/libSquish
|
||||||
|
url = ../libSquish.git
|
||||||
|
branch = master
|
||||||
|
[submodule "extern/athena"]
|
||||||
|
path = hecl/extern/athena
|
||||||
|
url = ../../libAthena/athena.git
|
||||||
|
branch = master
|
||||||
|
[submodule "extern/boo"]
|
||||||
|
path = hecl/extern/boo
|
||||||
|
url = ../boo.git
|
||||||
|
branch = master
|
||||||
|
[submodule "extern/libjpeg-turbo"]
|
||||||
|
path = hecl/extern/libjpeg-turbo
|
||||||
|
url = ../libjpeg-turbo.git
|
||||||
|
branch = thp
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
DataSpecRegistry.hpp
|
||||||
|
.DS_Store
|
|
@ -0,0 +1,63 @@
|
||||||
|
include_guard(GLOBAL)
|
||||||
|
|
||||||
|
file(RELATIVE_PATH REL_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR}/${REL_PATH})
|
||||||
|
|
||||||
|
unset(HECL_APPLICATION_REPS_TARGETS_LIST CACHE)
|
||||||
|
unset(HECL_APPLICATION_REPS_INCLUDES_LIST CACHE)
|
||||||
|
unset(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL CACHE)
|
||||||
|
unset(HECL_APPLICATION_PIPELINE_REPS CACHE)
|
||||||
|
unset(HECL_APPLICATION_STAGE_REPS CACHE)
|
||||||
|
|
||||||
|
# add_pipeline_rep(my::fully::qualified::class my_class_header.hpp [UNIVERSAL])
|
||||||
|
function(add_pipeline_rep name header)
|
||||||
|
if(IS_ABSOLUTE ${header})
|
||||||
|
set(theHeader ${header})
|
||||||
|
else()
|
||||||
|
set(theHeader ${CMAKE_CURRENT_SOURCE_DIR}/${header})
|
||||||
|
endif()
|
||||||
|
if (NOT ${theHeader} IN_LIST HECL_APPLICATION_REPS_INCLUDES_LIST)
|
||||||
|
set(HECL_APPLICATION_REPS_INCLUDES_LIST "${HECL_APPLICATION_REPS_INCLUDES_LIST};${theHeader}" CACHE INTERNAL "")
|
||||||
|
endif()
|
||||||
|
if ("${ARGV2}" STREQUAL "UNIVERSAL")
|
||||||
|
set(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL "${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL};${name}" CACHE INTERNAL "")
|
||||||
|
else()
|
||||||
|
set(HECL_APPLICATION_PIPELINE_REPS "${HECL_APPLICATION_PIPELINE_REPS};${name}" CACHE INTERNAL "")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# add_stage_rep(my::fully::qualified::class my_class_header.hpp)
|
||||||
|
function(add_stage_rep name header)
|
||||||
|
if(IS_ABSOLUTE ${header})
|
||||||
|
set(theHeader ${header})
|
||||||
|
else()
|
||||||
|
set(theHeader ${CMAKE_CURRENT_SOURCE_DIR}/${header})
|
||||||
|
endif()
|
||||||
|
if (NOT ${theHeader} IN_LIST HECL_APPLICATION_REPS_INCLUDES_LIST)
|
||||||
|
set(HECL_APPLICATION_REPS_INCLUDES_LIST "${HECL_APPLICATION_REPS_INCLUDES_LIST};${theHeader}" CACHE INTERNAL "")
|
||||||
|
endif()
|
||||||
|
set(HECL_APPLICATION_STAGE_REPS "${HECL_APPLICATION_STAGE_REPS};${name}" CACHE INTERNAL "")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(add_shader_target target)
|
||||||
|
if (NOT ${target} IN_LIST HECL_APPLICATION_REPS_TARGETS_LIST)
|
||||||
|
set(HECL_APPLICATION_REPS_TARGETS_LIST "${HECL_APPLICATION_REPS_TARGETS_LIST};${target}" CACHE INTERNAL "")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(add_shader file)
|
||||||
|
get_filename_component(name ${file} NAME)
|
||||||
|
get_filename_component(dir ${file} DIRECTORY)
|
||||||
|
shaderc(${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name} ${file}.shader)
|
||||||
|
add_stage_rep(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp)
|
||||||
|
add_pipeline_rep(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp UNIVERSAL)
|
||||||
|
add_library(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.cpp)
|
||||||
|
add_shader_target(shader_${name})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(add_special_shader name)
|
||||||
|
add_stage_rep(${name} ${name}.hpp)
|
||||||
|
add_pipeline_rep(${name} ${name}.hpp UNIVERSAL)
|
||||||
|
add_library(${name} ${name}.hpp ${ARGN})
|
||||||
|
add_shader_target(${name})
|
||||||
|
endfunction()
|
|
@ -0,0 +1,74 @@
|
||||||
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||||
|
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
|
||||||
|
project(hecl)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
add_compile_options(
|
||||||
|
# Disable exceptions
|
||||||
|
$<$<COMPILE_LANGUAGE:CXX>:/EHsc>
|
||||||
|
/wd4267 /wd4244
|
||||||
|
)
|
||||||
|
add_compile_definitions(UNICODE=1 _UNICODE=1 _CRT_SECURE_NO_WARNINGS=1)
|
||||||
|
else()
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
add_compile_options(
|
||||||
|
# Disable exceptions
|
||||||
|
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
|
||||||
|
-Wno-multichar
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(ApplicationTools.cmake)
|
||||||
|
|
||||||
|
configure_file(DataSpecRegistry.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/DataSpecRegistry.hpp @ONLY)
|
||||||
|
|
||||||
|
unset(HECL_APPLICATION_REPS_INCLUDES_LOCAL)
|
||||||
|
foreach(theHeader ${HECL_APPLICATION_REPS_INCLUDES_LIST})
|
||||||
|
set(HECL_APPLICATION_REPS_INCLUDES_LOCAL "${HECL_APPLICATION_REPS_INCLUDES_LOCAL}#include \"${theHeader}\"\n")
|
||||||
|
endforeach()
|
||||||
|
unset(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL)
|
||||||
|
foreach(name ${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL})
|
||||||
|
set(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL}UNIVERSAL_PIPELINES_${name} \\\n")
|
||||||
|
endforeach()
|
||||||
|
unset(HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL)
|
||||||
|
unset(HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL)
|
||||||
|
unset(HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL)
|
||||||
|
unset(HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL)
|
||||||
|
unset(HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL)
|
||||||
|
foreach(name ${HECL_APPLICATION_PIPELINE_REPS})
|
||||||
|
set(HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL}OPENGL_PIPELINES_${name} \\\n")
|
||||||
|
set(HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL}VULKAN_PIPELINES_${name} \\\n")
|
||||||
|
set(HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL}D3D11_PIPELINES_${name} \\\n")
|
||||||
|
set(HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL}METAL_PIPELINES_${name} \\\n")
|
||||||
|
set(HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL}NX_PIPELINES_${name} \\\n")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
unset(HECL_APPLICATION_STAGE_REPS_LOCAL)
|
||||||
|
foreach(name ${HECL_APPLICATION_STAGE_REPS})
|
||||||
|
set(HECL_APPLICATION_STAGE_REPS_LOCAL "${HECL_APPLICATION_STAGE_REPS_LOCAL}STAGES_${name}(P, S) \\\n")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
configure_file(include/hecl/ApplicationReps.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/ApplicationReps.hpp @ONLY)
|
||||||
|
|
||||||
|
add_subdirectory(extern)
|
||||||
|
add_subdirectory(bintoc)
|
||||||
|
|
||||||
|
if(NOT TARGET bintoc)
|
||||||
|
# Use native if cross compiling
|
||||||
|
find_package(hecl-bintoc REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET atdna)
|
||||||
|
# Import native atdna if cross-compiling
|
||||||
|
find_package(atdna REQUIRED)
|
||||||
|
if(NOT TARGET atdna)
|
||||||
|
message(FATAL_ERROR "atdna required for building hecl; please verify LLVM installation")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(lib)
|
||||||
|
add_subdirectory(blender)
|
||||||
|
add_subdirectory(driver)
|
||||||
|
install(DIRECTORY include/hecl DESTINATION include/hecl)
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* Include this file once in the main translation unit of any executable file
|
||||||
|
* using HECL's database functionality (see driver/main.cpp)
|
||||||
|
*/
|
||||||
|
#ifdef DATA_SPEC_REGISTRY_HPP
|
||||||
|
#error DataSpecRegistry.hpp may only be included once
|
||||||
|
#endif
|
||||||
|
#define DATA_SPEC_REGISTRY_HPP
|
||||||
|
|
||||||
|
#include "hecl/Database.hpp"
|
||||||
|
|
||||||
|
namespace hecl::Database {
|
||||||
|
/* Centralized registry for DataSpec lookup */
|
||||||
|
std::vector<const struct DataSpecEntry*> DATA_SPEC_REGISTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@HECL_DATASPEC_DECLS@
|
||||||
|
|
||||||
|
/* Please Call Me! */
|
||||||
|
void HECLRegisterDataSpecs() {
|
||||||
|
@HECL_DATASPEC_PUSHES@
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2015-2018 HECL Contributors
|
||||||
|
Original Authors: Jack Andersen and Phillip "Antidote" Stephens
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,29 @@
|
||||||
|
### HECL (high-level, extensible combiner language)
|
||||||
|
|
||||||
|
**HECL** is a toolkit for building custom asset pipelines, assisting the development of conversion tools and runtime loaders.
|
||||||
|
|
||||||
|
The most significant feature is the intermediate HECL language, using an expressive command syntax to represent cross-platform shaders. This includes a common source representation and intermediate binary representation. Complete vertex and fragment shader programs are generated for supported platforms and may be built on-demand as part of a 3D application runtime.
|
||||||
|
|
||||||
|
```py
|
||||||
|
# Combiner 1: Opaque *Diffuse* and *Emissive*
|
||||||
|
HECLOpaque(Texture(0, UV(0)) * Lighting() + Texture(1, UV(0)))
|
||||||
|
|
||||||
|
# Combiner 2: Alpha-blended single-texture
|
||||||
|
# (both texture-accesses folded to a single sample operation)
|
||||||
|
HECLAlpha(Texture(0, UV(0)), Texture(0, UV(0)).a)
|
||||||
|
|
||||||
|
# Combiner 3: Additive-blended single-texture
|
||||||
|
# (modern graphics APIs require blending configuration along with all shader configs)
|
||||||
|
HECLAdditive(Texture(0, UV(0)), Texture(0, UV(0)).a)
|
||||||
|
```
|
||||||
|
|
||||||
|
Beyond shaders, HECL also defines a rigged mesh format called HMDL. Meshes using this encoding interact with HECL, with pose transforms applied via the vertex shader.
|
||||||
|
|
||||||
|
For asset pipelines, HECL provides a project system with dependency-resolution much like an IDE or `make`. Assets in their editable representation are *cooked* in-bulk and whenever the source file is updated. Currently, blender is the only-supported input format for rigged meshes with node-materials.
|
||||||
|
|
||||||
|
#### Supported Backends
|
||||||
|
|
||||||
|
* GLSL 330 *(with optional SPIR-V conversion)*
|
||||||
|
* HLSL (Shader Model 4)
|
||||||
|
* Metal 1.1
|
||||||
|
* GX *(complete TexCoordGen and TEV configs in intermediate structures)*
|
|
@ -0,0 +1,62 @@
|
||||||
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
add_executable(bintoc bintoc.c)
|
||||||
|
target_include_directories(bintoc PRIVATE ${ZLIB_INCLUDE_DIR})
|
||||||
|
target_link_libraries(bintoc ${ZLIB_LIBRARIES})
|
||||||
|
function(bintoc out in sym)
|
||||||
|
if(IS_ABSOLUTE ${out})
|
||||||
|
set(theOut ${out})
|
||||||
|
else()
|
||||||
|
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
|
||||||
|
endif()
|
||||||
|
if(IS_ABSOLUTE ${in})
|
||||||
|
set(theIn ${in})
|
||||||
|
else()
|
||||||
|
set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in})
|
||||||
|
endif()
|
||||||
|
get_filename_component(outDir ${theOut} DIRECTORY)
|
||||||
|
file(MAKE_DIRECTORY ${outDir})
|
||||||
|
add_custom_command(OUTPUT ${theOut}
|
||||||
|
COMMAND $<TARGET_FILE:bintoc> ARGS ${theIn} ${theOut} ${sym}
|
||||||
|
DEPENDS ${theIn} bintoc)
|
||||||
|
endfunction()
|
||||||
|
function(bintoc_compress out in sym)
|
||||||
|
if(IS_ABSOLUTE ${out})
|
||||||
|
set(theOut ${out})
|
||||||
|
else()
|
||||||
|
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
|
||||||
|
endif()
|
||||||
|
if(IS_ABSOLUTE ${in})
|
||||||
|
set(theIn ${in})
|
||||||
|
else()
|
||||||
|
set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in})
|
||||||
|
endif()
|
||||||
|
get_filename_component(outDir ${theOut} DIRECTORY)
|
||||||
|
file(MAKE_DIRECTORY ${outDir})
|
||||||
|
add_custom_command(OUTPUT ${theOut}
|
||||||
|
COMMAND $<TARGET_FILE:bintoc> ARGS --compress ${theIn} ${theOut} ${sym}
|
||||||
|
DEPENDS ${theIn} bintoc)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Package Export #
|
||||||
|
##################
|
||||||
|
|
||||||
|
# Add all targets to the build-tree export set
|
||||||
|
export(TARGETS bintoc FILE "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocTargets.cmake")
|
||||||
|
|
||||||
|
# Export the package for use from the build-tree
|
||||||
|
# (this registers the build-tree with a global CMake-registry)
|
||||||
|
export(PACKAGE hecl-bintoc)
|
||||||
|
|
||||||
|
# Create the atdnaConfig.cmake
|
||||||
|
# ... for the build tree
|
||||||
|
configure_file(hecl-bintocConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocConfig.cmake" @ONLY)
|
||||||
|
# ... for the install tree
|
||||||
|
configure_file(hecl-bintocConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hecl-bintocConfig.cmake" @ONLY)
|
||||||
|
# ... for both
|
||||||
|
configure_file(hecl-bintocConfigVersion.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocConfigVersion.cmake" @ONLY)
|
||||||
|
|
||||||
|
else()
|
||||||
|
# Use native if cross compiling
|
||||||
|
find_package(hecl-bintoc REQUIRED)
|
||||||
|
endif()
|
|
@ -0,0 +1,95 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#define CHUNK 16384
|
||||||
|
#define LINE_BREAK 32
|
||||||
|
static uint8_t buf[CHUNK];
|
||||||
|
static uint8_t zbuf[CHUNK];
|
||||||
|
|
||||||
|
void print_usage() { fprintf(stderr, "Usage: bintoc [--compress] <in> <out> <symbol>\n"); }
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc < 4) {
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char* input = argv[1];
|
||||||
|
char* output = argv[2];
|
||||||
|
char* symbol = argv[3];
|
||||||
|
bool compress = false;
|
||||||
|
if (strcmp(input, "--compress") == 0) {
|
||||||
|
if (argc < 5) {
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
input = argv[2];
|
||||||
|
output = argv[3];
|
||||||
|
symbol = argv[4];
|
||||||
|
compress = true;
|
||||||
|
}
|
||||||
|
FILE* fin = fopen(input, "rb");
|
||||||
|
if (!fin) {
|
||||||
|
fprintf(stderr, "Unable to open %s for reading\n", input);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
FILE* fout = fopen(output, "wb");
|
||||||
|
if (!fout) {
|
||||||
|
fprintf(stderr, "Unable to open %s for writing\n", output);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fprintf(fout, "#include <cstdint>\n#include <cstddef>\n");
|
||||||
|
fprintf(fout, "extern \"C\" const uint8_t %s[] =\n{\n ", symbol);
|
||||||
|
size_t totalSz = 0;
|
||||||
|
size_t readSz;
|
||||||
|
if (compress) {
|
||||||
|
size_t compressedSz = 0;
|
||||||
|
z_stream strm = {.zalloc = Z_NULL, .zfree = Z_NULL, .opaque = Z_NULL};
|
||||||
|
int ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS | 16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
||||||
|
if (ret != Z_OK) {
|
||||||
|
fprintf(stderr, "zlib initialization failed %d\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while ((strm.avail_in = fread(buf, 1, sizeof(buf), fin))) {
|
||||||
|
totalSz += strm.avail_in;
|
||||||
|
strm.next_in = buf;
|
||||||
|
int eof = feof(fin);
|
||||||
|
do {
|
||||||
|
strm.next_out = zbuf;
|
||||||
|
strm.avail_out = sizeof(zbuf);
|
||||||
|
ret = deflate(&strm, eof ? Z_FINISH : Z_NO_FLUSH);
|
||||||
|
if (ret == Z_STREAM_ERROR) {
|
||||||
|
fprintf(stderr, "zlib compression failed %d\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
size_t sz = sizeof(zbuf) - strm.avail_out;
|
||||||
|
if (sz > 0) {
|
||||||
|
for (int b = 0; b < sz; ++b) {
|
||||||
|
fprintf(fout, "0x%02X, ", zbuf[b]);
|
||||||
|
if ((compressedSz + b + 1) % LINE_BREAK == 0)
|
||||||
|
fprintf(fout, "\n ");
|
||||||
|
}
|
||||||
|
compressedSz += sz;
|
||||||
|
}
|
||||||
|
} while (strm.avail_out == 0 || (eof && (ret == Z_OK || ret == Z_BUF_ERROR)));
|
||||||
|
}
|
||||||
|
deflateEnd(&strm);
|
||||||
|
fprintf(fout, "0x00};\nextern \"C\" const size_t %s_SZ = %zu;\n", symbol, compressedSz);
|
||||||
|
fprintf(fout, "extern \"C\" const size_t %s_DECOMPRESSED_SZ = %zu;\n", symbol, totalSz);
|
||||||
|
} else {
|
||||||
|
while ((readSz = fread(buf, 1, sizeof(buf), fin))) {
|
||||||
|
for (int b = 0; b < readSz; ++b) {
|
||||||
|
fprintf(fout, "0x%02X, ", buf[b]);
|
||||||
|
if ((totalSz + b + 1) % LINE_BREAK == 0)
|
||||||
|
fprintf(fout, "\n ");
|
||||||
|
}
|
||||||
|
totalSz += readSz;
|
||||||
|
}
|
||||||
|
fprintf(fout, "0x0};\nextern \"C\" const size_t %s_SZ = %zu;\n", symbol, totalSz);
|
||||||
|
}
|
||||||
|
fclose(fin);
|
||||||
|
fclose(fout);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
# - Config file for the bintoc package
|
||||||
|
|
||||||
|
# Compute paths
|
||||||
|
get_filename_component(BINTOC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||||
|
|
||||||
|
# Our library dependencies (contains definitions for IMPORTED targets)
|
||||||
|
if(NOT TARGET bintoc AND NOT bintoc_BINARY_DIR)
|
||||||
|
include("${BINTOC_CMAKE_DIR}/hecl-bintocTargets.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(bintoc out in sym)
|
||||||
|
if(IS_ABSOLUTE ${out})
|
||||||
|
set(theOut ${out})
|
||||||
|
else()
|
||||||
|
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
|
||||||
|
endif()
|
||||||
|
if(IS_ABSOLUTE ${in})
|
||||||
|
set(theIn ${in})
|
||||||
|
else()
|
||||||
|
set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in})
|
||||||
|
endif()
|
||||||
|
get_filename_component(outDir ${theOut} DIRECTORY)
|
||||||
|
file(MAKE_DIRECTORY ${outDir})
|
||||||
|
add_custom_command(OUTPUT ${theOut}
|
||||||
|
COMMAND $<TARGET_FILE:bintoc> ARGS ${theIn} ${theOut} ${sym}
|
||||||
|
DEPENDS ${theIn})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(bintoc_compress out in sym)
|
||||||
|
if(IS_ABSOLUTE ${out})
|
||||||
|
set(theOut ${out})
|
||||||
|
else()
|
||||||
|
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
|
||||||
|
endif()
|
||||||
|
if(IS_ABSOLUTE ${in})
|
||||||
|
set(theIn ${in})
|
||||||
|
else()
|
||||||
|
set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in})
|
||||||
|
endif()
|
||||||
|
get_filename_component(outDir ${theOut} DIRECTORY)
|
||||||
|
file(MAKE_DIRECTORY ${outDir})
|
||||||
|
add_custom_command(OUTPUT ${theOut}
|
||||||
|
COMMAND $<TARGET_FILE:bintoc> ARGS --compress ${theIn} ${theOut} ${sym}
|
||||||
|
DEPENDS ${theIn})
|
||||||
|
endfunction()
|
|
@ -0,0 +1,12 @@
|
||||||
|
set(PACKAGE_VERSION "@BINTOC_VERSION@")
|
||||||
|
|
||||||
|
# Check whether the requested PACKAGE_FIND_VERSION is compatible
|
||||||
|
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
|
||||||
|
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||||
|
else()
|
||||||
|
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||||
|
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
|
||||||
|
set(PACKAGE_VERSION_EXACT TRUE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
list(APPEND PY_SOURCES
|
||||||
|
hecl/__init__.py
|
||||||
|
hecl/Nodegrid.py
|
||||||
|
hecl/Patching.py
|
||||||
|
hecl/hmdl/__init__.py
|
||||||
|
hecl/hmdl/HMDLMesh.py
|
||||||
|
hecl/hmdl/HMDLShader.py
|
||||||
|
hecl/sact/__init__.py
|
||||||
|
hecl/sact/SACTAction.py
|
||||||
|
hecl/sact/SACTSubtype.py
|
||||||
|
hecl/srea/__init__.py
|
||||||
|
hecl/swld/__init__.py
|
||||||
|
hecl/armature.py
|
||||||
|
hecl/mapa.py
|
||||||
|
hecl/mapu.py
|
||||||
|
hecl/frme.py
|
||||||
|
hecl/path.py)
|
||||||
|
|
||||||
|
bintoc(hecl_blendershell.cpp hecl_blendershell.py HECL_BLENDERSHELL)
|
||||||
|
|
||||||
|
find_package(Python COMPONENTS Interpreter REQUIRED)
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT hecl.zip DEPENDS ${PY_SOURCES}
|
||||||
|
COMMAND ${Python_EXECUTABLE} ARGS zip_package.py ${CMAKE_CURRENT_BINARY_DIR}/hecl.zip
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
COMMENT "Generating addon package")
|
||||||
|
bintoc(hecl_addon.cpp "${CMAKE_CURRENT_BINARY_DIR}/hecl.zip" HECL_ADDON)
|
||||||
|
|
||||||
|
add_library(hecl-blender-addon
|
||||||
|
hecl_blendershell.py
|
||||||
|
hecl_blendershell.cpp
|
||||||
|
zip_package.py
|
||||||
|
hecl.zip
|
||||||
|
hecl_addon.cpp
|
||||||
|
${PY_SOURCES})
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Node Grid Arranger Class
|
||||||
|
NODE_PADDING = 80
|
||||||
|
FRAME_NAMES = ['Textures','Output','Blend']
|
||||||
|
FRAME_WIDTHS = [400, 180, 180]
|
||||||
|
TOTAL_WIDTH = 0.0
|
||||||
|
for width in FRAME_WIDTHS:
|
||||||
|
TOTAL_WIDTH += width + NODE_PADDING
|
||||||
|
FRAME_COLORS = [(0.6,0.48,0.44),(0.53,0.6,0.47),(0.56,0.46,0.90)]
|
||||||
|
class Nodegrid:
|
||||||
|
|
||||||
|
def __init__(self, nodetree, cycles=False):
|
||||||
|
self.ncol = len(FRAME_NAMES)
|
||||||
|
self.heights = []
|
||||||
|
self.frames = []
|
||||||
|
self.col_roffs = [[0.0,0.0]] * self.ncol
|
||||||
|
for i in range(self.ncol):
|
||||||
|
if cycles and i<1:
|
||||||
|
self.heights.append(-1600.0)
|
||||||
|
self.frames.append(None)
|
||||||
|
continue
|
||||||
|
elif cycles:
|
||||||
|
self.heights.append(-1600.0)
|
||||||
|
else:
|
||||||
|
self.heights.append(0.0)
|
||||||
|
frame_node = nodetree.nodes.new('NodeFrame')
|
||||||
|
frame_node.label = FRAME_NAMES[i]
|
||||||
|
frame_node.use_custom_color = True
|
||||||
|
frame_node.color = FRAME_COLORS[i]
|
||||||
|
self.frames.append(frame_node)
|
||||||
|
|
||||||
|
def place_node(self, node, col, update_height = True):
|
||||||
|
if col < 0 or col >= self.ncol:
|
||||||
|
return False
|
||||||
|
|
||||||
|
x_pos = NODE_PADDING
|
||||||
|
for i in range(col):
|
||||||
|
x_pos += FRAME_WIDTHS[i] + NODE_PADDING*2
|
||||||
|
node.location[0] = x_pos - TOTAL_WIDTH/2
|
||||||
|
node.location[1] = self.heights[col]
|
||||||
|
if update_height:
|
||||||
|
self.heights[col] -= node.height + NODE_PADDING
|
||||||
|
self.frames[col].height += node.height + NODE_PADDING
|
||||||
|
node.parent = self.frames[col]
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def place_node_right(self, node, col, srow):
|
||||||
|
heights_backup = self.heights[col]
|
||||||
|
if self.place_node(node, col):
|
||||||
|
node.location[0] += self.col_roffs[col][srow]
|
||||||
|
if srow == 1:
|
||||||
|
node.location[1] -= 175
|
||||||
|
self.col_roffs[col][srow] += 200
|
||||||
|
self.heights[col] = heights_backup
|
||||||
|
|
||||||
|
def row_break(self, col):
|
||||||
|
self.heights[col] -= 350
|
||||||
|
self.col_roffs[col][0] = 0.0
|
||||||
|
self.col_roffs[col][1] = 0.0
|
|
@ -0,0 +1,144 @@
|
||||||
|
import bpy
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def _mkdir(path):
|
||||||
|
try:
|
||||||
|
os.mkdir(path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def path_components(path):
|
||||||
|
retval = []
|
||||||
|
base, end = os.path.split(path)
|
||||||
|
while end != '':
|
||||||
|
retval.insert(0, end)
|
||||||
|
base, end = os.path.split(base)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def find_project_root():
|
||||||
|
if bpy.data.filepath == '':
|
||||||
|
return None
|
||||||
|
path = os.path.split(bpy.data.filepath)
|
||||||
|
test_path = os.path.join(path[0], '.hecl')
|
||||||
|
while not os.path.exists(test_path):
|
||||||
|
path = os.path.split(path[0])
|
||||||
|
test_path = os.path.join(path[0], '.hecl')
|
||||||
|
if os.path.exists(test_path):
|
||||||
|
return path[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_patching_dirs(make_dirs=False):
|
||||||
|
proj_root = find_project_root()
|
||||||
|
if not proj_root:
|
||||||
|
return None, None
|
||||||
|
rel_to_blend = os.path.relpath(bpy.data.filepath, start=proj_root)
|
||||||
|
rel_to_blend_comps = path_components(rel_to_blend)
|
||||||
|
trace_dir = os.path.join(proj_root, '.hecl', 'patches')
|
||||||
|
global_out = trace_dir
|
||||||
|
if not make_dirs and not os.path.exists(trace_dir):
|
||||||
|
return None, global_out
|
||||||
|
_mkdir(trace_dir)
|
||||||
|
for comp in rel_to_blend_comps:
|
||||||
|
ext_pair = os.path.splitext(comp)
|
||||||
|
if ext_pair[1] == '.blend':
|
||||||
|
trace_dir = os.path.join(trace_dir, ext_pair[0])
|
||||||
|
if not make_dirs and not os.path.exists(trace_dir):
|
||||||
|
return None, global_out
|
||||||
|
_mkdir(trace_dir)
|
||||||
|
return trace_dir, global_out
|
||||||
|
trace_dir = os.path.join(trace_dir, comp)
|
||||||
|
if not make_dirs and not os.path.exists(trace_dir):
|
||||||
|
return None, global_out
|
||||||
|
_mkdir(trace_dir)
|
||||||
|
|
||||||
|
class FILE_OT_hecl_patching_save(bpy.types.Operator):
|
||||||
|
'''Save text datablocks to hecl patching directory'''
|
||||||
|
bl_idname = "file.hecl_patching_save"
|
||||||
|
bl_label = "Save HECL Patches"
|
||||||
|
bl_options = {'REGISTER'}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
patching_dir, global_dir = get_patching_dirs(make_dirs=True)
|
||||||
|
if not patching_dir:
|
||||||
|
self.report({'WARNING'}, 'Unable to save patches for ' + bpy.data.filepath)
|
||||||
|
return {'CANCELLED'}
|
||||||
|
count = 0
|
||||||
|
for text in bpy.data.texts:
|
||||||
|
if not text.name.endswith('.py') or text.name.startswith('g_'):
|
||||||
|
continue
|
||||||
|
text_abspath = os.path.join(patching_dir, text.name)
|
||||||
|
text_file = open(text_abspath, 'w')
|
||||||
|
text_file.write(text.as_string())
|
||||||
|
text_file.close()
|
||||||
|
count += 1
|
||||||
|
if count == 1:
|
||||||
|
self.report({'INFO'}, 'saved 1 patch')
|
||||||
|
else:
|
||||||
|
self.report({'INFO'}, 'saved %d patches' % count)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class FILE_OT_hecl_patching_load(bpy.types.Operator):
|
||||||
|
'''Load text datablocks from hecl patching directory'''
|
||||||
|
bl_idname = "file.hecl_patching_load"
|
||||||
|
bl_label = "Load HECL Patches"
|
||||||
|
bl_options = {'REGISTER'}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
patching_dir, global_dir = get_patching_dirs()
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
# Locals
|
||||||
|
if patching_dir:
|
||||||
|
p = Path(patching_dir)
|
||||||
|
for path in p.glob('*.py'):
|
||||||
|
path = path.name
|
||||||
|
text_abspath = os.path.join(patching_dir, path)
|
||||||
|
text_file = open(text_abspath, 'r')
|
||||||
|
if path in bpy.data.texts:
|
||||||
|
text = bpy.data.texts[path]
|
||||||
|
else:
|
||||||
|
text = bpy.data.texts.new(path)
|
||||||
|
text.from_string(text_file.read())
|
||||||
|
text_file.close()
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
# Globals
|
||||||
|
if global_dir:
|
||||||
|
p = Path(global_dir)
|
||||||
|
for path in p.glob('g_*.py'):
|
||||||
|
path = path.name
|
||||||
|
text_abspath = os.path.join(global_dir, path)
|
||||||
|
text_file = open(text_abspath, 'r')
|
||||||
|
if path in bpy.data.texts:
|
||||||
|
text = bpy.data.texts[path]
|
||||||
|
else:
|
||||||
|
text = bpy.data.texts.new(path)
|
||||||
|
text.from_string(text_file.read())
|
||||||
|
text_file.close()
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
if count == 1:
|
||||||
|
self.report({'INFO'}, 'loaded 1 patch')
|
||||||
|
else:
|
||||||
|
self.report({'INFO'}, 'loaded %d patches' % count)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def save_func(self, context):
|
||||||
|
self.layout.operator("file.hecl_patching_save", text="Save HECL Patches")
|
||||||
|
|
||||||
|
def load_func(self, context):
|
||||||
|
self.layout.operator("file.hecl_patching_load", text="Load HECL Patches")
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(FILE_OT_hecl_patching_save)
|
||||||
|
bpy.utils.register_class(FILE_OT_hecl_patching_load)
|
||||||
|
bpy.types.TOPBAR_MT_file_external_data.append(load_func)
|
||||||
|
bpy.types.TOPBAR_MT_file_external_data.append(save_func)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(FILE_OT_hecl_patching_save)
|
||||||
|
bpy.utils.unregister_class(FILE_OT_hecl_patching_load)
|
||||||
|
bpy.types.TOPBAR_MT_file_external_data.remove(load_func)
|
||||||
|
bpy.types.TOPBAR_MT_file_external_data.remove(save_func)
|
|
@ -0,0 +1,267 @@
|
||||||
|
bl_info = {
|
||||||
|
"name": "HECL",
|
||||||
|
"author": "Jack Andersen <jackoalan@gmail.com>",
|
||||||
|
"version": (1, 0),
|
||||||
|
"blender": (2, 80, 0),
|
||||||
|
"tracker_url": "https://github.com/AxioDL/hecl/issues/new",
|
||||||
|
"location": "Properties > Scene > HECL",
|
||||||
|
"description": "Enables blender to gather meshes, materials, and textures for hecl",
|
||||||
|
"category": "System"}
|
||||||
|
|
||||||
|
# Package import
|
||||||
|
from . import hmdl, sact, srea, swld, armature, mapa, mapu, frme, path, Nodegrid, Patching
|
||||||
|
Nodegrid = Nodegrid.Nodegrid
|
||||||
|
parent_armature = sact.SACTSubtype.parent_armature
|
||||||
|
import bpy, os, sys, struct, math
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
|
# Appendable list allowing external addons to register additional resource types
|
||||||
|
hecl_typeS = [
|
||||||
|
('NONE', "None", "Active scene not using HECL", None),
|
||||||
|
('MESH', "Mesh", "Active scene represents an HMDL Mesh", hmdl.draw),
|
||||||
|
('CMESH', "Collision Mesh", "Active scene represents a Collision Mesh", None),
|
||||||
|
('ARMATURE', "Armature", "Active scene represents an Armature", armature.draw),
|
||||||
|
('ACTOR', "Actor", "Active scene represents a HECL Actor", sact.draw),
|
||||||
|
('AREA', "Area", "Active scene represents a HECL Area", srea.draw),
|
||||||
|
('WORLD', "World", "Active scene represents a HECL World", swld.draw),
|
||||||
|
('MAPAREA', "Map Area", "Active scene represents a HECL Map Area", mapa.draw),
|
||||||
|
('MAPUNIVERSE', "Map Universe", "Active scene represents a HECL Map Universe", mapu.draw),
|
||||||
|
('FRAME', "Gui Frame", "Active scene represents a HECL Gui Frame", frme.draw),
|
||||||
|
('PATH', "Path Mesh", "Active scene represents a HECL Path Mesh", path.draw)]
|
||||||
|
|
||||||
|
# Main Scene Panel
|
||||||
|
class hecl_scene_panel(bpy.types.Panel):
|
||||||
|
bl_idname = "SCENE_PT_hecl"
|
||||||
|
bl_label = "HECL"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
type_row = layout.row(align=True)
|
||||||
|
type_row.prop_menu_enum(context.scene, 'hecl_type', text='Export Type')
|
||||||
|
|
||||||
|
if context.scene.hecl_type == 'MESH' or context.scene.hecl_type == 'AREA' or context.scene.hecl_type == 'ACTOR':
|
||||||
|
sm_row = layout.row(align=True)
|
||||||
|
sm_row.prop_enum(context.scene, 'hecl_shader_model', 'ORIGINAL')
|
||||||
|
sm_row.prop_enum(context.scene, 'hecl_shader_model', 'PBR')
|
||||||
|
layout.prop(context.scene, 'hecl_mp3_bloom', text='View MP3 Bloom')
|
||||||
|
|
||||||
|
for exp_type in hecl_typeS:
|
||||||
|
if exp_type[0] == context.scene.hecl_type and callable(exp_type[3]):
|
||||||
|
exp_type[3](self.layout, context)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Light Panel
|
||||||
|
class hecl_light_panel(bpy.types.Panel):
|
||||||
|
bl_idname = "DATA_PT_hecl_light"
|
||||||
|
bl_label = "HECL"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "data"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.light
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(context.light, 'hecl_falloff_constant')
|
||||||
|
layout.prop(context.light, 'hecl_falloff_linear')
|
||||||
|
layout.prop(context.light, 'hecl_falloff_quadratic')
|
||||||
|
|
||||||
|
# Blender export-type registration
|
||||||
|
def register_export_type_enum():
|
||||||
|
bpy.types.Scene.hecl_type = bpy.props.EnumProperty(items=
|
||||||
|
[tp[:3] for tp in hecl_typeS],
|
||||||
|
name="HECL Export Type",
|
||||||
|
description="Selects how active scene is exported by HECL")
|
||||||
|
|
||||||
|
# Function for external addons to register export types with HECL
|
||||||
|
def add_export_type(type_tuple):
|
||||||
|
type_tup = tuple(type_tuple)
|
||||||
|
for tp in hecl_typeS:
|
||||||
|
if tp[0] == type_tup[0]:
|
||||||
|
raise RuntimeError("Type already registered with HECL")
|
||||||
|
hecl_types.append(type_tup)
|
||||||
|
register_export_type_enum()
|
||||||
|
|
||||||
|
# Shell command receiver (from HECL driver)
|
||||||
|
def command(cmdline, writepipeline, writepipebuf):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def mesh_aabb(writepipebuf):
|
||||||
|
scene = bpy.context.scene
|
||||||
|
total_min = Vector((99999.0, 99999.0, 99999.0))
|
||||||
|
total_max = Vector((-99999.0, -99999.0, -99999.0))
|
||||||
|
|
||||||
|
if bpy.context.scene.hecl_type == 'ACTOR':
|
||||||
|
sact_data = bpy.context.scene.hecl_sact_data
|
||||||
|
for subtype in sact_data.subtypes:
|
||||||
|
if subtype.linked_mesh in bpy.data.objects:
|
||||||
|
mesh = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
minPt = mesh.bound_box[0]
|
||||||
|
maxPt = mesh.bound_box[6]
|
||||||
|
for comp in range(3):
|
||||||
|
if minPt[comp] < total_min[comp]:
|
||||||
|
total_min[comp] = minPt[comp]
|
||||||
|
for comp in range(3):
|
||||||
|
if maxPt[comp] > total_max[comp]:
|
||||||
|
total_max[comp] = maxPt[comp]
|
||||||
|
|
||||||
|
elif bpy.context.scene.hecl_type == 'MESH':
|
||||||
|
meshName = bpy.context.scene.hecl_mesh_obj
|
||||||
|
if meshName in bpy.data.objects:
|
||||||
|
mesh = bpy.data.objects[meshName]
|
||||||
|
minPt = mesh.bound_box[0]
|
||||||
|
maxPt = mesh.bound_box[6]
|
||||||
|
for comp in range(3):
|
||||||
|
if minPt[comp] < total_min[comp]:
|
||||||
|
total_min[comp] = minPt[comp]
|
||||||
|
for comp in range(3):
|
||||||
|
if maxPt[comp] > total_max[comp]:
|
||||||
|
total_max[comp] = maxPt[comp]
|
||||||
|
|
||||||
|
writepipebuf(struct.pack('fff', total_min[0], total_min[1], total_min[2]))
|
||||||
|
writepipebuf(struct.pack('fff', total_max[0], total_max[1], total_max[2]))
|
||||||
|
|
||||||
|
def shader_model_update(self, context):
|
||||||
|
value = 0.0
|
||||||
|
if self.hecl_shader_model == 'PBR':
|
||||||
|
value = 1.0
|
||||||
|
bloom_value = 0.0
|
||||||
|
if self.hecl_mp3_bloom:
|
||||||
|
bloom_value = 1.0
|
||||||
|
for shad in ('RetroShader', 'RetroDynamicShader', 'RetroDynamicAlphaShader', 'RetroDynamicCharacterShader'):
|
||||||
|
if shad in bpy.data.node_groups and 'NewShaderModel' in bpy.data.node_groups[shad].nodes:
|
||||||
|
bpy.data.node_groups[shad].nodes['NewShaderModel'].outputs[0].default_value = value
|
||||||
|
for shad in ('RetroShaderMP3',):
|
||||||
|
if shad in bpy.data.node_groups and 'Mix Shader' in bpy.data.node_groups[shad].nodes:
|
||||||
|
bpy.data.node_groups[shad].nodes['Mix Shader'].inputs[0].default_value = bloom_value
|
||||||
|
|
||||||
|
# Load scene callback
|
||||||
|
from bpy.app.handlers import persistent
|
||||||
|
@persistent
|
||||||
|
def scene_loaded(dummy):
|
||||||
|
# Hide everything from an external library
|
||||||
|
if bpy.context.scene.hecl_type != 'FRAME':
|
||||||
|
for o in bpy.context.scene.objects:
|
||||||
|
if o.library or (o.data and o.data.library):
|
||||||
|
o.hide_set(True)
|
||||||
|
|
||||||
|
# Show PATH library objects as wireframes
|
||||||
|
if bpy.context.scene.hecl_type == 'PATH':
|
||||||
|
if bpy.context.scene.background_set:
|
||||||
|
for o in bpy.context.scene.background_set.objects:
|
||||||
|
o.display_type = 'WIRE'
|
||||||
|
if bpy.context.scene.hecl_path_obj in bpy.context.scene.objects:
|
||||||
|
path_obj = bpy.context.scene.objects[bpy.context.scene.hecl_path_obj]
|
||||||
|
path_obj.show_wire = True
|
||||||
|
|
||||||
|
# Linked-Child Detection
|
||||||
|
for scene in bpy.data.scenes:
|
||||||
|
if scene.hecl_type == 'ACTOR':
|
||||||
|
actor_data = scene.hecl_sact_data
|
||||||
|
for subtype in actor_data.subtypes:
|
||||||
|
if subtype.linked_mesh in bpy.data.objects:
|
||||||
|
mesh_obj = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
|
arm_obj = bpy.data.objects[subtype.linked_armature]
|
||||||
|
parent_armature(mesh_obj, arm_obj)
|
||||||
|
for overlay in subtype.overlays:
|
||||||
|
if overlay.linked_mesh in bpy.data.objects:
|
||||||
|
mesh_obj = bpy.data.objects[overlay.linked_mesh]
|
||||||
|
parent_armature(mesh_obj, arm_obj)
|
||||||
|
|
||||||
|
# Show only the active mesh and action
|
||||||
|
if sact.SACTSubtype.SACTSubtype_load.poll(bpy.context):
|
||||||
|
bpy.ops.scene.sactsubtype_load()
|
||||||
|
if sact.SACTAction.SACTAction_load.poll(bpy.context):
|
||||||
|
bpy.ops.scene.sactaction_load()
|
||||||
|
|
||||||
|
shader_model_update(bpy.context.scene, bpy.context)
|
||||||
|
|
||||||
|
def power_of_distance(context, light, dist):
|
||||||
|
color = light.color
|
||||||
|
return dist * dist * context.scene.eevee.light_threshold / max(color[0], max(color[1], color[2]))
|
||||||
|
|
||||||
|
def power_of_coefficients(context, light):
|
||||||
|
epsilon = 1.19e-07
|
||||||
|
if light.hecl_falloff_linear < epsilon and light.hecl_falloff_quadratic < epsilon:
|
||||||
|
return 0.0
|
||||||
|
color = light.color
|
||||||
|
intens = max(color[0], max(color[1], color[2]))
|
||||||
|
if light.hecl_falloff_quadratic > epsilon:
|
||||||
|
if intens <= epsilon:
|
||||||
|
return 0.0
|
||||||
|
return power_of_distance(context, light, math.sqrt(intens / (0.0588235 * light.hecl_falloff_quadratic)))
|
||||||
|
if light.hecl_falloff_linear > epsilon:
|
||||||
|
return power_of_distance(context, light, intens / (0.0588235 * light.hecl_falloff_linear))
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def set_light_falloff(self, context):
|
||||||
|
self.energy = power_of_coefficients(context, self)
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
register_export_type_enum()
|
||||||
|
hmdl.register()
|
||||||
|
sact.register()
|
||||||
|
srea.register()
|
||||||
|
frme.register()
|
||||||
|
mapa.register()
|
||||||
|
mapu.register()
|
||||||
|
path.register()
|
||||||
|
armature.register()
|
||||||
|
bpy.utils.register_class(hecl_scene_panel)
|
||||||
|
bpy.utils.register_class(hecl_light_panel)
|
||||||
|
bpy.types.Scene.hecl_auto_select = bpy.props.BoolProperty(name='HECL Auto Select', default=True)
|
||||||
|
bpy.types.Light.hecl_falloff_constant = bpy.props.FloatProperty(
|
||||||
|
name="HECL Falloff Constant",
|
||||||
|
description="Constant falloff coefficient",
|
||||||
|
update=set_light_falloff,
|
||||||
|
default=1.0,
|
||||||
|
min=0.0)
|
||||||
|
bpy.types.Light.hecl_falloff_linear = bpy.props.FloatProperty(
|
||||||
|
name="HECL Falloff Linear",
|
||||||
|
description="Linear falloff coefficient",
|
||||||
|
update=set_light_falloff,
|
||||||
|
default=0.0,
|
||||||
|
min=0.0)
|
||||||
|
bpy.types.Light.hecl_falloff_quadratic = bpy.props.FloatProperty(
|
||||||
|
name="HECL Falloff Quadratic",
|
||||||
|
description="Quadratic falloff coefficient",
|
||||||
|
update=set_light_falloff,
|
||||||
|
default=0.0,
|
||||||
|
min=0.0)
|
||||||
|
bpy.types.Scene.hecl_shader_model = bpy.props.EnumProperty(name="HECL Shader Model",
|
||||||
|
description="Which shader model to use for rendering",
|
||||||
|
items=[
|
||||||
|
('ORIGINAL', "Original", "Close approximation of GameCube materials"),
|
||||||
|
('PBR', "PBR", "Hybrid PBR materials replacing original reflection")],
|
||||||
|
update=shader_model_update,
|
||||||
|
default='ORIGINAL')
|
||||||
|
bpy.types.Scene.hecl_mp3_bloom = bpy.props.BoolProperty(name="HECL View MP3 Bloom",
|
||||||
|
description="Preview MP3 bloom factors of model",
|
||||||
|
update=shader_model_update,
|
||||||
|
default=False)
|
||||||
|
bpy.app.handlers.load_post.append(scene_loaded)
|
||||||
|
Patching.register()
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.app.handlers.load_post.remove(scene_loaded)
|
||||||
|
hmdl.unregister()
|
||||||
|
sact.unregister()
|
||||||
|
srea.unregister()
|
||||||
|
path.unregister()
|
||||||
|
bpy.utils.unregister_class(hecl_scene_panel)
|
||||||
|
bpy.utils.unregister_class(hecl_light_panel)
|
||||||
|
Patching.unregister()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
register()
|
|
@ -0,0 +1,35 @@
|
||||||
|
import struct
|
||||||
|
|
||||||
|
def cook(writebuf, arm):
|
||||||
|
writebuf(struct.pack('I', len(arm.bones)))
|
||||||
|
for bone in arm.bones:
|
||||||
|
writebuf(struct.pack('I', len(bone.name)))
|
||||||
|
writebuf(bone.name.encode())
|
||||||
|
|
||||||
|
writebuf(struct.pack('fff', bone.head_local[0], bone.head_local[1], bone.head_local[2]))
|
||||||
|
|
||||||
|
if bone.parent:
|
||||||
|
writebuf(struct.pack('i', arm.bones.find(bone.parent.name)))
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('i', -1))
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(bone.children)))
|
||||||
|
for child in bone.children:
|
||||||
|
writebuf(struct.pack('i', arm.bones.find(child.name)))
|
||||||
|
|
||||||
|
def draw(layout, context):
|
||||||
|
layout.prop_search(context.scene, 'hecl_arm_obj', context.scene, 'objects')
|
||||||
|
if not len(context.scene.hecl_arm_obj):
|
||||||
|
layout.label(text="Armature not specified", icon='ERROR')
|
||||||
|
elif context.scene.hecl_arm_obj not in context.scene.objects:
|
||||||
|
layout.label(text="'"+context.scene.hecl_arm_obj+"' not in scene", icon='ERROR')
|
||||||
|
else:
|
||||||
|
obj = context.scene.objects[context.scene.hecl_arm_obj]
|
||||||
|
if obj.type != 'ARMATURE':
|
||||||
|
layout.label(text="'"+context.scene.hecl_arm_obj+"' not an 'ARMATURE'", icon='ERROR')
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
def register():
|
||||||
|
bpy.types.Scene.hecl_arm_obj = bpy.props.StringProperty(
|
||||||
|
name='HECL Armature Object',
|
||||||
|
description='Blender Armature Object to export during HECL\'s cook process')
|
|
@ -0,0 +1,391 @@
|
||||||
|
import bpy, struct, math
|
||||||
|
from mathutils import Quaternion
|
||||||
|
|
||||||
|
def draw(layout, context):
|
||||||
|
if bpy.context.active_object:
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
layout.label(text="Widget Settings:", icon='OBJECT_DATA')
|
||||||
|
layout.prop_menu_enum(obj, 'retro_widget_type', text='Widget Type')
|
||||||
|
#layout.prop_search(obj, 'retro_widget_parent', context.scene, 'objects', text='Widget Parent')
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(obj, 'retro_widget_default_visible', text='Visible')
|
||||||
|
row.prop(obj, 'retro_widget_default_active', text='Active')
|
||||||
|
row.prop(obj, 'retro_widget_cull_faces', text='Cull Faces')
|
||||||
|
layout.prop(obj, 'retro_widget_color', text='Color')
|
||||||
|
layout.prop_menu_enum(obj, 'retro_widget_model_draw_flags', text='Draw Flags')
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(obj, 'retro_widget_is_worker', text='Is Worker')
|
||||||
|
if obj.retro_widget_is_worker:
|
||||||
|
row.prop(obj, 'retro_widget_worker_id', text='Worker Id')
|
||||||
|
|
||||||
|
if obj.retro_widget_type == 'RETRO_MODL':
|
||||||
|
layout.prop(obj, 'retro_model_light_mask', text='Light Mask')
|
||||||
|
elif obj.retro_widget_type == 'RETRO_PANE':
|
||||||
|
layout.prop(obj, 'retro_pane_dimensions', text='Dimensions')
|
||||||
|
layout.prop(obj, 'retro_pane_scale_center', text='Center')
|
||||||
|
elif obj.retro_widget_type == 'RETRO_TXPN':
|
||||||
|
layout.prop(obj, 'retro_pane_dimensions', text='Dimensions')
|
||||||
|
layout.prop(obj, 'retro_pane_scale_center', text='Center')
|
||||||
|
layout.prop(obj, 'retro_textpane_font_path', text='Font Path')
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(obj, 'retro_textpane_word_wrap', text='Word Wrap')
|
||||||
|
row.prop(obj, 'retro_textpane_horizontal', text='Horizontal')
|
||||||
|
layout.prop(obj, 'retro_textpane_fill_color', text='Fill Color')
|
||||||
|
layout.prop(obj, 'retro_textpane_outline_color', text='Outline Color')
|
||||||
|
layout.prop(obj, 'retro_textpane_block_extent', text='Point Dimensions')
|
||||||
|
layout.prop(obj, 'retro_textpane_jp_font_path', text='JP Font Path')
|
||||||
|
layout.prop(obj, 'retro_textpane_jp_font_scale', text='JP Point Dimensions')
|
||||||
|
layout.prop_menu_enum(obj, 'retro_textpane_hjustification', text='Horizontal Justification')
|
||||||
|
layout.prop_menu_enum(obj, 'retro_textpane_vjustification', text='Vertical Justification')
|
||||||
|
elif obj.retro_widget_type == 'RETRO_TBGP':
|
||||||
|
layout.prop(obj, 'retro_tablegroup_elem_count', text='Element Count')
|
||||||
|
layout.prop(obj, 'retro_tablegroup_elem_default', text='Default Element')
|
||||||
|
layout.prop(obj, 'retro_tablegroup_wraparound', text='Wraparound')
|
||||||
|
elif obj.retro_widget_type == 'RETRO_GRUP':
|
||||||
|
layout.prop(obj, 'retro_group_default_worker', text='Default Worker')
|
||||||
|
elif obj.retro_widget_type == 'RETRO_SLGP':
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(obj, 'retro_slider_min', text='Min')
|
||||||
|
row.prop(obj, 'retro_slider_max', text='Max')
|
||||||
|
layout.prop(obj, 'retro_slider_default', text='Default')
|
||||||
|
layout.prop(obj, 'retro_slider_increment', text='Increment')
|
||||||
|
elif obj.retro_widget_type == 'RETRO_ENRG':
|
||||||
|
layout.prop(obj, 'retro_energybar_texture_path', text='Energy Bar Texture Path')
|
||||||
|
elif obj.retro_widget_type == 'RETRO_METR':
|
||||||
|
layout.prop(obj, 'retro_meter_no_round_up', text='No Round Up')
|
||||||
|
layout.prop(obj, 'retro_meter_max_capacity', text='Max Capacity')
|
||||||
|
layout.prop(obj, 'retro_meter_worker_count', text='Worker Count')
|
||||||
|
elif obj.retro_widget_type == 'RETRO_LITE':
|
||||||
|
if obj.data and obj.type == 'LIGHT':
|
||||||
|
layout.prop(obj.data, 'retro_light_index', text='Index')
|
||||||
|
layout.label(text="Angular Falloff:", icon='LIGHT')
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(obj.data, 'retro_light_angle_constant', text='Constant')
|
||||||
|
row.prop(obj.data, 'retro_light_angle_linear', text='Linear')
|
||||||
|
row.prop(obj.data, 'retro_light_angle_quadratic', text='Quadratic')
|
||||||
|
|
||||||
|
hjustifications = None
|
||||||
|
vjustifications = None
|
||||||
|
model_draw_flags_e = None
|
||||||
|
|
||||||
|
def recursive_cook(buffer, obj, version, path_hasher, parent_name):
|
||||||
|
buffer += struct.pack('>4s', obj.retro_widget_type[6:].encode())
|
||||||
|
buffer += obj.name.encode() + b'\0'
|
||||||
|
buffer += parent_name.encode() + b'\0'
|
||||||
|
buffer += struct.pack('>bbbbffffI',
|
||||||
|
False,
|
||||||
|
obj.retro_widget_default_visible,
|
||||||
|
obj.retro_widget_default_active,
|
||||||
|
obj.retro_widget_cull_faces,
|
||||||
|
obj.retro_widget_color[0],
|
||||||
|
obj.retro_widget_color[1],
|
||||||
|
obj.retro_widget_color[2],
|
||||||
|
obj.retro_widget_color[3],
|
||||||
|
model_draw_flags_e[obj.retro_widget_model_draw_flags])
|
||||||
|
|
||||||
|
angle = Quaternion((1.0, 0.0, 0.0), 0)
|
||||||
|
|
||||||
|
if obj.retro_widget_type == 'RETRO_CAMR':
|
||||||
|
angle = Quaternion((1.0, 0.0, 0.0), math.radians(-90.0))
|
||||||
|
aspect = bpy.context.scene.render.resolution_x / bpy.context.scene.render.resolution_y
|
||||||
|
|
||||||
|
if obj.data.type == 'PERSP':
|
||||||
|
if aspect > 1.0:
|
||||||
|
fov = math.degrees(math.atan(math.tan(obj.data.angle / 2.0) / aspect)) * 2.0
|
||||||
|
else:
|
||||||
|
fov = math.degrees(obj.data.angle)
|
||||||
|
buffer += struct.pack('>Iffff', 0, fov, aspect, obj.data.clip_start, obj.data.clip_end)
|
||||||
|
|
||||||
|
elif obj.data.type == 'ORTHO':
|
||||||
|
ortho_half = obj.data.ortho_scale / 2.0
|
||||||
|
buffer += struct.pack('>Iffffff', 1, -ortho_half, ortho_half, ortho_half / aspect,
|
||||||
|
-ortho_half / aspect, obj.data.clip_start, obj.data.clip_end)
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_MODL':
|
||||||
|
if len(obj.children) == 0:
|
||||||
|
raise RuntimeException('Model Widget must have a child model object')
|
||||||
|
model_obj = obj.children[0]
|
||||||
|
if model_obj.type != 'MESH':
|
||||||
|
raise RuntimeException('Model Widget must have a child MESH')
|
||||||
|
if not model_obj.data.library:
|
||||||
|
raise RuntimeException('Model Widget must have a linked library MESH')
|
||||||
|
path = bpy.path.abspath(model_obj.data.library.filepath)
|
||||||
|
path_hash = path_hasher.hashpath32(path)
|
||||||
|
buffer += struct.pack('>III', path_hash, 0, obj.retro_model_light_mask)
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_PANE':
|
||||||
|
buffer += struct.pack('>fffff',
|
||||||
|
obj.retro_pane_dimensions[0],
|
||||||
|
obj.retro_pane_dimensions[1],
|
||||||
|
obj.retro_pane_scale_center[0],
|
||||||
|
obj.retro_pane_scale_center[1],
|
||||||
|
obj.retro_pane_scale_center[2])
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_TXPN':
|
||||||
|
path_hash = path_hasher.hashpath32(obj.retro_textpane_font_path)
|
||||||
|
buffer += struct.pack('>fffffIbbIIffffffffff',
|
||||||
|
obj.retro_pane_dimensions[0],
|
||||||
|
obj.retro_pane_dimensions[1],
|
||||||
|
obj.retro_pane_scale_center[0],
|
||||||
|
obj.retro_pane_scale_center[1],
|
||||||
|
obj.retro_pane_scale_center[2],
|
||||||
|
path_hash,
|
||||||
|
obj.retro_textpane_word_wrap,
|
||||||
|
obj.retro_textpane_horizontal,
|
||||||
|
hjustifications[obj.retro_textpane_hjustification],
|
||||||
|
vjustifications[obj.retro_textpane_vjustification],
|
||||||
|
obj.retro_textpane_fill_color[0],
|
||||||
|
obj.retro_textpane_fill_color[1],
|
||||||
|
obj.retro_textpane_fill_color[2],
|
||||||
|
obj.retro_textpane_fill_color[3],
|
||||||
|
obj.retro_textpane_outline_color[0],
|
||||||
|
obj.retro_textpane_outline_color[1],
|
||||||
|
obj.retro_textpane_outline_color[2],
|
||||||
|
obj.retro_textpane_outline_color[3],
|
||||||
|
obj.retro_textpane_block_extent[0],
|
||||||
|
obj.retro_textpane_block_extent[1])
|
||||||
|
if version >= 1:
|
||||||
|
path_hash = path_hasher.hashpath32(obj.retro_textpane_jp_font_path)
|
||||||
|
buffer += struct.pack('>III',
|
||||||
|
path_hash,
|
||||||
|
obj.retro_textpane_jp_font_scale[0],
|
||||||
|
obj.retro_textpane_jp_font_scale[1])
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_TBGP':
|
||||||
|
buffer += struct.pack('>HHIHHbbffbfHHHH',
|
||||||
|
obj.retro_tablegroup_elem_count,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
obj.retro_tablegroup_elem_default,
|
||||||
|
0,
|
||||||
|
obj.retro_tablegroup_wraparound,
|
||||||
|
False,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
False,
|
||||||
|
0.0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0)
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_GRUP':
|
||||||
|
buffer += struct.pack('>Hb',
|
||||||
|
obj.retro_group_default_worker,
|
||||||
|
False)
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_SLGP':
|
||||||
|
buffer += struct.pack('>ffff',
|
||||||
|
obj.retro_slider_min,
|
||||||
|
obj.retro_slider_max,
|
||||||
|
obj.retro_slider_default,
|
||||||
|
obj.retro_slider_increment)
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_ENRG':
|
||||||
|
path_hash = path_hasher.hashpath32(obj.retro_energybar_texture_path)
|
||||||
|
buffer += struct.pack('>I', path_hash)
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_METR':
|
||||||
|
buffer += struct.pack('>bbII',
|
||||||
|
False,
|
||||||
|
obj.retro_meter_no_round_up,
|
||||||
|
obj.retro_meter_max_capacity,
|
||||||
|
obj.retro_meter_worker_count)
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_LITE':
|
||||||
|
angle = Quaternion((1.0, 0.0, 0.0), math.radians(-90.0))
|
||||||
|
type_enum = 0
|
||||||
|
constant = 1.0
|
||||||
|
linear = 0.0
|
||||||
|
quadratic = 0.0
|
||||||
|
cutoff = 0.0
|
||||||
|
if obj.data.type == 'POINT':
|
||||||
|
type_enum = 4
|
||||||
|
elif obj.data.type == 'SUN':
|
||||||
|
type_enum = 2
|
||||||
|
elif obj.data.type == 'SPOT':
|
||||||
|
type_enum = 0
|
||||||
|
cutoff = obj.data.spot_size
|
||||||
|
|
||||||
|
if obj.data.type == 'POINT' or obj.data.type == 'SPOT':
|
||||||
|
constant = obj.data.constant_coefficient
|
||||||
|
linear = obj.data.linear_coefficient
|
||||||
|
quadratic = obj.data.quadratic_coefficient
|
||||||
|
|
||||||
|
buffer += struct.pack('>IffffffI',
|
||||||
|
type_enum, constant, linear, quadratic,
|
||||||
|
obj.data.retro_light_angle_constant,
|
||||||
|
obj.data.retro_light_angle_linear,
|
||||||
|
obj.data.retro_light_angle_quadratic,
|
||||||
|
obj.data.retro_light_index)
|
||||||
|
if obj.data.type == 'SPOT':
|
||||||
|
buffer += struct.pack('>f', cutoff)
|
||||||
|
|
||||||
|
elif obj.retro_widget_type == 'RETRO_IMGP':
|
||||||
|
if obj.type != 'MESH':
|
||||||
|
raise RuntimeException('Imagepane Widget must be a MESH')
|
||||||
|
if len(obj.data.loops) < 4:
|
||||||
|
raise RuntimeException('Imagepane Widget must be a MESH with 4 verts')
|
||||||
|
if len(obj.data.uv_layers) < 1:
|
||||||
|
raise RuntimeException('Imagepane Widget must ba a MESH with a UV layer')
|
||||||
|
path_hash = 0xffffffff
|
||||||
|
if len(obj.data.materials):
|
||||||
|
material = obj.data.materials[0]
|
||||||
|
if 'Image Texture' in material.node_tree.nodes:
|
||||||
|
image_node = material.node_tree.nodes['Image Texture']
|
||||||
|
if image_node.image:
|
||||||
|
image = image_node.image
|
||||||
|
path = bpy.path.abspath(image.filepath)
|
||||||
|
path_hash = path_hasher.hashpath32(path)
|
||||||
|
|
||||||
|
buffer += struct.pack('>IIII', path_hash, 0, 0, 4)
|
||||||
|
for i in range(4):
|
||||||
|
vi = obj.data.loops[i].vertex_index
|
||||||
|
co = obj.data.vertices[vi].co
|
||||||
|
buffer += struct.pack('>fff', co[0], co[1], co[2])
|
||||||
|
|
||||||
|
buffer += struct.pack('>I', 4)
|
||||||
|
for i in range(4):
|
||||||
|
co = obj.data.uv_layers[0].data[i].uv
|
||||||
|
buffer += struct.pack('>ff', co[0], co[1])
|
||||||
|
|
||||||
|
if obj.retro_widget_is_worker:
|
||||||
|
buffer += struct.pack('>bH', True, obj.retro_widget_worker_id)
|
||||||
|
else:
|
||||||
|
buffer += struct.pack('>b', False)
|
||||||
|
|
||||||
|
angMtx = angle.to_matrix() @ obj.matrix_local.to_3x3()
|
||||||
|
buffer += struct.pack('>fffffffffffffffIH',
|
||||||
|
obj.matrix_local[0][3],
|
||||||
|
obj.matrix_local[1][3],
|
||||||
|
obj.matrix_local[2][3],
|
||||||
|
angMtx[0][0], angMtx[0][1], angMtx[0][2],
|
||||||
|
angMtx[1][0], angMtx[1][1], angMtx[1][2],
|
||||||
|
angMtx[2][0], angMtx[2][1], angMtx[2][2],
|
||||||
|
0.0, 0.0, 0.0, 0, 0)
|
||||||
|
|
||||||
|
ch_list = []
|
||||||
|
for ch in obj.children:
|
||||||
|
ch_list.append((ch.pass_index, ch.name))
|
||||||
|
for s_pair in sorted(ch_list):
|
||||||
|
ch = bpy.data.objects[s_pair[1]]
|
||||||
|
if ch.retro_widget_type != 'RETRO_NONE':
|
||||||
|
recursive_cook(buffer, ch, version, path_hasher, obj.name)
|
||||||
|
|
||||||
|
|
||||||
|
def cook(writepipebuf, version, path_hasher):
|
||||||
|
global hjustifications, vjustifications, model_draw_flags_e
|
||||||
|
hjustifications = dict((i[0], i[3]) for i in bpy.types.Object.retro_textpane_hjustification[1]['items'])
|
||||||
|
vjustifications = dict((i[0], i[3]) for i in bpy.types.Object.retro_textpane_vjustification[1]['items'])
|
||||||
|
model_draw_flags_e = dict((i[0], i[3]) for i in bpy.types.Object.retro_widget_model_draw_flags[1]['items'])
|
||||||
|
|
||||||
|
buffer = bytearray()
|
||||||
|
buffer += struct.pack('>IIII', 0, 0, 0, 0)
|
||||||
|
|
||||||
|
widget_count = 0
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
if obj.retro_widget_type != 'RETRO_NONE':
|
||||||
|
widget_count += 1
|
||||||
|
buffer += struct.pack('>I', widget_count)
|
||||||
|
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
if obj.retro_widget_type != 'RETRO_NONE' and not obj.parent:
|
||||||
|
recursive_cook(buffer, obj, version, path_hasher, 'kGSYS_DummyWidgetID')
|
||||||
|
|
||||||
|
return buffer
|
||||||
|
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
frame_widget_types = [
|
||||||
|
('RETRO_NONE', 'Not a Widget', '', 0),
|
||||||
|
('RETRO_BWIG', 'Base Widget', '', 1),
|
||||||
|
('RETRO_CAMR', 'Camera', '', 2),
|
||||||
|
('RETRO_ENRG', 'Energy Bar', '', 3),
|
||||||
|
('RETRO_GRUP', 'Group', '', 4),
|
||||||
|
('RETRO_HWIG', 'Head Widget', '', 5),
|
||||||
|
('RETRO_IMGP', 'Image Pane', '', 6),
|
||||||
|
('RETRO_LITE', 'Light', '', 7),
|
||||||
|
('RETRO_MODL', 'Model', '', 8),
|
||||||
|
('RETRO_METR', 'Meter', '', 9),
|
||||||
|
('RETRO_PANE', 'Pane', '', 10),
|
||||||
|
('RETRO_SLGP', 'Slider Group', '', 11),
|
||||||
|
('RETRO_TBGP', 'Table Group', '', 12),
|
||||||
|
('RETRO_TXPN', 'Text Pane', '', 13)]
|
||||||
|
bpy.types.Object.retro_widget_type = bpy.props.EnumProperty(items=frame_widget_types, name='Retro: FRME Widget Type', default='RETRO_NONE')
|
||||||
|
model_draw_flags = [
|
||||||
|
('RETRO_SHADELESS', 'Shadeless', '', 0),
|
||||||
|
('RETRO_OPAQUE', 'Opaque', '', 1),
|
||||||
|
('RETRO_ALPHA', 'Alpha', '', 2),
|
||||||
|
('RETRO_ADDITIVE', 'Additive', '', 3),
|
||||||
|
('RETRO_ALPHA_ADDITIVE_OVERDRAW', 'Alpha Additive Overdraw', '', 4)]
|
||||||
|
bpy.types.Object.retro_widget_parent = bpy.props.StringProperty(name='Retro: FRME Widget Parent', description='Refers to internal frame widgets')
|
||||||
|
bpy.types.Object.retro_widget_use_anim_controller = bpy.props.BoolProperty(name='Retro: Use Animiation Conroller')
|
||||||
|
bpy.types.Object.retro_widget_default_visible = bpy.props.BoolProperty(name='Retro: Default Visible', description='Sets widget is visible by default')
|
||||||
|
bpy.types.Object.retro_widget_default_active = bpy.props.BoolProperty(name='Retro: Default Active', description='Sets widget is cases by default')
|
||||||
|
bpy.types.Object.retro_widget_cull_faces = bpy.props.BoolProperty(name='Retro: Cull Faces', description='Enables face culling')
|
||||||
|
bpy.types.Object.retro_widget_color = bpy.props.FloatVectorProperty(name='Retro: Color', description='Sets widget color', subtype='COLOR', size=4, min=0.0, max=1.0)
|
||||||
|
bpy.types.Object.retro_widget_model_draw_flags = bpy.props.EnumProperty(items=model_draw_flags, name='Retro: Model Draw Flags', default='RETRO_ALPHA')
|
||||||
|
bpy.types.Object.retro_widget_is_worker = bpy.props.BoolProperty(name='Retro: Is Worker Widget', default=False)
|
||||||
|
bpy.types.Object.retro_widget_worker_id = bpy.props.IntProperty(name='Retro: Worker Widget ID', min=0, default=0)
|
||||||
|
|
||||||
|
bpy.types.Object.retro_model_light_mask = bpy.props.IntProperty(name='Retro: Model Light Mask', min=0, default=0)
|
||||||
|
|
||||||
|
bpy.types.Object.retro_pane_dimensions = bpy.props.FloatVectorProperty(name='Retro: Pane Dimensions', min=0.0, size=2)
|
||||||
|
bpy.types.Object.retro_pane_scale_center = bpy.props.FloatVectorProperty(name='Retro: Scale Center', size=3)
|
||||||
|
|
||||||
|
bpy.types.Object.retro_textpane_font_path = bpy.props.StringProperty(name='Retro: Font Path')
|
||||||
|
bpy.types.Object.retro_textpane_word_wrap = bpy.props.BoolProperty(name='Retro: Word Wrap')
|
||||||
|
bpy.types.Object.retro_textpane_horizontal = bpy.props.BoolProperty(name='Retro: Horizontal', default=True)
|
||||||
|
bpy.types.Object.retro_textpane_fill_color = bpy.props.FloatVectorProperty(name='Retro: Fill Color', min=0.0, max=1.0, size=4, subtype='COLOR')
|
||||||
|
bpy.types.Object.retro_textpane_outline_color = bpy.props.FloatVectorProperty(name='Retro: Outline Color', min=0.0, max=1.0, size=4, subtype='COLOR')
|
||||||
|
bpy.types.Object.retro_textpane_block_extent = bpy.props.FloatVectorProperty(name='Retro: Block Extent', min=0.0, size=2)
|
||||||
|
bpy.types.Object.retro_textpane_jp_font_path = bpy.props.StringProperty(name='Retro: Japanese Font Path')
|
||||||
|
bpy.types.Object.retro_textpane_jp_font_scale = bpy.props.IntVectorProperty(name='Retro: Japanese Font Scale', min=0, size=2)
|
||||||
|
frame_textpane_hjustifications = [
|
||||||
|
('LEFT', 'Left', '', 0),
|
||||||
|
('CENTER', 'Center', '', 1),
|
||||||
|
('RIGHT', 'Right', '', 2),
|
||||||
|
('FULL', 'Full', '', 3),
|
||||||
|
('NLEFT', 'Left Normalized', '', 4),
|
||||||
|
('NCENTER', 'Center Normalized', '', 5),
|
||||||
|
('NRIGHT', 'Right Normalized', '', 6),
|
||||||
|
('LEFTMONO', 'Left Monospaced', '', 7),
|
||||||
|
('CENTERMONO', 'Center Monospaced', '', 8),
|
||||||
|
('RIGHTMONO', 'Right Monospaced', '', 9)]
|
||||||
|
bpy.types.Object.retro_textpane_hjustification = bpy.props.EnumProperty(items=frame_textpane_hjustifications, name='Retro: Horizontal Justification', default='LEFT')
|
||||||
|
frame_textpane_vjustifications = [
|
||||||
|
('TOP', 'Top', '', 0),
|
||||||
|
('CENTER', 'Center', '', 1),
|
||||||
|
('BOTTOM', 'Bottom', '', 2),
|
||||||
|
('FULL', 'Full', '', 3),
|
||||||
|
('NTOP', 'Top Normalized', '', 4),
|
||||||
|
('NCENTER', 'Center Normalized', '', 5),
|
||||||
|
('NBOTTOM', 'Bottom Normalized', '', 6),
|
||||||
|
('TOPMONO', 'Top Monospaced', '', 7),
|
||||||
|
('CENTERMONO', 'Center Monospaced', '', 8),
|
||||||
|
('BOTTOMMONO', 'Bottom Monospaced', '', 9)]
|
||||||
|
bpy.types.Object.retro_textpane_vjustification = bpy.props.EnumProperty(items=frame_textpane_vjustifications, name='Retro: Vertical Justification', default='TOP')
|
||||||
|
|
||||||
|
bpy.types.Object.retro_tablegroup_elem_count = bpy.props.IntProperty(name='Retro: Table Group Element Count', min=0, default=0)
|
||||||
|
bpy.types.Object.retro_tablegroup_elem_default = bpy.props.IntProperty(name='Retro: Table Group Default Element', min=0, default=0)
|
||||||
|
bpy.types.Object.retro_tablegroup_wraparound = bpy.props.BoolProperty(name='Retro: Table Group Wraparound', default=False)
|
||||||
|
|
||||||
|
bpy.types.Object.retro_group_default_worker = bpy.props.IntProperty(name='Retro: Group Default Worker', min=0, default=0)
|
||||||
|
|
||||||
|
bpy.types.Object.retro_slider_min = bpy.props.FloatProperty(name='Retro: Slider Min', default=0.0)
|
||||||
|
bpy.types.Object.retro_slider_max = bpy.props.FloatProperty(name='Retro: Slider Max', default=1.0)
|
||||||
|
bpy.types.Object.retro_slider_default = bpy.props.FloatProperty(name='Retro: Slider Default', default=0.0)
|
||||||
|
bpy.types.Object.retro_slider_increment = bpy.props.FloatProperty(name='Retro: Slider Increment', min=0.0, default=1.0)
|
||||||
|
|
||||||
|
bpy.types.Object.retro_energybar_texture_path = bpy.props.StringProperty(name='Retro: Energy Bar Texture Path')
|
||||||
|
|
||||||
|
bpy.types.Object.retro_meter_no_round_up = bpy.props.BoolProperty(name='Retro: No Round Up', default=True)
|
||||||
|
bpy.types.Object.retro_meter_max_capacity = bpy.props.IntProperty(name='Retro: Max Capacity', min=0, default=100)
|
||||||
|
bpy.types.Object.retro_meter_worker_count = bpy.props.IntProperty(name='Retro: Worker Count', min=0, default=1)
|
||||||
|
|
||||||
|
bpy.types.Light.retro_light_index = bpy.props.IntProperty(name='Retro: Light Index', min=0, default=0)
|
||||||
|
bpy.types.Light.retro_light_angle_constant = bpy.props.FloatProperty(name='Retro: Light Angle Constant', min=0.0, default=0.0)
|
||||||
|
bpy.types.Light.retro_light_angle_linear = bpy.props.FloatProperty(name='Retro: Light Angle Linear', min=0.0, default=0.0)
|
||||||
|
bpy.types.Light.retro_light_angle_quadratic = bpy.props.FloatProperty(name='Retro: Light Angle Quadratic', min=0.0, default=0.0)
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import bpy, bmesh, operator, struct
|
||||||
|
|
||||||
|
# Function to quantize normals to 15-bit precision
|
||||||
|
def quant_norm(n):
|
||||||
|
nf = n.copy()
|
||||||
|
for i in range(3):
|
||||||
|
nf[i] = int(nf[i] * 16384) / 16384.0
|
||||||
|
return nf.freeze()
|
||||||
|
|
||||||
|
# Function to quantize lightmap UVs to 15-bit precision
|
||||||
|
def quant_luv(n):
|
||||||
|
uf = n.copy()
|
||||||
|
for i in range(2):
|
||||||
|
uf[i] = int(uf[i] * 32768) / 32768.0
|
||||||
|
return uf.freeze()
|
||||||
|
|
||||||
|
# Function to output all mesh attribute values
|
||||||
|
def write_mesh_attrs(writebuf, bm, rna_loops, use_luv, material_slots):
|
||||||
|
dlay = None
|
||||||
|
if len(bm.verts.layers.deform):
|
||||||
|
dlay = bm.verts.layers.deform[0]
|
||||||
|
|
||||||
|
clays = []
|
||||||
|
for cl in range(len(bm.loops.layers.color)):
|
||||||
|
clays.append(bm.loops.layers.color[cl])
|
||||||
|
writebuf(struct.pack('I', len(clays)))
|
||||||
|
|
||||||
|
luvlay = None
|
||||||
|
if use_luv:
|
||||||
|
luvlay = bm.loops.layers.uv[0]
|
||||||
|
ulays = []
|
||||||
|
for ul in range(len(bm.loops.layers.uv)):
|
||||||
|
ulays.append(bm.loops.layers.uv[ul])
|
||||||
|
writebuf(struct.pack('I', len(ulays)))
|
||||||
|
|
||||||
|
# Verts
|
||||||
|
writebuf(struct.pack('I', len(bm.verts)))
|
||||||
|
for v in bm.verts:
|
||||||
|
writebuf(struct.pack('fff', v.co[0], v.co[1], v.co[2]))
|
||||||
|
if dlay:
|
||||||
|
sf = tuple(sorted(v[dlay].items()))
|
||||||
|
writebuf(struct.pack('I', len(sf)))
|
||||||
|
total_len = 0.0
|
||||||
|
for ent in sf:
|
||||||
|
total_len += ent[1]
|
||||||
|
for ent in sf:
|
||||||
|
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
# Loops
|
||||||
|
loop_count = 0
|
||||||
|
for f in bm.faces:
|
||||||
|
for l in f.loops:
|
||||||
|
loop_count += 1
|
||||||
|
writebuf(struct.pack('I', loop_count))
|
||||||
|
for f in bm.faces:
|
||||||
|
for l in f.loops:
|
||||||
|
if rna_loops:
|
||||||
|
nf = quant_norm(rna_loops[l.index].normal)
|
||||||
|
else:
|
||||||
|
nf = quant_norm(l.vert.normal)
|
||||||
|
writebuf(struct.pack('fff', nf[0], nf[1], nf[2]))
|
||||||
|
for cl in range(len(clays)):
|
||||||
|
col = l[clays[cl]]
|
||||||
|
writebuf(struct.pack('fff', col[0], col[1], col[2]))
|
||||||
|
for cl in range(len(ulays)):
|
||||||
|
if luvlay and cl == 0 and material_slots[l.face.material_index].material['retro_lightmapped']:
|
||||||
|
uv = quant_luv(l[luvlay].uv)
|
||||||
|
else:
|
||||||
|
uv = l[ulays[cl]].uv
|
||||||
|
writebuf(struct.pack('ff', uv[0], uv[1]))
|
||||||
|
writebuf(struct.pack('IIIII', l.vert.index, l.edge.index, l.face.index,
|
||||||
|
l.link_loop_next.index, l.link_loop_prev.index))
|
||||||
|
if l.edge.is_contiguous:
|
||||||
|
writebuf(struct.pack('II', l.link_loop_radial_next.index, l.link_loop_radial_prev.index))
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('II', 0xffffffff, 0xffffffff))
|
||||||
|
|
||||||
|
# Edges
|
||||||
|
writebuf(struct.pack('I', len(bm.edges)))
|
||||||
|
for e in bm.edges:
|
||||||
|
for v in e.verts:
|
||||||
|
writebuf(struct.pack('I', v.index))
|
||||||
|
writebuf(struct.pack('I', len(e.link_faces)))
|
||||||
|
for f in e.link_faces:
|
||||||
|
writebuf(struct.pack('I', f.index))
|
||||||
|
writebuf(struct.pack('b', e.is_contiguous))
|
||||||
|
|
||||||
|
# Faces
|
||||||
|
writebuf(struct.pack('I', len(bm.faces)))
|
||||||
|
for f in bm.faces:
|
||||||
|
norm = f.normal
|
||||||
|
writebuf(struct.pack('fff', norm[0], norm[1], norm[2]))
|
||||||
|
centroid = f.calc_center_bounds()
|
||||||
|
writebuf(struct.pack('fff', centroid[0], centroid[1], centroid[2]))
|
||||||
|
writebuf(struct.pack('I', f.material_index))
|
||||||
|
for l in f.loops:
|
||||||
|
writebuf(struct.pack('I', l.index))
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
import bpy, bpy.path, os.path, struct
|
||||||
|
|
||||||
|
def get_texture_path(image):
|
||||||
|
return os.path.normpath(bpy.path.abspath(image.filepath))
|
||||||
|
|
||||||
|
SHADER_TYPES = {
|
||||||
|
'RetroShader': b'RSHD',
|
||||||
|
'RetroDynamicShader': b'RDYN',
|
||||||
|
'RetroDynamicAlphaShader': b'RDAL',
|
||||||
|
'RetroDynamicCharacterShader': b'RCHR',
|
||||||
|
}
|
||||||
|
|
||||||
|
PASS_TYPE = {
|
||||||
|
'Lightmap': b'LMAP',
|
||||||
|
'Diffuse': b'DIFF',
|
||||||
|
'DiffuseMod': b'DIFM',
|
||||||
|
'Emissive': b'EMIS',
|
||||||
|
'Specular': b'SPEC',
|
||||||
|
'ExtendedSpecular': b'ESPC',
|
||||||
|
'Reflection': b'REFL',
|
||||||
|
'IndirectTex': b'INDR',
|
||||||
|
'Alpha': b'ALPH',
|
||||||
|
'AlphaMod': b'ALPM'
|
||||||
|
}
|
||||||
|
|
||||||
|
def write_chunks(writebuf, mat_obj, mesh_obj):
|
||||||
|
|
||||||
|
if not mat_obj.use_nodes:
|
||||||
|
raise RuntimeError("HMDL *requires* that shader nodes are used; '{0}' does not".format(mat_obj.name))
|
||||||
|
|
||||||
|
if 'Output' not in mat_obj.node_tree.nodes or mat_obj.node_tree.nodes['Output'].type != 'GROUP':
|
||||||
|
raise RuntimeError("HMDL *requires* that an group shader node named 'Output' is present")
|
||||||
|
|
||||||
|
# Root (output) node
|
||||||
|
output_node = mat_obj.node_tree.nodes['Output']
|
||||||
|
if output_node.node_tree.name not in SHADER_TYPES:
|
||||||
|
raise RuntimeError("HMDL *requires* one of the RetroShader group nodes for the 'Output' node")
|
||||||
|
writebuf(SHADER_TYPES[output_node.node_tree.name])
|
||||||
|
|
||||||
|
# Count sockets
|
||||||
|
chunk_count = 0
|
||||||
|
for inp, def_inp in zip(output_node.inputs, output_node.node_tree.inputs):
|
||||||
|
if inp.name in PASS_TYPE:
|
||||||
|
if inp.is_linked:
|
||||||
|
chunk_count += 1
|
||||||
|
else:
|
||||||
|
# Color pass
|
||||||
|
color_set = False
|
||||||
|
if inp.type == 'VALUE':
|
||||||
|
color_set = inp.default_value != def_inp.default_value
|
||||||
|
else:
|
||||||
|
for comp, def_comp in zip(inp.default_value, def_inp.default_value):
|
||||||
|
color_set |= comp != def_comp
|
||||||
|
if color_set:
|
||||||
|
chunk_count += 1
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', chunk_count))
|
||||||
|
|
||||||
|
# Enumerate sockets
|
||||||
|
for inp, def_inp in zip(output_node.inputs, output_node.node_tree.inputs):
|
||||||
|
if inp.name in PASS_TYPE:
|
||||||
|
pass_fourcc = PASS_TYPE[inp.name]
|
||||||
|
if inp.is_linked:
|
||||||
|
socket = inp.links[0].from_socket
|
||||||
|
node = socket.node
|
||||||
|
if node.type != 'TEX_IMAGE':
|
||||||
|
raise RuntimeError("HMDL requires all group node inputs connect to Image Texture nodes")
|
||||||
|
|
||||||
|
if not node.image:
|
||||||
|
raise RuntimeError("HMDL texture nodes must specify an image object")
|
||||||
|
|
||||||
|
if not node.inputs['Vector'].is_linked:
|
||||||
|
raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
|
||||||
|
|
||||||
|
# Determine matrix generator type
|
||||||
|
tex_coord_source = 0xff
|
||||||
|
uv_anim_type = 0xff
|
||||||
|
uv_anim_args = []
|
||||||
|
soc_from = node.inputs['Vector'].links[0].from_socket
|
||||||
|
|
||||||
|
if soc_from.node.type == 'GROUP':
|
||||||
|
if soc_from.node.node_tree.name.startswith('RetroUVMode'):
|
||||||
|
uv_anim_type = int(soc_from.node.node_tree.name[11:12])
|
||||||
|
if len(soc_from.node.inputs)-1:
|
||||||
|
for s in range(len(soc_from.node.inputs)-1):
|
||||||
|
soc = soc_from.node.inputs[s+1]
|
||||||
|
if len(soc.links):
|
||||||
|
raise RuntimeError("UV Modifier nodes may not have parameter links (default values only)")
|
||||||
|
if soc.type == 'VALUE':
|
||||||
|
uv_anim_args.append(soc.default_value)
|
||||||
|
else:
|
||||||
|
uv_anim_args.append(soc.default_value[0])
|
||||||
|
uv_anim_args.append(soc.default_value[1])
|
||||||
|
soc_from = soc_from.node.inputs[0].links[0].from_socket
|
||||||
|
|
||||||
|
elif soc_from.node.type == 'UVMAP' or soc_from.node.type == 'TEX_COORD':
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
|
||||||
|
|
||||||
|
if soc_from.node.type != 'UVMAP' and soc_from.node.type != 'TEX_COORD':
|
||||||
|
raise RuntimeError("Matrix animator nodes must connect to 'Texture Coordinate' or 'UV Map' node")
|
||||||
|
|
||||||
|
|
||||||
|
# Resolve map and matrix index
|
||||||
|
node_label = soc_from.node.label
|
||||||
|
matrix_idx = None
|
||||||
|
if node_label.startswith('MTX_'):
|
||||||
|
matrix_idx = int(node_label[4:])
|
||||||
|
|
||||||
|
if soc_from.name == 'UV':
|
||||||
|
if hasattr(soc_from.node, 'uv_map'):
|
||||||
|
uv_name = soc_from.node.uv_map
|
||||||
|
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
|
||||||
|
if uv_idx == -1:
|
||||||
|
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
|
||||||
|
tex_coord_source = uv_idx + 2
|
||||||
|
else:
|
||||||
|
tex_coord_source = 2
|
||||||
|
|
||||||
|
elif soc_from.name == 'Normal':
|
||||||
|
tex_coord_source = 1
|
||||||
|
|
||||||
|
elif soc_from.name == 'Window':
|
||||||
|
tex_coord_source = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Only the 'UV', 'Normal' and 'Window' sockets may be used from 'Texture Coordinate' nodes")
|
||||||
|
|
||||||
|
alpha = False
|
||||||
|
if socket.name == 'Alpha':
|
||||||
|
alpha = True
|
||||||
|
|
||||||
|
writebuf(b'PASS')
|
||||||
|
writebuf(pass_fourcc)
|
||||||
|
path = get_texture_path(node.image)
|
||||||
|
writebuf(struct.pack('I', len(path)))
|
||||||
|
writebuf(path.encode())
|
||||||
|
writebuf(struct.pack('B', tex_coord_source))
|
||||||
|
writebuf(struct.pack('B', uv_anim_type))
|
||||||
|
writebuf(struct.pack('I', len(uv_anim_args)))
|
||||||
|
for arg in uv_anim_args:
|
||||||
|
writebuf(struct.pack('f', arg))
|
||||||
|
writebuf(struct.pack('B', alpha))
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Color pass
|
||||||
|
color_set = False
|
||||||
|
if inp.type == 'VALUE':
|
||||||
|
color_set = inp.default_value != def_inp.default_value
|
||||||
|
else:
|
||||||
|
for comp, def_comp in zip(inp.default_value, def_inp.default_value):
|
||||||
|
color_set |= comp != def_comp
|
||||||
|
|
||||||
|
if color_set:
|
||||||
|
writebuf(b'CLR ')
|
||||||
|
writebuf(pass_fourcc)
|
||||||
|
if inp.type == 'VALUE':
|
||||||
|
writebuf(struct.pack('ffff', inp.default_value, inp.default_value,
|
||||||
|
inp.default_value, inp.default_value))
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('ffff', inp.default_value[0], inp.default_value[1],
|
||||||
|
inp.default_value[2], inp.default_value[3]))
|
|
@ -0,0 +1,312 @@
|
||||||
|
import struct, bpy, bmesh
|
||||||
|
from . import HMDLShader, HMDLMesh
|
||||||
|
|
||||||
|
BLEND_TYPES = {
|
||||||
|
'HECLAdditiveOutput': 2,
|
||||||
|
'HECLBlendOutput': 1,
|
||||||
|
'HECLOpaqueOutput': 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
def write_out_material(writebuf, mat, mesh_obj):
|
||||||
|
writebuf(struct.pack('I', len(mat.name)))
|
||||||
|
writebuf(mat.name.encode())
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', mat.pass_index))
|
||||||
|
|
||||||
|
HMDLShader.write_chunks(writebuf, mat, mesh_obj)
|
||||||
|
|
||||||
|
prop_count = 0
|
||||||
|
for prop in mat.items():
|
||||||
|
if isinstance(prop[1], int):
|
||||||
|
prop_count += 1
|
||||||
|
writebuf(struct.pack('I', prop_count))
|
||||||
|
for prop in mat.items():
|
||||||
|
if isinstance(prop[1], int):
|
||||||
|
writebuf(struct.pack('I', len(prop[0])))
|
||||||
|
writebuf(prop[0].encode())
|
||||||
|
writebuf(struct.pack('i', prop[1]))
|
||||||
|
|
||||||
|
blend_node = mat.node_tree.nodes['Blend']
|
||||||
|
if blend_node.node_tree.name not in BLEND_TYPES:
|
||||||
|
raise RuntimeError("HMDL *requires* one of the HMDL*Output group nodes for the 'Blend' node")
|
||||||
|
writebuf(struct.pack('I', BLEND_TYPES[blend_node.node_tree.name]))
|
||||||
|
|
||||||
|
# Takes a Blender 'Mesh' object (not the datablock)
|
||||||
|
# and performs a one-shot conversion process to HMDL
|
||||||
|
def cook(writebuf, mesh_obj, use_luv=False):
|
||||||
|
if mesh_obj.type != 'MESH':
|
||||||
|
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
||||||
|
|
||||||
|
# Copy mesh (and apply mesh modifiers with triangulation)
|
||||||
|
copy_name = mesh_obj.name + "_hmdltri"
|
||||||
|
copy_mesh = bpy.data.meshes.new_from_object(mesh_obj, preserve_all_data_layers=True,
|
||||||
|
depsgraph=bpy.context.evaluated_depsgraph_get())
|
||||||
|
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
||||||
|
copy_obj.scale = mesh_obj.scale
|
||||||
|
bpy.context.scene.collection.objects.link(copy_obj)
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.context.view_layer.objects.active = copy_obj
|
||||||
|
copy_obj.select_set(True)
|
||||||
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
bpy.ops.mesh.select_all(action='SELECT')
|
||||||
|
bpy.ops.mesh.quads_convert_to_tris()
|
||||||
|
bpy.ops.mesh.select_all(action='DESELECT')
|
||||||
|
bpy.context.scene.update_tag()
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
copy_mesh.calc_normals_split()
|
||||||
|
rna_loops = copy_mesh.loops
|
||||||
|
|
||||||
|
# Send scene matrix
|
||||||
|
wmtx = mesh_obj.matrix_world
|
||||||
|
writebuf(struct.pack('ffffffffffffffff',
|
||||||
|
wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
|
||||||
|
wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
|
||||||
|
wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
|
||||||
|
wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
|
||||||
|
|
||||||
|
# Filter out useless AABB points and send data
|
||||||
|
pt = copy_obj.bound_box[0]
|
||||||
|
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
|
||||||
|
pt = copy_obj.bound_box[6]
|
||||||
|
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
|
||||||
|
|
||||||
|
# Create master BMesh
|
||||||
|
bm_master = bmesh.new()
|
||||||
|
bm_master.from_mesh(copy_mesh)
|
||||||
|
|
||||||
|
# Generate shaders
|
||||||
|
if mesh_obj.data.hecl_material_count > 0:
|
||||||
|
writebuf(struct.pack('I', mesh_obj.data.hecl_material_count))
|
||||||
|
for grp_idx in range(mesh_obj.data.hecl_material_count):
|
||||||
|
writebuf(struct.pack('I', len(mesh_obj.data.materials)))
|
||||||
|
for ref_mat in mesh_obj.data.materials:
|
||||||
|
ref_mat_split = ref_mat.name.split('_')
|
||||||
|
if len(ref_mat_split) != 3:
|
||||||
|
raise RuntimeError('material names must follow MAT_%u_%u format')
|
||||||
|
ref_mat_idx = int(ref_mat_split[2])
|
||||||
|
found = False
|
||||||
|
for mat in bpy.data.materials:
|
||||||
|
if mat.name.endswith('_%u_%u' % (grp_idx, ref_mat_idx)):
|
||||||
|
write_out_material(writebuf, mat, mesh_obj)
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
raise RuntimeError('uneven material set %d in %s' % (grp_idx, mesh_obj.name))
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('II', 1, len(mesh_obj.data.materials)))
|
||||||
|
for mat in mesh_obj.data.materials:
|
||||||
|
write_out_material(writebuf, mat, mesh_obj)
|
||||||
|
|
||||||
|
# Output attribute lists
|
||||||
|
HMDLMesh.write_mesh_attrs(writebuf, bm_master, rna_loops, use_luv, mesh_obj.material_slots)
|
||||||
|
|
||||||
|
# Vertex groups
|
||||||
|
writebuf(struct.pack('I', len(mesh_obj.vertex_groups)))
|
||||||
|
for vgrp in mesh_obj.vertex_groups:
|
||||||
|
writebuf(struct.pack('I', len(vgrp.name)))
|
||||||
|
writebuf(vgrp.name.encode())
|
||||||
|
|
||||||
|
# Enumerate custom props
|
||||||
|
writebuf(struct.pack('I', len(mesh_obj.keys())))
|
||||||
|
for k in mesh_obj.keys():
|
||||||
|
writebuf(struct.pack('I', len(k)))
|
||||||
|
writebuf(k.encode())
|
||||||
|
val_str = str(mesh_obj[k])
|
||||||
|
writebuf(struct.pack('I', len(val_str)))
|
||||||
|
writebuf(val_str.encode())
|
||||||
|
|
||||||
|
# Delete copied mesh from scene
|
||||||
|
bm_master.free()
|
||||||
|
#bpy.context.scene.objects.unlink(copy_obj)
|
||||||
|
bpy.data.objects.remove(copy_obj)
|
||||||
|
bpy.data.meshes.remove(copy_mesh)
|
||||||
|
|
||||||
|
def prop_val_from_colmat(name, m):
|
||||||
|
if name in m:
|
||||||
|
return m[name]
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Takes a Blender 'Mesh' object (not the datablock)
|
||||||
|
# and performs a one-shot conversion process to collision geometry
|
||||||
|
def cookcol(writebuf, mesh_obj):
|
||||||
|
if mesh_obj.type != 'MESH':
|
||||||
|
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
||||||
|
|
||||||
|
# Copy mesh (and apply mesh modifiers with triangulation)
|
||||||
|
copy_name = mesh_obj.name + "_hmdltri"
|
||||||
|
copy_mesh = bpy.data.meshes.new_from_object(mesh_obj, preserve_all_data_layers=True,
|
||||||
|
depsgraph=bpy.context.evaluated_depsgraph_get())
|
||||||
|
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
||||||
|
copy_obj.scale = mesh_obj.scale
|
||||||
|
bpy.context.scene.collection.objects.link(copy_obj)
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.context.view_layer.objects.active = copy_obj
|
||||||
|
copy_obj.select_set(True)
|
||||||
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
bpy.ops.mesh.select_all(action='SELECT')
|
||||||
|
bpy.ops.mesh.quads_convert_to_tris()
|
||||||
|
bpy.ops.mesh.select_all(action='DESELECT')
|
||||||
|
bpy.context.scene.update_tag()
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
copy_mesh.calc_normals_split()
|
||||||
|
rna_loops = copy_mesh.loops
|
||||||
|
|
||||||
|
# Send scene matrix
|
||||||
|
wmtx = mesh_obj.matrix_world
|
||||||
|
#writebuf(struct.pack('ffffffffffffffff',
|
||||||
|
#wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
|
||||||
|
#wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
|
||||||
|
#wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
|
||||||
|
#wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
|
||||||
|
|
||||||
|
# Filter out useless AABB points and send data
|
||||||
|
#pt = wmtx * Vector(copy_obj.bound_box[0])
|
||||||
|
#writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
|
||||||
|
#pt = wmtx * Vector(copy_obj.bound_box[6])
|
||||||
|
#writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
|
||||||
|
|
||||||
|
# Send materials
|
||||||
|
writebuf(struct.pack('I', len(copy_mesh.materials)))
|
||||||
|
for m in copy_mesh.materials:
|
||||||
|
writebuf(struct.pack('I', len(m.name)))
|
||||||
|
writebuf(m.name.encode())
|
||||||
|
unknown = prop_val_from_colmat('retro_unknown', m)
|
||||||
|
surfaceStone = prop_val_from_colmat('retro_surface_stone', m)
|
||||||
|
surfaceMetal = prop_val_from_colmat('retro_surface_metal', m)
|
||||||
|
surfaceGrass = prop_val_from_colmat('retro_surface_grass', m)
|
||||||
|
surfaceIce = prop_val_from_colmat('retro_surface_ice', m)
|
||||||
|
pillar = prop_val_from_colmat('retro_pillar', m)
|
||||||
|
surfaceMetalGrating = prop_val_from_colmat('retro_surface_metal_grating', m)
|
||||||
|
surfacePhazon = prop_val_from_colmat('retro_surface_phazon', m)
|
||||||
|
surfaceDirt = prop_val_from_colmat('retro_surface_dirt', m)
|
||||||
|
surfaceLava = prop_val_from_colmat('retro_surface_lava', m)
|
||||||
|
surfaceSPMetal = prop_val_from_colmat('retro_surface_sp_metal', m)
|
||||||
|
surfaceStoneRock = prop_val_from_colmat('retro_surface_lava_stone', m)
|
||||||
|
surfaceSnow = prop_val_from_colmat('retro_surface_snow', m)
|
||||||
|
surfaceMudSlow = prop_val_from_colmat('retro_surface_mud_slow', m)
|
||||||
|
surfaceFabric = prop_val_from_colmat('retro_surface_fabric', m)
|
||||||
|
halfPipe = prop_val_from_colmat('retro_half_pipe', m)
|
||||||
|
surfaceMud = prop_val_from_colmat('retro_surface_mud', m)
|
||||||
|
surfaceGlass = prop_val_from_colmat('retro_surface_glass', m)
|
||||||
|
unused3 = prop_val_from_colmat('retro_unused3', m)
|
||||||
|
unused4 = prop_val_from_colmat('retro_unused4', m)
|
||||||
|
surfaceShield = prop_val_from_colmat('retro_surface_shield', m)
|
||||||
|
surfaceSand = prop_val_from_colmat('retro_surface_sand', m)
|
||||||
|
surfaceMothOrSeedOrganics = prop_val_from_colmat('retro_surface_moth_or_seed_organics', m)
|
||||||
|
surfaceWeb = prop_val_from_colmat('retro_surface_web', m)
|
||||||
|
projPassthrough = prop_val_from_colmat('retro_projectile_passthrough', m)
|
||||||
|
solid = prop_val_from_colmat('retro_solid', m)
|
||||||
|
noPlatformCollision = prop_val_from_colmat('retro_no_platform_collision', m)
|
||||||
|
camPassthrough = prop_val_from_colmat('retro_camera_passthrough', m)
|
||||||
|
surfaceWood = prop_val_from_colmat('retro_surface_wood', m)
|
||||||
|
surfaceOrganic = prop_val_from_colmat('retro_surface_organic', m)
|
||||||
|
noEdgeCollision = prop_val_from_colmat('retro_no_edge_collision', m)
|
||||||
|
surfaceRubber = prop_val_from_colmat('retro_surface_rubber', m)
|
||||||
|
seeThrough = prop_val_from_colmat('retro_see_through', m)
|
||||||
|
scanPassthrough = prop_val_from_colmat('retro_scan_passthrough', m)
|
||||||
|
aiPassthrough = prop_val_from_colmat('retro_ai_passthrough', m)
|
||||||
|
ceiling = prop_val_from_colmat('retro_ceiling', m)
|
||||||
|
wall = prop_val_from_colmat('retro_wall', m)
|
||||||
|
floor = prop_val_from_colmat('retro_floor', m)
|
||||||
|
aiBlock = prop_val_from_colmat('retro_ai_block', m)
|
||||||
|
jumpNotAllowed = prop_val_from_colmat('retro_jump_not_allowed', m)
|
||||||
|
spiderBall = prop_val_from_colmat('retro_spider_ball', m)
|
||||||
|
screwAttackWallJump = prop_val_from_colmat('retro_screw_attack_wall_jump', m)
|
||||||
|
|
||||||
|
writebuf(struct.pack('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', unknown, surfaceStone, surfaceMetal, surfaceGrass,
|
||||||
|
surfaceIce, pillar, surfaceMetalGrating, surfacePhazon, surfaceDirt, surfaceLava, surfaceSPMetal,
|
||||||
|
surfaceStoneRock, surfaceSnow, surfaceMudSlow, surfaceFabric, halfPipe, surfaceMud, surfaceGlass,
|
||||||
|
unused3, unused4, surfaceShield, surfaceSand, surfaceMothOrSeedOrganics, surfaceWeb, projPassthrough,
|
||||||
|
solid, noPlatformCollision, camPassthrough, surfaceWood, surfaceOrganic, noEdgeCollision, surfaceRubber,
|
||||||
|
seeThrough, scanPassthrough, aiPassthrough, ceiling, wall, floor, aiBlock, jumpNotAllowed, spiderBall,
|
||||||
|
screwAttackWallJump))
|
||||||
|
|
||||||
|
# Send verts
|
||||||
|
writebuf(struct.pack('I', len(copy_mesh.vertices)))
|
||||||
|
for v in copy_mesh.vertices:
|
||||||
|
xfVert = wmtx @ v.co
|
||||||
|
writebuf(struct.pack('fff', xfVert[0], xfVert[1], xfVert[2]))
|
||||||
|
|
||||||
|
# Send edges
|
||||||
|
writebuf(struct.pack('I', len(copy_mesh.edges)))
|
||||||
|
for e in copy_mesh.edges:
|
||||||
|
writebuf(struct.pack('IIb', e.vertices[0], e.vertices[1], e.use_seam))
|
||||||
|
|
||||||
|
# Send trianges
|
||||||
|
writebuf(struct.pack('I', len(copy_mesh.polygons)))
|
||||||
|
for p in copy_mesh.polygons:
|
||||||
|
edge_idxs = []
|
||||||
|
for loopi in p.loop_indices:
|
||||||
|
edge_idxs.append(copy_mesh.loops[loopi].edge_index)
|
||||||
|
l0 = copy_mesh.loops[p.loop_indices[0]]
|
||||||
|
e0 = copy_mesh.edges[l0.edge_index]
|
||||||
|
flip = l0.vertex_index != e0.vertices[0]
|
||||||
|
writebuf(struct.pack('IIIIb', edge_idxs[0], edge_idxs[1], edge_idxs[2], p.material_index, flip))
|
||||||
|
|
||||||
|
# Delete copied mesh from scene
|
||||||
|
#bpy.context.scene.objects.unlink(copy_obj)
|
||||||
|
bpy.data.objects.remove(copy_obj)
|
||||||
|
bpy.data.meshes.remove(copy_mesh)
|
||||||
|
|
||||||
|
|
||||||
|
def draw(layout, context):
|
||||||
|
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
|
||||||
|
if not len(context.scene.hecl_mesh_obj):
|
||||||
|
layout.label(text="Mesh not specified", icon='ERROR')
|
||||||
|
elif context.scene.hecl_mesh_obj not in context.scene.objects:
|
||||||
|
layout.label(text="'"+context.scene.hecl_mesh_obj+"' not in scene", icon='ERROR')
|
||||||
|
else:
|
||||||
|
obj = context.scene.objects[context.scene.hecl_mesh_obj]
|
||||||
|
if obj.type != 'MESH':
|
||||||
|
layout.label(text="'"+context.scene.hecl_mesh_obj+"' not a 'MESH'", icon='ERROR')
|
||||||
|
layout.prop(obj.data, 'cskr_id')
|
||||||
|
layout.prop(obj.data, 'hecl_active_material')
|
||||||
|
layout.prop(obj.data, 'hecl_material_count')
|
||||||
|
|
||||||
|
# Material update
|
||||||
|
def material_update(self, context):
|
||||||
|
target_idx = self.hecl_active_material
|
||||||
|
if target_idx >= self.hecl_material_count or target_idx < 0:
|
||||||
|
return
|
||||||
|
slot_count = len(self.materials)
|
||||||
|
for mat_idx in range(slot_count):
|
||||||
|
for mat in bpy.data.materials:
|
||||||
|
if mat.name.endswith('_%u_%u' % (target_idx, mat_idx)):
|
||||||
|
self.materials[mat_idx] = mat
|
||||||
|
|
||||||
|
|
||||||
|
def fake_writebuf(by):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# DEBUG operator
|
||||||
|
import bpy
|
||||||
|
class hecl_mesh_operator(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.hecl_mesh"
|
||||||
|
bl_label = "DEBUG HECL mesh maker"
|
||||||
|
bl_description = "Test mesh generation utility"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object and context.object.type == 'MESH'
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
cook(fake_writebuf, context.object, -1)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
def register():
|
||||||
|
bpy.types.Scene.hecl_mesh_obj = bpy.props.StringProperty(
|
||||||
|
name='HECL Mesh Object',
|
||||||
|
description='Blender Mesh Object to export during HECL\'s cook process')
|
||||||
|
bpy.types.Scene.hecl_actor_obj = bpy.props.StringProperty(
|
||||||
|
name='HECL Actor Object',
|
||||||
|
description='Blender Empty Object to export during HECL\'s cook process')
|
||||||
|
bpy.types.Mesh.cskr_id = bpy.props.StringProperty(name='Original CSKR ID')
|
||||||
|
bpy.types.Mesh.hecl_material_count = bpy.props.IntProperty(name='HECL Material Count', default=0, min=0)
|
||||||
|
bpy.types.Mesh.hecl_active_material = bpy.props.IntProperty(name='HECL Active Material', default=0, min=0, update=material_update)
|
||||||
|
bpy.utils.register_class(hecl_mesh_operator)
|
||||||
|
pass
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(hecl_mesh_operator)
|
||||||
|
pass
|
|
@ -0,0 +1,460 @@
|
||||||
|
import bpy, struct, bmesh, operator
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
|
# Function to quantize normals to 15-bit precision
|
||||||
|
def quant_norm(n):
|
||||||
|
nf = n.copy()
|
||||||
|
for i in range(3):
|
||||||
|
nf[i] = int(nf[i] * 16384) / 16384.0
|
||||||
|
return nf.freeze()
|
||||||
|
|
||||||
|
# Function to quantize lightmap UVs to 15-bit precision
|
||||||
|
def quant_luv(n):
|
||||||
|
uf = n.copy()
|
||||||
|
for i in range(2):
|
||||||
|
uf[i] = int(uf[i] * 32768) / 32768.0
|
||||||
|
return uf.freeze()
|
||||||
|
|
||||||
|
# Class for building unique sets of vertex attributes for VBO generation
|
||||||
|
class VertPool:
|
||||||
|
|
||||||
|
# Initialize hash-unique index for each available attribute
|
||||||
|
def __init__(self, bm, rna_loops, use_luv, material_slots):
|
||||||
|
self.bm = bm
|
||||||
|
self.rna_loops = rna_loops
|
||||||
|
self.material_slots = material_slots
|
||||||
|
self.pos = {}
|
||||||
|
self.norm = {}
|
||||||
|
self.skin = {}
|
||||||
|
self.color = {}
|
||||||
|
self.uv = {}
|
||||||
|
self.luv = {}
|
||||||
|
self.dlay = None
|
||||||
|
self.clays = []
|
||||||
|
self.ulays = []
|
||||||
|
self.luvlay = None
|
||||||
|
|
||||||
|
dlay = None
|
||||||
|
if len(bm.verts.layers.deform):
|
||||||
|
dlay = bm.verts.layers.deform[0]
|
||||||
|
self.dlay = dlay
|
||||||
|
|
||||||
|
clays = []
|
||||||
|
for cl in range(len(bm.loops.layers.color)):
|
||||||
|
clays.append(bm.loops.layers.color[cl])
|
||||||
|
self.clays = clays
|
||||||
|
|
||||||
|
luvlay = None
|
||||||
|
if use_luv:
|
||||||
|
luvlay = bm.loops.layers.uv[0]
|
||||||
|
self.luvlay = luvlay
|
||||||
|
ulays = []
|
||||||
|
for ul in range(len(bm.loops.layers.uv)):
|
||||||
|
ulays.append(bm.loops.layers.uv[ul])
|
||||||
|
self.ulays = ulays
|
||||||
|
|
||||||
|
# Per-vert pool attributes
|
||||||
|
for v in bm.verts:
|
||||||
|
pf = v.co.copy().freeze()
|
||||||
|
if pf not in self.pos:
|
||||||
|
self.pos[pf] = len(self.pos)
|
||||||
|
if not rna_loops:
|
||||||
|
nf = quant_norm(v.normal)
|
||||||
|
if nf not in self.norm:
|
||||||
|
self.norm[nf] = len(self.norm)
|
||||||
|
if dlay:
|
||||||
|
sf = tuple(sorted(v[dlay].items()))
|
||||||
|
if sf not in self.skin:
|
||||||
|
self.skin[sf] = len(self.skin)
|
||||||
|
|
||||||
|
# Per-loop pool attributes
|
||||||
|
for f in bm.faces:
|
||||||
|
lightmapped = f.material_index < len(material_slots) and \
|
||||||
|
material_slots[f.material_index].material['retro_lightmapped']
|
||||||
|
for l in f.loops:
|
||||||
|
if rna_loops:
|
||||||
|
nf = quant_norm(rna_loops[l.index].normal)
|
||||||
|
if nf not in self.norm:
|
||||||
|
self.norm[nf] = len(self.norm)
|
||||||
|
for cl in range(len(clays)):
|
||||||
|
cf = l[clays[cl]].copy().freeze()
|
||||||
|
if cf not in self.color:
|
||||||
|
self.color[cf] = len(self.color)
|
||||||
|
start_uvlay = 0
|
||||||
|
if use_luv and lightmapped:
|
||||||
|
start_uvlay = 1
|
||||||
|
uf = quant_luv(l[luvlay].uv)
|
||||||
|
if uf not in self.luv:
|
||||||
|
self.luv[uf] = len(self.luv)
|
||||||
|
for ul in range(start_uvlay, len(ulays)):
|
||||||
|
uf = l[ulays[ul]].uv.copy().freeze()
|
||||||
|
if uf not in self.uv:
|
||||||
|
self.uv[uf] = len(self.uv)
|
||||||
|
|
||||||
|
def write_out(self, writebuf, vert_groups):
|
||||||
|
writebuf(struct.pack('I', len(self.pos)))
|
||||||
|
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(self.norm)))
|
||||||
|
for n in sorted(self.norm.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('fff', n[0][0], n[0][1], n[0][2]))
|
||||||
|
|
||||||
|
writebuf(struct.pack('II', len(self.clays), len(self.color)))
|
||||||
|
for c in sorted(self.color.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('fff', c[0][0], c[0][1], c[0][2]))
|
||||||
|
|
||||||
|
writebuf(struct.pack('II', len(self.ulays), len(self.uv)))
|
||||||
|
for u in sorted(self.uv.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('ff', u[0][0], u[0][1]))
|
||||||
|
|
||||||
|
luv_count = 0
|
||||||
|
if self.luvlay is not None:
|
||||||
|
luv_count = 1
|
||||||
|
writebuf(struct.pack('II', luv_count, len(self.luv)))
|
||||||
|
for u in sorted(self.luv.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('ff', u[0][0], u[0][1]))
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(vert_groups)))
|
||||||
|
for vgrp in vert_groups:
|
||||||
|
writebuf(struct.pack('I', len(vgrp.name)))
|
||||||
|
writebuf(vgrp.name.encode())
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(self.skin)))
|
||||||
|
for s in sorted(self.skin.items(), key=operator.itemgetter(1)):
|
||||||
|
entries = s[0]
|
||||||
|
writebuf(struct.pack('I', len(entries)))
|
||||||
|
if len(entries):
|
||||||
|
total_len = 0.0
|
||||||
|
for ent in entries:
|
||||||
|
total_len += ent[1]
|
||||||
|
for ent in entries:
|
||||||
|
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
|
||||||
|
|
||||||
|
def write_out_map(self, writebuf):
|
||||||
|
writebuf(struct.pack('I', len(self.pos)))
|
||||||
|
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
|
||||||
|
|
||||||
|
def get_pos_idx(self, vert):
|
||||||
|
pf = vert.co.copy().freeze()
|
||||||
|
return self.pos[pf]
|
||||||
|
|
||||||
|
def get_norm_idx(self, loop):
|
||||||
|
if self.rna_loops:
|
||||||
|
nf = quant_norm(self.rna_loops[loop.index].normal)
|
||||||
|
else:
|
||||||
|
nf = quant_norm(loop.vert.normal)
|
||||||
|
return self.norm[nf]
|
||||||
|
|
||||||
|
def get_skin_idx(self, vert):
|
||||||
|
if not self.dlay:
|
||||||
|
return 0
|
||||||
|
sf = tuple(sorted(vert[self.dlay].items()))
|
||||||
|
return self.skin[sf]
|
||||||
|
|
||||||
|
def get_color_idx(self, loop, cidx):
|
||||||
|
cf = loop[self.clays[cidx]].copy().freeze()
|
||||||
|
return self.color[cf]
|
||||||
|
|
||||||
|
def get_uv_idx(self, loop, uidx):
|
||||||
|
if self.luvlay is not None and uidx == 0:
|
||||||
|
if self.material_slots[loop.face.material_index].material['retro_lightmapped']:
|
||||||
|
uf = quant_luv(loop[self.luvlay].uv)
|
||||||
|
return self.luv[uf]
|
||||||
|
uf = loop[self.ulays[uidx]].uv.copy().freeze()
|
||||||
|
return self.uv[uf]
|
||||||
|
|
||||||
|
def loops_contiguous(self, la, lb):
|
||||||
|
if la.vert != lb.vert:
|
||||||
|
return False
|
||||||
|
if self.get_norm_idx(la) != self.get_norm_idx(lb):
|
||||||
|
return False
|
||||||
|
for cl in range(len(self.clays)):
|
||||||
|
if self.get_color_idx(la, cl) != self.get_color_idx(lb, cl):
|
||||||
|
return False
|
||||||
|
for ul in range(len(self.ulays)):
|
||||||
|
if self.get_uv_idx(la, ul) != self.get_uv_idx(lb, ul):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def splitable_edge(self, edge):
|
||||||
|
if len(edge.link_faces) < 2:
|
||||||
|
return False
|
||||||
|
for v in edge.verts:
|
||||||
|
found = None
|
||||||
|
for f in edge.link_faces:
|
||||||
|
for l in f.loops:
|
||||||
|
if l.vert == v:
|
||||||
|
if not found:
|
||||||
|
found = l
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if not self.loops_contiguous(found, l):
|
||||||
|
return True
|
||||||
|
break
|
||||||
|
return False
|
||||||
|
|
||||||
|
def loop_out(self, writebuf, loop):
|
||||||
|
writebuf(struct.pack('B', 1))
|
||||||
|
writebuf(struct.pack('II', self.get_pos_idx(loop.vert), self.get_norm_idx(loop)))
|
||||||
|
for cl in range(len(self.clays)):
|
||||||
|
writebuf(struct.pack('I', self.get_color_idx(loop, cl)))
|
||||||
|
for ul in range(len(self.ulays)):
|
||||||
|
writebuf(struct.pack('I', self.get_uv_idx(loop, ul)))
|
||||||
|
sp = struct.pack('I', self.get_skin_idx(loop.vert))
|
||||||
|
writebuf(sp)
|
||||||
|
|
||||||
|
def null_loop_out(self, writebuf):
|
||||||
|
writebuf(struct.pack('B', 1))
|
||||||
|
writebuf(struct.pack('I', 0xffffffff))
|
||||||
|
|
||||||
|
def loop_out_map(self, writebuf, loop):
|
||||||
|
writebuf(struct.pack('B', 1))
|
||||||
|
writebuf(struct.pack('I', self.get_pos_idx(loop.vert)))
|
||||||
|
|
||||||
|
def vert_out_map(self, writebuf, vert):
|
||||||
|
writebuf(struct.pack('B', 1))
|
||||||
|
writebuf(struct.pack('I', self.get_pos_idx(vert)))
|
||||||
|
|
||||||
|
|
||||||
|
def strip_next_loop(prev_loop, out_count):
|
||||||
|
if out_count & 1:
|
||||||
|
radial_loop = prev_loop.link_loop_radial_next
|
||||||
|
loop = radial_loop.link_loop_prev
|
||||||
|
return loop, loop
|
||||||
|
else:
|
||||||
|
radial_loop = prev_loop.link_loop_radial_prev
|
||||||
|
loop = radial_loop.link_loop_next
|
||||||
|
return loop.link_loop_next, loop
|
||||||
|
|
||||||
|
|
||||||
|
def recursive_faces_islands(list_out, rem_list, face):
|
||||||
|
if face not in rem_list:
|
||||||
|
return []
|
||||||
|
|
||||||
|
list_out.append(face)
|
||||||
|
rem_list.remove(face)
|
||||||
|
next_faces = []
|
||||||
|
for e in face.edges:
|
||||||
|
if not e.is_contiguous or e.seam:
|
||||||
|
continue
|
||||||
|
for f in e.link_faces:
|
||||||
|
if f == face:
|
||||||
|
continue
|
||||||
|
next_faces.append(f)
|
||||||
|
return next_faces
|
||||||
|
|
||||||
|
def cook(writebuf, mesh_obj):
|
||||||
|
if mesh_obj.type != 'MESH':
|
||||||
|
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
||||||
|
|
||||||
|
obj_vismodes = dict((i[0], i[3]) for i in bpy.types.Object.retro_mapobj_vis_mode[1]['items'])
|
||||||
|
|
||||||
|
# Write out visibility type
|
||||||
|
vis_types = dict((i[0], i[3]) for i in bpy.types.Scene.retro_map_vis_mode[1]['items'])
|
||||||
|
writebuf(struct.pack('I', vis_types[bpy.context.scene.retro_map_vis_mode]))
|
||||||
|
|
||||||
|
# Copy mesh (and apply mesh modifiers with triangulation)
|
||||||
|
copy_name = mesh_obj.name + "_hmdltri"
|
||||||
|
copy_mesh = bpy.data.meshes.new_from_object(mesh_obj, preserve_all_data_layers=True,
|
||||||
|
depsgraph=bpy.context.evaluated_depsgraph_get())
|
||||||
|
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
|
||||||
|
copy_obj.scale = mesh_obj.scale
|
||||||
|
bpy.context.scene.collection.objects.link(copy_obj)
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.context.view_layer.objects.active = copy_obj
|
||||||
|
copy_obj.select_set(True)
|
||||||
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
bpy.ops.mesh.select_all(action='SELECT')
|
||||||
|
bpy.ops.mesh.quads_convert_to_tris()
|
||||||
|
bpy.ops.mesh.select_all(action='DESELECT')
|
||||||
|
bpy.context.scene.update_tag()
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
copy_mesh.calc_normals_split()
|
||||||
|
rna_loops = copy_mesh.loops
|
||||||
|
|
||||||
|
# Create master BMesh and VertPool
|
||||||
|
bm_master = bmesh.new()
|
||||||
|
bm_master.from_mesh(copy_obj.data)
|
||||||
|
vert_pool = VertPool(bm_master, rna_loops, False, mesh_obj.material_slots)
|
||||||
|
|
||||||
|
# Output vert pool
|
||||||
|
vert_pool.write_out_map(writebuf)
|
||||||
|
|
||||||
|
# Create map surfaces and borders
|
||||||
|
faces_rem = list(bm_master.faces)
|
||||||
|
loop_iter = 0
|
||||||
|
loop_ranges = []
|
||||||
|
while len(faces_rem):
|
||||||
|
island_faces = []
|
||||||
|
faces = [faces_rem[0]]
|
||||||
|
while len(faces):
|
||||||
|
next_faces = []
|
||||||
|
ret_faces = None
|
||||||
|
for f in faces:
|
||||||
|
ret_faces = recursive_faces_islands(island_faces, faces_rem, f)
|
||||||
|
if ret_faces == False:
|
||||||
|
break
|
||||||
|
next_faces.extend(ret_faces)
|
||||||
|
if ret_faces == False:
|
||||||
|
break
|
||||||
|
faces = next_faces
|
||||||
|
|
||||||
|
# island_faces now holds one island (map edge delimited)
|
||||||
|
prev_loop_emit = None
|
||||||
|
loop_set = set()
|
||||||
|
edge_set = set()
|
||||||
|
out_count = 0
|
||||||
|
loop_count = 0
|
||||||
|
while len(island_faces):
|
||||||
|
sel_lists_local = []
|
||||||
|
restore_out_count = out_count
|
||||||
|
for start_face in island_faces:
|
||||||
|
for l in start_face.loops:
|
||||||
|
out_count = restore_out_count
|
||||||
|
island_local = list(island_faces)
|
||||||
|
if out_count & 1:
|
||||||
|
prev_loop = l.link_loop_prev
|
||||||
|
loop = prev_loop.link_loop_prev
|
||||||
|
sel_list = [l, prev_loop, loop]
|
||||||
|
prev_loop = loop
|
||||||
|
else:
|
||||||
|
prev_loop = l.link_loop_next
|
||||||
|
loop = prev_loop.link_loop_next
|
||||||
|
sel_list = [l, prev_loop, loop]
|
||||||
|
out_count += 3
|
||||||
|
island_local.remove(start_face)
|
||||||
|
while True:
|
||||||
|
if not prev_loop.edge.is_contiguous or prev_loop.edge.seam:
|
||||||
|
break
|
||||||
|
loop, prev_loop = strip_next_loop(prev_loop, out_count)
|
||||||
|
face = loop.face
|
||||||
|
if face not in island_local:
|
||||||
|
break
|
||||||
|
sel_list.append(loop)
|
||||||
|
island_local.remove(face)
|
||||||
|
out_count += 1
|
||||||
|
sel_lists_local.append((sel_list, island_local, out_count))
|
||||||
|
max_count = 0
|
||||||
|
max_sl = None
|
||||||
|
max_island_faces = None
|
||||||
|
for sl in sel_lists_local:
|
||||||
|
if len(sl[0]) > max_count:
|
||||||
|
max_count = len(sl[0])
|
||||||
|
max_sl = sl[0]
|
||||||
|
max_island_faces = sl[1]
|
||||||
|
out_count = sl[2]
|
||||||
|
island_faces = max_island_faces
|
||||||
|
|
||||||
|
if prev_loop_emit:
|
||||||
|
vert_pool.loop_out_map(writebuf, prev_loop_emit)
|
||||||
|
vert_pool.loop_out_map(writebuf, max_sl[0])
|
||||||
|
loop_count += 2
|
||||||
|
loop_set.add(prev_loop_emit)
|
||||||
|
loop_set.add(max_sl[0])
|
||||||
|
loop_count += len(max_sl)
|
||||||
|
for loop in max_sl:
|
||||||
|
vert_pool.loop_out_map(writebuf, loop)
|
||||||
|
prev_loop_emit = loop
|
||||||
|
loop_set.add(loop)
|
||||||
|
for edge in loop.face.edges:
|
||||||
|
if edge.seam:
|
||||||
|
edge_set.add(edge)
|
||||||
|
|
||||||
|
# Create island surface with edges
|
||||||
|
if len(edge_set):
|
||||||
|
trace_edge = edge_set.pop()
|
||||||
|
else:
|
||||||
|
trace_edge = None
|
||||||
|
edge_ranges = []
|
||||||
|
edge_iter = loop_iter + loop_count
|
||||||
|
while trace_edge:
|
||||||
|
edge_count = 0
|
||||||
|
vert_pool.vert_out_map(writebuf, trace_edge.verts[0])
|
||||||
|
vert_pool.vert_out_map(writebuf, trace_edge.verts[1])
|
||||||
|
edge_count += 2
|
||||||
|
next_vert = trace_edge.verts[1]
|
||||||
|
found_edge = True
|
||||||
|
while found_edge:
|
||||||
|
found_edge = False
|
||||||
|
for edge in next_vert.link_edges:
|
||||||
|
if edge in edge_set:
|
||||||
|
edge_set.remove(edge)
|
||||||
|
next_vert = edge.other_vert(next_vert)
|
||||||
|
vert_pool.vert_out_map(writebuf, next_vert)
|
||||||
|
edge_count += 1
|
||||||
|
found_edge = True
|
||||||
|
break
|
||||||
|
if len(edge_set):
|
||||||
|
trace_edge = edge_set.pop()
|
||||||
|
else:
|
||||||
|
trace_edge = None
|
||||||
|
edge_ranges.append((edge_iter, edge_count))
|
||||||
|
edge_iter += edge_count
|
||||||
|
|
||||||
|
pos_avg = Vector()
|
||||||
|
norm_avg = Vector()
|
||||||
|
if len(loop_set):
|
||||||
|
for loop in loop_set:
|
||||||
|
pos_avg += loop.vert.co
|
||||||
|
norm_avg += loop.vert.normal
|
||||||
|
pos_avg /= len(loop_set)
|
||||||
|
norm_avg /= len(loop_set)
|
||||||
|
norm_avg.normalize()
|
||||||
|
|
||||||
|
loop_ranges.append((loop_iter, loop_count, edge_ranges, pos_avg, norm_avg))
|
||||||
|
loop_iter = edge_iter
|
||||||
|
|
||||||
|
# No more surfaces
|
||||||
|
writebuf(struct.pack('B', 0))
|
||||||
|
|
||||||
|
# Write out loop ranges and averages
|
||||||
|
writebuf(struct.pack('I', len(loop_ranges)))
|
||||||
|
for loop_range in loop_ranges:
|
||||||
|
writebuf(struct.pack('fff', loop_range[3][0], loop_range[3][1], loop_range[3][2]))
|
||||||
|
writebuf(struct.pack('fff', loop_range[4][0], loop_range[4][1], loop_range[4][2]))
|
||||||
|
writebuf(struct.pack('II', loop_range[0], loop_range[1]))
|
||||||
|
writebuf(struct.pack('I', len(loop_range[2])))
|
||||||
|
for edge_range in loop_range[2]:
|
||||||
|
writebuf(struct.pack('II', edge_range[0], edge_range[1]))
|
||||||
|
|
||||||
|
# Write out mappable objects
|
||||||
|
poi_count = 0
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.retro_mappable_type != -1:
|
||||||
|
poi_count += 1
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', poi_count))
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.retro_mappable_type != -1:
|
||||||
|
writebuf(struct.pack('III',
|
||||||
|
obj.retro_mappable_type, obj_vismodes[obj.retro_mapobj_vis_mode], int(obj.retro_mappable_sclyid, 0)))
|
||||||
|
writebuf(struct.pack('ffffffffffffffff',
|
||||||
|
obj.matrix_world[0][0], obj.matrix_world[0][1], obj.matrix_world[0][2], obj.matrix_world[0][3],
|
||||||
|
obj.matrix_world[1][0], obj.matrix_world[1][1], obj.matrix_world[1][2], obj.matrix_world[1][3],
|
||||||
|
obj.matrix_world[2][0], obj.matrix_world[2][1], obj.matrix_world[2][2], obj.matrix_world[2][3],
|
||||||
|
obj.matrix_world[3][0], obj.matrix_world[3][1], obj.matrix_world[3][2], obj.matrix_world[3][3]))
|
||||||
|
|
||||||
|
def draw(layout, context):
|
||||||
|
obj = context.active_object
|
||||||
|
layout.prop(context.scene, 'retro_map_vis_mode', text='Visibility Mode')
|
||||||
|
if obj and obj.retro_mappable_type != -1:
|
||||||
|
layout.prop(obj, 'retro_mappable_type', text='Object Type')
|
||||||
|
layout.prop(obj, 'retro_mapobj_vis_mode', text='Object Visibility Mode')
|
||||||
|
layout.prop(obj, 'retro_mappable_sclyid', text='Object ID')
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Object.retro_mappable_type = bpy.props.IntProperty(name='Retro: MAPA object type', default=-1)
|
||||||
|
bpy.types.Object.retro_mappable_sclyid = bpy.props.StringProperty(name='Retro: MAPA object SCLY ID')
|
||||||
|
bpy.types.Scene.retro_map_vis_mode = bpy.props.EnumProperty(items=[('ALWAYS', 'Always', 'Always Visible', 0),
|
||||||
|
('MAPSTATIONORVISIT', 'Map Station or Visit', 'Visible after Map Station or Visit', 1),
|
||||||
|
('VISIT', 'Visit', 'Visible after Visit', 2),
|
||||||
|
('NEVER', 'Never', 'Never Visible', 3)],
|
||||||
|
name='Retro: Map Visibility Mode')
|
||||||
|
bpy.types.Object.retro_mapobj_vis_mode = bpy.props.EnumProperty(items=[('ALWAYS', 'Always', 'Always Visible', 0),
|
||||||
|
('MAPSTATIONORVISIT', 'Map Station or Visit', 'Visible after Map Station or Visit', 1),
|
||||||
|
('VISIT', 'Visit', 'Visible after Door Visit', 2),
|
||||||
|
('NEVER', 'Never', 'Never Visible', 3),
|
||||||
|
('MAPSTATIONORVISIT2', 'Map Station or Visit 2', 'Visible after Map Station or Visit', 4)],
|
||||||
|
name='Retro: Map Object Visibility Mode')
|
|
@ -0,0 +1,55 @@
|
||||||
|
import bpy, os, struct
|
||||||
|
|
||||||
|
def cook(writebuf):
|
||||||
|
found_lib = False
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.data and obj.data.library:
|
||||||
|
path = os.path.normpath(bpy.path.abspath(obj.data.library.filepath))
|
||||||
|
writebuf(struct.pack('I', len(path)))
|
||||||
|
writebuf(path.encode())
|
||||||
|
found_lib = True
|
||||||
|
break
|
||||||
|
if not found_lib:
|
||||||
|
raise RuntimeError('No hexagon segments present')
|
||||||
|
|
||||||
|
world_count = 0
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if not obj.parent and obj.type == 'EMPTY':
|
||||||
|
world_count += 1
|
||||||
|
writebuf(struct.pack('I', world_count))
|
||||||
|
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if not obj.parent and obj.type == 'EMPTY':
|
||||||
|
writebuf(struct.pack('I', len(obj.name)))
|
||||||
|
writebuf(obj.name.encode())
|
||||||
|
writebuf(struct.pack('ffffffffffffffff',
|
||||||
|
obj.matrix_local[0][0], obj.matrix_local[0][1], obj.matrix_local[0][2], obj.matrix_local[0][3],
|
||||||
|
obj.matrix_local[1][0], obj.matrix_local[1][1], obj.matrix_local[1][2], obj.matrix_local[1][3],
|
||||||
|
obj.matrix_local[2][0], obj.matrix_local[2][1], obj.matrix_local[2][2], obj.matrix_local[2][3],
|
||||||
|
obj.matrix_local[3][0], obj.matrix_local[3][1], obj.matrix_local[3][2], obj.matrix_local[3][3]))
|
||||||
|
writebuf(struct.pack('I', len(obj.children)))
|
||||||
|
for child in obj.children:
|
||||||
|
writebuf(struct.pack('ffffffffffffffff',
|
||||||
|
child.matrix_local[0][0], child.matrix_local[0][1], child.matrix_local[0][2], child.matrix_local[0][3],
|
||||||
|
child.matrix_local[1][0], child.matrix_local[1][1], child.matrix_local[1][2], child.matrix_local[1][3],
|
||||||
|
child.matrix_local[2][0], child.matrix_local[2][1], child.matrix_local[2][2], child.matrix_local[2][3],
|
||||||
|
child.matrix_local[3][0], child.matrix_local[3][1], child.matrix_local[3][2], child.matrix_local[3][3]))
|
||||||
|
writebuf(struct.pack('ffff', obj.retro_mapworld_color[0], obj.retro_mapworld_color[1],
|
||||||
|
obj.retro_mapworld_color[2], obj.retro_mapworld_color[3]))
|
||||||
|
writebuf(struct.pack('I', len(obj.retro_mapworld_path)))
|
||||||
|
writebuf(obj.retro_mapworld_path.encode())
|
||||||
|
|
||||||
|
def draw(layout, context):
|
||||||
|
obj = context.active_object
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
while obj.parent:
|
||||||
|
obj = obj.parent
|
||||||
|
layout.prop(obj, 'retro_mapworld_color', text='Color')
|
||||||
|
layout.prop(obj, 'retro_mapworld_path', text='Path')
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
bpy.types.Object.retro_mapworld_color = bpy.props.FloatVectorProperty(name='Retro: MapWorld Color',\
|
||||||
|
description='Sets map world color', subtype='COLOR', size=4, min=0.0, max=1.0)
|
||||||
|
bpy.types.Object.retro_mapworld_path = bpy.props.StringProperty(name='Retro: MapWorld Path', description='Sets path to World root')
|
|
@ -0,0 +1,388 @@
|
||||||
|
import bpy, gpu, sys, bmesh, struct
|
||||||
|
from mathutils import Vector
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
|
# Convenience class that automatically brings active edit mesh's face into scope for get/set
|
||||||
|
class HeightRef:
|
||||||
|
def __init__(self):
|
||||||
|
self.bm = None
|
||||||
|
context = bpy.context
|
||||||
|
obj = context.scene.objects[context.scene.hecl_path_obj]
|
||||||
|
if obj.type != 'MESH':
|
||||||
|
return
|
||||||
|
if context.edit_object != obj:
|
||||||
|
return
|
||||||
|
bm = bmesh.from_edit_mesh(obj.data)
|
||||||
|
if 'Height' not in bm.faces.layers.float:
|
||||||
|
return
|
||||||
|
self.height_lay = bm.faces.layers.float['Height']
|
||||||
|
self.bm = bm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ready(self):
|
||||||
|
return self.bm is not None and self.bm.faces.active is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
if self.ready:
|
||||||
|
return self.bm.faces.active[self.height_lay]
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
if self.ready:
|
||||||
|
for f in self.bm.faces:
|
||||||
|
if f.select:
|
||||||
|
f[self.height_lay] = value
|
||||||
|
|
||||||
|
# Active edit face height get
|
||||||
|
def get_height(self):
|
||||||
|
return HeightRef().value
|
||||||
|
|
||||||
|
# Selected edit face(s) height set
|
||||||
|
def set_height(self, val):
|
||||||
|
HeightRef().value = val
|
||||||
|
for ar in bpy.context.screen.areas:
|
||||||
|
if ar.type == 'VIEW_3D':
|
||||||
|
ar.tag_redraw()
|
||||||
|
|
||||||
|
# Simple AABB class
|
||||||
|
class AABB:
|
||||||
|
def __init__(self):
|
||||||
|
self.min = Vector((999999.0, 999999.0, 999999.0))
|
||||||
|
self.max = Vector((-999999.0, -999999.0, -999999.0))
|
||||||
|
def accumulate(self, vec):
|
||||||
|
for i in range(3):
|
||||||
|
if vec[i] < self.min[i]:
|
||||||
|
self.min[i] = vec[i]
|
||||||
|
if vec[i] > self.max[i]:
|
||||||
|
self.max[i] = vec[i]
|
||||||
|
def isValid(self):
|
||||||
|
for i in range(3):
|
||||||
|
if self.min[i] > self.max[i]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Simple adjacency calculator
|
||||||
|
class AdjacencySet:
|
||||||
|
def __init__(self, bm, mesh_obj, type_mask):
|
||||||
|
self.faces = {}
|
||||||
|
for f in bm.faces:
|
||||||
|
material = mesh_obj.material_slots[f.material_index].material
|
||||||
|
if (material.retro_path_type_mask & type_mask) == 0:
|
||||||
|
continue
|
||||||
|
face_set = set()
|
||||||
|
face_set.add(f)
|
||||||
|
|
||||||
|
# Breadth-first loop to avoid crashing python with large recursion
|
||||||
|
next_level = [f]
|
||||||
|
while len(next_level):
|
||||||
|
next_next_level = []
|
||||||
|
for of in next_level:
|
||||||
|
for e in of.edges:
|
||||||
|
for of2 in e.link_faces:
|
||||||
|
if of2 == of:
|
||||||
|
continue
|
||||||
|
if of2 not in face_set:
|
||||||
|
material = mesh_obj.material_slots[of2.material_index].material
|
||||||
|
if material.retro_path_type_mask & type_mask:
|
||||||
|
face_set.add(of2)
|
||||||
|
next_next_level.append(of2)
|
||||||
|
next_level = next_next_level
|
||||||
|
self.faces[f] = face_set
|
||||||
|
|
||||||
|
def has_adjacency(self, face_a, face_b):
|
||||||
|
if face_a not in self.faces:
|
||||||
|
return False
|
||||||
|
return face_b in self.faces[face_a]
|
||||||
|
|
||||||
|
# Cooking entry point
|
||||||
|
def cook(writebuf, mesh_obj):
|
||||||
|
ba = bytearray()
|
||||||
|
# Version 4
|
||||||
|
ba += struct.pack('>I', 4)
|
||||||
|
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(mesh_obj.data)
|
||||||
|
bm.faces.ensure_lookup_table()
|
||||||
|
height_lay = None
|
||||||
|
if 'Height' in bm.faces.layers.float:
|
||||||
|
height_lay = bm.faces.layers.float['Height']
|
||||||
|
|
||||||
|
# Gather immediate adjacencies
|
||||||
|
node_list = []
|
||||||
|
link_list = []
|
||||||
|
region_list = []
|
||||||
|
up_vec = Vector((0.0, 0.0, 1.0))
|
||||||
|
for f in bm.faces:
|
||||||
|
start_loop = f.loops[0]
|
||||||
|
cur_loop = start_loop
|
||||||
|
node_idx = 0
|
||||||
|
start_node = len(node_list)
|
||||||
|
start_link = len(link_list)
|
||||||
|
while True:
|
||||||
|
node_list.append([cur_loop, up_vec])
|
||||||
|
nv1 = cur_loop.vert.co
|
||||||
|
cur_loop = cur_loop.link_loop_prev
|
||||||
|
nv0 = cur_loop.vert.co
|
||||||
|
node_list[-1][1] = (nv0 - nv1).cross(up_vec).normalized()
|
||||||
|
for other_face in cur_loop.edge.link_faces:
|
||||||
|
if other_face == f:
|
||||||
|
continue
|
||||||
|
link_list.append((node_idx, other_face.index, cur_loop.edge.calc_length()))
|
||||||
|
node_idx += 1
|
||||||
|
if cur_loop == start_loop:
|
||||||
|
break
|
||||||
|
region_list.append((f, range(start_node, len(node_list)), range(start_link, len(link_list))))
|
||||||
|
|
||||||
|
# Emit nodes
|
||||||
|
ba += struct.pack('>I', len(node_list))
|
||||||
|
for n in node_list:
|
||||||
|
v = n[0].vert
|
||||||
|
normal = n[1]
|
||||||
|
ba += struct.pack('>ffffff', v.co[0], v.co[1], v.co[2], normal[0], normal[1], normal[2])
|
||||||
|
|
||||||
|
# Emit links
|
||||||
|
ba += struct.pack('>I', len(link_list))
|
||||||
|
for l in link_list:
|
||||||
|
ba += struct.pack('>IIff', l[0], l[1], l[2], 1.0 / l[2])
|
||||||
|
|
||||||
|
# Emit regions
|
||||||
|
ba += struct.pack('>I', len(region_list))
|
||||||
|
for r in region_list:
|
||||||
|
material = mesh_obj.material_slots[r[0].material_index].material
|
||||||
|
height = 1.0
|
||||||
|
if height_lay is not None:
|
||||||
|
height = r[0][height_lay]
|
||||||
|
center = r[0].calc_center_median_weighted()
|
||||||
|
aabb = AABB()
|
||||||
|
for v in r[0].verts:
|
||||||
|
aabb.accumulate(v.co)
|
||||||
|
aabb.accumulate(v.co + Vector((0.0, 0.0, height)))
|
||||||
|
ba += struct.pack('>IIIIHHffffIfffffffffI', len(r[1]), r[1].start, len(r[2]), r[2].start,
|
||||||
|
material.retro_path_idx_mask, material.retro_path_type_mask,
|
||||||
|
height, r[0].normal[0], r[0].normal[1], r[0].normal[2],
|
||||||
|
r[0].index, center[0], center[1], center[2],
|
||||||
|
aabb.min[0], aabb.min[1], aabb.min[2],
|
||||||
|
aabb.max[0], aabb.max[1], aabb.max[2],
|
||||||
|
r[0].index)
|
||||||
|
|
||||||
|
num_regions = len(region_list)
|
||||||
|
total_adjacencies = num_regions * (num_regions - 1) // 2
|
||||||
|
num_words = (total_adjacencies + 31) // 32
|
||||||
|
|
||||||
|
# Find ground adjacencies
|
||||||
|
words = [0] * num_words
|
||||||
|
ground_adjacencies = AdjacencySet(bm, mesh_obj, 0x1)
|
||||||
|
for i in range(num_regions):
|
||||||
|
for j in range(num_regions):
|
||||||
|
if i == j:
|
||||||
|
continue
|
||||||
|
i1 = i
|
||||||
|
i2 = j
|
||||||
|
if i1 > i2:
|
||||||
|
continue
|
||||||
|
if ground_adjacencies.has_adjacency(bm.faces[i], bm.faces[j]):
|
||||||
|
rem_regions = num_regions - i1
|
||||||
|
rem_connections = rem_regions * (rem_regions - 1) // 2
|
||||||
|
bit = total_adjacencies - rem_connections + i2 - (i1 + 1)
|
||||||
|
words[bit // 32] |= 1 << (bit % 32)
|
||||||
|
for w in words:
|
||||||
|
ba += struct.pack('>I', w)
|
||||||
|
|
||||||
|
# Find flyer adjacencies
|
||||||
|
words = [0] * num_words
|
||||||
|
flyer_adjacencies = AdjacencySet(bm, mesh_obj, 0x3)
|
||||||
|
for i in range(num_regions):
|
||||||
|
for j in range(num_regions):
|
||||||
|
if i == j:
|
||||||
|
continue
|
||||||
|
i1 = i
|
||||||
|
i2 = j
|
||||||
|
if i1 > i2:
|
||||||
|
continue
|
||||||
|
if flyer_adjacencies.has_adjacency(bm.faces[i], bm.faces[j]):
|
||||||
|
rem_regions = num_regions - i1
|
||||||
|
rem_connections = rem_regions * (rem_regions - 1) // 2
|
||||||
|
bit = total_adjacencies - rem_connections + i2 - (i1 + 1)
|
||||||
|
words[bit // 32] |= 1 << (bit % 32)
|
||||||
|
for w in words:
|
||||||
|
ba += struct.pack('>I', w)
|
||||||
|
|
||||||
|
# Unused zero bits
|
||||||
|
for i in range((((num_regions * num_regions) + 31) // 32 - num_words) * 2):
|
||||||
|
ba += struct.pack('>I', 0)
|
||||||
|
|
||||||
|
# Empty octree (to be generated by hecl)
|
||||||
|
ba += struct.pack('>II', 0, 0)
|
||||||
|
|
||||||
|
# Write out
|
||||||
|
writebuf(struct.pack('I', len(ba)))
|
||||||
|
writebuf(ba)
|
||||||
|
|
||||||
|
try:
|
||||||
|
line_shader = gpu.shader.from_builtin('3D_FLAT_COLOR')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Line draw helper
|
||||||
|
def draw_line_3d(pos_vbo, color_vbo, color, start, end):
|
||||||
|
pos_vbo.append(start)
|
||||||
|
pos_vbo.append(end)
|
||||||
|
color_vbo.append(color)
|
||||||
|
color_vbo.append(color)
|
||||||
|
|
||||||
|
# Draw RNA polygon
|
||||||
|
def draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color):
|
||||||
|
for ek in p.edge_keys:
|
||||||
|
co0 = obj_mtx @ obj.data.vertices[ek[0]].co
|
||||||
|
co1 = obj_mtx @ obj.data.vertices[ek[1]].co
|
||||||
|
draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)),
|
||||||
|
co1 + Vector((0.0, 0.0, height)))
|
||||||
|
for vk in p.vertices:
|
||||||
|
co = obj_mtx @ obj.data.vertices[vk].co
|
||||||
|
draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height)))
|
||||||
|
|
||||||
|
# Draw bmesh face
|
||||||
|
def draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color):
|
||||||
|
for e in f.edges:
|
||||||
|
co0 = obj_mtx @ e.verts[0].co
|
||||||
|
co1 = obj_mtx @ e.verts[1].co
|
||||||
|
draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)),
|
||||||
|
co1 + Vector((0.0, 0.0, height)))
|
||||||
|
for v in f.verts:
|
||||||
|
co = obj_mtx @ v.co
|
||||||
|
draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height)))
|
||||||
|
|
||||||
|
# Viewport hook callback
|
||||||
|
def draw_callback_3d(self, context):
|
||||||
|
# object locations
|
||||||
|
if context.scene.hecl_path_obj not in context.scene.objects:
|
||||||
|
return
|
||||||
|
obj = context.scene.objects[context.scene.hecl_path_obj]
|
||||||
|
if obj.type != 'MESH':
|
||||||
|
return
|
||||||
|
obj_mtx = obj.matrix_world
|
||||||
|
|
||||||
|
bm = None
|
||||||
|
height_lay = None
|
||||||
|
if obj == context.edit_object:
|
||||||
|
bm = bmesh.from_edit_mesh(obj.data)
|
||||||
|
if 'Height' in bm.faces.layers.float:
|
||||||
|
height_lay = bm.faces.layers.float['Height']
|
||||||
|
else:
|
||||||
|
if 'Height' in obj.data.polygon_layers_float:
|
||||||
|
height_lay = obj.data.polygon_layers_float['Height']
|
||||||
|
|
||||||
|
pos_vbo = []
|
||||||
|
color_vbo = []
|
||||||
|
|
||||||
|
# Deselected colors
|
||||||
|
top_color = (0.0, 0.0, 1.0, 0.7)
|
||||||
|
side_color = (1.0, 0.0, 0.0, 0.7)
|
||||||
|
if bm is not None:
|
||||||
|
for f in bm.faces:
|
||||||
|
height = 1.0
|
||||||
|
if height_lay is not None:
|
||||||
|
selected = f.select
|
||||||
|
if selected:
|
||||||
|
continue
|
||||||
|
height = f[height_lay]
|
||||||
|
draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color)
|
||||||
|
else:
|
||||||
|
for p in obj.data.polygons:
|
||||||
|
height = 1.0
|
||||||
|
if height_lay is not None:
|
||||||
|
height = height_lay.data[p.index].value
|
||||||
|
draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color)
|
||||||
|
|
||||||
|
# Selected colors
|
||||||
|
if bm is not None:
|
||||||
|
top_color = (1.0, 0.0, 1.0, 0.7)
|
||||||
|
side_color = (0.0, 1.0, 0.0, 0.7)
|
||||||
|
# Avoid z-fighting on selected lines
|
||||||
|
#bgl.glDepthRange(-0.001, 0.999)
|
||||||
|
for f in bm.faces:
|
||||||
|
if height_lay is not None:
|
||||||
|
selected = f.select
|
||||||
|
if not selected:
|
||||||
|
continue
|
||||||
|
height = f[height_lay]
|
||||||
|
draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color)
|
||||||
|
#bgl.glEnd()
|
||||||
|
#bgl.glDepthRange(0.0, 1.0)
|
||||||
|
|
||||||
|
line_shader.bind()
|
||||||
|
batch = batch_for_shader(line_shader, 'LINES', {"pos": pos_vbo, "color": color_vbo})
|
||||||
|
batch.draw(line_shader)
|
||||||
|
|
||||||
|
|
||||||
|
# Toggle height viz button
|
||||||
|
class PathHeightDrawOperator(bpy.types.Operator):
|
||||||
|
bl_idname = "view3d.toggle_path_height_visualization"
|
||||||
|
bl_label = "Toggle PATH height visualization"
|
||||||
|
_handle_3d = None
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# the arguments we pass the the callback
|
||||||
|
args = (self, context)
|
||||||
|
# Add the region OpenGL drawing callback
|
||||||
|
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
|
||||||
|
if self._handle_3d is None:
|
||||||
|
PathHeightDrawOperator._handle_3d = \
|
||||||
|
bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d, args, 'WINDOW', 'POST_VIEW')
|
||||||
|
else:
|
||||||
|
bpy.types.SpaceView3D.draw_handler_remove(PathHeightDrawOperator._handle_3d, 'WINDOW')
|
||||||
|
PathHeightDrawOperator._handle_3d = None
|
||||||
|
|
||||||
|
for ar in bpy.context.screen.areas:
|
||||||
|
if ar.type == 'VIEW_3D':
|
||||||
|
ar.tag_redraw()
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Toggle background wire button
|
||||||
|
class PathBackgroundWireframeOperator(bpy.types.Operator):
|
||||||
|
bl_idname = "view3d.toggle_path_background_wireframe"
|
||||||
|
bl_label = "Toggle PATH background wireframe"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if context.scene.background_set:
|
||||||
|
to_wire = False
|
||||||
|
for o in context.scene.background_set.objects:
|
||||||
|
if o.display_type != 'WIRE':
|
||||||
|
to_wire = True
|
||||||
|
break
|
||||||
|
if to_wire:
|
||||||
|
for o in context.scene.background_set.objects:
|
||||||
|
o.display_type = 'WIRE'
|
||||||
|
else:
|
||||||
|
for o in context.scene.background_set.objects:
|
||||||
|
o.display_type = 'TEXTURED'
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Edit panel
|
||||||
|
def draw(layout, context):
|
||||||
|
layout.prop_search(context.scene, 'hecl_path_obj', context.scene, 'objects')
|
||||||
|
layout.operator('view3d.toggle_path_background_wireframe', text='Toggle Background Wire', icon='SHADING_WIRE')
|
||||||
|
if PathHeightDrawOperator._handle_3d:
|
||||||
|
icon = 'HIDE_OFF'
|
||||||
|
else:
|
||||||
|
icon = 'HIDE_ON'
|
||||||
|
layout.operator('view3d.toggle_path_height_visualization', text='Toggle Height Viz', icon=icon)
|
||||||
|
if HeightRef().ready:
|
||||||
|
layout.prop(context.window_manager, 'hecl_height_prop', text='Height')
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')
|
||||||
|
bpy.types.Material.retro_path_type_mask = bpy.props.IntProperty(name='Retro: Path Type Mask')
|
||||||
|
bpy.types.WindowManager.hecl_height_prop = bpy.props.FloatProperty(
|
||||||
|
get=get_height, set=set_height, options={'HIDDEN'})
|
||||||
|
bpy.types.Scene.hecl_path_obj = bpy.props.StringProperty(
|
||||||
|
name='HECL Path Object',
|
||||||
|
description='Blender Mesh Object to export during PATH\'s cook process')
|
||||||
|
bpy.utils.register_class(PathHeightDrawOperator)
|
||||||
|
bpy.utils.register_class(PathBackgroundWireframeOperator)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(PathHeightDrawOperator)
|
||||||
|
bpy.utils.unregister_class(PathBackgroundWireframeOperator)
|
|
@ -0,0 +1,208 @@
|
||||||
|
'''
|
||||||
|
This file provides a means to encode animation key-channels
|
||||||
|
in an interleaved, sparse array for use by the runtime
|
||||||
|
'''
|
||||||
|
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
import mathutils
|
||||||
|
|
||||||
|
# Regex RNA path matchers
|
||||||
|
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
|
||||||
|
rotation_matcher = re.compile(r'pose.bones\["(\S+)"\].rotation')
|
||||||
|
location_matcher = re.compile(r'pose.bones\["(\S+)"\].location')
|
||||||
|
|
||||||
|
# Effect transform modes
|
||||||
|
EFFECT_XF_MODES = {'STATIONARY':0, 'WORLD':1, 'LOCAL':2}
|
||||||
|
|
||||||
|
# Generate animation info
|
||||||
|
def generate_animation_info(action, res_db, rani_db_id, arg_package, endian_char='<'):
|
||||||
|
|
||||||
|
# Set of frame indices
|
||||||
|
frame_set = set()
|
||||||
|
|
||||||
|
# Set of unique bone names
|
||||||
|
bone_set = set()
|
||||||
|
|
||||||
|
# Scan through all fcurves to build animated bone set
|
||||||
|
for fcurve in action.fcurves:
|
||||||
|
data_path = fcurve.data_path
|
||||||
|
scale_match = scale_matcher.match(data_path)
|
||||||
|
rotation_match = rotation_matcher.match(data_path)
|
||||||
|
location_match = location_matcher.match(data_path)
|
||||||
|
|
||||||
|
if scale_match:
|
||||||
|
bone_set.add(scale_match.group(1))
|
||||||
|
elif rotation_match:
|
||||||
|
bone_set.add(rotation_match.group(1))
|
||||||
|
elif location_match:
|
||||||
|
bone_set.add(location_match.group(1))
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Count unified keyframes for interleaving channel data
|
||||||
|
for key in fcurve.keyframe_points:
|
||||||
|
frame_set.add(int(key.co[0]))
|
||||||
|
|
||||||
|
# Relate fcurves per-frame / per-bone and assemble data
|
||||||
|
key_stream = bytearray()
|
||||||
|
key_stream += struct.pack(endian_char + 'II', len(frame_set), len(bone_set))
|
||||||
|
duration = action.frame_range[1] / action.hecl_fps
|
||||||
|
interval = 1.0 / action.hecl_fps
|
||||||
|
key_stream += struct.pack(endian_char + 'ff', duration, interval)
|
||||||
|
|
||||||
|
# Generate keyframe bitmap
|
||||||
|
fr = int(round(action.frame_range[1]))
|
||||||
|
key_stream += struct.pack(endian_char + 'I', fr)
|
||||||
|
bitmap_words = [0] * (fr // 32)
|
||||||
|
if fr % 32:
|
||||||
|
bitmap_words.append(0)
|
||||||
|
for i in range(fr):
|
||||||
|
if i in frame_set:
|
||||||
|
bitmap_words[i//32] |= 1 << i%32
|
||||||
|
for word in bitmap_words:
|
||||||
|
key_stream += struct.pack(endian_char + 'I', word)
|
||||||
|
|
||||||
|
|
||||||
|
# Build bone table
|
||||||
|
bone_list = []
|
||||||
|
for bone in bone_set:
|
||||||
|
fc_dict = dict()
|
||||||
|
rotation_mode = None
|
||||||
|
property_bits = 0
|
||||||
|
for fcurve in action.fcurves:
|
||||||
|
if fcurve.data_path == 'pose.bones["'+bone+'"].scale':
|
||||||
|
if 'scale' not in fc_dict:
|
||||||
|
fc_dict['scale'] = [None, None, None]
|
||||||
|
property_bits |= 1
|
||||||
|
fc_dict['scale'][fcurve.array_index] = fcurve
|
||||||
|
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_euler':
|
||||||
|
if 'rotation_euler' not in fc_dict:
|
||||||
|
fc_dict['rotation_euler'] = [None, None, None]
|
||||||
|
rotation_mode = 'rotation_euler'
|
||||||
|
property_bits |= 2
|
||||||
|
fc_dict['rotation_euler'][fcurve.array_index] = fcurve
|
||||||
|
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_quaternion':
|
||||||
|
if 'rotation_quaternion' not in fc_dict:
|
||||||
|
fc_dict['rotation_quaternion'] = [None, None, None, None]
|
||||||
|
rotation_mode = 'rotation_quaternion'
|
||||||
|
property_bits |= 2
|
||||||
|
fc_dict['rotation_quaternion'][fcurve.array_index] = fcurve
|
||||||
|
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_axis_angle':
|
||||||
|
if 'rotation_axis_angle' not in fc_dict:
|
||||||
|
fc_dict['rotation_axis_angle'] = [None, None, None, None]
|
||||||
|
rotation_mode = 'rotation_axis_angle'
|
||||||
|
property_bits |= 2
|
||||||
|
fc_dict['rotation_axis_angle'][fcurve.array_index] = fcurve
|
||||||
|
elif fcurve.data_path == 'pose.bones["'+bone+'"].location':
|
||||||
|
if 'location' not in fc_dict:
|
||||||
|
fc_dict['location'] = [None, None, None]
|
||||||
|
property_bits |= 4
|
||||||
|
fc_dict['location'][fcurve.array_index] = fcurve
|
||||||
|
bone_list.append((bone, rotation_mode, fc_dict))
|
||||||
|
bone_head = hashbone(bone)
|
||||||
|
bone_head |= (property_bits << 28)
|
||||||
|
key_stream += struct.pack(endian_char + 'I', bone_head)
|
||||||
|
|
||||||
|
# Interleave / interpolate keyframe data
|
||||||
|
for frame in sorted(frame_set):
|
||||||
|
for bone in bone_list:
|
||||||
|
|
||||||
|
bone_name = bone[0]
|
||||||
|
rotation_mode = bone[1]
|
||||||
|
fc_dict = bone[2]
|
||||||
|
|
||||||
|
# Scale curves
|
||||||
|
if 'scale' in fc_dict:
|
||||||
|
for comp in range(3):
|
||||||
|
if fc_dict['scale'][comp]:
|
||||||
|
key_stream += struct.pack(endian_char + 'f', fc_dict['scale'][comp].evaluate(frame))
|
||||||
|
else:
|
||||||
|
key_stream += struct.pack(endian_char + 'f', 0.0)
|
||||||
|
|
||||||
|
# Rotation curves
|
||||||
|
if rotation_mode == 'rotation_quaternion':
|
||||||
|
for comp in range(4):
|
||||||
|
if fc_dict['rotation_quaternion'][comp]:
|
||||||
|
key_stream += struct.pack(endian_char + 'f', fc_dict['rotation_quaternion'][comp].evaluate(frame))
|
||||||
|
else:
|
||||||
|
key_stream += struct.pack(endian_char + 'f', 0.0)
|
||||||
|
|
||||||
|
elif rotation_mode == 'rotation_euler':
|
||||||
|
euler = [0.0, 0.0, 0.0]
|
||||||
|
for comp in range(3):
|
||||||
|
if fc_dict['rotation_euler'][comp]:
|
||||||
|
euler[comp] = fc_dict['rotation_euler'][comp].evaluate(frame)
|
||||||
|
euler_o = mathutils.Euler(euler, 'XYZ')
|
||||||
|
quat = euler_o.to_quaternion()
|
||||||
|
key_stream += struct.pack(endian_char + 'ffff', quat[0], quat[1], quat[2], quat[3])
|
||||||
|
|
||||||
|
elif rotation_mode == 'rotation_axis_angle':
|
||||||
|
axis_angle = [0.0, 0.0, 0.0, 0.0]
|
||||||
|
for comp in range(4):
|
||||||
|
if fc_dict['rotation_axis_angle'][comp]:
|
||||||
|
axis_angle[comp] = fc_dict['rotation_axis_angle'][comp].evaluate(frame)
|
||||||
|
quat = mathutils.Quaternion(axis_angle[1:4], axis_angle[0])
|
||||||
|
key_stream += struct.pack(endian_char + 'ffff', quat[0], quat[1], quat[2], quat[3])
|
||||||
|
|
||||||
|
# Location curves
|
||||||
|
if 'location' in fc_dict:
|
||||||
|
for comp in range(3):
|
||||||
|
if fc_dict['location'][comp]:
|
||||||
|
key_stream += struct.pack(endian_char + 'f', fc_dict['location'][comp].evaluate(frame))
|
||||||
|
else:
|
||||||
|
key_stream += struct.pack(endian_char + 'f', 0.0)
|
||||||
|
|
||||||
|
|
||||||
|
# Generate event buffer
|
||||||
|
event_buf = bytearray()
|
||||||
|
if hasattr(action, 'hecl_events'):
|
||||||
|
c1 = 0
|
||||||
|
c2 = 0
|
||||||
|
c3 = 0
|
||||||
|
c4 = 0
|
||||||
|
for event in action.hecl_events:
|
||||||
|
if event.type == 'LOOP':
|
||||||
|
c1 += 1
|
||||||
|
elif event.type == 'UEVT':
|
||||||
|
c2 += 1
|
||||||
|
elif event.type == 'EFFECT':
|
||||||
|
c3 += 1
|
||||||
|
elif event.type == 'SOUND':
|
||||||
|
c4 += 1
|
||||||
|
event_buf += struct.pack(endian_char + 'IIII', c1, c2, c3, c4)
|
||||||
|
|
||||||
|
for event in action.hecl_events:
|
||||||
|
if event.type == 'LOOP':
|
||||||
|
event_buf += struct.pack(endian_char + 'fi', event.time, event.loop_data.bool)
|
||||||
|
|
||||||
|
for event in action.hecl_events:
|
||||||
|
if event.type == 'UEVT':
|
||||||
|
event_buf += struct.pack(endian_char + 'fii', event.time, event.uevt_data.type,
|
||||||
|
hashbone(event.uevt_data.bone_name))
|
||||||
|
|
||||||
|
for event in action.hecl_events:
|
||||||
|
if event.type == 'EFFECT':
|
||||||
|
effect_db_id, effect_hash = res_db.search_for_resource(event.effect_data.uid, arg_package)
|
||||||
|
if effect_hash:
|
||||||
|
res_db.register_dependency(rani_db_id, effect_db_id)
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Error - unable to find effect '{0}'".format(event.effect_data.uid))
|
||||||
|
event_buf += struct.pack(endian_char + 'fiifi', event.time, event.effect_data.frame_count,
|
||||||
|
hashbone(event.effect_data.bone_name), event.effect_data.scale,
|
||||||
|
EFFECT_XF_MODES[event.effect_data.transform_mode])
|
||||||
|
event_buf += effect_hash
|
||||||
|
|
||||||
|
for event in action.hecl_events:
|
||||||
|
if event.type == 'SOUND':
|
||||||
|
sid = int.from_bytes(event.sound_data.sound_id.encode()[:4], byteorder='big', signed=False)
|
||||||
|
event_buf += struct.pack(endian_char + 'fIff', event.time, sid,
|
||||||
|
event.sound_data.ref_amp, event.sound_data.ref_dist)
|
||||||
|
|
||||||
|
else:
|
||||||
|
event_buf += struct.pack('IIII',0,0,0,0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return key_stream + event_buf
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
# Action update (if anything important changes)
|
||||||
|
def active_action_update(self, context):
|
||||||
|
if not bpy.app.background:
|
||||||
|
if context.scene.hecl_type == 'ACTOR' and context.scene.hecl_auto_select:
|
||||||
|
if SACTAction_load.poll(context):
|
||||||
|
bpy.ops.scene.sactaction_load()
|
||||||
|
|
||||||
|
# Action type update
|
||||||
|
def action_type_update(self, context):
|
||||||
|
if not bpy.app.background:
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
active_action_update(self, context)
|
||||||
|
|
||||||
|
# Actor action class
|
||||||
|
class SACTAction(bpy.types.PropertyGroup):
|
||||||
|
name: bpy.props.StringProperty(name="Action Name")
|
||||||
|
|
||||||
|
# Panel draw
|
||||||
|
def draw(layout, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.alignment = 'LEFT'
|
||||||
|
row.prop(actor_data, 'show_actions', text="Actions", icon='ACTION', emboss=False)
|
||||||
|
if actor_data.show_actions:
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.template_list("UI_UL_list", "SCENE_UL_SACTActions",
|
||||||
|
actor_data, 'actions', actor_data, 'active_action')
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator("scene.sactaction_add", icon="ADD", text="")
|
||||||
|
col.operator("scene.sactaction_remove", icon="REMOVE", text="")
|
||||||
|
|
||||||
|
if len(actor_data.actions) and actor_data.active_action >= 0:
|
||||||
|
action = actor_data.actions[actor_data.active_action]
|
||||||
|
|
||||||
|
# Load action operator
|
||||||
|
if not bpy.context.scene.hecl_auto_select:
|
||||||
|
layout.operator("scene.sactaction_load", icon='FILE_TICK', text="Load Action")
|
||||||
|
|
||||||
|
# Name edit field
|
||||||
|
layout.prop_search(action, 'name', bpy.data, 'actions', text="Name")
|
||||||
|
linked_action = None
|
||||||
|
if bpy.data.actions.find(action.name) != -1:
|
||||||
|
linked_action = bpy.data.actions[action.name]
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
if linked_action is None:
|
||||||
|
layout.label(text="Source action not set", icon='ERROR')
|
||||||
|
else:
|
||||||
|
#layout.prop(linked_action, 'hecl_index', text="Index")
|
||||||
|
#layout.prop(linked_action, 'hecl_anim_props', text="Props")
|
||||||
|
layout.prop(linked_action, 'anim_id', text="ANIM ID")
|
||||||
|
layout.prop(linked_action, 'hecl_fps', text="Frame Rate")
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(context.scene, 'hecl_auto_remap', text="60-fps Remap")
|
||||||
|
row.prop(linked_action, 'hecl_additive', text="Additive")
|
||||||
|
#row.prop(linked_action, 'hecl_looping', text="Looping")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Action 'add' operator
|
||||||
|
class SACTAction_add(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactaction_add"
|
||||||
|
bl_label = "New HECL Actor Action"
|
||||||
|
bl_description = "Add New HECL Actor Action to active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
action_name = 'ActorAction'
|
||||||
|
if action_name in actor_data.actions:
|
||||||
|
action_name = 'ActorAction.001'
|
||||||
|
action_idx = 1
|
||||||
|
while action_name in actor_data.actions:
|
||||||
|
action_idx += 1
|
||||||
|
action_name = 'ActorAction.{:0>3}'.format(action_idx)
|
||||||
|
action = actor_data.actions.add()
|
||||||
|
action.name = action_name
|
||||||
|
actor_data.active_action = len(actor_data.actions)-1
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Action 'remove' operator
|
||||||
|
class SACTAction_remove(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactaction_remove"
|
||||||
|
bl_label = "Remove HECL Actor Action"
|
||||||
|
bl_description = "Remove HECL Actor Action from active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
actor_data.active_action >= 0 and
|
||||||
|
len(actor_data.actions))
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
actor_data.actions.remove(actor_data.active_action)
|
||||||
|
actor_data.active_action -= 1
|
||||||
|
if actor_data.active_action == -1:
|
||||||
|
actor_data.active_action = 0
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
# Action 'load' operator
|
||||||
|
class SACTAction_load(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactaction_load"
|
||||||
|
bl_label = "Load HECL Actor Action"
|
||||||
|
bl_description = "Loads Action for playback in active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
len(context.scene.hecl_sact_data.actions) and
|
||||||
|
context.scene.hecl_sact_data.active_action >= 0)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
if actor_data.active_action not in range(len(actor_data.actions)):
|
||||||
|
return {'CANCELLED'}
|
||||||
|
if actor_data.active_subtype not in range(len(actor_data.subtypes)):
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
action_data = actor_data.actions[actor_data.active_action]
|
||||||
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
|
||||||
|
# Refresh event markers
|
||||||
|
#SACTEvent.clear_event_markers(actor_data, context)
|
||||||
|
#SACTEvent.update_action_events(None)
|
||||||
|
#SACTEvent.active_event_update(None, context)
|
||||||
|
|
||||||
|
# Clear animation data for all subtypes
|
||||||
|
for s in range(len(actor_data.subtypes)):
|
||||||
|
st = actor_data.subtypes[s]
|
||||||
|
if st.linked_armature in bpy.data.objects:
|
||||||
|
am = bpy.data.objects[st.linked_armature]
|
||||||
|
am.animation_data_clear()
|
||||||
|
|
||||||
|
# Set single action into armature
|
||||||
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
|
armature_objs = [bpy.data.objects[subtype.linked_armature]]
|
||||||
|
|
||||||
|
for attachment in actor_data.attachments:
|
||||||
|
if attachment.linked_armature in bpy.data.objects:
|
||||||
|
attachment_armature = bpy.data.objects[attachment.linked_armature]
|
||||||
|
armature_objs.append(attachment_armature)
|
||||||
|
|
||||||
|
for armature_obj in armature_objs:
|
||||||
|
for bone in armature_obj.pose.bones:
|
||||||
|
bone.location = (0,0,0)
|
||||||
|
bone.rotation_quaternion = (1,0,0,0)
|
||||||
|
bone.scale = (1,1,1)
|
||||||
|
|
||||||
|
if action_data.name in bpy.data.actions:
|
||||||
|
action_obj =\
|
||||||
|
bpy.data.actions[action_data.name]
|
||||||
|
armature_obj.animation_data_clear()
|
||||||
|
armature_obj.animation_data_create()
|
||||||
|
armature_obj.animation_data.action = action_obj
|
||||||
|
|
||||||
|
# Time remapping
|
||||||
|
if context.scene.hecl_auto_remap:
|
||||||
|
bpy.context.scene.render.fps = 60
|
||||||
|
bpy.context.scene.render.frame_map_old = action_obj.hecl_fps
|
||||||
|
bpy.context.scene.render.frame_map_new = 60
|
||||||
|
bpy.context.scene.frame_start = 0
|
||||||
|
bpy.context.scene.frame_end = action_obj.frame_range[1] * (60 / action_obj.hecl_fps)
|
||||||
|
else:
|
||||||
|
bpy.context.scene.render.fps = action_obj.hecl_fps
|
||||||
|
bpy.context.scene.render.frame_map_old = action_obj.hecl_fps
|
||||||
|
bpy.context.scene.render.frame_map_new = action_obj.hecl_fps
|
||||||
|
bpy.context.scene.frame_start = 0
|
||||||
|
bpy.context.scene.frame_end = action_obj.frame_range[1]
|
||||||
|
|
||||||
|
# Events
|
||||||
|
#SACTEvent.clear_action_events(self, context, actor_data)
|
||||||
|
#SACTEvent.load_action_events(self, context, action_obj, 0)
|
||||||
|
|
||||||
|
else:
|
||||||
|
armature_obj.animation_data_clear()
|
||||||
|
self.report({'WARNING'}, "Unable to load action; check HECL panel")
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.report({'WARNING'}, "Unable to load armature; check HECL panel")
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
bpy.types.Action.hecl_fps = bpy.props.IntProperty(name="HECL Actor Sub-action Frame-rate",
|
||||||
|
description="Frame-rate at which action is authored; to be interpolated at 60-fps by runtime",
|
||||||
|
min=1, max=60, default=30, update=active_action_update)
|
||||||
|
bpy.utils.register_class(SACTAction)
|
||||||
|
bpy.utils.register_class(SACTAction_add)
|
||||||
|
bpy.utils.register_class(SACTAction_load)
|
||||||
|
bpy.utils.register_class(SACTAction_remove)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(SACTAction)
|
||||||
|
bpy.utils.unregister_class(SACTAction_add)
|
||||||
|
bpy.utils.unregister_class(SACTAction_load)
|
||||||
|
bpy.utils.unregister_class(SACTAction_remove)
|
|
@ -0,0 +1,408 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
# Subtype update (if anything important changes)
|
||||||
|
def active_subtype_update(self, context):
|
||||||
|
if context.scene.hecl_type == 'ACTOR' and context.scene.hecl_auto_select:
|
||||||
|
if SACTSubtype_load.poll(context):
|
||||||
|
bpy.ops.scene.sactsubtype_load()
|
||||||
|
|
||||||
|
|
||||||
|
# Actor subtype overlay class
|
||||||
|
class SACTSubtypeOverlay(bpy.types.PropertyGroup):
|
||||||
|
name: bpy.props.StringProperty(name="Overlay Name")
|
||||||
|
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
||||||
|
show_overlay: bpy.props.BoolProperty(name="Show Overlay Mesh", update=active_subtype_update)
|
||||||
|
|
||||||
|
# Actor attachment class
|
||||||
|
class SACTAttachment(bpy.types.PropertyGroup):
|
||||||
|
name: bpy.props.StringProperty(name="Attachment Name")
|
||||||
|
linked_armature: bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
|
||||||
|
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
||||||
|
show_attachment: bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update)
|
||||||
|
|
||||||
|
# Actor subtype class
|
||||||
|
class SACTSubtype(bpy.types.PropertyGroup):
|
||||||
|
name: bpy.props.StringProperty(name="Actor Mesh Name")
|
||||||
|
linked_armature: bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
|
||||||
|
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
||||||
|
show_mesh: bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update)
|
||||||
|
|
||||||
|
overlays: bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List")
|
||||||
|
active_overlay: bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update)
|
||||||
|
|
||||||
|
# Panel draw
|
||||||
|
def draw(layout, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.alignment = 'LEFT'
|
||||||
|
row.prop(actor_data, 'show_subtypes', text="Subtypes", icon='MESH_DATA', emboss=False)
|
||||||
|
if actor_data.show_subtypes:
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypes",
|
||||||
|
actor_data, 'subtypes', actor_data, 'active_subtype')
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator("scene.sactsubtype_add", icon="ADD", text="")
|
||||||
|
col.operator("scene.sactsubtype_remove", icon="REMOVE", text="")
|
||||||
|
|
||||||
|
if len(actor_data.subtypes) and actor_data.active_subtype >= 0:
|
||||||
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
|
||||||
|
# Load subtype operator
|
||||||
|
if not bpy.context.scene.hecl_auto_select:
|
||||||
|
layout.operator("scene.sactsubtype_load", icon='FILE_TICK', text="Load Subtype")
|
||||||
|
|
||||||
|
# Name edit field
|
||||||
|
layout.prop(subtype, 'name', text="Name")
|
||||||
|
|
||||||
|
|
||||||
|
# Link external armature search
|
||||||
|
layout.prop_search(subtype, 'linked_armature', bpy.data, 'objects', text="Armature")
|
||||||
|
linked_armature = None
|
||||||
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
|
linked_armature = bpy.data.objects[subtype.linked_armature]
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
if linked_armature is None:
|
||||||
|
layout.label(text="Source armature not set", icon='ERROR')
|
||||||
|
elif linked_armature is not None and linked_armature.type != 'ARMATURE':
|
||||||
|
layout.label(text="Source armature is not an 'ARMATURE'", icon='ERROR')
|
||||||
|
|
||||||
|
|
||||||
|
# Link external mesh search
|
||||||
|
layout.prop_search(subtype, 'linked_mesh', bpy.data, 'objects', text="Mesh")
|
||||||
|
linked_mesh = None
|
||||||
|
if subtype.linked_mesh in bpy.data.objects:
|
||||||
|
linked_mesh = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
layout.prop(subtype, 'show_mesh', text="Show Mesh")
|
||||||
|
|
||||||
|
# Mesh overlays
|
||||||
|
layout.label(text="Overlay Meshes:")
|
||||||
|
row = layout.row()
|
||||||
|
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypeOverlays",
|
||||||
|
subtype, 'overlays', subtype, 'active_overlay')
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator("scene.sactsubtypeoverlay_add", icon="ADD", text="")
|
||||||
|
col.operator("scene.sactsubtypeoverlay_remove", icon="REMOVE", text="")
|
||||||
|
|
||||||
|
overlay_mesh = None
|
||||||
|
if len(subtype.overlays) and subtype.active_overlay >= 0:
|
||||||
|
overlay = subtype.overlays[subtype.active_overlay]
|
||||||
|
layout.prop(overlay, 'name', text="Name")
|
||||||
|
layout.prop_search(overlay, 'linked_mesh', bpy.data, 'objects', text="Mesh")
|
||||||
|
if overlay.linked_mesh in bpy.data.objects:
|
||||||
|
overlay_mesh = bpy.data.objects[overlay.linked_mesh]
|
||||||
|
layout.prop(overlay, 'show_overlay', text="Show Overlay")
|
||||||
|
|
||||||
|
# Mesh attachments
|
||||||
|
layout.label(text="Attachment Meshes:")
|
||||||
|
row = layout.row()
|
||||||
|
row.template_list("UI_UL_list", "SCENE_UL_SACTAttachments",
|
||||||
|
actor_data, 'attachments', actor_data, 'active_attachment')
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator("scene.sactattachment_add", icon="ADD", text="")
|
||||||
|
col.operator("scene.sactattachment_remove", icon="REMOVE", text="")
|
||||||
|
|
||||||
|
attachment_armature = linked_armature
|
||||||
|
attachment_mesh = None
|
||||||
|
if len(actor_data.attachments) and actor_data.active_attachment >= 0:
|
||||||
|
attachment = actor_data.attachments[actor_data.active_attachment]
|
||||||
|
layout.prop(attachment, 'name', text="Name")
|
||||||
|
layout.prop_search(attachment, 'linked_armature', bpy.data, 'objects', text="Armature")
|
||||||
|
if attachment.linked_armature in bpy.data.objects:
|
||||||
|
attachment_armature = bpy.data.objects[attachment.linked_armature]
|
||||||
|
layout.prop_search(attachment, 'linked_mesh', bpy.data, 'objects', text="Mesh")
|
||||||
|
if attachment.linked_mesh in bpy.data.objects:
|
||||||
|
attachment_mesh = bpy.data.objects[attachment.linked_mesh]
|
||||||
|
layout.prop(attachment, 'show_attachment', text="Show Attachment")
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
if linked_mesh is None:
|
||||||
|
layout.label(text="Source mesh not set", icon='ERROR')
|
||||||
|
elif linked_mesh.type != 'MESH':
|
||||||
|
layout.label(text="Source mesh not 'MESH'", icon='ERROR')
|
||||||
|
elif linked_armature is not None and linked_mesh not in linked_armature.children:
|
||||||
|
layout.label(linked_mesh.name+" not a child of "+linked_armature.name, icon='ERROR')
|
||||||
|
elif linked_mesh.parent_type != 'ARMATURE':
|
||||||
|
layout.label(text="Source mesh not 'ARMATURE' parent type", icon='ERROR')
|
||||||
|
|
||||||
|
if overlay_mesh:
|
||||||
|
if overlay_mesh.type != 'MESH':
|
||||||
|
layout.label(text="Overlay mesh not 'MESH'", icon='ERROR')
|
||||||
|
elif overlay_mesh.parent_type != 'ARMATURE':
|
||||||
|
layout.label(text="Overlay mesh not 'ARMATURE' parent type", icon='ERROR')
|
||||||
|
|
||||||
|
if attachment_mesh:
|
||||||
|
if attachment_mesh.type != 'MESH':
|
||||||
|
layout.label(text="Attachment mesh not 'MESH'", icon='ERROR')
|
||||||
|
elif attachment_armature is not None and attachment_mesh not in attachment_armature.children:
|
||||||
|
layout.label(attachment_mesh.name+" not a child of "+attachment_armature.name, icon='ERROR')
|
||||||
|
elif attachment_mesh.parent_type != 'ARMATURE':
|
||||||
|
layout.label(text="Attachment mesh not 'ARMATURE' parent type", icon='ERROR')
|
||||||
|
|
||||||
|
|
||||||
|
# Subtype 'add' operator
|
||||||
|
class SACTSubtype_add(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactsubtype_add"
|
||||||
|
bl_label = "New HECL Actor Subtype"
|
||||||
|
bl_description = "Add New HECL Actor Subtype to active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
mesh_name = 'ActorMesh'
|
||||||
|
if mesh_name in actor_data.subtypes:
|
||||||
|
mesh_name = 'ActorMesh.001'
|
||||||
|
mesh_idx = 1
|
||||||
|
while mesh_name in actor_data.subtypes:
|
||||||
|
mesh_idx += 1
|
||||||
|
mesh_name = 'ActorMesh.{:0>3}'.format(mesh_idx)
|
||||||
|
mesh = actor_data.subtypes.add()
|
||||||
|
mesh.name = mesh_name
|
||||||
|
actor_data.active_subtype = len(actor_data.subtypes)-1
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Subtype 'remove' operator
|
||||||
|
class SACTSubtype_remove(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactsubtype_remove"
|
||||||
|
bl_label = "Remove HECL Actor Subtype"
|
||||||
|
bl_description = "Remove HECL Actor Subtype from active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
actor_data.active_subtype >= 0 and
|
||||||
|
len(actor_data.subtypes))
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
actor_data.subtypes.remove(actor_data.active_subtype)
|
||||||
|
actor_data.active_subtype -= 1
|
||||||
|
if actor_data.active_subtype == -1:
|
||||||
|
actor_data.active_subtype = 0
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
def parent_armature(mesh_obj, arm_obj):
|
||||||
|
mesh_obj.parent = None
|
||||||
|
for mod in mesh_obj.modifiers:
|
||||||
|
if mod.type == 'ARMATURE':
|
||||||
|
mod.object = arm_obj
|
||||||
|
return
|
||||||
|
mod = mesh_obj.modifiers.new('Parent', 'ARMATURE')
|
||||||
|
mod.object = arm_obj
|
||||||
|
#mesh_obj.parent = arm_obj
|
||||||
|
#mesh_obj.parent_type = 'ARMATURE'
|
||||||
|
|
||||||
|
# Subtype 'load' operator
|
||||||
|
class SACTSubtype_load(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactsubtype_load"
|
||||||
|
bl_label = "Load HECL Actor Subtype"
|
||||||
|
bl_description = "Loads Subtype for viewing in active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
len(context.scene.hecl_sact_data.subtypes) and
|
||||||
|
context.scene.hecl_sact_data.active_subtype >= 0)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
|
||||||
|
# Armature
|
||||||
|
linked_armature = None
|
||||||
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
|
linked_armature = bpy.data.objects[subtype.linked_armature]
|
||||||
|
else:
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Hide armature children
|
||||||
|
for object in linked_armature.children:
|
||||||
|
if object.name in context.scene.objects:
|
||||||
|
object.hide_set(True)
|
||||||
|
|
||||||
|
# Hide all meshes (incl overlays)
|
||||||
|
for subtype_data in actor_data.subtypes:
|
||||||
|
if subtype_data.linked_mesh in bpy.data.objects:
|
||||||
|
mesh = bpy.data.objects[subtype_data.linked_mesh]
|
||||||
|
if mesh.name in context.scene.objects:
|
||||||
|
mesh.hide_set(True)
|
||||||
|
for overlay in subtype_data.overlays:
|
||||||
|
if overlay.linked_mesh in bpy.data.objects:
|
||||||
|
mesh = bpy.data.objects[overlay.linked_mesh]
|
||||||
|
if mesh.name in context.scene.objects:
|
||||||
|
mesh.hide_set(True)
|
||||||
|
|
||||||
|
# Hide/Show selected attachment meshes
|
||||||
|
for attachment in actor_data.attachments:
|
||||||
|
if attachment.linked_mesh in bpy.data.objects:
|
||||||
|
mesh_obj = bpy.data.objects[attachment.linked_mesh]
|
||||||
|
if mesh_obj.name in context.scene.objects:
|
||||||
|
mesh_obj.hide_set(not attachment.show_attachment)
|
||||||
|
attachment_armature = linked_armature
|
||||||
|
if attachment.linked_armature in bpy.data.objects:
|
||||||
|
attachment_armature = bpy.data.objects[attachment.linked_armature]
|
||||||
|
if mesh_obj != attachment_armature:
|
||||||
|
parent_armature(mesh_obj, attachment_armature)
|
||||||
|
|
||||||
|
# Show only the chosen subtype (and selected overlays)
|
||||||
|
if subtype.linked_mesh in bpy.data.objects:
|
||||||
|
mesh_obj = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
if subtype.show_mesh:
|
||||||
|
mesh_obj.hide_set(False)
|
||||||
|
if mesh_obj != linked_armature:
|
||||||
|
parent_armature(mesh_obj, linked_armature)
|
||||||
|
for overlay in subtype.overlays:
|
||||||
|
if overlay.linked_mesh in bpy.data.objects:
|
||||||
|
mesh_obj = bpy.data.objects[overlay.linked_mesh]
|
||||||
|
if overlay.show_overlay:
|
||||||
|
mesh_obj.hide_set(False)
|
||||||
|
if mesh_obj != linked_armature:
|
||||||
|
parent_armature(mesh_obj, linked_armature)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
# Subtype overlay 'add' operator
|
||||||
|
class SACTSubtypeOverlay_add(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactsubtypeoverlay_add"
|
||||||
|
bl_label = "New HECL Actor Subtype Overlay"
|
||||||
|
bl_description = "Add New HECL Actor Subtype Overlay"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
len(actor_data.subtypes) and actor_data.active_subtype >= 0)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
overlay_name = 'ActorOverlay'
|
||||||
|
if overlay_name in subtype.overlays:
|
||||||
|
overlay_name = 'ActorOverlay.001'
|
||||||
|
overlay_idx = 1
|
||||||
|
while overlay_name in subtype.overlays:
|
||||||
|
overlay_idx += 1
|
||||||
|
overlay_name = 'ActorOverlay.{:0>3}'.format(overlay_idx)
|
||||||
|
overlay = subtype.overlays.add()
|
||||||
|
overlay.name = overlay_name
|
||||||
|
subtype.active_overlay = len(subtype.overlays)-1
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Subtype overlay 'remove' operator
|
||||||
|
class SACTSubtypeOverlay_remove(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactsubtypeoverlay_remove"
|
||||||
|
bl_label = "Remove HECL Actor Subtype Overlay"
|
||||||
|
bl_description = "Remove HECL Actor Subtype Overlay"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
actor_data.active_subtype >= 0 and
|
||||||
|
len(actor_data.subtypes) and
|
||||||
|
actor_data.subtypes[actor_data.active_subtype].active_overlay >= 0 and
|
||||||
|
len(actor_data.subtypes[actor_data.active_subtype].overlays))
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
subtype.overlays.remove(subtype.active_overlay)
|
||||||
|
subtype.active_overlay -= 1
|
||||||
|
if subtype.active_overlay == -1:
|
||||||
|
subtype.active_overlay = 0
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Subtype overlay 'add' operator
|
||||||
|
class SACTAttachment_add(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactattachment_add"
|
||||||
|
bl_label = "New HECL Actor Attachment"
|
||||||
|
bl_description = "Add New HECL Actor Attachment"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
attachment_name = 'ActorAttachment'
|
||||||
|
if attachment_name in actor_data.attachments:
|
||||||
|
attachment_name = 'ActorAttachment.001'
|
||||||
|
attachment_idx = 1
|
||||||
|
while attachment_name in actor_data.attachments:
|
||||||
|
attachment_idx += 1
|
||||||
|
attachment_name = 'ActorAttachment.{:0>3}'.format(attachment_idx)
|
||||||
|
attachment = actor_data.attachments.add()
|
||||||
|
attachment.name = attachment_name
|
||||||
|
actor_data.active_attachment = len(actor_data.attachments)-1
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Subtype overlay 'remove' operator
|
||||||
|
class SACTAttachment_remove(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactattachment_remove"
|
||||||
|
bl_label = "Remove HECL Actor Attachment"
|
||||||
|
bl_description = "Remove HECL Actor Attachment"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
actor_data.active_attachment >= 0 and
|
||||||
|
len(actor_data.attachments))
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
actor_data.attachments.remove(actor_data.active_attachment)
|
||||||
|
actor_data.active_attachment -= 1
|
||||||
|
if actor_data.active_attachment == -1:
|
||||||
|
actor_data.active_attachment = 0
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(SACTSubtypeOverlay)
|
||||||
|
bpy.utils.register_class(SACTSubtypeOverlay_add)
|
||||||
|
bpy.utils.register_class(SACTSubtypeOverlay_remove)
|
||||||
|
bpy.utils.register_class(SACTAttachment)
|
||||||
|
bpy.utils.register_class(SACTAttachment_add)
|
||||||
|
bpy.utils.register_class(SACTAttachment_remove)
|
||||||
|
bpy.utils.register_class(SACTSubtype)
|
||||||
|
bpy.utils.register_class(SACTSubtype_add)
|
||||||
|
bpy.utils.register_class(SACTSubtype_remove)
|
||||||
|
bpy.utils.register_class(SACTSubtype_load)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(SACTSubtype)
|
||||||
|
bpy.utils.unregister_class(SACTSubtype_add)
|
||||||
|
bpy.utils.unregister_class(SACTSubtype_remove)
|
||||||
|
bpy.utils.unregister_class(SACTSubtype_load)
|
||||||
|
bpy.utils.unregister_class(SACTAttachment)
|
||||||
|
bpy.utils.unregister_class(SACTAttachment_add)
|
||||||
|
bpy.utils.unregister_class(SACTAttachment_remove)
|
||||||
|
bpy.utils.unregister_class(SACTSubtypeOverlay)
|
||||||
|
bpy.utils.unregister_class(SACTSubtypeOverlay_add)
|
||||||
|
bpy.utils.unregister_class(SACTSubtypeOverlay_remove)
|
|
@ -0,0 +1,488 @@
|
||||||
|
from . import SACTSubtype, SACTAction, ANIM
|
||||||
|
from .. import armature
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import bpy.path
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
from mathutils import Vector, Quaternion, Euler
|
||||||
|
|
||||||
|
# Actor data class
|
||||||
|
class SACTData(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
|
subtypes: bpy.props.CollectionProperty(type=SACTSubtype.SACTSubtype, name="Actor Subtype List")
|
||||||
|
active_subtype: bpy.props.IntProperty(name="Active Actor Subtype", default=0, update=SACTSubtype.active_subtype_update)
|
||||||
|
show_subtypes: bpy.props.BoolProperty()
|
||||||
|
|
||||||
|
attachments: bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List")
|
||||||
|
active_attachment: bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update)
|
||||||
|
|
||||||
|
actions: bpy.props.CollectionProperty(type=SACTAction.SACTAction, name="Actor Action List")
|
||||||
|
active_action: bpy.props.IntProperty(name="Active Actor Action", default=0, update=SACTAction.active_action_update)
|
||||||
|
show_actions: bpy.props.BoolProperty()
|
||||||
|
|
||||||
|
# Regex RNA path matchers
|
||||||
|
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
|
||||||
|
rotation_matcher = re.compile(r'pose.bones\["(\S+)"\].rotation')
|
||||||
|
location_matcher = re.compile(r'pose.bones\["(\S+)"\].location')
|
||||||
|
|
||||||
|
def write_action_channels(writebuf, action):
|
||||||
|
# Set of frame indices
|
||||||
|
frame_set = set()
|
||||||
|
|
||||||
|
# Set of unique bone names
|
||||||
|
bone_set = []
|
||||||
|
|
||||||
|
# Scan through all fcurves to build animated bone set
|
||||||
|
for fcurve in action.fcurves:
|
||||||
|
data_path = fcurve.data_path
|
||||||
|
scale_match = scale_matcher.match(data_path)
|
||||||
|
rotation_match = rotation_matcher.match(data_path)
|
||||||
|
location_match = location_matcher.match(data_path)
|
||||||
|
|
||||||
|
if scale_match:
|
||||||
|
if scale_match.group(1) not in bone_set:
|
||||||
|
bone_set.append(scale_match.group(1))
|
||||||
|
elif rotation_match:
|
||||||
|
if rotation_match.group(1) not in bone_set:
|
||||||
|
bone_set.append(rotation_match.group(1))
|
||||||
|
elif location_match:
|
||||||
|
if location_match.group(1) not in bone_set:
|
||||||
|
bone_set.append(location_match.group(1))
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Count unified keyframes for interleaving channel data
|
||||||
|
for key in fcurve.keyframe_points:
|
||||||
|
frame_set.add(int(key.co[0]))
|
||||||
|
|
||||||
|
# Build bone table
|
||||||
|
bone_list = []
|
||||||
|
for bone in bone_set:
|
||||||
|
fc_dict = dict()
|
||||||
|
rotation_mode = None
|
||||||
|
property_bits = 0
|
||||||
|
for fcurve in action.fcurves:
|
||||||
|
if fcurve.data_path == 'pose.bones["'+bone+'"].scale':
|
||||||
|
if 'scale' not in fc_dict:
|
||||||
|
fc_dict['scale'] = [None, None, None]
|
||||||
|
property_bits |= 4
|
||||||
|
fc_dict['scale'][fcurve.array_index] = fcurve
|
||||||
|
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_euler':
|
||||||
|
if 'rotation_euler' not in fc_dict:
|
||||||
|
fc_dict['rotation_euler'] = [None, None, None]
|
||||||
|
rotation_mode = 'rotation_euler'
|
||||||
|
property_bits |= 1
|
||||||
|
fc_dict['rotation_euler'][fcurve.array_index] = fcurve
|
||||||
|
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_quaternion':
|
||||||
|
if 'rotation_quaternion' not in fc_dict:
|
||||||
|
fc_dict['rotation_quaternion'] = [None, None, None, None]
|
||||||
|
rotation_mode = 'rotation_quaternion'
|
||||||
|
property_bits |= 1
|
||||||
|
fc_dict['rotation_quaternion'][fcurve.array_index] = fcurve
|
||||||
|
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_axis_angle':
|
||||||
|
if 'rotation_axis_angle' not in fc_dict:
|
||||||
|
fc_dict['rotation_axis_angle'] = [None, None, None, None]
|
||||||
|
rotation_mode = 'rotation_axis_angle'
|
||||||
|
property_bits |= 1
|
||||||
|
fc_dict['rotation_axis_angle'][fcurve.array_index] = fcurve
|
||||||
|
elif fcurve.data_path == 'pose.bones["'+bone+'"].location':
|
||||||
|
if 'location' not in fc_dict:
|
||||||
|
fc_dict['location'] = [None, None, None]
|
||||||
|
property_bits |= 2
|
||||||
|
fc_dict['location'][fcurve.array_index] = fcurve
|
||||||
|
bone_list.append((bone, rotation_mode, fc_dict, property_bits))
|
||||||
|
|
||||||
|
# Write out frame indices
|
||||||
|
sorted_frames = sorted(frame_set)
|
||||||
|
writebuf(struct.pack('I', len(sorted_frames)))
|
||||||
|
for frame in sorted_frames:
|
||||||
|
writebuf(struct.pack('i', frame))
|
||||||
|
|
||||||
|
# Interleave / interpolate keyframe data
|
||||||
|
writebuf(struct.pack('I', len(bone_list)))
|
||||||
|
for bone in bone_list:
|
||||||
|
|
||||||
|
bone_name = bone[0]
|
||||||
|
rotation_mode = bone[1]
|
||||||
|
fc_dict = bone[2]
|
||||||
|
property_bits = bone[3]
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(bone_name)))
|
||||||
|
writebuf(bone_name.encode())
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', property_bits))
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(sorted_frames)))
|
||||||
|
for frame in sorted_frames:
|
||||||
|
|
||||||
|
# Rotation curves
|
||||||
|
if rotation_mode == 'rotation_quaternion':
|
||||||
|
quat = [0.0]*4
|
||||||
|
for comp in range(4):
|
||||||
|
if fc_dict['rotation_quaternion'][comp]:
|
||||||
|
quat[comp] = fc_dict['rotation_quaternion'][comp].evaluate(frame)
|
||||||
|
quat = Quaternion(quat).normalized()
|
||||||
|
writebuf(struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))
|
||||||
|
|
||||||
|
elif rotation_mode == 'rotation_euler':
|
||||||
|
euler = [0.0]*3
|
||||||
|
for comp in range(3):
|
||||||
|
if fc_dict['rotation_euler'][comp]:
|
||||||
|
euler[comp] = fc_dict['rotation_euler'][comp].evaluate(frame)
|
||||||
|
euler_o = Euler(euler, 'XYZ')
|
||||||
|
quat = euler_o.to_quaternion()
|
||||||
|
writebuf(struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))
|
||||||
|
|
||||||
|
elif rotation_mode == 'rotation_axis_angle':
|
||||||
|
axis_angle = [0.0]*4
|
||||||
|
for comp in range(4):
|
||||||
|
if fc_dict['rotation_axis_angle'][comp]:
|
||||||
|
axis_angle[comp] = fc_dict['rotation_axis_angle'][comp].evaluate(frame)
|
||||||
|
quat = Quaternion(axis_angle[1:4], axis_angle[0])
|
||||||
|
writebuf(struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))
|
||||||
|
|
||||||
|
# Location curves
|
||||||
|
if 'location' in fc_dict:
|
||||||
|
writevec = [0.0]*3
|
||||||
|
for comp in range(3):
|
||||||
|
if fc_dict['location'][comp]:
|
||||||
|
writevec[comp] = fc_dict['location'][comp].evaluate(frame)
|
||||||
|
writebuf(struct.pack('fff', writevec[0], writevec[1], writevec[2]))
|
||||||
|
|
||||||
|
# Scale curves
|
||||||
|
if 'scale' in fc_dict:
|
||||||
|
writevec = [1.0]*3
|
||||||
|
for comp in range(3):
|
||||||
|
if fc_dict['scale'][comp]:
|
||||||
|
writevec[comp] = fc_dict['scale'][comp].evaluate(frame)
|
||||||
|
writebuf(struct.pack('fff', writevec[0], writevec[1], writevec[2]))
|
||||||
|
|
||||||
|
|
||||||
|
def write_action_aabb(writebuf, arm_obj, mesh_obj, action):
|
||||||
|
scene = bpy.context.scene
|
||||||
|
|
||||||
|
# Mute root channels
|
||||||
|
for fcurve in action.fcurves:
|
||||||
|
fcurve.mute = fcurve.data_path == 'pose.bones["root"].location'
|
||||||
|
|
||||||
|
# Transform against root
|
||||||
|
root_bone = arm_obj.pose.bones['root']
|
||||||
|
root_bone.location = (0.0,0.0,0.0)
|
||||||
|
if root_bone.rotation_mode == 'QUATERNION':
|
||||||
|
root_bone.rotation_quaternion = (1.0,0.0,0.0,0.0)
|
||||||
|
else:
|
||||||
|
root_bone.rotation_euler = (0.0,0.0,0.0)
|
||||||
|
|
||||||
|
# Frame 1
|
||||||
|
scene.frame_set(1)
|
||||||
|
|
||||||
|
root_aabb_min = Vector(mesh_obj.bound_box[0])
|
||||||
|
root_aabb_max = Vector(mesh_obj.bound_box[6])
|
||||||
|
|
||||||
|
# Accumulate AABB for each frame
|
||||||
|
for frame_idx in range(2, scene.frame_end + 1):
|
||||||
|
scene.frame_set(frame_idx)
|
||||||
|
|
||||||
|
test_aabb_min = Vector(mesh_obj.bound_box[0])
|
||||||
|
test_aabb_max = Vector(mesh_obj.bound_box[6])
|
||||||
|
|
||||||
|
for comp in range(3):
|
||||||
|
if test_aabb_min[comp] < root_aabb_min[comp]:
|
||||||
|
root_aabb_min[comp] = test_aabb_min[comp]
|
||||||
|
for comp in range(3):
|
||||||
|
if test_aabb_max[comp] > root_aabb_max[comp]:
|
||||||
|
root_aabb_max[comp] = test_aabb_max[comp]
|
||||||
|
|
||||||
|
# Unmute root channels
|
||||||
|
for fcurve in action.fcurves:
|
||||||
|
fcurve.mute = False
|
||||||
|
|
||||||
|
writebuf(struct.pack('ffffff',
|
||||||
|
root_aabb_min[0], root_aabb_min[1], root_aabb_min[2],
|
||||||
|
root_aabb_max[0], root_aabb_max[1], root_aabb_max[2]))
|
||||||
|
|
||||||
|
def _out_armatures(sact_data, writebuf):
|
||||||
|
writebuf(struct.pack('I', len(bpy.data.armatures)))
|
||||||
|
for arm in bpy.data.armatures:
|
||||||
|
writebuf(struct.pack('I', len(arm.name)))
|
||||||
|
writebuf(arm.name.encode())
|
||||||
|
|
||||||
|
if arm.library:
|
||||||
|
arm_path = bpy.path.abspath(arm.library.filepath)
|
||||||
|
writebuf(struct.pack('I', len(arm_path)))
|
||||||
|
writebuf(arm_path.encode())
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
armature.cook(writebuf, arm)
|
||||||
|
|
||||||
|
def _out_subtypes(sact_data, writebuf):
|
||||||
|
writebuf(struct.pack('I', len(sact_data.subtypes)))
|
||||||
|
for subtype in sact_data.subtypes:
|
||||||
|
writebuf(struct.pack('I', len(subtype.name)))
|
||||||
|
writebuf(subtype.name.encode())
|
||||||
|
|
||||||
|
mesh = None
|
||||||
|
if subtype.linked_mesh in bpy.data.objects:
|
||||||
|
mesh = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
cskr_id = mesh.data.cskr_id
|
||||||
|
writebuf(struct.pack('I', len(cskr_id)))
|
||||||
|
writebuf(cskr_id.encode())
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
if mesh and mesh.data.library:
|
||||||
|
mesh_path = bpy.path.abspath(mesh.data.library.filepath)
|
||||||
|
writebuf(struct.pack('I', len(mesh_path)))
|
||||||
|
writebuf(mesh_path.encode())
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
arm = None
|
||||||
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
|
arm = bpy.data.objects[subtype.linked_armature]
|
||||||
|
|
||||||
|
arm_idx = -1
|
||||||
|
if arm:
|
||||||
|
arm_idx = bpy.data.armatures.find(arm.name)
|
||||||
|
writebuf(struct.pack('i', arm_idx))
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(subtype.overlays)))
|
||||||
|
for overlay in subtype.overlays:
|
||||||
|
writebuf(struct.pack('I', len(overlay.name)))
|
||||||
|
writebuf(overlay.name.encode())
|
||||||
|
|
||||||
|
mesh = None
|
||||||
|
if overlay.linked_mesh in bpy.data.objects:
|
||||||
|
mesh = bpy.data.objects[overlay.linked_mesh]
|
||||||
|
cskr_id = mesh.data.cskr_id
|
||||||
|
writebuf(struct.pack('I', len(cskr_id)))
|
||||||
|
writebuf(cskr_id.encode())
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
if mesh and mesh.data.library:
|
||||||
|
mesh_path = bpy.path.abspath(mesh.data.library.filepath)
|
||||||
|
writebuf(struct.pack('I', len(mesh_path)))
|
||||||
|
writebuf(mesh_path.encode())
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
def _out_attachments(sact_data, writebuf):
|
||||||
|
writebuf(struct.pack('I', len(sact_data.attachments)))
|
||||||
|
for attachment in sact_data.attachments:
|
||||||
|
writebuf(struct.pack('I', len(attachment.name)))
|
||||||
|
writebuf(attachment.name.encode())
|
||||||
|
|
||||||
|
mesh = None
|
||||||
|
if attachment.linked_mesh in bpy.data.objects:
|
||||||
|
mesh = bpy.data.objects[attachment.linked_mesh]
|
||||||
|
cskr_id = mesh.data.cskr_id
|
||||||
|
writebuf(struct.pack('I', len(cskr_id)))
|
||||||
|
writebuf(cskr_id.encode())
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
if mesh and mesh.data.library:
|
||||||
|
mesh_path = bpy.path.abspath(mesh.data.library.filepath)
|
||||||
|
writebuf(struct.pack('I', len(mesh_path)))
|
||||||
|
writebuf(mesh_path.encode())
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
arm = None
|
||||||
|
if attachment.linked_armature in bpy.data.objects:
|
||||||
|
arm = bpy.data.objects[attachment.linked_armature]
|
||||||
|
|
||||||
|
arm_idx = -1
|
||||||
|
if arm:
|
||||||
|
arm_idx = bpy.data.armatures.find(arm.name)
|
||||||
|
writebuf(struct.pack('i', arm_idx))
|
||||||
|
|
||||||
|
def _out_actions(sact_data, writebuf):
|
||||||
|
writebuf(struct.pack('I', len(sact_data.actions)))
|
||||||
|
for action_idx in range(len(sact_data.actions)):
|
||||||
|
sact_data.active_action = action_idx
|
||||||
|
action = sact_data.actions[action_idx]
|
||||||
|
writebuf(struct.pack('I', len(action.name)))
|
||||||
|
writebuf(action.name.encode())
|
||||||
|
|
||||||
|
bact = None
|
||||||
|
if action.name in bpy.data.actions:
|
||||||
|
bact = bpy.data.actions[action.name]
|
||||||
|
anim_id = bact.anim_id
|
||||||
|
writebuf(struct.pack('I', len(anim_id)))
|
||||||
|
writebuf(anim_id.encode())
|
||||||
|
if not bact:
|
||||||
|
raise RuntimeError('action %s not found' % action.name)
|
||||||
|
|
||||||
|
writebuf(struct.pack('f', 1.0 / bact.hecl_fps))
|
||||||
|
writebuf(struct.pack('b', int(bact.hecl_additive)))
|
||||||
|
writebuf(struct.pack('b', int(bact.hecl_looping)))
|
||||||
|
|
||||||
|
write_action_channels(writebuf, bact)
|
||||||
|
writebuf(struct.pack('I', len(sact_data.subtypes)))
|
||||||
|
for subtype_idx in range(len(sact_data.subtypes)):
|
||||||
|
subtype = sact_data.subtypes[subtype_idx]
|
||||||
|
sact_data.active_subtype = subtype_idx
|
||||||
|
bpy.ops.scene.sactaction_load()
|
||||||
|
if subtype.linked_armature not in bpy.data.objects:
|
||||||
|
raise RuntimeError('armature %s not found' % subtype.linked_armature)
|
||||||
|
arm = bpy.data.objects[subtype.linked_armature]
|
||||||
|
if subtype.linked_mesh not in bpy.data.objects:
|
||||||
|
raise RuntimeError('mesh %s not found' % subtype.linked_mesh)
|
||||||
|
mesh = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
write_action_aabb(writebuf, arm, mesh, bact)
|
||||||
|
|
||||||
|
def _out_action_no_subtypes(sact_data, writebuf, action_name):
|
||||||
|
for action_idx in range(len(sact_data.actions)):
|
||||||
|
action = sact_data.actions[action_idx]
|
||||||
|
if action.name == action_name:
|
||||||
|
sact_data.active_action = action_idx
|
||||||
|
writebuf(struct.pack('I', len(action.name)))
|
||||||
|
writebuf(action.name.encode())
|
||||||
|
|
||||||
|
bact = None
|
||||||
|
if action.name in bpy.data.actions:
|
||||||
|
bact = bpy.data.actions[action.name]
|
||||||
|
anim_id = bact.anim_id
|
||||||
|
writebuf(struct.pack('I', len(anim_id)))
|
||||||
|
writebuf(anim_id.encode())
|
||||||
|
if not bact:
|
||||||
|
raise RuntimeError('action %s not found' % action.name)
|
||||||
|
|
||||||
|
writebuf(struct.pack('f', 1.0 / bact.hecl_fps))
|
||||||
|
writebuf(struct.pack('b', int(bact.hecl_additive)))
|
||||||
|
writebuf(struct.pack('b', int(bact.hecl_looping)))
|
||||||
|
|
||||||
|
write_action_channels(writebuf, bact)
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
return
|
||||||
|
|
||||||
|
raise RuntimeError("Unable to find action '%s'" % action_name)
|
||||||
|
|
||||||
|
# Cook
|
||||||
|
def cook(writebuf):
|
||||||
|
bpy.context.scene.hecl_auto_remap = False
|
||||||
|
sact_data = bpy.context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
# Output armatures
|
||||||
|
_out_armatures(sact_data, writebuf)
|
||||||
|
|
||||||
|
# Output subtypes
|
||||||
|
_out_subtypes(sact_data, writebuf)
|
||||||
|
|
||||||
|
# Output attachments
|
||||||
|
_out_attachments(sact_data, writebuf)
|
||||||
|
|
||||||
|
# Output actions
|
||||||
|
_out_actions(sact_data, writebuf)
|
||||||
|
|
||||||
|
# Cook Character Data only
|
||||||
|
def cook_character_only(writebuf):
|
||||||
|
sact_data = bpy.context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
# Output armatures
|
||||||
|
_out_armatures(sact_data, writebuf)
|
||||||
|
|
||||||
|
# Output subtypes
|
||||||
|
_out_subtypes(sact_data, writebuf)
|
||||||
|
|
||||||
|
# Output attachments
|
||||||
|
_out_attachments(sact_data, writebuf)
|
||||||
|
|
||||||
|
# Output no actions
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
def cook_action_channels_only(writebuf, action_name):
|
||||||
|
sact_data = bpy.context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
# Output action without AABBs
|
||||||
|
_out_action_no_subtypes(sact_data, writebuf, action_name)
|
||||||
|
|
||||||
|
# Access actor's contained subtype names
|
||||||
|
def get_subtype_names(writebuf):
|
||||||
|
sact_data = bpy.context.scene.hecl_sact_data
|
||||||
|
writebuf(struct.pack('I', len(sact_data.subtypes)))
|
||||||
|
for sub_idx in range(len(sact_data.subtypes)):
|
||||||
|
subtype = sact_data.subtypes[sub_idx]
|
||||||
|
writebuf(struct.pack('I', len(subtype.name)))
|
||||||
|
writebuf(subtype.name.encode())
|
||||||
|
obj = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
cskr_id = obj.data.cskr_id
|
||||||
|
writebuf(struct.pack('I', len(cskr_id)))
|
||||||
|
writebuf(cskr_id.encode())
|
||||||
|
|
||||||
|
# Access subtype's contained overlay names
|
||||||
|
def get_subtype_overlay_names(writebuf, subtypeName):
|
||||||
|
sact_data = bpy.context.scene.hecl_sact_data
|
||||||
|
for sub_idx in range(len(sact_data.subtypes)):
|
||||||
|
subtype = sact_data.subtypes[sub_idx]
|
||||||
|
if subtype.name == subtypeName:
|
||||||
|
writebuf(struct.pack('I', len(subtype.overlays)))
|
||||||
|
for overlay in subtype.overlays:
|
||||||
|
writebuf(struct.pack('I', len(overlay.name)))
|
||||||
|
writebuf(overlay.name.encode())
|
||||||
|
obj = bpy.data.objects[overlay.linked_mesh]
|
||||||
|
cskr_id = obj.data.cskr_id
|
||||||
|
writebuf(struct.pack('I', len(cskr_id)))
|
||||||
|
writebuf(cskr_id.encode())
|
||||||
|
return
|
||||||
|
writebuf(struct.pack('I', 0))
|
||||||
|
|
||||||
|
# Access contained attachment names
|
||||||
|
def get_attachment_names(writebuf):
|
||||||
|
sact_data = bpy.context.scene.hecl_sact_data
|
||||||
|
writebuf(struct.pack('I', len(sact_data.attachments)))
|
||||||
|
for att_idx in range(len(sact_data.attachments)):
|
||||||
|
attachment = sact_data.attachments[att_idx]
|
||||||
|
writebuf(struct.pack('I', len(attachment.name)))
|
||||||
|
writebuf(attachment.name.encode())
|
||||||
|
obj = bpy.data.objects[attachment.linked_mesh]
|
||||||
|
cskr_id = obj.data.cskr_id
|
||||||
|
writebuf(struct.pack('I', len(cskr_id)))
|
||||||
|
writebuf(cskr_id.encode())
|
||||||
|
|
||||||
|
# Access actor's contained action names
|
||||||
|
def get_action_names(writebuf):
|
||||||
|
sact_data = bpy.context.scene.hecl_sact_data
|
||||||
|
writebuf(struct.pack('I', len(sact_data.actions)))
|
||||||
|
for action_idx in range(len(sact_data.actions)):
|
||||||
|
action = sact_data.actions[action_idx]
|
||||||
|
writebuf(struct.pack('I', len(action.name)))
|
||||||
|
writebuf(action.name.encode())
|
||||||
|
anim_id = bpy.data.actions[action.name].anim_id
|
||||||
|
writebuf(struct.pack('I', len(anim_id)))
|
||||||
|
writebuf(anim_id.encode())
|
||||||
|
|
||||||
|
# Panel draw
|
||||||
|
def draw(layout, context):
|
||||||
|
SACTSubtype.draw(layout.box(), context)
|
||||||
|
SACTAction.draw(layout.box(), context)
|
||||||
|
|
||||||
|
|
||||||
|
# Time-remap option update
|
||||||
|
def time_remap_update(self, context):
|
||||||
|
if context.scene.hecl_type == 'ACTOR':
|
||||||
|
SACTAction.active_action_update(self, context)
|
||||||
|
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
SACTSubtype.register()
|
||||||
|
SACTAction.register()
|
||||||
|
bpy.utils.register_class(SACTData)
|
||||||
|
bpy.types.Scene.hecl_sact_data = bpy.props.PointerProperty(type=SACTData)
|
||||||
|
bpy.types.Action.anim_id = bpy.props.StringProperty(name='Original ANIM ID')
|
||||||
|
bpy.types.Action.hecl_fps = bpy.props.IntProperty(name='HECL Action FPS', default=30)
|
||||||
|
bpy.types.Action.hecl_additive = bpy.props.BoolProperty(name='HECL Additive Action', default=False)
|
||||||
|
bpy.types.Action.hecl_looping = bpy.props.BoolProperty(name='HECL Looping Action', default=False)
|
||||||
|
bpy.types.Scene.hecl_auto_remap = bpy.props.BoolProperty(name="Auto Remap",
|
||||||
|
description="Enables automatic 60-fps time-remapping for playback-validation purposes",
|
||||||
|
default=True, update=time_remap_update)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(SACTData)
|
||||||
|
SACTSubtype.unregister()
|
||||||
|
SACTAction.unregister()
|
|
@ -0,0 +1,490 @@
|
||||||
|
import bpy
|
||||||
|
from bpy.app.handlers import persistent
|
||||||
|
from mathutils import Quaternion, Color
|
||||||
|
import math
|
||||||
|
import os.path
|
||||||
|
from .. import swld
|
||||||
|
|
||||||
|
# Preview update func (for lighting preview)
|
||||||
|
def preview_update(self, context):
|
||||||
|
if context.scene.hecl_type == 'AREA':
|
||||||
|
area_data = context.scene.hecl_srea_data
|
||||||
|
|
||||||
|
# Original Lightmaps
|
||||||
|
if area_data.lightmap_mode == 'ORIGINAL':
|
||||||
|
for material in bpy.data.materials:
|
||||||
|
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
|
||||||
|
lm_node = material.node_tree.nodes['Lightmap']
|
||||||
|
# Reference original game lightmaps
|
||||||
|
if material.hecl_lightmap in bpy.data.images:
|
||||||
|
lm_node.image = bpy.data.images[material.hecl_lightmap]
|
||||||
|
else:
|
||||||
|
lm_node.image = None
|
||||||
|
|
||||||
|
# Cycles Lightmaps
|
||||||
|
elif area_data.lightmap_mode == 'CYCLES':
|
||||||
|
for material in bpy.data.materials:
|
||||||
|
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
|
||||||
|
lm_node = material.node_tree.nodes['Lightmap']
|
||||||
|
# Reference newly-generated lightmaps
|
||||||
|
img_name = material.hecl_lightmap + '_CYCLES'
|
||||||
|
if img_name in bpy.data.images:
|
||||||
|
lm_node.image = bpy.data.images[img_name]
|
||||||
|
else:
|
||||||
|
lm_node.image = None
|
||||||
|
|
||||||
|
# White Lightmaps
|
||||||
|
elif area_data.lightmap_mode == 'NONE':
|
||||||
|
img_name = 'NONE'
|
||||||
|
img = None
|
||||||
|
if img_name in bpy.data.images:
|
||||||
|
img = bpy.data.images[img_name]
|
||||||
|
else:
|
||||||
|
img = bpy.data.images.new(img_name, width=1, height=1)
|
||||||
|
pixels = [1.0] * (4 * 1 * 1)
|
||||||
|
img.pixels = pixels
|
||||||
|
img.use_fake_user = True
|
||||||
|
img.file_format = 'PNG'
|
||||||
|
|
||||||
|
for material in bpy.data.materials:
|
||||||
|
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
|
||||||
|
lm_node = material.node_tree.nodes['Lightmap']
|
||||||
|
# Reference NONE
|
||||||
|
lm_node.image = img
|
||||||
|
|
||||||
|
|
||||||
|
# Update lightmap output-resolution
|
||||||
|
def set_lightmap_resolution(self, context):
|
||||||
|
area_data = context.scene.hecl_srea_data
|
||||||
|
pixel_size = int(area_data.lightmap_resolution)
|
||||||
|
for mat in bpy.data.materials:
|
||||||
|
if not mat.hecl_lightmap:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Determine proportional aspect
|
||||||
|
old_image = bpy.data.images[mat.hecl_lightmap]
|
||||||
|
width_fac = 1
|
||||||
|
height_fac = 1
|
||||||
|
if old_image.size[0] > old_image.size[1]:
|
||||||
|
height_fac = old_image.size[0] // old_image.size[1]
|
||||||
|
else:
|
||||||
|
width_fac = old_image.size[1] // old_image.size[0]
|
||||||
|
width = pixel_size // width_fac
|
||||||
|
height = pixel_size // height_fac
|
||||||
|
|
||||||
|
# Find target texture and scale connected image
|
||||||
|
if 'CYCLES_OUT' in mat.node_tree.nodes:
|
||||||
|
out_node = mat.node_tree.nodes['CYCLES_OUT']
|
||||||
|
if out_node.type == 'TEX_IMAGE':
|
||||||
|
image = out_node.image
|
||||||
|
if image:
|
||||||
|
image.scale(width, height)
|
||||||
|
|
||||||
|
def make_or_load_cycles_image(mat, area_data):
|
||||||
|
if not mat.hecl_lightmap:
|
||||||
|
return
|
||||||
|
pixel_size = int(area_data.lightmap_resolution)
|
||||||
|
tex_name = mat.hecl_lightmap + '_CYCLES'
|
||||||
|
if area_data.adjacent_area < 0:
|
||||||
|
path_name = '//' + mat.hecl_lightmap + '_CYCLES.png'
|
||||||
|
else:
|
||||||
|
path_name = '//' + mat.hecl_lightmap + '_CYCLES_%d.png' % area_data.adjacent_area
|
||||||
|
|
||||||
|
# Determine proportional aspect
|
||||||
|
old_image = bpy.data.images[mat.hecl_lightmap]
|
||||||
|
width_fac = 1
|
||||||
|
height_fac = 1
|
||||||
|
if old_image.size[0] > old_image.size[1]:
|
||||||
|
height_fac = old_image.size[0] // old_image.size[1]
|
||||||
|
else:
|
||||||
|
width_fac = old_image.size[1] // old_image.size[0]
|
||||||
|
width = pixel_size // width_fac
|
||||||
|
height = pixel_size // height_fac
|
||||||
|
|
||||||
|
# Check for consistency with on-disk image
|
||||||
|
if tex_name in bpy.data.images:
|
||||||
|
image = bpy.data.images[tex_name]
|
||||||
|
image.use_fake_user = True
|
||||||
|
image.file_format = 'PNG'
|
||||||
|
image.filepath = path_name
|
||||||
|
good = True
|
||||||
|
if image.size[0] != width or image.size[1] != height:
|
||||||
|
try:
|
||||||
|
image.scale(width, height)
|
||||||
|
except:
|
||||||
|
good = False
|
||||||
|
if good:
|
||||||
|
return image
|
||||||
|
# Remove and recreate if we get here
|
||||||
|
bpy.data.images.remove(bpy.data.images[tex_name])
|
||||||
|
|
||||||
|
# New image (or load from disk if available)
|
||||||
|
try:
|
||||||
|
new_image = bpy.data.images.load(path_name)
|
||||||
|
new_image.name = tex_name
|
||||||
|
new_image.use_fake_user = True
|
||||||
|
if new_image.size[0] != width or new_image.size[1] != height:
|
||||||
|
new_image.scale(width, height)
|
||||||
|
except:
|
||||||
|
new_image = bpy.data.images.new(tex_name, width, height)
|
||||||
|
new_image.use_fake_user = True
|
||||||
|
new_image.file_format = 'PNG'
|
||||||
|
new_image.filepath = path_name
|
||||||
|
|
||||||
|
return new_image
|
||||||
|
|
||||||
|
# Set adjacent area lightmaps
|
||||||
|
def set_adjacent_area(self, context):
|
||||||
|
bg_scene = context.scene.background_set
|
||||||
|
dock_idx = context.scene.hecl_srea_data.adjacent_area
|
||||||
|
if bg_scene is None:
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, 'No background world scene is set')
|
||||||
|
return
|
||||||
|
|
||||||
|
if bg_scene.hecl_type != 'WORLD':
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, 'Scene "%s" is not a hecl WORLD' % bg_scene.name)
|
||||||
|
return
|
||||||
|
|
||||||
|
adjacent = dock_idx >= 0
|
||||||
|
if len(context.scene.render.layers):
|
||||||
|
context.scene.view_layers[0].use_sky = not adjacent
|
||||||
|
|
||||||
|
# Remove linked lamps and show/hide locals
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
if obj.library is not None and (obj.type == 'LIGHT' or obj.type == 'MESH'):
|
||||||
|
try:
|
||||||
|
context.scene.collection.children.unlink(obj)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
continue
|
||||||
|
if obj.type == 'LIGHT':
|
||||||
|
obj.hide_render = adjacent
|
||||||
|
|
||||||
|
# Remove linked scenes
|
||||||
|
to_remove = []
|
||||||
|
for scene in bpy.data.scenes:
|
||||||
|
if scene.hecl_type == 'AREA' and scene.library is not None:
|
||||||
|
to_remove.append(scene)
|
||||||
|
for scene in to_remove:
|
||||||
|
bpy.data.scenes.remove(scene)
|
||||||
|
|
||||||
|
# Link scene, meshes, and lamps
|
||||||
|
if dock_idx >= 0:
|
||||||
|
other_area_name = get_other_area_name(self, bg_scene, dock_idx)
|
||||||
|
if other_area_name is None:
|
||||||
|
return
|
||||||
|
other_area_scene_name = None
|
||||||
|
this_dir = os.path.split(bpy.data.filepath)[0]
|
||||||
|
try:
|
||||||
|
with bpy.data.libraries.load('%s/../%s/!area.blend' % (this_dir, other_area_name),
|
||||||
|
link=True, relative=True) as (data_from, data_to):
|
||||||
|
for scene in data_from.scenes:
|
||||||
|
other_area_scene_name = scene
|
||||||
|
data_to.scenes = [other_area_scene_name]
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, 'Unable to open "%s" blend file: %s' % (other_area_name, str(e)))
|
||||||
|
return
|
||||||
|
if other_area_scene_name is None:
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, '"%s" does not have an area scene' % other_area_name)
|
||||||
|
return
|
||||||
|
other_scene = bpy.data.scenes[other_area_scene_name]
|
||||||
|
if other_scene.hecl_type != 'AREA':
|
||||||
|
self.report({'ERROR_INVALID_CONTEXT'}, '"%s" does not have an area scene' % other_area_name)
|
||||||
|
bpy.data.scenes.remove(other_scene)
|
||||||
|
return
|
||||||
|
for obj in other_scene.objects:
|
||||||
|
if (obj.type == 'LIGHT' or obj.type == 'MESH') and obj.layers[0]:
|
||||||
|
context.scene.collection.objects.link(obj)
|
||||||
|
obj.hide_render = False
|
||||||
|
|
||||||
|
# Ensure filepaths target the current dock index
|
||||||
|
for mat in bpy.data.materials:
|
||||||
|
if not mat.library and mat.use_nodes and 'CYCLES_OUT' in mat.node_tree.nodes:
|
||||||
|
texture_node = mat.node_tree.nodes['CYCLES_OUT']
|
||||||
|
texture_node.image = make_or_load_cycles_image(mat, context.scene.hecl_srea_data)
|
||||||
|
|
||||||
|
# Area data class
|
||||||
|
class SREAData(bpy.types.PropertyGroup):
|
||||||
|
lightmap_resolution: bpy.props.EnumProperty(name="HECL Area Lightmap Resolution",
|
||||||
|
description="Set square resolution to use when rendering new lightmaps",
|
||||||
|
items=[
|
||||||
|
('256', "256", "256x256 (original quality)"),
|
||||||
|
('512', "512", "512x512"),
|
||||||
|
('1024', "1024", "1024x1024"),
|
||||||
|
('2048', "2048", "2048x2048"),
|
||||||
|
('4096', "4096", "4096x4096")],
|
||||||
|
update=set_lightmap_resolution,
|
||||||
|
default='1024')
|
||||||
|
|
||||||
|
lightmap_mode: bpy.props.EnumProperty(name="HECL Area Lightmap Mode",
|
||||||
|
description="Simple way to manipulate all lightmap-using materials",
|
||||||
|
items=[
|
||||||
|
('NONE', "None", "Pure white lightmaps"),
|
||||||
|
('ORIGINAL', "Original", "Original extracted lightmaps"),
|
||||||
|
('CYCLES', "Cycles", "Blender-rendered lightmaps")],
|
||||||
|
update=preview_update,
|
||||||
|
default='ORIGINAL')
|
||||||
|
|
||||||
|
adjacent_area: bpy.props.IntProperty(name="HECL Adjacent Area Lightmap",
|
||||||
|
description="Dock index of adjacent area to render, or -1 for local lights",
|
||||||
|
update=set_adjacent_area,
|
||||||
|
default=-1,
|
||||||
|
min=-1,
|
||||||
|
max=8)
|
||||||
|
|
||||||
|
def report(self, code, string):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Trace color output searching for material node and making list from it
|
||||||
|
def recursive_build_material_chain(node):
|
||||||
|
if node.type == 'OUTPUT':
|
||||||
|
if node.inputs[0].is_linked:
|
||||||
|
ret = recursive_build_material_chain(node.inputs[0].links[0].from_node)
|
||||||
|
if ret:
|
||||||
|
ret.append(node)
|
||||||
|
return ret
|
||||||
|
elif node.type == 'MIX_RGB':
|
||||||
|
if node.inputs[1].is_linked:
|
||||||
|
ret = recursive_build_material_chain(node.inputs[1].links[0].from_node)
|
||||||
|
if ret:
|
||||||
|
ret.append(node)
|
||||||
|
return ret
|
||||||
|
if node.inputs[2].is_linked:
|
||||||
|
ret = recursive_build_material_chain(node.inputs[2].links[0].from_node)
|
||||||
|
if ret:
|
||||||
|
ret.append(node)
|
||||||
|
return ret
|
||||||
|
elif node.type == 'MATERIAL':
|
||||||
|
return [node]
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Get Diffuse and Emissive output sockets from chain
|
||||||
|
def get_de_sockets(chain):
|
||||||
|
found_add = None
|
||||||
|
found_mul = None
|
||||||
|
n = None
|
||||||
|
for nn in reversed(chain):
|
||||||
|
if not n:
|
||||||
|
n = nn
|
||||||
|
continue
|
||||||
|
if n.type == 'MIX_RGB':
|
||||||
|
if not found_add and n.blend_type == 'ADD' and nn.type != 'MATERIAL':
|
||||||
|
use_idx = 1
|
||||||
|
if n.inputs[1].is_linked:
|
||||||
|
tn = n.inputs[1].links[0].from_node
|
||||||
|
if tn == nn:
|
||||||
|
use_idx = 2
|
||||||
|
if n.inputs[use_idx].is_linked:
|
||||||
|
found_add = n.inputs[use_idx].links[0].from_socket
|
||||||
|
found_mul = None
|
||||||
|
elif n.blend_type == 'MULTIPLY':
|
||||||
|
use_idx = 1
|
||||||
|
if n.inputs[1].is_linked:
|
||||||
|
tn = n.inputs[1].links[0].from_node
|
||||||
|
if tn == nn:
|
||||||
|
use_idx = 2
|
||||||
|
if n.inputs[use_idx].is_linked:
|
||||||
|
found_mul = n.inputs[use_idx].links[0].from_socket
|
||||||
|
n = nn
|
||||||
|
|
||||||
|
return found_mul, found_add
|
||||||
|
|
||||||
|
# Lookup the directory name of other area via dock link
|
||||||
|
def get_other_area_name(op, bg_scene, dock_idx):
|
||||||
|
dock_conns = swld.build_dock_connections(bg_scene)
|
||||||
|
this_dir = os.path.split(bpy.data.filepath)[0]
|
||||||
|
this_area_name = os.path.basename(this_dir)
|
||||||
|
wld_area = next((area for area in dock_conns[0] if area[0].name == this_area_name), None)
|
||||||
|
if wld_area is None:
|
||||||
|
op.report({'ERROR_INVALID_CONTEXT'}, 'Unable to resolve area in world')
|
||||||
|
return None
|
||||||
|
if dock_idx not in range(len(wld_area[1])):
|
||||||
|
op.report({'ERROR_INVALID_CONTEXT'}, 'Dock %d is out of this area\'s range [0,%d]' %
|
||||||
|
(dock_idx, len(wld_area[1])))
|
||||||
|
return None
|
||||||
|
dock_obj = wld_area[1][dock_idx]
|
||||||
|
if dock_obj.name not in dock_conns[1]:
|
||||||
|
op.report({'ERROR_INVALID_CONTEXT'}, 'Unable to find sister dock for %s' % dock_obj.name)
|
||||||
|
return None
|
||||||
|
other_wld_area = dock_conns[1][dock_obj.name][2].parent
|
||||||
|
if other_wld_area is None:
|
||||||
|
op.report({'ERROR_INVALID_CONTEXT'}, '%s does not have a parent area' % dock_obj.name)
|
||||||
|
return None
|
||||||
|
return other_wld_area.name
|
||||||
|
|
||||||
|
# Shared lightmap render procedure
|
||||||
|
def render_lightmaps(context):
|
||||||
|
if context.scene is not None:
|
||||||
|
area_data = context.scene.hecl_srea_data
|
||||||
|
|
||||||
|
# Resolution
|
||||||
|
pixel_size = int(area_data.lightmap_resolution)
|
||||||
|
|
||||||
|
# Mmm Cycles
|
||||||
|
context.scene.render.engine = 'CYCLES'
|
||||||
|
context.scene.render.bake.margin = pixel_size // 256
|
||||||
|
|
||||||
|
# Iterate materials and setup cycles
|
||||||
|
for mat in bpy.data.materials:
|
||||||
|
if mat.use_nodes:
|
||||||
|
# Set bake target node active
|
||||||
|
if 'CYCLES_OUT' in mat.node_tree.nodes:
|
||||||
|
mat.node_tree.nodes.active = mat.node_tree.nodes['CYCLES_OUT']
|
||||||
|
elif mat.hecl_lightmap and not mat.library:
|
||||||
|
image_out_node = mat.node_tree.nodes.new('ShaderNodeTexImage')
|
||||||
|
mat.node_tree.nodes.active = image_out_node
|
||||||
|
image_out_node.name = 'CYCLES_OUT'
|
||||||
|
image_out_node.image = make_or_load_cycles_image(mat, area_data)
|
||||||
|
else:
|
||||||
|
image_out_node = mat.node_tree.nodes.new('ShaderNodeTexImage')
|
||||||
|
mat.node_tree.nodes.active = image_out_node
|
||||||
|
image_out_node.name = 'CYCLES_OUT'
|
||||||
|
if 'FAKE' in bpy.data.images:
|
||||||
|
image_out_node.image = bpy.data.images['FAKE']
|
||||||
|
else:
|
||||||
|
fake_img = bpy.data.images.new('FAKE', 1, 1)
|
||||||
|
image_out_node.image = fake_img
|
||||||
|
|
||||||
|
# Iterate mesh objects and set UV 0 as the active UV layer
|
||||||
|
for obj in context.scene.objects:
|
||||||
|
if obj.type == 'MESH':
|
||||||
|
|
||||||
|
if not len(obj.data.uv_layers):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Make correct UV layer active
|
||||||
|
obj.data.uv_layers.active_index = 0
|
||||||
|
|
||||||
|
# Make lightmaps
|
||||||
|
bpy.ops.object.bake('INVOKE_DEFAULT', type='DIFFUSE', pass_filter={'AO', 'DIRECT', 'INDIRECT'})
|
||||||
|
|
||||||
|
# Lightmap render operator
|
||||||
|
class SREARenderLightmaps(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.hecl_area_render_lightmaps"
|
||||||
|
bl_label = "HECL Render New Lightmaps"
|
||||||
|
bl_description = "Bake new lightmaps for HECL runtime"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.scene is not None
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if not context.selected_objects:
|
||||||
|
for obj in context.scene.objects:
|
||||||
|
if obj.type == 'MESH' and not obj.library:
|
||||||
|
obj.select_set(True)
|
||||||
|
context.view_layer.objects.active = obj
|
||||||
|
|
||||||
|
render_lightmaps(context)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def shadeless_material(idx):
|
||||||
|
name = 'SHADELESS_MAT_%d' % idx
|
||||||
|
if name in bpy.data.materials:
|
||||||
|
return bpy.data.materials[name]
|
||||||
|
mat = bpy.data.materials.new(name)
|
||||||
|
r = idx % 256
|
||||||
|
g = (idx % 65536) // 256
|
||||||
|
b = idx // 65536
|
||||||
|
mat.diffuse_color = Color((r / 255.0, g / 255.0, b / 255.0))
|
||||||
|
return mat
|
||||||
|
|
||||||
|
look_forward = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||||
|
look_backward = Quaternion((0.0, 0.0, 1.0), math.radians(180.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||||
|
look_up = Quaternion((1.0, 0.0, 0.0), math.radians(180.0))
|
||||||
|
look_down = Quaternion((1.0, 0.0, 0.0), math.radians(0.0))
|
||||||
|
look_left = Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||||
|
look_right = Quaternion((0.0, 0.0, 1.0), math.radians(-90.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||||
|
look_list = (look_forward, look_backward, look_up, look_down, look_left, look_right)
|
||||||
|
|
||||||
|
# Render PVS for location
|
||||||
|
def render_pvs(pathOut, location):
|
||||||
|
bpy.context.scene.render.resolution_x = 256
|
||||||
|
bpy.context.scene.render.resolution_y = 256
|
||||||
|
bpy.context.scene.render.resolution_percentage = 100
|
||||||
|
bpy.context.scene.render.use_antialiasing = False
|
||||||
|
bpy.context.scene.render.use_textures = False
|
||||||
|
bpy.context.scene.render.use_shadows = False
|
||||||
|
bpy.context.scene.render.use_sss = False
|
||||||
|
bpy.context.scene.render.use_envmaps = False
|
||||||
|
bpy.context.scene.render.use_raytrace = False
|
||||||
|
bpy.context.scene.display_settings.display_device = 'None'
|
||||||
|
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
||||||
|
bpy.context.scene.world.horizon_color = Color((1.0, 1.0, 1.0))
|
||||||
|
bpy.context.scene.world.zenith_color = Color((1.0, 1.0, 1.0))
|
||||||
|
|
||||||
|
cam = bpy.data.cameras.new('CUBIC_CAM')
|
||||||
|
cam_obj = bpy.data.objects.new('CUBIC_CAM', cam)
|
||||||
|
bpy.context.scene.collection.objects.link(cam_obj)
|
||||||
|
bpy.context.scene.camera = cam_obj
|
||||||
|
cam.lens_unit = 'FOV'
|
||||||
|
cam.angle = math.radians(90.0)
|
||||||
|
|
||||||
|
mat_idx = 0
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.type == 'MESH':
|
||||||
|
if obj.name == 'CMESH':
|
||||||
|
continue
|
||||||
|
mat = shadeless_material(mat_idx)
|
||||||
|
for slot in obj.material_slots:
|
||||||
|
slot.material = mat
|
||||||
|
mat_idx += 1
|
||||||
|
|
||||||
|
cam_obj.location = location
|
||||||
|
cam_obj.rotation_mode = 'QUATERNION'
|
||||||
|
|
||||||
|
for i in range(6):
|
||||||
|
cam_obj.rotation_quaternion = look_list[i]
|
||||||
|
bpy.context.scene.render.filepath = '%s%d' % (pathOut, i)
|
||||||
|
bpy.ops.render.render(write_still=True)
|
||||||
|
|
||||||
|
bpy.context.scene.camera = None
|
||||||
|
#bpy.context.scene.objects.unlink(cam_obj)
|
||||||
|
bpy.data.objects.remove(cam_obj)
|
||||||
|
bpy.data.cameras.remove(cam)
|
||||||
|
|
||||||
|
# Render PVS for light
|
||||||
|
def render_pvs_light(pathOut, lightName):
|
||||||
|
if lightName not in bpy.context.scene.objects:
|
||||||
|
raise RuntimeError('Unable to find light %s' % lightName)
|
||||||
|
render_pvs(pathOut, bpy.context.scene.objects[lightName].location)
|
||||||
|
|
||||||
|
# Cook
|
||||||
|
def cook(writebuffunc, platform, endianchar):
|
||||||
|
print('COOKING SREA')
|
||||||
|
|
||||||
|
# Panel draw
|
||||||
|
def draw(layout, context):
|
||||||
|
area_data = context.scene.hecl_srea_data
|
||||||
|
layout.label(text="Lighting:", icon='LIGHT')
|
||||||
|
light_row = layout.row(align=True)
|
||||||
|
light_row.prop_enum(area_data, 'lightmap_mode', 'NONE')
|
||||||
|
light_row.prop_enum(area_data, 'lightmap_mode', 'ORIGINAL')
|
||||||
|
light_row.prop_enum(area_data, 'lightmap_mode', 'CYCLES')
|
||||||
|
layout.prop(area_data, 'lightmap_resolution', text="Resolution")
|
||||||
|
layout.popover("CYCLES_PT_sampling_presets", text=bpy.types.CYCLES_PT_sampling_presets.bl_label)
|
||||||
|
layout.prop(context.scene.render.bake, "use_clear", text="Clear Before Baking")
|
||||||
|
layout.prop(area_data, 'adjacent_area', text='Adjacent Dock Index', icon='MOD_OPACITY')
|
||||||
|
layout.operator("scene.hecl_area_render_lightmaps", text="Bake Cycles Lightmaps", icon='RENDER_STILL')
|
||||||
|
layout.operator("image.save_dirty", text="Save Lightmaps", icon='FILE_TICK')
|
||||||
|
|
||||||
|
|
||||||
|
# Load scene callback
|
||||||
|
@persistent
|
||||||
|
def scene_loaded(dummy):
|
||||||
|
preview_update(None, bpy.context)
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(SREAData)
|
||||||
|
bpy.utils.register_class(SREARenderLightmaps)
|
||||||
|
bpy.types.Scene.hecl_srea_data = bpy.props.PointerProperty(type=SREAData)
|
||||||
|
bpy.types.Material.hecl_lightmap = bpy.props.StringProperty(name='HECL: Lightmap Base Name')
|
||||||
|
bpy.app.handlers.load_post.append(scene_loaded)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(SREAData)
|
||||||
|
bpy.utils.unregister_class(SREARenderLightmaps)
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
import bpy, struct
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
|
def build_dock_connections(scene):
|
||||||
|
areas = []
|
||||||
|
docks = []
|
||||||
|
|
||||||
|
for obj in sorted(scene.objects, key=lambda x: x.name):
|
||||||
|
if obj.type == 'MESH' and obj.parent is None:
|
||||||
|
dock_list = []
|
||||||
|
for ch in obj.children:
|
||||||
|
if ch.type == 'MESH':
|
||||||
|
docks.append((len(areas), len(dock_list), ch))
|
||||||
|
dock_list.append(ch)
|
||||||
|
areas.append((obj, dock_list))
|
||||||
|
|
||||||
|
dock_dict = dict()
|
||||||
|
|
||||||
|
for dockA in docks:
|
||||||
|
mtxA = dockA[2].matrix_world
|
||||||
|
locA = Vector((mtxA[0][3], mtxA[1][3], mtxA[2][3]))
|
||||||
|
match = False
|
||||||
|
for dockB in docks:
|
||||||
|
if dockA == dockB:
|
||||||
|
continue
|
||||||
|
mtxB = dockB[2].matrix_world
|
||||||
|
locB = Vector((mtxB[0][3], mtxB[1][3], mtxB[2][3]))
|
||||||
|
if (locA - locB).magnitude < 0.1:
|
||||||
|
dock_dict[dockA[2].name] = dockB
|
||||||
|
match = True
|
||||||
|
break
|
||||||
|
#if not match:
|
||||||
|
# raise RuntimeError('No dock match for %s' % dockA[2].name)
|
||||||
|
|
||||||
|
return (areas, dock_dict)
|
||||||
|
|
||||||
|
# Cook
|
||||||
|
def cook(writebuf):
|
||||||
|
areas, dock_conns = build_dock_connections(bpy.context.scene)
|
||||||
|
writebuf(struct.pack('I', len(areas)))
|
||||||
|
for area in areas:
|
||||||
|
obj = area[0]
|
||||||
|
dock_list = area[1]
|
||||||
|
writebuf(struct.pack('I', len(obj.name)))
|
||||||
|
writebuf(obj.name.encode())
|
||||||
|
|
||||||
|
pt = Vector(obj.bound_box[0])
|
||||||
|
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
|
||||||
|
pt = Vector(obj.bound_box[6])
|
||||||
|
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
|
||||||
|
|
||||||
|
wmtx = obj.matrix_world
|
||||||
|
writebuf(struct.pack('ffffffffffffffff',
|
||||||
|
wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
|
||||||
|
wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
|
||||||
|
wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
|
||||||
|
wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
|
||||||
|
|
||||||
|
wmtx_inv = wmtx.inverted()
|
||||||
|
|
||||||
|
writebuf(struct.pack('I', len(dock_list)))
|
||||||
|
for ch in dock_list:
|
||||||
|
if len(ch.data.vertices) < 4:
|
||||||
|
raise RuntimeError('Not enough vertices in dock %s' % ch.name)
|
||||||
|
wmtx = wmtx_inv @ ch.matrix_world
|
||||||
|
for vi in range(4):
|
||||||
|
v = wmtx @ ch.data.vertices[vi].co
|
||||||
|
writebuf(struct.pack('fff', v[0], v[1], v[2]))
|
||||||
|
if ch.name in dock_conns:
|
||||||
|
conn_dock = dock_conns[ch.name]
|
||||||
|
writebuf(struct.pack('I', conn_dock[0]))
|
||||||
|
writebuf(struct.pack('I', conn_dock[1]))
|
||||||
|
else:
|
||||||
|
writebuf(struct.pack('I', 0xffffffff))
|
||||||
|
writebuf(struct.pack('I', 0xffffffff))
|
||||||
|
|
||||||
|
# Panel draw
|
||||||
|
def draw(layout, context):
|
||||||
|
pass
|
|
@ -0,0 +1,590 @@
|
||||||
|
import bpy, sys, os, re, struct, traceback
|
||||||
|
|
||||||
|
ARGS_PATTERN = re.compile(r'''(?:"([^"]+)"|'([^']+)'|(\S+))''')
|
||||||
|
|
||||||
|
# Background mode seems to require quit() in some 2.80 builds
|
||||||
|
def _quitblender():
|
||||||
|
bpy.ops.wm.quit_blender()
|
||||||
|
quit()
|
||||||
|
|
||||||
|
MIN_BLENDER_MAJOR = 2
|
||||||
|
MIN_BLENDER_MINOR = 83
|
||||||
|
# Extract pipe file descriptors from arguments
|
||||||
|
print('HECL Blender Launch', sys.argv)
|
||||||
|
if '--' not in sys.argv:
|
||||||
|
_quitblender()
|
||||||
|
args = sys.argv[sys.argv.index('--')+1:]
|
||||||
|
readfd = int(args[0])
|
||||||
|
writefd = int(args[1])
|
||||||
|
verbosity_level = int(args[2])
|
||||||
|
err_path = ""
|
||||||
|
if sys.platform == "win32":
|
||||||
|
import msvcrt
|
||||||
|
readfd = msvcrt.open_osfhandle(readfd, os.O_RDONLY | os.O_BINARY)
|
||||||
|
writefd = msvcrt.open_osfhandle(writefd, os.O_WRONLY | os.O_BINARY)
|
||||||
|
err_path = "/Temp"
|
||||||
|
if 'TEMP' in os.environ:
|
||||||
|
err_path = os.environ['TEMP']
|
||||||
|
else:
|
||||||
|
err_path = "/tmp"
|
||||||
|
if 'TMPDIR' in os.environ:
|
||||||
|
err_path = os.environ['TMPDIR']
|
||||||
|
|
||||||
|
err_path += "/hecl_%016X.derp" % os.getpid()
|
||||||
|
|
||||||
|
def readpipestr():
|
||||||
|
read_bytes = os.read(readfd, 4)
|
||||||
|
if len(read_bytes) != 4:
|
||||||
|
print('HECL connection lost or desynchronized')
|
||||||
|
_quitblender()
|
||||||
|
read_len = struct.unpack('I', read_bytes)[0]
|
||||||
|
return os.read(readfd, read_len)
|
||||||
|
|
||||||
|
def writepipestr(linebytes):
|
||||||
|
#print('LINE', linebytes)
|
||||||
|
os.write(writefd, struct.pack('I', len(linebytes)))
|
||||||
|
os.write(writefd, linebytes)
|
||||||
|
|
||||||
|
def writepipebuf(linebytes):
|
||||||
|
#print('BUF', linebytes)
|
||||||
|
os.write(writefd, linebytes)
|
||||||
|
|
||||||
|
def quitblender():
|
||||||
|
writepipestr(b'QUITTING')
|
||||||
|
_quitblender()
|
||||||
|
|
||||||
|
class PathHasher:
|
||||||
|
def hashpath32(self, path):
|
||||||
|
writepipestr(path.encode())
|
||||||
|
read_str = readpipestr()
|
||||||
|
return int(read_str[0:8], 16)
|
||||||
|
|
||||||
|
# Ensure Blender 2.83+ is being used
|
||||||
|
if bpy.app.version < (MIN_BLENDER_MAJOR, MIN_BLENDER_MINOR, 0):
|
||||||
|
writepipestr(b'INVALIDBLENDERVER')
|
||||||
|
_quitblender()
|
||||||
|
|
||||||
|
# If there's a third argument, use it as the .zip path containing the addon
|
||||||
|
did_install = False
|
||||||
|
if len(args) >= 4 and args[3] != 'SKIPINSTALL':
|
||||||
|
bpy.ops.preferences.addon_install(overwrite=True, target='DEFAULT', filepath=args[3])
|
||||||
|
bpy.ops.preferences.addon_refresh()
|
||||||
|
did_install = True
|
||||||
|
|
||||||
|
# Make addon available to commands
|
||||||
|
if bpy.context.preferences.addons.find('hecl') == -1:
|
||||||
|
try:
|
||||||
|
bpy.ops.preferences.addon_enable(module='hecl')
|
||||||
|
bpy.ops.wm.save_userpref()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
import hecl
|
||||||
|
except:
|
||||||
|
writepipestr(b'NOADDON')
|
||||||
|
_quitblender()
|
||||||
|
|
||||||
|
# Quit if just installed
|
||||||
|
if did_install:
|
||||||
|
writepipestr(b'ADDONINSTALLED')
|
||||||
|
_quitblender()
|
||||||
|
|
||||||
|
# Intro handshake
|
||||||
|
writepipestr(b'READY')
|
||||||
|
ackbytes = readpipestr()
|
||||||
|
if ackbytes != b'ACK':
|
||||||
|
quitblender()
|
||||||
|
|
||||||
|
# Count brackets
|
||||||
|
def count_brackets(linestr):
|
||||||
|
bracket_count = 0
|
||||||
|
for ch in linestr:
|
||||||
|
if ch in {'[','{','('}:
|
||||||
|
bracket_count += 1
|
||||||
|
elif ch in {']','}',')'}:
|
||||||
|
bracket_count -= 1
|
||||||
|
return bracket_count
|
||||||
|
|
||||||
|
# Read line of space-separated/quoted arguments
|
||||||
|
def read_cmdargs():
|
||||||
|
cmdline = readpipestr()
|
||||||
|
if cmdline == b'':
|
||||||
|
print('HECL connection lost')
|
||||||
|
_quitblender()
|
||||||
|
cmdargs = []
|
||||||
|
for match in ARGS_PATTERN.finditer(cmdline.decode()):
|
||||||
|
cmdargs.append(match.group(match.lastindex))
|
||||||
|
return cmdargs
|
||||||
|
|
||||||
|
# Complete sequences of statements compiled/executed here
|
||||||
|
def exec_compbuf(compbuf, globals):
|
||||||
|
if verbosity_level >= 3:
|
||||||
|
print(compbuf)
|
||||||
|
try:
|
||||||
|
co = compile(compbuf, '<HECL>', 'exec')
|
||||||
|
exec(co, globals)
|
||||||
|
except Exception as e:
|
||||||
|
trace_prefix = 'Error processing:\n'
|
||||||
|
trace_prefix += compbuf
|
||||||
|
raise RuntimeError(trace_prefix) from e
|
||||||
|
|
||||||
|
# Command loop for writing animation key data to blender
|
||||||
|
def animin_loop(globals):
|
||||||
|
writepipestr(b'ANIMREADY')
|
||||||
|
while True:
|
||||||
|
crv_type = struct.unpack('b', os.read(readfd, 1))
|
||||||
|
if crv_type[0] < 0:
|
||||||
|
writepipestr(b'ANIMDONE')
|
||||||
|
return
|
||||||
|
elif crv_type[0] == 0:
|
||||||
|
crvs = globals['rotCurves']
|
||||||
|
elif crv_type[0] == 1:
|
||||||
|
crvs = globals['transCurves']
|
||||||
|
elif crv_type[0] == 2:
|
||||||
|
crvs = globals['scaleCurves']
|
||||||
|
|
||||||
|
key_info = struct.unpack('ii', os.read(readfd, 8))
|
||||||
|
crv = crvs[key_info[0]]
|
||||||
|
crv.keyframe_points.add(count=key_info[1])
|
||||||
|
|
||||||
|
if crv_type[0] == 1:
|
||||||
|
for k in range(key_info[1]):
|
||||||
|
key_data = struct.unpack('if', os.read(readfd, 8))
|
||||||
|
pt = crv.keyframe_points[k]
|
||||||
|
pt.interpolation = 'LINEAR'
|
||||||
|
pt.co = (key_data[0], key_data[1])
|
||||||
|
else:
|
||||||
|
for k in range(key_info[1]):
|
||||||
|
key_data = struct.unpack('if', os.read(readfd, 8))
|
||||||
|
pt = crv.keyframe_points[k]
|
||||||
|
pt.interpolation = 'LINEAR'
|
||||||
|
pt.co = (key_data[0], key_data[1])
|
||||||
|
|
||||||
|
def writelight(obj):
|
||||||
|
wmtx = obj.matrix_world
|
||||||
|
writepipebuf(struct.pack('ffffffffffffffff',
|
||||||
|
wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
|
||||||
|
wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
|
||||||
|
wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
|
||||||
|
wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
|
||||||
|
writepipebuf(struct.pack('fff', obj.data.color[0], obj.data.color[1], obj.data.color[2]))
|
||||||
|
|
||||||
|
type = 2
|
||||||
|
spotCutoff = 0.0
|
||||||
|
hasFalloff = False
|
||||||
|
castShadow = False
|
||||||
|
if obj.data.type == 'POINT':
|
||||||
|
type = 2
|
||||||
|
hasFalloff = True
|
||||||
|
castShadow = obj.data.use_shadow
|
||||||
|
elif obj.data.type == 'SPOT':
|
||||||
|
type = 3
|
||||||
|
hasFalloff = True
|
||||||
|
spotCutoff = obj.data.spot_size
|
||||||
|
castShadow = obj.data.use_shadow
|
||||||
|
elif obj.data.type == 'SUN':
|
||||||
|
type = 1
|
||||||
|
castShadow = obj.data.use_shadow
|
||||||
|
|
||||||
|
constant = 1.0
|
||||||
|
linear = 0.0
|
||||||
|
quadratic = 0.0
|
||||||
|
if hasFalloff:
|
||||||
|
if obj.data.falloff_type == 'INVERSE_COEFFICIENTS':
|
||||||
|
constant = obj.data.constant_coefficient
|
||||||
|
linear = obj.data.linear_coefficient
|
||||||
|
quadratic = obj.data.quadratic_coefficient
|
||||||
|
|
||||||
|
layer = 0
|
||||||
|
if 'retro_layer' in obj.data.keys():
|
||||||
|
layer = obj.data['retro_layer']
|
||||||
|
|
||||||
|
writepipebuf(struct.pack('IIfffffb', layer, type, obj.data.energy, spotCutoff, constant, linear, quadratic,
|
||||||
|
castShadow))
|
||||||
|
|
||||||
|
writepipestr(obj.name.encode())
|
||||||
|
|
||||||
|
# Command loop for reading data from blender
|
||||||
|
def dataout_loop():
|
||||||
|
writepipestr(b'READY')
|
||||||
|
while True:
|
||||||
|
cmdargs = read_cmdargs()
|
||||||
|
print(cmdargs)
|
||||||
|
|
||||||
|
if cmdargs[0] == 'DATAEND':
|
||||||
|
writepipestr(b'DONE')
|
||||||
|
return
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MESHLIST':
|
||||||
|
meshCount = 0
|
||||||
|
for meshobj in bpy.data.objects:
|
||||||
|
if meshobj.type == 'MESH' and not meshobj.data.library:
|
||||||
|
meshCount += 1
|
||||||
|
writepipebuf(struct.pack('I', meshCount))
|
||||||
|
for meshobj in bpy.data.objects:
|
||||||
|
if meshobj.type == 'MESH' and not meshobj.data.library:
|
||||||
|
writepipestr(meshobj.name.encode())
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'LIGHTLIST':
|
||||||
|
lightCount = 0
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.type == 'LIGHT' and not obj.data.library:
|
||||||
|
lightCount += 1
|
||||||
|
writepipebuf(struct.pack('I', lightCount))
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.type == 'LIGHT' and not obj.data.library:
|
||||||
|
writepipestr(obj.name.encode())
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MESHAABB':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.mesh_aabb(writepipebuf)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MESHCOMPILE':
|
||||||
|
meshName = bpy.context.scene.hecl_mesh_obj
|
||||||
|
if meshName not in bpy.data.objects:
|
||||||
|
writepipestr(('mesh %s not found' % meshName).encode())
|
||||||
|
continue
|
||||||
|
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName])
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'ARMATURECOMPILE':
|
||||||
|
armName = bpy.context.scene.hecl_arm_obj
|
||||||
|
if armName not in bpy.data.objects:
|
||||||
|
writepipestr(('armature %s not found' % armName).encode())
|
||||||
|
continue
|
||||||
|
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.armature.cook(writepipebuf, bpy.data.objects[armName].data)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MESHCOMPILENAME':
|
||||||
|
meshName = cmdargs[1]
|
||||||
|
useLuv = int(cmdargs[2])
|
||||||
|
|
||||||
|
if meshName not in bpy.data.objects:
|
||||||
|
writepipestr(('mesh %s not found' % meshName).encode())
|
||||||
|
continue
|
||||||
|
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], useLuv)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MESHCOMPILENAMECOLLISION':
|
||||||
|
meshName = cmdargs[1]
|
||||||
|
|
||||||
|
if meshName not in bpy.data.objects:
|
||||||
|
writepipestr(('mesh %s not found' % meshName).encode())
|
||||||
|
continue
|
||||||
|
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.hmdl.cookcol(writepipebuf, bpy.data.objects[meshName])
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MESHCOMPILECOLLISIONALL':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
colCount = 0
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.type == 'MESH' and not obj.data.library:
|
||||||
|
colCount += 1
|
||||||
|
|
||||||
|
writepipebuf(struct.pack('I', colCount))
|
||||||
|
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.type == 'MESH' and not obj.data.library:
|
||||||
|
hecl.hmdl.cookcol(writepipebuf, obj)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MESHCOMPILEPATH':
|
||||||
|
meshName = bpy.context.scene.hecl_path_obj
|
||||||
|
if meshName not in bpy.data.objects:
|
||||||
|
writepipestr(('mesh %s not found' % meshName).encode())
|
||||||
|
continue
|
||||||
|
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.path.cook(writepipebuf, bpy.data.objects[meshName])
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'WORLDCOMPILE':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.swld.cook(writepipebuf)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'FRAMECOMPILE':
|
||||||
|
version = int(cmdargs[1])
|
||||||
|
if version != 0 and version != 1:
|
||||||
|
writepipestr(b'bad version')
|
||||||
|
continue
|
||||||
|
|
||||||
|
writepipestr(b'OK')
|
||||||
|
buffer = hecl.frme.cook(writepipebuf, version, PathHasher())
|
||||||
|
writepipestr(b'FRAMEDONE')
|
||||||
|
writepipebuf(struct.pack('I', len(buffer)))
|
||||||
|
writepipebuf(buffer)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'LIGHTCOMPILEALL':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
lampCount = 0
|
||||||
|
firstSpot = None
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj.type == 'LIGHT':
|
||||||
|
lampCount += 1
|
||||||
|
if firstSpot is None and obj.data.type == 'SPOT':
|
||||||
|
firstSpot = obj
|
||||||
|
|
||||||
|
# Ambient
|
||||||
|
world = bpy.context.scene.world
|
||||||
|
ambient_energy = 0.0
|
||||||
|
ambient_color = None
|
||||||
|
if world.use_nodes and 'Background' in world.node_tree.nodes:
|
||||||
|
bg_node = world.node_tree.nodes['Background']
|
||||||
|
ambient_energy = bg_node.inputs[1].default_value
|
||||||
|
ambient_color = bg_node.inputs[0].default_value
|
||||||
|
if ambient_energy:
|
||||||
|
lampCount += 1
|
||||||
|
|
||||||
|
writepipebuf(struct.pack('I', lampCount))
|
||||||
|
|
||||||
|
if firstSpot is not None:
|
||||||
|
writelight(firstSpot)
|
||||||
|
|
||||||
|
if ambient_energy:
|
||||||
|
writepipebuf(struct.pack('ffffffffffffffff',
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1.0))
|
||||||
|
writepipebuf(struct.pack('fff', ambient_color[0], ambient_color[1], ambient_color[2]))
|
||||||
|
writepipebuf(struct.pack('IIfffffb', 0, 0, ambient_energy, 0.0, 1.0, 0.0, 0.0, False))
|
||||||
|
writepipestr(b'AMBIENT')
|
||||||
|
|
||||||
|
# Lamp objects
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if obj != firstSpot and obj.type == 'LIGHT':
|
||||||
|
writelight(obj)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'GETTEXTURES':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
|
||||||
|
img_count = 0
|
||||||
|
for img in bpy.data.images:
|
||||||
|
if img.type == 'IMAGE':
|
||||||
|
img_count += 1
|
||||||
|
writepipebuf(struct.pack('I', img_count))
|
||||||
|
|
||||||
|
for img in bpy.data.images:
|
||||||
|
if img.type == 'IMAGE':
|
||||||
|
path = os.path.normpath(bpy.path.abspath(img.filepath))
|
||||||
|
writepipebuf(struct.pack('I', len(path)))
|
||||||
|
writepipebuf(path.encode())
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'ACTORCOMPILE':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.sact.cook(writepipebuf)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'ACTORCOMPILECHARACTERONLY':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.sact.cook_character_only(writepipebuf)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'ACTIONCOMPILECHANNELSONLY':
|
||||||
|
actionName = cmdargs[1]
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.sact.cook_action_channels_only(writepipebuf, actionName)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'GETSUBTYPENAMES':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.sact.get_subtype_names(writepipebuf)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'GETSUBTYPEOVERLAYNAMES':
|
||||||
|
subtypeName = cmdargs[1]
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.sact.get_subtype_overlay_names(writepipebuf, subtypeName)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'GETATTACHMENTNAMES':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.sact.get_attachment_names(writepipebuf)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'GETACTIONNAMES':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.sact.get_action_names(writepipebuf)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'GETBONEMATRICES':
|
||||||
|
armName = cmdargs[1]
|
||||||
|
|
||||||
|
if armName not in bpy.data.objects:
|
||||||
|
writepipestr(('armature %s not found' % armName).encode())
|
||||||
|
continue
|
||||||
|
|
||||||
|
armObj = bpy.data.objects[armName]
|
||||||
|
if armObj.type != 'ARMATURE':
|
||||||
|
writepipestr(('object %s not an ARMATURE' % armName).encode())
|
||||||
|
continue
|
||||||
|
|
||||||
|
writepipestr(b'OK')
|
||||||
|
writepipebuf(struct.pack('I', len(armObj.data.bones)))
|
||||||
|
for bone in armObj.data.bones:
|
||||||
|
writepipebuf(struct.pack('I', len(bone.name)))
|
||||||
|
writepipebuf(bone.name.encode())
|
||||||
|
for r in bone.matrix_local.to_3x3():
|
||||||
|
for c in r:
|
||||||
|
writepipebuf(struct.pack('f', c))
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'RENDERPVS':
|
||||||
|
pathOut = cmdargs[1]
|
||||||
|
locX = float(cmdargs[2])
|
||||||
|
locY = float(cmdargs[3])
|
||||||
|
locZ = float(cmdargs[4])
|
||||||
|
hecl.srea.render_pvs(pathOut, (locX, locY, locZ))
|
||||||
|
writepipestr(b'OK')
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'RENDERPVSLIGHT':
|
||||||
|
pathOut = cmdargs[1]
|
||||||
|
lightName = cmdargs[2]
|
||||||
|
hecl.srea.render_pvs_light(pathOut, lightName)
|
||||||
|
writepipestr(b'OK')
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MAPAREACOMPILE':
|
||||||
|
if 'MAP' not in bpy.data.objects:
|
||||||
|
writepipestr(('"MAP" object not in .blend').encode())
|
||||||
|
continue
|
||||||
|
map_obj = bpy.data.objects['MAP']
|
||||||
|
if map_obj.type != 'MESH':
|
||||||
|
writepipestr(('object "MAP" not a MESH').encode())
|
||||||
|
continue
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.mapa.cook(writepipebuf, map_obj)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MAPUNIVERSECOMPILE':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
hecl.mapu.cook(writepipebuf)
|
||||||
|
|
||||||
|
loaded_blend = None
|
||||||
|
|
||||||
|
# Main exception handling
|
||||||
|
try:
|
||||||
|
# Command loop
|
||||||
|
while True:
|
||||||
|
cmdargs = read_cmdargs()
|
||||||
|
print(cmdargs)
|
||||||
|
|
||||||
|
if cmdargs[0] == 'QUIT':
|
||||||
|
quitblender()
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'OPEN':
|
||||||
|
if 'FINISHED' in bpy.ops.wm.open_mainfile(filepath=cmdargs[1]):
|
||||||
|
if bpy.ops.object.mode_set.poll():
|
||||||
|
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||||
|
loaded_blend = cmdargs[1]
|
||||||
|
writepipestr(b'FINISHED')
|
||||||
|
else:
|
||||||
|
writepipestr(b'CANCELLED')
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'CREATE':
|
||||||
|
if len(cmdargs) >= 4:
|
||||||
|
bpy.ops.wm.open_mainfile(filepath=cmdargs[3])
|
||||||
|
else:
|
||||||
|
bpy.ops.wm.read_homefile(use_empty=True)
|
||||||
|
bpy.context.scene.world = bpy.data.worlds.new('World')
|
||||||
|
loaded_blend = cmdargs[1]
|
||||||
|
bpy.context.preferences.filepaths.save_version = 0
|
||||||
|
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdargs[1]):
|
||||||
|
bpy.ops.file.hecl_patching_load()
|
||||||
|
bpy.context.scene.hecl_type = cmdargs[2]
|
||||||
|
writepipestr(b'FINISHED')
|
||||||
|
else:
|
||||||
|
writepipestr(b'CANCELLED')
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'GETTYPE':
|
||||||
|
writepipestr(bpy.context.scene.hecl_type.encode())
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'GETMESHRIGGED':
|
||||||
|
meshName = bpy.context.scene.hecl_mesh_obj
|
||||||
|
if meshName not in bpy.data.objects:
|
||||||
|
writepipestr(b'FALSE')
|
||||||
|
else:
|
||||||
|
if len(bpy.data.objects[meshName].vertex_groups):
|
||||||
|
writepipestr(b'TRUE')
|
||||||
|
else:
|
||||||
|
writepipestr(b'FALSE')
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'SAVE':
|
||||||
|
bpy.context.preferences.filepaths.save_version = 0
|
||||||
|
print('SAVING %s' % loaded_blend)
|
||||||
|
if loaded_blend:
|
||||||
|
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=loaded_blend, check_existing=False, compress=True):
|
||||||
|
writepipestr(b'FINISHED')
|
||||||
|
else:
|
||||||
|
writepipestr(b'CANCELLED')
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'PYBEGIN':
|
||||||
|
writepipestr(b'READY')
|
||||||
|
globals = {'hecl':hecl}
|
||||||
|
compbuf = str()
|
||||||
|
bracket_count = 0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
line = readpipestr()
|
||||||
|
|
||||||
|
# ANIM check
|
||||||
|
if line == b'PYANIM':
|
||||||
|
# Ensure remaining block gets executed
|
||||||
|
if len(compbuf):
|
||||||
|
exec_compbuf(compbuf, globals)
|
||||||
|
compbuf = str()
|
||||||
|
animin_loop(globals)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# End check
|
||||||
|
elif line == b'PYEND':
|
||||||
|
# Ensure remaining block gets executed
|
||||||
|
if len(compbuf):
|
||||||
|
exec_compbuf(compbuf, globals)
|
||||||
|
compbuf = str()
|
||||||
|
writepipestr(b'DONE')
|
||||||
|
break
|
||||||
|
|
||||||
|
# Syntax filter
|
||||||
|
linestr = line.decode().rstrip()
|
||||||
|
if not len(linestr) or linestr.lstrip()[0] == '#':
|
||||||
|
writepipestr(b'OK')
|
||||||
|
continue
|
||||||
|
leading_spaces = len(linestr) - len(linestr.lstrip())
|
||||||
|
|
||||||
|
# Block lines always get appended right away
|
||||||
|
if linestr.endswith(':') or leading_spaces or bracket_count:
|
||||||
|
if len(compbuf):
|
||||||
|
compbuf += '\n'
|
||||||
|
compbuf += linestr
|
||||||
|
bracket_count += count_brackets(linestr)
|
||||||
|
writepipestr(b'OK')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Complete non-block statement in compbuf
|
||||||
|
if len(compbuf):
|
||||||
|
exec_compbuf(compbuf, globals)
|
||||||
|
|
||||||
|
# Establish new compbuf
|
||||||
|
compbuf = linestr
|
||||||
|
bracket_count += count_brackets(linestr)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
writepipestr(b'EXCEPTION')
|
||||||
|
raise
|
||||||
|
break
|
||||||
|
writepipestr(b'OK')
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'PYEND':
|
||||||
|
writepipestr(b'ERROR')
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'DATABEGIN':
|
||||||
|
try:
|
||||||
|
dataout_loop()
|
||||||
|
except Exception as e:
|
||||||
|
writepipestr(b'EXCEPTION')
|
||||||
|
raise
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'DATAEND':
|
||||||
|
writepipestr(b'ERROR')
|
||||||
|
|
||||||
|
else:
|
||||||
|
hecl.command(cmdargs, writepipestr, writepipebuf)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
fout = open(err_path, 'w')
|
||||||
|
traceback.print_exc(file=fout)
|
||||||
|
fout.close()
|
||||||
|
raise
|
|
@ -0,0 +1,18 @@
|
||||||
|
import sys, os
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
def zipdir(path, ziph):
|
||||||
|
# ziph is zipfile handle
|
||||||
|
for root, dirs, files in os.walk(path):
|
||||||
|
for file in files:
|
||||||
|
ziph.write(os.path.join(root, file))
|
||||||
|
|
||||||
|
package_path = 'hecl'
|
||||||
|
target_zip = sys.argv[1]
|
||||||
|
|
||||||
|
zf = zipfile.ZipFile(target_zip, mode='w', compression=zipfile.ZIP_DEFLATED)
|
||||||
|
#print('GENERATING', target_zip)
|
||||||
|
if os.path.isdir(package_path):
|
||||||
|
zipdir(package_path, zf)
|
||||||
|
|
||||||
|
zf.close()
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
git submodule update --init --recursive
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
if(NOT WINDOWS_STORE)
|
||||||
|
|
||||||
|
add_executable(hecl main.cpp
|
||||||
|
ToolBase.hpp
|
||||||
|
ToolPackage.hpp
|
||||||
|
ToolExtract.hpp
|
||||||
|
ToolInit.hpp
|
||||||
|
ToolHelp.hpp
|
||||||
|
ToolCook.hpp
|
||||||
|
ToolImage.hpp
|
||||||
|
ToolSpec.hpp
|
||||||
|
../DataSpecRegistry.hpp.in)
|
||||||
|
if(COMMAND add_sanitizers)
|
||||||
|
add_sanitizers(hecl)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT WIN32)
|
||||||
|
list(APPEND PLAT_LIBS pthread)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
find_library(CF_LIBRARY CoreFoundation)
|
||||||
|
list(APPEND PLAT_LIBS ${CF_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(hecl PUBLIC ${DATA_SPEC_LIBS} hecl-full)
|
||||||
|
|
||||||
|
if(TARGET nod)
|
||||||
|
target_link_libraries(hecl PUBLIC nod)
|
||||||
|
target_compile_definitions(hecl PUBLIC HECL_HAS_NOD=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endif()
|
|
@ -0,0 +1,235 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#else
|
||||||
|
#include <conio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "hecl/Database.hpp"
|
||||||
|
#include "logvisor/logvisor.hpp"
|
||||||
|
|
||||||
|
extern logvisor::Module LogModule;
|
||||||
|
|
||||||
|
struct ToolPassInfo {
|
||||||
|
hecl::SystemString pname;
|
||||||
|
hecl::SystemString cwd;
|
||||||
|
std::vector<hecl::SystemString> args;
|
||||||
|
std::vector<hecl::SystemChar> flags;
|
||||||
|
hecl::SystemString output;
|
||||||
|
hecl::Database::Project* project = nullptr;
|
||||||
|
unsigned verbosityLevel = 0;
|
||||||
|
bool force = false;
|
||||||
|
bool yes = false;
|
||||||
|
bool gui = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RED "\033[0;31m"
|
||||||
|
#define GREEN "\033[0;32m"
|
||||||
|
#define YELLOW "\033[0;33m"
|
||||||
|
#define BLUE "\033[0;34m"
|
||||||
|
#define MAGENTA "\033[0;35m"
|
||||||
|
#define CYAN "\033[0;36m"
|
||||||
|
#define BOLD "\033[1m"
|
||||||
|
#define NORMAL "\033[0m"
|
||||||
|
|
||||||
|
#define HIDE_CURSOR "\033[?25l"
|
||||||
|
#define SHOW_CURSOR "\033[?25h"
|
||||||
|
|
||||||
|
#define WRAP_INDENT 4
|
||||||
|
|
||||||
|
extern bool XTERM_COLOR;
|
||||||
|
|
||||||
|
class ToolBase {
|
||||||
|
protected:
|
||||||
|
const ToolPassInfo& m_info;
|
||||||
|
bool m_good = false;
|
||||||
|
|
||||||
|
bool continuePrompt() {
|
||||||
|
if (!m_info.yes) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\n" BLUE BOLD "Continue?" NORMAL " (Y/n) ")));
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\nContinue? (Y/n) ")));
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
int ch;
|
||||||
|
#ifndef _WIN32
|
||||||
|
struct termios tioOld, tioNew;
|
||||||
|
tcgetattr(0, &tioOld);
|
||||||
|
tioNew = tioOld;
|
||||||
|
tioNew.c_lflag &= ~ICANON;
|
||||||
|
tcsetattr(0, TCSANOW, &tioNew);
|
||||||
|
while ((ch = getchar()))
|
||||||
|
#else
|
||||||
|
while ((ch = getch()))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (ch == 'n' || ch == 'N') {
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\n")));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ch == 'y' || ch == 'Y' || ch == '\r' || ch == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#ifndef _WIN32
|
||||||
|
tcsetattr(0, TCSANOW, &tioOld);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\n")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ToolBase(const ToolPassInfo& info) : m_info(info) {
|
||||||
|
hecl::VerbosityLevel = info.verbosityLevel;
|
||||||
|
hecl::GuiMode = info.gui;
|
||||||
|
}
|
||||||
|
virtual ~ToolBase() = default;
|
||||||
|
virtual hecl::SystemStringView toolName() const = 0;
|
||||||
|
virtual int run() = 0;
|
||||||
|
virtual void cancel() {}
|
||||||
|
explicit operator bool() const { return m_good; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class HelpOutput {
|
||||||
|
public:
|
||||||
|
using HelpFunc = void (*)(HelpOutput&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE* m_sout = nullptr;
|
||||||
|
HelpFunc m_helpFunc;
|
||||||
|
int m_lineWidth;
|
||||||
|
hecl::SystemString m_wrapBuffer;
|
||||||
|
|
||||||
|
void _wrapBuf(hecl::SystemString& string) {
|
||||||
|
int counter;
|
||||||
|
hecl::SystemString::iterator it = string.begin();
|
||||||
|
|
||||||
|
while (it != string.end()) {
|
||||||
|
std::ptrdiff_t v = it - string.begin();
|
||||||
|
|
||||||
|
/* copy string until the end of the line is reached */
|
||||||
|
for (counter = WRAP_INDENT; counter < m_lineWidth; ++counter) {
|
||||||
|
if (it >= string.end())
|
||||||
|
return;
|
||||||
|
if (*it == _SYS_STR('\n')) {
|
||||||
|
counter = WRAP_INDENT;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
if (counter == WRAP_INDENT) {
|
||||||
|
for (int i = 0; i < WRAP_INDENT; ++i)
|
||||||
|
it = string.insert(it, _SYS_STR(' ')) + 1;
|
||||||
|
}
|
||||||
|
if (it >= string.end())
|
||||||
|
return;
|
||||||
|
if (*it != _SYS_STR('\n'))
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
/* check for whitespace */
|
||||||
|
if (isspace(*it)) {
|
||||||
|
*it = _SYS_STR('\n');
|
||||||
|
counter = WRAP_INDENT;
|
||||||
|
++it;
|
||||||
|
} else {
|
||||||
|
/* check for nearest whitespace back in string */
|
||||||
|
for (hecl::SystemString::iterator k = it; k != string.begin(); --k) {
|
||||||
|
if (isspace(*k)) {
|
||||||
|
counter = WRAP_INDENT;
|
||||||
|
if (k - string.begin() < v)
|
||||||
|
k = string.insert(it, _SYS_STR('\n'));
|
||||||
|
else
|
||||||
|
*k = _SYS_STR('\n');
|
||||||
|
it = k + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HelpOutput(HelpFunc helpFunc)
|
||||||
|
: m_helpFunc(helpFunc), m_lineWidth(hecl::GuiMode ? 120 : hecl::ConsoleWidth()) {}
|
||||||
|
|
||||||
|
void go() {
|
||||||
|
#if _WIN32
|
||||||
|
m_sout = stdout;
|
||||||
|
m_helpFunc(*this);
|
||||||
|
#else
|
||||||
|
m_sout = popen("less -R", "w");
|
||||||
|
if (m_sout) {
|
||||||
|
m_helpFunc(*this);
|
||||||
|
pclose(m_sout);
|
||||||
|
} else {
|
||||||
|
m_sout = stdout;
|
||||||
|
m_helpFunc(*this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(const hecl::SystemChar* str) { fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), str); }
|
||||||
|
|
||||||
|
void printBold(const hecl::SystemChar* str) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "")), str);
|
||||||
|
else
|
||||||
|
fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void secHead(const hecl::SystemChar* headName) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "\n")), headName);
|
||||||
|
else
|
||||||
|
fmt::print(m_sout, FMT_STRING(_SYS_STR("{}\n")), headName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void optionHead(const hecl::SystemChar* flag, const hecl::SystemChar* synopsis) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL " ({})\n")), flag, synopsis);
|
||||||
|
else
|
||||||
|
fmt::print(m_sout, FMT_STRING(_SYS_STR("{} ({})\n")), flag, synopsis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void beginWrap() { m_wrapBuffer.clear(); }
|
||||||
|
|
||||||
|
void wrap(const hecl::SystemChar* str) { m_wrapBuffer += str; }
|
||||||
|
|
||||||
|
void wrapBold(const hecl::SystemChar* str) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
m_wrapBuffer += _SYS_STR("" BOLD "");
|
||||||
|
m_wrapBuffer += str;
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
m_wrapBuffer += _SYS_STR("" NORMAL "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void endWrap() {
|
||||||
|
_wrapBuf(m_wrapBuffer);
|
||||||
|
m_wrapBuffer += _SYS_STR('\n');
|
||||||
|
fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), m_wrapBuffer);
|
||||||
|
m_wrapBuffer.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static hecl::SystemString MakePathArgAbsolute(const hecl::SystemString& arg, const hecl::SystemString& cwd) {
|
||||||
|
#if _WIN32
|
||||||
|
if (arg.size() >= 2 && iswalpha(arg[0]) && arg[1] == _SYS_STR(':'))
|
||||||
|
return arg;
|
||||||
|
if (arg[0] == _SYS_STR('\\') || arg[0] == _SYS_STR('/'))
|
||||||
|
return arg;
|
||||||
|
return cwd + _SYS_STR('\\') + arg;
|
||||||
|
#else
|
||||||
|
if (arg[0] == _SYS_STR('/') || arg[0] == _SYS_STR('\\'))
|
||||||
|
return arg;
|
||||||
|
if (cwd.back() == _SYS_STR('/') || cwd.back() == _SYS_STR('\\'))
|
||||||
|
return cwd + arg;
|
||||||
|
return cwd + _SYS_STR('/') + arg;
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
#include "hecl/ClientProcess.hpp"
|
||||||
|
|
||||||
|
class ToolCook final : public ToolBase {
|
||||||
|
std::vector<hecl::ProjectPath> m_selectedItems;
|
||||||
|
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
|
||||||
|
hecl::Database::Project* m_useProj;
|
||||||
|
const hecl::Database::DataSpecEntry* m_spec = nullptr;
|
||||||
|
bool m_recursive = false;
|
||||||
|
bool m_fast = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ToolCook(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
|
||||||
|
/* Check for recursive flag */
|
||||||
|
for (hecl::SystemChar arg : info.flags)
|
||||||
|
if (arg == _SYS_STR('r'))
|
||||||
|
m_recursive = true;
|
||||||
|
|
||||||
|
/* Scan args */
|
||||||
|
if (info.args.size()) {
|
||||||
|
/* See if project path is supplied via args and use that over the getcwd one */
|
||||||
|
m_selectedItems.reserve(info.args.size());
|
||||||
|
for (const hecl::SystemString& arg : info.args) {
|
||||||
|
if (arg.empty())
|
||||||
|
continue;
|
||||||
|
else if (arg == _SYS_STR("--fast")) {
|
||||||
|
m_fast = true;
|
||||||
|
continue;
|
||||||
|
} else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) {
|
||||||
|
hecl::SystemString specName(arg.begin() + 7, arg.end());
|
||||||
|
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
|
||||||
|
if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str())) {
|
||||||
|
m_spec = spec;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_spec)
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find data spec '{}'")), specName);
|
||||||
|
continue;
|
||||||
|
} else if (arg.size() >= 2 && arg[0] == _SYS_STR('-') && arg[1] == _SYS_STR('-'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hecl::SystemString subPath;
|
||||||
|
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
|
||||||
|
if (root) {
|
||||||
|
if (!m_fallbackProj) {
|
||||||
|
m_fallbackProj.reset(new hecl::Database::Project(root));
|
||||||
|
m_useProj = m_fallbackProj.get();
|
||||||
|
} else if (m_fallbackProj->getProjectRootPath() != root)
|
||||||
|
LogModule.report(logvisor::Fatal,
|
||||||
|
FMT_STRING(_SYS_STR("hecl cook can only process multiple items in the same project; ")
|
||||||
|
_SYS_STR("'{}' and '{}' are different projects")),
|
||||||
|
m_fallbackProj->getProjectRootPath().getAbsolutePath(),
|
||||||
|
root.getAbsolutePath());
|
||||||
|
m_selectedItems.emplace_back(*m_useProj, subPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_useProj)
|
||||||
|
LogModule.report(logvisor::Fatal,
|
||||||
|
FMT_STRING("hecl cook must be ran within a project directory or "
|
||||||
|
"provided a path within a project"));
|
||||||
|
|
||||||
|
/* Default case: recursive at root */
|
||||||
|
if (m_selectedItems.empty()) {
|
||||||
|
m_selectedItems.reserve(1);
|
||||||
|
m_selectedItems.push_back({hecl::ProjectPath(*m_useProj, _SYS_STR(""))});
|
||||||
|
m_recursive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Help(HelpOutput& help) {
|
||||||
|
help.secHead(_SYS_STR("NAME"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl-cook - Cook objects within the project database\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("SYNOPSIS"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl cook [-rf] [--fast] [--spec=<spec>] [<pathspec>...]\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("DESCRIPTION"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("This command initiates a cooking pass on the project database. Cooking ")
|
||||||
|
_SYS_STR("is analogous to compiling in software development. The resulting object buffers ")
|
||||||
|
_SYS_STR("are cached within the project database. HECL performs the following ")
|
||||||
|
_SYS_STR("tasks for each object during the cook process:\n\n"));
|
||||||
|
help.wrapBold(_SYS_STR("- Object Gather: "));
|
||||||
|
help.wrap(_SYS_STR("Files added with "));
|
||||||
|
help.wrapBold(_SYS_STR("hecl add"));
|
||||||
|
help.wrap(_SYS_STR(" are queried for their dependent files (e.g. "));
|
||||||
|
help.wrapBold(_SYS_STR(".blend"));
|
||||||
|
help.wrap(_SYS_STR(" files return any linked "));
|
||||||
|
help.wrapBold(_SYS_STR(".png"));
|
||||||
|
help.wrap(_SYS_STR(" images). If the dependent files are unable to be found, the cook process aborts.\n\n"));
|
||||||
|
help.wrapBold(_SYS_STR("- Modtime Comparison: "));
|
||||||
|
help.wrap(_SYS_STR("Files that have previously finished a cook pass are inspected for their time of ")
|
||||||
|
_SYS_STR("last modification. If the file hasn't changed since its previous cook-pass, the ") _SYS_STR(
|
||||||
|
"process is skipped. If the file has been moved or deleted, the object is automatically ")
|
||||||
|
_SYS_STR("removed from the project database.\n\n"));
|
||||||
|
help.wrapBold(_SYS_STR("- Cook: "));
|
||||||
|
help.wrap(_SYS_STR("A type-specific procedure compiles the file's contents into an efficient format ")
|
||||||
|
_SYS_STR("for use by the runtime. A data-buffer is provided to HECL.\n\n"));
|
||||||
|
help.wrapBold(_SYS_STR("- Hash and Compress: "));
|
||||||
|
help.wrap(_SYS_STR("The data-buffer is hashed and compressed before being cached in the object database.\n\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("OPTIONS"));
|
||||||
|
help.optionHead(_SYS_STR("<pathspec>..."), _SYS_STR("input file(s)"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Specifies working file(s) containing production data to be cooked by HECL. ")
|
||||||
|
_SYS_STR("Glob-strings may be specified (e.g. "));
|
||||||
|
help.wrapBold(_SYS_STR("*.blend"));
|
||||||
|
help.wrap(_SYS_STR(") to automatically cook all matching current-directory files in the project database. ")
|
||||||
|
_SYS_STR("If no path specified, all files in the project database are cooked.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.optionHead(_SYS_STR("-r"), _SYS_STR("recursion"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Enables recursive file-matching for cooking entire directories of working files.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
help.optionHead(_SYS_STR("-f"), _SYS_STR("force"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Forces cooking of all matched files, ignoring timestamp differences.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
help.optionHead(_SYS_STR("--fast"), _SYS_STR("fast cook"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Performs draft-optimization cooking for supported data types.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking. ")
|
||||||
|
_SYS_STR("This build of hecl supports the following values of <spec>:\n"));
|
||||||
|
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
|
||||||
|
if (!spec->m_factory)
|
||||||
|
continue;
|
||||||
|
help.wrap(_SYS_STR(" "));
|
||||||
|
help.wrapBold(spec->m_name.data());
|
||||||
|
help.wrap(_SYS_STR("\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemStringView toolName() const override { return _SYS_STR("cook"sv); }
|
||||||
|
|
||||||
|
int run() override {
|
||||||
|
hecl::MultiProgressPrinter printer(true);
|
||||||
|
hecl::ClientProcess cp(&printer);
|
||||||
|
for (const hecl::ProjectPath& path : m_selectedItems)
|
||||||
|
m_useProj->cookPath(path, printer, m_recursive, m_info.force, m_fast, m_spec, &cp);
|
||||||
|
cp.waitUntilComplete();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancel() override { m_useProj->interruptCook(); }
|
||||||
|
};
|
|
@ -0,0 +1,166 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include <conio.h>
|
||||||
|
#else
|
||||||
|
#include <termios.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "hecl/MultiProgressPrinter.hpp"
|
||||||
|
|
||||||
|
class ToolExtract final : public ToolBase {
|
||||||
|
hecl::Database::IDataSpec::ExtractPassInfo m_einfo;
|
||||||
|
struct SpecExtractPass {
|
||||||
|
const hecl::Database::DataSpecEntry* m_entry;
|
||||||
|
std::unique_ptr<hecl::Database::IDataSpec> m_instance;
|
||||||
|
SpecExtractPass(const hecl::Database::DataSpecEntry* entry, std::unique_ptr<hecl::Database::IDataSpec>&& instance)
|
||||||
|
: m_entry(entry), m_instance(std::move(instance)) {}
|
||||||
|
SpecExtractPass(const SpecExtractPass& other) = delete;
|
||||||
|
SpecExtractPass(SpecExtractPass&& other) = default;
|
||||||
|
};
|
||||||
|
std::vector<SpecExtractPass> m_specPasses;
|
||||||
|
std::vector<hecl::Database::IDataSpec::ExtractReport> m_reps;
|
||||||
|
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
|
||||||
|
hecl::Database::Project* m_useProj = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ToolExtract(const ToolPassInfo& info) : ToolBase(info) {
|
||||||
|
if (!m_info.args.size())
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING("hecl extract needs a source path as its first argument"));
|
||||||
|
|
||||||
|
if (!info.project) {
|
||||||
|
hecl::SystemString rootDir;
|
||||||
|
|
||||||
|
if (info.output.empty()) {
|
||||||
|
/* Get name from input file and init project there */
|
||||||
|
hecl::SystemString baseFile = info.args.front();
|
||||||
|
size_t slashPos = baseFile.rfind(_SYS_STR('/'));
|
||||||
|
if (slashPos == hecl::SystemString::npos)
|
||||||
|
slashPos = baseFile.rfind(_SYS_STR('\\'));
|
||||||
|
if (slashPos != hecl::SystemString::npos)
|
||||||
|
baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end());
|
||||||
|
size_t dotPos = baseFile.rfind(_SYS_STR('.'));
|
||||||
|
if (dotPos != hecl::SystemString::npos)
|
||||||
|
baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos);
|
||||||
|
|
||||||
|
if (baseFile.empty())
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING("hecl extract must be ran within a project directory"));
|
||||||
|
|
||||||
|
rootDir = info.cwd + baseFile;
|
||||||
|
} else {
|
||||||
|
if (hecl::PathRelative(info.output.c_str()))
|
||||||
|
rootDir = info.cwd + info.output;
|
||||||
|
else
|
||||||
|
rootDir = info.output;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ErrorRef = logvisor::ErrorCount;
|
||||||
|
hecl::ProjectRootPath newProjRoot(rootDir);
|
||||||
|
newProjRoot.makeDir();
|
||||||
|
m_fallbackProj.reset(new hecl::Database::Project(newProjRoot));
|
||||||
|
if (logvisor::ErrorCount > ErrorRef)
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to init project at '{}'")), rootDir);
|
||||||
|
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("initialized project at '{}/.hecl'")), rootDir);
|
||||||
|
m_useProj = m_fallbackProj.get();
|
||||||
|
} else
|
||||||
|
m_useProj = info.project;
|
||||||
|
|
||||||
|
m_einfo.srcpath = m_info.args.front();
|
||||||
|
m_einfo.force = info.force;
|
||||||
|
m_einfo.extractArgs.reserve(info.args.size());
|
||||||
|
auto it = info.args.cbegin();
|
||||||
|
++it;
|
||||||
|
for (; it != info.args.cend(); ++it)
|
||||||
|
m_einfo.extractArgs.push_back(*it);
|
||||||
|
|
||||||
|
m_specPasses.reserve(hecl::Database::DATA_SPEC_REGISTRY.size());
|
||||||
|
for (const hecl::Database::DataSpecEntry* entry : hecl::Database::DATA_SPEC_REGISTRY) {
|
||||||
|
if (entry->m_factory) {
|
||||||
|
auto ds = entry->m_factory(*m_useProj, hecl::Database::DataSpecTool::Extract);
|
||||||
|
if (ds && ds->canExtract(m_einfo, m_reps))
|
||||||
|
m_specPasses.emplace_back(entry, std::move(ds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Help(HelpOutput& help) {
|
||||||
|
help.secHead(_SYS_STR("NAME"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl-extract - Extract objects from supported package/image formats\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("SYNOPSIS"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl extract <packagefile> [<subnode>...]\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("DESCRIPTION"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("This command recursively extracts all or part of a dataspec-supported ")
|
||||||
|
_SYS_STR("package format. Each object is decoded to a working format and added to the project.\n\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("OPTIONS"));
|
||||||
|
help.optionHead(_SYS_STR("<packagefile>[/<subnode>...]"), _SYS_STR("input file"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Specifies the package file or disc image to source data from. ")
|
||||||
|
_SYS_STR("An optional subnode specifies a named hierarchical-node specific ")
|
||||||
|
_SYS_STR("to the game architecture (levels/areas)."));
|
||||||
|
help.endWrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemStringView toolName() const override { return _SYS_STR("extract"sv); }
|
||||||
|
|
||||||
|
static void _recursivePrint(int level, hecl::Database::IDataSpec::ExtractReport& rep) {
|
||||||
|
for (int l = 0; l < level; ++l)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" ")));
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "")), rep.name);
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("{}")), rep.name);
|
||||||
|
|
||||||
|
if (rep.desc.size())
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" [{}]")), rep.desc);
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\n")));
|
||||||
|
for (hecl::Database::IDataSpec::ExtractReport& child : rep.childOpts)
|
||||||
|
_recursivePrint(level + 1, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() override {
|
||||||
|
if (m_specPasses.empty()) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" RED BOLD "NOTHING TO EXTRACT" NORMAL "\n")));
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("NOTHING TO EXTRACT\n")));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" GREEN BOLD "ABOUT TO EXTRACT:" NORMAL "\n")));
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("ABOUT TO EXTRACT:\n")));
|
||||||
|
|
||||||
|
for (hecl::Database::IDataSpec::ExtractReport& rep : m_reps) {
|
||||||
|
_recursivePrint(0, rep);
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\n")));
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (continuePrompt()) {
|
||||||
|
for (SpecExtractPass& ds : m_specPasses) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" MAGENTA BOLD "Using DataSpec {}:" NORMAL "\n")), ds.m_entry->m_name);
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("Using DataSpec {}:\n")), ds.m_entry->m_name);
|
||||||
|
|
||||||
|
ds.m_instance->doExtract(m_einfo, {true});
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\n\n")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,80 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class ToolHelp final : public ToolBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ToolHelp(const ToolPassInfo& info) : ToolBase(info) {
|
||||||
|
if (m_info.args.empty()) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING("help requires a tool name argument"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_good = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ToolHelp() override = default;
|
||||||
|
|
||||||
|
static void Help(HelpOutput& help) {
|
||||||
|
/* clang-format off */
|
||||||
|
help.printBold(
|
||||||
|
_SYS_STR(" ___________ \n")
|
||||||
|
_SYS_STR(" ,.-'\"...........``~., \n")
|
||||||
|
_SYS_STR(" ,.-\".......................\"-., \n")
|
||||||
|
_SYS_STR(" ,/..................................\":, \n")
|
||||||
|
_SYS_STR(" .,?........................................, \n")
|
||||||
|
_SYS_STR(" /...........................................,}\n")
|
||||||
|
_SYS_STR(" ./........................................,:`^`..}\n")
|
||||||
|
_SYS_STR(" ./.......................................,:\"...../\n")
|
||||||
|
_SYS_STR(" ?.....__..................................:`...../\n")
|
||||||
|
_SYS_STR(" /__.(...\"~-,_...........................,:`....../\n")
|
||||||
|
_SYS_STR(" /(_....\"~,_....\"~,_.....................,:`...._/ \n")
|
||||||
|
_SYS_STR(" {.._$;_....\"=,_.....\"-,_......,.-~-,},.~\";/....} \n")
|
||||||
|
_SYS_STR(" ((...*~_......\"=-._...\";,,./`........../\"..../ \n")
|
||||||
|
_SYS_STR(" ,,,___.`~,......\"~.,....................`......}....../ \n")
|
||||||
|
_SYS_STR("............(....`=-,,...`.........................(...;_,,-\" \n")
|
||||||
|
_SYS_STR("............/.`~,......`-.................................../ \n")
|
||||||
|
_SYS_STR(".............`~.*-,.....................................|,./...,__ \n")
|
||||||
|
_SYS_STR(",,_..........}.>-._...................................|.......`=~-, \n")
|
||||||
|
_SYS_STR(".....`=~-,__......`,................................. \n")
|
||||||
|
_SYS_STR("...................`=~-,,.,........................... \n")
|
||||||
|
_SYS_STR(".........................`:,,..........................`\n")
|
||||||
|
_SYS_STR("...........................`=-,...............,%%`>--==`` \n")
|
||||||
|
_SYS_STR(".................................._.........._,-%%...` \n")
|
||||||
|
_SYS_STR("...................................,\n"));
|
||||||
|
/* clang-format on */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ShowHelp(const hecl::SystemString& toolName) {
|
||||||
|
/* Select tool's help-text streamer */
|
||||||
|
HelpOutput::HelpFunc helpFunc = nullptr;
|
||||||
|
if (toolName == _SYS_STR("init"))
|
||||||
|
helpFunc = ToolInit::Help;
|
||||||
|
else if (toolName == _SYS_STR("spec"))
|
||||||
|
helpFunc = ToolSpec::Help;
|
||||||
|
else if (toolName == _SYS_STR("extract"))
|
||||||
|
helpFunc = ToolExtract::Help;
|
||||||
|
else if (toolName == _SYS_STR("cook"))
|
||||||
|
helpFunc = ToolCook::Help;
|
||||||
|
else if (toolName == _SYS_STR("package") || toolName == _SYS_STR("pack"))
|
||||||
|
helpFunc = ToolPackage::Help;
|
||||||
|
else if (toolName == _SYS_STR("help"))
|
||||||
|
helpFunc = ToolHelp::Help;
|
||||||
|
else {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unrecognized tool '{}' - can't help")), toolName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HelpOutput ho(helpFunc);
|
||||||
|
ho.go();
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemStringView toolName() const override { return _SYS_STR("help"sv); }
|
||||||
|
|
||||||
|
int run() override {
|
||||||
|
ShowHelp(m_info.args.front());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,137 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if HECL_HAS_NOD
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
#include "nod/DiscGCN.hpp"
|
||||||
|
#include "nod/DiscWii.hpp"
|
||||||
|
#include "athena/FileReader.hpp"
|
||||||
|
|
||||||
|
class ToolImage final : public ToolBase {
|
||||||
|
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
|
||||||
|
hecl::Database::Project* m_useProj;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ToolImage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
|
||||||
|
if (!info.project)
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING("hecl image must be ran within a project directory"));
|
||||||
|
|
||||||
|
/* Scan args */
|
||||||
|
if (info.args.size()) {
|
||||||
|
/* See if project path is supplied via args and use that over the getcwd one */
|
||||||
|
for (const hecl::SystemString& arg : info.args) {
|
||||||
|
if (arg.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hecl::SystemString subPath;
|
||||||
|
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
if (!m_fallbackProj) {
|
||||||
|
m_fallbackProj.reset(new hecl::Database::Project(root));
|
||||||
|
m_useProj = m_fallbackProj.get();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_useProj)
|
||||||
|
LogModule.report(logvisor::Fatal,
|
||||||
|
FMT_STRING("hecl image must be ran within a project directory or "
|
||||||
|
"provided a path within a project"));
|
||||||
|
}
|
||||||
|
|
||||||
|
~ToolImage() override = default;
|
||||||
|
|
||||||
|
static void Help(HelpOutput& help) {
|
||||||
|
help.secHead(_SYS_STR("NAME"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl-image - Generate GameCube/Wii disc image from packaged files\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("SYNOPSIS"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl image [<input-dir>]\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("DESCRIPTION"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("This command uses the current contents of `out` to generate a GameCube or ")
|
||||||
|
_SYS_STR("Wii disc image. `hecl package` must have been run previously to be effective.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("OPTIONS"));
|
||||||
|
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting image from. ")
|
||||||
|
_SYS_STR("Project must contain an out/sys and out/files directory to succeed.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemStringView toolName() const override { return _SYS_STR("image"sv); }
|
||||||
|
|
||||||
|
int run() override {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" GREEN BOLD "ABOUT TO IMAGE:" NORMAL "\n")));
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("ABOUT TO IMAGE:\n")));
|
||||||
|
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" {}\n")), m_useProj->getProjectRootPath().getAbsolutePath());
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (continuePrompt()) {
|
||||||
|
hecl::ProjectPath outPath(m_useProj->getProjectWorkingPath(), _SYS_STR("out"));
|
||||||
|
if (!outPath.isDirectory()) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("{} is not a directory")), outPath.getAbsolutePath());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::ProjectPath bootBinPath(outPath, _SYS_STR("sys/boot.bin"));
|
||||||
|
if (!bootBinPath.isFile()) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("{} is not a file")), bootBinPath.getAbsolutePath());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
athena::io::FileReader r(bootBinPath.getAbsolutePath());
|
||||||
|
if (r.hasError()) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open {}")), bootBinPath.getAbsolutePath());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string id = r.readString(6);
|
||||||
|
r.close();
|
||||||
|
|
||||||
|
hecl::SystemStringConv idView(id);
|
||||||
|
hecl::SystemString fileOut = hecl::SystemString(outPath.getAbsolutePath()) + _SYS_STR('/') + idView.c_str();
|
||||||
|
hecl::MultiProgressPrinter printer(true);
|
||||||
|
auto progFunc = [&printer](float totalProg, nod::SystemStringView fileName, size_t fileBytesXfered) {
|
||||||
|
printer.print(fileName.data(), nullptr, totalProg);
|
||||||
|
};
|
||||||
|
if (id[0] == 'G') {
|
||||||
|
fileOut += _SYS_STR(".gcm");
|
||||||
|
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(outPath.getAbsolutePath()) == UINT64_MAX)
|
||||||
|
return 1;
|
||||||
|
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Generating {} as GameCube image")), fileOut);
|
||||||
|
nod::DiscBuilderGCN db(fileOut, progFunc);
|
||||||
|
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
fileOut += _SYS_STR(".iso");
|
||||||
|
bool dualLayer;
|
||||||
|
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(outPath.getAbsolutePath(), dualLayer) == UINT64_MAX)
|
||||||
|
return 1;
|
||||||
|
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Generating {} as {}-layer Wii image")), fileOut,
|
||||||
|
dualLayer ? _SYS_STR("dual") : _SYS_STR("single"));
|
||||||
|
nod::DiscBuilderWii db(fileOut, dualLayer, progFunc);
|
||||||
|
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,77 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
class ToolInit final : public ToolBase {
|
||||||
|
const hecl::SystemString* m_dir = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ToolInit(const ToolPassInfo& info) : ToolBase(info) {
|
||||||
|
hecl::Sstat theStat;
|
||||||
|
const hecl::SystemString* dir;
|
||||||
|
if (info.args.size())
|
||||||
|
dir = &info.args.front();
|
||||||
|
else
|
||||||
|
dir = &info.cwd;
|
||||||
|
|
||||||
|
if (hecl::Stat(dir->c_str(), &theStat)) {
|
||||||
|
hecl::MakeDir(dir->c_str());
|
||||||
|
if (hecl::Stat(dir->c_str(), &theStat)) {
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to stat '{}'")), *dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!S_ISDIR(theStat.st_mode)) {
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}' is not a directory")), *dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemString testPath = *dir + _SYS_STR("/.hecl/beacon");
|
||||||
|
if (!hecl::Stat(testPath.c_str(), &theStat)) {
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("project already exists at '{}'")), *dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dir = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() override {
|
||||||
|
if (!m_dir)
|
||||||
|
return 1;
|
||||||
|
size_t ErrorRef = logvisor::ErrorCount;
|
||||||
|
hecl::Database::Project proj((hecl::ProjectRootPath(*m_dir)));
|
||||||
|
if (logvisor::ErrorCount > ErrorRef)
|
||||||
|
return 1;
|
||||||
|
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("initialized project at '{}/.hecl'")), *m_dir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Help(HelpOutput& help) {
|
||||||
|
help.secHead(_SYS_STR("NAME"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl-init - Initialize a brand-new project database\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("SYNOPSIS"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl init [<dir>]\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("DESCRIPTION"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Creates a "));
|
||||||
|
help.wrapBold(_SYS_STR(".hecl"));
|
||||||
|
help.wrap(_SYS_STR(" directory within the selected directory with an initialized database index. ")
|
||||||
|
_SYS_STR("This constitutes an empty HECL project, ready for making stuff!!\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("OPTIONS"));
|
||||||
|
help.optionHead(_SYS_STR("<dir>"), _SYS_STR("group directory path"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Directory to create new project database in. If not specified, current directory is used.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemStringView toolName() const override { return _SYS_STR("init"sv); }
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
class ToolInstallAddon final : public ToolBase {
|
||||||
|
public:
|
||||||
|
explicit ToolInstallAddon(const ToolPassInfo& info) : ToolBase(info) {}
|
||||||
|
|
||||||
|
int run() override {
|
||||||
|
hecl::blender::SharedBlenderToken.getBlenderConnection();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Help(HelpOutput& help) {
|
||||||
|
help.secHead(_SYS_STR("NAME"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl-installaddon - Installs embedded Blender addon into local Blender\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("SYNOPSIS"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl installaddon\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("DESCRIPTION"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Installs the hecl Blender addon into Blender. The path to the blender executable ")
|
||||||
|
_SYS_STR("can be overridden by setting the BLENDER_BIN environment variable."));
|
||||||
|
help.endWrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemStringView toolName() const override { return _SYS_STR("installaddon"sv); }
|
||||||
|
};
|
|
@ -0,0 +1,180 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
class ToolPackage final : public ToolBase {
|
||||||
|
std::vector<hecl::ProjectPath> m_selectedItems;
|
||||||
|
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
|
||||||
|
hecl::Database::Project* m_useProj;
|
||||||
|
const hecl::Database::DataSpecEntry* m_spec = nullptr;
|
||||||
|
bool m_fast = false;
|
||||||
|
|
||||||
|
void AddSelectedItem(const hecl::ProjectPath& path) {
|
||||||
|
for (const hecl::ProjectPath& item : m_selectedItems)
|
||||||
|
if (item == path)
|
||||||
|
return;
|
||||||
|
m_selectedItems.push_back(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckFile(const hecl::ProjectPath& path) {
|
||||||
|
auto lastComp = path.getLastComponent();
|
||||||
|
if (hecl::StringUtils::BeginsWith(lastComp, _SYS_STR("!world_")) &&
|
||||||
|
hecl::StringUtils::EndsWith(lastComp, _SYS_STR(".blend")))
|
||||||
|
AddSelectedItem(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindSelectedItems(const hecl::ProjectPath& path, bool checkGeneral) {
|
||||||
|
if (path.isFile()) {
|
||||||
|
CheckFile(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t origSize = m_selectedItems.size();
|
||||||
|
hecl::DirectoryEnumerator dEnum(path.getAbsolutePath(), hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false,
|
||||||
|
false, true);
|
||||||
|
for (const auto& ent : dEnum) {
|
||||||
|
hecl::ProjectPath childPath(path, ent.m_name);
|
||||||
|
if (ent.m_isDir)
|
||||||
|
FindSelectedItems(childPath, checkGeneral && childPath.getPathComponents().size() <= 2);
|
||||||
|
else
|
||||||
|
CheckFile(childPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Directory with 2 components not "Shared" or macOS app bundle
|
||||||
|
* and no nested !world.blend files == General PAK */
|
||||||
|
if (checkGeneral && origSize == m_selectedItems.size()) {
|
||||||
|
auto pathComps = path.getPathComponents();
|
||||||
|
if (pathComps.size() == 2 && pathComps[0] != _SYS_STR("out") && pathComps[1] != _SYS_STR("Shared") &&
|
||||||
|
pathComps[0].find(_SYS_STR(".app")) == hecl::SystemString::npos)
|
||||||
|
AddSelectedItem(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ToolPackage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
|
||||||
|
if (!info.project)
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING("hecl package must be ran within a project directory"));
|
||||||
|
|
||||||
|
/* Scan args */
|
||||||
|
if (info.args.size()) {
|
||||||
|
/* See if project path is supplied via args and use that over the getcwd one */
|
||||||
|
m_selectedItems.reserve(info.args.size());
|
||||||
|
for (const hecl::SystemString& arg : info.args) {
|
||||||
|
if (arg.empty())
|
||||||
|
continue;
|
||||||
|
else if (arg == _SYS_STR("--fast")) {
|
||||||
|
m_fast = true;
|
||||||
|
continue;
|
||||||
|
} else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) {
|
||||||
|
hecl::SystemString specName(arg.begin() + 7, arg.end());
|
||||||
|
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
|
||||||
|
if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str())) {
|
||||||
|
m_spec = spec;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_spec)
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find data spec '{}'")), specName);
|
||||||
|
continue;
|
||||||
|
} else if (arg.size() >= 2 && arg[0] == _SYS_STR('-') && arg[1] == _SYS_STR('-'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hecl::SystemString subPath;
|
||||||
|
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
if (!m_fallbackProj) {
|
||||||
|
m_fallbackProj.reset(new hecl::Database::Project(root));
|
||||||
|
m_useProj = m_fallbackProj.get();
|
||||||
|
} else if (m_fallbackProj->getProjectRootPath() != root)
|
||||||
|
LogModule.report(logvisor::Fatal,
|
||||||
|
FMT_STRING(_SYS_STR("hecl package can only process multiple items in the same project; ")
|
||||||
|
_SYS_STR("'{}' and '{}' are different projects")),
|
||||||
|
m_fallbackProj->getProjectRootPath().getAbsolutePath(),
|
||||||
|
root.getAbsolutePath());
|
||||||
|
|
||||||
|
FindSelectedItems({*m_useProj, subPath}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_useProj)
|
||||||
|
LogModule.report(logvisor::Fatal,
|
||||||
|
FMT_STRING("hecl package must be ran within a project directory or "
|
||||||
|
"provided a path within a project"));
|
||||||
|
|
||||||
|
/* Default case: recursive at root */
|
||||||
|
if (m_selectedItems.empty())
|
||||||
|
FindSelectedItems({*m_useProj, _SYS_STR("")}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Help(HelpOutput& help) {
|
||||||
|
help.secHead(_SYS_STR("NAME"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl-pack\n") _SYS_STR("hecl-package - Package objects within the project database\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("SYNOPSIS"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl package [--spec=<spec>] [<input-dir>]\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("DESCRIPTION"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("This command initiates a packaging pass on the project database. Packaging ")
|
||||||
|
_SYS_STR("is analogous to linking in software development. All objects necessary to ") _SYS_STR(
|
||||||
|
"generate a complete package are gathered, grouped, and indexed within a .upak file.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("OPTIONS"));
|
||||||
|
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking and generating the package. ")
|
||||||
|
_SYS_STR("This build of hecl supports the following values of <spec>:\n"));
|
||||||
|
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
|
||||||
|
if (!spec->m_factory)
|
||||||
|
continue;
|
||||||
|
help.wrap(_SYS_STR(" "));
|
||||||
|
help.wrapBold(spec->m_name.data());
|
||||||
|
help.wrap(_SYS_STR("\n"));
|
||||||
|
}
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("OPTIONS"));
|
||||||
|
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting package from. ")
|
||||||
|
_SYS_STR("If any dependent files fall outside this subdirectory, they will be implicitly ")
|
||||||
|
_SYS_STR("gathered and packaged.\n"));
|
||||||
|
help.endWrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemStringView toolName() const override { return _SYS_STR("package"sv); }
|
||||||
|
|
||||||
|
int run() override {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" GREEN BOLD "ABOUT TO PACKAGE:" NORMAL "\n")));
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("ABOUT TO PACKAGE:\n")));
|
||||||
|
|
||||||
|
for (auto& item : m_selectedItems)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" {}\n")), item.getRelativePath());
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (continuePrompt()) {
|
||||||
|
hecl::MultiProgressPrinter printer(true);
|
||||||
|
hecl::ClientProcess cp(&printer);
|
||||||
|
for (const hecl::ProjectPath& path : m_selectedItems) {
|
||||||
|
if (!m_useProj->packagePath(path, printer, m_fast, m_spec, &cp))
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to package {}")), path.getAbsolutePath());
|
||||||
|
}
|
||||||
|
cp.waitUntilComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancel() override { m_useProj->interruptCook(); }
|
||||||
|
};
|
|
@ -0,0 +1,131 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
class ToolSpec final : public ToolBase {
|
||||||
|
enum Mode { MLIST = 0, MENABLE, MDISABLE } mode = MLIST;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ToolSpec(const ToolPassInfo& info) : ToolBase(info) {
|
||||||
|
if (info.args.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!info.project)
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING("hecl spec must be ran within a project directory"));
|
||||||
|
|
||||||
|
const auto& specs = info.project->getDataSpecs();
|
||||||
|
hecl::SystemString firstArg = info.args.front();
|
||||||
|
hecl::ToLower(firstArg);
|
||||||
|
|
||||||
|
if (firstArg == _SYS_STR("enable"))
|
||||||
|
mode = MENABLE;
|
||||||
|
else if (firstArg == _SYS_STR("disable"))
|
||||||
|
mode = MDISABLE;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (info.args.size() < 2)
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING("Speclist argument required"));
|
||||||
|
|
||||||
|
auto it = info.args.begin();
|
||||||
|
++it;
|
||||||
|
for (; it != info.args.end(); ++it) {
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (auto& spec : specs) {
|
||||||
|
if (!it->compare(spec.spec.m_name)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}' is not found in the dataspec registry")), *it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Help(HelpOutput& help) {
|
||||||
|
help.secHead(_SYS_STR("NAME"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl-spec - Configure target data options\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("SYNOPSIS"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("hecl spec [enable|disable] [<specname>...]\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("DESCRIPTION"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(
|
||||||
|
_SYS_STR("This command configures the HECL project with the user's preferred target DataSpecs.\n\n")
|
||||||
|
_SYS_STR("Providing enable/disable argument will bulk-set the enable status of the provided spec(s)")
|
||||||
|
_SYS_STR("list. If enable/disable is not provided, a list of supported DataSpecs is printed.\n\n"));
|
||||||
|
help.endWrap();
|
||||||
|
|
||||||
|
help.secHead(_SYS_STR("OPTIONS"));
|
||||||
|
help.optionHead(_SYS_STR("<specname>..."), _SYS_STR("DataSpec name(s)"));
|
||||||
|
help.beginWrap();
|
||||||
|
help.wrap(_SYS_STR("Specifies platform-names to enable/disable"));
|
||||||
|
help.endWrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemStringView toolName() const override { return _SYS_STR("spec"sv); }
|
||||||
|
|
||||||
|
int run() override {
|
||||||
|
if (!m_info.project) {
|
||||||
|
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" BOLD CYAN "{}" NORMAL "\n")), spec->m_name);
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("{}\n")), spec->m_name);
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" {}\n")), spec->m_desc);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& specs = m_info.project->getDataSpecs();
|
||||||
|
if (mode == MLIST) {
|
||||||
|
for (auto& spec : specs) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" BOLD CYAN "{}" NORMAL "")), spec.spec.m_name);
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("{}")), spec.spec.m_name);
|
||||||
|
if (spec.active) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" " BOLD GREEN "[ENABLED]" NORMAL "")));
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" [ENABLED]")));
|
||||||
|
}
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\n {}\n")), spec.spec.m_desc);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<hecl::SystemString> opSpecs;
|
||||||
|
auto it = m_info.args.begin();
|
||||||
|
++it;
|
||||||
|
for (; it != m_info.args.end(); ++it) {
|
||||||
|
hecl::SystemString itName = *it;
|
||||||
|
hecl::ToLower(itName);
|
||||||
|
for (auto& spec : specs) {
|
||||||
|
hecl::SystemString compName(spec.spec.m_name);
|
||||||
|
hecl::ToLower(compName);
|
||||||
|
if (itName == compName) {
|
||||||
|
opSpecs.emplace_back(spec.spec.m_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opSpecs.size()) {
|
||||||
|
if (mode == MENABLE)
|
||||||
|
m_info.project->enableDataSpecs(opSpecs);
|
||||||
|
else if (mode == MDISABLE)
|
||||||
|
m_info.project->disableDataSpecs(opSpecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,378 @@
|
||||||
|
#if _WIN32
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#define WIN_PAUSE 0
|
||||||
|
#include <objbase.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <clocale>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <regex>
|
||||||
|
#include <list>
|
||||||
|
#include "hecl/Database.hpp"
|
||||||
|
#include "hecl/Blender/Connection.hpp"
|
||||||
|
#include "hecl/Runtime.hpp"
|
||||||
|
#include "logvisor/logvisor.hpp"
|
||||||
|
#include "../version.h"
|
||||||
|
|
||||||
|
logvisor::Module LogModule("hecl::Driver");
|
||||||
|
|
||||||
|
#include "ToolBase.hpp"
|
||||||
|
#include "ToolInit.hpp"
|
||||||
|
#include "ToolSpec.hpp"
|
||||||
|
#include "ToolExtract.hpp"
|
||||||
|
#include "ToolCook.hpp"
|
||||||
|
#include "ToolPackage.hpp"
|
||||||
|
#include "ToolImage.hpp"
|
||||||
|
#include "ToolInstallAddon.hpp"
|
||||||
|
#include "ToolHelp.hpp"
|
||||||
|
|
||||||
|
/* Static reference to dataspec additions
|
||||||
|
* (used by MSVC to definitively link DataSpecs) */
|
||||||
|
#include "DataSpecRegistry.hpp"
|
||||||
|
|
||||||
|
bool XTERM_COLOR = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define HECL_GIT 1234567
|
||||||
|
#define HECL_GIT_S "1234567"
|
||||||
|
#define HECL_BRANCH master
|
||||||
|
#define HECL_BRANCH_S "master"
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Main usage message */
|
||||||
|
static void printHelp(const hecl::SystemChar* pname) {
|
||||||
|
if (XTERM_COLOR)
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("" BOLD "HECL" NORMAL "")));
|
||||||
|
else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("HECL")));
|
||||||
|
#if HECL_HAS_NOD
|
||||||
|
#define TOOL_LIST "extract|init|cook|package|image|installaddon|help"
|
||||||
|
#else
|
||||||
|
#define TOOL_LIST "extract|init|cook|package|installaddon|help"
|
||||||
|
#endif
|
||||||
|
#if HECL_GIT
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" Commit " HECL_GIT_S " " HECL_BRANCH_S "\nUsage: {} " TOOL_LIST "\n")), pname);
|
||||||
|
#elif HECL_VER
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR(" Version " HECL_VER_S "\nUsage: {} " TOOL_LIST "\n")), pname);
|
||||||
|
#else
|
||||||
|
fmt::print(FMT_STRING(_SYS_STR("\nUsage: {} " TOOL_LIST "\n")), pname);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Regex patterns */
|
||||||
|
static const hecl::SystemRegex regOPEN(_SYS_STR("-o([^\"]*|\\S*)"), std::regex::ECMAScript | std::regex::optimize);
|
||||||
|
|
||||||
|
static ToolBase* ToolPtr = nullptr;
|
||||||
|
|
||||||
|
/* SIGINT will gracefully close blender connections and delete blends in progress */
|
||||||
|
static void SIGINTHandler(int sig) {
|
||||||
|
if (ToolPtr)
|
||||||
|
ToolPtr->cancel();
|
||||||
|
hecl::blender::Connection::Shutdown();
|
||||||
|
logvisor::KillProcessTree();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static logvisor::Module AthenaLog("Athena");
|
||||||
|
static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, fmt::string_view fmt,
|
||||||
|
fmt::format_args args) {
|
||||||
|
AthenaLog.vreport(logvisor::Level(level), fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::SystemString ExeDir;
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
static ToolPassInfo CreateInfo(int argc, const wchar_t** argv) {
|
||||||
|
#else
|
||||||
|
static ToolPassInfo CreateInfo(int argc, const char** argv) {
|
||||||
|
#endif
|
||||||
|
hecl::SystemChar cwdbuf[1024];
|
||||||
|
|
||||||
|
ToolPassInfo info;
|
||||||
|
info.pname = argv[0];
|
||||||
|
|
||||||
|
if (hecl::Getcwd(cwdbuf, static_cast<int>(std::size(cwdbuf)))) {
|
||||||
|
info.cwd = cwdbuf;
|
||||||
|
if (info.cwd.size() && info.cwd.back() != _SYS_STR('/') && info.cwd.back() != _SYS_STR('\\')) {
|
||||||
|
#if _WIN32
|
||||||
|
info.cwd += _SYS_STR('\\');
|
||||||
|
#else
|
||||||
|
info.cwd += _SYS_STR('/');
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hecl::PathRelative(argv[0])) {
|
||||||
|
ExeDir = hecl::SystemString(cwdbuf) + _SYS_STR('/');
|
||||||
|
}
|
||||||
|
hecl::SystemString Argv0(argv[0]);
|
||||||
|
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
|
||||||
|
if (lastIdx != hecl::SystemString::npos) {
|
||||||
|
ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Concatenate args */
|
||||||
|
std::vector<hecl::SystemString> args;
|
||||||
|
args.reserve(argc - 2);
|
||||||
|
for (int i = 2; i < argc; ++i) {
|
||||||
|
args.emplace_back(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args.empty()) {
|
||||||
|
/* Extract output argument */
|
||||||
|
for (auto it = args.cbegin(); it != args.cend();) {
|
||||||
|
const hecl::SystemString& arg = *it;
|
||||||
|
hecl::SystemRegexMatch oMatch;
|
||||||
|
|
||||||
|
if (std::regex_search(arg, oMatch, regOPEN)) {
|
||||||
|
const hecl::SystemString& token = oMatch[1].str();
|
||||||
|
|
||||||
|
if (token.size()) {
|
||||||
|
if (info.output.empty()) {
|
||||||
|
info.output = oMatch[1].str();
|
||||||
|
}
|
||||||
|
|
||||||
|
it = args.erase(it);
|
||||||
|
} else {
|
||||||
|
it = args.erase(it);
|
||||||
|
|
||||||
|
if (it == args.end()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.output.empty()) {
|
||||||
|
info.output = *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = args.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate flags */
|
||||||
|
bool threadArg = false;
|
||||||
|
for (auto it = args.cbegin(); it != args.cend();) {
|
||||||
|
const hecl::SystemString& arg = *it;
|
||||||
|
if (threadArg) {
|
||||||
|
threadArg = false;
|
||||||
|
hecl::CpuCountOverride = int(hecl::StrToUl(arg.c_str(), nullptr, 0));
|
||||||
|
it = args.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg.size() < 2 || arg[0] != _SYS_STR('-') || arg[1] == _SYS_STR('-')) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto chit = arg.cbegin() + 1; chit != arg.cend(); ++chit) {
|
||||||
|
if (*chit == _SYS_STR('v'))
|
||||||
|
++info.verbosityLevel;
|
||||||
|
else if (*chit == _SYS_STR('f'))
|
||||||
|
info.force = true;
|
||||||
|
else if (*chit == _SYS_STR('y'))
|
||||||
|
info.yes = true;
|
||||||
|
else if (*chit == _SYS_STR('g'))
|
||||||
|
info.gui = true;
|
||||||
|
else if (*chit == _SYS_STR('j')) {
|
||||||
|
++chit;
|
||||||
|
if (*chit)
|
||||||
|
hecl::CpuCountOverride = int(hecl::StrToUl(&*chit, nullptr, 0));
|
||||||
|
else
|
||||||
|
threadArg = true;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
info.flags.push_back(*chit);
|
||||||
|
}
|
||||||
|
|
||||||
|
it = args.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gather remaining args */
|
||||||
|
info.args.reserve(args.size());
|
||||||
|
for (const hecl::SystemString& arg : args)
|
||||||
|
info.args.push_back(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<hecl::Database::Project> FindProject(hecl::SystemStringView cwd) {
|
||||||
|
const hecl::ProjectRootPath rootPath = hecl::SearchForProject(cwd);
|
||||||
|
if (!rootPath) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t ErrorRef = logvisor::ErrorCount;
|
||||||
|
auto newProj = std::make_unique<hecl::Database::Project>(rootPath);
|
||||||
|
if (logvisor::ErrorCount > ErrorRef) {
|
||||||
|
#if WIN_PAUSE
|
||||||
|
system("PAUSE");
|
||||||
|
#endif
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<ToolBase> MakeSelectedTool(hecl::SystemString toolName, ToolPassInfo& info) {
|
||||||
|
hecl::SystemString toolNameLower = toolName;
|
||||||
|
hecl::ToLower(toolNameLower);
|
||||||
|
|
||||||
|
if (toolNameLower == _SYS_STR("init")) {
|
||||||
|
return std::make_unique<ToolInit>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolNameLower == _SYS_STR("spec")) {
|
||||||
|
return std::make_unique<ToolSpec>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolNameLower == _SYS_STR("extract")) {
|
||||||
|
return std::make_unique<ToolExtract>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolNameLower == _SYS_STR("cook")) {
|
||||||
|
return std::make_unique<ToolCook>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolNameLower == _SYS_STR("package") || toolNameLower == _SYS_STR("pack")) {
|
||||||
|
return std::make_unique<ToolPackage>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HECL_HAS_NOD
|
||||||
|
if (toolNameLower == _SYS_STR("image")) {
|
||||||
|
return std::make_unique<ToolImage>(info);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (toolNameLower == _SYS_STR("installaddon")) {
|
||||||
|
return std::make_unique<ToolInstallAddon>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolNameLower == _SYS_STR("help")) {
|
||||||
|
return std::make_unique<ToolHelp>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fp = hecl::FopenUnique(toolName.c_str(), _SYS_STR("rb"));
|
||||||
|
if (fp == nullptr) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unrecognized tool '{}'")), toolNameLower);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
fp.reset();
|
||||||
|
|
||||||
|
/* Shortcut-case: implicit extract */
|
||||||
|
info.args.insert(info.args.begin(), std::move(toolName));
|
||||||
|
return std::make_unique<ToolExtract>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
int wmain(int argc, const wchar_t** argv)
|
||||||
|
#else
|
||||||
|
/* SIGWINCH should do nothing */
|
||||||
|
static void SIGWINCHHandler(int sig) {}
|
||||||
|
int main(int argc, const char** argv)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) {
|
||||||
|
fmt::print(FMT_STRING("{}\n"), URDE_DLPACKAGE);
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
||||||
|
#else
|
||||||
|
std::setlocale(LC_ALL, "en-US.UTF-8");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Xterm check */
|
||||||
|
#if _WIN32
|
||||||
|
const char* conemuANSI = getenv("ConEmuANSI");
|
||||||
|
if (conemuANSI && !strcmp(conemuANSI, "ON"))
|
||||||
|
XTERM_COLOR = true;
|
||||||
|
#else
|
||||||
|
const char* term = getenv("TERM");
|
||||||
|
if (term && !strncmp(term, "xterm", 5))
|
||||||
|
XTERM_COLOR = true;
|
||||||
|
signal(SIGWINCH, SIGWINCHHandler);
|
||||||
|
#endif
|
||||||
|
signal(SIGINT, SIGINTHandler);
|
||||||
|
|
||||||
|
logvisor::RegisterStandardExceptions();
|
||||||
|
logvisor::RegisterConsoleLogger();
|
||||||
|
atSetExceptionHandler(AthenaExc);
|
||||||
|
|
||||||
|
#if SENTRY_ENABLED
|
||||||
|
hecl::Runtime::FileStoreManager fileMgr{_SYS_STR("sentry-native-hecl")};
|
||||||
|
hecl::SystemUTF8Conv cacheDir{fileMgr.getStoreRoot()};
|
||||||
|
logvisor::RegisterSentry("hecl", URDE_WC_DESCRIBE, cacheDir.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Basic usage check */
|
||||||
|
if (argc == 1) {
|
||||||
|
printHelp(argv[0]);
|
||||||
|
#if WIN_PAUSE
|
||||||
|
system("PAUSE");
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
} else if (argc == 0) {
|
||||||
|
printHelp(_SYS_STR("hecl"));
|
||||||
|
#if WIN_PAUSE
|
||||||
|
system("PAUSE");
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare DataSpecs */
|
||||||
|
HECLRegisterDataSpecs();
|
||||||
|
|
||||||
|
/* Assemble common tool pass info */
|
||||||
|
ToolPassInfo info = CreateInfo(argc, argv);
|
||||||
|
|
||||||
|
/* Attempt to find hecl project */
|
||||||
|
auto project = FindProject(info.cwd);
|
||||||
|
if (project != nullptr) {
|
||||||
|
info.project = project.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Construct selected tool */
|
||||||
|
const size_t MakeToolErrorRef = logvisor::ErrorCount;
|
||||||
|
auto tool = MakeSelectedTool(argv[1], info);
|
||||||
|
if (logvisor::ErrorCount > MakeToolErrorRef) {
|
||||||
|
#if WIN_PAUSE
|
||||||
|
system("PAUSE");
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (info.verbosityLevel) {
|
||||||
|
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Constructed tool '{}' {}\n")), tool->toolName(),
|
||||||
|
info.verbosityLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run tool */
|
||||||
|
const size_t RunToolErrorRef = logvisor::ErrorCount;
|
||||||
|
ToolPtr = tool.get();
|
||||||
|
const int retval = tool->run();
|
||||||
|
ToolPtr = nullptr;
|
||||||
|
if (logvisor::ErrorCount > RunToolErrorRef) {
|
||||||
|
hecl::blender::Connection::Shutdown();
|
||||||
|
#if WIN_PAUSE
|
||||||
|
system("PAUSE");
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::blender::Connection::Shutdown();
|
||||||
|
#if WIN_PAUSE
|
||||||
|
system("PAUSE");
|
||||||
|
#endif
|
||||||
|
return retval;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
add_subdirectory(boo)
|
||||||
|
add_subdirectory(libSquish)
|
||||||
|
add_subdirectory(athena)
|
||||||
|
add_subdirectory(libpng)
|
||||||
|
add_subdirectory(libjpeg-turbo)
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit fc3e9a51d2c8cf449c05b9eeb1da251ea389ea53
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 500775f3db012f7516ff081cb0f18d4a266299f1
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit fd5e6f4fe294bcdde24e591fcef37dd5d740e060
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 3d2da99c69d000c6351f6d2390b694f800e5ef4d
|
|
@ -0,0 +1,115 @@
|
||||||
|
Libpng 1.6.19 - November 12, 2015
|
||||||
|
|
||||||
|
This is a public release of libpng, intended for use in production codes.
|
||||||
|
|
||||||
|
Files available for download:
|
||||||
|
|
||||||
|
Source files with LF line endings (for Unix/Linux) and with a
|
||||||
|
"configure" script
|
||||||
|
|
||||||
|
libpng-1.6.19.tar.xz (LZMA-compressed, recommended)
|
||||||
|
libpng-1.6.19.tar.gz
|
||||||
|
|
||||||
|
Source files with CRLF line endings (for Windows), without the
|
||||||
|
"configure" script
|
||||||
|
|
||||||
|
lpng1619.7z (LZMA-compressed, recommended)
|
||||||
|
lpng1619.zip
|
||||||
|
|
||||||
|
Other information:
|
||||||
|
|
||||||
|
libpng-1.6.19-README.txt
|
||||||
|
libpng-1.6.19-LICENSE.txt
|
||||||
|
libpng-1.6.19-*.asc (armored detached GPG signatures)
|
||||||
|
|
||||||
|
Changes since the last public release (1.6.18):
|
||||||
|
|
||||||
|
Updated obsolete information about the simplified API macros in the
|
||||||
|
manual pages (Bug report by Arc Riley).
|
||||||
|
Avoid potentially dereferencing NULL info_ptr in png_info_init_3().
|
||||||
|
Rearranged png.h to put the major sections in the same order as
|
||||||
|
in libpng17.
|
||||||
|
Eliminated unused PNG_COST_SHIFT, PNG_WEIGHT_SHIFT, PNG_COST_FACTOR, and
|
||||||
|
PNG_WEIGHT_FACTOR macros.
|
||||||
|
Suppressed some warnings from the Borland C++ 5.5.1/5.82 compiler
|
||||||
|
(Bug report by Viktor Szakats). Several warnings remain and are
|
||||||
|
unavoidable, where we test for overflow.
|
||||||
|
Fixed potential leak of png_pixels in contrib/pngminus/pnm2png.c
|
||||||
|
Fixed uninitialized variable in contrib/gregbook/rpng2-x.c
|
||||||
|
Moved config.h.in~ from the "libpng_autotools_files" list to the
|
||||||
|
"libpng_autotools_extra" list in autogen.sh because it was causing a
|
||||||
|
false positive for missing files (bug report by Robert C. Seacord).
|
||||||
|
Removed unreachable "break" statements in png.c, pngread.c, and pngrtran.c
|
||||||
|
to suppress clang warnings (Bug report by Viktor Szakats).
|
||||||
|
Fixed some bad links in the man page.
|
||||||
|
Changed "n bit" to "n-bit" in comments.
|
||||||
|
Added signed/unsigned 16-bit safety net. This removes the dubious
|
||||||
|
0x8000 flag definitions on 16-bit systems. They aren't supported
|
||||||
|
yet the defs *probably* work, however it seems much safer to do this
|
||||||
|
and be advised if anyone, contrary to advice, is building libpng 1.6
|
||||||
|
on a 16-bit system. It also adds back various switch default clauses
|
||||||
|
for GCC; GCC errors out if they are not present (with an appropriately
|
||||||
|
high level of warnings).
|
||||||
|
Safely convert num_bytes to a png_byte in png_set_sig_bytes() (Robert
|
||||||
|
Seacord).
|
||||||
|
Fixed the recently reported 1's complement security issue by replacing
|
||||||
|
the value that is illegal in the PNG spec, in both signed and unsigned
|
||||||
|
values, with 0. Illegal unsigned values (anything greater than or equal
|
||||||
|
to 0x80000000) can still pass through, but since these are not illegal
|
||||||
|
in ANSI-C (unlike 0x80000000 in the signed case) the checking that
|
||||||
|
occurs later can catch them (John Bowler).
|
||||||
|
Fixed png_save_int_32 when int is not 2's complement (John Bowler).
|
||||||
|
Updated libpng16 with all the recent test changes from libpng17,
|
||||||
|
including changes to pngvalid.c to ensure that the original,
|
||||||
|
distributed, version of contrib/visupng/cexcept.h can be used
|
||||||
|
(John Bowler).
|
||||||
|
pngvalid contains the correction to the use of SAVE/STORE_
|
||||||
|
UNKNOWN_CHUNKS; a bug revealed by changes in libpng 1.7. More
|
||||||
|
tests contain the --strict option to detect warnings and the
|
||||||
|
pngvalid-standard test has been corrected so that it does not
|
||||||
|
turn on progressive-read. There is a separate test which does
|
||||||
|
that. (John Bowler)
|
||||||
|
Also made some signed/unsigned fixes.
|
||||||
|
Make pngstest error limits version specific. Splitting the machine
|
||||||
|
generated error structs out to a file allows the values to be updated
|
||||||
|
without changing pngstest.c itself. Since libpng 1.6 and 1.7 have
|
||||||
|
slightly different error limits this simplifies maintenance. The
|
||||||
|
makepngs.sh script has also been updated to more accurately reflect
|
||||||
|
current problems in libpng 1.7 (John Bowler).
|
||||||
|
Incorporated new test PNG files into make check. tests/pngstest-*
|
||||||
|
are changed so that the new test files are divided into 8 groups by
|
||||||
|
gamma and alpha channel. These tests have considerably better code
|
||||||
|
and pixel-value coverage than contrib/pngsuite; however,coverage is
|
||||||
|
still incomplete (John Bowler).
|
||||||
|
Removed the '--strict' in 1.6 because of the double-gamma-correction
|
||||||
|
warning, updated pngstest-errors.h for the errors detected with the
|
||||||
|
new contrib/testspngs PNG test files (John Bowler).
|
||||||
|
Worked around rgb-to-gray issues in libpng 1.6. The previous
|
||||||
|
attempts to ignore the errors in the code aren't quite enough to
|
||||||
|
deal with the 'channel selection' encoding added to libpng 1.7; abort.
|
||||||
|
Fixed 'pow' macros in pngvalid.c. It is legal for 'pow' to be a
|
||||||
|
macro, therefore the argument list cannot contain preprocessing
|
||||||
|
directives. Make sure pow is a function where this happens. This is
|
||||||
|
a minimal safe fix, the issue only arises in non-performance-critical
|
||||||
|
code (bug report by Curtis Leach, fix by John Bowler).
|
||||||
|
Added sPLT support to pngtest.c
|
||||||
|
Prevent setting or writing over-length PLTE chunk (Cosmin Truta).
|
||||||
|
Silently truncate over-length PLTE chunk while reading.
|
||||||
|
Libpng incorrectly calculated the output rowbytes when the application
|
||||||
|
decreased either the number of channels or the bit depth (or both) in
|
||||||
|
a user transform. This was safe; libpng overallocated buffer space
|
||||||
|
(potentially by quite a lot; up to 4 times the amount required) but,
|
||||||
|
from 1.5.4 on, resulted in a png_error (John Bowler).
|
||||||
|
Fixed some inconsequential cut-and-paste typos in png_set_cHRM_XYZ_fixed().
|
||||||
|
Clarified COPYRIGHT information to state explicitly that versions
|
||||||
|
are derived from previous versions.
|
||||||
|
Removed much of the long list of previous versions from png.h and
|
||||||
|
libpng.3.
|
||||||
|
|
||||||
|
Send comments/corrections/commendations to png-mng-implement at lists.sf.net
|
||||||
|
(subscription required; visit
|
||||||
|
https://lists.sourceforge.net/lists/listinfo/png-mng-implement
|
||||||
|
to subscribe)
|
||||||
|
or to glennrp at users.sourceforge.net
|
||||||
|
|
||||||
|
Glenn R-P
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,53 @@
|
||||||
|
if(NOT WIN32 AND NOT APPLE AND NOT NX) # remove WIN32 when specter/freetype is gone
|
||||||
|
find_library(PNG_LIB NAMES png libpng)
|
||||||
|
endif()
|
||||||
|
if(NOT PNG_LIB)
|
||||||
|
message(STATUS "Using HECL's built-in libpng")
|
||||||
|
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm(64)?")
|
||||||
|
set(INTRINSICS
|
||||||
|
arm/arm_init.c
|
||||||
|
arm/filter_neon.S
|
||||||
|
arm/filter_neon_intrinsics.c
|
||||||
|
arm/palette_neon_intrinsics.c)
|
||||||
|
elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL x86_64)
|
||||||
|
set(INTRINSICS
|
||||||
|
intel/filter_sse2_intrinsics.c
|
||||||
|
intel/intel_init.c)
|
||||||
|
endif()
|
||||||
|
add_library(png
|
||||||
|
png.h
|
||||||
|
pngconf.h
|
||||||
|
pngdebug.h
|
||||||
|
pnginfo.h
|
||||||
|
pngpriv.h
|
||||||
|
pngstruct.h
|
||||||
|
pnglibconf.h
|
||||||
|
|
||||||
|
png.c
|
||||||
|
pngerror.c
|
||||||
|
pngget.c
|
||||||
|
pngmem.c
|
||||||
|
pngpread.c
|
||||||
|
pngread.c
|
||||||
|
pngrio.c
|
||||||
|
pngrtran.c
|
||||||
|
pngrutil.c
|
||||||
|
pngset.c
|
||||||
|
pngtrans.c
|
||||||
|
pngwio.c
|
||||||
|
pngwrite.c
|
||||||
|
pngwtran.c
|
||||||
|
pngwutil.c
|
||||||
|
${INTRINSICS})
|
||||||
|
if(APPLE)
|
||||||
|
target_compile_options(png PRIVATE -Wno-implicit-fallthrough)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(png PUBLIC ${ZLIB_LIBRARIES})
|
||||||
|
target_include_directories(png PUBLIC ${ZLIB_INCLUDE_DIR})
|
||||||
|
set(PNG_LIBRARIES png CACHE PATH "PNG libraries" FORCE)
|
||||||
|
set(PNG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "PNG include path" FORCE)
|
||||||
|
else()
|
||||||
|
set(PNG_LIBRARIES ${PNG_LIB} CACHE PATH "PNG libraries" FORCE)
|
||||||
|
find_path(PNG_INCLUDE_DIR png.h)
|
||||||
|
set(PNG_INCLUDE_DIR ${PNG_INCLUDE_DIR} CACHE PATH "PNG include path" FORCE)
|
||||||
|
endif()
|
|
@ -0,0 +1,183 @@
|
||||||
|
README for libpng version 1.6.37 - April 14, 2019
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
See the note about version numbers near the top of png.h.
|
||||||
|
See INSTALL for instructions on how to install libpng.
|
||||||
|
|
||||||
|
Libpng comes in several distribution formats. Get libpng-*.tar.gz or
|
||||||
|
libpng-*.tar.xz or if you want UNIX-style line endings in the text
|
||||||
|
files, or lpng*.7z or lpng*.zip if you want DOS-style line endings.
|
||||||
|
|
||||||
|
Version 0.89 was the first official release of libpng. Don't let the
|
||||||
|
fact that it's the first release fool you. The libpng library has been
|
||||||
|
in extensive use and testing since mid-1995. By late 1997 it had
|
||||||
|
finally gotten to the stage where there hadn't been significant
|
||||||
|
changes to the API in some time, and people have a bad feeling about
|
||||||
|
libraries with versions < 1.0. Version 1.0.0 was released in
|
||||||
|
March 1998.
|
||||||
|
|
||||||
|
****
|
||||||
|
Note that some of the changes to the png_info structure render this
|
||||||
|
version of the library binary incompatible with libpng-0.89 or
|
||||||
|
earlier versions if you are using a shared library. The type of the
|
||||||
|
"filler" parameter for png_set_filler() has changed from png_byte to
|
||||||
|
png_uint_32, which will affect shared-library applications that use
|
||||||
|
this function.
|
||||||
|
|
||||||
|
To avoid problems with changes to the internals of the png info_struct,
|
||||||
|
new APIs have been made available in 0.95 to avoid direct application
|
||||||
|
access to info_ptr. These functions are the png_set_<chunk> and
|
||||||
|
png_get_<chunk> functions. These functions should be used when
|
||||||
|
accessing/storing the info_struct data, rather than manipulating it
|
||||||
|
directly, to avoid such problems in the future.
|
||||||
|
|
||||||
|
It is important to note that the APIs did not make current programs
|
||||||
|
that access the info struct directly incompatible with the new
|
||||||
|
library, through libpng-1.2.x. In libpng-1.4.x, which was meant to
|
||||||
|
be a transitional release, members of the png_struct and the
|
||||||
|
info_struct can still be accessed, but the compiler will issue a
|
||||||
|
warning about deprecated usage. Since libpng-1.5.0, direct access
|
||||||
|
to these structs is not allowed, and the definitions of the structs
|
||||||
|
reside in private pngstruct.h and pnginfo.h header files that are not
|
||||||
|
accessible to applications. It is strongly suggested that new
|
||||||
|
programs use the new APIs (as shown in example.c and pngtest.c), and
|
||||||
|
older programs be converted to the new format, to facilitate upgrades
|
||||||
|
in the future.
|
||||||
|
****
|
||||||
|
|
||||||
|
Additions since 0.90 include the ability to compile libpng as a
|
||||||
|
Windows DLL, and new APIs for accessing data in the info struct.
|
||||||
|
Experimental functions include the ability to set weighting and cost
|
||||||
|
factors for row filter selection, direct reads of integers from buffers
|
||||||
|
on big-endian processors that support misaligned data access, faster
|
||||||
|
methods of doing alpha composition, and more accurate 16->8 bit color
|
||||||
|
conversion.
|
||||||
|
|
||||||
|
The additions since 0.89 include the ability to read from a PNG stream
|
||||||
|
which has had some (or all) of the signature bytes read by the calling
|
||||||
|
application. This also allows the reading of embedded PNG streams that
|
||||||
|
do not have the PNG file signature. As well, it is now possible to set
|
||||||
|
the library action on the detection of chunk CRC errors. It is possible
|
||||||
|
to set different actions based on whether the CRC error occurred in a
|
||||||
|
critical or an ancillary chunk.
|
||||||
|
|
||||||
|
For a detailed description on using libpng, read libpng-manual.txt.
|
||||||
|
For examples of libpng in a program, see example.c and pngtest.c. For
|
||||||
|
usage information and restrictions (what little they are) on libpng,
|
||||||
|
see png.h. For a description on using zlib (the compression library
|
||||||
|
used by libpng) and zlib's restrictions, see zlib.h
|
||||||
|
|
||||||
|
I have included a general makefile, as well as several machine and
|
||||||
|
compiler specific ones, but you may have to modify one for your own
|
||||||
|
needs.
|
||||||
|
|
||||||
|
You should use zlib 1.0.4 or later to run this, but it MAY work with
|
||||||
|
versions as old as zlib 0.95. Even so, there are bugs in older zlib
|
||||||
|
versions which can cause the output of invalid compression streams for
|
||||||
|
some images.
|
||||||
|
|
||||||
|
You should also note that zlib is a compression library that is useful
|
||||||
|
for more things than just PNG files. You can use zlib as a drop-in
|
||||||
|
replacement for fread() and fwrite(), if you are so inclined.
|
||||||
|
|
||||||
|
zlib should be available at the same place that libpng is, or at
|
||||||
|
https://zlib.net.
|
||||||
|
|
||||||
|
You may also want a copy of the PNG specification. It is available
|
||||||
|
as an RFC, a W3C Recommendation, and an ISO/IEC Standard. You can find
|
||||||
|
these at http://www.libpng.org/pub/png/pngdocs.html .
|
||||||
|
|
||||||
|
This code is currently being archived at libpng.sourceforge.io in the
|
||||||
|
[DOWNLOAD] area, and at http://libpng.download/src .
|
||||||
|
|
||||||
|
This release, based in a large way on Glenn's, Guy's and Andreas'
|
||||||
|
earlier work, was created and will be supported by myself and the PNG
|
||||||
|
development group.
|
||||||
|
|
||||||
|
Send comments/corrections/commendations to png-mng-implement at
|
||||||
|
lists.sourceforge.net (subscription required; visit
|
||||||
|
https://lists.sourceforge.net/lists/listinfo/png-mng-implement
|
||||||
|
to subscribe).
|
||||||
|
|
||||||
|
Send general questions about the PNG specification to png-mng-misc
|
||||||
|
at lists.sourceforge.net (subscription required; visit
|
||||||
|
https://lists.sourceforge.net/lists/listinfo/png-mng-misc to
|
||||||
|
subscribe).
|
||||||
|
|
||||||
|
Files in this distribution:
|
||||||
|
|
||||||
|
ANNOUNCE => Announcement of this version, with recent changes
|
||||||
|
AUTHORS => List of contributing authors
|
||||||
|
CHANGES => Description of changes between libpng versions
|
||||||
|
KNOWNBUG => List of known bugs and deficiencies
|
||||||
|
LICENSE => License to use and redistribute libpng
|
||||||
|
README => This file
|
||||||
|
TODO => Things not implemented in the current library
|
||||||
|
TRADEMARK => Trademark information
|
||||||
|
example.c => Example code for using libpng functions
|
||||||
|
libpng.3 => manual page for libpng (includes libpng-manual.txt)
|
||||||
|
libpng-manual.txt => Description of libpng and its functions
|
||||||
|
libpngpf.3 => manual page for libpng's private functions
|
||||||
|
png.5 => manual page for the PNG format
|
||||||
|
png.c => Basic interface functions common to library
|
||||||
|
png.h => Library function and interface declarations (public)
|
||||||
|
pngpriv.h => Library function and interface declarations (private)
|
||||||
|
pngconf.h => System specific library configuration (public)
|
||||||
|
pngstruct.h => png_struct declaration (private)
|
||||||
|
pnginfo.h => png_info struct declaration (private)
|
||||||
|
pngdebug.h => debugging macros (private)
|
||||||
|
pngerror.c => Error/warning message I/O functions
|
||||||
|
pngget.c => Functions for retrieving info from struct
|
||||||
|
pngmem.c => Memory handling functions
|
||||||
|
pngbar.png => PNG logo, 88x31
|
||||||
|
pngnow.png => PNG logo, 98x31
|
||||||
|
pngpread.c => Progressive reading functions
|
||||||
|
pngread.c => Read data/helper high-level functions
|
||||||
|
pngrio.c => Lowest-level data read I/O functions
|
||||||
|
pngrtran.c => Read data transformation functions
|
||||||
|
pngrutil.c => Read data utility functions
|
||||||
|
pngset.c => Functions for storing data into the info_struct
|
||||||
|
pngtest.c => Library test program
|
||||||
|
pngtest.png => Library test sample image
|
||||||
|
pngtrans.c => Common data transformation functions
|
||||||
|
pngwio.c => Lowest-level write I/O functions
|
||||||
|
pngwrite.c => High-level write functions
|
||||||
|
pngwtran.c => Write data transformations
|
||||||
|
pngwutil.c => Write utility functions
|
||||||
|
arm => Contains optimized code for the ARM platform
|
||||||
|
powerpc => Contains optimized code for the PowerPC platform
|
||||||
|
contrib => Contributions
|
||||||
|
arm-neon => Optimized code for ARM-NEON platform
|
||||||
|
powerpc-vsx => Optimized code for POWERPC-VSX platform
|
||||||
|
examples => Example programs
|
||||||
|
gregbook => source code for PNG reading and writing, from
|
||||||
|
Greg Roelofs' "PNG: The Definitive Guide",
|
||||||
|
O'Reilly, 1999
|
||||||
|
libtests => Test programs
|
||||||
|
mips-msa => Optimized code for MIPS-MSA platform
|
||||||
|
pngminim => Minimal decoder, encoder, and progressive decoder
|
||||||
|
programs demonstrating use of pngusr.dfa
|
||||||
|
pngminus => Simple pnm2png and png2pnm programs
|
||||||
|
pngsuite => Test images
|
||||||
|
testpngs
|
||||||
|
tools => Various tools
|
||||||
|
visupng => Contains a MSVC workspace for VisualPng
|
||||||
|
intel => Optimized code for INTEL-SSE2 platform
|
||||||
|
mips => Optimized code for MIPS platform
|
||||||
|
projects => Contains project files and workspaces for
|
||||||
|
building a DLL
|
||||||
|
owatcom => Contains a WATCOM project for building libpng
|
||||||
|
visualc71 => Contains a Microsoft Visual C++ (MSVC)
|
||||||
|
workspace for building libpng and zlib
|
||||||
|
vstudio => Contains a Microsoft Visual C++ (MSVC)
|
||||||
|
workspace for building libpng and zlib
|
||||||
|
scripts => Directory containing scripts for building libpng:
|
||||||
|
(see scripts/README.txt for the list of scripts)
|
||||||
|
|
||||||
|
Good luck, and happy coding!
|
||||||
|
|
||||||
|
* Cosmin Truta (current maintainer, since 2018)
|
||||||
|
* Glenn Randers-Pehrson (former maintainer, 1998-2018)
|
||||||
|
* Andreas Eric Dilger (former maintainer, 1996-1997)
|
||||||
|
* Guy Eric Schalnat (original author and former maintainer, 1995-1996)
|
||||||
|
(formerly of Group 42, Inc.)
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
TODO - list of things to do for libpng:
|
||||||
|
|
||||||
|
Final bug fixes.
|
||||||
|
Better C++ wrapper/full C++ implementation?
|
||||||
|
Fix problem with C++ and EXTERN "C".
|
||||||
|
cHRM transformation.
|
||||||
|
Remove setjmp/longjmp usage in favor of returning error codes.
|
||||||
|
Palette creation.
|
||||||
|
Add "grayscale->palette" transformation and "palette->grayscale" detection.
|
||||||
|
Improved dithering.
|
||||||
|
Multi-lingual error and warning message support.
|
||||||
|
Complete sRGB transformation (presently it simply uses gamma=0.45455).
|
||||||
|
Make profile checking optional via a png_set_something() call.
|
||||||
|
Man pages for function calls.
|
||||||
|
Better documentation.
|
||||||
|
Better filter selection
|
||||||
|
(counting huffman bits/precompression? filter inertia? filter costs?).
|
||||||
|
Histogram creation.
|
||||||
|
Text conversion between different code pages (Latin-1 -> Mac and DOS).
|
||||||
|
Avoid building gamma tables whenever possible.
|
||||||
|
Use greater precision when changing to linear gamma for compositing against
|
||||||
|
background and doing rgb-to-gray transformation.
|
||||||
|
Investigate pre-incremented loop counters and other loop constructions.
|
||||||
|
Add interpolated method of handling interlacing.
|
||||||
|
Switch to the simpler zlib (zlib/libpng) license if legally possible.
|
||||||
|
Extend pngvalid.c to validate more of the libpng transformations.
|
||||||
|
|
||||||
|
*/
|
|
@ -0,0 +1,136 @@
|
||||||
|
|
||||||
|
/* arm_init.c - NEON optimised filter functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 2014,2016 Glenn Randers-Pehrson
|
||||||
|
* Written by Mans Rullgard, 2011.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are
|
||||||
|
* called.
|
||||||
|
*/
|
||||||
|
#define _POSIX_SOURCE 1
|
||||||
|
|
||||||
|
#include "../pngpriv.h"
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
|
||||||
|
#if PNG_ARM_NEON_OPT > 0
|
||||||
|
#ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */
|
||||||
|
/* WARNING: it is strongly recommended that you do not build libpng with
|
||||||
|
* run-time checks for CPU features if at all possible. In the case of the ARM
|
||||||
|
* NEON instructions there is no processor-specific way of detecting the
|
||||||
|
* presence of the required support, therefore run-time detection is extremely
|
||||||
|
* OS specific.
|
||||||
|
*
|
||||||
|
* You may set the macro PNG_ARM_NEON_FILE to the file name of file containing
|
||||||
|
* a fragment of C source code which defines the png_have_neon function. There
|
||||||
|
* are a number of implementations in contrib/arm-neon, but the only one that
|
||||||
|
* has partial support is contrib/arm-neon/linux.c - a generic Linux
|
||||||
|
* implementation which reads /proc/cpufino.
|
||||||
|
*/
|
||||||
|
#ifndef PNG_ARM_NEON_FILE
|
||||||
|
# ifdef __linux__
|
||||||
|
# define PNG_ARM_NEON_FILE "contrib/arm-neon/linux.c"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_ARM_NEON_FILE
|
||||||
|
|
||||||
|
#include <signal.h> /* for sig_atomic_t */
|
||||||
|
static int png_have_neon(png_structp png_ptr);
|
||||||
|
#include PNG_ARM_NEON_FILE
|
||||||
|
|
||||||
|
#else /* PNG_ARM_NEON_FILE */
|
||||||
|
# error "PNG_ARM_NEON_FILE undefined: no support for run-time ARM NEON checks"
|
||||||
|
#endif /* PNG_ARM_NEON_FILE */
|
||||||
|
#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */
|
||||||
|
|
||||||
|
#ifndef PNG_ALIGNED_MEMORY_SUPPORTED
|
||||||
|
# error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
png_init_filter_functions_neon(png_structp pp, unsigned int bpp)
|
||||||
|
{
|
||||||
|
/* The switch statement is compiled in for ARM_NEON_API, the call to
|
||||||
|
* png_have_neon is compiled in for ARM_NEON_CHECK. If both are defined
|
||||||
|
* the check is only performed if the API has not set the NEON option on
|
||||||
|
* or off explicitly. In this case the check controls what happens.
|
||||||
|
*
|
||||||
|
* If the CHECK is not compiled in and the option is UNSET the behavior prior
|
||||||
|
* to 1.6.7 was to use the NEON code - this was a bug caused by having the
|
||||||
|
* wrong order of the 'ON' and 'default' cases. UNSET now defaults to OFF,
|
||||||
|
* as documented in png.h
|
||||||
|
*/
|
||||||
|
png_debug(1, "in png_init_filter_functions_neon");
|
||||||
|
#ifdef PNG_ARM_NEON_API_SUPPORTED
|
||||||
|
switch ((pp->options >> PNG_ARM_NEON) & 3)
|
||||||
|
{
|
||||||
|
case PNG_OPTION_UNSET:
|
||||||
|
/* Allow the run-time check to execute if it has been enabled -
|
||||||
|
* thus both API and CHECK can be turned on. If it isn't supported
|
||||||
|
* this case will fall through to the 'default' below, which just
|
||||||
|
* returns.
|
||||||
|
*/
|
||||||
|
#endif /* PNG_ARM_NEON_API_SUPPORTED */
|
||||||
|
#ifdef PNG_ARM_NEON_CHECK_SUPPORTED
|
||||||
|
{
|
||||||
|
static volatile sig_atomic_t no_neon = -1; /* not checked */
|
||||||
|
|
||||||
|
if (no_neon < 0)
|
||||||
|
no_neon = !png_have_neon(pp);
|
||||||
|
|
||||||
|
if (no_neon)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef PNG_ARM_NEON_API_SUPPORTED
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */
|
||||||
|
|
||||||
|
#ifdef PNG_ARM_NEON_API_SUPPORTED
|
||||||
|
default: /* OFF or INVALID */
|
||||||
|
return;
|
||||||
|
|
||||||
|
case PNG_OPTION_ON:
|
||||||
|
/* Option turned on */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* IMPORTANT: any new external functions used here must be declared using
|
||||||
|
* PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the
|
||||||
|
* 'prefix' option to configure works:
|
||||||
|
*
|
||||||
|
* ./configure --with-libpng-prefix=foobar_
|
||||||
|
*
|
||||||
|
* Verify you have got this right by running the above command, doing a build
|
||||||
|
* and examining pngprefix.h; it must contain a #define for every external
|
||||||
|
* function you add. (Notice that this happens automatically for the
|
||||||
|
* initialization function.)
|
||||||
|
*/
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_neon;
|
||||||
|
|
||||||
|
if (bpp == 3)
|
||||||
|
{
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_neon;
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_neon;
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
|
||||||
|
png_read_filter_row_paeth3_neon;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (bpp == 4)
|
||||||
|
{
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_neon;
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_neon;
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
|
||||||
|
png_read_filter_row_paeth4_neon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* PNG_ARM_NEON_OPT > 0 */
|
||||||
|
#endif /* READ */
|
|
@ -0,0 +1,253 @@
|
||||||
|
|
||||||
|
/* filter_neon.S - NEON optimised filter functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 2014,2017 Glenn Randers-Pehrson
|
||||||
|
* Written by Mans Rullgard, 2011.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This is required to get the symbol renames, which are #defines, and the
|
||||||
|
* definitions (or not) of PNG_ARM_NEON_OPT and PNG_ARM_NEON_IMPLEMENTATION.
|
||||||
|
*/
|
||||||
|
#define PNG_VERSION_INFO_ONLY
|
||||||
|
#include "../pngpriv.h"
|
||||||
|
|
||||||
|
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(__ELF__)
|
||||||
|
.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
|
||||||
|
/* Assembler NEON support - only works for 32-bit ARM (i.e. it does not work for
|
||||||
|
* ARM64). The code in arm/filter_neon_intrinsics.c supports ARM64, however it
|
||||||
|
* only works if -mfpu=neon is specified on the GCC command line. See pngpriv.h
|
||||||
|
* for the logic which sets PNG_USE_ARM_NEON_ASM:
|
||||||
|
*/
|
||||||
|
#if PNG_ARM_NEON_IMPLEMENTATION == 2 /* hand-coded assembler */
|
||||||
|
|
||||||
|
#if PNG_ARM_NEON_OPT > 0
|
||||||
|
|
||||||
|
#ifdef __ELF__
|
||||||
|
# define ELF
|
||||||
|
#else
|
||||||
|
# define ELF @
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.arch armv7-a
|
||||||
|
.fpu neon
|
||||||
|
|
||||||
|
.macro func name, export=0
|
||||||
|
.macro endfunc
|
||||||
|
ELF .size \name, . - \name
|
||||||
|
.endfunc
|
||||||
|
.purgem endfunc
|
||||||
|
.endm
|
||||||
|
.text
|
||||||
|
|
||||||
|
/* Explicitly specifying alignment here because some versions of
|
||||||
|
* GAS don't align code correctly. This is harmless in correctly
|
||||||
|
* written versions of GAS.
|
||||||
|
*/
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
.if \export
|
||||||
|
.global \name
|
||||||
|
.endif
|
||||||
|
ELF .type \name, STT_FUNC
|
||||||
|
.func \name
|
||||||
|
\name:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
func png_read_filter_row_sub4_neon, export=1
|
||||||
|
ldr r3, [r0, #4] @ rowbytes
|
||||||
|
vmov.i8 d3, #0
|
||||||
|
1:
|
||||||
|
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
|
||||||
|
vadd.u8 d0, d3, d4
|
||||||
|
vadd.u8 d1, d0, d5
|
||||||
|
vadd.u8 d2, d1, d6
|
||||||
|
vadd.u8 d3, d2, d7
|
||||||
|
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
|
||||||
|
subs r3, r3, #16
|
||||||
|
bgt 1b
|
||||||
|
|
||||||
|
bx lr
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func png_read_filter_row_sub3_neon, export=1
|
||||||
|
ldr r3, [r0, #4] @ rowbytes
|
||||||
|
vmov.i8 d3, #0
|
||||||
|
mov r0, r1
|
||||||
|
mov r2, #3
|
||||||
|
mov r12, #12
|
||||||
|
vld1.8 {q11}, [r0], r12
|
||||||
|
1:
|
||||||
|
vext.8 d5, d22, d23, #3
|
||||||
|
vadd.u8 d0, d3, d22
|
||||||
|
vext.8 d6, d22, d23, #6
|
||||||
|
vadd.u8 d1, d0, d5
|
||||||
|
vext.8 d7, d23, d23, #1
|
||||||
|
vld1.8 {q11}, [r0], r12
|
||||||
|
vst1.32 {d0[0]}, [r1,:32], r2
|
||||||
|
vadd.u8 d2, d1, d6
|
||||||
|
vst1.32 {d1[0]}, [r1], r2
|
||||||
|
vadd.u8 d3, d2, d7
|
||||||
|
vst1.32 {d2[0]}, [r1], r2
|
||||||
|
vst1.32 {d3[0]}, [r1], r2
|
||||||
|
subs r3, r3, #12
|
||||||
|
bgt 1b
|
||||||
|
|
||||||
|
bx lr
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func png_read_filter_row_up_neon, export=1
|
||||||
|
ldr r3, [r0, #4] @ rowbytes
|
||||||
|
1:
|
||||||
|
vld1.8 {q0}, [r1,:128]
|
||||||
|
vld1.8 {q1}, [r2,:128]!
|
||||||
|
vadd.u8 q0, q0, q1
|
||||||
|
vst1.8 {q0}, [r1,:128]!
|
||||||
|
subs r3, r3, #16
|
||||||
|
bgt 1b
|
||||||
|
|
||||||
|
bx lr
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func png_read_filter_row_avg4_neon, export=1
|
||||||
|
ldr r12, [r0, #4] @ rowbytes
|
||||||
|
vmov.i8 d3, #0
|
||||||
|
1:
|
||||||
|
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
|
||||||
|
vld4.32 {d16[],d17[],d18[],d19[]},[r2,:128]!
|
||||||
|
vhadd.u8 d0, d3, d16
|
||||||
|
vadd.u8 d0, d0, d4
|
||||||
|
vhadd.u8 d1, d0, d17
|
||||||
|
vadd.u8 d1, d1, d5
|
||||||
|
vhadd.u8 d2, d1, d18
|
||||||
|
vadd.u8 d2, d2, d6
|
||||||
|
vhadd.u8 d3, d2, d19
|
||||||
|
vadd.u8 d3, d3, d7
|
||||||
|
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
|
||||||
|
subs r12, r12, #16
|
||||||
|
bgt 1b
|
||||||
|
|
||||||
|
bx lr
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func png_read_filter_row_avg3_neon, export=1
|
||||||
|
push {r4,lr}
|
||||||
|
ldr r12, [r0, #4] @ rowbytes
|
||||||
|
vmov.i8 d3, #0
|
||||||
|
mov r0, r1
|
||||||
|
mov r4, #3
|
||||||
|
mov lr, #12
|
||||||
|
vld1.8 {q11}, [r0], lr
|
||||||
|
1:
|
||||||
|
vld1.8 {q10}, [r2], lr
|
||||||
|
vext.8 d5, d22, d23, #3
|
||||||
|
vhadd.u8 d0, d3, d20
|
||||||
|
vext.8 d17, d20, d21, #3
|
||||||
|
vadd.u8 d0, d0, d22
|
||||||
|
vext.8 d6, d22, d23, #6
|
||||||
|
vhadd.u8 d1, d0, d17
|
||||||
|
vext.8 d18, d20, d21, #6
|
||||||
|
vadd.u8 d1, d1, d5
|
||||||
|
vext.8 d7, d23, d23, #1
|
||||||
|
vld1.8 {q11}, [r0], lr
|
||||||
|
vst1.32 {d0[0]}, [r1,:32], r4
|
||||||
|
vhadd.u8 d2, d1, d18
|
||||||
|
vst1.32 {d1[0]}, [r1], r4
|
||||||
|
vext.8 d19, d21, d21, #1
|
||||||
|
vadd.u8 d2, d2, d6
|
||||||
|
vhadd.u8 d3, d2, d19
|
||||||
|
vst1.32 {d2[0]}, [r1], r4
|
||||||
|
vadd.u8 d3, d3, d7
|
||||||
|
vst1.32 {d3[0]}, [r1], r4
|
||||||
|
subs r12, r12, #12
|
||||||
|
bgt 1b
|
||||||
|
|
||||||
|
pop {r4,pc}
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
.macro paeth rx, ra, rb, rc
|
||||||
|
vaddl.u8 q12, \ra, \rb @ a + b
|
||||||
|
vaddl.u8 q15, \rc, \rc @ 2*c
|
||||||
|
vabdl.u8 q13, \rb, \rc @ pa
|
||||||
|
vabdl.u8 q14, \ra, \rc @ pb
|
||||||
|
vabd.u16 q15, q12, q15 @ pc
|
||||||
|
vcle.u16 q12, q13, q14 @ pa <= pb
|
||||||
|
vcle.u16 q13, q13, q15 @ pa <= pc
|
||||||
|
vcle.u16 q14, q14, q15 @ pb <= pc
|
||||||
|
vand q12, q12, q13 @ pa <= pb && pa <= pc
|
||||||
|
vmovn.u16 d28, q14
|
||||||
|
vmovn.u16 \rx, q12
|
||||||
|
vbsl d28, \rb, \rc
|
||||||
|
vbsl \rx, \ra, d28
|
||||||
|
.endm
|
||||||
|
|
||||||
|
func png_read_filter_row_paeth4_neon, export=1
|
||||||
|
ldr r12, [r0, #4] @ rowbytes
|
||||||
|
vmov.i8 d3, #0
|
||||||
|
vmov.i8 d20, #0
|
||||||
|
1:
|
||||||
|
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
|
||||||
|
vld4.32 {d16[],d17[],d18[],d19[]},[r2,:128]!
|
||||||
|
paeth d0, d3, d16, d20
|
||||||
|
vadd.u8 d0, d0, d4
|
||||||
|
paeth d1, d0, d17, d16
|
||||||
|
vadd.u8 d1, d1, d5
|
||||||
|
paeth d2, d1, d18, d17
|
||||||
|
vadd.u8 d2, d2, d6
|
||||||
|
paeth d3, d2, d19, d18
|
||||||
|
vmov d20, d19
|
||||||
|
vadd.u8 d3, d3, d7
|
||||||
|
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
|
||||||
|
subs r12, r12, #16
|
||||||
|
bgt 1b
|
||||||
|
|
||||||
|
bx lr
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func png_read_filter_row_paeth3_neon, export=1
|
||||||
|
push {r4,lr}
|
||||||
|
ldr r12, [r0, #4] @ rowbytes
|
||||||
|
vmov.i8 d3, #0
|
||||||
|
vmov.i8 d4, #0
|
||||||
|
mov r0, r1
|
||||||
|
mov r4, #3
|
||||||
|
mov lr, #12
|
||||||
|
vld1.8 {q11}, [r0], lr
|
||||||
|
1:
|
||||||
|
vld1.8 {q10}, [r2], lr
|
||||||
|
paeth d0, d3, d20, d4
|
||||||
|
vext.8 d5, d22, d23, #3
|
||||||
|
vadd.u8 d0, d0, d22
|
||||||
|
vext.8 d17, d20, d21, #3
|
||||||
|
paeth d1, d0, d17, d20
|
||||||
|
vst1.32 {d0[0]}, [r1,:32], r4
|
||||||
|
vext.8 d6, d22, d23, #6
|
||||||
|
vadd.u8 d1, d1, d5
|
||||||
|
vext.8 d18, d20, d21, #6
|
||||||
|
paeth d2, d1, d18, d17
|
||||||
|
vext.8 d7, d23, d23, #1
|
||||||
|
vld1.8 {q11}, [r0], lr
|
||||||
|
vst1.32 {d1[0]}, [r1], r4
|
||||||
|
vadd.u8 d2, d2, d6
|
||||||
|
vext.8 d19, d21, d21, #1
|
||||||
|
paeth d3, d2, d19, d18
|
||||||
|
vst1.32 {d2[0]}, [r1], r4
|
||||||
|
vmov d4, d19
|
||||||
|
vadd.u8 d3, d3, d7
|
||||||
|
vst1.32 {d3[0]}, [r1], r4
|
||||||
|
subs r12, r12, #12
|
||||||
|
bgt 1b
|
||||||
|
|
||||||
|
pop {r4,pc}
|
||||||
|
endfunc
|
||||||
|
#endif /* PNG_ARM_NEON_OPT > 0 */
|
||||||
|
#endif /* PNG_ARM_NEON_IMPLEMENTATION == 2 (assembler) */
|
||||||
|
#endif /* READ */
|
|
@ -0,0 +1,402 @@
|
||||||
|
|
||||||
|
/* filter_neon_intrinsics.c - NEON optimised filter functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 2014,2016 Glenn Randers-Pehrson
|
||||||
|
* Written by James Yu <james.yu at linaro.org>, October 2013.
|
||||||
|
* Based on filter_neon.S, written by Mans Rullgard, 2011.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pngpriv.h"
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
|
||||||
|
/* This code requires -mfpu=neon on the command line: */
|
||||||
|
#if PNG_ARM_NEON_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && defined(_M_ARM64)
|
||||||
|
# include <arm64_neon.h>
|
||||||
|
#else
|
||||||
|
# include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* libpng row pointers are not necessarily aligned to any particular boundary,
|
||||||
|
* however this code will only work with appropriate alignment. arm/arm_init.c
|
||||||
|
* checks for this (and will not compile unless it is done). This code uses
|
||||||
|
* variants of png_aligncast to avoid compiler warnings.
|
||||||
|
*/
|
||||||
|
#define png_ptr(type,pointer) png_aligncast(type *,pointer)
|
||||||
|
#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer)
|
||||||
|
|
||||||
|
/* The following relies on a variable 'temp_pointer' being declared with type
|
||||||
|
* 'type'. This is written this way just to hide the GCC strict aliasing
|
||||||
|
* warning; note that the code is safe because there never is an alias between
|
||||||
|
* the input and output pointers.
|
||||||
|
*
|
||||||
|
* When compiling with MSVC ARM64, the png_ldr macro can't be passed directly
|
||||||
|
* to vst4_lane_u32, because of an internal compiler error inside MSVC.
|
||||||
|
* To avoid this compiler bug, we use a temporary variable (vdest_val) to store
|
||||||
|
* the result of png_ldr.
|
||||||
|
*/
|
||||||
|
#define png_ldr(type,pointer)\
|
||||||
|
(temp_pointer = png_ptr(type,pointer), *temp_pointer)
|
||||||
|
|
||||||
|
#if PNG_ARM_NEON_OPT > 0
|
||||||
|
|
||||||
|
void
|
||||||
|
png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev_row)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
png_bytep rp_stop = row + row_info->rowbytes;
|
||||||
|
png_const_bytep pp = prev_row;
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_up_neon");
|
||||||
|
|
||||||
|
for (; rp < rp_stop; rp += 16, pp += 16)
|
||||||
|
{
|
||||||
|
uint8x16_t qrp, qpp;
|
||||||
|
|
||||||
|
qrp = vld1q_u8(rp);
|
||||||
|
qpp = vld1q_u8(pp);
|
||||||
|
qrp = vaddq_u8(qrp, qpp);
|
||||||
|
vst1q_u8(rp, qrp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev_row)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
png_bytep rp_stop = row + row_info->rowbytes;
|
||||||
|
|
||||||
|
uint8x16_t vtmp = vld1q_u8(rp);
|
||||||
|
uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp);
|
||||||
|
uint8x8x2_t vrp = *vrpt;
|
||||||
|
|
||||||
|
uint8x8x4_t vdest;
|
||||||
|
vdest.val[3] = vdup_n_u8(0);
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_sub3_neon");
|
||||||
|
|
||||||
|
for (; rp < rp_stop;)
|
||||||
|
{
|
||||||
|
uint8x8_t vtmp1, vtmp2;
|
||||||
|
uint32x2_t *temp_pointer;
|
||||||
|
|
||||||
|
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
|
||||||
|
vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
|
||||||
|
vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6);
|
||||||
|
vdest.val[1] = vadd_u8(vdest.val[0], vtmp1);
|
||||||
|
|
||||||
|
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
|
||||||
|
vdest.val[2] = vadd_u8(vdest.val[1], vtmp2);
|
||||||
|
vdest.val[3] = vadd_u8(vdest.val[2], vtmp1);
|
||||||
|
|
||||||
|
vtmp = vld1q_u8(rp + 12);
|
||||||
|
vrpt = png_ptr(uint8x8x2_t, &vtmp);
|
||||||
|
vrp = *vrpt;
|
||||||
|
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
|
||||||
|
rp += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
PNG_UNUSED(prev_row)
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev_row)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
png_bytep rp_stop = row + row_info->rowbytes;
|
||||||
|
|
||||||
|
uint8x8x4_t vdest;
|
||||||
|
vdest.val[3] = vdup_n_u8(0);
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_sub4_neon");
|
||||||
|
|
||||||
|
for (; rp < rp_stop; rp += 16)
|
||||||
|
{
|
||||||
|
uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp));
|
||||||
|
uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp);
|
||||||
|
uint8x8x4_t vrp = *vrpt;
|
||||||
|
uint32x2x4_t *temp_pointer;
|
||||||
|
uint32x2x4_t vdest_val;
|
||||||
|
|
||||||
|
vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
|
||||||
|
vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]);
|
||||||
|
vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]);
|
||||||
|
vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]);
|
||||||
|
|
||||||
|
vdest_val = png_ldr(uint32x2x4_t, &vdest);
|
||||||
|
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PNG_UNUSED(prev_row)
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev_row)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
png_const_bytep pp = prev_row;
|
||||||
|
png_bytep rp_stop = row + row_info->rowbytes;
|
||||||
|
|
||||||
|
uint8x16_t vtmp;
|
||||||
|
uint8x8x2_t *vrpt;
|
||||||
|
uint8x8x2_t vrp;
|
||||||
|
uint8x8x4_t vdest;
|
||||||
|
vdest.val[3] = vdup_n_u8(0);
|
||||||
|
|
||||||
|
vtmp = vld1q_u8(rp);
|
||||||
|
vrpt = png_ptr(uint8x8x2_t,&vtmp);
|
||||||
|
vrp = *vrpt;
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_avg3_neon");
|
||||||
|
|
||||||
|
for (; rp < rp_stop; pp += 12)
|
||||||
|
{
|
||||||
|
uint8x8_t vtmp1, vtmp2, vtmp3;
|
||||||
|
|
||||||
|
uint8x8x2_t *vppt;
|
||||||
|
uint8x8x2_t vpp;
|
||||||
|
|
||||||
|
uint32x2_t *temp_pointer;
|
||||||
|
|
||||||
|
vtmp = vld1q_u8(pp);
|
||||||
|
vppt = png_ptr(uint8x8x2_t,&vtmp);
|
||||||
|
vpp = *vppt;
|
||||||
|
|
||||||
|
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
|
||||||
|
vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);
|
||||||
|
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
|
||||||
|
|
||||||
|
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);
|
||||||
|
vtmp3 = vext_u8(vrp.val[0], vrp.val[1], 6);
|
||||||
|
vdest.val[1] = vhadd_u8(vdest.val[0], vtmp2);
|
||||||
|
vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);
|
||||||
|
|
||||||
|
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 6);
|
||||||
|
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
|
||||||
|
|
||||||
|
vtmp = vld1q_u8(rp + 12);
|
||||||
|
vrpt = png_ptr(uint8x8x2_t,&vtmp);
|
||||||
|
vrp = *vrpt;
|
||||||
|
|
||||||
|
vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2);
|
||||||
|
vdest.val[2] = vadd_u8(vdest.val[2], vtmp3);
|
||||||
|
|
||||||
|
vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);
|
||||||
|
|
||||||
|
vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2);
|
||||||
|
vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);
|
||||||
|
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
|
||||||
|
rp += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev_row)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
png_bytep rp_stop = row + row_info->rowbytes;
|
||||||
|
png_const_bytep pp = prev_row;
|
||||||
|
|
||||||
|
uint8x8x4_t vdest;
|
||||||
|
vdest.val[3] = vdup_n_u8(0);
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_avg4_neon");
|
||||||
|
|
||||||
|
for (; rp < rp_stop; rp += 16, pp += 16)
|
||||||
|
{
|
||||||
|
uint32x2x4_t vtmp;
|
||||||
|
uint8x8x4_t *vrpt, *vppt;
|
||||||
|
uint8x8x4_t vrp, vpp;
|
||||||
|
uint32x2x4_t *temp_pointer;
|
||||||
|
uint32x2x4_t vdest_val;
|
||||||
|
|
||||||
|
vtmp = vld4_u32(png_ptr(uint32_t,rp));
|
||||||
|
vrpt = png_ptr(uint8x8x4_t,&vtmp);
|
||||||
|
vrp = *vrpt;
|
||||||
|
vtmp = vld4_u32(png_ptrc(uint32_t,pp));
|
||||||
|
vppt = png_ptr(uint8x8x4_t,&vtmp);
|
||||||
|
vpp = *vppt;
|
||||||
|
|
||||||
|
vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);
|
||||||
|
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
|
||||||
|
vdest.val[1] = vhadd_u8(vdest.val[0], vpp.val[1]);
|
||||||
|
vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);
|
||||||
|
vdest.val[2] = vhadd_u8(vdest.val[1], vpp.val[2]);
|
||||||
|
vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);
|
||||||
|
vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]);
|
||||||
|
vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
|
||||||
|
|
||||||
|
vdest_val = png_ldr(uint32x2x4_t, &vdest);
|
||||||
|
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8x8_t
|
||||||
|
paeth(uint8x8_t a, uint8x8_t b, uint8x8_t c)
|
||||||
|
{
|
||||||
|
uint8x8_t d, e;
|
||||||
|
uint16x8_t p1, pa, pb, pc;
|
||||||
|
|
||||||
|
p1 = vaddl_u8(a, b); /* a + b */
|
||||||
|
pc = vaddl_u8(c, c); /* c * 2 */
|
||||||
|
pa = vabdl_u8(b, c); /* pa */
|
||||||
|
pb = vabdl_u8(a, c); /* pb */
|
||||||
|
pc = vabdq_u16(p1, pc); /* pc */
|
||||||
|
|
||||||
|
p1 = vcleq_u16(pa, pb); /* pa <= pb */
|
||||||
|
pa = vcleq_u16(pa, pc); /* pa <= pc */
|
||||||
|
pb = vcleq_u16(pb, pc); /* pb <= pc */
|
||||||
|
|
||||||
|
p1 = vandq_u16(p1, pa); /* pa <= pb && pa <= pc */
|
||||||
|
|
||||||
|
d = vmovn_u16(pb);
|
||||||
|
e = vmovn_u16(p1);
|
||||||
|
|
||||||
|
d = vbsl_u8(d, b, c);
|
||||||
|
e = vbsl_u8(e, a, d);
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev_row)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
png_const_bytep pp = prev_row;
|
||||||
|
png_bytep rp_stop = row + row_info->rowbytes;
|
||||||
|
|
||||||
|
uint8x16_t vtmp;
|
||||||
|
uint8x8x2_t *vrpt;
|
||||||
|
uint8x8x2_t vrp;
|
||||||
|
uint8x8_t vlast = vdup_n_u8(0);
|
||||||
|
uint8x8x4_t vdest;
|
||||||
|
vdest.val[3] = vdup_n_u8(0);
|
||||||
|
|
||||||
|
vtmp = vld1q_u8(rp);
|
||||||
|
vrpt = png_ptr(uint8x8x2_t,&vtmp);
|
||||||
|
vrp = *vrpt;
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_paeth3_neon");
|
||||||
|
|
||||||
|
for (; rp < rp_stop; pp += 12)
|
||||||
|
{
|
||||||
|
uint8x8x2_t *vppt;
|
||||||
|
uint8x8x2_t vpp;
|
||||||
|
uint8x8_t vtmp1, vtmp2, vtmp3;
|
||||||
|
uint32x2_t *temp_pointer;
|
||||||
|
|
||||||
|
vtmp = vld1q_u8(pp);
|
||||||
|
vppt = png_ptr(uint8x8x2_t,&vtmp);
|
||||||
|
vpp = *vppt;
|
||||||
|
|
||||||
|
vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast);
|
||||||
|
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
|
||||||
|
|
||||||
|
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
|
||||||
|
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);
|
||||||
|
vdest.val[1] = paeth(vdest.val[0], vtmp2, vpp.val[0]);
|
||||||
|
vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);
|
||||||
|
|
||||||
|
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 6);
|
||||||
|
vtmp3 = vext_u8(vpp.val[0], vpp.val[1], 6);
|
||||||
|
vdest.val[2] = paeth(vdest.val[1], vtmp3, vtmp2);
|
||||||
|
vdest.val[2] = vadd_u8(vdest.val[2], vtmp1);
|
||||||
|
|
||||||
|
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
|
||||||
|
vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);
|
||||||
|
|
||||||
|
vtmp = vld1q_u8(rp + 12);
|
||||||
|
vrpt = png_ptr(uint8x8x2_t,&vtmp);
|
||||||
|
vrp = *vrpt;
|
||||||
|
|
||||||
|
vdest.val[3] = paeth(vdest.val[2], vtmp2, vtmp3);
|
||||||
|
vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);
|
||||||
|
|
||||||
|
vlast = vtmp2;
|
||||||
|
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
|
||||||
|
rp += 3;
|
||||||
|
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
|
||||||
|
rp += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev_row)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
png_bytep rp_stop = row + row_info->rowbytes;
|
||||||
|
png_const_bytep pp = prev_row;
|
||||||
|
|
||||||
|
uint8x8_t vlast = vdup_n_u8(0);
|
||||||
|
uint8x8x4_t vdest;
|
||||||
|
vdest.val[3] = vdup_n_u8(0);
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_paeth4_neon");
|
||||||
|
|
||||||
|
for (; rp < rp_stop; rp += 16, pp += 16)
|
||||||
|
{
|
||||||
|
uint32x2x4_t vtmp;
|
||||||
|
uint8x8x4_t *vrpt, *vppt;
|
||||||
|
uint8x8x4_t vrp, vpp;
|
||||||
|
uint32x2x4_t *temp_pointer;
|
||||||
|
uint32x2x4_t vdest_val;
|
||||||
|
|
||||||
|
vtmp = vld4_u32(png_ptr(uint32_t,rp));
|
||||||
|
vrpt = png_ptr(uint8x8x4_t,&vtmp);
|
||||||
|
vrp = *vrpt;
|
||||||
|
vtmp = vld4_u32(png_ptrc(uint32_t,pp));
|
||||||
|
vppt = png_ptr(uint8x8x4_t,&vtmp);
|
||||||
|
vpp = *vppt;
|
||||||
|
|
||||||
|
vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast);
|
||||||
|
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
|
||||||
|
vdest.val[1] = paeth(vdest.val[0], vpp.val[1], vpp.val[0]);
|
||||||
|
vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);
|
||||||
|
vdest.val[2] = paeth(vdest.val[1], vpp.val[2], vpp.val[1]);
|
||||||
|
vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);
|
||||||
|
vdest.val[3] = paeth(vdest.val[2], vpp.val[3], vpp.val[2]);
|
||||||
|
vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
|
||||||
|
|
||||||
|
vlast = vpp.val[3];
|
||||||
|
|
||||||
|
vdest_val = png_ldr(uint32x2x4_t, &vdest);
|
||||||
|
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PNG_ARM_NEON_OPT > 0 */
|
||||||
|
#endif /* PNG_ARM_NEON_IMPLEMENTATION == 1 (intrinsics) */
|
||||||
|
#endif /* READ */
|
|
@ -0,0 +1,149 @@
|
||||||
|
|
||||||
|
/* palette_neon_intrinsics.c - NEON optimised palette expansion functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-2019 Cosmin Truta
|
||||||
|
* Copyright (c) 2017-2018 Arm Holdings. All rights reserved.
|
||||||
|
* Written by Richard Townsend <Richard.Townsend@arm.com>, February 2017.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pngpriv.h"
|
||||||
|
|
||||||
|
#if PNG_ARM_NEON_IMPLEMENTATION == 1
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && defined(_M_ARM64)
|
||||||
|
# include <arm64_neon.h>
|
||||||
|
#else
|
||||||
|
# include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Build an RGBA8 palette from the separate RGB and alpha palettes. */
|
||||||
|
void
|
||||||
|
png_riffle_palette_neon(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_const_colorp palette = png_ptr->palette;
|
||||||
|
png_bytep riffled_palette = png_ptr->riffled_palette;
|
||||||
|
png_const_bytep trans_alpha = png_ptr->trans_alpha;
|
||||||
|
int num_trans = png_ptr->num_trans;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
png_debug(1, "in png_riffle_palette_neon");
|
||||||
|
|
||||||
|
/* Initially black, opaque. */
|
||||||
|
uint8x16x4_t w = {{
|
||||||
|
vdupq_n_u8(0x00),
|
||||||
|
vdupq_n_u8(0x00),
|
||||||
|
vdupq_n_u8(0x00),
|
||||||
|
vdupq_n_u8(0xff),
|
||||||
|
}};
|
||||||
|
|
||||||
|
/* First, riffle the RGB colours into an RGBA8 palette.
|
||||||
|
* The alpha component is set to opaque for now.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < 256; i += 16)
|
||||||
|
{
|
||||||
|
uint8x16x3_t v = vld3q_u8((png_const_bytep)(palette + i));
|
||||||
|
w.val[0] = v.val[0];
|
||||||
|
w.val[1] = v.val[1];
|
||||||
|
w.val[2] = v.val[2];
|
||||||
|
vst4q_u8(riffled_palette + (i << 2), w);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix up the missing transparency values. */
|
||||||
|
for (i = 0; i < num_trans; i++)
|
||||||
|
riffled_palette[(i << 2) + 3] = trans_alpha[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expands a palettized row into RGBA8. */
|
||||||
|
int
|
||||||
|
png_do_expand_palette_rgba8_neon(png_structrp png_ptr, png_row_infop row_info,
|
||||||
|
png_const_bytep row, png_bytepp ssp, png_bytepp ddp)
|
||||||
|
{
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
const png_uint_32 *riffled_palette =
|
||||||
|
(const png_uint_32 *)png_ptr->riffled_palette;
|
||||||
|
const png_int_32 pixels_per_chunk = 4;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
png_debug(1, "in png_do_expand_palette_rgba8_neon");
|
||||||
|
|
||||||
|
if (row_width < pixels_per_chunk)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* This function originally gets the last byte of the output row.
|
||||||
|
* The NEON part writes forward from a given position, so we have
|
||||||
|
* to seek this back by 4 pixels x 4 bytes.
|
||||||
|
*/
|
||||||
|
*ddp = *ddp - ((pixels_per_chunk * sizeof(png_uint_32)) - 1);
|
||||||
|
|
||||||
|
for (i = 0; i < row_width; i += pixels_per_chunk)
|
||||||
|
{
|
||||||
|
uint32x4_t cur;
|
||||||
|
png_bytep sp = *ssp - i, dp = *ddp - (i << 2);
|
||||||
|
cur = vld1q_dup_u32 (riffled_palette + *(sp - 3));
|
||||||
|
cur = vld1q_lane_u32(riffled_palette + *(sp - 2), cur, 1);
|
||||||
|
cur = vld1q_lane_u32(riffled_palette + *(sp - 1), cur, 2);
|
||||||
|
cur = vld1q_lane_u32(riffled_palette + *(sp - 0), cur, 3);
|
||||||
|
vst1q_u32((void *)dp, cur);
|
||||||
|
}
|
||||||
|
if (i != row_width)
|
||||||
|
{
|
||||||
|
/* Remove the amount that wasn't processed. */
|
||||||
|
i -= pixels_per_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrement output pointers. */
|
||||||
|
*ssp = *ssp - i;
|
||||||
|
*ddp = *ddp - (i << 2);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expands a palettized row into RGB8. */
|
||||||
|
int
|
||||||
|
png_do_expand_palette_rgb8_neon(png_structrp png_ptr, png_row_infop row_info,
|
||||||
|
png_const_bytep row, png_bytepp ssp, png_bytepp ddp)
|
||||||
|
{
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
png_const_bytep palette = (png_const_bytep)png_ptr->palette;
|
||||||
|
const png_uint_32 pixels_per_chunk = 8;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
png_debug(1, "in png_do_expand_palette_rgb8_neon");
|
||||||
|
|
||||||
|
if (row_width <= pixels_per_chunk)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Seeking this back by 8 pixels x 3 bytes. */
|
||||||
|
*ddp = *ddp - ((pixels_per_chunk * sizeof(png_color)) - 1);
|
||||||
|
|
||||||
|
for (i = 0; i < row_width; i += pixels_per_chunk)
|
||||||
|
{
|
||||||
|
uint8x8x3_t cur;
|
||||||
|
png_bytep sp = *ssp - i, dp = *ddp - ((i << 1) + i);
|
||||||
|
cur = vld3_dup_u8(palette + sizeof(png_color) * (*(sp - 7)));
|
||||||
|
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 6)), cur, 1);
|
||||||
|
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 5)), cur, 2);
|
||||||
|
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 4)), cur, 3);
|
||||||
|
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 3)), cur, 4);
|
||||||
|
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 2)), cur, 5);
|
||||||
|
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 1)), cur, 6);
|
||||||
|
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 0)), cur, 7);
|
||||||
|
vst3_u8((void *)dp, cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != row_width)
|
||||||
|
{
|
||||||
|
/* Remove the amount that wasn't processed. */
|
||||||
|
i -= pixels_per_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrement output pointers. */
|
||||||
|
*ssp = *ssp - i;
|
||||||
|
*ddp = *ddp - ((i << 1) + i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PNG_ARM_NEON_IMPLEMENTATION */
|
|
@ -0,0 +1,391 @@
|
||||||
|
|
||||||
|
/* filter_sse2_intrinsics.c - SSE2 optimized filter functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 2016-2017 Glenn Randers-Pehrson
|
||||||
|
* Written by Mike Klein and Matt Sarett
|
||||||
|
* Derived from arm/filter_neon_intrinsics.c
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pngpriv.h"
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
|
||||||
|
#if PNG_INTEL_SSE_IMPLEMENTATION > 0
|
||||||
|
|
||||||
|
#include <immintrin.h>
|
||||||
|
|
||||||
|
/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).
|
||||||
|
* They're positioned like this:
|
||||||
|
* prev: c b
|
||||||
|
* row: a d
|
||||||
|
* The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be
|
||||||
|
* whichever of a, b, or c is closest to p=a+b-c.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static __m128i load4(const void* p) {
|
||||||
|
int tmp;
|
||||||
|
memcpy(&tmp, p, sizeof(tmp));
|
||||||
|
return _mm_cvtsi32_si128(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void store4(void* p, __m128i v) {
|
||||||
|
int tmp = _mm_cvtsi128_si32(v);
|
||||||
|
memcpy(p, &tmp, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
static __m128i load3(const void* p) {
|
||||||
|
png_uint_32 tmp = 0;
|
||||||
|
memcpy(&tmp, p, 3);
|
||||||
|
return _mm_cvtsi32_si128(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void store3(void* p, __m128i v) {
|
||||||
|
int tmp = _mm_cvtsi128_si32(v);
|
||||||
|
memcpy(p, &tmp, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void png_read_filter_row_sub3_sse2(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev)
|
||||||
|
{
|
||||||
|
/* The Sub filter predicts each pixel as the previous pixel, a.
|
||||||
|
* There is no pixel to the left of the first pixel. It's encoded directly.
|
||||||
|
* That works with our main loop if we just say that left pixel was zero.
|
||||||
|
*/
|
||||||
|
size_t rb;
|
||||||
|
|
||||||
|
__m128i a, d = _mm_setzero_si128();
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_sub3_sse2");
|
||||||
|
|
||||||
|
rb = row_info->rowbytes;
|
||||||
|
while (rb >= 4) {
|
||||||
|
a = d; d = load4(row);
|
||||||
|
d = _mm_add_epi8(d, a);
|
||||||
|
store3(row, d);
|
||||||
|
|
||||||
|
row += 3;
|
||||||
|
rb -= 3;
|
||||||
|
}
|
||||||
|
if (rb > 0) {
|
||||||
|
a = d; d = load3(row);
|
||||||
|
d = _mm_add_epi8(d, a);
|
||||||
|
store3(row, d);
|
||||||
|
|
||||||
|
row += 3;
|
||||||
|
rb -= 3;
|
||||||
|
}
|
||||||
|
PNG_UNUSED(prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
void png_read_filter_row_sub4_sse2(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev)
|
||||||
|
{
|
||||||
|
/* The Sub filter predicts each pixel as the previous pixel, a.
|
||||||
|
* There is no pixel to the left of the first pixel. It's encoded directly.
|
||||||
|
* That works with our main loop if we just say that left pixel was zero.
|
||||||
|
*/
|
||||||
|
size_t rb;
|
||||||
|
|
||||||
|
__m128i a, d = _mm_setzero_si128();
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_sub4_sse2");
|
||||||
|
|
||||||
|
rb = row_info->rowbytes+4;
|
||||||
|
while (rb > 4) {
|
||||||
|
a = d; d = load4(row);
|
||||||
|
d = _mm_add_epi8(d, a);
|
||||||
|
store4(row, d);
|
||||||
|
|
||||||
|
row += 4;
|
||||||
|
rb -= 4;
|
||||||
|
}
|
||||||
|
PNG_UNUSED(prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
void png_read_filter_row_avg3_sse2(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev)
|
||||||
|
{
|
||||||
|
/* The Avg filter predicts each pixel as the (truncated) average of a and b.
|
||||||
|
* There's no pixel to the left of the first pixel. Luckily, it's
|
||||||
|
* predicted to be half of the pixel above it. So again, this works
|
||||||
|
* perfectly with our loop if we make sure a starts at zero.
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t rb;
|
||||||
|
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
|
||||||
|
__m128i b;
|
||||||
|
__m128i a, d = zero;
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_avg3_sse2");
|
||||||
|
rb = row_info->rowbytes;
|
||||||
|
while (rb >= 4) {
|
||||||
|
__m128i avg;
|
||||||
|
b = load4(prev);
|
||||||
|
a = d; d = load4(row );
|
||||||
|
|
||||||
|
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
|
||||||
|
avg = _mm_avg_epu8(a,b);
|
||||||
|
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
|
||||||
|
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
|
||||||
|
_mm_set1_epi8(1)));
|
||||||
|
d = _mm_add_epi8(d, avg);
|
||||||
|
store3(row, d);
|
||||||
|
|
||||||
|
prev += 3;
|
||||||
|
row += 3;
|
||||||
|
rb -= 3;
|
||||||
|
}
|
||||||
|
if (rb > 0) {
|
||||||
|
__m128i avg;
|
||||||
|
b = load3(prev);
|
||||||
|
a = d; d = load3(row );
|
||||||
|
|
||||||
|
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
|
||||||
|
avg = _mm_avg_epu8(a,b);
|
||||||
|
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
|
||||||
|
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
|
||||||
|
_mm_set1_epi8(1)));
|
||||||
|
|
||||||
|
d = _mm_add_epi8(d, avg);
|
||||||
|
store3(row, d);
|
||||||
|
|
||||||
|
prev += 3;
|
||||||
|
row += 3;
|
||||||
|
rb -= 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void png_read_filter_row_avg4_sse2(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev)
|
||||||
|
{
|
||||||
|
/* The Avg filter predicts each pixel as the (truncated) average of a and b.
|
||||||
|
* There's no pixel to the left of the first pixel. Luckily, it's
|
||||||
|
* predicted to be half of the pixel above it. So again, this works
|
||||||
|
* perfectly with our loop if we make sure a starts at zero.
|
||||||
|
*/
|
||||||
|
size_t rb;
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
__m128i b;
|
||||||
|
__m128i a, d = zero;
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_avg4_sse2");
|
||||||
|
|
||||||
|
rb = row_info->rowbytes+4;
|
||||||
|
while (rb > 4) {
|
||||||
|
__m128i avg;
|
||||||
|
b = load4(prev);
|
||||||
|
a = d; d = load4(row );
|
||||||
|
|
||||||
|
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
|
||||||
|
avg = _mm_avg_epu8(a,b);
|
||||||
|
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
|
||||||
|
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
|
||||||
|
_mm_set1_epi8(1)));
|
||||||
|
|
||||||
|
d = _mm_add_epi8(d, avg);
|
||||||
|
store4(row, d);
|
||||||
|
|
||||||
|
prev += 4;
|
||||||
|
row += 4;
|
||||||
|
rb -= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns |x| for 16-bit lanes. */
|
||||||
|
static __m128i abs_i16(__m128i x) {
|
||||||
|
#if PNG_INTEL_SSE_IMPLEMENTATION >= 2
|
||||||
|
return _mm_abs_epi16(x);
|
||||||
|
#else
|
||||||
|
/* Read this all as, return x<0 ? -x : x.
|
||||||
|
* To negate two's complement, you flip all the bits then add 1.
|
||||||
|
*/
|
||||||
|
__m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128());
|
||||||
|
|
||||||
|
/* Flip negative lanes. */
|
||||||
|
x = _mm_xor_si128(x, is_negative);
|
||||||
|
|
||||||
|
/* +1 to negative lanes, else +0. */
|
||||||
|
x = _mm_sub_epi16(x, is_negative);
|
||||||
|
return x;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bytewise c ? t : e. */
|
||||||
|
static __m128i if_then_else(__m128i c, __m128i t, __m128i e) {
|
||||||
|
#if PNG_INTEL_SSE_IMPLEMENTATION >= 3
|
||||||
|
return _mm_blendv_epi8(e,t,c);
|
||||||
|
#else
|
||||||
|
return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void png_read_filter_row_paeth3_sse2(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev)
|
||||||
|
{
|
||||||
|
/* Paeth tries to predict pixel d using the pixel to the left of it, a,
|
||||||
|
* and two pixels from the previous row, b and c:
|
||||||
|
* prev: c b
|
||||||
|
* row: a d
|
||||||
|
* The Paeth function predicts d to be whichever of a, b, or c is nearest to
|
||||||
|
* p=a+b-c.
|
||||||
|
*
|
||||||
|
* The first pixel has no left context, and so uses an Up filter, p = b.
|
||||||
|
* This works naturally with our main loop's p = a+b-c if we force a and c
|
||||||
|
* to zero.
|
||||||
|
* Here we zero b and d, which become c and a respectively at the start of
|
||||||
|
* the loop.
|
||||||
|
*/
|
||||||
|
size_t rb;
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
__m128i c, b = zero,
|
||||||
|
a, d = zero;
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_paeth3_sse2");
|
||||||
|
|
||||||
|
rb = row_info->rowbytes;
|
||||||
|
while (rb >= 4) {
|
||||||
|
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
|
||||||
|
* intermediates.
|
||||||
|
*/
|
||||||
|
__m128i pa,pb,pc,smallest,nearest;
|
||||||
|
c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
|
||||||
|
a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
|
||||||
|
|
||||||
|
/* (p-a) == (a+b-c - a) == (b-c) */
|
||||||
|
|
||||||
|
pa = _mm_sub_epi16(b,c);
|
||||||
|
|
||||||
|
/* (p-b) == (a+b-c - b) == (a-c) */
|
||||||
|
pb = _mm_sub_epi16(a,c);
|
||||||
|
|
||||||
|
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
|
||||||
|
pc = _mm_add_epi16(pa,pb);
|
||||||
|
|
||||||
|
pa = abs_i16(pa); /* |p-a| */
|
||||||
|
pb = abs_i16(pb); /* |p-b| */
|
||||||
|
pc = abs_i16(pc); /* |p-c| */
|
||||||
|
|
||||||
|
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
|
||||||
|
|
||||||
|
/* Paeth breaks ties favoring a over b over c. */
|
||||||
|
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
|
||||||
|
if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
|
||||||
|
c));
|
||||||
|
|
||||||
|
/* Note `_epi8`: we need addition to wrap modulo 255. */
|
||||||
|
d = _mm_add_epi8(d, nearest);
|
||||||
|
store3(row, _mm_packus_epi16(d,d));
|
||||||
|
|
||||||
|
prev += 3;
|
||||||
|
row += 3;
|
||||||
|
rb -= 3;
|
||||||
|
}
|
||||||
|
if (rb > 0) {
|
||||||
|
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
|
||||||
|
* intermediates.
|
||||||
|
*/
|
||||||
|
__m128i pa,pb,pc,smallest,nearest;
|
||||||
|
c = b; b = _mm_unpacklo_epi8(load3(prev), zero);
|
||||||
|
a = d; d = _mm_unpacklo_epi8(load3(row ), zero);
|
||||||
|
|
||||||
|
/* (p-a) == (a+b-c - a) == (b-c) */
|
||||||
|
pa = _mm_sub_epi16(b,c);
|
||||||
|
|
||||||
|
/* (p-b) == (a+b-c - b) == (a-c) */
|
||||||
|
pb = _mm_sub_epi16(a,c);
|
||||||
|
|
||||||
|
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
|
||||||
|
pc = _mm_add_epi16(pa,pb);
|
||||||
|
|
||||||
|
pa = abs_i16(pa); /* |p-a| */
|
||||||
|
pb = abs_i16(pb); /* |p-b| */
|
||||||
|
pc = abs_i16(pc); /* |p-c| */
|
||||||
|
|
||||||
|
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
|
||||||
|
|
||||||
|
/* Paeth breaks ties favoring a over b over c. */
|
||||||
|
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
|
||||||
|
if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
|
||||||
|
c));
|
||||||
|
|
||||||
|
/* Note `_epi8`: we need addition to wrap modulo 255. */
|
||||||
|
d = _mm_add_epi8(d, nearest);
|
||||||
|
store3(row, _mm_packus_epi16(d,d));
|
||||||
|
|
||||||
|
prev += 3;
|
||||||
|
row += 3;
|
||||||
|
rb -= 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void png_read_filter_row_paeth4_sse2(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_bytep prev)
|
||||||
|
{
|
||||||
|
/* Paeth tries to predict pixel d using the pixel to the left of it, a,
|
||||||
|
* and two pixels from the previous row, b and c:
|
||||||
|
* prev: c b
|
||||||
|
* row: a d
|
||||||
|
* The Paeth function predicts d to be whichever of a, b, or c is nearest to
|
||||||
|
* p=a+b-c.
|
||||||
|
*
|
||||||
|
* The first pixel has no left context, and so uses an Up filter, p = b.
|
||||||
|
* This works naturally with our main loop's p = a+b-c if we force a and c
|
||||||
|
* to zero.
|
||||||
|
* Here we zero b and d, which become c and a respectively at the start of
|
||||||
|
* the loop.
|
||||||
|
*/
|
||||||
|
size_t rb;
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
__m128i pa,pb,pc,smallest,nearest;
|
||||||
|
__m128i c, b = zero,
|
||||||
|
a, d = zero;
|
||||||
|
|
||||||
|
png_debug(1, "in png_read_filter_row_paeth4_sse2");
|
||||||
|
|
||||||
|
rb = row_info->rowbytes+4;
|
||||||
|
while (rb > 4) {
|
||||||
|
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
|
||||||
|
* intermediates.
|
||||||
|
*/
|
||||||
|
c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
|
||||||
|
a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
|
||||||
|
|
||||||
|
/* (p-a) == (a+b-c - a) == (b-c) */
|
||||||
|
pa = _mm_sub_epi16(b,c);
|
||||||
|
|
||||||
|
/* (p-b) == (a+b-c - b) == (a-c) */
|
||||||
|
pb = _mm_sub_epi16(a,c);
|
||||||
|
|
||||||
|
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
|
||||||
|
pc = _mm_add_epi16(pa,pb);
|
||||||
|
|
||||||
|
pa = abs_i16(pa); /* |p-a| */
|
||||||
|
pb = abs_i16(pb); /* |p-b| */
|
||||||
|
pc = abs_i16(pc); /* |p-c| */
|
||||||
|
|
||||||
|
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
|
||||||
|
|
||||||
|
/* Paeth breaks ties favoring a over b over c. */
|
||||||
|
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
|
||||||
|
if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
|
||||||
|
c));
|
||||||
|
|
||||||
|
/* Note `_epi8`: we need addition to wrap modulo 255. */
|
||||||
|
d = _mm_add_epi8(d, nearest);
|
||||||
|
store4(row, _mm_packus_epi16(d,d));
|
||||||
|
|
||||||
|
prev += 4;
|
||||||
|
row += 4;
|
||||||
|
rb -= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
|
||||||
|
#endif /* READ */
|
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
/* intel_init.c - SSE2 optimized filter functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 2016-2017 Glenn Randers-Pehrson
|
||||||
|
* Written by Mike Klein and Matt Sarett, Google, Inc.
|
||||||
|
* Derived from arm/arm_init.c
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pngpriv.h"
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
#if PNG_INTEL_SSE_IMPLEMENTATION > 0
|
||||||
|
|
||||||
|
void
|
||||||
|
png_init_filter_functions_sse2(png_structp pp, unsigned int bpp)
|
||||||
|
{
|
||||||
|
/* The techniques used to implement each of these filters in SSE operate on
|
||||||
|
* one pixel at a time.
|
||||||
|
* So they generally speed up 3bpp images about 3x, 4bpp images about 4x.
|
||||||
|
* They can scale up to 6 and 8 bpp images and down to 2 bpp images,
|
||||||
|
* but they'd not likely have any benefit for 1bpp images.
|
||||||
|
* Most of these can be implemented using only MMX and 64-bit registers,
|
||||||
|
* but they end up a bit slower than using the equally-ubiquitous SSE2.
|
||||||
|
*/
|
||||||
|
png_debug(1, "in png_init_filter_functions_sse2");
|
||||||
|
if (bpp == 3)
|
||||||
|
{
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2;
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2;
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
|
||||||
|
png_read_filter_row_paeth3_sse2;
|
||||||
|
}
|
||||||
|
else if (bpp == 4)
|
||||||
|
{
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2;
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2;
|
||||||
|
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
|
||||||
|
png_read_filter_row_paeth4_sse2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No need optimize PNG_FILTER_VALUE_UP. The compiler should
|
||||||
|
* autovectorize.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
|
||||||
|
#endif /* PNG_READ_SUPPORTED */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,623 @@
|
||||||
|
|
||||||
|
/* pngconf.h - machine-configurable file for libpng
|
||||||
|
*
|
||||||
|
* libpng version 1.6.37
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-2019 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*
|
||||||
|
* Any machine specific code is near the front of this file, so if you
|
||||||
|
* are configuring libpng for a machine, you may want to read the section
|
||||||
|
* starting here down to where it starts to typedef png_color, png_text,
|
||||||
|
* and png_info.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PNGCONF_H
|
||||||
|
#define PNGCONF_H
|
||||||
|
|
||||||
|
#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */
|
||||||
|
|
||||||
|
/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C
|
||||||
|
* compiler for correct compilation. The following header files are required by
|
||||||
|
* the standard. If your compiler doesn't provide these header files, or they
|
||||||
|
* do not match the standard, you will need to provide/improve them.
|
||||||
|
*/
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* Library header files. These header files are all defined by ISOC90; libpng
|
||||||
|
* expects conformant implementations, however, an ISOC90 conformant system need
|
||||||
|
* not provide these header files if the functionality cannot be implemented.
|
||||||
|
* In this case it will be necessary to disable the relevant parts of libpng in
|
||||||
|
* the build of pnglibconf.h.
|
||||||
|
*
|
||||||
|
* Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not
|
||||||
|
* include this unnecessary header file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef PNG_STDIO_SUPPORTED
|
||||||
|
/* Required for the definition of FILE: */
|
||||||
|
# include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_SETJMP_SUPPORTED
|
||||||
|
/* Required for the definition of jmp_buf and the declaration of longjmp: */
|
||||||
|
# include <setjmp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_CONVERT_tIME_SUPPORTED
|
||||||
|
/* Required for struct tm: */
|
||||||
|
# include <time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* PNG_BUILDING_SYMBOL_TABLE */
|
||||||
|
|
||||||
|
/* Prior to 1.6.0, it was possible to turn off 'const' in declarations,
|
||||||
|
* using PNG_NO_CONST. This is no longer supported.
|
||||||
|
*/
|
||||||
|
#define PNG_CONST const /* backward compatibility only */
|
||||||
|
|
||||||
|
/* This controls optimization of the reading of 16-bit and 32-bit
|
||||||
|
* values from PNG files. It can be set on a per-app-file basis: it
|
||||||
|
* just changes whether a macro is used when the function is called.
|
||||||
|
* The library builder sets the default; if read functions are not
|
||||||
|
* built into the library the macro implementation is forced on.
|
||||||
|
*/
|
||||||
|
#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED
|
||||||
|
# define PNG_USE_READ_MACROS
|
||||||
|
#endif
|
||||||
|
#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS)
|
||||||
|
# if PNG_DEFAULT_READ_MACROS
|
||||||
|
# define PNG_USE_READ_MACROS
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* COMPILER SPECIFIC OPTIONS.
|
||||||
|
*
|
||||||
|
* These options are provided so that a variety of difficult compilers
|
||||||
|
* can be used. Some are fixed at build time (e.g. PNG_API_RULE
|
||||||
|
* below) but still have compiler specific implementations, others
|
||||||
|
* may be changed on a per-file basis when compiling against libpng.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect
|
||||||
|
* against legacy (pre ISOC90) compilers that did not understand function
|
||||||
|
* prototypes. It is not required for modern C compilers.
|
||||||
|
*/
|
||||||
|
#ifndef PNGARG
|
||||||
|
# define PNGARG(arglist) arglist
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Function calling conventions.
|
||||||
|
* =============================
|
||||||
|
* Normally it is not necessary to specify to the compiler how to call
|
||||||
|
* a function - it just does it - however on x86 systems derived from
|
||||||
|
* Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems
|
||||||
|
* and some others) there are multiple ways to call a function and the
|
||||||
|
* default can be changed on the compiler command line. For this reason
|
||||||
|
* libpng specifies the calling convention of every exported function and
|
||||||
|
* every function called via a user supplied function pointer. This is
|
||||||
|
* done in this file by defining the following macros:
|
||||||
|
*
|
||||||
|
* PNGAPI Calling convention for exported functions.
|
||||||
|
* PNGCBAPI Calling convention for user provided (callback) functions.
|
||||||
|
* PNGCAPI Calling convention used by the ANSI-C library (required
|
||||||
|
* for longjmp callbacks and sometimes used internally to
|
||||||
|
* specify the calling convention for zlib).
|
||||||
|
*
|
||||||
|
* These macros should never be overridden. If it is necessary to
|
||||||
|
* change calling convention in a private build this can be done
|
||||||
|
* by setting PNG_API_RULE (which defaults to 0) to one of the values
|
||||||
|
* below to select the correct 'API' variants.
|
||||||
|
*
|
||||||
|
* PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout.
|
||||||
|
* This is correct in every known environment.
|
||||||
|
* PNG_API_RULE=1 Use the operating system convention for PNGAPI and
|
||||||
|
* the 'C' calling convention (from PNGCAPI) for
|
||||||
|
* callbacks (PNGCBAPI). This is no longer required
|
||||||
|
* in any known environment - if it has to be used
|
||||||
|
* please post an explanation of the problem to the
|
||||||
|
* libpng mailing list.
|
||||||
|
*
|
||||||
|
* These cases only differ if the operating system does not use the C
|
||||||
|
* calling convention, at present this just means the above cases
|
||||||
|
* (x86 DOS/Windows systems) and, even then, this does not apply to
|
||||||
|
* Cygwin running on those systems.
|
||||||
|
*
|
||||||
|
* Note that the value must be defined in pnglibconf.h so that what
|
||||||
|
* the application uses to call the library matches the conventions
|
||||||
|
* set when building the library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Symbol export
|
||||||
|
* =============
|
||||||
|
* When building a shared library it is almost always necessary to tell
|
||||||
|
* the compiler which symbols to export. The png.h macro 'PNG_EXPORT'
|
||||||
|
* is used to mark the symbols. On some systems these symbols can be
|
||||||
|
* extracted at link time and need no special processing by the compiler,
|
||||||
|
* on other systems the symbols are flagged by the compiler and just
|
||||||
|
* the declaration requires a special tag applied (unfortunately) in a
|
||||||
|
* compiler dependent way. Some systems can do either.
|
||||||
|
*
|
||||||
|
* A small number of older systems also require a symbol from a DLL to
|
||||||
|
* be flagged to the program that calls it. This is a problem because
|
||||||
|
* we do not know in the header file included by application code that
|
||||||
|
* the symbol will come from a shared library, as opposed to a statically
|
||||||
|
* linked one. For this reason the application must tell us by setting
|
||||||
|
* the magic flag PNG_USE_DLL to turn on the special processing before
|
||||||
|
* it includes png.h.
|
||||||
|
*
|
||||||
|
* Four additional macros are used to make this happen:
|
||||||
|
*
|
||||||
|
* PNG_IMPEXP The magic (if any) to cause a symbol to be exported from
|
||||||
|
* the build or imported if PNG_USE_DLL is set - compiler
|
||||||
|
* and system specific.
|
||||||
|
*
|
||||||
|
* PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to
|
||||||
|
* 'type', compiler specific.
|
||||||
|
*
|
||||||
|
* PNG_DLL_EXPORT Set to the magic to use during a libpng build to
|
||||||
|
* make a symbol exported from the DLL. Not used in the
|
||||||
|
* public header files; see pngpriv.h for how it is used
|
||||||
|
* in the libpng build.
|
||||||
|
*
|
||||||
|
* PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come
|
||||||
|
* from a DLL - used to define PNG_IMPEXP when
|
||||||
|
* PNG_USE_DLL is set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* System specific discovery.
|
||||||
|
* ==========================
|
||||||
|
* This code is used at build time to find PNG_IMPEXP, the API settings
|
||||||
|
* and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL
|
||||||
|
* import processing is possible. On Windows systems it also sets
|
||||||
|
* compiler-specific macros to the values required to change the calling
|
||||||
|
* conventions of the various functions.
|
||||||
|
*/
|
||||||
|
#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\
|
||||||
|
defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
|
||||||
|
/* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or
|
||||||
|
* MinGW on any architecture currently supported by Windows. Also includes
|
||||||
|
* Watcom builds but these need special treatment because they are not
|
||||||
|
* compatible with GCC or Visual C because of different calling conventions.
|
||||||
|
*/
|
||||||
|
# if PNG_API_RULE == 2
|
||||||
|
/* If this line results in an error, either because __watcall is not
|
||||||
|
* understood or because of a redefine just below you cannot use *this*
|
||||||
|
* build of the library with the compiler you are using. *This* build was
|
||||||
|
* build using Watcom and applications must also be built using Watcom!
|
||||||
|
*/
|
||||||
|
# define PNGCAPI __watcall
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800))
|
||||||
|
# define PNGCAPI __cdecl
|
||||||
|
# if PNG_API_RULE == 1
|
||||||
|
/* If this line results in an error __stdcall is not understood and
|
||||||
|
* PNG_API_RULE should not have been set to '1'.
|
||||||
|
*/
|
||||||
|
# define PNGAPI __stdcall
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
/* An older compiler, or one not detected (erroneously) above,
|
||||||
|
* if necessary override on the command line to get the correct
|
||||||
|
* variants for the compiler.
|
||||||
|
*/
|
||||||
|
# ifndef PNGCAPI
|
||||||
|
# define PNGCAPI _cdecl
|
||||||
|
# endif
|
||||||
|
# if PNG_API_RULE == 1 && !defined(PNGAPI)
|
||||||
|
# define PNGAPI _stdcall
|
||||||
|
# endif
|
||||||
|
# endif /* compiler/api */
|
||||||
|
|
||||||
|
/* NOTE: PNGCBAPI always defaults to PNGCAPI. */
|
||||||
|
|
||||||
|
# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD)
|
||||||
|
# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed"
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if (defined(_MSC_VER) && _MSC_VER < 800) ||\
|
||||||
|
(defined(__BORLANDC__) && __BORLANDC__ < 0x500)
|
||||||
|
/* older Borland and MSC
|
||||||
|
* compilers used '__export' and required this to be after
|
||||||
|
* the type.
|
||||||
|
*/
|
||||||
|
# ifndef PNG_EXPORT_TYPE
|
||||||
|
# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP
|
||||||
|
# endif
|
||||||
|
# define PNG_DLL_EXPORT __export
|
||||||
|
# else /* newer compiler */
|
||||||
|
# define PNG_DLL_EXPORT __declspec(dllexport)
|
||||||
|
# ifndef PNG_DLL_IMPORT
|
||||||
|
# define PNG_DLL_IMPORT __declspec(dllimport)
|
||||||
|
# endif
|
||||||
|
# endif /* compiler */
|
||||||
|
|
||||||
|
#else /* !Windows */
|
||||||
|
# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
|
||||||
|
# define PNGAPI _System
|
||||||
|
# else /* !Windows/x86 && !OS/2 */
|
||||||
|
/* Use the defaults, or define PNG*API on the command line (but
|
||||||
|
* this will have to be done for every compile!)
|
||||||
|
*/
|
||||||
|
# endif /* other system, !OS/2 */
|
||||||
|
#endif /* !Windows/x86 */
|
||||||
|
|
||||||
|
/* Now do all the defaulting . */
|
||||||
|
#ifndef PNGCAPI
|
||||||
|
# define PNGCAPI
|
||||||
|
#endif
|
||||||
|
#ifndef PNGCBAPI
|
||||||
|
# define PNGCBAPI PNGCAPI
|
||||||
|
#endif
|
||||||
|
#ifndef PNGAPI
|
||||||
|
# define PNGAPI PNGCAPI
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* PNG_IMPEXP may be set on the compilation system command line or (if not set)
|
||||||
|
* then in an internal header file when building the library, otherwise (when
|
||||||
|
* using the library) it is set here.
|
||||||
|
*/
|
||||||
|
#ifndef PNG_IMPEXP
|
||||||
|
# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)
|
||||||
|
/* This forces use of a DLL, disallowing static linking */
|
||||||
|
# define PNG_IMPEXP PNG_DLL_IMPORT
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef PNG_IMPEXP
|
||||||
|
# define PNG_IMPEXP
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat
|
||||||
|
* 'attributes' as a storage class - the attributes go at the start of the
|
||||||
|
* function definition, and attributes are always appended regardless of the
|
||||||
|
* compiler. This considerably simplifies these macros but may cause problems
|
||||||
|
* if any compilers both need function attributes and fail to handle them as
|
||||||
|
* a storage class (this is unlikely.)
|
||||||
|
*/
|
||||||
|
#ifndef PNG_FUNCTION
|
||||||
|
# define PNG_FUNCTION(type, name, args, attributes) attributes type name args
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PNG_EXPORT_TYPE
|
||||||
|
# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The ordinal value is only relevant when preprocessing png.h for symbol
|
||||||
|
* table entries, so we discard it here. See the .dfn files in the
|
||||||
|
* scripts directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PNG_EXPORTA
|
||||||
|
# define PNG_EXPORTA(ordinal, type, name, args, attributes) \
|
||||||
|
PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \
|
||||||
|
PNG_LINKAGE_API attributes)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument,
|
||||||
|
* so make something non-empty to satisfy the requirement:
|
||||||
|
*/
|
||||||
|
#define PNG_EMPTY /*empty list*/
|
||||||
|
|
||||||
|
#define PNG_EXPORT(ordinal, type, name, args) \
|
||||||
|
PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY)
|
||||||
|
|
||||||
|
/* Use PNG_REMOVED to comment out a removed interface. */
|
||||||
|
#ifndef PNG_REMOVED
|
||||||
|
# define PNG_REMOVED(ordinal, type, name, args, attributes)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PNG_CALLBACK
|
||||||
|
# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Support for compiler specific function attributes. These are used
|
||||||
|
* so that where compiler support is available incorrect use of API
|
||||||
|
* functions in png.h will generate compiler warnings.
|
||||||
|
*
|
||||||
|
* Added at libpng-1.2.41.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PNG_NO_PEDANTIC_WARNINGS
|
||||||
|
# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED
|
||||||
|
# define PNG_PEDANTIC_WARNINGS_SUPPORTED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED
|
||||||
|
/* Support for compiler specific function attributes. These are used
|
||||||
|
* so that where compiler support is available, incorrect use of API
|
||||||
|
* functions in png.h will generate compiler warnings. Added at libpng
|
||||||
|
* version 1.2.41. Disabling these removes the warnings but may also produce
|
||||||
|
* less efficient code.
|
||||||
|
*/
|
||||||
|
# if defined(__clang__) && defined(__has_attribute)
|
||||||
|
/* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */
|
||||||
|
# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__)
|
||||||
|
# define PNG_USE_RESULT __attribute__((__warn_unused_result__))
|
||||||
|
# endif
|
||||||
|
# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__)
|
||||||
|
# define PNG_NORETURN __attribute__((__noreturn__))
|
||||||
|
# endif
|
||||||
|
# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__)
|
||||||
|
# define PNG_ALLOCATED __attribute__((__malloc__))
|
||||||
|
# endif
|
||||||
|
# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__)
|
||||||
|
# define PNG_DEPRECATED __attribute__((__deprecated__))
|
||||||
|
# endif
|
||||||
|
# if !defined(PNG_PRIVATE)
|
||||||
|
# ifdef __has_extension
|
||||||
|
# if __has_extension(attribute_unavailable_with_message)
|
||||||
|
# define PNG_PRIVATE __attribute__((__unavailable__(\
|
||||||
|
"This function is not exported by libpng.")))
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_RESTRICT
|
||||||
|
# define PNG_RESTRICT __restrict
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# ifndef PNG_USE_RESULT
|
||||||
|
# define PNG_USE_RESULT __attribute__((__warn_unused_result__))
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_NORETURN
|
||||||
|
# define PNG_NORETURN __attribute__((__noreturn__))
|
||||||
|
# endif
|
||||||
|
# if __GNUC__ >= 3
|
||||||
|
# ifndef PNG_ALLOCATED
|
||||||
|
# define PNG_ALLOCATED __attribute__((__malloc__))
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_DEPRECATED
|
||||||
|
# define PNG_DEPRECATED __attribute__((__deprecated__))
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_PRIVATE
|
||||||
|
# if 0 /* Doesn't work so we use deprecated instead*/
|
||||||
|
# define PNG_PRIVATE \
|
||||||
|
__attribute__((warning("This function is not exported by libpng.")))
|
||||||
|
# else
|
||||||
|
# define PNG_PRIVATE \
|
||||||
|
__attribute__((__deprecated__))
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1))
|
||||||
|
# ifndef PNG_RESTRICT
|
||||||
|
# define PNG_RESTRICT __restrict
|
||||||
|
# endif
|
||||||
|
# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */
|
||||||
|
# endif /* __GNUC__ >= 3 */
|
||||||
|
|
||||||
|
# elif defined(_MSC_VER) && (_MSC_VER >= 1300)
|
||||||
|
# ifndef PNG_USE_RESULT
|
||||||
|
# define PNG_USE_RESULT /* not supported */
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_NORETURN
|
||||||
|
# define PNG_NORETURN __declspec(noreturn)
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_ALLOCATED
|
||||||
|
# if (_MSC_VER >= 1400)
|
||||||
|
# define PNG_ALLOCATED __declspec(restrict)
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_DEPRECATED
|
||||||
|
# define PNG_DEPRECATED __declspec(deprecated)
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_PRIVATE
|
||||||
|
# define PNG_PRIVATE __declspec(deprecated)
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_RESTRICT
|
||||||
|
# if (_MSC_VER >= 1400)
|
||||||
|
# define PNG_RESTRICT __restrict
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# elif defined(__WATCOMC__)
|
||||||
|
# ifndef PNG_RESTRICT
|
||||||
|
# define PNG_RESTRICT __restrict
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif /* PNG_PEDANTIC_WARNINGS */
|
||||||
|
|
||||||
|
#ifndef PNG_DEPRECATED
|
||||||
|
# define PNG_DEPRECATED /* Use of this function is deprecated */
|
||||||
|
#endif
|
||||||
|
#ifndef PNG_USE_RESULT
|
||||||
|
# define PNG_USE_RESULT /* The result of this function must be checked */
|
||||||
|
#endif
|
||||||
|
#ifndef PNG_NORETURN
|
||||||
|
# define PNG_NORETURN /* This function does not return */
|
||||||
|
#endif
|
||||||
|
#ifndef PNG_ALLOCATED
|
||||||
|
# define PNG_ALLOCATED /* The result of the function is new memory */
|
||||||
|
#endif
|
||||||
|
#ifndef PNG_PRIVATE
|
||||||
|
# define PNG_PRIVATE /* This is a private libpng function */
|
||||||
|
#endif
|
||||||
|
#ifndef PNG_RESTRICT
|
||||||
|
# define PNG_RESTRICT /* The C99 "restrict" feature */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PNG_FP_EXPORT /* A floating point API. */
|
||||||
|
# ifdef PNG_FLOATING_POINT_SUPPORTED
|
||||||
|
# define PNG_FP_EXPORT(ordinal, type, name, args)\
|
||||||
|
PNG_EXPORT(ordinal, type, name, args);
|
||||||
|
# else /* No floating point APIs */
|
||||||
|
# define PNG_FP_EXPORT(ordinal, type, name, args)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
#ifndef PNG_FIXED_EXPORT /* A fixed point API. */
|
||||||
|
# ifdef PNG_FIXED_POINT_SUPPORTED
|
||||||
|
# define PNG_FIXED_EXPORT(ordinal, type, name, args)\
|
||||||
|
PNG_EXPORT(ordinal, type, name, args);
|
||||||
|
# else /* No fixed point APIs */
|
||||||
|
# define PNG_FIXED_EXPORT(ordinal, type, name, args)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PNG_BUILDING_SYMBOL_TABLE
|
||||||
|
/* Some typedefs to get us started. These should be safe on most of the common
|
||||||
|
* platforms.
|
||||||
|
*
|
||||||
|
* png_uint_32 and png_int_32 may, currently, be larger than required to hold a
|
||||||
|
* 32-bit value however this is not normally advisable.
|
||||||
|
*
|
||||||
|
* png_uint_16 and png_int_16 should always be two bytes in size - this is
|
||||||
|
* verified at library build time.
|
||||||
|
*
|
||||||
|
* png_byte must always be one byte in size.
|
||||||
|
*
|
||||||
|
* The checks below use constants from limits.h, as defined by the ISOC90
|
||||||
|
* standard.
|
||||||
|
*/
|
||||||
|
#if CHAR_BIT == 8 && UCHAR_MAX == 255
|
||||||
|
typedef unsigned char png_byte;
|
||||||
|
#else
|
||||||
|
# error "libpng requires 8-bit bytes"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if INT_MIN == -32768 && INT_MAX == 32767
|
||||||
|
typedef int png_int_16;
|
||||||
|
#elif SHRT_MIN == -32768 && SHRT_MAX == 32767
|
||||||
|
typedef short png_int_16;
|
||||||
|
#else
|
||||||
|
# error "libpng requires a signed 16-bit type"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UINT_MAX == 65535
|
||||||
|
typedef unsigned int png_uint_16;
|
||||||
|
#elif USHRT_MAX == 65535
|
||||||
|
typedef unsigned short png_uint_16;
|
||||||
|
#else
|
||||||
|
# error "libpng requires an unsigned 16-bit type"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if INT_MIN < -2147483646 && INT_MAX > 2147483646
|
||||||
|
typedef int png_int_32;
|
||||||
|
#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646
|
||||||
|
typedef long int png_int_32;
|
||||||
|
#else
|
||||||
|
# error "libpng requires a signed 32-bit (or more) type"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UINT_MAX > 4294967294U
|
||||||
|
typedef unsigned int png_uint_32;
|
||||||
|
#elif ULONG_MAX > 4294967294U
|
||||||
|
typedef unsigned long int png_uint_32;
|
||||||
|
#else
|
||||||
|
# error "libpng requires an unsigned 32-bit (or more) type"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t.
|
||||||
|
* From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant
|
||||||
|
* behavior of sizeof and ptrdiff_t are required.
|
||||||
|
* The legacy typedefs are provided here for backwards compatibility.
|
||||||
|
*/
|
||||||
|
typedef size_t png_size_t;
|
||||||
|
typedef ptrdiff_t png_ptrdiff_t;
|
||||||
|
|
||||||
|
/* libpng needs to know the maximum value of 'size_t' and this controls the
|
||||||
|
* definition of png_alloc_size_t, below. This maximum value of size_t limits
|
||||||
|
* but does not control the maximum allocations the library makes - there is
|
||||||
|
* direct application control of this through png_set_user_limits().
|
||||||
|
*/
|
||||||
|
#ifndef PNG_SMALL_SIZE_T
|
||||||
|
/* Compiler specific tests for systems where size_t is known to be less than
|
||||||
|
* 32 bits (some of these systems may no longer work because of the lack of
|
||||||
|
* 'far' support; see above.)
|
||||||
|
*/
|
||||||
|
# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\
|
||||||
|
(defined(_MSC_VER) && defined(MAXSEG_64K))
|
||||||
|
# define PNG_SMALL_SIZE_T
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller
|
||||||
|
* than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are
|
||||||
|
* not necessary; in fact, it is recommended not to use them at all, so that
|
||||||
|
* the compiler can complain when something turns out to be problematic.
|
||||||
|
*
|
||||||
|
* Casts in the other direction (from png_alloc_size_t to size_t or
|
||||||
|
* png_uint_32) should be explicitly applied; however, we do not expect to
|
||||||
|
* encounter practical situations that require such conversions.
|
||||||
|
*
|
||||||
|
* PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than
|
||||||
|
* 4294967295 - i.e. less than the maximum value of png_uint_32.
|
||||||
|
*/
|
||||||
|
#ifdef PNG_SMALL_SIZE_T
|
||||||
|
typedef png_uint_32 png_alloc_size_t;
|
||||||
|
#else
|
||||||
|
typedef size_t png_alloc_size_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler
|
||||||
|
* implementations of Intel CPU specific support of user-mode segmented address
|
||||||
|
* spaces, where 16-bit pointers address more than 65536 bytes of memory using
|
||||||
|
* separate 'segment' registers. The implementation requires two different
|
||||||
|
* types of pointer (only one of which includes the segment value.)
|
||||||
|
*
|
||||||
|
* If required this support is available in version 1.2 of libpng and may be
|
||||||
|
* available in versions through 1.5, although the correctness of the code has
|
||||||
|
* not been verified recently.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Typedef for floating-point numbers that are converted to fixed-point with a
|
||||||
|
* multiple of 100,000, e.g., gamma
|
||||||
|
*/
|
||||||
|
typedef png_int_32 png_fixed_point;
|
||||||
|
|
||||||
|
/* Add typedefs for pointers */
|
||||||
|
typedef void * png_voidp;
|
||||||
|
typedef const void * png_const_voidp;
|
||||||
|
typedef png_byte * png_bytep;
|
||||||
|
typedef const png_byte * png_const_bytep;
|
||||||
|
typedef png_uint_32 * png_uint_32p;
|
||||||
|
typedef const png_uint_32 * png_const_uint_32p;
|
||||||
|
typedef png_int_32 * png_int_32p;
|
||||||
|
typedef const png_int_32 * png_const_int_32p;
|
||||||
|
typedef png_uint_16 * png_uint_16p;
|
||||||
|
typedef const png_uint_16 * png_const_uint_16p;
|
||||||
|
typedef png_int_16 * png_int_16p;
|
||||||
|
typedef const png_int_16 * png_const_int_16p;
|
||||||
|
typedef char * png_charp;
|
||||||
|
typedef const char * png_const_charp;
|
||||||
|
typedef png_fixed_point * png_fixed_point_p;
|
||||||
|
typedef const png_fixed_point * png_const_fixed_point_p;
|
||||||
|
typedef size_t * png_size_tp;
|
||||||
|
typedef const size_t * png_const_size_tp;
|
||||||
|
|
||||||
|
#ifdef PNG_STDIO_SUPPORTED
|
||||||
|
typedef FILE * png_FILE_p;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_FLOATING_POINT_SUPPORTED
|
||||||
|
typedef double * png_doublep;
|
||||||
|
typedef const double * png_const_doublep;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Pointers to pointers; i.e. arrays */
|
||||||
|
typedef png_byte * * png_bytepp;
|
||||||
|
typedef png_uint_32 * * png_uint_32pp;
|
||||||
|
typedef png_int_32 * * png_int_32pp;
|
||||||
|
typedef png_uint_16 * * png_uint_16pp;
|
||||||
|
typedef png_int_16 * * png_int_16pp;
|
||||||
|
typedef const char * * png_const_charpp;
|
||||||
|
typedef char * * png_charpp;
|
||||||
|
typedef png_fixed_point * * png_fixed_point_pp;
|
||||||
|
#ifdef PNG_FLOATING_POINT_SUPPORTED
|
||||||
|
typedef double * * png_doublepp;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Pointers to pointers to pointers; i.e., pointer to array */
|
||||||
|
typedef char * * * png_charppp;
|
||||||
|
|
||||||
|
#endif /* PNG_BUILDING_SYMBOL_TABLE */
|
||||||
|
|
||||||
|
#endif /* PNGCONF_H */
|
|
@ -0,0 +1,153 @@
|
||||||
|
|
||||||
|
/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Define PNG_DEBUG at compile time for debugging information. Higher
|
||||||
|
* numbers for PNG_DEBUG mean more debugging information. This has
|
||||||
|
* only been added since version 0.95 so it is not implemented throughout
|
||||||
|
* libpng yet, but more support will be added as needed.
|
||||||
|
*
|
||||||
|
* png_debug[1-2]?(level, message ,arg{0-2})
|
||||||
|
* Expands to a statement (either a simple expression or a compound
|
||||||
|
* do..while(0) statement) that outputs a message with parameter
|
||||||
|
* substitution if PNG_DEBUG is defined to 2 or more. If PNG_DEBUG
|
||||||
|
* is undefined, 0 or 1 every png_debug expands to a simple expression
|
||||||
|
* (actually ((void)0)).
|
||||||
|
*
|
||||||
|
* level: level of detail of message, starting at 0. A level 'n'
|
||||||
|
* message is preceded by 'n' 3-space indentations (not implemented
|
||||||
|
* on Microsoft compilers unless PNG_DEBUG_FILE is also
|
||||||
|
* defined, to allow debug DLL compilation with no standard IO).
|
||||||
|
* message: a printf(3) style text string. A trailing '\n' is added
|
||||||
|
* to the message.
|
||||||
|
* arg: 0 to 2 arguments for printf(3) style substitution in message.
|
||||||
|
*/
|
||||||
|
#ifndef PNGDEBUG_H
|
||||||
|
#define PNGDEBUG_H
|
||||||
|
/* These settings control the formatting of messages in png.c and pngerror.c */
|
||||||
|
/* Moved to pngdebug.h at 1.5.0 */
|
||||||
|
# ifndef PNG_LITERAL_SHARP
|
||||||
|
# define PNG_LITERAL_SHARP 0x23
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET
|
||||||
|
# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET
|
||||||
|
# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_STRING_NEWLINE
|
||||||
|
# define PNG_STRING_NEWLINE "\n"
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#ifdef PNG_DEBUG
|
||||||
|
# if (PNG_DEBUG > 0)
|
||||||
|
# if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)
|
||||||
|
# include <crtdbg.h>
|
||||||
|
# if (PNG_DEBUG > 1)
|
||||||
|
# ifndef _DEBUG
|
||||||
|
# define _DEBUG
|
||||||
|
# endif
|
||||||
|
# ifndef png_debug
|
||||||
|
# define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE)
|
||||||
|
# endif
|
||||||
|
# ifndef png_debug1
|
||||||
|
# define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1)
|
||||||
|
# endif
|
||||||
|
# ifndef png_debug2
|
||||||
|
# define png_debug2(l,m,p1,p2) \
|
||||||
|
_RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2)
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# else /* PNG_DEBUG_FILE || !_MSC_VER */
|
||||||
|
# ifndef PNG_STDIO_SUPPORTED
|
||||||
|
# include <stdio.h> /* not included yet */
|
||||||
|
# endif
|
||||||
|
# ifndef PNG_DEBUG_FILE
|
||||||
|
# define PNG_DEBUG_FILE stderr
|
||||||
|
# endif /* PNG_DEBUG_FILE */
|
||||||
|
|
||||||
|
# if (PNG_DEBUG > 1)
|
||||||
|
# ifdef __STDC__
|
||||||
|
# ifndef png_debug
|
||||||
|
# define png_debug(l,m) \
|
||||||
|
do { \
|
||||||
|
int num_tabs=l; \
|
||||||
|
fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \
|
||||||
|
(num_tabs==2 ? " " : (num_tabs>2 ? " " : "")))); \
|
||||||
|
} while (0)
|
||||||
|
# endif
|
||||||
|
# ifndef png_debug1
|
||||||
|
# define png_debug1(l,m,p1) \
|
||||||
|
do { \
|
||||||
|
int num_tabs=l; \
|
||||||
|
fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \
|
||||||
|
(num_tabs==2 ? " " : (num_tabs>2 ? " " : ""))),p1); \
|
||||||
|
} while (0)
|
||||||
|
# endif
|
||||||
|
# ifndef png_debug2
|
||||||
|
# define png_debug2(l,m,p1,p2) \
|
||||||
|
do { \
|
||||||
|
int num_tabs=l; \
|
||||||
|
fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \
|
||||||
|
(num_tabs==2 ? " " : (num_tabs>2 ? " " : ""))),p1,p2);\
|
||||||
|
} while (0)
|
||||||
|
# endif
|
||||||
|
# else /* __STDC __ */
|
||||||
|
# ifndef png_debug
|
||||||
|
# define png_debug(l,m) \
|
||||||
|
do { \
|
||||||
|
int num_tabs=l; \
|
||||||
|
char format[256]; \
|
||||||
|
snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
|
||||||
|
(num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
|
||||||
|
m,PNG_STRING_NEWLINE); \
|
||||||
|
fprintf(PNG_DEBUG_FILE,format); \
|
||||||
|
} while (0)
|
||||||
|
# endif
|
||||||
|
# ifndef png_debug1
|
||||||
|
# define png_debug1(l,m,p1) \
|
||||||
|
do { \
|
||||||
|
int num_tabs=l; \
|
||||||
|
char format[256]; \
|
||||||
|
snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
|
||||||
|
(num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
|
||||||
|
m,PNG_STRING_NEWLINE); \
|
||||||
|
fprintf(PNG_DEBUG_FILE,format,p1); \
|
||||||
|
} while (0)
|
||||||
|
# endif
|
||||||
|
# ifndef png_debug2
|
||||||
|
# define png_debug2(l,m,p1,p2) \
|
||||||
|
do { \
|
||||||
|
int num_tabs=l; \
|
||||||
|
char format[256]; \
|
||||||
|
snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
|
||||||
|
(num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
|
||||||
|
m,PNG_STRING_NEWLINE); \
|
||||||
|
fprintf(PNG_DEBUG_FILE,format,p1,p2); \
|
||||||
|
} while (0)
|
||||||
|
# endif
|
||||||
|
# endif /* __STDC __ */
|
||||||
|
# endif /* (PNG_DEBUG > 1) */
|
||||||
|
|
||||||
|
# endif /* _MSC_VER */
|
||||||
|
# endif /* (PNG_DEBUG > 0) */
|
||||||
|
#endif /* PNG_DEBUG */
|
||||||
|
#ifndef png_debug
|
||||||
|
# define png_debug(l, m) ((void)0)
|
||||||
|
#endif
|
||||||
|
#ifndef png_debug1
|
||||||
|
# define png_debug1(l, m, p1) ((void)0)
|
||||||
|
#endif
|
||||||
|
#ifndef png_debug2
|
||||||
|
# define png_debug2(l, m, p1, p2) ((void)0)
|
||||||
|
#endif
|
||||||
|
#endif /* PNGDEBUG_H */
|
|
@ -0,0 +1,963 @@
|
||||||
|
|
||||||
|
/* pngerror.c - stub functions for i/o and memory allocation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*
|
||||||
|
* This file provides a location for all error handling. Users who
|
||||||
|
* need special error handling are expected to write replacement functions
|
||||||
|
* and use png_set_error_fn() to use those functions. See the instructions
|
||||||
|
* at each function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pngpriv.h"
|
||||||
|
|
||||||
|
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
|
||||||
|
|
||||||
|
static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr,
|
||||||
|
png_const_charp error_message)),PNG_NORETURN);
|
||||||
|
|
||||||
|
#ifdef PNG_WARNINGS_SUPPORTED
|
||||||
|
static void /* PRIVATE */
|
||||||
|
png_default_warning PNGARG((png_const_structrp png_ptr,
|
||||||
|
png_const_charp warning_message));
|
||||||
|
#endif /* WARNINGS */
|
||||||
|
|
||||||
|
/* This function is called whenever there is a fatal error. This function
|
||||||
|
* should not be changed. If there is a need to handle errors differently,
|
||||||
|
* you should supply a replacement error function and use png_set_error_fn()
|
||||||
|
* to replace the error function at run-time.
|
||||||
|
*/
|
||||||
|
#ifdef PNG_ERROR_TEXT_SUPPORTED
|
||||||
|
PNG_FUNCTION(void,PNGAPI
|
||||||
|
png_error,(png_const_structrp png_ptr, png_const_charp error_message),
|
||||||
|
PNG_NORETURN)
|
||||||
|
{
|
||||||
|
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
|
||||||
|
char msg[16];
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
{
|
||||||
|
if ((png_ptr->flags &
|
||||||
|
(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0)
|
||||||
|
{
|
||||||
|
if (*error_message == PNG_LITERAL_SHARP)
|
||||||
|
{
|
||||||
|
/* Strip "#nnnn " from beginning of error message. */
|
||||||
|
int offset;
|
||||||
|
for (offset = 1; offset<15; offset++)
|
||||||
|
if (error_message[offset] == ' ')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < offset - 1; i++)
|
||||||
|
msg[i] = error_message[i + 1];
|
||||||
|
msg[i - 1] = '\0';
|
||||||
|
error_message = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
error_message += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
|
||||||
|
{
|
||||||
|
msg[0] = '0';
|
||||||
|
msg[1] = '\0';
|
||||||
|
error_message = msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (png_ptr != NULL && png_ptr->error_fn != NULL)
|
||||||
|
(*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr),
|
||||||
|
error_message);
|
||||||
|
|
||||||
|
/* If the custom handler doesn't exist, or if it returns,
|
||||||
|
use the default handler, which will not return. */
|
||||||
|
png_default_error(png_ptr, error_message);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PNG_FUNCTION(void,PNGAPI
|
||||||
|
png_err,(png_const_structrp png_ptr),PNG_NORETURN)
|
||||||
|
{
|
||||||
|
/* Prior to 1.5.2 the error_fn received a NULL pointer, expressed
|
||||||
|
* erroneously as '\0', instead of the empty string "". This was
|
||||||
|
* apparently an error, introduced in libpng-1.2.20, and png_default_error
|
||||||
|
* will crash in this case.
|
||||||
|
*/
|
||||||
|
if (png_ptr != NULL && png_ptr->error_fn != NULL)
|
||||||
|
(*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), "");
|
||||||
|
|
||||||
|
/* If the custom handler doesn't exist, or if it returns,
|
||||||
|
use the default handler, which will not return. */
|
||||||
|
png_default_error(png_ptr, "");
|
||||||
|
}
|
||||||
|
#endif /* ERROR_TEXT */
|
||||||
|
|
||||||
|
/* Utility to safely appends strings to a buffer. This never errors out so
|
||||||
|
* error checking is not required in the caller.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
png_safecat(png_charp buffer, size_t bufsize, size_t pos,
|
||||||
|
png_const_charp string)
|
||||||
|
{
|
||||||
|
if (buffer != NULL && pos < bufsize)
|
||||||
|
{
|
||||||
|
if (string != NULL)
|
||||||
|
while (*string != '\0' && pos < bufsize-1)
|
||||||
|
buffer[pos++] = *string++;
|
||||||
|
|
||||||
|
buffer[pos] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
|
||||||
|
/* Utility to dump an unsigned value into a buffer, given a start pointer and
|
||||||
|
* and end pointer (which should point just *beyond* the end of the buffer!)
|
||||||
|
* Returns the pointer to the start of the formatted string.
|
||||||
|
*/
|
||||||
|
png_charp
|
||||||
|
png_format_number(png_const_charp start, png_charp end, int format,
|
||||||
|
png_alloc_size_t number)
|
||||||
|
{
|
||||||
|
int count = 0; /* number of digits output */
|
||||||
|
int mincount = 1; /* minimum number required */
|
||||||
|
int output = 0; /* digit output (for the fixed point format) */
|
||||||
|
|
||||||
|
*--end = '\0';
|
||||||
|
|
||||||
|
/* This is written so that the loop always runs at least once, even with
|
||||||
|
* number zero.
|
||||||
|
*/
|
||||||
|
while (end > start && (number != 0 || count < mincount))
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char digits[] = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case PNG_NUMBER_FORMAT_fixed:
|
||||||
|
/* Needs five digits (the fraction) */
|
||||||
|
mincount = 5;
|
||||||
|
if (output != 0 || number % 10 != 0)
|
||||||
|
{
|
||||||
|
*--end = digits[number % 10];
|
||||||
|
output = 1;
|
||||||
|
}
|
||||||
|
number /= 10;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PNG_NUMBER_FORMAT_02u:
|
||||||
|
/* Expects at least 2 digits. */
|
||||||
|
mincount = 2;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
|
case PNG_NUMBER_FORMAT_u:
|
||||||
|
*--end = digits[number % 10];
|
||||||
|
number /= 10;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PNG_NUMBER_FORMAT_02x:
|
||||||
|
/* This format expects at least two digits */
|
||||||
|
mincount = 2;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
|
case PNG_NUMBER_FORMAT_x:
|
||||||
|
*--end = digits[number & 0xf];
|
||||||
|
number >>= 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: /* an error */
|
||||||
|
number = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep track of the number of digits added */
|
||||||
|
++count;
|
||||||
|
|
||||||
|
/* Float a fixed number here: */
|
||||||
|
if ((format == PNG_NUMBER_FORMAT_fixed) && (count == 5) && (end > start))
|
||||||
|
{
|
||||||
|
/* End of the fraction, but maybe nothing was output? In that case
|
||||||
|
* drop the decimal point. If the number is a true zero handle that
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
|
if (output != 0)
|
||||||
|
*--end = '.';
|
||||||
|
else if (number == 0) /* and !output */
|
||||||
|
*--end = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WARNINGS_SUPPORTED
|
||||||
|
/* This function is called whenever there is a non-fatal error. This function
|
||||||
|
* should not be changed. If there is a need to handle warnings differently,
|
||||||
|
* you should supply a replacement warning function and use
|
||||||
|
* png_set_error_fn() to replace the warning function at run-time.
|
||||||
|
*/
|
||||||
|
void PNGAPI
|
||||||
|
png_warning(png_const_structrp png_ptr, png_const_charp warning_message)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
{
|
||||||
|
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
|
||||||
|
if ((png_ptr->flags &
|
||||||
|
(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (*warning_message == PNG_LITERAL_SHARP)
|
||||||
|
{
|
||||||
|
for (offset = 1; offset < 15; offset++)
|
||||||
|
if (warning_message[offset] == ' ')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (png_ptr != NULL && png_ptr->warning_fn != NULL)
|
||||||
|
(*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr),
|
||||||
|
warning_message + offset);
|
||||||
|
else
|
||||||
|
png_default_warning(png_ptr, warning_message + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* These functions support 'formatted' warning messages with up to
|
||||||
|
* PNG_WARNING_PARAMETER_COUNT parameters. In the format string the parameter
|
||||||
|
* is introduced by @<number>, where 'number' starts at 1. This follows the
|
||||||
|
* standard established by X/Open for internationalizable error messages.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
png_warning_parameter(png_warning_parameters p, int number,
|
||||||
|
png_const_charp string)
|
||||||
|
{
|
||||||
|
if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)
|
||||||
|
(void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
|
||||||
|
png_alloc_size_t value)
|
||||||
|
{
|
||||||
|
char buffer[PNG_NUMBER_BUFFER_SIZE];
|
||||||
|
png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_warning_parameter_signed(png_warning_parameters p, int number, int format,
|
||||||
|
png_int_32 value)
|
||||||
|
{
|
||||||
|
png_alloc_size_t u;
|
||||||
|
png_charp str;
|
||||||
|
char buffer[PNG_NUMBER_BUFFER_SIZE];
|
||||||
|
|
||||||
|
/* Avoid overflow by doing the negate in a png_alloc_size_t: */
|
||||||
|
u = (png_alloc_size_t)value;
|
||||||
|
if (value < 0)
|
||||||
|
u = ~u + 1;
|
||||||
|
|
||||||
|
str = PNG_FORMAT_NUMBER(buffer, format, u);
|
||||||
|
|
||||||
|
if (value < 0 && str > buffer)
|
||||||
|
*--str = '-';
|
||||||
|
|
||||||
|
png_warning_parameter(p, number, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p,
|
||||||
|
png_const_charp message)
|
||||||
|
{
|
||||||
|
/* The internal buffer is just 192 bytes - enough for all our messages,
|
||||||
|
* overflow doesn't happen because this code checks! If someone figures
|
||||||
|
* out how to send us a message longer than 192 bytes, all that will
|
||||||
|
* happen is that the message will be truncated appropriately.
|
||||||
|
*/
|
||||||
|
size_t i = 0; /* Index in the msg[] buffer: */
|
||||||
|
char msg[192];
|
||||||
|
|
||||||
|
/* Each iteration through the following loop writes at most one character
|
||||||
|
* to msg[i++] then returns here to validate that there is still space for
|
||||||
|
* the trailing '\0'. It may (in the case of a parameter) read more than
|
||||||
|
* one character from message[]; it must check for '\0' and continue to the
|
||||||
|
* test if it finds the end of string.
|
||||||
|
*/
|
||||||
|
while (i<(sizeof msg)-1 && *message != '\0')
|
||||||
|
{
|
||||||
|
/* '@' at end of string is now just printed (previously it was skipped);
|
||||||
|
* it is an error in the calling code to terminate the string with @.
|
||||||
|
*/
|
||||||
|
if (p != NULL && *message == '@' && message[1] != '\0')
|
||||||
|
{
|
||||||
|
int parameter_char = *++message; /* Consume the '@' */
|
||||||
|
static const char valid_parameters[] = "123456789";
|
||||||
|
int parameter = 0;
|
||||||
|
|
||||||
|
/* Search for the parameter digit, the index in the string is the
|
||||||
|
* parameter to use.
|
||||||
|
*/
|
||||||
|
while (valid_parameters[parameter] != parameter_char &&
|
||||||
|
valid_parameters[parameter] != '\0')
|
||||||
|
++parameter;
|
||||||
|
|
||||||
|
/* If the parameter digit is out of range it will just get printed. */
|
||||||
|
if (parameter < PNG_WARNING_PARAMETER_COUNT)
|
||||||
|
{
|
||||||
|
/* Append this parameter */
|
||||||
|
png_const_charp parm = p[parameter];
|
||||||
|
png_const_charp pend = p[parameter] + (sizeof p[parameter]);
|
||||||
|
|
||||||
|
/* No need to copy the trailing '\0' here, but there is no guarantee
|
||||||
|
* that parm[] has been initialized, so there is no guarantee of a
|
||||||
|
* trailing '\0':
|
||||||
|
*/
|
||||||
|
while (i<(sizeof msg)-1 && *parm != '\0' && parm < pend)
|
||||||
|
msg[i++] = *parm++;
|
||||||
|
|
||||||
|
/* Consume the parameter digit too: */
|
||||||
|
++message;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* else not a parameter and there is a character after the @ sign; just
|
||||||
|
* copy that. This is known not to be '\0' because of the test above.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* At this point *message can't be '\0', even in the bad parameter case
|
||||||
|
* above where there is a lone '@' at the end of the message string.
|
||||||
|
*/
|
||||||
|
msg[i++] = *message++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* i is always less than (sizeof msg), so: */
|
||||||
|
msg[i] = '\0';
|
||||||
|
|
||||||
|
/* And this is the formatted message. It may be larger than
|
||||||
|
* PNG_MAX_ERROR_TEXT, but that is only used for 'chunk' errors and these
|
||||||
|
* are not (currently) formatted.
|
||||||
|
*/
|
||||||
|
png_warning(png_ptr, msg);
|
||||||
|
}
|
||||||
|
#endif /* WARNINGS */
|
||||||
|
|
||||||
|
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
|
||||||
|
void PNGAPI
|
||||||
|
png_benign_error(png_const_structrp png_ptr, png_const_charp error_message)
|
||||||
|
{
|
||||||
|
if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
|
||||||
|
{
|
||||||
|
# ifdef PNG_READ_SUPPORTED
|
||||||
|
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
|
||||||
|
png_ptr->chunk_name != 0)
|
||||||
|
png_chunk_warning(png_ptr, error_message);
|
||||||
|
else
|
||||||
|
# endif
|
||||||
|
png_warning(png_ptr, error_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# ifdef PNG_READ_SUPPORTED
|
||||||
|
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
|
||||||
|
png_ptr->chunk_name != 0)
|
||||||
|
png_chunk_error(png_ptr, error_message);
|
||||||
|
else
|
||||||
|
# endif
|
||||||
|
png_error(png_ptr, error_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
# ifndef PNG_ERROR_TEXT_SUPPORTED
|
||||||
|
PNG_UNUSED(error_message)
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_app_warning(png_const_structrp png_ptr, png_const_charp error_message)
|
||||||
|
{
|
||||||
|
if ((png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) != 0)
|
||||||
|
png_warning(png_ptr, error_message);
|
||||||
|
else
|
||||||
|
png_error(png_ptr, error_message);
|
||||||
|
|
||||||
|
# ifndef PNG_ERROR_TEXT_SUPPORTED
|
||||||
|
PNG_UNUSED(error_message)
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_app_error(png_const_structrp png_ptr, png_const_charp error_message)
|
||||||
|
{
|
||||||
|
if ((png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) != 0)
|
||||||
|
png_warning(png_ptr, error_message);
|
||||||
|
else
|
||||||
|
png_error(png_ptr, error_message);
|
||||||
|
|
||||||
|
# ifndef PNG_ERROR_TEXT_SUPPORTED
|
||||||
|
PNG_UNUSED(error_message)
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
#endif /* BENIGN_ERRORS */
|
||||||
|
|
||||||
|
#define PNG_MAX_ERROR_TEXT 196 /* Currently limited by profile_error in png.c */
|
||||||
|
#if defined(PNG_WARNINGS_SUPPORTED) || \
|
||||||
|
(defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED))
|
||||||
|
/* These utilities are used internally to build an error message that relates
|
||||||
|
* to the current chunk. The chunk name comes from png_ptr->chunk_name,
|
||||||
|
* which is used to prefix the message. The message is limited in length
|
||||||
|
* to 63 bytes. The name characters are output as hex digits wrapped in []
|
||||||
|
* if the character is invalid.
|
||||||
|
*/
|
||||||
|
#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
|
||||||
|
static const char png_digit[16] = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F'
|
||||||
|
};
|
||||||
|
|
||||||
|
static void /* PRIVATE */
|
||||||
|
png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
|
||||||
|
error_message)
|
||||||
|
{
|
||||||
|
png_uint_32 chunk_name = png_ptr->chunk_name;
|
||||||
|
int iout = 0, ishift = 24;
|
||||||
|
|
||||||
|
while (ishift >= 0)
|
||||||
|
{
|
||||||
|
int c = (int)(chunk_name >> ishift) & 0xff;
|
||||||
|
|
||||||
|
ishift -= 8;
|
||||||
|
if (isnonalpha(c) != 0)
|
||||||
|
{
|
||||||
|
buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET;
|
||||||
|
buffer[iout++] = png_digit[(c & 0xf0) >> 4];
|
||||||
|
buffer[iout++] = png_digit[c & 0x0f];
|
||||||
|
buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer[iout++] = (char)c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_message == NULL)
|
||||||
|
buffer[iout] = '\0';
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int iin = 0;
|
||||||
|
|
||||||
|
buffer[iout++] = ':';
|
||||||
|
buffer[iout++] = ' ';
|
||||||
|
|
||||||
|
while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0')
|
||||||
|
buffer[iout++] = error_message[iin++];
|
||||||
|
|
||||||
|
/* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */
|
||||||
|
buffer[iout] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* WARNINGS || ERROR_TEXT */
|
||||||
|
|
||||||
|
#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
|
||||||
|
PNG_FUNCTION(void,PNGAPI
|
||||||
|
png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message),
|
||||||
|
PNG_NORETURN)
|
||||||
|
{
|
||||||
|
char msg[18+PNG_MAX_ERROR_TEXT];
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
png_error(png_ptr, error_message);
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
png_format_buffer(png_ptr, msg, error_message);
|
||||||
|
png_error(png_ptr, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* READ && ERROR_TEXT */
|
||||||
|
|
||||||
|
#ifdef PNG_WARNINGS_SUPPORTED
|
||||||
|
void PNGAPI
|
||||||
|
png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)
|
||||||
|
{
|
||||||
|
char msg[18+PNG_MAX_ERROR_TEXT];
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
png_warning(png_ptr, warning_message);
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
png_format_buffer(png_ptr, msg, warning_message);
|
||||||
|
png_warning(png_ptr, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* WARNINGS */
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
|
||||||
|
void PNGAPI
|
||||||
|
png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp
|
||||||
|
error_message)
|
||||||
|
{
|
||||||
|
if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
|
||||||
|
png_chunk_warning(png_ptr, error_message);
|
||||||
|
|
||||||
|
else
|
||||||
|
png_chunk_error(png_ptr, error_message);
|
||||||
|
|
||||||
|
# ifndef PNG_ERROR_TEXT_SUPPORTED
|
||||||
|
PNG_UNUSED(error_message)
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* READ */
|
||||||
|
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
|
||||||
|
{
|
||||||
|
# ifndef PNG_WARNINGS_SUPPORTED
|
||||||
|
PNG_UNUSED(message)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* This is always supported, but for just read or just write it
|
||||||
|
* unconditionally does the right thing.
|
||||||
|
*/
|
||||||
|
# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
|
||||||
|
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef PNG_READ_SUPPORTED
|
||||||
|
{
|
||||||
|
if (error < PNG_CHUNK_ERROR)
|
||||||
|
png_chunk_warning(png_ptr, message);
|
||||||
|
|
||||||
|
else
|
||||||
|
png_chunk_benign_error(png_ptr, message);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
|
||||||
|
else if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef PNG_WRITE_SUPPORTED
|
||||||
|
{
|
||||||
|
if (error < PNG_CHUNK_WRITE_ERROR)
|
||||||
|
png_app_warning(png_ptr, message);
|
||||||
|
|
||||||
|
else
|
||||||
|
png_app_error(png_ptr, message);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_ERROR_TEXT_SUPPORTED
|
||||||
|
#ifdef PNG_FLOATING_POINT_SUPPORTED
|
||||||
|
PNG_FUNCTION(void,
|
||||||
|
png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN)
|
||||||
|
{
|
||||||
|
# define fixed_message "fixed point overflow in "
|
||||||
|
# define fixed_message_ln ((sizeof fixed_message)-1)
|
||||||
|
unsigned int iin;
|
||||||
|
char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT];
|
||||||
|
memcpy(msg, fixed_message, fixed_message_ln);
|
||||||
|
iin = 0;
|
||||||
|
if (name != NULL)
|
||||||
|
while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0)
|
||||||
|
{
|
||||||
|
msg[fixed_message_ln + iin] = name[iin];
|
||||||
|
++iin;
|
||||||
|
}
|
||||||
|
msg[fixed_message_ln + iin] = 0;
|
||||||
|
png_error(png_ptr, msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_SETJMP_SUPPORTED
|
||||||
|
/* This API only exists if ANSI-C style error handling is used,
|
||||||
|
* otherwise it is necessary for png_default_error to be overridden.
|
||||||
|
*/
|
||||||
|
jmp_buf* PNGAPI
|
||||||
|
png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn,
|
||||||
|
size_t jmp_buf_size)
|
||||||
|
{
|
||||||
|
/* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value
|
||||||
|
* and it must not change after that. Libpng doesn't care how big the
|
||||||
|
* buffer is, just that it doesn't change.
|
||||||
|
*
|
||||||
|
* If the buffer size is no *larger* than the size of jmp_buf when libpng is
|
||||||
|
* compiled a built in jmp_buf is returned; this preserves the pre-1.6.0
|
||||||
|
* semantics that this call will not fail. If the size is larger, however,
|
||||||
|
* the buffer is allocated and this may fail, causing the function to return
|
||||||
|
* NULL.
|
||||||
|
*/
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (png_ptr->jmp_buf_ptr == NULL)
|
||||||
|
{
|
||||||
|
png_ptr->jmp_buf_size = 0; /* not allocated */
|
||||||
|
|
||||||
|
if (jmp_buf_size <= (sizeof png_ptr->jmp_buf_local))
|
||||||
|
png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local;
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *,
|
||||||
|
png_malloc_warn(png_ptr, jmp_buf_size));
|
||||||
|
|
||||||
|
if (png_ptr->jmp_buf_ptr == NULL)
|
||||||
|
return NULL; /* new NULL return on OOM */
|
||||||
|
|
||||||
|
png_ptr->jmp_buf_size = jmp_buf_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else /* Already allocated: check the size */
|
||||||
|
{
|
||||||
|
size_t size = png_ptr->jmp_buf_size;
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
size = (sizeof png_ptr->jmp_buf_local);
|
||||||
|
if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local)
|
||||||
|
{
|
||||||
|
/* This is an internal error in libpng: somehow we have been left
|
||||||
|
* with a stack allocated jmp_buf when the application regained
|
||||||
|
* control. It's always possible to fix this up, but for the moment
|
||||||
|
* this is a png_error because that makes it easy to detect.
|
||||||
|
*/
|
||||||
|
png_error(png_ptr, "Libpng jmp_buf still allocated");
|
||||||
|
/* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size != jmp_buf_size)
|
||||||
|
{
|
||||||
|
png_warning(png_ptr, "Application jmp_buf size changed");
|
||||||
|
return NULL; /* caller will probably crash: no choice here */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally fill in the function, now we have a satisfactory buffer. It is
|
||||||
|
* valid to change the function on every call.
|
||||||
|
*/
|
||||||
|
png_ptr->longjmp_fn = longjmp_fn;
|
||||||
|
return png_ptr->jmp_buf_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_free_jmpbuf(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
{
|
||||||
|
jmp_buf *jb = png_ptr->jmp_buf_ptr;
|
||||||
|
|
||||||
|
/* A size of 0 is used to indicate a local, stack, allocation of the
|
||||||
|
* pointer; used here and in png.c
|
||||||
|
*/
|
||||||
|
if (jb != NULL && png_ptr->jmp_buf_size > 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* This stuff is so that a failure to free the error control structure
|
||||||
|
* does not leave libpng in a state with no valid error handling: the
|
||||||
|
* free always succeeds, if there is an error it gets ignored.
|
||||||
|
*/
|
||||||
|
if (jb != &png_ptr->jmp_buf_local)
|
||||||
|
{
|
||||||
|
/* Make an internal, libpng, jmp_buf to return here */
|
||||||
|
jmp_buf free_jmp_buf;
|
||||||
|
|
||||||
|
if (!setjmp(free_jmp_buf))
|
||||||
|
{
|
||||||
|
png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */
|
||||||
|
png_ptr->jmp_buf_size = 0; /* stack allocation */
|
||||||
|
png_ptr->longjmp_fn = longjmp;
|
||||||
|
png_free(png_ptr, jb); /* Return to setjmp on error */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *Always* cancel everything out: */
|
||||||
|
png_ptr->jmp_buf_size = 0;
|
||||||
|
png_ptr->jmp_buf_ptr = NULL;
|
||||||
|
png_ptr->longjmp_fn = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This is the default error handling function. Note that replacements for
|
||||||
|
* this function MUST NOT RETURN, or the program will likely crash. This
|
||||||
|
* function is used by default, or if the program supplies NULL for the
|
||||||
|
* error function pointer in png_set_error_fn().
|
||||||
|
*/
|
||||||
|
static PNG_FUNCTION(void /* PRIVATE */,
|
||||||
|
png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),
|
||||||
|
PNG_NORETURN)
|
||||||
|
{
|
||||||
|
#ifdef PNG_CONSOLE_IO_SUPPORTED
|
||||||
|
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
|
||||||
|
/* Check on NULL only added in 1.5.4 */
|
||||||
|
if (error_message != NULL && *error_message == PNG_LITERAL_SHARP)
|
||||||
|
{
|
||||||
|
/* Strip "#nnnn " from beginning of error message. */
|
||||||
|
int offset;
|
||||||
|
char error_number[16];
|
||||||
|
for (offset = 0; offset<15; offset++)
|
||||||
|
{
|
||||||
|
error_number[offset] = error_message[offset + 1];
|
||||||
|
if (error_message[offset] == ' ')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((offset > 1) && (offset < 15))
|
||||||
|
{
|
||||||
|
error_number[offset - 1] = '\0';
|
||||||
|
fprintf(stderr, "libpng error no. %s: %s",
|
||||||
|
error_number, error_message + offset + 1);
|
||||||
|
fprintf(stderr, PNG_STRING_NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "libpng error: %s, offset=%d",
|
||||||
|
error_message, offset);
|
||||||
|
fprintf(stderr, PNG_STRING_NEWLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
fprintf(stderr, "libpng error: %s", error_message ? error_message :
|
||||||
|
"undefined");
|
||||||
|
fprintf(stderr, PNG_STRING_NEWLINE);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PNG_UNUSED(error_message) /* Make compiler happy */
|
||||||
|
#endif
|
||||||
|
png_longjmp(png_ptr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
PNG_FUNCTION(void,PNGAPI
|
||||||
|
png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)
|
||||||
|
{
|
||||||
|
#ifdef PNG_SETJMP_SUPPORTED
|
||||||
|
if (png_ptr != NULL && png_ptr->longjmp_fn != NULL &&
|
||||||
|
png_ptr->jmp_buf_ptr != NULL)
|
||||||
|
png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val);
|
||||||
|
#else
|
||||||
|
PNG_UNUSED(png_ptr)
|
||||||
|
PNG_UNUSED(val)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If control reaches this point, png_longjmp() must not return. The only
|
||||||
|
* choice is to terminate the whole process (or maybe the thread); to do
|
||||||
|
* this the ANSI-C abort() function is used unless a different method is
|
||||||
|
* implemented by overriding the default configuration setting for
|
||||||
|
* PNG_ABORT().
|
||||||
|
*/
|
||||||
|
PNG_ABORT();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_WARNINGS_SUPPORTED
|
||||||
|
/* This function is called when there is a warning, but the library thinks
|
||||||
|
* it can continue anyway. Replacement functions don't have to do anything
|
||||||
|
* here if you don't want them to. In the default configuration, png_ptr is
|
||||||
|
* not used, but it is passed in case it may be useful.
|
||||||
|
*/
|
||||||
|
static void /* PRIVATE */
|
||||||
|
png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message)
|
||||||
|
{
|
||||||
|
#ifdef PNG_CONSOLE_IO_SUPPORTED
|
||||||
|
# ifdef PNG_ERROR_NUMBERS_SUPPORTED
|
||||||
|
if (*warning_message == PNG_LITERAL_SHARP)
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
char warning_number[16];
|
||||||
|
for (offset = 0; offset < 15; offset++)
|
||||||
|
{
|
||||||
|
warning_number[offset] = warning_message[offset + 1];
|
||||||
|
if (warning_message[offset] == ' ')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((offset > 1) && (offset < 15))
|
||||||
|
{
|
||||||
|
warning_number[offset + 1] = '\0';
|
||||||
|
fprintf(stderr, "libpng warning no. %s: %s",
|
||||||
|
warning_number, warning_message + offset);
|
||||||
|
fprintf(stderr, PNG_STRING_NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "libpng warning: %s",
|
||||||
|
warning_message);
|
||||||
|
fprintf(stderr, PNG_STRING_NEWLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# endif
|
||||||
|
|
||||||
|
{
|
||||||
|
fprintf(stderr, "libpng warning: %s", warning_message);
|
||||||
|
fprintf(stderr, PNG_STRING_NEWLINE);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PNG_UNUSED(warning_message) /* Make compiler happy */
|
||||||
|
#endif
|
||||||
|
PNG_UNUSED(png_ptr) /* Make compiler happy */
|
||||||
|
}
|
||||||
|
#endif /* WARNINGS */
|
||||||
|
|
||||||
|
/* This function is called when the application wants to use another method
|
||||||
|
* of handling errors and warnings. Note that the error function MUST NOT
|
||||||
|
* return to the calling routine or serious problems will occur. The return
|
||||||
|
* method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 1)
|
||||||
|
*/
|
||||||
|
void PNGAPI
|
||||||
|
png_set_error_fn(png_structrp png_ptr, png_voidp error_ptr,
|
||||||
|
png_error_ptr error_fn, png_error_ptr warning_fn)
|
||||||
|
{
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_ptr->error_ptr = error_ptr;
|
||||||
|
png_ptr->error_fn = error_fn;
|
||||||
|
#ifdef PNG_WARNINGS_SUPPORTED
|
||||||
|
png_ptr->warning_fn = warning_fn;
|
||||||
|
#else
|
||||||
|
PNG_UNUSED(warning_fn)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This function returns a pointer to the error_ptr associated with the user
|
||||||
|
* functions. The application should free any memory associated with this
|
||||||
|
* pointer before png_write_destroy and png_read_destroy are called.
|
||||||
|
*/
|
||||||
|
png_voidp PNGAPI
|
||||||
|
png_get_error_ptr(png_const_structrp png_ptr)
|
||||||
|
{
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return ((png_voidp)png_ptr->error_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
|
||||||
|
void PNGAPI
|
||||||
|
png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode)
|
||||||
|
{
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
{
|
||||||
|
png_ptr->flags &=
|
||||||
|
((~(PNG_FLAG_STRIP_ERROR_NUMBERS |
|
||||||
|
PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
|
||||||
|
defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
|
||||||
|
/* Currently the above both depend on SETJMP_SUPPORTED, however it would be
|
||||||
|
* possible to implement without setjmp support just so long as there is some
|
||||||
|
* way to handle the error return here:
|
||||||
|
*/
|
||||||
|
PNG_FUNCTION(void /* PRIVATE */, (PNGCBAPI
|
||||||
|
png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message),
|
||||||
|
PNG_NORETURN)
|
||||||
|
{
|
||||||
|
png_const_structrp png_ptr = png_nonconst_ptr;
|
||||||
|
png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
|
||||||
|
|
||||||
|
/* An error is always logged here, overwriting anything (typically a warning)
|
||||||
|
* that is already there:
|
||||||
|
*/
|
||||||
|
if (image != NULL)
|
||||||
|
{
|
||||||
|
png_safecat(image->message, (sizeof image->message), 0, error_message);
|
||||||
|
image->warning_or_error |= PNG_IMAGE_ERROR;
|
||||||
|
|
||||||
|
/* Retrieve the jmp_buf from within the png_control, making this work for
|
||||||
|
* C++ compilation too is pretty tricky: C++ wants a pointer to the first
|
||||||
|
* element of a jmp_buf, but C doesn't tell us the type of that.
|
||||||
|
*/
|
||||||
|
if (image->opaque != NULL && image->opaque->error_buf != NULL)
|
||||||
|
longjmp(png_control_jmp_buf(image->opaque), 1);
|
||||||
|
|
||||||
|
/* Missing longjmp buffer, the following is to help debugging: */
|
||||||
|
{
|
||||||
|
size_t pos = png_safecat(image->message, (sizeof image->message), 0,
|
||||||
|
"bad longjmp: ");
|
||||||
|
png_safecat(image->message, (sizeof image->message), pos,
|
||||||
|
error_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Here on an internal programming error. */
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_WARNINGS_SUPPORTED
|
||||||
|
void /* PRIVATE */ PNGCBAPI
|
||||||
|
png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message)
|
||||||
|
{
|
||||||
|
png_const_structrp png_ptr = png_nonconst_ptr;
|
||||||
|
png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
|
||||||
|
|
||||||
|
/* A warning is only logged if there is no prior warning or error. */
|
||||||
|
if (image->warning_or_error == 0)
|
||||||
|
{
|
||||||
|
png_safecat(image->message, (sizeof image->message), 0, warning_message);
|
||||||
|
image->warning_or_error |= PNG_IMAGE_WARNING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int /* PRIVATE */
|
||||||
|
png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg)
|
||||||
|
{
|
||||||
|
volatile png_imagep image = image_in;
|
||||||
|
volatile int result;
|
||||||
|
volatile png_voidp saved_error_buf;
|
||||||
|
jmp_buf safe_jmpbuf;
|
||||||
|
|
||||||
|
/* Safely execute function(arg) with png_error returning to this function. */
|
||||||
|
saved_error_buf = image->opaque->error_buf;
|
||||||
|
result = setjmp(safe_jmpbuf) == 0;
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
image->opaque->error_buf = safe_jmpbuf;
|
||||||
|
result = function(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
image->opaque->error_buf = saved_error_buf;
|
||||||
|
|
||||||
|
/* And do the cleanup prior to any failure return. */
|
||||||
|
if (result == 0)
|
||||||
|
png_image_free(image);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */
|
||||||
|
#endif /* READ || WRITE */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,267 @@
|
||||||
|
|
||||||
|
/* pnginfo.h - header file for PNG reference library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2013,2018 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* png_info is a structure that holds the information in a PNG file so
|
||||||
|
* that the application can find out the characteristics of the image.
|
||||||
|
* If you are reading the file, this structure will tell you what is
|
||||||
|
* in the PNG file. If you are writing the file, fill in the information
|
||||||
|
* you want to put into the PNG file, using png_set_*() functions, then
|
||||||
|
* call png_write_info().
|
||||||
|
*
|
||||||
|
* The names chosen should be very close to the PNG specification, so
|
||||||
|
* consult that document for information about the meaning of each field.
|
||||||
|
*
|
||||||
|
* With libpng < 0.95, it was only possible to directly set and read the
|
||||||
|
* the values in the png_info_struct, which meant that the contents and
|
||||||
|
* order of the values had to remain fixed. With libpng 0.95 and later,
|
||||||
|
* however, there are now functions that abstract the contents of
|
||||||
|
* png_info_struct from the application, so this makes it easier to use
|
||||||
|
* libpng with dynamic libraries, and even makes it possible to use
|
||||||
|
* libraries that don't have all of the libpng ancillary chunk-handing
|
||||||
|
* functionality. In libpng-1.5.0 this was moved into a separate private
|
||||||
|
* file that is not visible to applications.
|
||||||
|
*
|
||||||
|
* The following members may have allocated storage attached that should be
|
||||||
|
* cleaned up before the structure is discarded: palette, trans, text,
|
||||||
|
* pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,
|
||||||
|
* splt_palettes, scal_unit, row_pointers, and unknowns. By default, these
|
||||||
|
* are automatically freed when the info structure is deallocated, if they were
|
||||||
|
* allocated internally by libpng. This behavior can be changed by means
|
||||||
|
* of the png_data_freer() function.
|
||||||
|
*
|
||||||
|
* More allocation details: all the chunk-reading functions that
|
||||||
|
* change these members go through the corresponding png_set_*
|
||||||
|
* functions. A function to clear these members is available: see
|
||||||
|
* png_free_data(). The png_set_* functions do not depend on being
|
||||||
|
* able to point info structure members to any of the storage they are
|
||||||
|
* passed (they make their own copies), EXCEPT that the png_set_text
|
||||||
|
* functions use the same storage passed to them in the text_ptr or
|
||||||
|
* itxt_ptr structure argument, and the png_set_rows and png_set_unknowns
|
||||||
|
* functions do not make their own copies.
|
||||||
|
*/
|
||||||
|
#ifndef PNGINFO_H
|
||||||
|
#define PNGINFO_H
|
||||||
|
|
||||||
|
struct png_info_def
|
||||||
|
{
|
||||||
|
/* The following are necessary for every PNG file */
|
||||||
|
png_uint_32 width; /* width of image in pixels (from IHDR) */
|
||||||
|
png_uint_32 height; /* height of image in pixels (from IHDR) */
|
||||||
|
png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */
|
||||||
|
size_t rowbytes; /* bytes needed to hold an untransformed row */
|
||||||
|
png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */
|
||||||
|
png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
|
||||||
|
png_uint_16 num_trans; /* number of transparent palette color (tRNS) */
|
||||||
|
png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
|
||||||
|
png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */
|
||||||
|
/* The following three should have been named *_method not *_type */
|
||||||
|
png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
|
||||||
|
png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
|
||||||
|
png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
|
||||||
|
|
||||||
|
/* The following are set by png_set_IHDR, called from the application on
|
||||||
|
* write, but the are never actually used by the write code.
|
||||||
|
*/
|
||||||
|
png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */
|
||||||
|
png_byte pixel_depth; /* number of bits per pixel */
|
||||||
|
png_byte spare_byte; /* to align the data, and for future use */
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
/* This is never set during write */
|
||||||
|
png_byte signature[8]; /* magic bytes read by libpng from start of file */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The rest of the data is optional. If you are reading, check the
|
||||||
|
* valid field to see if the information in these are valid. If you
|
||||||
|
* are writing, set the valid field to those chunks you want written,
|
||||||
|
* and initialize the appropriate fields below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
|
||||||
|
/* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are
|
||||||
|
* defined. When COLORSPACE is switched on all the colorspace-defining
|
||||||
|
* chunks should be enabled, when GAMMA is switched on all the gamma-defining
|
||||||
|
* chunks should be enabled. If this is not done it becomes possible to read
|
||||||
|
* inconsistent PNG files and assign a probably incorrect interpretation to
|
||||||
|
* the information. (In other words, by carefully choosing which chunks to
|
||||||
|
* recognize the system configuration can select an interpretation for PNG
|
||||||
|
* files containing ambiguous data and this will result in inconsistent
|
||||||
|
* behavior between different libpng builds!)
|
||||||
|
*/
|
||||||
|
png_colorspace colorspace;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_iCCP_SUPPORTED
|
||||||
|
/* iCCP chunk data. */
|
||||||
|
png_charp iccp_name; /* profile name */
|
||||||
|
png_bytep iccp_profile; /* International Color Consortium profile data */
|
||||||
|
png_uint_32 iccp_proflen; /* ICC profile data length */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_TEXT_SUPPORTED
|
||||||
|
/* The tEXt, and zTXt chunks contain human-readable textual data in
|
||||||
|
* uncompressed, compressed, and optionally compressed forms, respectively.
|
||||||
|
* The data in "text" is an array of pointers to uncompressed,
|
||||||
|
* null-terminated C strings. Each chunk has a keyword that describes the
|
||||||
|
* textual data contained in that chunk. Keywords are not required to be
|
||||||
|
* unique, and the text string may be empty. Any number of text chunks may
|
||||||
|
* be in an image.
|
||||||
|
*/
|
||||||
|
int num_text; /* number of comments read or comments to write */
|
||||||
|
int max_text; /* current size of text array */
|
||||||
|
png_textp text; /* array of comments read or comments to write */
|
||||||
|
#endif /* TEXT */
|
||||||
|
|
||||||
|
#ifdef PNG_tIME_SUPPORTED
|
||||||
|
/* The tIME chunk holds the last time the displayed image data was
|
||||||
|
* modified. See the png_time struct for the contents of this struct.
|
||||||
|
*/
|
||||||
|
png_time mod_time;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_sBIT_SUPPORTED
|
||||||
|
/* The sBIT chunk specifies the number of significant high-order bits
|
||||||
|
* in the pixel data. Values are in the range [1, bit_depth], and are
|
||||||
|
* only specified for the channels in the pixel data. The contents of
|
||||||
|
* the low-order bits is not specified. Data is valid if
|
||||||
|
* (valid & PNG_INFO_sBIT) is non-zero.
|
||||||
|
*/
|
||||||
|
png_color_8 sig_bit; /* significant bits in color channels */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \
|
||||||
|
defined(PNG_READ_BACKGROUND_SUPPORTED)
|
||||||
|
/* The tRNS chunk supplies transparency data for paletted images and
|
||||||
|
* other image types that don't need a full alpha channel. There are
|
||||||
|
* "num_trans" transparency values for a paletted image, stored in the
|
||||||
|
* same order as the palette colors, starting from index 0. Values
|
||||||
|
* for the data are in the range [0, 255], ranging from fully transparent
|
||||||
|
* to fully opaque, respectively. For non-paletted images, there is a
|
||||||
|
* single color specified that should be treated as fully transparent.
|
||||||
|
* Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
|
||||||
|
*/
|
||||||
|
png_bytep trans_alpha; /* alpha values for paletted image */
|
||||||
|
png_color_16 trans_color; /* transparent color for non-palette image */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
|
||||||
|
/* The bKGD chunk gives the suggested image background color if the
|
||||||
|
* display program does not have its own background color and the image
|
||||||
|
* is needs to composited onto a background before display. The colors
|
||||||
|
* in "background" are normally in the same color space/depth as the
|
||||||
|
* pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
|
||||||
|
*/
|
||||||
|
png_color_16 background;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_oFFs_SUPPORTED
|
||||||
|
/* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
|
||||||
|
* and downwards from the top-left corner of the display, page, or other
|
||||||
|
* application-specific co-ordinate space. See the PNG_OFFSET_ defines
|
||||||
|
* below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero.
|
||||||
|
*/
|
||||||
|
png_int_32 x_offset; /* x offset on page */
|
||||||
|
png_int_32 y_offset; /* y offset on page */
|
||||||
|
png_byte offset_unit_type; /* offset units type */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_pHYs_SUPPORTED
|
||||||
|
/* The pHYs chunk gives the physical pixel density of the image for
|
||||||
|
* display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
|
||||||
|
* defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
|
||||||
|
*/
|
||||||
|
png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
|
||||||
|
png_uint_32 y_pixels_per_unit; /* vertical pixel density */
|
||||||
|
png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_eXIf_SUPPORTED
|
||||||
|
int num_exif; /* Added at libpng-1.6.31 */
|
||||||
|
png_bytep exif;
|
||||||
|
# ifdef PNG_READ_eXIf_SUPPORTED
|
||||||
|
png_bytep eXIf_buf; /* Added at libpng-1.6.32 */
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_hIST_SUPPORTED
|
||||||
|
/* The hIST chunk contains the relative frequency or importance of the
|
||||||
|
* various palette entries, so that a viewer can intelligently select a
|
||||||
|
* reduced-color palette, if required. Data is an array of "num_palette"
|
||||||
|
* values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
|
||||||
|
* is non-zero.
|
||||||
|
*/
|
||||||
|
png_uint_16p hist;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_pCAL_SUPPORTED
|
||||||
|
/* The pCAL chunk describes a transformation between the stored pixel
|
||||||
|
* values and original physical data values used to create the image.
|
||||||
|
* The integer range [0, 2^bit_depth - 1] maps to the floating-point
|
||||||
|
* range given by [pcal_X0, pcal_X1], and are further transformed by a
|
||||||
|
* (possibly non-linear) transformation function given by "pcal_type"
|
||||||
|
* and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_
|
||||||
|
* defines below, and the PNG-Group's PNG extensions document for a
|
||||||
|
* complete description of the transformations and how they should be
|
||||||
|
* implemented, and for a description of the ASCII parameter strings.
|
||||||
|
* Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
|
||||||
|
*/
|
||||||
|
png_charp pcal_purpose; /* pCAL chunk description string */
|
||||||
|
png_int_32 pcal_X0; /* minimum value */
|
||||||
|
png_int_32 pcal_X1; /* maximum value */
|
||||||
|
png_charp pcal_units; /* Latin-1 string giving physical units */
|
||||||
|
png_charpp pcal_params; /* ASCII strings containing parameter values */
|
||||||
|
png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */
|
||||||
|
png_byte pcal_nparams; /* number of parameters given in pcal_params */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New members added in libpng-1.0.6 */
|
||||||
|
png_uint_32 free_me; /* flags items libpng is responsible for freeing */
|
||||||
|
|
||||||
|
#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
/* Storage for unknown chunks that the library doesn't recognize. */
|
||||||
|
png_unknown_chunkp unknown_chunks;
|
||||||
|
|
||||||
|
/* The type of this field is limited by the type of
|
||||||
|
* png_struct::user_chunk_cache_max, else overflow can occur.
|
||||||
|
*/
|
||||||
|
int unknown_chunks_num;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_sPLT_SUPPORTED
|
||||||
|
/* Data on sPLT chunks (there may be more than one). */
|
||||||
|
png_sPLT_tp splt_palettes;
|
||||||
|
int splt_palettes_num; /* Match type returned by png_get API */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_sCAL_SUPPORTED
|
||||||
|
/* The sCAL chunk describes the actual physical dimensions of the
|
||||||
|
* subject matter of the graphic. The chunk contains a unit specification
|
||||||
|
* a byte value, and two ASCII strings representing floating-point
|
||||||
|
* values. The values are width and height corresponding to one pixel
|
||||||
|
* in the image. Data values are valid if (valid & PNG_INFO_sCAL) is
|
||||||
|
* non-zero.
|
||||||
|
*/
|
||||||
|
png_byte scal_unit; /* unit of physical scale */
|
||||||
|
png_charp scal_s_width; /* string containing height */
|
||||||
|
png_charp scal_s_height; /* string containing width */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_INFO_IMAGE_SUPPORTED
|
||||||
|
/* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS)
|
||||||
|
non-zero */
|
||||||
|
/* Data valid if (valid & PNG_INFO_IDAT) non-zero */
|
||||||
|
png_bytepp row_pointers; /* the image bits */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
#endif /* PNGINFO_H */
|
|
@ -0,0 +1,219 @@
|
||||||
|
/* pnglibconf.h - library build configuration */
|
||||||
|
|
||||||
|
/* libpng version 1.6.37 */
|
||||||
|
|
||||||
|
/* Copyright (c) 2018-2019 Cosmin Truta */
|
||||||
|
/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
|
||||||
|
|
||||||
|
/* This code is released under the libpng license. */
|
||||||
|
/* For conditions of distribution and use, see the disclaimer */
|
||||||
|
/* and license in png.h */
|
||||||
|
|
||||||
|
/* pnglibconf.h */
|
||||||
|
/* Machine generated file: DO NOT EDIT */
|
||||||
|
/* Derived from: scripts/pnglibconf.dfa */
|
||||||
|
#ifndef PNGLCONF_H
|
||||||
|
#define PNGLCONF_H
|
||||||
|
/* options */
|
||||||
|
#define PNG_16BIT_SUPPORTED
|
||||||
|
#define PNG_ALIGNED_MEMORY_SUPPORTED
|
||||||
|
/*#undef PNG_ARM_NEON_API_SUPPORTED*/
|
||||||
|
/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/
|
||||||
|
#define PNG_BENIGN_ERRORS_SUPPORTED
|
||||||
|
#define PNG_BENIGN_READ_ERRORS_SUPPORTED
|
||||||
|
/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
|
||||||
|
#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
|
||||||
|
#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
|
||||||
|
#define PNG_COLORSPACE_SUPPORTED
|
||||||
|
#define PNG_CONSOLE_IO_SUPPORTED
|
||||||
|
#define PNG_CONVERT_tIME_SUPPORTED
|
||||||
|
#define PNG_EASY_ACCESS_SUPPORTED
|
||||||
|
/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
|
||||||
|
#define PNG_ERROR_TEXT_SUPPORTED
|
||||||
|
#define PNG_FIXED_POINT_SUPPORTED
|
||||||
|
#define PNG_FLOATING_ARITHMETIC_SUPPORTED
|
||||||
|
#define PNG_FLOATING_POINT_SUPPORTED
|
||||||
|
#define PNG_FORMAT_AFIRST_SUPPORTED
|
||||||
|
#define PNG_FORMAT_BGR_SUPPORTED
|
||||||
|
#define PNG_GAMMA_SUPPORTED
|
||||||
|
#define PNG_GET_PALETTE_MAX_SUPPORTED
|
||||||
|
#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
|
||||||
|
#define PNG_INCH_CONVERSIONS_SUPPORTED
|
||||||
|
#define PNG_INFO_IMAGE_SUPPORTED
|
||||||
|
#define PNG_IO_STATE_SUPPORTED
|
||||||
|
#define PNG_MNG_FEATURES_SUPPORTED
|
||||||
|
#define PNG_POINTER_INDEXING_SUPPORTED
|
||||||
|
/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/
|
||||||
|
/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/
|
||||||
|
#define PNG_PROGRESSIVE_READ_SUPPORTED
|
||||||
|
#define PNG_READ_16BIT_SUPPORTED
|
||||||
|
#define PNG_READ_ALPHA_MODE_SUPPORTED
|
||||||
|
#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_READ_BACKGROUND_SUPPORTED
|
||||||
|
#define PNG_READ_BGR_SUPPORTED
|
||||||
|
#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
|
||||||
|
#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
|
||||||
|
#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
|
||||||
|
#define PNG_READ_EXPAND_16_SUPPORTED
|
||||||
|
#define PNG_READ_EXPAND_SUPPORTED
|
||||||
|
#define PNG_READ_FILLER_SUPPORTED
|
||||||
|
#define PNG_READ_GAMMA_SUPPORTED
|
||||||
|
#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
|
||||||
|
#define PNG_READ_GRAY_TO_RGB_SUPPORTED
|
||||||
|
#define PNG_READ_INTERLACING_SUPPORTED
|
||||||
|
#define PNG_READ_INT_FUNCTIONS_SUPPORTED
|
||||||
|
#define PNG_READ_INVERT_ALPHA_SUPPORTED
|
||||||
|
#define PNG_READ_INVERT_SUPPORTED
|
||||||
|
#define PNG_READ_OPT_PLTE_SUPPORTED
|
||||||
|
#define PNG_READ_PACKSWAP_SUPPORTED
|
||||||
|
#define PNG_READ_PACK_SUPPORTED
|
||||||
|
#define PNG_READ_QUANTIZE_SUPPORTED
|
||||||
|
#define PNG_READ_RGB_TO_GRAY_SUPPORTED
|
||||||
|
#define PNG_READ_SCALE_16_TO_8_SUPPORTED
|
||||||
|
#define PNG_READ_SHIFT_SUPPORTED
|
||||||
|
#define PNG_READ_STRIP_16_TO_8_SUPPORTED
|
||||||
|
#define PNG_READ_STRIP_ALPHA_SUPPORTED
|
||||||
|
#define PNG_READ_SUPPORTED
|
||||||
|
#define PNG_READ_SWAP_ALPHA_SUPPORTED
|
||||||
|
#define PNG_READ_SWAP_SUPPORTED
|
||||||
|
#define PNG_READ_TEXT_SUPPORTED
|
||||||
|
#define PNG_READ_TRANSFORMS_SUPPORTED
|
||||||
|
#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_READ_USER_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_READ_USER_TRANSFORM_SUPPORTED
|
||||||
|
#define PNG_READ_bKGD_SUPPORTED
|
||||||
|
#define PNG_READ_cHRM_SUPPORTED
|
||||||
|
#define PNG_READ_eXIf_SUPPORTED
|
||||||
|
#define PNG_READ_gAMA_SUPPORTED
|
||||||
|
#define PNG_READ_hIST_SUPPORTED
|
||||||
|
#define PNG_READ_iCCP_SUPPORTED
|
||||||
|
#define PNG_READ_iTXt_SUPPORTED
|
||||||
|
#define PNG_READ_oFFs_SUPPORTED
|
||||||
|
#define PNG_READ_pCAL_SUPPORTED
|
||||||
|
#define PNG_READ_pHYs_SUPPORTED
|
||||||
|
#define PNG_READ_sBIT_SUPPORTED
|
||||||
|
#define PNG_READ_sCAL_SUPPORTED
|
||||||
|
#define PNG_READ_sPLT_SUPPORTED
|
||||||
|
#define PNG_READ_sRGB_SUPPORTED
|
||||||
|
#define PNG_READ_tEXt_SUPPORTED
|
||||||
|
#define PNG_READ_tIME_SUPPORTED
|
||||||
|
#define PNG_READ_tRNS_SUPPORTED
|
||||||
|
#define PNG_READ_zTXt_SUPPORTED
|
||||||
|
#define PNG_SAVE_INT_32_SUPPORTED
|
||||||
|
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_SEQUENTIAL_READ_SUPPORTED
|
||||||
|
#define PNG_SETJMP_SUPPORTED
|
||||||
|
#define PNG_SET_OPTION_SUPPORTED
|
||||||
|
#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_SET_USER_LIMITS_SUPPORTED
|
||||||
|
#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
|
||||||
|
#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED
|
||||||
|
#define PNG_SIMPLIFIED_READ_SUPPORTED
|
||||||
|
#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
|
||||||
|
#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
|
||||||
|
#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
|
||||||
|
#define PNG_SIMPLIFIED_WRITE_SUPPORTED
|
||||||
|
#define PNG_STDIO_SUPPORTED
|
||||||
|
#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_TEXT_SUPPORTED
|
||||||
|
#define PNG_TIME_RFC1123_SUPPORTED
|
||||||
|
#define PNG_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_USER_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_USER_LIMITS_SUPPORTED
|
||||||
|
#define PNG_USER_MEM_SUPPORTED
|
||||||
|
#define PNG_USER_TRANSFORM_INFO_SUPPORTED
|
||||||
|
#define PNG_USER_TRANSFORM_PTR_SUPPORTED
|
||||||
|
#define PNG_WARNINGS_SUPPORTED
|
||||||
|
#define PNG_WRITE_16BIT_SUPPORTED
|
||||||
|
#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_WRITE_BGR_SUPPORTED
|
||||||
|
#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
|
||||||
|
#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
|
||||||
|
#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
|
||||||
|
#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
|
||||||
|
#define PNG_WRITE_FILLER_SUPPORTED
|
||||||
|
#define PNG_WRITE_FILTER_SUPPORTED
|
||||||
|
#define PNG_WRITE_FLUSH_SUPPORTED
|
||||||
|
#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED
|
||||||
|
#define PNG_WRITE_INTERLACING_SUPPORTED
|
||||||
|
#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
|
||||||
|
#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
|
||||||
|
#define PNG_WRITE_INVERT_SUPPORTED
|
||||||
|
#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
|
||||||
|
#define PNG_WRITE_PACKSWAP_SUPPORTED
|
||||||
|
#define PNG_WRITE_PACK_SUPPORTED
|
||||||
|
#define PNG_WRITE_SHIFT_SUPPORTED
|
||||||
|
#define PNG_WRITE_SUPPORTED
|
||||||
|
#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
|
||||||
|
#define PNG_WRITE_SWAP_SUPPORTED
|
||||||
|
#define PNG_WRITE_TEXT_SUPPORTED
|
||||||
|
#define PNG_WRITE_TRANSFORMS_SUPPORTED
|
||||||
|
#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
|
||||||
|
#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
|
||||||
|
#define PNG_WRITE_bKGD_SUPPORTED
|
||||||
|
#define PNG_WRITE_cHRM_SUPPORTED
|
||||||
|
#define PNG_WRITE_eXIf_SUPPORTED
|
||||||
|
#define PNG_WRITE_gAMA_SUPPORTED
|
||||||
|
#define PNG_WRITE_hIST_SUPPORTED
|
||||||
|
#define PNG_WRITE_iCCP_SUPPORTED
|
||||||
|
#define PNG_WRITE_iTXt_SUPPORTED
|
||||||
|
#define PNG_WRITE_oFFs_SUPPORTED
|
||||||
|
#define PNG_WRITE_pCAL_SUPPORTED
|
||||||
|
#define PNG_WRITE_pHYs_SUPPORTED
|
||||||
|
#define PNG_WRITE_sBIT_SUPPORTED
|
||||||
|
#define PNG_WRITE_sCAL_SUPPORTED
|
||||||
|
#define PNG_WRITE_sPLT_SUPPORTED
|
||||||
|
#define PNG_WRITE_sRGB_SUPPORTED
|
||||||
|
#define PNG_WRITE_tEXt_SUPPORTED
|
||||||
|
#define PNG_WRITE_tIME_SUPPORTED
|
||||||
|
#define PNG_WRITE_tRNS_SUPPORTED
|
||||||
|
#define PNG_WRITE_zTXt_SUPPORTED
|
||||||
|
#define PNG_bKGD_SUPPORTED
|
||||||
|
#define PNG_cHRM_SUPPORTED
|
||||||
|
#define PNG_eXIf_SUPPORTED
|
||||||
|
#define PNG_gAMA_SUPPORTED
|
||||||
|
#define PNG_hIST_SUPPORTED
|
||||||
|
#define PNG_iCCP_SUPPORTED
|
||||||
|
#define PNG_iTXt_SUPPORTED
|
||||||
|
#define PNG_oFFs_SUPPORTED
|
||||||
|
#define PNG_pCAL_SUPPORTED
|
||||||
|
#define PNG_pHYs_SUPPORTED
|
||||||
|
#define PNG_sBIT_SUPPORTED
|
||||||
|
#define PNG_sCAL_SUPPORTED
|
||||||
|
#define PNG_sPLT_SUPPORTED
|
||||||
|
#define PNG_sRGB_SUPPORTED
|
||||||
|
#define PNG_tEXt_SUPPORTED
|
||||||
|
#define PNG_tIME_SUPPORTED
|
||||||
|
#define PNG_tRNS_SUPPORTED
|
||||||
|
#define PNG_zTXt_SUPPORTED
|
||||||
|
/* end of options */
|
||||||
|
/* settings */
|
||||||
|
#define PNG_API_RULE 0
|
||||||
|
#define PNG_DEFAULT_READ_MACROS 1
|
||||||
|
#define PNG_GAMMA_THRESHOLD_FIXED 5000
|
||||||
|
#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
|
||||||
|
#define PNG_INFLATE_BUF_SIZE 1024
|
||||||
|
#define PNG_LINKAGE_API extern
|
||||||
|
#define PNG_LINKAGE_CALLBACK extern
|
||||||
|
#define PNG_LINKAGE_DATA extern
|
||||||
|
#define PNG_LINKAGE_FUNCTION extern
|
||||||
|
#define PNG_MAX_GAMMA_8 11
|
||||||
|
#define PNG_QUANTIZE_BLUE_BITS 5
|
||||||
|
#define PNG_QUANTIZE_GREEN_BITS 5
|
||||||
|
#define PNG_QUANTIZE_RED_BITS 5
|
||||||
|
#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
|
||||||
|
#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
|
||||||
|
#define PNG_USER_CHUNK_CACHE_MAX 1000
|
||||||
|
#define PNG_USER_CHUNK_MALLOC_MAX 8000000
|
||||||
|
#define PNG_USER_HEIGHT_MAX 1000000
|
||||||
|
#define PNG_USER_WIDTH_MAX 1000000
|
||||||
|
#define PNG_ZBUF_SIZE 8192
|
||||||
|
#define PNG_ZLIB_VERNUM 0 /* unknown */
|
||||||
|
#define PNG_Z_DEFAULT_COMPRESSION (-1)
|
||||||
|
#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
|
||||||
|
#define PNG_Z_DEFAULT_STRATEGY 1
|
||||||
|
#define PNG_sCAL_PRECISION 5
|
||||||
|
#define PNG_sRGB_PROFILE_CHECKS 2
|
||||||
|
/* end of settings */
|
||||||
|
#endif /* PNGLCONF_H */
|
|
@ -0,0 +1,284 @@
|
||||||
|
|
||||||
|
/* pngmem.c - stub functions for memory allocation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*
|
||||||
|
* This file provides a location for all memory allocation. Users who
|
||||||
|
* need special memory handling are expected to supply replacement
|
||||||
|
* functions for png_malloc() and png_free(), and to use
|
||||||
|
* png_create_read_struct_2() and png_create_write_struct_2() to
|
||||||
|
* identify the replacement functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pngpriv.h"
|
||||||
|
|
||||||
|
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
|
||||||
|
/* Free a png_struct */
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_destroy_png_struct(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
{
|
||||||
|
/* png_free might call png_error and may certainly call
|
||||||
|
* png_get_mem_ptr, so fake a temporary png_struct to support this.
|
||||||
|
*/
|
||||||
|
png_struct dummy_struct = *png_ptr;
|
||||||
|
memset(png_ptr, 0, (sizeof *png_ptr));
|
||||||
|
png_free(&dummy_struct, png_ptr);
|
||||||
|
|
||||||
|
# ifdef PNG_SETJMP_SUPPORTED
|
||||||
|
/* We may have a jmp_buf left to deallocate. */
|
||||||
|
png_free_jmpbuf(&dummy_struct);
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory. For reasonable files, size should never exceed
|
||||||
|
* 64K. However, zlib may allocate more than 64K if you don't tell
|
||||||
|
* it not to. See zconf.h and png.h for more information. zlib does
|
||||||
|
* need to allocate exactly 64K, so whatever you call here must
|
||||||
|
* have the ability to do that.
|
||||||
|
*/
|
||||||
|
PNG_FUNCTION(png_voidp,PNGAPI
|
||||||
|
png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
|
||||||
|
{
|
||||||
|
png_voidp ret;
|
||||||
|
|
||||||
|
ret = png_malloc(png_ptr, size);
|
||||||
|
|
||||||
|
if (ret != NULL)
|
||||||
|
memset(ret, 0, size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of
|
||||||
|
* allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED.
|
||||||
|
* Checking and error handling must happen outside this routine; it returns NULL
|
||||||
|
* if the allocation cannot be done (for any reason.)
|
||||||
|
*/
|
||||||
|
PNG_FUNCTION(png_voidp /* PRIVATE */,
|
||||||
|
png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
|
||||||
|
PNG_ALLOCATED)
|
||||||
|
{
|
||||||
|
/* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS
|
||||||
|
* allocators have also been removed in 1.6.0, so any 16-bit system now has
|
||||||
|
* to implement a user memory handler. This checks to be sure it isn't
|
||||||
|
* called with big numbers.
|
||||||
|
*/
|
||||||
|
#ifndef PNG_USER_MEM_SUPPORTED
|
||||||
|
PNG_UNUSED(png_ptr)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Some compilers complain that this is always true. However, it
|
||||||
|
* can be false when integer overflow happens.
|
||||||
|
*/
|
||||||
|
if (size > 0 && size <= PNG_SIZE_MAX
|
||||||
|
# ifdef PNG_MAX_MALLOC_64K
|
||||||
|
&& size <= 65536U
|
||||||
|
# endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
#ifdef PNG_USER_MEM_SUPPORTED
|
||||||
|
if (png_ptr != NULL && png_ptr->malloc_fn != NULL)
|
||||||
|
return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size);
|
||||||
|
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
return malloc((size_t)size); /* checked for truncation above */
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
|
||||||
|
defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)
|
||||||
|
/* This is really here only to work round a spurious warning in GCC 4.6 and 4.7
|
||||||
|
* that arises because of the checks in png_realloc_array that are repeated in
|
||||||
|
* png_malloc_array.
|
||||||
|
*/
|
||||||
|
static png_voidp
|
||||||
|
png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
|
||||||
|
size_t element_size)
|
||||||
|
{
|
||||||
|
png_alloc_size_t req = (png_alloc_size_t)nelements; /* known to be > 0 */
|
||||||
|
|
||||||
|
if (req <= PNG_SIZE_MAX/element_size)
|
||||||
|
return png_malloc_base(png_ptr, req * element_size);
|
||||||
|
|
||||||
|
/* The failure case when the request is too large */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PNG_FUNCTION(png_voidp /* PRIVATE */,
|
||||||
|
png_malloc_array,(png_const_structrp png_ptr, int nelements,
|
||||||
|
size_t element_size),PNG_ALLOCATED)
|
||||||
|
{
|
||||||
|
if (nelements <= 0 || element_size == 0)
|
||||||
|
png_error(png_ptr, "internal error: array alloc");
|
||||||
|
|
||||||
|
return png_malloc_array_checked(png_ptr, nelements, element_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
PNG_FUNCTION(png_voidp /* PRIVATE */,
|
||||||
|
png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
|
||||||
|
int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
|
||||||
|
{
|
||||||
|
/* These are internal errors: */
|
||||||
|
if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||
|
||||||
|
(old_array == NULL && old_elements > 0))
|
||||||
|
png_error(png_ptr, "internal error: array realloc");
|
||||||
|
|
||||||
|
/* Check for overflow on the elements count (so the caller does not have to
|
||||||
|
* check.)
|
||||||
|
*/
|
||||||
|
if (add_elements <= INT_MAX - old_elements)
|
||||||
|
{
|
||||||
|
png_voidp new_array = png_malloc_array_checked(png_ptr,
|
||||||
|
old_elements+add_elements, element_size);
|
||||||
|
|
||||||
|
if (new_array != NULL)
|
||||||
|
{
|
||||||
|
/* Because png_malloc_array worked the size calculations below cannot
|
||||||
|
* overflow.
|
||||||
|
*/
|
||||||
|
if (old_elements > 0)
|
||||||
|
memcpy(new_array, old_array, element_size*(unsigned)old_elements);
|
||||||
|
|
||||||
|
memset((char*)new_array + element_size*(unsigned)old_elements, 0,
|
||||||
|
element_size*(unsigned)add_elements);
|
||||||
|
|
||||||
|
return new_array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL; /* error */
|
||||||
|
}
|
||||||
|
#endif /* TEXT || sPLT || STORE_UNKNOWN_CHUNKS */
|
||||||
|
|
||||||
|
/* Various functions that have different error handling are derived from this.
|
||||||
|
* png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate
|
||||||
|
* function png_malloc_default is also provided.
|
||||||
|
*/
|
||||||
|
PNG_FUNCTION(png_voidp,PNGAPI
|
||||||
|
png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
|
||||||
|
{
|
||||||
|
png_voidp ret;
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret = png_malloc_base(png_ptr, size);
|
||||||
|
|
||||||
|
if (ret == NULL)
|
||||||
|
png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_USER_MEM_SUPPORTED
|
||||||
|
PNG_FUNCTION(png_voidp,PNGAPI
|
||||||
|
png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size),
|
||||||
|
PNG_ALLOCATED PNG_DEPRECATED)
|
||||||
|
{
|
||||||
|
png_voidp ret;
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Passing 'NULL' here bypasses the application provided memory handler. */
|
||||||
|
ret = png_malloc_base(NULL/*use malloc*/, size);
|
||||||
|
|
||||||
|
if (ret == NULL)
|
||||||
|
png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif /* USER_MEM */
|
||||||
|
|
||||||
|
/* This function was added at libpng version 1.2.3. The png_malloc_warn()
|
||||||
|
* function will issue a png_warning and return NULL instead of issuing a
|
||||||
|
* png_error, if it fails to allocate the requested memory.
|
||||||
|
*/
|
||||||
|
PNG_FUNCTION(png_voidp,PNGAPI
|
||||||
|
png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size),
|
||||||
|
PNG_ALLOCATED)
|
||||||
|
{
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
{
|
||||||
|
png_voidp ret = png_malloc_base(png_ptr, size);
|
||||||
|
|
||||||
|
if (ret != NULL)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
png_warning(png_ptr, "Out of memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free a pointer allocated by png_malloc(). If ptr is NULL, return
|
||||||
|
* without taking any action.
|
||||||
|
*/
|
||||||
|
void PNGAPI
|
||||||
|
png_free(png_const_structrp png_ptr, png_voidp ptr)
|
||||||
|
{
|
||||||
|
if (png_ptr == NULL || ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef PNG_USER_MEM_SUPPORTED
|
||||||
|
if (png_ptr->free_fn != NULL)
|
||||||
|
png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr);
|
||||||
|
|
||||||
|
else
|
||||||
|
png_free_default(png_ptr, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
PNG_FUNCTION(void,PNGAPI
|
||||||
|
png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)
|
||||||
|
{
|
||||||
|
if (png_ptr == NULL || ptr == NULL)
|
||||||
|
return;
|
||||||
|
#endif /* USER_MEM */
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_USER_MEM_SUPPORTED
|
||||||
|
/* This function is called when the application wants to use another method
|
||||||
|
* of allocating and freeing memory.
|
||||||
|
*/
|
||||||
|
void PNGAPI
|
||||||
|
png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr
|
||||||
|
malloc_fn, png_free_ptr free_fn)
|
||||||
|
{
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
{
|
||||||
|
png_ptr->mem_ptr = mem_ptr;
|
||||||
|
png_ptr->malloc_fn = malloc_fn;
|
||||||
|
png_ptr->free_fn = free_fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function returns a pointer to the mem_ptr associated with the user
|
||||||
|
* functions. The application should free any memory associated with this
|
||||||
|
* pointer before png_write_destroy and png_read_destroy are called.
|
||||||
|
*/
|
||||||
|
png_voidp PNGAPI
|
||||||
|
png_get_mem_ptr(png_const_structrp png_ptr)
|
||||||
|
{
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return png_ptr->mem_ptr;
|
||||||
|
}
|
||||||
|
#endif /* USER_MEM */
|
||||||
|
#endif /* READ || WRITE */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,120 @@
|
||||||
|
|
||||||
|
/* pngrio.c - functions for data input
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*
|
||||||
|
* This file provides a location for all input. Users who need
|
||||||
|
* special handling are expected to write a function that has the same
|
||||||
|
* arguments as this and performs a similar function, but that possibly
|
||||||
|
* has a different input method. Note that you shouldn't change this
|
||||||
|
* function, but rather write a replacement function and then make
|
||||||
|
* libpng use it at run time with png_set_read_fn(...).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pngpriv.h"
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
|
||||||
|
/* Read the data from whatever input you are using. The default routine
|
||||||
|
* reads from a file pointer. Note that this routine sometimes gets called
|
||||||
|
* with very small lengths, so you should implement some kind of simple
|
||||||
|
* buffering if you are using unbuffered reads. This should never be asked
|
||||||
|
* to read more than 64K on a 16-bit machine.
|
||||||
|
*/
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_read_data(png_structrp png_ptr, png_bytep data, size_t length)
|
||||||
|
{
|
||||||
|
png_debug1(4, "reading %d bytes", (int)length);
|
||||||
|
|
||||||
|
if (png_ptr->read_data_fn != NULL)
|
||||||
|
(*(png_ptr->read_data_fn))(png_ptr, data, length);
|
||||||
|
|
||||||
|
else
|
||||||
|
png_error(png_ptr, "Call to NULL read function");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_STDIO_SUPPORTED
|
||||||
|
/* This is the function that does the actual reading of data. If you are
|
||||||
|
* not reading from a standard C stream, you should create a replacement
|
||||||
|
* read_data function and use it at run time with png_set_read_fn(), rather
|
||||||
|
* than changing the library.
|
||||||
|
*/
|
||||||
|
void PNGCBAPI
|
||||||
|
png_default_read_data(png_structp png_ptr, png_bytep data, size_t length)
|
||||||
|
{
|
||||||
|
size_t check;
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* fread() returns 0 on error, so it is OK to store this in a size_t
|
||||||
|
* instead of an int, which is what fread() actually returns.
|
||||||
|
*/
|
||||||
|
check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr));
|
||||||
|
|
||||||
|
if (check != length)
|
||||||
|
png_error(png_ptr, "Read Error");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This function allows the application to supply a new input function
|
||||||
|
* for libpng if standard C streams aren't being used.
|
||||||
|
*
|
||||||
|
* This function takes as its arguments:
|
||||||
|
*
|
||||||
|
* png_ptr - pointer to a png input data structure
|
||||||
|
*
|
||||||
|
* io_ptr - pointer to user supplied structure containing info about
|
||||||
|
* the input functions. May be NULL.
|
||||||
|
*
|
||||||
|
* read_data_fn - pointer to a new input function that takes as its
|
||||||
|
* arguments a pointer to a png_struct, a pointer to
|
||||||
|
* a location where input data can be stored, and a 32-bit
|
||||||
|
* unsigned int that is the number of bytes to be read.
|
||||||
|
* To exit and output any fatal error messages the new write
|
||||||
|
* function should call png_error(png_ptr, "Error msg").
|
||||||
|
* May be NULL, in which case libpng's default function will
|
||||||
|
* be used.
|
||||||
|
*/
|
||||||
|
void PNGAPI
|
||||||
|
png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr,
|
||||||
|
png_rw_ptr read_data_fn)
|
||||||
|
{
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_ptr->io_ptr = io_ptr;
|
||||||
|
|
||||||
|
#ifdef PNG_STDIO_SUPPORTED
|
||||||
|
if (read_data_fn != NULL)
|
||||||
|
png_ptr->read_data_fn = read_data_fn;
|
||||||
|
|
||||||
|
else
|
||||||
|
png_ptr->read_data_fn = png_default_read_data;
|
||||||
|
#else
|
||||||
|
png_ptr->read_data_fn = read_data_fn;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SUPPORTED
|
||||||
|
/* It is an error to write to a read device */
|
||||||
|
if (png_ptr->write_data_fn != NULL)
|
||||||
|
{
|
||||||
|
png_ptr->write_data_fn = NULL;
|
||||||
|
png_warning(png_ptr,
|
||||||
|
"Can't set both read_data_fn and write_data_fn in the"
|
||||||
|
" same structure");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_FLUSH_SUPPORTED
|
||||||
|
png_ptr->output_flush_fn = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* READ */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,489 @@
|
||||||
|
|
||||||
|
/* pngstruct.h - header file for PNG reference library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-2019 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The structure that holds the information to read and write PNG files.
|
||||||
|
* The only people who need to care about what is inside of this are the
|
||||||
|
* people who will be modifying the library for their own special needs.
|
||||||
|
* It should NOT be accessed directly by an application.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PNGSTRUCT_H
|
||||||
|
#define PNGSTRUCT_H
|
||||||
|
/* zlib.h defines the structure z_stream, an instance of which is included
|
||||||
|
* in this structure and is required for decompressing the LZ compressed
|
||||||
|
* data in PNG files.
|
||||||
|
*/
|
||||||
|
#ifndef ZLIB_CONST
|
||||||
|
/* We must ensure that zlib uses 'const' in declarations. */
|
||||||
|
# define ZLIB_CONST
|
||||||
|
#endif
|
||||||
|
#include "zlib.h"
|
||||||
|
#ifdef const
|
||||||
|
/* zlib.h sometimes #defines const to nothing, undo this. */
|
||||||
|
# undef const
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
|
||||||
|
* with older builds.
|
||||||
|
*/
|
||||||
|
#if ZLIB_VERNUM < 0x1260
|
||||||
|
# define PNGZ_MSG_CAST(s) png_constcast(char*,s)
|
||||||
|
# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
|
||||||
|
#else
|
||||||
|
# define PNGZ_MSG_CAST(s) (s)
|
||||||
|
# define PNGZ_INPUT_CAST(b) (b)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
|
||||||
|
* can handle at once. This type need be no larger than 16 bits (so maximum of
|
||||||
|
* 65535), this define allows us to discover how big it is, but limited by the
|
||||||
|
* maximum for size_t. The value can be overridden in a library build
|
||||||
|
* (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
|
||||||
|
* lower value (e.g. 255 works). A lower value may help memory usage (slightly)
|
||||||
|
* and may even improve performance on some systems (and degrade it on others.)
|
||||||
|
*/
|
||||||
|
#ifndef ZLIB_IO_MAX
|
||||||
|
# define ZLIB_IO_MAX ((uInt)-1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SUPPORTED
|
||||||
|
/* The type of a compression buffer list used by the write code. */
|
||||||
|
typedef struct png_compression_buffer
|
||||||
|
{
|
||||||
|
struct png_compression_buffer *next;
|
||||||
|
png_byte output[1]; /* actually zbuf_size */
|
||||||
|
} png_compression_buffer, *png_compression_bufferp;
|
||||||
|
|
||||||
|
#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
|
||||||
|
(offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Colorspace support; structures used in png_struct, png_info and in internal
|
||||||
|
* functions to hold and communicate information about the color space.
|
||||||
|
*
|
||||||
|
* PNG_COLORSPACE_SUPPORTED is only required if the application will perform
|
||||||
|
* colorspace corrections, otherwise all the colorspace information can be
|
||||||
|
* skipped and the size of libpng can be reduced (significantly) by compiling
|
||||||
|
* out the colorspace support.
|
||||||
|
*/
|
||||||
|
#ifdef PNG_COLORSPACE_SUPPORTED
|
||||||
|
/* The chromaticities of the red, green and blue colorants and the chromaticity
|
||||||
|
* of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)).
|
||||||
|
*/
|
||||||
|
typedef struct png_xy
|
||||||
|
{
|
||||||
|
png_fixed_point redx, redy;
|
||||||
|
png_fixed_point greenx, greeny;
|
||||||
|
png_fixed_point bluex, bluey;
|
||||||
|
png_fixed_point whitex, whitey;
|
||||||
|
} png_xy;
|
||||||
|
|
||||||
|
/* The same data as above but encoded as CIE XYZ values. When this data comes
|
||||||
|
* from chromaticities the sum of the Y values is assumed to be 1.0
|
||||||
|
*/
|
||||||
|
typedef struct png_XYZ
|
||||||
|
{
|
||||||
|
png_fixed_point red_X, red_Y, red_Z;
|
||||||
|
png_fixed_point green_X, green_Y, green_Z;
|
||||||
|
png_fixed_point blue_X, blue_Y, blue_Z;
|
||||||
|
} png_XYZ;
|
||||||
|
#endif /* COLORSPACE */
|
||||||
|
|
||||||
|
#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
|
||||||
|
/* A colorspace is all the above plus, potentially, profile information;
|
||||||
|
* however at present libpng does not use the profile internally so it is only
|
||||||
|
* stored in the png_info struct (if iCCP is supported.) The rendering intent
|
||||||
|
* is retained here and is checked.
|
||||||
|
*
|
||||||
|
* The file gamma encoding information is also stored here and gamma correction
|
||||||
|
* is done by libpng, whereas color correction must currently be done by the
|
||||||
|
* application.
|
||||||
|
*/
|
||||||
|
typedef struct png_colorspace
|
||||||
|
{
|
||||||
|
#ifdef PNG_GAMMA_SUPPORTED
|
||||||
|
png_fixed_point gamma; /* File gamma */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_COLORSPACE_SUPPORTED
|
||||||
|
png_xy end_points_xy; /* End points as chromaticities */
|
||||||
|
png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */
|
||||||
|
png_uint_16 rendering_intent; /* Rendering intent of a profile */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Flags are always defined to simplify the code. */
|
||||||
|
png_uint_16 flags; /* As defined below */
|
||||||
|
} png_colorspace, * PNG_RESTRICT png_colorspacerp;
|
||||||
|
|
||||||
|
typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp;
|
||||||
|
|
||||||
|
/* General flags for the 'flags' field */
|
||||||
|
#define PNG_COLORSPACE_HAVE_GAMMA 0x0001
|
||||||
|
#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002
|
||||||
|
#define PNG_COLORSPACE_HAVE_INTENT 0x0004
|
||||||
|
#define PNG_COLORSPACE_FROM_gAMA 0x0008
|
||||||
|
#define PNG_COLORSPACE_FROM_cHRM 0x0010
|
||||||
|
#define PNG_COLORSPACE_FROM_sRGB 0x0020
|
||||||
|
#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040
|
||||||
|
#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */
|
||||||
|
#define PNG_COLORSPACE_INVALID 0x8000
|
||||||
|
#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags))
|
||||||
|
#endif /* COLORSPACE || GAMMA */
|
||||||
|
|
||||||
|
struct png_struct_def
|
||||||
|
{
|
||||||
|
#ifdef PNG_SETJMP_SUPPORTED
|
||||||
|
jmp_buf jmp_buf_local; /* New name in 1.6.0 for jmp_buf in png_struct */
|
||||||
|
png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */
|
||||||
|
jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */
|
||||||
|
size_t jmp_buf_size; /* size of the above, if allocated */
|
||||||
|
#endif
|
||||||
|
png_error_ptr error_fn; /* function for printing errors and aborting */
|
||||||
|
#ifdef PNG_WARNINGS_SUPPORTED
|
||||||
|
png_error_ptr warning_fn; /* function for printing warnings */
|
||||||
|
#endif
|
||||||
|
png_voidp error_ptr; /* user supplied struct for error functions */
|
||||||
|
png_rw_ptr write_data_fn; /* function for writing output data */
|
||||||
|
png_rw_ptr read_data_fn; /* function for reading input data */
|
||||||
|
png_voidp io_ptr; /* ptr to application struct for I/O functions */
|
||||||
|
|
||||||
|
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
|
||||||
|
png_user_transform_ptr read_user_transform_fn; /* user read transform */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
|
||||||
|
png_user_transform_ptr write_user_transform_fn; /* user write transform */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* These were added in libpng-1.0.2 */
|
||||||
|
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
|
||||||
|
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
|
||||||
|
defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
|
||||||
|
png_voidp user_transform_ptr; /* user supplied struct for user transform */
|
||||||
|
png_byte user_transform_depth; /* bit depth of user transformed pixels */
|
||||||
|
png_byte user_transform_channels; /* channels in user transformed pixels */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
png_uint_32 mode; /* tells us where we are in the PNG file */
|
||||||
|
png_uint_32 flags; /* flags indicating various things to libpng */
|
||||||
|
png_uint_32 transformations; /* which transformations to perform */
|
||||||
|
|
||||||
|
png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */
|
||||||
|
z_stream zstream; /* decompression structure */
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SUPPORTED
|
||||||
|
png_compression_bufferp zbuffer_list; /* Created on demand during write */
|
||||||
|
uInt zbuffer_size; /* size of the actual buffer */
|
||||||
|
|
||||||
|
int zlib_level; /* holds zlib compression level */
|
||||||
|
int zlib_method; /* holds zlib compression method */
|
||||||
|
int zlib_window_bits; /* holds zlib compression window bits */
|
||||||
|
int zlib_mem_level; /* holds zlib compression memory level */
|
||||||
|
int zlib_strategy; /* holds zlib compression strategy */
|
||||||
|
#endif
|
||||||
|
/* Added at libpng 1.5.4 */
|
||||||
|
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
|
||||||
|
int zlib_text_level; /* holds zlib compression level */
|
||||||
|
int zlib_text_method; /* holds zlib compression method */
|
||||||
|
int zlib_text_window_bits; /* holds zlib compression window bits */
|
||||||
|
int zlib_text_mem_level; /* holds zlib compression memory level */
|
||||||
|
int zlib_text_strategy; /* holds zlib compression strategy */
|
||||||
|
#endif
|
||||||
|
/* End of material added at libpng 1.5.4 */
|
||||||
|
/* Added at libpng 1.6.0 */
|
||||||
|
#ifdef PNG_WRITE_SUPPORTED
|
||||||
|
int zlib_set_level; /* Actual values set into the zstream on write */
|
||||||
|
int zlib_set_method;
|
||||||
|
int zlib_set_window_bits;
|
||||||
|
int zlib_set_mem_level;
|
||||||
|
int zlib_set_strategy;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
png_uint_32 width; /* width of image in pixels */
|
||||||
|
png_uint_32 height; /* height of image in pixels */
|
||||||
|
png_uint_32 num_rows; /* number of rows in current pass */
|
||||||
|
png_uint_32 usr_width; /* width of row at start of write */
|
||||||
|
size_t rowbytes; /* size of row in bytes */
|
||||||
|
png_uint_32 iwidth; /* width of current interlaced row in pixels */
|
||||||
|
png_uint_32 row_number; /* current row in interlace pass */
|
||||||
|
png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */
|
||||||
|
png_bytep prev_row; /* buffer to save previous (unfiltered) row.
|
||||||
|
* While reading this is a pointer into
|
||||||
|
* big_prev_row; while writing it is separately
|
||||||
|
* allocated if needed.
|
||||||
|
*/
|
||||||
|
png_bytep row_buf; /* buffer to save current (unfiltered) row.
|
||||||
|
* While reading, this is a pointer into
|
||||||
|
* big_row_buf; while writing it is separately
|
||||||
|
* allocated.
|
||||||
|
*/
|
||||||
|
#ifdef PNG_WRITE_FILTER_SUPPORTED
|
||||||
|
png_bytep try_row; /* buffer to save trial row when filtering */
|
||||||
|
png_bytep tst_row; /* buffer to save best trial row when filtering */
|
||||||
|
#endif
|
||||||
|
size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */
|
||||||
|
|
||||||
|
png_uint_32 idat_size; /* current IDAT size for read */
|
||||||
|
png_uint_32 crc; /* current chunk CRC value */
|
||||||
|
png_colorp palette; /* palette from the input file */
|
||||||
|
png_uint_16 num_palette; /* number of color entries in palette */
|
||||||
|
|
||||||
|
/* Added at libpng-1.5.10 */
|
||||||
|
#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
|
||||||
|
int num_palette_max; /* maximum palette index found in IDAT */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
png_uint_16 num_trans; /* number of transparency values */
|
||||||
|
png_byte compression; /* file compression type (always 0) */
|
||||||
|
png_byte filter; /* file filter type (always 0) */
|
||||||
|
png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
|
||||||
|
png_byte pass; /* current interlace pass (0 - 6) */
|
||||||
|
png_byte do_filter; /* row filter flags (see PNG_FILTER_ in png.h ) */
|
||||||
|
png_byte color_type; /* color type of file */
|
||||||
|
png_byte bit_depth; /* bit depth of file */
|
||||||
|
png_byte usr_bit_depth; /* bit depth of users row: write only */
|
||||||
|
png_byte pixel_depth; /* number of bits per pixel */
|
||||||
|
png_byte channels; /* number of channels in file */
|
||||||
|
#ifdef PNG_WRITE_SUPPORTED
|
||||||
|
png_byte usr_channels; /* channels at start of write: write only */
|
||||||
|
#endif
|
||||||
|
png_byte sig_bytes; /* magic bytes read/written from start of file */
|
||||||
|
png_byte maximum_pixel_depth;
|
||||||
|
/* pixel depth used for the row buffers */
|
||||||
|
png_byte transformed_pixel_depth;
|
||||||
|
/* pixel depth after read/write transforms */
|
||||||
|
#if ZLIB_VERNUM >= 0x1240
|
||||||
|
png_byte zstream_start; /* at start of an input zlib stream */
|
||||||
|
#endif /* Zlib >= 1.2.4 */
|
||||||
|
#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
|
||||||
|
png_uint_16 filler; /* filler bytes for pixel expansion */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
|
||||||
|
defined(PNG_READ_ALPHA_MODE_SUPPORTED)
|
||||||
|
png_byte background_gamma_type;
|
||||||
|
png_fixed_point background_gamma;
|
||||||
|
png_color_16 background; /* background color in screen gamma space */
|
||||||
|
#ifdef PNG_READ_GAMMA_SUPPORTED
|
||||||
|
png_color_16 background_1; /* background normalized to gamma 1.0 */
|
||||||
|
#endif
|
||||||
|
#endif /* bKGD */
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_FLUSH_SUPPORTED
|
||||||
|
png_flush_ptr output_flush_fn; /* Function for flushing output */
|
||||||
|
png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */
|
||||||
|
png_uint_32 flush_rows; /* number of rows written since last flush */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_READ_GAMMA_SUPPORTED
|
||||||
|
int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */
|
||||||
|
png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */
|
||||||
|
|
||||||
|
png_bytep gamma_table; /* gamma table for 8-bit depth files */
|
||||||
|
png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
|
||||||
|
#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
|
||||||
|
defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
|
||||||
|
defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
|
||||||
|
png_bytep gamma_from_1; /* converts from 1.0 to screen */
|
||||||
|
png_bytep gamma_to_1; /* converts from file to 1.0 */
|
||||||
|
png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
|
||||||
|
png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
|
||||||
|
#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
|
||||||
|
png_color_8 sig_bit; /* significant bits in each available channel */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
|
||||||
|
png_color_8 shift; /* shift for significant bit transformation */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
|
||||||
|
|| defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
|
||||||
|
png_bytep trans_alpha; /* alpha values for paletted files */
|
||||||
|
png_color_16 trans_color; /* transparent color for non-paletted files */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
png_read_status_ptr read_row_fn; /* called after each row is decoded */
|
||||||
|
png_write_status_ptr write_row_fn; /* called after each row is encoded */
|
||||||
|
#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
|
||||||
|
png_progressive_info_ptr info_fn; /* called after header data fully read */
|
||||||
|
png_progressive_row_ptr row_fn; /* called after a prog. row is decoded */
|
||||||
|
png_progressive_end_ptr end_fn; /* called after image is complete */
|
||||||
|
png_bytep save_buffer_ptr; /* current location in save_buffer */
|
||||||
|
png_bytep save_buffer; /* buffer for previously read data */
|
||||||
|
png_bytep current_buffer_ptr; /* current location in current_buffer */
|
||||||
|
png_bytep current_buffer; /* buffer for recently used data */
|
||||||
|
png_uint_32 push_length; /* size of current input chunk */
|
||||||
|
png_uint_32 skip_length; /* bytes to skip in input data */
|
||||||
|
size_t save_buffer_size; /* amount of data now in save_buffer */
|
||||||
|
size_t save_buffer_max; /* total size of save_buffer */
|
||||||
|
size_t buffer_size; /* total amount of available input data */
|
||||||
|
size_t current_buffer_size; /* amount of data now in current_buffer */
|
||||||
|
int process_mode; /* what push library is currently doing */
|
||||||
|
int cur_palette; /* current push library palette index */
|
||||||
|
|
||||||
|
#endif /* PROGRESSIVE_READ */
|
||||||
|
|
||||||
|
#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
|
||||||
|
/* For the Borland special 64K segment handler */
|
||||||
|
png_bytepp offset_table_ptr;
|
||||||
|
png_bytep offset_table;
|
||||||
|
png_uint_16 offset_table_number;
|
||||||
|
png_uint_16 offset_table_count;
|
||||||
|
png_uint_16 offset_table_count_free;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_READ_QUANTIZE_SUPPORTED
|
||||||
|
png_bytep palette_lookup; /* lookup table for quantizing */
|
||||||
|
png_bytep quantize_index; /* index translation for palette files */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Options */
|
||||||
|
#ifdef PNG_SET_OPTION_SUPPORTED
|
||||||
|
png_uint_32 options; /* On/off state (up to 16 options) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PNG_LIBPNG_VER < 10700
|
||||||
|
/* To do: remove this from libpng-1.7 */
|
||||||
|
#ifdef PNG_TIME_RFC1123_SUPPORTED
|
||||||
|
char time_buffer[29]; /* String to hold RFC 1123 time text */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New members added in libpng-1.0.6 */
|
||||||
|
|
||||||
|
png_uint_32 free_me; /* flags items libpng is responsible for freeing */
|
||||||
|
|
||||||
|
#ifdef PNG_USER_CHUNKS_SUPPORTED
|
||||||
|
png_voidp user_chunk_ptr;
|
||||||
|
#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
|
||||||
|
png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
int unknown_default; /* As PNG_HANDLE_* */
|
||||||
|
unsigned int num_chunk_list; /* Number of entries in the list */
|
||||||
|
png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name
|
||||||
|
* followed by a PNG_HANDLE_* byte */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New members added in libpng-1.0.3 */
|
||||||
|
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
|
||||||
|
png_byte rgb_to_gray_status;
|
||||||
|
/* Added in libpng 1.5.5 to record setting of coefficients: */
|
||||||
|
png_byte rgb_to_gray_coefficients_set;
|
||||||
|
/* These were changed from png_byte in libpng-1.0.6 */
|
||||||
|
png_uint_16 rgb_to_gray_red_coeff;
|
||||||
|
png_uint_16 rgb_to_gray_green_coeff;
|
||||||
|
/* deleted in 1.5.5: rgb_to_gray_blue_coeff; */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New member added in libpng-1.6.36 */
|
||||||
|
#if defined(PNG_READ_EXPAND_SUPPORTED) && \
|
||||||
|
defined(PNG_ARM_NEON_IMPLEMENTATION)
|
||||||
|
png_bytep riffled_palette; /* buffer for accelerated palette expansion */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New member added in libpng-1.0.4 (renamed in 1.0.9) */
|
||||||
|
#if defined(PNG_MNG_FEATURES_SUPPORTED)
|
||||||
|
/* Changed from png_byte to png_uint_32 at version 1.2.0 */
|
||||||
|
png_uint_32 mng_features_permitted;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */
|
||||||
|
#ifdef PNG_MNG_FEATURES_SUPPORTED
|
||||||
|
png_byte filter_type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New members added in libpng-1.2.0 */
|
||||||
|
|
||||||
|
/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
|
||||||
|
#ifdef PNG_USER_MEM_SUPPORTED
|
||||||
|
png_voidp mem_ptr; /* user supplied struct for mem functions */
|
||||||
|
png_malloc_ptr malloc_fn; /* function for allocating memory */
|
||||||
|
png_free_ptr free_fn; /* function for freeing memory */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New member added in libpng-1.0.13 and 1.2.0 */
|
||||||
|
png_bytep big_row_buf; /* buffer to save current (unfiltered) row */
|
||||||
|
|
||||||
|
#ifdef PNG_READ_QUANTIZE_SUPPORTED
|
||||||
|
/* The following three members were added at version 1.0.14 and 1.2.4 */
|
||||||
|
png_bytep quantize_sort; /* working sort array */
|
||||||
|
png_bytep index_to_palette; /* where the original index currently is
|
||||||
|
in the palette */
|
||||||
|
png_bytep palette_to_index; /* which original index points to this
|
||||||
|
palette color */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New members added in libpng-1.0.16 and 1.2.6 */
|
||||||
|
png_byte compression_type;
|
||||||
|
|
||||||
|
#ifdef PNG_USER_LIMITS_SUPPORTED
|
||||||
|
png_uint_32 user_width_max;
|
||||||
|
png_uint_32 user_height_max;
|
||||||
|
|
||||||
|
/* Added in libpng-1.4.0: Total number of sPLT, text, and unknown
|
||||||
|
* chunks that can be stored (0 means unlimited).
|
||||||
|
*/
|
||||||
|
png_uint_32 user_chunk_cache_max;
|
||||||
|
|
||||||
|
/* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk
|
||||||
|
* can occupy when decompressed. 0 means unlimited.
|
||||||
|
*/
|
||||||
|
png_alloc_size_t user_chunk_malloc_max;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New member added in libpng-1.0.25 and 1.2.17 */
|
||||||
|
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
|
||||||
|
/* Temporary storage for unknown chunk that the library doesn't recognize,
|
||||||
|
* used while reading the chunk.
|
||||||
|
*/
|
||||||
|
png_unknown_chunk unknown_chunk;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New member added in libpng-1.2.26 */
|
||||||
|
size_t old_big_row_buf_size;
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
/* New member added in libpng-1.2.30 */
|
||||||
|
png_bytep read_buffer; /* buffer for reading chunk data */
|
||||||
|
png_alloc_size_t read_buffer_size; /* current size of the buffer */
|
||||||
|
#endif
|
||||||
|
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
|
||||||
|
uInt IDAT_read_size; /* limit on read buffer size for IDAT */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_IO_STATE_SUPPORTED
|
||||||
|
/* New member added in libpng-1.4.0 */
|
||||||
|
png_uint_32 io_state;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* New member added in libpng-1.5.6 */
|
||||||
|
png_bytep big_prev_row;
|
||||||
|
|
||||||
|
/* New member added in libpng-1.5.7 */
|
||||||
|
void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info,
|
||||||
|
png_bytep row, png_const_bytep prev_row);
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
|
||||||
|
png_colorspace colorspace;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
#endif /* PNGSTRUCT_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,864 @@
|
||||||
|
|
||||||
|
/* pngtrans.c - transforms the data in a row (used by both readers and writers)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pngpriv.h"
|
||||||
|
|
||||||
|
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
|
||||||
|
|
||||||
|
#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
|
||||||
|
/* Turn on BGR-to-RGB mapping */
|
||||||
|
void PNGAPI
|
||||||
|
png_set_bgr(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_bgr");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_ptr->transformations |= PNG_BGR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
|
||||||
|
/* Turn on 16-bit byte swapping */
|
||||||
|
void PNGAPI
|
||||||
|
png_set_swap(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_swap");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (png_ptr->bit_depth == 16)
|
||||||
|
png_ptr->transformations |= PNG_SWAP_BYTES;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
|
||||||
|
/* Turn on pixel packing */
|
||||||
|
void PNGAPI
|
||||||
|
png_set_packing(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_packing");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (png_ptr->bit_depth < 8)
|
||||||
|
{
|
||||||
|
png_ptr->transformations |= PNG_PACK;
|
||||||
|
# ifdef PNG_WRITE_SUPPORTED
|
||||||
|
png_ptr->usr_bit_depth = 8;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
|
||||||
|
/* Turn on packed pixel swapping */
|
||||||
|
void PNGAPI
|
||||||
|
png_set_packswap(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_packswap");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (png_ptr->bit_depth < 8)
|
||||||
|
png_ptr->transformations |= PNG_PACKSWAP;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
|
||||||
|
void PNGAPI
|
||||||
|
png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_shift");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_ptr->transformations |= PNG_SHIFT;
|
||||||
|
png_ptr->shift = *true_bits;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
|
||||||
|
defined(PNG_WRITE_INTERLACING_SUPPORTED)
|
||||||
|
int PNGAPI
|
||||||
|
png_set_interlace_handling(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_interlace handling");
|
||||||
|
|
||||||
|
if (png_ptr != 0 && png_ptr->interlaced != 0)
|
||||||
|
{
|
||||||
|
png_ptr->transformations |= PNG_INTERLACE;
|
||||||
|
return (7);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
|
||||||
|
/* Add a filler byte on read, or remove a filler or alpha byte on write.
|
||||||
|
* The filler type has changed in v0.95 to allow future 2-byte fillers
|
||||||
|
* for 48-bit input data, as well as to avoid problems with some compilers
|
||||||
|
* that don't like bytes as parameters.
|
||||||
|
*/
|
||||||
|
void PNGAPI
|
||||||
|
png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_filler");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* In libpng 1.6 it is possible to determine whether this is a read or write
|
||||||
|
* operation and therefore to do more checking here for a valid call.
|
||||||
|
*/
|
||||||
|
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
|
||||||
|
{
|
||||||
|
# ifdef PNG_READ_FILLER_SUPPORTED
|
||||||
|
/* On read png_set_filler is always valid, regardless of the base PNG
|
||||||
|
* format, because other transformations can give a format where the
|
||||||
|
* filler code can execute (basically an 8 or 16-bit component RGB or G
|
||||||
|
* format.)
|
||||||
|
*
|
||||||
|
* NOTE: usr_channels is not used by the read code! (This has led to
|
||||||
|
* confusion in the past.) The filler is only used in the read code.
|
||||||
|
*/
|
||||||
|
png_ptr->filler = (png_uint_16)filler;
|
||||||
|
# else
|
||||||
|
png_app_error(png_ptr, "png_set_filler not supported on read");
|
||||||
|
PNG_UNUSED(filler) /* not used in the write case */
|
||||||
|
return;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
else /* write */
|
||||||
|
{
|
||||||
|
# ifdef PNG_WRITE_FILLER_SUPPORTED
|
||||||
|
/* On write the usr_channels parameter must be set correctly at the
|
||||||
|
* start to record the number of channels in the app-supplied data.
|
||||||
|
*/
|
||||||
|
switch (png_ptr->color_type)
|
||||||
|
{
|
||||||
|
case PNG_COLOR_TYPE_RGB:
|
||||||
|
png_ptr->usr_channels = 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PNG_COLOR_TYPE_GRAY:
|
||||||
|
if (png_ptr->bit_depth >= 8)
|
||||||
|
{
|
||||||
|
png_ptr->usr_channels = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* There simply isn't any code in libpng to strip out bits
|
||||||
|
* from bytes when the components are less than a byte in
|
||||||
|
* size!
|
||||||
|
*/
|
||||||
|
png_app_error(png_ptr,
|
||||||
|
"png_set_filler is invalid for"
|
||||||
|
" low bit depth gray output");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
png_app_error(png_ptr,
|
||||||
|
"png_set_filler: inappropriate color type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
png_app_error(png_ptr, "png_set_filler not supported on write");
|
||||||
|
return;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Here on success - libpng supports the operation, set the transformation
|
||||||
|
* and the flag to say where the filler channel is.
|
||||||
|
*/
|
||||||
|
png_ptr->transformations |= PNG_FILLER;
|
||||||
|
|
||||||
|
if (filler_loc == PNG_FILLER_AFTER)
|
||||||
|
png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
|
||||||
|
|
||||||
|
else
|
||||||
|
png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Added to libpng-1.2.7 */
|
||||||
|
void PNGAPI
|
||||||
|
png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_add_alpha");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_set_filler(png_ptr, filler, filler_loc);
|
||||||
|
/* The above may fail to do anything. */
|
||||||
|
if ((png_ptr->transformations & PNG_FILLER) != 0)
|
||||||
|
png_ptr->transformations |= PNG_ADD_ALPHA;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
|
||||||
|
defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
|
||||||
|
void PNGAPI
|
||||||
|
png_set_swap_alpha(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_swap_alpha");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_ptr->transformations |= PNG_SWAP_ALPHA;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
|
||||||
|
defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
|
||||||
|
void PNGAPI
|
||||||
|
png_set_invert_alpha(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_invert_alpha");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_ptr->transformations |= PNG_INVERT_ALPHA;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
|
||||||
|
void PNGAPI
|
||||||
|
png_set_invert_mono(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_invert_mono");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_ptr->transformations |= PNG_INVERT_MONO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invert monochrome grayscale data */
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_do_invert(png_row_infop row_info, png_bytep row)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_invert");
|
||||||
|
|
||||||
|
/* This test removed from libpng version 1.0.13 and 1.2.0:
|
||||||
|
* if (row_info->bit_depth == 1 &&
|
||||||
|
*/
|
||||||
|
if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
size_t i;
|
||||||
|
size_t istop = row_info->rowbytes;
|
||||||
|
|
||||||
|
for (i = 0; i < istop; i++)
|
||||||
|
{
|
||||||
|
*rp = (png_byte)(~(*rp));
|
||||||
|
rp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
|
||||||
|
row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
size_t i;
|
||||||
|
size_t istop = row_info->rowbytes;
|
||||||
|
|
||||||
|
for (i = 0; i < istop; i += 2)
|
||||||
|
{
|
||||||
|
*rp = (png_byte)(~(*rp));
|
||||||
|
rp += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_16BIT_SUPPORTED
|
||||||
|
else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
|
||||||
|
row_info->bit_depth == 16)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
size_t i;
|
||||||
|
size_t istop = row_info->rowbytes;
|
||||||
|
|
||||||
|
for (i = 0; i < istop; i += 4)
|
||||||
|
{
|
||||||
|
*rp = (png_byte)(~(*rp));
|
||||||
|
*(rp + 1) = (png_byte)(~(*(rp + 1)));
|
||||||
|
rp += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_16BIT_SUPPORTED
|
||||||
|
#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
|
||||||
|
/* Swaps byte order on 16-bit depth images */
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_do_swap(png_row_infop row_info, png_bytep row)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_swap");
|
||||||
|
|
||||||
|
if (row_info->bit_depth == 16)
|
||||||
|
{
|
||||||
|
png_bytep rp = row;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 istop= row_info->width * row_info->channels;
|
||||||
|
|
||||||
|
for (i = 0; i < istop; i++, rp += 2)
|
||||||
|
{
|
||||||
|
#ifdef PNG_BUILTIN_BSWAP16_SUPPORTED
|
||||||
|
/* Feature added to libpng-1.6.11 for testing purposes, not
|
||||||
|
* enabled by default.
|
||||||
|
*/
|
||||||
|
*(png_uint_16*)rp = __builtin_bswap16(*(png_uint_16*)rp);
|
||||||
|
#else
|
||||||
|
png_byte t = *rp;
|
||||||
|
*rp = *(rp + 1);
|
||||||
|
*(rp + 1) = t;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
|
||||||
|
static const png_byte onebppswaptable[256] = {
|
||||||
|
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
|
||||||
|
0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||||
|
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
|
||||||
|
0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||||
|
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
|
||||||
|
0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||||
|
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
|
||||||
|
0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
|
||||||
|
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
|
||||||
|
0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
|
||||||
|
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
|
||||||
|
0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
|
||||||
|
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
|
||||||
|
0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
|
||||||
|
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
|
||||||
|
0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
|
||||||
|
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
|
||||||
|
0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
|
||||||
|
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
|
||||||
|
0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
|
||||||
|
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
|
||||||
|
0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
|
||||||
|
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
|
||||||
|
0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
|
||||||
|
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
|
||||||
|
0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||||
|
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
|
||||||
|
0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||||
|
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
|
||||||
|
0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||||
|
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
|
||||||
|
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
static const png_byte twobppswaptable[256] = {
|
||||||
|
0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
|
||||||
|
0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
|
||||||
|
0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
|
||||||
|
0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
|
||||||
|
0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
|
||||||
|
0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
|
||||||
|
0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
|
||||||
|
0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
|
||||||
|
0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
|
||||||
|
0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
|
||||||
|
0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
|
||||||
|
0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
|
||||||
|
0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
|
||||||
|
0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
|
||||||
|
0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
|
||||||
|
0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
|
||||||
|
0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
|
||||||
|
0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
|
||||||
|
0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
|
||||||
|
0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
|
||||||
|
0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
|
||||||
|
0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
|
||||||
|
0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
|
||||||
|
0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
|
||||||
|
0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
|
||||||
|
0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
|
||||||
|
0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
|
||||||
|
0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
|
||||||
|
0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
|
||||||
|
0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
|
||||||
|
0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
|
||||||
|
0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
static const png_byte fourbppswaptable[256] = {
|
||||||
|
0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
|
||||||
|
0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
|
||||||
|
0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
|
||||||
|
0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
|
||||||
|
0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
|
||||||
|
0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
|
||||||
|
0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
|
||||||
|
0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
|
||||||
|
0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
|
||||||
|
0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
|
||||||
|
0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
|
||||||
|
0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
|
||||||
|
0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
|
||||||
|
0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
|
||||||
|
0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
|
||||||
|
0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
|
||||||
|
0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
|
||||||
|
0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
|
||||||
|
0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
|
||||||
|
0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
|
||||||
|
0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
|
||||||
|
0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
|
||||||
|
0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
|
||||||
|
0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
|
||||||
|
0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
|
||||||
|
0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
|
||||||
|
0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
|
||||||
|
0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
|
||||||
|
0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
|
||||||
|
0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
|
||||||
|
0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
|
||||||
|
0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Swaps pixel packing order within bytes */
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_do_packswap(png_row_infop row_info, png_bytep row)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_packswap");
|
||||||
|
|
||||||
|
if (row_info->bit_depth < 8)
|
||||||
|
{
|
||||||
|
png_bytep rp;
|
||||||
|
png_const_bytep end, table;
|
||||||
|
|
||||||
|
end = row + row_info->rowbytes;
|
||||||
|
|
||||||
|
if (row_info->bit_depth == 1)
|
||||||
|
table = onebppswaptable;
|
||||||
|
|
||||||
|
else if (row_info->bit_depth == 2)
|
||||||
|
table = twobppswaptable;
|
||||||
|
|
||||||
|
else if (row_info->bit_depth == 4)
|
||||||
|
table = fourbppswaptable;
|
||||||
|
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (rp = row; rp < end; rp++)
|
||||||
|
*rp = table[*rp];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* PACKSWAP || WRITE_PACKSWAP */
|
||||||
|
|
||||||
|
#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
|
||||||
|
defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
|
||||||
|
/* Remove a channel - this used to be 'png_do_strip_filler' but it used a
|
||||||
|
* somewhat weird combination of flags to determine what to do. All the calls
|
||||||
|
* to png_do_strip_filler are changed in 1.5.2 to call this instead with the
|
||||||
|
* correct arguments.
|
||||||
|
*
|
||||||
|
* The routine isn't general - the channel must be the channel at the start or
|
||||||
|
* end (not in the middle) of each pixel.
|
||||||
|
*/
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
|
||||||
|
{
|
||||||
|
png_bytep sp = row; /* source pointer */
|
||||||
|
png_bytep dp = row; /* destination pointer */
|
||||||
|
png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */
|
||||||
|
|
||||||
|
/* At the start sp will point to the first byte to copy and dp to where
|
||||||
|
* it is copied to. ep always points just beyond the end of the row, so
|
||||||
|
* the loop simply copies (channels-1) channels until sp reaches ep.
|
||||||
|
*
|
||||||
|
* at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc.
|
||||||
|
* nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* GA, GX, XG cases */
|
||||||
|
if (row_info->channels == 2)
|
||||||
|
{
|
||||||
|
if (row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
if (at_start != 0) /* Skip initial filler */
|
||||||
|
++sp;
|
||||||
|
else /* Skip initial channel and, for sp, the filler */
|
||||||
|
{
|
||||||
|
sp += 2; ++dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For a 1 pixel wide image there is nothing to do */
|
||||||
|
while (sp < ep)
|
||||||
|
{
|
||||||
|
*dp++ = *sp; sp += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
row_info->pixel_depth = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (row_info->bit_depth == 16)
|
||||||
|
{
|
||||||
|
if (at_start != 0) /* Skip initial filler */
|
||||||
|
sp += 2;
|
||||||
|
else /* Skip initial channel and, for sp, the filler */
|
||||||
|
{
|
||||||
|
sp += 4; dp += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sp < ep)
|
||||||
|
{
|
||||||
|
*dp++ = *sp++; *dp++ = *sp; sp += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
row_info->pixel_depth = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
return; /* bad bit depth */
|
||||||
|
|
||||||
|
row_info->channels = 1;
|
||||||
|
|
||||||
|
/* Finally fix the color type if it records an alpha channel */
|
||||||
|
if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
|
row_info->color_type = PNG_COLOR_TYPE_GRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RGBA, RGBX, XRGB cases */
|
||||||
|
else if (row_info->channels == 4)
|
||||||
|
{
|
||||||
|
if (row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
if (at_start != 0) /* Skip initial filler */
|
||||||
|
++sp;
|
||||||
|
else /* Skip initial channels and, for sp, the filler */
|
||||||
|
{
|
||||||
|
sp += 4; dp += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note that the loop adds 3 to dp and 4 to sp each time. */
|
||||||
|
while (sp < ep)
|
||||||
|
{
|
||||||
|
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp; sp += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
row_info->pixel_depth = 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (row_info->bit_depth == 16)
|
||||||
|
{
|
||||||
|
if (at_start != 0) /* Skip initial filler */
|
||||||
|
sp += 2;
|
||||||
|
else /* Skip initial channels and, for sp, the filler */
|
||||||
|
{
|
||||||
|
sp += 8; dp += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sp < ep)
|
||||||
|
{
|
||||||
|
/* Copy 6 bytes, skip 2 */
|
||||||
|
*dp++ = *sp++; *dp++ = *sp++;
|
||||||
|
*dp++ = *sp++; *dp++ = *sp++;
|
||||||
|
*dp++ = *sp++; *dp++ = *sp; sp += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
row_info->pixel_depth = 48;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
return; /* bad bit depth */
|
||||||
|
|
||||||
|
row_info->channels = 3;
|
||||||
|
|
||||||
|
/* Finally fix the color type if it records an alpha channel */
|
||||||
|
if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||||||
|
row_info->color_type = PNG_COLOR_TYPE_RGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
return; /* The filler channel has gone already */
|
||||||
|
|
||||||
|
/* Fix the rowbytes value. */
|
||||||
|
row_info->rowbytes = (size_t)(dp-row);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
|
||||||
|
/* Swaps red and blue bytes within a pixel */
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_do_bgr(png_row_infop row_info, png_bytep row)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_bgr");
|
||||||
|
|
||||||
|
if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
|
||||||
|
{
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
if (row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
if (row_info->color_type == PNG_COLOR_TYPE_RGB)
|
||||||
|
{
|
||||||
|
png_bytep rp;
|
||||||
|
png_uint_32 i;
|
||||||
|
|
||||||
|
for (i = 0, rp = row; i < row_width; i++, rp += 3)
|
||||||
|
{
|
||||||
|
png_byte save = *rp;
|
||||||
|
*rp = *(rp + 2);
|
||||||
|
*(rp + 2) = save;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||||||
|
{
|
||||||
|
png_bytep rp;
|
||||||
|
png_uint_32 i;
|
||||||
|
|
||||||
|
for (i = 0, rp = row; i < row_width; i++, rp += 4)
|
||||||
|
{
|
||||||
|
png_byte save = *rp;
|
||||||
|
*rp = *(rp + 2);
|
||||||
|
*(rp + 2) = save;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_16BIT_SUPPORTED
|
||||||
|
else if (row_info->bit_depth == 16)
|
||||||
|
{
|
||||||
|
if (row_info->color_type == PNG_COLOR_TYPE_RGB)
|
||||||
|
{
|
||||||
|
png_bytep rp;
|
||||||
|
png_uint_32 i;
|
||||||
|
|
||||||
|
for (i = 0, rp = row; i < row_width; i++, rp += 6)
|
||||||
|
{
|
||||||
|
png_byte save = *rp;
|
||||||
|
*rp = *(rp + 4);
|
||||||
|
*(rp + 4) = save;
|
||||||
|
save = *(rp + 1);
|
||||||
|
*(rp + 1) = *(rp + 5);
|
||||||
|
*(rp + 5) = save;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||||||
|
{
|
||||||
|
png_bytep rp;
|
||||||
|
png_uint_32 i;
|
||||||
|
|
||||||
|
for (i = 0, rp = row; i < row_width; i++, rp += 8)
|
||||||
|
{
|
||||||
|
png_byte save = *rp;
|
||||||
|
*rp = *(rp + 4);
|
||||||
|
*(rp + 4) = save;
|
||||||
|
save = *(rp + 1);
|
||||||
|
*(rp + 1) = *(rp + 5);
|
||||||
|
*(rp + 5) = save;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* READ_BGR || WRITE_BGR */
|
||||||
|
|
||||||
|
#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
|
||||||
|
defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
|
||||||
|
/* Added at libpng-1.5.10 */
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
|
||||||
|
{
|
||||||
|
if (png_ptr->num_palette < (1 << row_info->bit_depth) &&
|
||||||
|
png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */
|
||||||
|
{
|
||||||
|
/* Calculations moved outside switch in an attempt to stop different
|
||||||
|
* compiler warnings. 'padding' is in *bits* within the last byte, it is
|
||||||
|
* an 'int' because pixel_depth becomes an 'int' in the expression below,
|
||||||
|
* and this calculation is used because it avoids warnings that other
|
||||||
|
* forms produced on either GCC or MSVC.
|
||||||
|
*/
|
||||||
|
int padding = PNG_PADBITS(row_info->pixel_depth, row_info->width);
|
||||||
|
png_bytep rp = png_ptr->row_buf + row_info->rowbytes - 1;
|
||||||
|
|
||||||
|
switch (row_info->bit_depth)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
/* in this case, all bytes must be 0 so we don't need
|
||||||
|
* to unpack the pixels except for the rightmost one.
|
||||||
|
*/
|
||||||
|
for (; rp > png_ptr->row_buf; rp--)
|
||||||
|
{
|
||||||
|
if ((*rp >> padding) != 0)
|
||||||
|
png_ptr->num_palette_max = 1;
|
||||||
|
padding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
for (; rp > png_ptr->row_buf; rp--)
|
||||||
|
{
|
||||||
|
int i = ((*rp >> padding) & 0x03);
|
||||||
|
|
||||||
|
if (i > png_ptr->num_palette_max)
|
||||||
|
png_ptr->num_palette_max = i;
|
||||||
|
|
||||||
|
i = (((*rp >> padding) >> 2) & 0x03);
|
||||||
|
|
||||||
|
if (i > png_ptr->num_palette_max)
|
||||||
|
png_ptr->num_palette_max = i;
|
||||||
|
|
||||||
|
i = (((*rp >> padding) >> 4) & 0x03);
|
||||||
|
|
||||||
|
if (i > png_ptr->num_palette_max)
|
||||||
|
png_ptr->num_palette_max = i;
|
||||||
|
|
||||||
|
i = (((*rp >> padding) >> 6) & 0x03);
|
||||||
|
|
||||||
|
if (i > png_ptr->num_palette_max)
|
||||||
|
png_ptr->num_palette_max = i;
|
||||||
|
|
||||||
|
padding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
for (; rp > png_ptr->row_buf; rp--)
|
||||||
|
{
|
||||||
|
int i = ((*rp >> padding) & 0x0f);
|
||||||
|
|
||||||
|
if (i > png_ptr->num_palette_max)
|
||||||
|
png_ptr->num_palette_max = i;
|
||||||
|
|
||||||
|
i = (((*rp >> padding) >> 4) & 0x0f);
|
||||||
|
|
||||||
|
if (i > png_ptr->num_palette_max)
|
||||||
|
png_ptr->num_palette_max = i;
|
||||||
|
|
||||||
|
padding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
{
|
||||||
|
for (; rp > png_ptr->row_buf; rp--)
|
||||||
|
{
|
||||||
|
if (*rp > png_ptr->num_palette_max)
|
||||||
|
png_ptr->num_palette_max = (int) *rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CHECK_FOR_INVALID_INDEX */
|
||||||
|
|
||||||
|
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
|
||||||
|
defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
|
||||||
|
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
|
||||||
|
void PNGAPI
|
||||||
|
png_set_user_transform_info(png_structrp png_ptr, png_voidp
|
||||||
|
user_transform_ptr, int user_transform_depth, int user_transform_channels)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_set_user_transform_info");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
|
||||||
|
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
|
||||||
|
(png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
|
||||||
|
{
|
||||||
|
png_app_error(png_ptr,
|
||||||
|
"info change after png_start_read_image or png_read_update_info");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
png_ptr->user_transform_ptr = user_transform_ptr;
|
||||||
|
png_ptr->user_transform_depth = (png_byte)user_transform_depth;
|
||||||
|
png_ptr->user_transform_channels = (png_byte)user_transform_channels;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This function returns a pointer to the user_transform_ptr associated with
|
||||||
|
* the user transform functions. The application should free any memory
|
||||||
|
* associated with this pointer before png_write_destroy and png_read_destroy
|
||||||
|
* are called.
|
||||||
|
*/
|
||||||
|
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
|
||||||
|
png_voidp PNGAPI
|
||||||
|
png_get_user_transform_ptr(png_const_structrp png_ptr)
|
||||||
|
{
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
return png_ptr->user_transform_ptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
|
||||||
|
png_uint_32 PNGAPI
|
||||||
|
png_get_current_row_number(png_const_structrp png_ptr)
|
||||||
|
{
|
||||||
|
/* See the comments in png.h - this is the sub-image row when reading an
|
||||||
|
* interlaced image.
|
||||||
|
*/
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
return png_ptr->row_number;
|
||||||
|
|
||||||
|
return PNG_UINT_32_MAX; /* help the app not to fail silently */
|
||||||
|
}
|
||||||
|
|
||||||
|
png_byte PNGAPI
|
||||||
|
png_get_current_pass_number(png_const_structrp png_ptr)
|
||||||
|
{
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
return png_ptr->pass;
|
||||||
|
return 8; /* invalid */
|
||||||
|
}
|
||||||
|
#endif /* USER_TRANSFORM_INFO */
|
||||||
|
#endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */
|
||||||
|
#endif /* READ || WRITE */
|
|
@ -0,0 +1,168 @@
|
||||||
|
|
||||||
|
/* pngwio.c - functions for data output
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2014,2016,2018 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*
|
||||||
|
* This file provides a location for all output. Users who need
|
||||||
|
* special handling are expected to write functions that have the same
|
||||||
|
* arguments as these and perform similar functions, but that possibly
|
||||||
|
* use different output methods. Note that you shouldn't change these
|
||||||
|
* functions, but rather write replacement functions and then change
|
||||||
|
* them at run time with png_set_write_fn(...).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pngpriv.h"
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SUPPORTED
|
||||||
|
|
||||||
|
/* Write the data to whatever output you are using. The default routine
|
||||||
|
* writes to a file pointer. Note that this routine sometimes gets called
|
||||||
|
* with very small lengths, so you should implement some kind of simple
|
||||||
|
* buffering if you are using unbuffered writes. This should never be asked
|
||||||
|
* to write more than 64K on a 16-bit machine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_write_data(png_structrp png_ptr, png_const_bytep data, size_t length)
|
||||||
|
{
|
||||||
|
/* NOTE: write_data_fn must not change the buffer! */
|
||||||
|
if (png_ptr->write_data_fn != NULL )
|
||||||
|
(*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data),
|
||||||
|
length);
|
||||||
|
|
||||||
|
else
|
||||||
|
png_error(png_ptr, "Call to NULL write function");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_STDIO_SUPPORTED
|
||||||
|
/* This is the function that does the actual writing of data. If you are
|
||||||
|
* not writing to a standard C stream, you should create a replacement
|
||||||
|
* write_data function and use it at run time with png_set_write_fn(), rather
|
||||||
|
* than changing the library.
|
||||||
|
*/
|
||||||
|
void PNGCBAPI
|
||||||
|
png_default_write_data(png_structp png_ptr, png_bytep data, size_t length)
|
||||||
|
{
|
||||||
|
size_t check;
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
|
||||||
|
|
||||||
|
if (check != length)
|
||||||
|
png_error(png_ptr, "Write Error");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This function is called to output any data pending writing (normally
|
||||||
|
* to disk). After png_flush is called, there should be no data pending
|
||||||
|
* writing in any buffers.
|
||||||
|
*/
|
||||||
|
#ifdef PNG_WRITE_FLUSH_SUPPORTED
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_flush(png_structrp png_ptr)
|
||||||
|
{
|
||||||
|
if (png_ptr->output_flush_fn != NULL)
|
||||||
|
(*(png_ptr->output_flush_fn))(png_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
# ifdef PNG_STDIO_SUPPORTED
|
||||||
|
void PNGCBAPI
|
||||||
|
png_default_flush(png_structp png_ptr)
|
||||||
|
{
|
||||||
|
png_FILE_p io_ptr;
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr));
|
||||||
|
fflush(io_ptr);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This function allows the application to supply new output functions for
|
||||||
|
* libpng if standard C streams aren't being used.
|
||||||
|
*
|
||||||
|
* This function takes as its arguments:
|
||||||
|
* png_ptr - pointer to a png output data structure
|
||||||
|
* io_ptr - pointer to user supplied structure containing info about
|
||||||
|
* the output functions. May be NULL.
|
||||||
|
* write_data_fn - pointer to a new output function that takes as its
|
||||||
|
* arguments a pointer to a png_struct, a pointer to
|
||||||
|
* data to be written, and a 32-bit unsigned int that is
|
||||||
|
* the number of bytes to be written. The new write
|
||||||
|
* function should call png_error(png_ptr, "Error msg")
|
||||||
|
* to exit and output any fatal error messages. May be
|
||||||
|
* NULL, in which case libpng's default function will
|
||||||
|
* be used.
|
||||||
|
* flush_data_fn - pointer to a new flush function that takes as its
|
||||||
|
* arguments a pointer to a png_struct. After a call to
|
||||||
|
* the flush function, there should be no data in any buffers
|
||||||
|
* or pending transmission. If the output method doesn't do
|
||||||
|
* any buffering of output, a function prototype must still be
|
||||||
|
* supplied although it doesn't have to do anything. If
|
||||||
|
* PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile
|
||||||
|
* time, output_flush_fn will be ignored, although it must be
|
||||||
|
* supplied for compatibility. May be NULL, in which case
|
||||||
|
* libpng's default function will be used, if
|
||||||
|
* PNG_WRITE_FLUSH_SUPPORTED is defined. This is not
|
||||||
|
* a good idea if io_ptr does not point to a standard
|
||||||
|
* *FILE structure.
|
||||||
|
*/
|
||||||
|
void PNGAPI
|
||||||
|
png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr,
|
||||||
|
png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)
|
||||||
|
{
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
png_ptr->io_ptr = io_ptr;
|
||||||
|
|
||||||
|
#ifdef PNG_STDIO_SUPPORTED
|
||||||
|
if (write_data_fn != NULL)
|
||||||
|
png_ptr->write_data_fn = write_data_fn;
|
||||||
|
|
||||||
|
else
|
||||||
|
png_ptr->write_data_fn = png_default_write_data;
|
||||||
|
#else
|
||||||
|
png_ptr->write_data_fn = write_data_fn;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_FLUSH_SUPPORTED
|
||||||
|
# ifdef PNG_STDIO_SUPPORTED
|
||||||
|
|
||||||
|
if (output_flush_fn != NULL)
|
||||||
|
png_ptr->output_flush_fn = output_flush_fn;
|
||||||
|
|
||||||
|
else
|
||||||
|
png_ptr->output_flush_fn = png_default_flush;
|
||||||
|
|
||||||
|
# else
|
||||||
|
png_ptr->output_flush_fn = output_flush_fn;
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
PNG_UNUSED(output_flush_fn)
|
||||||
|
#endif /* WRITE_FLUSH */
|
||||||
|
|
||||||
|
#ifdef PNG_READ_SUPPORTED
|
||||||
|
/* It is an error to read while writing a png file */
|
||||||
|
if (png_ptr->read_data_fn != NULL)
|
||||||
|
{
|
||||||
|
png_ptr->read_data_fn = NULL;
|
||||||
|
|
||||||
|
png_warning(png_ptr,
|
||||||
|
"Can't set both read_data_fn and write_data_fn in the"
|
||||||
|
" same structure");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* WRITE */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,575 @@
|
||||||
|
|
||||||
|
/* pngwtran.c - transforms the data in a row for PNG writers
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Cosmin Truta
|
||||||
|
* Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
|
||||||
|
* Copyright (c) 1996-1997 Andreas Dilger
|
||||||
|
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
|
||||||
|
*
|
||||||
|
* This code is released under the libpng license.
|
||||||
|
* For conditions of distribution and use, see the disclaimer
|
||||||
|
* and license in png.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pngpriv.h"
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SUPPORTED
|
||||||
|
#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_PACK_SUPPORTED
|
||||||
|
/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The
|
||||||
|
* row_info bit depth should be 8 (one pixel per byte). The channels
|
||||||
|
* should be 1 (this only happens on grayscale and paletted images).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_pack");
|
||||||
|
|
||||||
|
if (row_info->bit_depth == 8 &&
|
||||||
|
row_info->channels == 1)
|
||||||
|
{
|
||||||
|
switch ((int)bit_depth)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
png_bytep sp, dp;
|
||||||
|
int mask, v;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
sp = row;
|
||||||
|
dp = row;
|
||||||
|
mask = 0x80;
|
||||||
|
v = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < row_width; i++)
|
||||||
|
{
|
||||||
|
if (*sp != 0)
|
||||||
|
v |= mask;
|
||||||
|
|
||||||
|
sp++;
|
||||||
|
|
||||||
|
if (mask > 1)
|
||||||
|
mask >>= 1;
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mask = 0x80;
|
||||||
|
*dp = (png_byte)v;
|
||||||
|
dp++;
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask != 0x80)
|
||||||
|
*dp = (png_byte)v;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
png_bytep sp, dp;
|
||||||
|
unsigned int shift;
|
||||||
|
int v;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
sp = row;
|
||||||
|
dp = row;
|
||||||
|
shift = 6;
|
||||||
|
v = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < row_width; i++)
|
||||||
|
{
|
||||||
|
png_byte value;
|
||||||
|
|
||||||
|
value = (png_byte)(*sp & 0x03);
|
||||||
|
v |= (value << shift);
|
||||||
|
|
||||||
|
if (shift == 0)
|
||||||
|
{
|
||||||
|
shift = 6;
|
||||||
|
*dp = (png_byte)v;
|
||||||
|
dp++;
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
shift -= 2;
|
||||||
|
|
||||||
|
sp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift != 6)
|
||||||
|
*dp = (png_byte)v;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
png_bytep sp, dp;
|
||||||
|
unsigned int shift;
|
||||||
|
int v;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
sp = row;
|
||||||
|
dp = row;
|
||||||
|
shift = 4;
|
||||||
|
v = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < row_width; i++)
|
||||||
|
{
|
||||||
|
png_byte value;
|
||||||
|
|
||||||
|
value = (png_byte)(*sp & 0x0f);
|
||||||
|
v |= (value << shift);
|
||||||
|
|
||||||
|
if (shift == 0)
|
||||||
|
{
|
||||||
|
shift = 4;
|
||||||
|
*dp = (png_byte)v;
|
||||||
|
dp++;
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
shift -= 4;
|
||||||
|
|
||||||
|
sp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift != 4)
|
||||||
|
*dp = (png_byte)v;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
row_info->bit_depth = (png_byte)bit_depth;
|
||||||
|
row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
|
||||||
|
row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
|
||||||
|
row_info->width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SHIFT_SUPPORTED
|
||||||
|
/* Shift pixel values to take advantage of whole range. Pass the
|
||||||
|
* true number of bits in bit_depth. The row should be packed
|
||||||
|
* according to row_info->bit_depth. Thus, if you had a row of
|
||||||
|
* bit depth 4, but the pixels only had values from 0 to 7, you
|
||||||
|
* would pass 3 as bit_depth, and this routine would translate the
|
||||||
|
* data to 0 to 15.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
png_do_shift(png_row_infop row_info, png_bytep row,
|
||||||
|
png_const_color_8p bit_depth)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_shift");
|
||||||
|
|
||||||
|
if (row_info->color_type != PNG_COLOR_TYPE_PALETTE)
|
||||||
|
{
|
||||||
|
int shift_start[4], shift_dec[4];
|
||||||
|
unsigned int channels = 0;
|
||||||
|
|
||||||
|
if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
|
||||||
|
{
|
||||||
|
shift_start[channels] = row_info->bit_depth - bit_depth->red;
|
||||||
|
shift_dec[channels] = bit_depth->red;
|
||||||
|
channels++;
|
||||||
|
|
||||||
|
shift_start[channels] = row_info->bit_depth - bit_depth->green;
|
||||||
|
shift_dec[channels] = bit_depth->green;
|
||||||
|
channels++;
|
||||||
|
|
||||||
|
shift_start[channels] = row_info->bit_depth - bit_depth->blue;
|
||||||
|
shift_dec[channels] = bit_depth->blue;
|
||||||
|
channels++;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shift_start[channels] = row_info->bit_depth - bit_depth->gray;
|
||||||
|
shift_dec[channels] = bit_depth->gray;
|
||||||
|
channels++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0)
|
||||||
|
{
|
||||||
|
shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
|
||||||
|
shift_dec[channels] = bit_depth->alpha;
|
||||||
|
channels++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* With low row depths, could only be grayscale, so one channel */
|
||||||
|
if (row_info->bit_depth < 8)
|
||||||
|
{
|
||||||
|
png_bytep bp = row;
|
||||||
|
size_t i;
|
||||||
|
unsigned int mask;
|
||||||
|
size_t row_bytes = row_info->rowbytes;
|
||||||
|
|
||||||
|
if (bit_depth->gray == 1 && row_info->bit_depth == 2)
|
||||||
|
mask = 0x55;
|
||||||
|
|
||||||
|
else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
|
||||||
|
mask = 0x11;
|
||||||
|
|
||||||
|
else
|
||||||
|
mask = 0xff;
|
||||||
|
|
||||||
|
for (i = 0; i < row_bytes; i++, bp++)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
unsigned int v, out;
|
||||||
|
|
||||||
|
v = *bp;
|
||||||
|
out = 0;
|
||||||
|
|
||||||
|
for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
|
||||||
|
{
|
||||||
|
if (j > 0)
|
||||||
|
out |= v << j;
|
||||||
|
|
||||||
|
else
|
||||||
|
out |= (v >> (-j)) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
*bp = (png_byte)(out & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
png_bytep bp = row;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 istop = channels * row_info->width;
|
||||||
|
|
||||||
|
for (i = 0; i < istop; i++, bp++)
|
||||||
|
{
|
||||||
|
unsigned int c = i%channels;
|
||||||
|
int j;
|
||||||
|
unsigned int v, out;
|
||||||
|
|
||||||
|
v = *bp;
|
||||||
|
out = 0;
|
||||||
|
|
||||||
|
for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
|
||||||
|
{
|
||||||
|
if (j > 0)
|
||||||
|
out |= v << j;
|
||||||
|
|
||||||
|
else
|
||||||
|
out |= v >> (-j);
|
||||||
|
}
|
||||||
|
|
||||||
|
*bp = (png_byte)(out & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
png_bytep bp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 istop = channels * row_info->width;
|
||||||
|
|
||||||
|
for (bp = row, i = 0; i < istop; i++)
|
||||||
|
{
|
||||||
|
unsigned int c = i%channels;
|
||||||
|
int j;
|
||||||
|
unsigned int value, v;
|
||||||
|
|
||||||
|
v = png_get_uint_16(bp);
|
||||||
|
value = 0;
|
||||||
|
|
||||||
|
for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
|
||||||
|
{
|
||||||
|
if (j > 0)
|
||||||
|
value |= v << j;
|
||||||
|
|
||||||
|
else
|
||||||
|
value |= v >> (-j);
|
||||||
|
}
|
||||||
|
*bp++ = (png_byte)((value >> 8) & 0xff);
|
||||||
|
*bp++ = (png_byte)(value & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
|
||||||
|
static void
|
||||||
|
png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_write_swap_alpha");
|
||||||
|
|
||||||
|
{
|
||||||
|
if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||||||
|
{
|
||||||
|
if (row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
/* This converts from ARGB to RGBA */
|
||||||
|
png_bytep sp, dp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
for (i = 0, sp = dp = row; i < row_width; i++)
|
||||||
|
{
|
||||||
|
png_byte save = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = save;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_16BIT_SUPPORTED
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* This converts from AARRGGBB to RRGGBBAA */
|
||||||
|
png_bytep sp, dp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
for (i = 0, sp = dp = row; i < row_width; i++)
|
||||||
|
{
|
||||||
|
png_byte save[2];
|
||||||
|
save[0] = *(sp++);
|
||||||
|
save[1] = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = save[0];
|
||||||
|
*(dp++) = save[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* WRITE_16BIT */
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
|
{
|
||||||
|
if (row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
/* This converts from AG to GA */
|
||||||
|
png_bytep sp, dp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
for (i = 0, sp = dp = row; i < row_width; i++)
|
||||||
|
{
|
||||||
|
png_byte save = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = save;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_16BIT_SUPPORTED
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* This converts from AAGG to GGAA */
|
||||||
|
png_bytep sp, dp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
for (i = 0, sp = dp = row; i < row_width; i++)
|
||||||
|
{
|
||||||
|
png_byte save[2];
|
||||||
|
save[0] = *(sp++);
|
||||||
|
save[1] = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = save[0];
|
||||||
|
*(dp++) = save[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* WRITE_16BIT */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
|
||||||
|
static void
|
||||||
|
png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_write_invert_alpha");
|
||||||
|
|
||||||
|
{
|
||||||
|
if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||||||
|
{
|
||||||
|
if (row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
/* This inverts the alpha channel in RGBA */
|
||||||
|
png_bytep sp, dp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
for (i = 0, sp = dp = row; i < row_width; i++)
|
||||||
|
{
|
||||||
|
/* Does nothing
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*/
|
||||||
|
sp+=3; dp = sp;
|
||||||
|
*dp = (png_byte)(255 - *(sp++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_16BIT_SUPPORTED
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* This inverts the alpha channel in RRGGBBAA */
|
||||||
|
png_bytep sp, dp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
for (i = 0, sp = dp = row; i < row_width; i++)
|
||||||
|
{
|
||||||
|
/* Does nothing
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*/
|
||||||
|
sp+=6; dp = sp;
|
||||||
|
*(dp++) = (png_byte)(255 - *(sp++));
|
||||||
|
*dp = (png_byte)(255 - *(sp++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* WRITE_16BIT */
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
|
{
|
||||||
|
if (row_info->bit_depth == 8)
|
||||||
|
{
|
||||||
|
/* This inverts the alpha channel in GA */
|
||||||
|
png_bytep sp, dp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
for (i = 0, sp = dp = row; i < row_width; i++)
|
||||||
|
{
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = (png_byte)(255 - *(sp++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_16BIT_SUPPORTED
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* This inverts the alpha channel in GGAA */
|
||||||
|
png_bytep sp, dp;
|
||||||
|
png_uint_32 i;
|
||||||
|
png_uint_32 row_width = row_info->width;
|
||||||
|
|
||||||
|
for (i = 0, sp = dp = row; i < row_width; i++)
|
||||||
|
{
|
||||||
|
/* Does nothing
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*(dp++) = *(sp++);
|
||||||
|
*/
|
||||||
|
sp+=2; dp = sp;
|
||||||
|
*(dp++) = (png_byte)(255 - *(sp++));
|
||||||
|
*dp = (png_byte)(255 - *(sp++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* WRITE_16BIT */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Transform the data according to the user's wishes. The order of
|
||||||
|
* transformations is significant.
|
||||||
|
*/
|
||||||
|
void /* PRIVATE */
|
||||||
|
png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info)
|
||||||
|
{
|
||||||
|
png_debug(1, "in png_do_write_transformations");
|
||||||
|
|
||||||
|
if (png_ptr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
|
||||||
|
if (png_ptr->write_user_transform_fn != NULL)
|
||||||
|
(*(png_ptr->write_user_transform_fn)) /* User write transform
|
||||||
|
function */
|
||||||
|
(png_ptr, /* png_ptr */
|
||||||
|
row_info, /* row_info: */
|
||||||
|
/* png_uint_32 width; width of row */
|
||||||
|
/* size_t rowbytes; number of bytes in row */
|
||||||
|
/* png_byte color_type; color type of pixels */
|
||||||
|
/* png_byte bit_depth; bit depth of samples */
|
||||||
|
/* png_byte channels; number of channels (1-4) */
|
||||||
|
/* png_byte pixel_depth; bits per pixel (depth*channels) */
|
||||||
|
png_ptr->row_buf + 1); /* start of pixel data for row */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_FILLER_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_FILLER) != 0)
|
||||||
|
png_do_strip_channel(row_info, png_ptr->row_buf + 1,
|
||||||
|
!(png_ptr->flags & PNG_FLAG_FILLER_AFTER));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
|
||||||
|
png_do_packswap(row_info, png_ptr->row_buf + 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_PACK_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_PACK) != 0)
|
||||||
|
png_do_pack(row_info, png_ptr->row_buf + 1,
|
||||||
|
(png_uint_32)png_ptr->bit_depth);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SWAP_SUPPORTED
|
||||||
|
# ifdef PNG_16BIT_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
|
||||||
|
png_do_swap(row_info, png_ptr->row_buf + 1);
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SHIFT_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_SHIFT) != 0)
|
||||||
|
png_do_shift(row_info, png_ptr->row_buf + 1,
|
||||||
|
&(png_ptr->shift));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0)
|
||||||
|
png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
|
||||||
|
png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_BGR_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_BGR) != 0)
|
||||||
|
png_do_bgr(row_info, png_ptr->row_buf + 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PNG_WRITE_INVERT_SUPPORTED
|
||||||
|
if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
|
||||||
|
png_do_invert(row_info, png_ptr->row_buf + 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* WRITE_TRANSFORMS */
|
||||||
|
#endif /* WRITE */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
_hecl ()
|
||||||
|
{
|
||||||
|
local word=${COMP_WORDS[COMP_CWORD]}
|
||||||
|
local filecmds=(init spec extract add remove group cook clean package)
|
||||||
|
|
||||||
|
if [ $COMP_CWORD == 1 ]
|
||||||
|
then
|
||||||
|
COMPREPLY=($(compgen -W "${filecmds[*]} help" "${word}"))
|
||||||
|
return 0
|
||||||
|
elif [ $COMP_CWORD == 2 ]
|
||||||
|
then
|
||||||
|
case ${COMP_WORDS[1]} in
|
||||||
|
init|extract|add|remove|group|cook|clean|package)
|
||||||
|
COMPREPLY=($(compgen -f -- "${word}"))
|
||||||
|
;;
|
||||||
|
spec)
|
||||||
|
COMPREPLY=($(compgen -W "enable disable" "${word}"))
|
||||||
|
;;
|
||||||
|
help)
|
||||||
|
COMPREPLY=($(compgen -W "${filecmds[*]}" "${word}"))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _hecl hecl
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/* CMake-curated application reps header */
|
||||||
|
|
||||||
|
#define STAGE_SPECIALIZATIONS(T, P) \
|
||||||
|
T<P, hecl::PipelineStage::Vertex>, \
|
||||||
|
T<P, hecl::PipelineStage::Fragment>, \
|
||||||
|
T<P, hecl::PipelineStage::Geometry>, \
|
||||||
|
T<P, hecl::PipelineStage::Control>, \
|
||||||
|
T<P, hecl::PipelineStage::Evaluation>,
|
||||||
|
|
||||||
|
@HECL_APPLICATION_REPS_INCLUDES_LOCAL@
|
||||||
|
|
||||||
|
#define HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL \
|
||||||
|
@HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL@
|
||||||
|
|
||||||
|
#define HECL_APPLICATION_STAGE_REPS(P, S) \
|
||||||
|
@HECL_APPLICATION_STAGE_REPS_LOCAL@
|
|
@ -0,0 +1,299 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
|
||||||
|
|
||||||
|
#include "hecl.hpp"
|
||||||
|
#include "../extern/boo/xxhash/xxhash.h"
|
||||||
|
|
||||||
|
namespace hecl::Backend {
|
||||||
|
struct ExtensionSlot;
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
enum class TexCoordSource : uint8_t {
|
||||||
|
Invalid = 0xff,
|
||||||
|
Position = 0,
|
||||||
|
Normal = 1,
|
||||||
|
Tex0 = 2,
|
||||||
|
Tex1 = 3,
|
||||||
|
Tex2 = 4,
|
||||||
|
Tex3 = 5,
|
||||||
|
Tex4 = 6,
|
||||||
|
Tex5 = 7,
|
||||||
|
Tex6 = 8,
|
||||||
|
Tex7 = 9,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class BlendFactor : uint8_t {
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
SrcColor,
|
||||||
|
InvSrcColor,
|
||||||
|
DstColor,
|
||||||
|
InvDstColor,
|
||||||
|
SrcAlpha,
|
||||||
|
InvSrcAlpha,
|
||||||
|
DstAlpha,
|
||||||
|
InvDstAlpha,
|
||||||
|
SrcColor1,
|
||||||
|
InvSrcColor1,
|
||||||
|
Original = 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::string_view BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor) {
|
||||||
|
switch (factor) {
|
||||||
|
case BlendFactor::Zero:
|
||||||
|
return "ZERO"sv;
|
||||||
|
case BlendFactor::One:
|
||||||
|
return "ONE"sv;
|
||||||
|
case BlendFactor::SrcColor:
|
||||||
|
return "SRCCOLOR"sv;
|
||||||
|
case BlendFactor::InvSrcColor:
|
||||||
|
return "INVSRCCOLOR"sv;
|
||||||
|
case BlendFactor::DstColor:
|
||||||
|
return "DSTCOLOR"sv;
|
||||||
|
case BlendFactor::InvDstColor:
|
||||||
|
return "INVDSTCOLOR"sv;
|
||||||
|
case BlendFactor::SrcAlpha:
|
||||||
|
return "SRCALPHA"sv;
|
||||||
|
case BlendFactor::InvSrcAlpha:
|
||||||
|
return "INVSRCALPHA"sv;
|
||||||
|
case BlendFactor::DstAlpha:
|
||||||
|
return "DSTALPHA"sv;
|
||||||
|
case BlendFactor::InvDstAlpha:
|
||||||
|
return "INVDSTALPHA"sv;
|
||||||
|
case BlendFactor::SrcColor1:
|
||||||
|
return "SRCCOLOR1"sv;
|
||||||
|
case BlendFactor::InvSrcColor1:
|
||||||
|
return "INVSRCCOLOR1"sv;
|
||||||
|
default:
|
||||||
|
return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ZTest : uint8_t { None, LEqual, Greater, Equal, GEqual, Original = 0xff };
|
||||||
|
|
||||||
|
enum class CullMode : uint8_t { None, Backface, Frontface, Original = 0xff };
|
||||||
|
|
||||||
|
struct TextureInfo {
|
||||||
|
TexCoordSource src;
|
||||||
|
uint8_t mtxIdx;
|
||||||
|
bool normalize;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ReflectionType { None, Simple, Indirect };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hash subclass for identifying shaders and their metadata
|
||||||
|
*/
|
||||||
|
class ShaderTag : public Hash {
|
||||||
|
union {
|
||||||
|
uint64_t m_meta = 0;
|
||||||
|
struct {
|
||||||
|
uint8_t m_colorCount;
|
||||||
|
uint8_t m_uvCount;
|
||||||
|
uint8_t m_weightCount;
|
||||||
|
uint8_t m_skinSlotCount;
|
||||||
|
uint8_t m_primitiveType;
|
||||||
|
uint8_t m_reflectionType;
|
||||||
|
bool m_depthTest : 1;
|
||||||
|
bool m_depthWrite : 1;
|
||||||
|
bool m_backfaceCulling : 1;
|
||||||
|
bool m_alphaTest : 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
ShaderTag() = default;
|
||||||
|
ShaderTag(std::string_view source, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
|
||||||
|
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
|
||||||
|
bool alphaTest)
|
||||||
|
: Hash(source) {
|
||||||
|
m_colorCount = c;
|
||||||
|
m_uvCount = u;
|
||||||
|
m_weightCount = w;
|
||||||
|
m_skinSlotCount = s;
|
||||||
|
m_primitiveType = uint8_t(pt);
|
||||||
|
m_reflectionType = uint8_t(reflectionType);
|
||||||
|
m_depthTest = depthTest;
|
||||||
|
m_depthWrite = depthWrite;
|
||||||
|
m_backfaceCulling = backfaceCulling;
|
||||||
|
m_alphaTest = alphaTest;
|
||||||
|
hash ^= m_meta;
|
||||||
|
}
|
||||||
|
ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
|
||||||
|
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
|
||||||
|
bool alphaTest)
|
||||||
|
: Hash(hashin) {
|
||||||
|
m_colorCount = c;
|
||||||
|
m_uvCount = u;
|
||||||
|
m_weightCount = w;
|
||||||
|
m_skinSlotCount = s;
|
||||||
|
m_primitiveType = uint8_t(pt);
|
||||||
|
m_reflectionType = uint8_t(reflectionType);
|
||||||
|
m_depthTest = depthTest;
|
||||||
|
m_depthWrite = depthWrite;
|
||||||
|
m_backfaceCulling = backfaceCulling;
|
||||||
|
m_alphaTest = alphaTest;
|
||||||
|
hash ^= m_meta;
|
||||||
|
}
|
||||||
|
ShaderTag(uint64_t comphashin, uint64_t meta) : Hash(comphashin), m_meta(meta) {}
|
||||||
|
ShaderTag(const ShaderTag& other) : Hash(other), m_meta(other.m_meta) {}
|
||||||
|
uint8_t getColorCount() const { return m_colorCount; }
|
||||||
|
uint8_t getUvCount() const { return m_uvCount; }
|
||||||
|
uint8_t getWeightCount() const { return m_weightCount; }
|
||||||
|
uint8_t getSkinSlotCount() const { return m_skinSlotCount; }
|
||||||
|
boo::Primitive getPrimType() const { return boo::Primitive(m_primitiveType); }
|
||||||
|
Backend::ReflectionType getReflectionType() const { return Backend::ReflectionType(m_reflectionType); }
|
||||||
|
bool getDepthTest() const { return m_depthTest; }
|
||||||
|
bool getDepthWrite() const { return m_depthWrite; }
|
||||||
|
bool getBackfaceCulling() const { return m_backfaceCulling; }
|
||||||
|
bool getAlphaTest() const { return m_alphaTest; }
|
||||||
|
uint64_t getMetaData() const { return m_meta; }
|
||||||
|
|
||||||
|
std::vector<boo::VertexElementDescriptor> vertexFormat() const {
|
||||||
|
std::vector<boo::VertexElementDescriptor> ret;
|
||||||
|
size_t elemCount = 2 + m_colorCount + m_uvCount + m_weightCount;
|
||||||
|
ret.resize(elemCount);
|
||||||
|
|
||||||
|
ret[0].semantic = boo::VertexSemantic::Position3;
|
||||||
|
ret[1].semantic = boo::VertexSemantic::Normal3;
|
||||||
|
size_t e = 2;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_colorCount; ++i, ++e) {
|
||||||
|
ret[e].semantic = boo::VertexSemantic::ColorUNorm;
|
||||||
|
ret[e].semanticIdx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_uvCount; ++i, ++e) {
|
||||||
|
ret[e].semantic = boo::VertexSemantic::UV2;
|
||||||
|
ret[e].semanticIdx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_weightCount; ++i, ++e) {
|
||||||
|
ret[e].semantic = boo::VertexSemantic::Weight;
|
||||||
|
ret[e].semanticIdx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
boo::AdditionalPipelineInfo additionalInfo(const ExtensionSlot& ext,
|
||||||
|
std::pair<BlendFactor, BlendFactor> blendFactors) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Function {
|
||||||
|
std::string_view m_source;
|
||||||
|
std::string_view m_entry;
|
||||||
|
Function() = default;
|
||||||
|
Function(std::string_view source, std::string_view entry) : m_source(source), m_entry(entry) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExtensionSlot {
|
||||||
|
const char* shaderMacro = nullptr;
|
||||||
|
size_t texCount = 0;
|
||||||
|
const Backend::TextureInfo* texs = nullptr;
|
||||||
|
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original;
|
||||||
|
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original;
|
||||||
|
Backend::ZTest depthTest = Backend::ZTest::Original;
|
||||||
|
Backend::CullMode cullMode = Backend::CullMode::Backface;
|
||||||
|
bool noDepthWrite = false;
|
||||||
|
bool noColorWrite = false;
|
||||||
|
bool noAlphaWrite = false;
|
||||||
|
bool noAlphaOverwrite = false;
|
||||||
|
bool noReflection = false;
|
||||||
|
bool forceAlphaTest = false;
|
||||||
|
bool diffuseOnly = false;
|
||||||
|
|
||||||
|
constexpr ExtensionSlot(size_t texCount = 0, const Backend::TextureInfo* texs = nullptr,
|
||||||
|
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original,
|
||||||
|
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original,
|
||||||
|
Backend::ZTest depthTest = Backend::ZTest::Original,
|
||||||
|
Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false,
|
||||||
|
bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false,
|
||||||
|
bool noReflection = false, bool forceAlphaTest = false, bool diffuseOnly = false) noexcept
|
||||||
|
: texCount(texCount)
|
||||||
|
, texs(texs)
|
||||||
|
, srcFactor(srcFactor)
|
||||||
|
, dstFactor(dstFactor)
|
||||||
|
, depthTest(depthTest)
|
||||||
|
, cullMode(cullMode)
|
||||||
|
, noDepthWrite(noDepthWrite)
|
||||||
|
, noColorWrite(noColorWrite)
|
||||||
|
, noAlphaWrite(noAlphaWrite)
|
||||||
|
, noAlphaOverwrite(noAlphaOverwrite)
|
||||||
|
, noReflection(noReflection)
|
||||||
|
, forceAlphaTest(forceAlphaTest)
|
||||||
|
, diffuseOnly(diffuseOnly) {}
|
||||||
|
|
||||||
|
mutable uint64_t m_hash = 0;
|
||||||
|
void calculateHash() const {
|
||||||
|
XXH64_state_t st;
|
||||||
|
XXH64_reset(&st, 0);
|
||||||
|
XXH64_update(&st, shaderMacro, strlen(shaderMacro));
|
||||||
|
for (size_t i = 0; i < texCount; ++i) {
|
||||||
|
const Backend::TextureInfo& tinfo = texs[i];
|
||||||
|
XXH64_update(&st, &tinfo, sizeof(tinfo));
|
||||||
|
}
|
||||||
|
XXH64_update(&st, &srcFactor, offsetof(ExtensionSlot, m_hash) - offsetof(ExtensionSlot, srcFactor));
|
||||||
|
m_hash = XXH64_digest(&st);
|
||||||
|
}
|
||||||
|
uint64_t hash() const {
|
||||||
|
if (m_hash == 0)
|
||||||
|
calculateHash();
|
||||||
|
return m_hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline boo::AdditionalPipelineInfo ShaderTag::additionalInfo(const ExtensionSlot& ext,
|
||||||
|
std::pair<BlendFactor, BlendFactor> blendFactors) const {
|
||||||
|
boo::ZTest zTest;
|
||||||
|
switch (ext.depthTest) {
|
||||||
|
case hecl::Backend::ZTest::Original:
|
||||||
|
default:
|
||||||
|
zTest = getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None;
|
||||||
|
break;
|
||||||
|
case hecl::Backend::ZTest::None:
|
||||||
|
zTest = boo::ZTest::None;
|
||||||
|
break;
|
||||||
|
case hecl::Backend::ZTest::LEqual:
|
||||||
|
zTest = boo::ZTest::LEqual;
|
||||||
|
break;
|
||||||
|
case hecl::Backend::ZTest::Greater:
|
||||||
|
zTest = boo::ZTest::Greater;
|
||||||
|
break;
|
||||||
|
case hecl::Backend::ZTest::Equal:
|
||||||
|
zTest = boo::ZTest::Equal;
|
||||||
|
break;
|
||||||
|
case hecl::Backend::ZTest::GEqual:
|
||||||
|
zTest = boo::ZTest::GEqual;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlendFactor srcFactor = m_alphaTest ? BlendFactor::One : blendFactors.first;
|
||||||
|
BlendFactor dstFactor = m_alphaTest ? BlendFactor::Zero : blendFactors.second;
|
||||||
|
return {boo::BlendFactor((ext.srcFactor == BlendFactor::Original) ? srcFactor : ext.srcFactor),
|
||||||
|
boo::BlendFactor((ext.dstFactor == BlendFactor::Original) ? dstFactor : ext.dstFactor),
|
||||||
|
getPrimType(),
|
||||||
|
zTest,
|
||||||
|
ext.noDepthWrite ? false : getDepthWrite(),
|
||||||
|
!ext.noColorWrite,
|
||||||
|
!ext.noAlphaWrite,
|
||||||
|
(ext.cullMode == hecl::Backend::CullMode::Original)
|
||||||
|
? (getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None)
|
||||||
|
: boo::CullMode(ext.cullMode),
|
||||||
|
!ext.noAlphaOverwrite};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace hecl::Backend
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<hecl::Backend::ShaderTag> {
|
||||||
|
size_t operator()(const hecl::Backend::ShaderTag& val) const noexcept { return val.valSizeT(); }
|
||||||
|
};
|
||||||
|
} // namespace std
|
|
@ -0,0 +1,588 @@
|
||||||
|
//===- llvm/ADT/BitVector.h - Bit vectors -----------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file implements the BitVector class.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MathExtras.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
namespace llvm {
|
||||||
|
|
||||||
|
class BitVector {
|
||||||
|
typedef unsigned long BitWord;
|
||||||
|
|
||||||
|
enum { BITWORD_SIZE = (unsigned)sizeof(BitWord) * CHAR_BIT };
|
||||||
|
|
||||||
|
static_assert(BITWORD_SIZE == 64 || BITWORD_SIZE == 32, "Unsupported word size");
|
||||||
|
|
||||||
|
BitWord* Bits; // Actual bits.
|
||||||
|
unsigned Size; // Size of bitvector in bits.
|
||||||
|
unsigned Capacity; // Number of BitWords allocated in the Bits array.
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef unsigned size_type;
|
||||||
|
// Encapsulation of a single bit.
|
||||||
|
class reference {
|
||||||
|
friend class BitVector;
|
||||||
|
|
||||||
|
BitWord* WordRef;
|
||||||
|
unsigned BitPos;
|
||||||
|
|
||||||
|
reference(); // Undefined
|
||||||
|
|
||||||
|
public:
|
||||||
|
reference(BitVector& b, unsigned Idx) {
|
||||||
|
WordRef = &b.Bits[Idx / BITWORD_SIZE];
|
||||||
|
BitPos = Idx % BITWORD_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference(const reference&) = default;
|
||||||
|
|
||||||
|
reference& operator=(reference t) {
|
||||||
|
*this = bool(t);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference& operator=(bool t) {
|
||||||
|
if (t)
|
||||||
|
*WordRef |= BitWord(1) << BitPos;
|
||||||
|
else
|
||||||
|
*WordRef &= ~(BitWord(1) << BitPos);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const { return ((*WordRef) & (BitWord(1) << BitPos)) != 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// BitVector default ctor - Creates an empty bitvector.
|
||||||
|
BitVector() : Size(0), Capacity(0) { Bits = nullptr; }
|
||||||
|
|
||||||
|
/// BitVector ctor - Creates a bitvector of specified number of bits. All
|
||||||
|
/// bits are initialized to the specified value.
|
||||||
|
explicit BitVector(unsigned s, bool t = false) : Size(s) {
|
||||||
|
Capacity = NumBitWords(s);
|
||||||
|
Bits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
|
||||||
|
init_words(Bits, Capacity, t);
|
||||||
|
if (t)
|
||||||
|
clear_unused_bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BitVector copy ctor.
|
||||||
|
BitVector(const BitVector& RHS) : Size(RHS.size()) {
|
||||||
|
if (Size == 0) {
|
||||||
|
Bits = nullptr;
|
||||||
|
Capacity = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Capacity = NumBitWords(RHS.size());
|
||||||
|
Bits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
|
||||||
|
std::memcpy(Bits, RHS.Bits, Capacity * sizeof(BitWord));
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector(BitVector&& RHS) : Bits(RHS.Bits), Size(RHS.Size), Capacity(RHS.Capacity) {
|
||||||
|
RHS.Bits = nullptr;
|
||||||
|
RHS.Size = RHS.Capacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~BitVector() { std::free(Bits); }
|
||||||
|
|
||||||
|
/// empty - Tests whether there are no bits in this bitvector.
|
||||||
|
bool empty() const { return Size == 0; }
|
||||||
|
|
||||||
|
/// size - Returns the number of bits in this bitvector.
|
||||||
|
size_type size() const { return Size; }
|
||||||
|
|
||||||
|
/// count - Returns the number of bits which are set.
|
||||||
|
size_type count() const {
|
||||||
|
unsigned NumBits = 0;
|
||||||
|
for (unsigned i = 0; i < NumBitWords(size()); ++i)
|
||||||
|
NumBits += countPopulation(Bits[i]);
|
||||||
|
return NumBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// any - Returns true if any bit is set.
|
||||||
|
bool any() const {
|
||||||
|
for (unsigned i = 0; i < NumBitWords(size()); ++i)
|
||||||
|
if (Bits[i] != 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// all - Returns true if all bits are set.
|
||||||
|
bool all() const {
|
||||||
|
for (unsigned i = 0; i < Size / BITWORD_SIZE; ++i)
|
||||||
|
if (Bits[i] != ~0UL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If bits remain check that they are ones. The unused bits are always zero.
|
||||||
|
if (unsigned Remainder = Size % BITWORD_SIZE)
|
||||||
|
return Bits[Size / BITWORD_SIZE] == (1UL << Remainder) - 1;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// none - Returns true if none of the bits are set.
|
||||||
|
bool none() const { return !any(); }
|
||||||
|
|
||||||
|
/// find_first - Returns the index of the first set bit, -1 if none
|
||||||
|
/// of the bits are set.
|
||||||
|
int find_first() const {
|
||||||
|
for (unsigned i = 0; i < NumBitWords(size()); ++i)
|
||||||
|
if (Bits[i] != 0)
|
||||||
|
return i * BITWORD_SIZE + countTrailingZeros(Bits[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// find_next - Returns the index of the next set bit following the
|
||||||
|
/// "Prev" bit. Returns -1 if the next set bit is not found.
|
||||||
|
int find_next(unsigned Prev) const {
|
||||||
|
++Prev;
|
||||||
|
if (Prev >= Size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
unsigned WordPos = Prev / BITWORD_SIZE;
|
||||||
|
unsigned BitPos = Prev % BITWORD_SIZE;
|
||||||
|
BitWord Copy = Bits[WordPos];
|
||||||
|
// Mask off previous bits.
|
||||||
|
Copy &= ~0UL << BitPos;
|
||||||
|
|
||||||
|
if (Copy != 0)
|
||||||
|
return WordPos * BITWORD_SIZE + countTrailingZeros(Copy);
|
||||||
|
|
||||||
|
// Check subsequent words.
|
||||||
|
for (unsigned i = WordPos + 1; i < NumBitWords(size()); ++i)
|
||||||
|
if (Bits[i] != 0)
|
||||||
|
return i * BITWORD_SIZE + countTrailingZeros(Bits[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// find_first_contiguous - Returns the index of the first contiguous
|
||||||
|
/// set of bits of "Length", -1 if no contiguous bits found.
|
||||||
|
int find_first_contiguous(unsigned Length, unsigned BucketSz) const {
|
||||||
|
for (int idx = find_first(); idx != -1; idx = find_next(idx)) {
|
||||||
|
if (idx + Length > size())
|
||||||
|
return -1;
|
||||||
|
bool good = true;
|
||||||
|
for (unsigned i = 0; i < Length; ++i) {
|
||||||
|
int ThisIdx = idx + i;
|
||||||
|
if (!test(ThisIdx)) {
|
||||||
|
good = false;
|
||||||
|
idx = ThisIdx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (good) {
|
||||||
|
unsigned space = BucketSz - (idx % BucketSz);
|
||||||
|
if (space >= Length)
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// clear - Clear all bits.
|
||||||
|
void clear() { Size = 0; }
|
||||||
|
|
||||||
|
/// resize - Grow or shrink the bitvector.
|
||||||
|
void resize(unsigned N, bool t = false) {
|
||||||
|
if (N > Capacity * BITWORD_SIZE) {
|
||||||
|
unsigned OldCapacity = Capacity;
|
||||||
|
grow(N);
|
||||||
|
init_words(&Bits[OldCapacity], (Capacity - OldCapacity), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set any old unused bits that are now included in the BitVector. This
|
||||||
|
// may set bits that are not included in the new vector, but we will clear
|
||||||
|
// them back out below.
|
||||||
|
if (N > Size)
|
||||||
|
set_unused_bits(t);
|
||||||
|
|
||||||
|
// Update the size, and clear out any bits that are now unused
|
||||||
|
unsigned OldSize = Size;
|
||||||
|
Size = N;
|
||||||
|
if (t || N < OldSize)
|
||||||
|
clear_unused_bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(unsigned N) {
|
||||||
|
if (N > Capacity * BITWORD_SIZE)
|
||||||
|
grow(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set, reset, flip
|
||||||
|
BitVector& set() {
|
||||||
|
init_words(Bits, Capacity, true);
|
||||||
|
clear_unused_bits();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector& set(unsigned Idx) {
|
||||||
|
assert(Bits && "Bits never allocated");
|
||||||
|
Bits[Idx / BITWORD_SIZE] |= BitWord(1) << (Idx % BITWORD_SIZE);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set - Efficiently set a range of bits in [I, E)
|
||||||
|
BitVector& set(unsigned I, unsigned E) {
|
||||||
|
assert(I <= E && "Attempted to set backwards range!");
|
||||||
|
assert(E <= size() && "Attempted to set out-of-bounds range!");
|
||||||
|
|
||||||
|
if (I == E)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
if (I / BITWORD_SIZE == E / BITWORD_SIZE) {
|
||||||
|
BitWord EMask = 1UL << (E % BITWORD_SIZE);
|
||||||
|
BitWord IMask = 1UL << (I % BITWORD_SIZE);
|
||||||
|
BitWord Mask = EMask - IMask;
|
||||||
|
Bits[I / BITWORD_SIZE] |= Mask;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitWord PrefixMask = ~0UL << (I % BITWORD_SIZE);
|
||||||
|
Bits[I / BITWORD_SIZE] |= PrefixMask;
|
||||||
|
I = alignTo(I, BITWORD_SIZE);
|
||||||
|
|
||||||
|
for (; I + BITWORD_SIZE <= E; I += BITWORD_SIZE)
|
||||||
|
Bits[I / BITWORD_SIZE] = ~0UL;
|
||||||
|
|
||||||
|
BitWord PostfixMask = (1UL << (E % BITWORD_SIZE)) - 1;
|
||||||
|
if (I < E)
|
||||||
|
Bits[I / BITWORD_SIZE] |= PostfixMask;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector& reset() {
|
||||||
|
init_words(Bits, Capacity, false);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector& reset(unsigned Idx) {
|
||||||
|
Bits[Idx / BITWORD_SIZE] &= ~(BitWord(1) << (Idx % BITWORD_SIZE));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// reset - Efficiently reset a range of bits in [I, E)
|
||||||
|
BitVector& reset(unsigned I, unsigned E) {
|
||||||
|
assert(I <= E && "Attempted to reset backwards range!");
|
||||||
|
assert(E <= size() && "Attempted to reset out-of-bounds range!");
|
||||||
|
|
||||||
|
if (I == E)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
if (I / BITWORD_SIZE == E / BITWORD_SIZE) {
|
||||||
|
BitWord EMask = 1UL << (E % BITWORD_SIZE);
|
||||||
|
BitWord IMask = 1UL << (I % BITWORD_SIZE);
|
||||||
|
BitWord Mask = EMask - IMask;
|
||||||
|
Bits[I / BITWORD_SIZE] &= ~Mask;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitWord PrefixMask = ~0UL << (I % BITWORD_SIZE);
|
||||||
|
Bits[I / BITWORD_SIZE] &= ~PrefixMask;
|
||||||
|
I = alignTo(I, BITWORD_SIZE);
|
||||||
|
|
||||||
|
for (; I + BITWORD_SIZE <= E; I += BITWORD_SIZE)
|
||||||
|
Bits[I / BITWORD_SIZE] = 0UL;
|
||||||
|
|
||||||
|
BitWord PostfixMask = (1UL << (E % BITWORD_SIZE)) - 1;
|
||||||
|
if (I < E)
|
||||||
|
Bits[I / BITWORD_SIZE] &= ~PostfixMask;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector& flip() {
|
||||||
|
for (unsigned i = 0; i < NumBitWords(size()); ++i)
|
||||||
|
Bits[i] = ~Bits[i];
|
||||||
|
clear_unused_bits();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector& flip(unsigned Idx) {
|
||||||
|
Bits[Idx / BITWORD_SIZE] ^= BitWord(1) << (Idx % BITWORD_SIZE);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indexing.
|
||||||
|
reference operator[](unsigned Idx) {
|
||||||
|
assert(Idx < Size && "Out-of-bounds Bit access.");
|
||||||
|
return reference(*this, Idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator[](unsigned Idx) const {
|
||||||
|
assert(Idx < Size && "Out-of-bounds Bit access.");
|
||||||
|
BitWord Mask = BitWord(1) << (Idx % BITWORD_SIZE);
|
||||||
|
return (Bits[Idx / BITWORD_SIZE] & Mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test(unsigned Idx) const { return (*this)[Idx]; }
|
||||||
|
|
||||||
|
/// Test if any common bits are set.
|
||||||
|
bool anyCommon(const BitVector& RHS) const {
|
||||||
|
unsigned ThisWords = NumBitWords(size());
|
||||||
|
unsigned RHSWords = NumBitWords(RHS.size());
|
||||||
|
for (unsigned i = 0, e = std::min(ThisWords, RHSWords); i != e; ++i)
|
||||||
|
if (Bits[i] & RHS.Bits[i])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparison operators.
|
||||||
|
bool operator==(const BitVector& RHS) const {
|
||||||
|
unsigned ThisWords = NumBitWords(size());
|
||||||
|
unsigned RHSWords = NumBitWords(RHS.size());
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
|
||||||
|
if (Bits[i] != RHS.Bits[i])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Verify that any extra words are all zeros.
|
||||||
|
if (i != ThisWords) {
|
||||||
|
for (; i != ThisWords; ++i)
|
||||||
|
if (Bits[i])
|
||||||
|
return false;
|
||||||
|
} else if (i != RHSWords) {
|
||||||
|
for (; i != RHSWords; ++i)
|
||||||
|
if (RHS.Bits[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const BitVector& RHS) const { return !(*this == RHS); }
|
||||||
|
|
||||||
|
/// Intersection, union, disjoint union.
|
||||||
|
BitVector& operator&=(const BitVector& RHS) {
|
||||||
|
unsigned ThisWords = NumBitWords(size());
|
||||||
|
unsigned RHSWords = NumBitWords(RHS.size());
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
|
||||||
|
Bits[i] &= RHS.Bits[i];
|
||||||
|
|
||||||
|
// Any bits that are just in this bitvector become zero, because they aren't
|
||||||
|
// in the RHS bit vector. Any words only in RHS are ignored because they
|
||||||
|
// are already zero in the LHS.
|
||||||
|
for (; i != ThisWords; ++i)
|
||||||
|
Bits[i] = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// reset - Reset bits that are set in RHS. Same as *this &= ~RHS.
|
||||||
|
BitVector& reset(const BitVector& RHS) {
|
||||||
|
unsigned ThisWords = NumBitWords(size());
|
||||||
|
unsigned RHSWords = NumBitWords(RHS.size());
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
|
||||||
|
Bits[i] &= ~RHS.Bits[i];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// test - Check if (This - RHS) is zero.
|
||||||
|
/// This is the same as reset(RHS) and any().
|
||||||
|
bool test(const BitVector& RHS) const {
|
||||||
|
unsigned ThisWords = NumBitWords(size());
|
||||||
|
unsigned RHSWords = NumBitWords(RHS.size());
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
|
||||||
|
if ((Bits[i] & ~RHS.Bits[i]) != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (; i != ThisWords; ++i)
|
||||||
|
if (Bits[i] != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector& operator|=(const BitVector& RHS) {
|
||||||
|
if (size() < RHS.size())
|
||||||
|
resize(RHS.size());
|
||||||
|
for (size_t i = 0, e = NumBitWords(RHS.size()); i != e; ++i)
|
||||||
|
Bits[i] |= RHS.Bits[i];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector& operator^=(const BitVector& RHS) {
|
||||||
|
if (size() < RHS.size())
|
||||||
|
resize(RHS.size());
|
||||||
|
for (size_t i = 0, e = NumBitWords(RHS.size()); i != e; ++i)
|
||||||
|
Bits[i] ^= RHS.Bits[i];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assignment operator.
|
||||||
|
const BitVector& operator=(const BitVector& RHS) {
|
||||||
|
if (this == &RHS)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
Size = RHS.size();
|
||||||
|
unsigned RHSWords = NumBitWords(Size);
|
||||||
|
if (Size <= Capacity * BITWORD_SIZE) {
|
||||||
|
if (Size)
|
||||||
|
std::memcpy(Bits, RHS.Bits, RHSWords * sizeof(BitWord));
|
||||||
|
clear_unused_bits();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow the bitvector to have enough elements.
|
||||||
|
Capacity = RHSWords;
|
||||||
|
assert(Capacity > 0 && "negative capacity?");
|
||||||
|
BitWord* NewBits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
|
||||||
|
std::memcpy(NewBits, RHS.Bits, Capacity * sizeof(BitWord));
|
||||||
|
|
||||||
|
// Destroy the old bits.
|
||||||
|
std::free(Bits);
|
||||||
|
Bits = NewBits;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BitVector& operator=(BitVector&& RHS) {
|
||||||
|
if (this == &RHS)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
std::free(Bits);
|
||||||
|
Bits = RHS.Bits;
|
||||||
|
Size = RHS.Size;
|
||||||
|
Capacity = RHS.Capacity;
|
||||||
|
|
||||||
|
RHS.Bits = nullptr;
|
||||||
|
RHS.Size = RHS.Capacity = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(BitVector& RHS) {
|
||||||
|
std::swap(Bits, RHS.Bits);
|
||||||
|
std::swap(Size, RHS.Size);
|
||||||
|
std::swap(Capacity, RHS.Capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
//===--------------------------------------------------------------------===//
|
||||||
|
// Portable bit mask operations.
|
||||||
|
//===--------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// These methods all operate on arrays of uint32_t, each holding 32 bits. The
|
||||||
|
// fixed word size makes it easier to work with literal bit vector constants
|
||||||
|
// in portable code.
|
||||||
|
//
|
||||||
|
// The LSB in each word is the lowest numbered bit. The size of a portable
|
||||||
|
// bit mask is always a whole multiple of 32 bits. If no bit mask size is
|
||||||
|
// given, the bit mask is assumed to cover the entire BitVector.
|
||||||
|
|
||||||
|
/// setBitsInMask - Add '1' bits from Mask to this vector. Don't resize.
|
||||||
|
/// This computes "*this |= Mask".
|
||||||
|
void setBitsInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<true, false>(Mask, MaskWords); }
|
||||||
|
|
||||||
|
/// clearBitsInMask - Clear any bits in this vector that are set in Mask.
|
||||||
|
/// Don't resize. This computes "*this &= ~Mask".
|
||||||
|
void clearBitsInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<false, false>(Mask, MaskWords); }
|
||||||
|
|
||||||
|
/// setBitsNotInMask - Add a bit to this vector for every '0' bit in Mask.
|
||||||
|
/// Don't resize. This computes "*this |= ~Mask".
|
||||||
|
void setBitsNotInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<true, true>(Mask, MaskWords); }
|
||||||
|
|
||||||
|
/// clearBitsNotInMask - Clear a bit in this vector for every '0' bit in Mask.
|
||||||
|
/// Don't resize. This computes "*this &= Mask".
|
||||||
|
void clearBitsNotInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<false, true>(Mask, MaskWords); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned NumBitWords(unsigned S) const { return (S + BITWORD_SIZE - 1) / BITWORD_SIZE; }
|
||||||
|
|
||||||
|
// Set the unused bits in the high words.
|
||||||
|
void set_unused_bits(bool t = true) {
|
||||||
|
// Set high words first.
|
||||||
|
unsigned UsedWords = NumBitWords(Size);
|
||||||
|
if (Capacity > UsedWords)
|
||||||
|
init_words(&Bits[UsedWords], (Capacity - UsedWords), t);
|
||||||
|
|
||||||
|
// Then set any stray high bits of the last used word.
|
||||||
|
unsigned ExtraBits = Size % BITWORD_SIZE;
|
||||||
|
if (ExtraBits) {
|
||||||
|
BitWord ExtraBitMask = ~0UL << ExtraBits;
|
||||||
|
if (t)
|
||||||
|
Bits[UsedWords - 1] |= ExtraBitMask;
|
||||||
|
else
|
||||||
|
Bits[UsedWords - 1] &= ~ExtraBitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the unused bits in the high words.
|
||||||
|
void clear_unused_bits() { set_unused_bits(false); }
|
||||||
|
|
||||||
|
void grow(unsigned NewSize) {
|
||||||
|
Capacity = std::max(NumBitWords(NewSize), Capacity * 2);
|
||||||
|
assert(Capacity > 0 && "realloc-ing zero space");
|
||||||
|
Bits = (BitWord*)std::realloc(Bits, Capacity * sizeof(BitWord));
|
||||||
|
|
||||||
|
clear_unused_bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_words(BitWord* B, unsigned NumWords, bool t) { memset(B, 0 - (int)t, NumWords * sizeof(BitWord)); }
|
||||||
|
|
||||||
|
template <bool AddBits, bool InvertMask>
|
||||||
|
void applyMask(const uint32_t* Mask, unsigned MaskWords) {
|
||||||
|
static_assert(BITWORD_SIZE % 32 == 0, "Unsupported BitWord size.");
|
||||||
|
MaskWords = std::min(MaskWords, (size() + 31) / 32);
|
||||||
|
const unsigned Scale = BITWORD_SIZE / 32;
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; MaskWords >= Scale; ++i, MaskWords -= Scale) {
|
||||||
|
BitWord BW = Bits[i];
|
||||||
|
// This inner loop should unroll completely when BITWORD_SIZE > 32.
|
||||||
|
for (unsigned b = 0; b != BITWORD_SIZE; b += 32) {
|
||||||
|
uint32_t M = *Mask++;
|
||||||
|
if (InvertMask)
|
||||||
|
M = ~M;
|
||||||
|
if (AddBits)
|
||||||
|
BW |= BitWord(M) << b;
|
||||||
|
else
|
||||||
|
BW &= ~(BitWord(M) << b);
|
||||||
|
}
|
||||||
|
Bits[i] = BW;
|
||||||
|
}
|
||||||
|
for (unsigned b = 0; MaskWords; b += 32, --MaskWords) {
|
||||||
|
uint32_t M = *Mask++;
|
||||||
|
if (InvertMask)
|
||||||
|
M = ~M;
|
||||||
|
if (AddBits)
|
||||||
|
Bits[i] |= BitWord(M) << b;
|
||||||
|
else
|
||||||
|
Bits[i] &= ~(BitWord(M) << b);
|
||||||
|
}
|
||||||
|
if (AddBits)
|
||||||
|
clear_unused_bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Return the size (in bytes) of the bit vector.
|
||||||
|
size_t getMemorySize() const { return Capacity * sizeof(BitWord); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline size_t capacity_in_bytes(const BitVector& X) { return X.getMemorySize(); }
|
||||||
|
|
||||||
|
} // end namespace llvm
|
||||||
|
} // end namespace hecl
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
/// Implement std::swap in terms of BitVector swap.
|
||||||
|
inline void swap(hecl::llvm::BitVector& LHS, hecl::llvm::BitVector& RHS) { LHS.swap(RHS); }
|
||||||
|
} // end namespace std
|
|
@ -0,0 +1,938 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN 1
|
||||||
|
#endif
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "hecl/hecl.hpp"
|
||||||
|
#include "hecl/Backend.hpp"
|
||||||
|
#include "hecl/HMDLMeta.hpp"
|
||||||
|
#include "hecl/TypedVariant.hpp"
|
||||||
|
|
||||||
|
#include <athena/Types.hpp>
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
#include <logvisor/logvisor.hpp>
|
||||||
|
|
||||||
|
namespace hecl::blender {
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
class Connection;
|
||||||
|
class HMDLBuffers;
|
||||||
|
|
||||||
|
extern logvisor::Module BlenderLog;
|
||||||
|
|
||||||
|
struct PoolSkinIndex {
|
||||||
|
std::size_t m_poolSz = 0;
|
||||||
|
std::unique_ptr<uint32_t[]> m_poolToSkinIndex;
|
||||||
|
|
||||||
|
void allocate(std::size_t poolSz) {
|
||||||
|
m_poolSz = poolSz;
|
||||||
|
if (poolSz)
|
||||||
|
m_poolToSkinIndex.reset(new uint32_t[poolSz]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ANIMCurveType { Rotate, Translate, Scale };
|
||||||
|
|
||||||
|
class ANIMOutStream {
|
||||||
|
Connection* m_parent;
|
||||||
|
unsigned m_curCount = 0;
|
||||||
|
unsigned m_totalCount = 0;
|
||||||
|
bool m_inCurve = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using CurveType = ANIMCurveType;
|
||||||
|
ANIMOutStream(Connection* parent);
|
||||||
|
~ANIMOutStream();
|
||||||
|
void changeCurve(CurveType type, unsigned crvIdx, unsigned keyCount);
|
||||||
|
void write(unsigned frame, float val);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PyOutStream : public std::ostream {
|
||||||
|
friend class Connection;
|
||||||
|
Connection* m_parent;
|
||||||
|
struct StreamBuf : std::streambuf {
|
||||||
|
PyOutStream& m_parent;
|
||||||
|
std::string m_lineBuf;
|
||||||
|
bool m_deleteOnError;
|
||||||
|
StreamBuf(PyOutStream& parent, bool deleteOnError) : m_parent(parent), m_deleteOnError(deleteOnError) {}
|
||||||
|
StreamBuf(const StreamBuf& other) = delete;
|
||||||
|
StreamBuf(StreamBuf&& other) = default;
|
||||||
|
bool sendLine(std::string_view line);
|
||||||
|
int_type overflow(int_type ch) override;
|
||||||
|
std::streamsize xsputn(const char_type* __s, std::streamsize __n) override;
|
||||||
|
} m_sbuf;
|
||||||
|
PyOutStream(Connection* parent, bool deleteOnError);
|
||||||
|
|
||||||
|
public:
|
||||||
|
PyOutStream(const PyOutStream& other) = delete;
|
||||||
|
PyOutStream(PyOutStream&& other) : std::ostream(&m_sbuf), m_parent(other.m_parent), m_sbuf(std::move(other.m_sbuf)) {
|
||||||
|
other.m_parent = nullptr;
|
||||||
|
}
|
||||||
|
~PyOutStream() override { close(); }
|
||||||
|
void close();
|
||||||
|
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
|
||||||
|
void format(const S& format, Args&&... args);
|
||||||
|
void linkBlend(std::string_view target, std::string_view objName, bool link = true);
|
||||||
|
void linkArmature(std::string_view target, std::string_view armName);
|
||||||
|
void linkMesh(std::string_view target, std::string_view meshName);
|
||||||
|
void linkBackground(std::string_view target, std::string_view sceneName = {});
|
||||||
|
void AABBToBMesh(const atVec3f& min, const atVec3f& max);
|
||||||
|
void centerView();
|
||||||
|
|
||||||
|
ANIMOutStream beginANIMCurve() { return ANIMOutStream(m_parent); }
|
||||||
|
Connection& getConnection() { return *m_parent; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Vector types with integrated stream reading constructor */
|
||||||
|
struct Vector2f {
|
||||||
|
atVec2f val;
|
||||||
|
Vector2f() = default;
|
||||||
|
void read(Connection& conn);
|
||||||
|
Vector2f(Connection& conn) { read(conn); }
|
||||||
|
operator const atVec2f&() const { return val; }
|
||||||
|
bool operator==(const Vector2f& other) const {
|
||||||
|
return val.simd[0] == other.val.simd[0] &&
|
||||||
|
val.simd[1] == other.val.simd[1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct Vector3f {
|
||||||
|
atVec3f val;
|
||||||
|
Vector3f() = default;
|
||||||
|
void read(Connection& conn);
|
||||||
|
Vector3f(Connection& conn) { read(conn); }
|
||||||
|
operator const atVec3f&() const { return val; }
|
||||||
|
bool operator==(const Vector3f& other) const {
|
||||||
|
return val.simd[0] == other.val.simd[0] &&
|
||||||
|
val.simd[1] == other.val.simd[1] &&
|
||||||
|
val.simd[2] == other.val.simd[2];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct Vector4f {
|
||||||
|
atVec4f val;
|
||||||
|
Vector4f() = default;
|
||||||
|
void read(Connection& conn);
|
||||||
|
Vector4f(Connection& conn) { read(conn); }
|
||||||
|
operator const atVec4f&() const { return val; }
|
||||||
|
bool operator==(const Vector4f& other) const {
|
||||||
|
return val.simd[0] == other.val.simd[0] &&
|
||||||
|
val.simd[1] == other.val.simd[1] &&
|
||||||
|
val.simd[2] == other.val.simd[2] &&
|
||||||
|
val.simd[3] == other.val.simd[3];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct Matrix3f {
|
||||||
|
std::array<atVec3f, 3> m;
|
||||||
|
atVec3f& operator[](std::size_t idx) { return m[idx]; }
|
||||||
|
const atVec3f& operator[](std::size_t idx) const { return m[idx]; }
|
||||||
|
};
|
||||||
|
struct Matrix4f {
|
||||||
|
std::array<atVec4f, 4> val;
|
||||||
|
Matrix4f() = default;
|
||||||
|
Matrix4f(Connection& conn) { read(conn); }
|
||||||
|
void read(Connection& conn);
|
||||||
|
const atVec4f& operator[](std::size_t idx) const { return val[idx]; }
|
||||||
|
};
|
||||||
|
struct Index {
|
||||||
|
uint32_t val;
|
||||||
|
Index() = default;
|
||||||
|
void read(Connection& conn);
|
||||||
|
Index(Connection& conn) { read(conn); }
|
||||||
|
operator const uint32_t&() const { return val; }
|
||||||
|
};
|
||||||
|
struct Float {
|
||||||
|
float val;
|
||||||
|
Float() = default;
|
||||||
|
void read(Connection& conn);
|
||||||
|
Float(Connection& conn) { read(conn); }
|
||||||
|
operator const float&() const { return val; }
|
||||||
|
};
|
||||||
|
struct Boolean {
|
||||||
|
bool val;
|
||||||
|
Boolean() = default;
|
||||||
|
void read(Connection& conn);
|
||||||
|
Boolean(Connection& conn) { read(conn); }
|
||||||
|
operator const bool&() const { return val; }
|
||||||
|
};
|
||||||
|
|
||||||
|
atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec);
|
||||||
|
atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec);
|
||||||
|
|
||||||
|
/** Intermediate material representation */
|
||||||
|
struct Material {
|
||||||
|
enum class ShaderType : uint32_t {
|
||||||
|
Invalid = 0,
|
||||||
|
RetroShader = 'RSHD',
|
||||||
|
RetroDynamicShader = 'RDYN',
|
||||||
|
RetroDynamicAlphaShader = 'RDAL',
|
||||||
|
RetroDynamicCharacterShader = 'RCHR',
|
||||||
|
};
|
||||||
|
enum class ChunkType : uint32_t {
|
||||||
|
Invalid = 0,
|
||||||
|
TexturePass = 'PASS',
|
||||||
|
ColorPass = 'CLR ',
|
||||||
|
};
|
||||||
|
enum class PassType : uint32_t {
|
||||||
|
Invalid = 0,
|
||||||
|
Lightmap = 'LMAP',
|
||||||
|
Diffuse = 'DIFF',
|
||||||
|
Emissive = 'EMIS',
|
||||||
|
Specular = 'SPEC',
|
||||||
|
ExtendedSpecular = 'ESPC',
|
||||||
|
Reflection = 'REFL',
|
||||||
|
IndirectTex = 'INDR',
|
||||||
|
Alpha = 'ALPH',
|
||||||
|
};
|
||||||
|
static constexpr std::string_view PassTypeToString(PassType tp) {
|
||||||
|
switch (tp) {
|
||||||
|
case PassType::Lightmap: return "lightmap"sv;
|
||||||
|
case PassType::Diffuse: return "diffuse"sv;
|
||||||
|
case PassType::Emissive: return "emissive"sv;
|
||||||
|
case PassType::Specular: return "specular"sv;
|
||||||
|
case PassType::ExtendedSpecular: return "extendedSpecular"sv;
|
||||||
|
case PassType::Reflection: return "reflection"sv;
|
||||||
|
case PassType::Alpha: return "alpha"sv;
|
||||||
|
default:
|
||||||
|
assert(false && "Unknown pass type");
|
||||||
|
return ""sv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enum class UVAnimType : uint8_t {
|
||||||
|
MvInvNoTranslation,
|
||||||
|
MvInv,
|
||||||
|
Scroll,
|
||||||
|
Rotation,
|
||||||
|
HStrip,
|
||||||
|
VStrip,
|
||||||
|
Model,
|
||||||
|
CylinderEnvironment,
|
||||||
|
Eight,
|
||||||
|
Invalid = UINT8_MAX
|
||||||
|
};
|
||||||
|
using TexCoordSource = hecl::Backend::TexCoordSource;
|
||||||
|
struct PASS : hecl::TypedRecord<ChunkType::TexturePass> {
|
||||||
|
PassType type;
|
||||||
|
ProjectPath tex;
|
||||||
|
TexCoordSource source;
|
||||||
|
UVAnimType uvAnimType;
|
||||||
|
std::array<float, 9> uvAnimParms = {};
|
||||||
|
bool alpha;
|
||||||
|
explicit PASS(Connection& conn);
|
||||||
|
bool operator==(const PASS& other) const {
|
||||||
|
return type == other.type &&
|
||||||
|
tex == other.tex &&
|
||||||
|
source == other.source &&
|
||||||
|
uvAnimType == other.uvAnimType &&
|
||||||
|
uvAnimParms == other.uvAnimParms &&
|
||||||
|
alpha == other.alpha;
|
||||||
|
}
|
||||||
|
void hash(XXH64_state_t* st) const {
|
||||||
|
XXH64_update(st, &type, sizeof(type));
|
||||||
|
XXH64_update(st, &source, sizeof(source));
|
||||||
|
XXH64_update(st, &uvAnimType, sizeof(uvAnimType));
|
||||||
|
XXH64_update(st, &alpha, sizeof(alpha));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct CLR : hecl::TypedRecord<ChunkType::ColorPass> {
|
||||||
|
PassType type;
|
||||||
|
Vector4f color;
|
||||||
|
explicit CLR(Connection& conn);
|
||||||
|
bool operator==(const CLR& other) const {
|
||||||
|
return type == other.type && color == other.color;
|
||||||
|
}
|
||||||
|
void hash(XXH64_state_t* st) const {
|
||||||
|
XXH64_update(st, &type, sizeof(type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using Chunk = hecl::TypedVariant<PASS, CLR>;
|
||||||
|
|
||||||
|
enum class BlendMode : uint32_t {
|
||||||
|
Opaque = 0,
|
||||||
|
Alpha = 1,
|
||||||
|
Additive = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
uint32_t passIndex;
|
||||||
|
ShaderType shaderType;
|
||||||
|
std::vector<Chunk> chunks;
|
||||||
|
std::unordered_map<std::string, int32_t> iprops;
|
||||||
|
BlendMode blendMode = BlendMode::Opaque;
|
||||||
|
|
||||||
|
explicit Material(Connection& conn);
|
||||||
|
bool operator==(const Material& other) const {
|
||||||
|
return chunks == other.chunks && iprops == other.iprops && blendMode == other.blendMode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate mesh representation prepared by blender from a single mesh object */
|
||||||
|
struct Mesh {
|
||||||
|
static constexpr std::size_t MaxColorLayers = 4;
|
||||||
|
static constexpr std::size_t MaxUVLayers = 8;
|
||||||
|
static constexpr std::size_t MaxSkinEntries = 16;
|
||||||
|
|
||||||
|
HMDLTopology topology;
|
||||||
|
|
||||||
|
/* Object transform in scene */
|
||||||
|
Matrix4f sceneXf;
|
||||||
|
|
||||||
|
/* Cumulative AABB */
|
||||||
|
Vector3f aabbMin;
|
||||||
|
Vector3f aabbMax;
|
||||||
|
|
||||||
|
std::vector<std::vector<Material>> materialSets;
|
||||||
|
|
||||||
|
/* Vertex buffer data */
|
||||||
|
std::vector<Vector3f> pos;
|
||||||
|
std::vector<Vector3f> norm;
|
||||||
|
uint32_t colorLayerCount = 0;
|
||||||
|
std::vector<Vector3f> color;
|
||||||
|
uint32_t uvLayerCount = 0;
|
||||||
|
std::vector<Vector2f> uv;
|
||||||
|
uint32_t luvLayerCount = 0;
|
||||||
|
std::vector<Vector2f> luv;
|
||||||
|
|
||||||
|
/* Skinning data */
|
||||||
|
std::vector<std::string> boneNames;
|
||||||
|
struct SkinBind {
|
||||||
|
uint32_t vg_idx = UINT32_MAX;
|
||||||
|
float weight = 0.f;
|
||||||
|
SkinBind() = default;
|
||||||
|
explicit SkinBind(Connection& conn);
|
||||||
|
bool valid() const { return vg_idx != UINT32_MAX; }
|
||||||
|
bool operator==(const SkinBind& other) const { return vg_idx == other.vg_idx && weight == other.weight; }
|
||||||
|
};
|
||||||
|
std::vector<std::array<SkinBind, MaxSkinEntries>> skins;
|
||||||
|
std::vector<std::size_t> contiguousSkinVertCounts;
|
||||||
|
|
||||||
|
static std::size_t countSkinBinds(const std::array<SkinBind, MaxSkinEntries>& arr) {
|
||||||
|
std::size_t ret = 0;
|
||||||
|
for (const auto& b : arr)
|
||||||
|
if (b.valid())
|
||||||
|
++ret;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void normalizeSkinBinds();
|
||||||
|
|
||||||
|
/** Islands of the same material/skinBank are represented here */
|
||||||
|
struct Surface {
|
||||||
|
Vector3f centroid;
|
||||||
|
uint32_t materialIdx;
|
||||||
|
Vector3f aabbMin;
|
||||||
|
Vector3f aabbMax;
|
||||||
|
Vector3f reflectionNormal;
|
||||||
|
uint32_t skinBankIdx;
|
||||||
|
|
||||||
|
/** Vertex indexing data (all primitives joined as degenerate tri-strip) */
|
||||||
|
struct Vert {
|
||||||
|
uint32_t iPos = 0xffffffff;
|
||||||
|
uint32_t iNorm = 0xffffffff;
|
||||||
|
std::array<uint32_t, 4> iColor = {0xffffffff};
|
||||||
|
std::array<uint32_t, 8> iUv = {0xffffffff};
|
||||||
|
uint32_t iSkin = 0xffffffff;
|
||||||
|
uint32_t iBankSkin = 0xffffffff;
|
||||||
|
|
||||||
|
bool operator==(const Vert& other) const;
|
||||||
|
};
|
||||||
|
std::vector<Vert> verts;
|
||||||
|
};
|
||||||
|
std::vector<Surface> surfaces;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> customProps;
|
||||||
|
|
||||||
|
struct SkinBanks {
|
||||||
|
struct Bank {
|
||||||
|
std::vector<uint32_t> m_skinIdxs;
|
||||||
|
std::vector<uint32_t> m_boneIdxs;
|
||||||
|
|
||||||
|
void addSkins(const Mesh& parent, const std::vector<uint32_t>& skinIdxs);
|
||||||
|
};
|
||||||
|
std::vector<Bank> banks;
|
||||||
|
std::vector<Bank>::iterator addSkinBank(int skinSlotCount);
|
||||||
|
uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount);
|
||||||
|
} skinBanks;
|
||||||
|
|
||||||
|
Mesh(Connection& conn, HMDLTopology topology, int skinSlotCount, bool useLuvs = false);
|
||||||
|
|
||||||
|
Mesh getContiguousSkinningVersion() const;
|
||||||
|
|
||||||
|
/** Prepares mesh representation for indexed access on modern APIs.
|
||||||
|
* Mesh must remain resident for accessing reference members
|
||||||
|
*/
|
||||||
|
HMDLBuffers getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinIndex) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate collision mesh representation prepared by blender from a single mesh object */
|
||||||
|
struct ColMesh {
|
||||||
|
/** HECL source and metadata of each material */
|
||||||
|
struct Material {
|
||||||
|
std::string name;
|
||||||
|
bool unknown;
|
||||||
|
bool surfaceStone;
|
||||||
|
bool surfaceMetal;
|
||||||
|
bool surfaceGrass;
|
||||||
|
bool surfaceIce;
|
||||||
|
bool pillar;
|
||||||
|
bool surfaceMetalGrating;
|
||||||
|
bool surfacePhazon;
|
||||||
|
bool surfaceDirt;
|
||||||
|
bool surfaceLava;
|
||||||
|
bool surfaceSPMetal;
|
||||||
|
bool surfaceLavaStone;
|
||||||
|
bool surfaceSnow;
|
||||||
|
bool surfaceMudSlow;
|
||||||
|
bool surfaceFabric;
|
||||||
|
bool halfPipe;
|
||||||
|
bool surfaceMud;
|
||||||
|
bool surfaceGlass;
|
||||||
|
bool unused3;
|
||||||
|
bool unused4;
|
||||||
|
bool surfaceShield;
|
||||||
|
bool surfaceSand;
|
||||||
|
bool surfaceMothOrSeedOrganics;
|
||||||
|
bool surfaceWeb;
|
||||||
|
bool projPassthrough;
|
||||||
|
bool solid;
|
||||||
|
bool noPlatformCollision;
|
||||||
|
bool camPassthrough;
|
||||||
|
bool surfaceWood;
|
||||||
|
bool surfaceOrganic;
|
||||||
|
bool noEdgeCollision;
|
||||||
|
bool surfaceRubber;
|
||||||
|
bool seeThrough;
|
||||||
|
bool scanPassthrough;
|
||||||
|
bool aiPassthrough;
|
||||||
|
bool ceiling;
|
||||||
|
bool wall;
|
||||||
|
bool floor;
|
||||||
|
bool aiBlock;
|
||||||
|
bool jumpNotAllowed;
|
||||||
|
bool spiderBall;
|
||||||
|
bool screwAttackWallJump;
|
||||||
|
|
||||||
|
Material(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Material> materials;
|
||||||
|
|
||||||
|
std::vector<Vector3f> verts;
|
||||||
|
|
||||||
|
struct Edge {
|
||||||
|
std::array<uint32_t, 2> verts;
|
||||||
|
bool seam;
|
||||||
|
Edge(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Edge> edges;
|
||||||
|
|
||||||
|
struct Triangle {
|
||||||
|
std::array<uint32_t, 3> edges;
|
||||||
|
uint32_t matIdx;
|
||||||
|
bool flip;
|
||||||
|
Triangle(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Triangle> trianges;
|
||||||
|
|
||||||
|
ColMesh(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate world representation */
|
||||||
|
struct World {
|
||||||
|
struct Area {
|
||||||
|
ProjectPath path;
|
||||||
|
std::array<Vector3f, 2> aabb;
|
||||||
|
Matrix4f transform;
|
||||||
|
struct Dock {
|
||||||
|
std::array<Vector3f, 4> verts;
|
||||||
|
Index targetArea;
|
||||||
|
Index targetDock;
|
||||||
|
Dock(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Dock> docks;
|
||||||
|
Area(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Area> areas;
|
||||||
|
World(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate lamp representation */
|
||||||
|
struct Light {
|
||||||
|
/* Object transform in scene */
|
||||||
|
Matrix4f sceneXf;
|
||||||
|
Vector3f color;
|
||||||
|
|
||||||
|
uint32_t layer;
|
||||||
|
|
||||||
|
enum class Type : uint32_t { Ambient, Directional, Custom, Spot } type;
|
||||||
|
|
||||||
|
float energy;
|
||||||
|
float spotCutoff;
|
||||||
|
float constant;
|
||||||
|
float linear;
|
||||||
|
float quadratic;
|
||||||
|
bool shadow;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
Light(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate MapArea representation */
|
||||||
|
struct MapArea {
|
||||||
|
uint32_t visType;
|
||||||
|
std::vector<Vector3f> verts;
|
||||||
|
std::vector<uint32_t> indices;
|
||||||
|
struct Surface {
|
||||||
|
Vector3f normal;
|
||||||
|
Vector3f centerOfMass;
|
||||||
|
uint32_t start;
|
||||||
|
uint32_t count;
|
||||||
|
std::vector<std::pair<uint32_t, uint32_t>> borders;
|
||||||
|
Surface(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Surface> surfaces;
|
||||||
|
struct POI {
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t visMode;
|
||||||
|
uint32_t objid;
|
||||||
|
Matrix4f xf;
|
||||||
|
POI(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<POI> pois;
|
||||||
|
MapArea(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate MapUniverse representation */
|
||||||
|
struct MapUniverse {
|
||||||
|
hecl::ProjectPath hexagonPath;
|
||||||
|
struct World {
|
||||||
|
std::string name;
|
||||||
|
Matrix4f xf;
|
||||||
|
std::vector<Matrix4f> hexagons;
|
||||||
|
Vector4f color;
|
||||||
|
hecl::ProjectPath worldPath;
|
||||||
|
World(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<World> worlds;
|
||||||
|
MapUniverse(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate bone representation used in Armature */
|
||||||
|
struct Bone {
|
||||||
|
std::string name;
|
||||||
|
Vector3f origin;
|
||||||
|
int32_t parent = -1;
|
||||||
|
std::vector<int32_t> children;
|
||||||
|
Bone(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate armature representation used in Actor */
|
||||||
|
struct Armature {
|
||||||
|
std::vector<Bone> bones;
|
||||||
|
const Bone* lookupBone(const char* name) const;
|
||||||
|
const Bone* getParent(const Bone* bone) const;
|
||||||
|
const Bone* getChild(const Bone* bone, std::size_t child) const;
|
||||||
|
const Bone* getRoot() const;
|
||||||
|
Armature(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate action representation used in Actor */
|
||||||
|
struct Action {
|
||||||
|
std::string name;
|
||||||
|
std::string animId;
|
||||||
|
float interval;
|
||||||
|
bool additive;
|
||||||
|
bool looping;
|
||||||
|
std::vector<int32_t> frames;
|
||||||
|
struct Channel {
|
||||||
|
std::string boneName;
|
||||||
|
uint32_t attrMask;
|
||||||
|
struct Key {
|
||||||
|
Vector4f rotation;
|
||||||
|
Vector3f position;
|
||||||
|
Vector3f scale;
|
||||||
|
Key(Connection& conn, uint32_t attrMask);
|
||||||
|
};
|
||||||
|
std::vector<Key> keys;
|
||||||
|
Channel(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Channel> channels;
|
||||||
|
std::vector<std::pair<Vector3f, Vector3f>> subtypeAABBs;
|
||||||
|
Action(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate actor representation prepared by blender from a single HECL actor blend */
|
||||||
|
struct Actor {
|
||||||
|
struct ActorArmature {
|
||||||
|
std::string name;
|
||||||
|
ProjectPath path;
|
||||||
|
std::optional<Armature> armature;
|
||||||
|
ActorArmature(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<ActorArmature> armatures;
|
||||||
|
|
||||||
|
struct Subtype {
|
||||||
|
std::string name;
|
||||||
|
std::string cskrId;
|
||||||
|
ProjectPath mesh;
|
||||||
|
int32_t armature = -1;
|
||||||
|
struct OverlayMesh {
|
||||||
|
std::string name;
|
||||||
|
std::string cskrId;
|
||||||
|
ProjectPath mesh;
|
||||||
|
OverlayMesh(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<OverlayMesh> overlayMeshes;
|
||||||
|
Subtype(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Subtype> subtypes;
|
||||||
|
struct Attachment {
|
||||||
|
std::string name;
|
||||||
|
std::string cskrId;
|
||||||
|
ProjectPath mesh;
|
||||||
|
int32_t armature = -1;
|
||||||
|
Attachment(Connection& conn);
|
||||||
|
};
|
||||||
|
std::vector<Attachment> attachments;
|
||||||
|
std::vector<Action> actions;
|
||||||
|
|
||||||
|
Actor(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Intermediate pathfinding representation prepared by blender */
|
||||||
|
struct PathMesh {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
PathMesh(Connection& conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DataStream {
|
||||||
|
friend class Connection;
|
||||||
|
Connection* m_parent;
|
||||||
|
DataStream(Connection* parent);
|
||||||
|
|
||||||
|
public:
|
||||||
|
DataStream(const DataStream& other) = delete;
|
||||||
|
DataStream(DataStream&& other) : m_parent(other.m_parent) { other.m_parent = nullptr; }
|
||||||
|
~DataStream() { close(); }
|
||||||
|
void close();
|
||||||
|
std::vector<std::string> getMeshList();
|
||||||
|
std::vector<std::string> getLightList();
|
||||||
|
std::pair<atVec3f, atVec3f> getMeshAABB();
|
||||||
|
static const char* MeshOutputModeString(HMDLTopology topology);
|
||||||
|
|
||||||
|
/** Compile mesh by context (MESH blends only) */
|
||||||
|
Mesh compileMesh(HMDLTopology topology, int skinSlotCount = 10);
|
||||||
|
|
||||||
|
/** Compile mesh by name (AREA blends only) */
|
||||||
|
Mesh compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount = 10, bool useLuv = false);
|
||||||
|
|
||||||
|
/** Compile collision mesh by name (AREA blends only) */
|
||||||
|
ColMesh compileColMesh(std::string_view name);
|
||||||
|
|
||||||
|
/** Compile all meshes as collision meshes (CMESH blends only) */
|
||||||
|
std::vector<ColMesh> compileColMeshes();
|
||||||
|
|
||||||
|
/** Compile world intermediate (WORLD blends only) */
|
||||||
|
World compileWorld();
|
||||||
|
|
||||||
|
/** Gather all lights in scene (AREA blends only) */
|
||||||
|
std::vector<Light> compileLights();
|
||||||
|
|
||||||
|
/** Get PathMesh from scene (PATH blends only) */
|
||||||
|
PathMesh compilePathMesh();
|
||||||
|
|
||||||
|
/** Compile GUI into FRME data (FRAME blends only) */
|
||||||
|
std::vector<uint8_t> compileGuiFrame(int version);
|
||||||
|
|
||||||
|
/** Gather all texture paths in scene */
|
||||||
|
std::vector<ProjectPath> getTextures();
|
||||||
|
|
||||||
|
Actor compileActor();
|
||||||
|
Actor compileActorCharacterOnly();
|
||||||
|
Armature compileArmature();
|
||||||
|
Action compileActionChannelsOnly(std::string_view name);
|
||||||
|
std::vector<std::pair<std::string, std::string>> getSubtypeNames();
|
||||||
|
std::vector<std::pair<std::string, std::string>> getActionNames();
|
||||||
|
std::vector<std::pair<std::string, std::string>> getSubtypeOverlayNames(std::string_view name);
|
||||||
|
std::vector<std::pair<std::string, std::string>> getAttachmentNames();
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Matrix3f> getBoneMatrices(std::string_view name);
|
||||||
|
|
||||||
|
bool renderPvs(std::string_view path, const atVec3f& location);
|
||||||
|
bool renderPvsLight(std::string_view path, std::string_view lightName);
|
||||||
|
|
||||||
|
MapArea compileMapArea();
|
||||||
|
MapUniverse compileMapUniverse();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Connection {
|
||||||
|
friend class ANIMOutStream;
|
||||||
|
friend class DataStream;
|
||||||
|
friend class PyOutStream;
|
||||||
|
friend struct Action;
|
||||||
|
friend struct Actor;
|
||||||
|
friend struct Armature;
|
||||||
|
friend struct Bone;
|
||||||
|
friend struct Boolean;
|
||||||
|
friend struct ColMesh;
|
||||||
|
friend struct Float;
|
||||||
|
friend struct Index;
|
||||||
|
friend struct Light;
|
||||||
|
friend struct MapArea;
|
||||||
|
friend struct MapUniverse;
|
||||||
|
friend struct Material;
|
||||||
|
friend struct Matrix3f;
|
||||||
|
friend struct Matrix4f;
|
||||||
|
friend struct Mesh;
|
||||||
|
friend struct PathMesh;
|
||||||
|
friend struct PyOutStream::StreamBuf;
|
||||||
|
friend struct Vector2f;
|
||||||
|
friend struct Vector3f;
|
||||||
|
friend struct Vector4f;
|
||||||
|
friend struct World;
|
||||||
|
friend class MeshOptimizer;
|
||||||
|
|
||||||
|
std::atomic_bool m_lock = {false};
|
||||||
|
bool m_pyStreamActive = false;
|
||||||
|
bool m_dataStreamActive = false;
|
||||||
|
bool m_blenderQuit = false;
|
||||||
|
#if _WIN32
|
||||||
|
PROCESS_INFORMATION m_pinfo = {};
|
||||||
|
std::thread m_consoleThread;
|
||||||
|
bool m_consoleThreadRunning = true;
|
||||||
|
#else
|
||||||
|
pid_t m_blenderProc = 0;
|
||||||
|
#endif
|
||||||
|
std::array<int, 2> m_readpipe{};
|
||||||
|
std::array<int, 2> m_writepipe{};
|
||||||
|
BlendType m_loadedType = BlendType::None;
|
||||||
|
bool m_loadedRigged = false;
|
||||||
|
ProjectPath m_loadedBlend;
|
||||||
|
hecl::SystemString m_errPath;
|
||||||
|
uint32_t _readStr(char* buf, uint32_t bufSz);
|
||||||
|
uint32_t _writeStr(const char* str, uint32_t len, int wpipe);
|
||||||
|
uint32_t _writeStr(const char* str, uint32_t len) { return _writeStr(str, len, m_writepipe[1]); }
|
||||||
|
uint32_t _writeStr(std::string_view view) { return _writeStr(view.data(), view.size()); }
|
||||||
|
std::size_t _readBuf(void* buf, std::size_t len);
|
||||||
|
std::size_t _writeBuf(const void* buf, std::size_t len);
|
||||||
|
std::string _readStdString() {
|
||||||
|
uint32_t bufSz;
|
||||||
|
_readBuf(&bufSz, 4);
|
||||||
|
std::string ret(bufSz, ' ');
|
||||||
|
_readBuf(&ret[0], bufSz);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
template<typename T, std::enable_if_t<std::disjunction_v<std::is_arithmetic<T>, std::is_enum<T>>, int> = 0>
|
||||||
|
void _readValue(T& v) { _readBuf(&v, sizeof(T)); }
|
||||||
|
template<typename T>
|
||||||
|
void _readItems(T enumerator) {
|
||||||
|
uint32_t nItems;
|
||||||
|
_readBuf(&nItems, 4);
|
||||||
|
for (uint32_t i = 0; i < nItems; ++i)
|
||||||
|
enumerator(*this);
|
||||||
|
}
|
||||||
|
template<typename T, typename... Args, std::enable_if_t<
|
||||||
|
!std::disjunction_v<std::is_arithmetic<T>, std::is_enum<T>, std::is_same<T, std::string>>, int> = 0>
|
||||||
|
void _readVector(std::vector<T>& container, Args&&... args) {
|
||||||
|
uint32_t nItems;
|
||||||
|
_readBuf(&nItems, 4);
|
||||||
|
container.clear();
|
||||||
|
container.reserve(nItems);
|
||||||
|
for (uint32_t i = 0; i < nItems; ++i)
|
||||||
|
container.emplace_back(*this, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
template<typename T, std::enable_if_t<std::disjunction_v<std::is_arithmetic<T>, std::is_enum<T>>, int> = 0>
|
||||||
|
void _readVector(std::vector<T>& container) {
|
||||||
|
uint32_t nItems;
|
||||||
|
_readBuf(&nItems, 4);
|
||||||
|
container.clear();
|
||||||
|
container.resize(nItems);
|
||||||
|
_readBuf(&container[0], sizeof(T) * nItems);
|
||||||
|
}
|
||||||
|
void _readVector(std::vector<std::string>& container) {
|
||||||
|
uint32_t nItems;
|
||||||
|
_readBuf(&nItems, 4);
|
||||||
|
container.clear();
|
||||||
|
container.reserve(nItems);
|
||||||
|
for (uint32_t i = 0; i < nItems; ++i) {
|
||||||
|
uint32_t strSize;
|
||||||
|
_readBuf(&strSize, 4);
|
||||||
|
_readBuf(&container.emplace_back(strSize, ' ')[0], strSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<typename T, typename F>
|
||||||
|
void _readVectorFunc(std::vector<T>& container, F func) {
|
||||||
|
uint32_t nItems;
|
||||||
|
_readBuf(&nItems, 4);
|
||||||
|
container.clear();
|
||||||
|
container.reserve(nItems);
|
||||||
|
for (uint32_t i = 0; i < nItems; ++i)
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
ProjectPath _readPath();
|
||||||
|
bool _isStatus(const char* status) {
|
||||||
|
char readBuf[16];
|
||||||
|
_readStr(readBuf, 16);
|
||||||
|
return std::strcmp(readBuf, status) == 0;
|
||||||
|
}
|
||||||
|
bool _isOk() { return _isStatus("OK"); }
|
||||||
|
bool _isFinished() { return _isStatus("FINISHED"); }
|
||||||
|
bool _isTrue() { return _isStatus("TRUE"); }
|
||||||
|
void _checkStatus(std::string_view action, std::string_view status) {
|
||||||
|
char readBuf[16];
|
||||||
|
_readStr(readBuf, 16);
|
||||||
|
if (status != readBuf)
|
||||||
|
BlenderLog.report(logvisor::Fatal, FMT_STRING("{}: {}: {}"), m_loadedBlend.getRelativePathUTF8(), action, readBuf);
|
||||||
|
}
|
||||||
|
void _checkReady(std::string_view action) { _checkStatus(action, "READY"sv); }
|
||||||
|
void _checkDone(std::string_view action) { _checkStatus(action, "DONE"sv); }
|
||||||
|
void _checkOk(std::string_view action) { _checkStatus(action, "OK"sv); }
|
||||||
|
void _checkAnimReady(std::string_view action) { _checkStatus(action, "ANIMREADY"sv); }
|
||||||
|
void _checkAnimDone(std::string_view action) { _checkStatus(action, "ANIMDONE"sv); }
|
||||||
|
void _closePipe();
|
||||||
|
void _blenderDied();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Connection(int verbosityLevel = 1);
|
||||||
|
~Connection();
|
||||||
|
|
||||||
|
Connection(const Connection&) = delete;
|
||||||
|
Connection& operator=(const Connection&) = delete;
|
||||||
|
Connection(Connection&&) = delete;
|
||||||
|
Connection& operator=(Connection&&) = delete;
|
||||||
|
|
||||||
|
bool createBlend(const ProjectPath& path, BlendType type);
|
||||||
|
BlendType getBlendType() const { return m_loadedType; }
|
||||||
|
const ProjectPath& getBlendPath() const { return m_loadedBlend; }
|
||||||
|
bool getRigged() const { return m_loadedRigged; }
|
||||||
|
bool openBlend(const ProjectPath& path, bool force = false);
|
||||||
|
bool saveBlend();
|
||||||
|
void deleteBlend();
|
||||||
|
|
||||||
|
PyOutStream beginPythonOut(bool deleteOnError = false) {
|
||||||
|
bool expect = false;
|
||||||
|
if (!m_lock.compare_exchange_strong(expect, true))
|
||||||
|
BlenderLog.report(logvisor::Fatal, FMT_STRING("lock already held for blender::Connection::beginPythonOut()"));
|
||||||
|
return PyOutStream(this, deleteOnError);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataStream beginData() {
|
||||||
|
bool expect = false;
|
||||||
|
if (!m_lock.compare_exchange_strong(expect, true))
|
||||||
|
BlenderLog.report(logvisor::Fatal, FMT_STRING("lock already held for blender::Connection::beginDataIn()"));
|
||||||
|
return DataStream(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void quitBlender();
|
||||||
|
|
||||||
|
void closeStream() {
|
||||||
|
if (m_lock)
|
||||||
|
deleteBlend();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Connection& SharedConnection();
|
||||||
|
static void Shutdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename S, typename... Args, typename Char>
|
||||||
|
void PyOutStream::format(const S& format, Args&&... args) {
|
||||||
|
if (!m_parent || !m_parent->m_lock)
|
||||||
|
BlenderLog.report(logvisor::Fatal, FMT_STRING("lock not held for PyOutStream::format()"));
|
||||||
|
fmt::print(*this, format, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
class HMDLBuffers {
|
||||||
|
public:
|
||||||
|
struct Surface;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct Mesh;
|
||||||
|
HMDLBuffers(HMDLMeta&& meta, std::size_t vboSz, const std::vector<atUint32>& iboData, std::vector<Surface>&& surfaces,
|
||||||
|
const Mesh::SkinBanks& skinBanks);
|
||||||
|
|
||||||
|
public:
|
||||||
|
HMDLMeta m_meta;
|
||||||
|
std::size_t m_vboSz;
|
||||||
|
std::unique_ptr<uint8_t[]> m_vboData;
|
||||||
|
std::size_t m_iboSz;
|
||||||
|
std::unique_ptr<uint8_t[]> m_iboData;
|
||||||
|
|
||||||
|
struct Surface {
|
||||||
|
Surface(const Mesh::Surface& origSurf, atUint32 start, atUint32 count)
|
||||||
|
: m_origSurf(origSurf), m_start(start), m_count(count) {}
|
||||||
|
const Mesh::Surface& m_origSurf;
|
||||||
|
atUint32 m_start;
|
||||||
|
atUint32 m_count;
|
||||||
|
};
|
||||||
|
std::vector<Surface> m_surfaces;
|
||||||
|
|
||||||
|
const Mesh::SkinBanks& m_skinBanks;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hecl::blender
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<hecl::blender::Vector2f> {
|
||||||
|
std::size_t operator()(const hecl::blender::Vector2f& val) const noexcept {
|
||||||
|
std::size_t h = std::hash<float>()(val.val.simd[0]);
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct hash<hecl::blender::Vector3f> {
|
||||||
|
std::size_t operator()(const hecl::blender::Vector3f& val) const noexcept {
|
||||||
|
std::size_t h = std::hash<float>()(val.val.simd[0]);
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct hash<hecl::blender::Vector4f> {
|
||||||
|
std::size_t operator()(const hecl::blender::Vector4f& val) const noexcept {
|
||||||
|
std::size_t h = std::hash<float>()(val.val.simd[0]);
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[3]));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct hash<array<hecl::blender::Mesh::SkinBind, 16>> {
|
||||||
|
std::size_t operator()(const array<hecl::blender::Mesh::SkinBind, 16>& val) const noexcept {
|
||||||
|
std::size_t h = 0;
|
||||||
|
for (const auto& bind : val) {
|
||||||
|
if (!bind.valid())
|
||||||
|
break;
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(bind.vg_idx));
|
||||||
|
hecl::hash_combine_impl(h, std::hash<float>()(bind.weight));
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "hecl/FourCC.hpp"
|
||||||
|
#include "hecl/SystemChar.hpp"
|
||||||
|
|
||||||
|
#include <athena/DNA.hpp>
|
||||||
|
|
||||||
|
namespace athena::io {
|
||||||
|
class MemoryReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace hecl::blender {
|
||||||
|
enum class BlendType;
|
||||||
|
|
||||||
|
struct SDNABlock : public athena::io::DNA<athena::Endian::Little> {
|
||||||
|
AT_DECL_DNA
|
||||||
|
DNAFourCC magic;
|
||||||
|
DNAFourCC nameMagic;
|
||||||
|
Value<atUint32> numNames;
|
||||||
|
Vector<String<-1>, AT_DNA_COUNT(numNames)> names;
|
||||||
|
Align<4> align1;
|
||||||
|
DNAFourCC typeMagic;
|
||||||
|
Value<atUint32> numTypes;
|
||||||
|
Vector<String<-1>, AT_DNA_COUNT(numTypes)> types;
|
||||||
|
Align<4> align2;
|
||||||
|
DNAFourCC tlenMagic;
|
||||||
|
Vector<Value<atUint16>, AT_DNA_COUNT(numTypes)> tlens;
|
||||||
|
Align<4> align3;
|
||||||
|
DNAFourCC strcMagic;
|
||||||
|
Value<atUint32> numStrcs;
|
||||||
|
struct SDNAStruct : public athena::io::DNA<athena::Endian::Little> {
|
||||||
|
AT_DECL_DNA
|
||||||
|
Value<atUint16> type;
|
||||||
|
Value<atUint16> numFields;
|
||||||
|
struct SDNAField : public athena::io::DNA<athena::Endian::Little> {
|
||||||
|
AT_DECL_DNA
|
||||||
|
Value<atUint16> type;
|
||||||
|
Value<atUint16> name;
|
||||||
|
atUint32 offset;
|
||||||
|
};
|
||||||
|
Vector<SDNAField, AT_DNA_COUNT(numFields)> fields;
|
||||||
|
|
||||||
|
void computeOffsets(const SDNABlock& block);
|
||||||
|
const SDNAField* lookupField(const SDNABlock& block, const char* n) const;
|
||||||
|
};
|
||||||
|
Vector<SDNAStruct, AT_DNA_COUNT(numStrcs)> strcs;
|
||||||
|
|
||||||
|
const SDNAStruct* lookupStruct(const char* n, atUint32& idx) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileBlock : public athena::io::DNA<athena::Endian::Little> {
|
||||||
|
AT_DECL_DNA
|
||||||
|
DNAFourCC type;
|
||||||
|
Value<atUint32> size;
|
||||||
|
Value<atUint64> ptr;
|
||||||
|
Value<atUint32> sdnaIdx;
|
||||||
|
Value<atUint32> count;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SDNARead {
|
||||||
|
std::vector<uint8_t> m_data;
|
||||||
|
SDNABlock m_sdnaBlock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SDNARead(SystemStringView path);
|
||||||
|
explicit operator bool() const { return !m_data.empty(); }
|
||||||
|
const SDNABlock& sdnaBlock() const { return m_sdnaBlock; }
|
||||||
|
void enumerate(const std::function<bool(const FileBlock& block, athena::io::MemoryReader& r)>& func) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
BlendType GetBlendType(SystemStringView path);
|
||||||
|
|
||||||
|
} // namespace hecl::blender
|
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace hecl::blender {
|
||||||
|
class Connection;
|
||||||
|
|
||||||
|
class Token {
|
||||||
|
std::unique_ptr<Connection> m_conn;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Connection& getBlenderConnection();
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
Token() = default;
|
||||||
|
~Token();
|
||||||
|
Token(const Token&) = delete;
|
||||||
|
Token& operator=(const Token&) = delete;
|
||||||
|
Token(Token&&) = default;
|
||||||
|
Token& operator=(Token&&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hecl::blender
|
|
@ -0,0 +1,320 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <athena/DNAYaml.hpp>
|
||||||
|
#include <athena/Global.hpp>
|
||||||
|
#include <athena/Types.hpp>
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
namespace DNACVAR {
|
||||||
|
enum class EType : atUint8 { Boolean, Signed, Unsigned, Real, Literal, Vec2f, Vec2d, Vec3f, Vec3d, Vec4f, Vec4d };
|
||||||
|
|
||||||
|
enum class EFlags {
|
||||||
|
None = 0,
|
||||||
|
System = (1 << 0),
|
||||||
|
Game = (1 << 1),
|
||||||
|
Editor = (1 << 2),
|
||||||
|
Gui = (1 << 3),
|
||||||
|
Cheat = (1 << 4),
|
||||||
|
Hidden = (1 << 5),
|
||||||
|
ReadOnly = (1 << 6),
|
||||||
|
Archive = (1 << 7),
|
||||||
|
InternalArchivable = (1 << 8),
|
||||||
|
Modified = (1 << 9),
|
||||||
|
ModifyRestart = (1 << 10), //!< If this bit is set, any modification will inform the user that a restart is required
|
||||||
|
Color = (1 << 11), //!< If this bit is set, Vec3f and Vec4f will be displayed in the console with a colored square
|
||||||
|
NoDeveloper = (1 << 12), //!< Not even developer mode can modify this
|
||||||
|
Any = -1
|
||||||
|
};
|
||||||
|
ENABLE_BITWISE_ENUM(EFlags)
|
||||||
|
|
||||||
|
class CVar : public athena::io::DNA<athena::Endian::Big> {
|
||||||
|
public:
|
||||||
|
AT_DECL_DNA
|
||||||
|
String<-1> m_name;
|
||||||
|
String<-1> m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CVarContainer : public athena::io::DNA<athena::Endian::Big> {
|
||||||
|
AT_DECL_DNA
|
||||||
|
Value<atUint32> magic = 'CVAR';
|
||||||
|
Value<atUint32> cvarCount;
|
||||||
|
Vector<CVar, AT_DNA_COUNT(cvarCount)> cvars;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace DNACVAR
|
||||||
|
|
||||||
|
class CVarManager;
|
||||||
|
class CVar : protected DNACVAR::CVar {
|
||||||
|
friend class CVarManager;
|
||||||
|
Delete _d;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef std::function<void(CVar*)> ListenerFunc;
|
||||||
|
|
||||||
|
using EType = DNACVAR::EType;
|
||||||
|
using EFlags = DNACVAR::EFlags;
|
||||||
|
|
||||||
|
CVar(std::string_view name, std::string_view value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, const atVec2f& value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, const atVec2d& value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, const atVec3f& value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, const atVec3d& value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, const atVec4d& value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, double value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, bool value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, int32_t value, std::string_view help, EFlags flags);
|
||||||
|
CVar(std::string_view name, uint32_t value, std::string_view help, EFlags flags);
|
||||||
|
|
||||||
|
std::string_view name() const { return m_name; }
|
||||||
|
std::string_view rawHelp() const { return m_help; }
|
||||||
|
std::string help() const;
|
||||||
|
std::string value() const { return m_value; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline bool toValue(T& value) const;
|
||||||
|
atVec2f toVec2f(bool* isValid = nullptr) const;
|
||||||
|
atVec2d toVec2d(bool* isValid = nullptr) const;
|
||||||
|
atVec3f toVec3f(bool* isValid = nullptr) const;
|
||||||
|
atVec3d toVec3d(bool* isValid = nullptr) const;
|
||||||
|
atVec4f toVec4f(bool* isValid = nullptr) const;
|
||||||
|
atVec4d toVec4d(bool* isValid = nullptr) const;
|
||||||
|
double toReal(bool* isValid = nullptr) const;
|
||||||
|
bool toBoolean(bool* isValid = nullptr) const;
|
||||||
|
int32_t toSigned(bool* isValid = nullptr) const;
|
||||||
|
uint32_t toUnsigned(bool* isValid = nullptr) const;
|
||||||
|
std::wstring toWideLiteral(bool* isValid = nullptr) const;
|
||||||
|
std::string toLiteral(bool* isValid = nullptr) const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline bool fromValue(T value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool fromVec2f(const atVec2f& val);
|
||||||
|
bool fromVec2d(const atVec2d& val);
|
||||||
|
bool fromVec3f(const atVec3f& val);
|
||||||
|
bool fromVec3d(const atVec3d& val);
|
||||||
|
bool fromVec4f(const atVec4f& val);
|
||||||
|
bool fromVec4d(const atVec4d& val);
|
||||||
|
bool fromReal(double val);
|
||||||
|
bool fromBoolean(bool val);
|
||||||
|
bool fromInteger(int32_t val);
|
||||||
|
bool fromInteger(uint32_t val);
|
||||||
|
bool fromLiteral(std::string_view val);
|
||||||
|
bool fromLiteral(std::wstring_view val);
|
||||||
|
bool fromLiteralToType(std::string_view val);
|
||||||
|
bool fromLiteralToType(std::wstring_view val);
|
||||||
|
|
||||||
|
bool isVec2f() const { return m_type == EType::Vec2f; }
|
||||||
|
bool isVec2d() const { return m_type == EType::Vec2d; }
|
||||||
|
bool isVec3f() const { return m_type == EType::Vec3f; }
|
||||||
|
bool isVec3d() const { return m_type == EType::Vec3d; }
|
||||||
|
bool isVec4f() const { return m_type == EType::Vec4f; }
|
||||||
|
bool isVec4d() const { return m_type == EType::Vec4d; }
|
||||||
|
bool isFloat() const { return m_type == EType::Real; }
|
||||||
|
bool isBoolean() const { return m_type == EType::Boolean; }
|
||||||
|
bool isInteger() const { return m_type == EType::Signed || m_type == EType::Unsigned; }
|
||||||
|
bool isLiteral() const { return m_type == EType::Literal; }
|
||||||
|
bool isModified() const;
|
||||||
|
bool modificationRequiresRestart() const;
|
||||||
|
bool isReadOnly() const;
|
||||||
|
bool isCheat() const;
|
||||||
|
bool isHidden() const;
|
||||||
|
bool isArchive() const;
|
||||||
|
bool isInternalArchivable() const;
|
||||||
|
bool isNoDeveloper() const;
|
||||||
|
bool isColor() const;
|
||||||
|
bool wasDeserialized() const;
|
||||||
|
bool hasDefaultValue() const;
|
||||||
|
|
||||||
|
EType type() const { return m_type; }
|
||||||
|
EFlags flags() const { return (m_unlocked ? m_oldFlags : m_flags); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Unlocks the CVar for writing if it is ReadOnly.
|
||||||
|
* <b>Handle with care!!!</b> if you use unlock(), make sure
|
||||||
|
* you lock the cvar using lock()
|
||||||
|
* \see lock
|
||||||
|
*/
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Locks the CVar to prevent writing if it is ReadOnly.
|
||||||
|
* Unlike its partner function unlock, lock is harmless
|
||||||
|
* \see unlock
|
||||||
|
*/
|
||||||
|
void lock();
|
||||||
|
|
||||||
|
void addListener(ListenerFunc func) { m_listeners.push_back(std::move(func)); }
|
||||||
|
|
||||||
|
bool isValidInput(std::string_view input) const;
|
||||||
|
bool isValidInput(std::wstring_view input) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CVar(std::string_view name, std::string_view help, EType type) : m_help(help), m_type(type) { m_name = name; }
|
||||||
|
void dispatch();
|
||||||
|
void clearModified();
|
||||||
|
void setModified();
|
||||||
|
std::string m_help;
|
||||||
|
EType m_type;
|
||||||
|
std::string m_defaultValue;
|
||||||
|
EFlags m_flags = EFlags::None;
|
||||||
|
EFlags m_oldFlags = EFlags::None;
|
||||||
|
bool m_unlocked = false;
|
||||||
|
bool m_wasDeserialized = false;
|
||||||
|
std::vector<ListenerFunc> m_listeners;
|
||||||
|
bool safeToModify(EType type) const;
|
||||||
|
void init(EFlags flags, bool removeColor = true);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(atVec2f& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toVec2f(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(atVec2d& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toVec2d(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(atVec3f& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toVec3f(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(atVec3d& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toVec3d(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(atVec4f& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toVec4f(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(atVec4d& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toVec4d(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(double& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toReal(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(float& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = static_cast<float>(toReal(&isValid));
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(bool& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toBoolean(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(int32_t& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toSigned(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(uint32_t& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toUnsigned(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(std::wstring& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toWideLiteral(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::toValue(std::string& value) const {
|
||||||
|
bool isValid = false;
|
||||||
|
value = toLiteral(&isValid);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(const atVec2f& val) {
|
||||||
|
return fromVec2f(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(const atVec2d& val) {
|
||||||
|
return fromVec2d(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(const atVec3f& val) {
|
||||||
|
return fromVec3f(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(const atVec3d& val) {
|
||||||
|
return fromVec3d(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(const atVec4f& val) {
|
||||||
|
return fromVec4f(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(const atVec4d& val) {
|
||||||
|
return fromVec4d(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(float val) {
|
||||||
|
return fromReal(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(double val) {
|
||||||
|
return fromReal(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(bool val) {
|
||||||
|
return fromBoolean(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(int32_t val) {
|
||||||
|
return fromInteger(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(uint32_t val) {
|
||||||
|
return fromInteger(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(std::string_view val) {
|
||||||
|
return fromLiteral(val);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline bool CVar::fromValue(std::wstring_view val) {
|
||||||
|
return fromLiteral(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CVarUnlocker {
|
||||||
|
CVar* m_cvar;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CVarUnlocker(CVar* cvar) : m_cvar(cvar) {
|
||||||
|
if (m_cvar)
|
||||||
|
m_cvar->unlock();
|
||||||
|
}
|
||||||
|
~CVarUnlocker() {
|
||||||
|
if (m_cvar)
|
||||||
|
m_cvar->lock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hecl
|
|
@ -0,0 +1,82 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "hecl/CVarManager.hpp"
|
||||||
|
|
||||||
|
#undef min
|
||||||
|
#undef max
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#define DEFAULT_GRAPHICS_API "Metal"sv
|
||||||
|
#else
|
||||||
|
#define DEFAULT_GRAPHICS_API "Vulkan"sv
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct CVarCommons {
|
||||||
|
CVarManager& m_mgr;
|
||||||
|
CVar* m_fullscreen = nullptr;
|
||||||
|
CVar* m_graphicsApi = nullptr;
|
||||||
|
CVar* m_drawSamples = nullptr;
|
||||||
|
CVar* m_texAnisotropy = nullptr;
|
||||||
|
CVar* m_deepColor = nullptr;
|
||||||
|
CVar* m_variableDt = nullptr;
|
||||||
|
|
||||||
|
CVar* m_debugOverlayPlayerInfo = nullptr;
|
||||||
|
CVar* m_debugOverlayWorldInfo = nullptr;
|
||||||
|
CVar* m_debugOverlayAreaInfo = nullptr;
|
||||||
|
CVar* m_debugOverlayShowFrameCounter = nullptr;
|
||||||
|
CVar* m_debugOverlayShowFramerate = nullptr;
|
||||||
|
CVar* m_debugOverlayShowInGameTime = nullptr;
|
||||||
|
CVar* m_debugOverlayShowResourceStats = nullptr;
|
||||||
|
CVar* m_debugOverlayShowRandomStats = nullptr;
|
||||||
|
CVar* m_debugOverlayShowRoomTimer = nullptr;
|
||||||
|
CVar* m_debugToolDrawAiPath = nullptr;
|
||||||
|
CVar* m_debugToolDrawLighting = nullptr;
|
||||||
|
CVar* m_debugToolDrawCollisionActors = nullptr;
|
||||||
|
CVar* m_debugToolDrawMazePath = nullptr;
|
||||||
|
CVar* m_debugToolDrawPlatformCollision = nullptr;
|
||||||
|
CVar* m_logFile = nullptr;
|
||||||
|
|
||||||
|
CVarCommons(CVarManager& manager);
|
||||||
|
|
||||||
|
bool getFullscreen() const { return m_fullscreen->toBoolean(); }
|
||||||
|
|
||||||
|
void setFullscreen(bool b) { m_fullscreen->fromBoolean(b); }
|
||||||
|
|
||||||
|
std::string getGraphicsApi() const { return m_graphicsApi->toLiteral(); }
|
||||||
|
|
||||||
|
void setGraphicsApi(std::string_view api) { m_graphicsApi->fromLiteral(api); }
|
||||||
|
|
||||||
|
uint32_t getSamples() const { return std::max(1u, m_drawSamples->toUnsigned()); }
|
||||||
|
|
||||||
|
void setSamples(uint32_t v) { m_drawSamples->fromInteger(std::max(uint32_t(1), v)); }
|
||||||
|
|
||||||
|
uint32_t getAnisotropy() const { return std::max(1u, uint32_t(m_texAnisotropy->toUnsigned())); }
|
||||||
|
|
||||||
|
void setAnisotropy(uint32_t v) { m_texAnisotropy->fromInteger(std::max(1u, v)); }
|
||||||
|
|
||||||
|
bool getDeepColor() const { return m_deepColor->toBoolean(); }
|
||||||
|
|
||||||
|
void setDeepColor(bool b) { m_deepColor->fromBoolean(b); }
|
||||||
|
|
||||||
|
bool getVariableFrameTime() const { return m_variableDt->toBoolean(); }
|
||||||
|
|
||||||
|
void setVariableFrameTime(bool b) { m_variableDt->fromBoolean(b); }
|
||||||
|
|
||||||
|
std::string getLogFile() const { return m_logFile->toLiteral(); };
|
||||||
|
|
||||||
|
void setLogFile(std::string_view log) { m_logFile->fromLiteral(log); }
|
||||||
|
|
||||||
|
void serialize() { m_mgr.serialize(); }
|
||||||
|
|
||||||
|
static CVarCommons* instance();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hecl
|
|
@ -0,0 +1,120 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "hecl/CVar.hpp"
|
||||||
|
#include "hecl/SystemChar.hpp"
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
namespace Runtime {
|
||||||
|
class FileStoreManager;
|
||||||
|
}
|
||||||
|
extern CVar* com_developer;
|
||||||
|
extern CVar* com_configfile;
|
||||||
|
extern CVar* com_enableCheats;
|
||||||
|
extern CVar* com_cubemaps;
|
||||||
|
class CVarManager final {
|
||||||
|
using CVarContainer = DNACVAR::CVarContainer;
|
||||||
|
template <typename T>
|
||||||
|
CVar* _newCVar(std::string_view name, std::string_view help, const T& value, CVar::EFlags flags) {
|
||||||
|
if (CVar* ret = registerCVar(std::make_unique<CVar>(name, value, help, flags))) {
|
||||||
|
deserialize(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::Runtime::FileStoreManager& m_store;
|
||||||
|
bool m_useBinary;
|
||||||
|
static CVarManager* m_instance;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CVarManager() = delete;
|
||||||
|
CVarManager(const CVarManager&) = delete;
|
||||||
|
CVarManager& operator=(const CVarManager&) = delete;
|
||||||
|
CVarManager& operator=(const CVarManager&&) = delete;
|
||||||
|
CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary = false);
|
||||||
|
~CVarManager();
|
||||||
|
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, const atVec2f& value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<atVec2f>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, const atVec2d& value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<atVec2d>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, const atVec3f& value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<atVec3f>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, const atVec3d& value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<atVec3d>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, const atVec4f& value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<atVec4f>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, const atVec4d& value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<atVec4d>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, std::string_view value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<std::string_view>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, bool value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<bool>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
// Float and double are internally identical, all floating point values are stored as `double`
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, float value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<double>(name, help, static_cast<double>(value), flags);
|
||||||
|
}
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, double value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<double>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
// Integer CVars can be seamlessly converted between either type, the distinction is to make usage absolutely clear
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, int32_t value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<int32_t>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
CVar* newCVar(std::string_view name, std::string_view help, uint32_t value, CVar::EFlags flags) {
|
||||||
|
return _newCVar<uint32_t>(name, help, value, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
CVar* registerCVar(std::unique_ptr<CVar>&& cvar);
|
||||||
|
|
||||||
|
CVar* findCVar(std::string_view name);
|
||||||
|
template <class... _Args>
|
||||||
|
CVar* findOrMakeCVar(std::string_view name, _Args&&... args) {
|
||||||
|
if (CVar* cv = findCVar(name))
|
||||||
|
return cv;
|
||||||
|
return newCVar(name, std::forward<_Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CVar*> archivedCVars() const;
|
||||||
|
std::vector<CVar*> cvars(CVar::EFlags filter = CVar::EFlags::Any) const;
|
||||||
|
|
||||||
|
void deserialize(CVar* cvar);
|
||||||
|
void serialize();
|
||||||
|
|
||||||
|
static CVarManager* instance();
|
||||||
|
|
||||||
|
void proc();
|
||||||
|
|
||||||
|
void list(class Console* con, const std::vector<std::string>& args);
|
||||||
|
void setCVar(class Console* con, const std::vector<std::string>& args);
|
||||||
|
void getCVar(class Console* con, const std::vector<std::string>& args);
|
||||||
|
|
||||||
|
void setDeveloperMode(bool v, bool setDeserialized = false);
|
||||||
|
void setCheatsEnabled(bool v, bool setDeserialized = false);
|
||||||
|
bool restartRequired() const;
|
||||||
|
|
||||||
|
void parseCommandLine(const std::vector<SystemString>& args);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool suppressDeveloper();
|
||||||
|
void restoreDeveloper(bool oldDeveloper);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<CVar>> m_cvars;
|
||||||
|
std::unordered_map<std::string, std::string> m_deferedCVars;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hecl
|
|
@ -0,0 +1,110 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "hecl/Blender/Token.hpp"
|
||||||
|
#include "hecl/hecl.hpp"
|
||||||
|
#include "hecl/SystemChar.hpp"
|
||||||
|
|
||||||
|
#include <boo/ThreadLocalPtr.hpp>
|
||||||
|
|
||||||
|
namespace hecl::Database {
|
||||||
|
class IDataSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
class MultiProgressPrinter;
|
||||||
|
|
||||||
|
extern int CpuCountOverride;
|
||||||
|
void SetCpuCountOverride(int argc, const SystemChar** argv);
|
||||||
|
|
||||||
|
class ClientProcess {
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::condition_variable m_cv;
|
||||||
|
std::condition_variable m_initCv;
|
||||||
|
std::condition_variable m_waitCv;
|
||||||
|
const MultiProgressPrinter* m_progPrinter;
|
||||||
|
int m_completedCooks = 0;
|
||||||
|
int m_addedCooks = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Transaction {
|
||||||
|
ClientProcess& m_parent;
|
||||||
|
enum class Type { Buffer, Cook, Lambda } m_type;
|
||||||
|
bool m_complete = false;
|
||||||
|
virtual void run(blender::Token& btok) = 0;
|
||||||
|
Transaction(ClientProcess& parent, Type tp) : m_parent(parent), m_type(tp) {}
|
||||||
|
};
|
||||||
|
struct BufferTransaction final : Transaction {
|
||||||
|
ProjectPath m_path;
|
||||||
|
void* m_targetBuf;
|
||||||
|
size_t m_maxLen;
|
||||||
|
size_t m_offset;
|
||||||
|
void run(blender::Token& btok) override;
|
||||||
|
BufferTransaction(ClientProcess& parent, const ProjectPath& path, void* target, size_t maxLen, size_t offset)
|
||||||
|
: Transaction(parent, Type::Buffer), m_path(path), m_targetBuf(target), m_maxLen(maxLen), m_offset(offset) {}
|
||||||
|
};
|
||||||
|
struct CookTransaction final : Transaction {
|
||||||
|
ProjectPath m_path;
|
||||||
|
Database::IDataSpec* m_dataSpec;
|
||||||
|
bool m_returnResult = false;
|
||||||
|
bool m_force;
|
||||||
|
bool m_fast;
|
||||||
|
void run(blender::Token& btok) override;
|
||||||
|
CookTransaction(ClientProcess& parent, const ProjectPath& path, bool force, bool fast, Database::IDataSpec* spec)
|
||||||
|
: Transaction(parent, Type::Cook), m_path(path), m_dataSpec(spec), m_force(force), m_fast(fast) {}
|
||||||
|
};
|
||||||
|
struct LambdaTransaction final : Transaction {
|
||||||
|
std::function<void(blender::Token&)> m_func;
|
||||||
|
void run(blender::Token& btok) override;
|
||||||
|
LambdaTransaction(ClientProcess& parent, std::function<void(blender::Token&)>&& func)
|
||||||
|
: Transaction(parent, Type::Lambda), m_func(std::move(func)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::list<std::shared_ptr<Transaction>> m_pendingQueue;
|
||||||
|
std::list<std::shared_ptr<Transaction>> m_completedQueue;
|
||||||
|
int m_inProgress = 0;
|
||||||
|
bool m_running = true;
|
||||||
|
|
||||||
|
struct Worker {
|
||||||
|
ClientProcess& m_proc;
|
||||||
|
int m_idx;
|
||||||
|
std::thread m_thr;
|
||||||
|
blender::Token m_blendTok;
|
||||||
|
bool m_didInit = false;
|
||||||
|
Worker(ClientProcess& proc, int idx);
|
||||||
|
void proc();
|
||||||
|
};
|
||||||
|
std::vector<Worker> m_workers;
|
||||||
|
static ThreadLocalPtr<ClientProcess::Worker> ThreadWorker;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClientProcess(const MultiProgressPrinter* progPrinter = nullptr);
|
||||||
|
~ClientProcess() { shutdown(); }
|
||||||
|
std::shared_ptr<const BufferTransaction> addBufferTransaction(const hecl::ProjectPath& path, void* target,
|
||||||
|
size_t maxLen, size_t offset);
|
||||||
|
std::shared_ptr<const CookTransaction> addCookTransaction(const hecl::ProjectPath& path, bool force, bool fast,
|
||||||
|
Database::IDataSpec* spec);
|
||||||
|
std::shared_ptr<const LambdaTransaction> addLambdaTransaction(std::function<void(blender::Token&)>&& func);
|
||||||
|
bool syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force, bool fast);
|
||||||
|
void swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue);
|
||||||
|
void waitUntilComplete();
|
||||||
|
void shutdown();
|
||||||
|
bool isBusy() const { return m_pendingQueue.size() || m_inProgress; }
|
||||||
|
|
||||||
|
static int GetThreadWorkerIdx() {
|
||||||
|
Worker* w = ThreadWorker.get();
|
||||||
|
if (w)
|
||||||
|
return w->m_idx;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hecl
|
|
@ -0,0 +1,96 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <boo/graphicsdev/D3D.hpp>
|
||||||
|
#include <boo/graphicsdev/GL.hpp>
|
||||||
|
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
|
||||||
|
#include <boo/graphicsdev/Metal.hpp>
|
||||||
|
#include <boo/graphicsdev/Vulkan.hpp>
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
|
||||||
|
namespace PlatformType {
|
||||||
|
using PlatformEnum = boo::IGraphicsDataFactory::Platform;
|
||||||
|
struct Null {};
|
||||||
|
struct OpenGL {
|
||||||
|
static constexpr PlatformEnum Enum = PlatformEnum::OpenGL;
|
||||||
|
static constexpr char Name[] = "OpenGL";
|
||||||
|
#if BOO_HAS_GL
|
||||||
|
using Context = boo::GLDataFactory::Context;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
struct D3D11 {
|
||||||
|
static constexpr PlatformEnum Enum = PlatformEnum::D3D11;
|
||||||
|
static constexpr char Name[] = "D3D11";
|
||||||
|
#if _WIN32
|
||||||
|
using Context = boo::D3D11DataFactory::Context;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
struct Metal {
|
||||||
|
static constexpr PlatformEnum Enum = PlatformEnum::Metal;
|
||||||
|
static constexpr char Name[] = "Metal";
|
||||||
|
#if BOO_HAS_METAL
|
||||||
|
using Context = boo::MetalDataFactory::Context;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
struct Vulkan {
|
||||||
|
static constexpr PlatformEnum Enum = PlatformEnum::Vulkan;
|
||||||
|
static constexpr char Name[] = "Vulkan";
|
||||||
|
#if BOO_HAS_VULKAN
|
||||||
|
using Context = boo::VulkanDataFactory::Context;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
struct NX {
|
||||||
|
static constexpr PlatformEnum Enum = PlatformEnum::NX;
|
||||||
|
static constexpr char Name[] = "NX";
|
||||||
|
#if BOO_HAS_NX
|
||||||
|
using Context = boo::NXDataFactory::Context;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
} // namespace PlatformType
|
||||||
|
|
||||||
|
namespace PipelineStage {
|
||||||
|
using StageEnum = boo::PipelineStage;
|
||||||
|
struct Null {
|
||||||
|
static constexpr StageEnum Enum = StageEnum::Null;
|
||||||
|
static constexpr char Name[] = "Null";
|
||||||
|
};
|
||||||
|
struct Vertex {
|
||||||
|
static constexpr StageEnum Enum = StageEnum::Vertex;
|
||||||
|
static constexpr char Name[] = "Vertex";
|
||||||
|
};
|
||||||
|
struct Fragment {
|
||||||
|
static constexpr StageEnum Enum = StageEnum::Fragment;
|
||||||
|
static constexpr char Name[] = "Fragment";
|
||||||
|
};
|
||||||
|
struct Geometry {
|
||||||
|
static constexpr StageEnum Enum = StageEnum::Geometry;
|
||||||
|
static constexpr char Name[] = "Geometry";
|
||||||
|
};
|
||||||
|
struct Control {
|
||||||
|
static constexpr StageEnum Enum = StageEnum::Control;
|
||||||
|
static constexpr char Name[] = "Control";
|
||||||
|
};
|
||||||
|
struct Evaluation {
|
||||||
|
static constexpr StageEnum Enum = StageEnum::Evaluation;
|
||||||
|
static constexpr char Name[] = "Evaluation";
|
||||||
|
};
|
||||||
|
} // namespace PipelineStage
|
||||||
|
|
||||||
|
#ifdef _LIBCPP_VERSION
|
||||||
|
using StageBinaryData = std::shared_ptr<uint8_t>;
|
||||||
|
inline StageBinaryData MakeStageBinaryData(size_t sz) {
|
||||||
|
return StageBinaryData(new uint8_t[sz], std::default_delete<uint8_t[]>{});
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
using StageBinaryData = std::shared_ptr<uint8_t[]>;
|
||||||
|
inline StageBinaryData MakeStageBinaryData(size_t sz) { return StageBinaryData(new uint8_t[sz]); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename P, typename S>
|
||||||
|
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text);
|
||||||
|
|
||||||
|
} // namespace hecl
|
|
@ -0,0 +1,113 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boo/System.hpp>
|
||||||
|
#include <logvisor/logvisor.hpp>
|
||||||
|
|
||||||
|
namespace boo {
|
||||||
|
class IWindow;
|
||||||
|
|
||||||
|
enum class EModifierKey;
|
||||||
|
enum class ESpecialKey;
|
||||||
|
|
||||||
|
struct IGraphicsCommandQueue;
|
||||||
|
} // namespace boo
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
class CVarManager;
|
||||||
|
class CVar;
|
||||||
|
struct SConsoleCommand {
|
||||||
|
enum class ECommandFlags { Normal = 0, Cheat = (1 << 0), Developer = (1 << 1) };
|
||||||
|
std::string m_displayName;
|
||||||
|
std::string m_helpString;
|
||||||
|
std::string m_usage;
|
||||||
|
std::function<void(class Console*, const std::vector<std::string>&)> m_func;
|
||||||
|
ECommandFlags m_flags;
|
||||||
|
};
|
||||||
|
ENABLE_BITWISE_ENUM(SConsoleCommand::ECommandFlags)
|
||||||
|
|
||||||
|
class Console {
|
||||||
|
friend class LogVisorAdapter;
|
||||||
|
struct LogVisorAdapter : logvisor::ILogger {
|
||||||
|
Console* m_con;
|
||||||
|
LogVisorAdapter(Console* con) : logvisor::ILogger(log_typeid(LogVisorAdapter)), m_con(con) {}
|
||||||
|
|
||||||
|
~LogVisorAdapter() override = default;
|
||||||
|
void report(const char* modName, logvisor::Level severity, fmt::string_view format, fmt::format_args args) override;
|
||||||
|
void report(const char* modName, logvisor::Level severity, fmt::wstring_view format,
|
||||||
|
fmt::wformat_args args) override;
|
||||||
|
void reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum,
|
||||||
|
fmt::string_view format, fmt::format_args args) override;
|
||||||
|
void reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum,
|
||||||
|
fmt::wstring_view format, fmt::wformat_args args) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Console* m_instance;
|
||||||
|
enum class Level {
|
||||||
|
Info, /**< Non-error informative message */
|
||||||
|
Warning, /**< Non-error warning message */
|
||||||
|
Error, /**< Recoverable error message */
|
||||||
|
Fatal /**< Non-recoverable error message (Kept for compatibility with logvisor) */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class State { Closed, Closing, Opened, Opening };
|
||||||
|
|
||||||
|
private:
|
||||||
|
CVarManager* m_cvarMgr = nullptr;
|
||||||
|
boo::IWindow* m_window = nullptr;
|
||||||
|
std::unordered_map<std::string, SConsoleCommand> m_commands;
|
||||||
|
std::vector<std::pair<std::string, Level>> m_log;
|
||||||
|
int m_logOffset = 0;
|
||||||
|
std::string m_commandString;
|
||||||
|
std::vector<std::string> m_commandHistory;
|
||||||
|
int m_cursorPosition = -1;
|
||||||
|
int m_currentCommand = -1;
|
||||||
|
size_t m_maxLines = 0;
|
||||||
|
bool m_overwrite : 1;
|
||||||
|
bool m_cursorAtEnd : 1;
|
||||||
|
State m_state = State::Closed;
|
||||||
|
CVar* m_conSpeed;
|
||||||
|
CVar* m_conHeight;
|
||||||
|
float m_cachedConSpeed;
|
||||||
|
float m_cachedConHeight;
|
||||||
|
bool m_showCursor = true;
|
||||||
|
float m_cursorTime = 0.f;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Console(CVarManager*);
|
||||||
|
void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage,
|
||||||
|
std::function<void(Console*, const std::vector<std::string>&)>&& func,
|
||||||
|
SConsoleCommand::ECommandFlags cmdFlags = SConsoleCommand::ECommandFlags::Normal);
|
||||||
|
void unregisterCommand(std::string_view name);
|
||||||
|
|
||||||
|
void executeString(const std::string& strToExec);
|
||||||
|
|
||||||
|
void help(Console* con, const std::vector<std::string>& args);
|
||||||
|
void listCommands(Console* con, const std::vector<std::string>& args);
|
||||||
|
bool commandExists(std::string_view cmd) const;
|
||||||
|
|
||||||
|
void vreport(Level level, fmt::string_view format, fmt::format_args args);
|
||||||
|
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
|
||||||
|
void report(Level level, const S& format, Args&&... args) {
|
||||||
|
vreport(level, fmt::to_string_view<Char>(format),
|
||||||
|
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
||||||
|
fmt::make_args_checked<Args...>(format, args...)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(boo::IWindow* ctx);
|
||||||
|
void proc();
|
||||||
|
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||||
|
void handleCharCode(unsigned long chr, boo::EModifierKey mod, bool repeat);
|
||||||
|
void handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool repeat);
|
||||||
|
void handleSpecialKeyUp(boo::ESpecialKey sp, boo::EModifierKey mod);
|
||||||
|
void dumpLog();
|
||||||
|
static Console* instance();
|
||||||
|
static void RegisterLogger(Console* con);
|
||||||
|
bool isOpen() const { return m_state == State::Opened; }
|
||||||
|
};
|
||||||
|
} // namespace hecl
|
|
@ -0,0 +1,442 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "hecl/hecl.hpp"
|
||||||
|
|
||||||
|
#include <logvisor/logvisor.hpp>
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
class ClientProcess;
|
||||||
|
|
||||||
|
namespace Database {
|
||||||
|
class Project;
|
||||||
|
|
||||||
|
extern logvisor::Module LogModule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Nodegraph class for gathering dependency-resolved objects for packaging
|
||||||
|
*/
|
||||||
|
class PackageDepsgraph {
|
||||||
|
public:
|
||||||
|
struct Node {
|
||||||
|
enum class Type { Data, Group } type;
|
||||||
|
ProjectPath path;
|
||||||
|
ProjectPath cookedPath;
|
||||||
|
class ObjectBase* projectObj;
|
||||||
|
Node* sub;
|
||||||
|
Node* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Project;
|
||||||
|
std::vector<Node> m_nodes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const Node* getRootNode() const { return &m_nodes[0]; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subclassed by dataspec entries to manage per-game aspects of the data pipeline
|
||||||
|
*
|
||||||
|
* The DataSpec class manages interfaces for unpackaging, cooking, and packaging
|
||||||
|
* of data for interacting with a specific system/game-engine.
|
||||||
|
*/
|
||||||
|
class IDataSpec {
|
||||||
|
const DataSpecEntry* m_specEntry;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IDataSpec(const DataSpecEntry* specEntry) : m_specEntry(specEntry) {}
|
||||||
|
virtual ~IDataSpec() = default;
|
||||||
|
using FCookProgress = std::function<void(const SystemChar*)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract Pass Info
|
||||||
|
*
|
||||||
|
* An extract pass iterates through a source package or image and
|
||||||
|
* reverses the cooking process by emitting editable resources
|
||||||
|
*/
|
||||||
|
struct ExtractPassInfo {
|
||||||
|
SystemString srcpath;
|
||||||
|
std::vector<SystemString> extractArgs;
|
||||||
|
bool force;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract Report Representation
|
||||||
|
*
|
||||||
|
* Constructed by canExtract() to advise the user of the content about
|
||||||
|
* to be extracted
|
||||||
|
*/
|
||||||
|
struct ExtractReport {
|
||||||
|
SystemString name;
|
||||||
|
SystemString desc;
|
||||||
|
std::vector<ExtractReport> childOpts;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void setThreadProject() {}
|
||||||
|
|
||||||
|
virtual bool canExtract([[maybe_unused]] const ExtractPassInfo& info,
|
||||||
|
[[maybe_unused]] std::vector<ExtractReport>& reps) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING("not implemented"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual void doExtract([[maybe_unused]] const ExtractPassInfo& info,
|
||||||
|
[[maybe_unused]] const MultiProgressPrinter& progress) {}
|
||||||
|
|
||||||
|
virtual bool canCook([[maybe_unused]] const ProjectPath& path, [[maybe_unused]] blender::Token& btok) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING("not implemented"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual const DataSpecEntry* overrideDataSpec([[maybe_unused]] const ProjectPath& path,
|
||||||
|
const DataSpecEntry* oldEntry) const {
|
||||||
|
return oldEntry;
|
||||||
|
}
|
||||||
|
virtual void doCook([[maybe_unused]] const ProjectPath& path, [[maybe_unused]] const ProjectPath& cookedPath,
|
||||||
|
[[maybe_unused]] bool fast, [[maybe_unused]] blender::Token& btok,
|
||||||
|
[[maybe_unused]] FCookProgress progress) {}
|
||||||
|
|
||||||
|
virtual bool canPackage([[maybe_unused]] const ProjectPath& path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual void doPackage([[maybe_unused]] const ProjectPath& path, [[maybe_unused]] const DataSpecEntry* entry,
|
||||||
|
[[maybe_unused]] bool fast, [[maybe_unused]] blender::Token& btok,
|
||||||
|
[[maybe_unused]] const MultiProgressPrinter& progress,
|
||||||
|
[[maybe_unused]] ClientProcess* cp = nullptr) {}
|
||||||
|
|
||||||
|
virtual void interruptCook() {}
|
||||||
|
|
||||||
|
const DataSpecEntry* getDataSpecEntry() const { return m_specEntry; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pre-emptive indication of what the constructed DataSpec is used for
|
||||||
|
*/
|
||||||
|
enum class DataSpecTool { Extract, Cook, Package };
|
||||||
|
|
||||||
|
extern std::vector<const struct DataSpecEntry*> DATA_SPEC_REGISTRY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief IDataSpec registry entry
|
||||||
|
*
|
||||||
|
* Auto-registers with data spec registry
|
||||||
|
*/
|
||||||
|
struct DataSpecEntry {
|
||||||
|
SystemStringView m_name;
|
||||||
|
SystemStringView m_desc;
|
||||||
|
SystemStringView m_pakExt;
|
||||||
|
std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
|
||||||
|
|
||||||
|
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt,
|
||||||
|
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
|
||||||
|
: m_name(name), m_desc(desc), m_pakExt(pakExt), m_factory(std::move(factory)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base object to subclass for integrating with key project operations
|
||||||
|
*
|
||||||
|
* All project objects are provided with IDataObject pointers to their database
|
||||||
|
* entries. Subclasses register themselves with a type registry so instances
|
||||||
|
* are automatically constructed when performing operations like cooking and packaging.
|
||||||
|
*
|
||||||
|
* DO NOT CONSTRUCT THIS OR SUBCLASSES DIRECTLY!!
|
||||||
|
*/
|
||||||
|
class ObjectBase {
|
||||||
|
friend class Project;
|
||||||
|
SystemString m_path;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* @brief Byte-order of target system
|
||||||
|
*/
|
||||||
|
enum class DataEndianness {
|
||||||
|
None,
|
||||||
|
Big, /**< Big-endian (PowerPC) */
|
||||||
|
Little /**< Little-endian (Intel) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data-formats of target system
|
||||||
|
*/
|
||||||
|
enum class DataPlatform {
|
||||||
|
None,
|
||||||
|
Generic, /**< Scanline textures and 3-way shader bundle (GLSL, HLSL, SPIR-V) */
|
||||||
|
Revolution, /**< Tiled textures and GX register buffers */
|
||||||
|
Cafe /**< Swizzled textures and R700 shader objects */
|
||||||
|
};
|
||||||
|
|
||||||
|
using FDataAppender = std::function<void(const void* data, size_t len)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Optional private method implemented by subclasses to cook objects
|
||||||
|
* @param dataAppender subclass calls this function zero or more times to provide cooked-data linearly
|
||||||
|
* @param endianness byte-order to target
|
||||||
|
* @param platform data-formats to target
|
||||||
|
* @return true if cook succeeded
|
||||||
|
*
|
||||||
|
* This method is called during IProject::cookPath().
|
||||||
|
* Part of the cooking process may include embedding database-refs to dependencies.
|
||||||
|
* This method should store the 64-bit value provided by IDataObject::id() when doing this.
|
||||||
|
*/
|
||||||
|
virtual bool cookObject([[maybe_unused]] FDataAppender dataAppender, [[maybe_unused]] DataEndianness endianness,
|
||||||
|
[[maybe_unused]] DataPlatform platform) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
using FDepAdder = std::function<void(ObjectBase*)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Optional private method implemented by CProjectObject subclasses to resolve dependencies
|
||||||
|
* @param depAdder subclass calls this function zero or more times to register each dependency
|
||||||
|
*
|
||||||
|
* This method is called during IProject::packagePath().
|
||||||
|
* Dependencies registered via this method will eventually have this method called on themselves
|
||||||
|
* as well. This is a non-recursive operation, no need for subclasses to implement recursion-control.
|
||||||
|
*/
|
||||||
|
virtual void gatherDeps([[maybe_unused]] FDepAdder depAdder) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a packagable FourCC representation of the object's type
|
||||||
|
* @return FourCC of the type
|
||||||
|
*/
|
||||||
|
virtual FourCC getType() const { return FourCC("NULL"); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
ObjectBase(SystemStringView path) : m_path(path) {}
|
||||||
|
|
||||||
|
SystemStringView getPath() const { return m_path; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main project interface
|
||||||
|
*
|
||||||
|
* Projects are intermediate working directories used for staging
|
||||||
|
* resources in their ideal editor-formats. This interface exposes all
|
||||||
|
* primary operations to perform on a given project.
|
||||||
|
*/
|
||||||
|
class Project {
|
||||||
|
public:
|
||||||
|
struct ProjectDataSpec {
|
||||||
|
const DataSpecEntry& spec;
|
||||||
|
ProjectPath cookedPath;
|
||||||
|
bool active;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
ProjectRootPath m_rootPath;
|
||||||
|
ProjectPath m_workRoot;
|
||||||
|
ProjectPath m_dotPath;
|
||||||
|
ProjectPath m_cookedRoot;
|
||||||
|
std::vector<ProjectDataSpec> m_compiledSpecs;
|
||||||
|
std::unordered_map<uint64_t, ProjectPath> m_bridgePathCache;
|
||||||
|
std::vector<std::unique_ptr<IDataSpec>> m_cookSpecs;
|
||||||
|
std::unique_ptr<IDataSpec> m_lastPackageSpec;
|
||||||
|
bool m_valid = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Project(const ProjectRootPath& rootPath);
|
||||||
|
explicit operator bool() const { return m_valid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration file handle
|
||||||
|
*
|
||||||
|
* Holds a path to a line-delimited textual configuration file;
|
||||||
|
* opening a locked handle for read/write transactions
|
||||||
|
*/
|
||||||
|
class ConfigFile {
|
||||||
|
SystemString m_filepath;
|
||||||
|
std::vector<std::string> m_lines;
|
||||||
|
UniqueFilePtr m_lockedFile;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConfigFile(const Project& project, SystemStringView name, SystemStringView subdir = _SYS_STR("/.hecl/"));
|
||||||
|
std::vector<std::string>& lockAndRead();
|
||||||
|
void addLine(std::string_view line);
|
||||||
|
void removeLine(std::string_view refLine);
|
||||||
|
bool checkForLine(std::string_view refLine) const;
|
||||||
|
void unlockAndDiscard();
|
||||||
|
bool unlockAndCommit();
|
||||||
|
};
|
||||||
|
ConfigFile m_specs;
|
||||||
|
ConfigFile m_paths;
|
||||||
|
ConfigFile m_groups;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A rough description of how 'expensive' a given cook operation is
|
||||||
|
*
|
||||||
|
* This is used to provide pretty colors during the cook operation
|
||||||
|
*/
|
||||||
|
enum class Cost { None, Light, Medium, Heavy };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the path of the project's root-directory
|
||||||
|
* @return project root path
|
||||||
|
*
|
||||||
|
* Self explanatory
|
||||||
|
*/
|
||||||
|
const ProjectRootPath& getProjectRootPath() const { return m_rootPath; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the path of project's working directory
|
||||||
|
* @return project working path
|
||||||
|
*/
|
||||||
|
const ProjectPath& getProjectWorkingPath() const { return m_workRoot; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the path of project's cooked directory for a specific DataSpec
|
||||||
|
* @param spec DataSpec to retrieve path for
|
||||||
|
* @return project cooked path
|
||||||
|
*
|
||||||
|
* The cooked path matches the directory layout of the working directory
|
||||||
|
*/
|
||||||
|
const ProjectPath& getProjectCookedPath(const DataSpecEntry& spec) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add given file(s) to the database
|
||||||
|
* @param paths files or patterns within project
|
||||||
|
* @return true on success
|
||||||
|
*
|
||||||
|
* This method blocks while object hashing takes place
|
||||||
|
*/
|
||||||
|
bool addPaths(const std::vector<ProjectPath>& paths);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a given file or file-pattern from the database
|
||||||
|
* @param paths file(s) or pattern(s) within project
|
||||||
|
* @param recursive traverse into matched subdirectories
|
||||||
|
* @return true on success
|
||||||
|
*
|
||||||
|
* This method will not delete actual working files from the project
|
||||||
|
* directory. It will delete associated cooked objects though.
|
||||||
|
*/
|
||||||
|
bool removePaths(const std::vector<ProjectPath>& paths, bool recursive = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a working sub-directory as a Dependency Group
|
||||||
|
* @param path directory to register as Dependency Group
|
||||||
|
* @return true on success
|
||||||
|
*
|
||||||
|
* Dependency Groups are used at runtime to stage burst load-transactions.
|
||||||
|
* They may only be added to directories and will automatically claim
|
||||||
|
* subdirectories as well.
|
||||||
|
*
|
||||||
|
* Cooked objects in dependency groups will be packaged contiguously
|
||||||
|
* and automatically duplicated if shared with other dependency groups.
|
||||||
|
* This contiguous storage makes for optimal loading from slow block-devices
|
||||||
|
* like optical drives.
|
||||||
|
*/
|
||||||
|
bool addGroup(const ProjectPath& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregister a working sub-directory as a dependency group
|
||||||
|
* @param path directory to unregister as Dependency Group
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool removeGroup(const ProjectPath& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Re-reads the data store holding user's spec preferences
|
||||||
|
*
|
||||||
|
* Call periodically in a long-term use of the hecl::Database::Project class.
|
||||||
|
* Install filesystem event-hooks if possible.
|
||||||
|
*/
|
||||||
|
void rescanDataSpecs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return map populated with dataspecs targetable by this project interface
|
||||||
|
* @return Platform map with name-string keys and enable-status values
|
||||||
|
*/
|
||||||
|
const std::vector<ProjectDataSpec>& getDataSpecs() const { return m_compiledSpecs; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable persistent user preference for particular spec string(s)
|
||||||
|
* @param specs String(s) representing unique spec(s) from getDataSpecs
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool enableDataSpecs(const std::vector<SystemString>& specs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disable persistent user preference for particular spec string(s)
|
||||||
|
* @param specs String(s) representing unique spec(s) from getDataSpecs
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool disableDataSpecs(const std::vector<SystemString>& specs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Begin cook process for specified directory
|
||||||
|
* @param path directory of intermediates to cook
|
||||||
|
* @param feedbackCb a callback to run reporting cook-progress
|
||||||
|
* @param recursive traverse subdirectories to cook as well
|
||||||
|
* @param fast enables faster (draft) extraction for supported data types
|
||||||
|
* @param spec if non-null, cook using a manually-selected dataspec
|
||||||
|
* @param cp if non-null, cook asynchronously via the ClientProcess
|
||||||
|
* @return true on success
|
||||||
|
*
|
||||||
|
* Object cooking is generally an expensive process for large projects.
|
||||||
|
* This method blocks execution during the procedure, with periodic
|
||||||
|
* feedback delivered via feedbackCb.
|
||||||
|
*/
|
||||||
|
bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool recursive = false,
|
||||||
|
bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr,
|
||||||
|
ClientProcess* cp = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Begin package process for specified !world.blend or directory
|
||||||
|
* @param path Path to !world.blend or directory
|
||||||
|
* @param feedbackCb a callback to run reporting cook-progress
|
||||||
|
* @param fast enables faster (draft) extraction for supported data types
|
||||||
|
* @param spec if non-null, cook using a manually-selected dataspec
|
||||||
|
* @param cp if non-null, cook asynchronously via the ClientProcess
|
||||||
|
*/
|
||||||
|
bool packagePath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool fast = false,
|
||||||
|
const DataSpecEntry* spec = nullptr, ClientProcess* cp = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interrupts a cook in progress (call from SIGINT handler)
|
||||||
|
*
|
||||||
|
* Database corruption is bad! HECL spreads its data objects through
|
||||||
|
* the filesystem; this ensures that open objects are cleanly
|
||||||
|
* finalized or discarded before stopping.
|
||||||
|
*
|
||||||
|
* Note that this method returns immediately; the resumed cookPath()
|
||||||
|
* call will return as quickly as possible.
|
||||||
|
*/
|
||||||
|
void interruptCook();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete cooked objects for directory
|
||||||
|
* @param path directory of intermediates to clean
|
||||||
|
* @param recursive traverse subdirectories to clean as well
|
||||||
|
* @return true on success
|
||||||
|
*
|
||||||
|
* Developers understand how useful 'clean' is. While ideally not required,
|
||||||
|
* it's useful for verifying that a rebuild from ground-up is doable.
|
||||||
|
*/
|
||||||
|
bool cleanPath(const ProjectPath& path, bool recursive = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a full depsgraph of the project-subpath provided
|
||||||
|
* @param path Subpath of project to root depsgraph at
|
||||||
|
* @return Populated depsgraph ready to traverse
|
||||||
|
*/
|
||||||
|
PackageDepsgraph buildPackageDepsgraph(const ProjectPath& path);
|
||||||
|
|
||||||
|
/** Add ProjectPath to bridge cache */
|
||||||
|
void addBridgePathToCache(uint64_t id, const ProjectPath& path);
|
||||||
|
|
||||||
|
/** Clear all ProjectPaths in bridge cache */
|
||||||
|
void clearBridgePathCache();
|
||||||
|
|
||||||
|
/** Lookup ProjectPath from bridge cache */
|
||||||
|
const ProjectPath* lookupBridgePath(uint64_t id) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Database
|
||||||
|
} // namespace hecl
|
|
@ -0,0 +1,103 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <athena/DNA.hpp>
|
||||||
|
#include <logvisor/logvisor.hpp>
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief FourCC representation used within HECL's database
|
||||||
|
*
|
||||||
|
* FourCCs are efficient, mnemonic four-char-sequences used to represent types
|
||||||
|
* while fitting comfortably in a 32-bit word. HECL uses a four-char array
|
||||||
|
* to remain endian-independent.
|
||||||
|
*/
|
||||||
|
class FourCC {
|
||||||
|
protected:
|
||||||
|
union {
|
||||||
|
char fcc[4];
|
||||||
|
uint32_t num = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Sentinel FourCC
|
||||||
|
constexpr FourCC() noexcept = default;
|
||||||
|
constexpr FourCC(const FourCC& other) noexcept = default;
|
||||||
|
constexpr FourCC(FourCC&& other) noexcept = default;
|
||||||
|
constexpr FourCC(const char* name) noexcept : fcc{name[0], name[1], name[2], name[3]} {}
|
||||||
|
constexpr FourCC(uint32_t n) noexcept : num(n) {}
|
||||||
|
|
||||||
|
constexpr FourCC& operator=(const FourCC&) noexcept = default;
|
||||||
|
constexpr FourCC& operator=(FourCC&&) noexcept = default;
|
||||||
|
|
||||||
|
constexpr bool operator==(const FourCC& other) const noexcept { return num == other.num; }
|
||||||
|
constexpr bool operator!=(const FourCC& other) const noexcept { return !operator==(other); }
|
||||||
|
constexpr bool operator==(const char* other) const noexcept {
|
||||||
|
return other[0] == fcc[0] && other[1] == fcc[1] && other[2] == fcc[2] && other[3] == fcc[3];
|
||||||
|
}
|
||||||
|
constexpr bool operator!=(const char* other) const noexcept { return !operator==(other); }
|
||||||
|
constexpr bool operator==(int32_t other) const noexcept { return num == uint32_t(other); }
|
||||||
|
constexpr bool operator!=(int32_t other) const noexcept { return !operator==(other); }
|
||||||
|
constexpr bool operator==(uint32_t other) const noexcept { return num == other; }
|
||||||
|
constexpr bool operator!=(uint32_t other) const noexcept { return !operator==(other); }
|
||||||
|
|
||||||
|
std::string toString() const { return std::string(std::begin(fcc), std::end(fcc)); }
|
||||||
|
constexpr std::string_view toStringView() const { return std::string_view(fcc, std::size(fcc)); }
|
||||||
|
constexpr uint32_t toUint32() const noexcept { return num; }
|
||||||
|
constexpr const char* getChars() const noexcept { return fcc; }
|
||||||
|
constexpr char* getChars() noexcept { return fcc; }
|
||||||
|
constexpr bool IsValid() const noexcept { return num != 0; }
|
||||||
|
};
|
||||||
|
#define FOURCC(chars) FourCC(SBIG(chars))
|
||||||
|
|
||||||
|
using BigDNA = athena::io::DNA<athena::Endian::Big>;
|
||||||
|
|
||||||
|
/** FourCC with DNA read/write */
|
||||||
|
class DNAFourCC final : public BigDNA, public FourCC {
|
||||||
|
public:
|
||||||
|
constexpr DNAFourCC() noexcept : FourCC() {}
|
||||||
|
constexpr DNAFourCC(const FourCC& other) noexcept : FourCC(other) {}
|
||||||
|
constexpr DNAFourCC(const char* name) noexcept : FourCC(name) {}
|
||||||
|
constexpr DNAFourCC(uint32_t n) noexcept : FourCC(n) {}
|
||||||
|
DNAFourCC(athena::io::IStreamReader& r) { read(r); }
|
||||||
|
AT_DECL_EXPLICIT_DNA_YAML
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
inline void DNAFourCC::Enumerate<BigDNA::Read>(Read::StreamT& r) {
|
||||||
|
r.readUBytesToBuf(fcc, std::size(fcc));
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void DNAFourCC::Enumerate<BigDNA::Write>(Write::StreamT& w) {
|
||||||
|
w.writeBytes(fcc, std::size(fcc));
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void DNAFourCC::Enumerate<BigDNA::ReadYaml>(ReadYaml::StreamT& r) {
|
||||||
|
const std::string rs = r.readString();
|
||||||
|
rs.copy(fcc, std::size(fcc));
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void DNAFourCC::Enumerate<BigDNA::WriteYaml>(WriteYaml::StreamT& w) {
|
||||||
|
w.writeString(std::string_view{fcc, std::size(fcc)});
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void DNAFourCC::Enumerate<BigDNA::BinarySize>(BinarySize::StreamT& s) {
|
||||||
|
s += std::size(fcc);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace hecl
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<hecl::FourCC> {
|
||||||
|
size_t operator()(const hecl::FourCC& val) const noexcept { return val.toUint32(); }
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
FMT_CUSTOM_FORMATTER(hecl::FourCC, "{:c}{:c}{:c}{:c}", obj.getChars()[0], obj.getChars()[1], obj.getChars()[2],
|
||||||
|
obj.getChars()[3])
|
||||||
|
FMT_CUSTOM_FORMATTER(hecl::DNAFourCC, "{:c}{:c}{:c}{:c}", obj.getChars()[0], obj.getChars()[1], obj.getChars()[2],
|
||||||
|
obj.getChars()[3])
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <athena/DNA.hpp>
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
|
||||||
|
enum class HMDLTopology : atUint32 {
|
||||||
|
Triangles,
|
||||||
|
TriStrips,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define HECL_HMDL_META_SZ 32
|
||||||
|
|
||||||
|
struct HMDLMeta : athena::io::DNA<athena::Endian::Big> {
|
||||||
|
AT_DECL_DNA
|
||||||
|
Value<atUint32> magic = 'TACO';
|
||||||
|
Value<HMDLTopology> topology;
|
||||||
|
Value<atUint32> vertStride;
|
||||||
|
Value<atUint32> vertCount;
|
||||||
|
Value<atUint32> indexCount;
|
||||||
|
Value<atUint32> colorCount;
|
||||||
|
Value<atUint32> uvCount;
|
||||||
|
Value<atUint16> weightCount;
|
||||||
|
Value<atUint16> bankCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hecl
|
|
@ -0,0 +1,821 @@
|
||||||
|
//===-- llvm/Support/MathExtras.h - Useful math functions -------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file contains some functions that are useful for math stuff.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// \macro LLVM_GNUC_PREREQ
|
||||||
|
/// \brief Extend the default __GNUC_PREREQ even if glibc's features.h isn't
|
||||||
|
/// available.
|
||||||
|
#ifndef LLVM_GNUC_PREREQ
|
||||||
|
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
|
||||||
|
#define LLVM_GNUC_PREREQ(maj, min, patch) \
|
||||||
|
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= ((maj) << 20) + ((min) << 10) + (patch))
|
||||||
|
#elif defined(__GNUC__) && defined(__GNUC_MINOR__)
|
||||||
|
#define LLVM_GNUC_PREREQ(maj, min, patch) ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))
|
||||||
|
#else
|
||||||
|
#define LLVM_GNUC_PREREQ(maj, min, patch) 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __has_builtin
|
||||||
|
#define __has_builtin(x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "hecl.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ANDROID_NDK__
|
||||||
|
#include <android/api-level.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace hecl {
|
||||||
|
namespace llvm {
|
||||||
|
/// \brief The behavior an operation has on an input of 0.
|
||||||
|
enum ZeroBehavior {
|
||||||
|
/// \brief The returned value is undefined.
|
||||||
|
ZB_Undefined,
|
||||||
|
/// \brief The returned value is numeric_limits<T>::max()
|
||||||
|
ZB_Max,
|
||||||
|
/// \brief The returned value is numeric_limits<T>::digits
|
||||||
|
ZB_Width
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename T, std::size_t SizeOfT>
|
||||||
|
struct TrailingZerosCounter {
|
||||||
|
static std::size_t count(T Val, ZeroBehavior) {
|
||||||
|
if (!Val)
|
||||||
|
return std::numeric_limits<T>::digits;
|
||||||
|
if (Val & 0x1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Bisection method.
|
||||||
|
std::size_t ZeroBits = 0;
|
||||||
|
T Shift = std::numeric_limits<T>::digits >> 1;
|
||||||
|
T Mask = std::numeric_limits<T>::max() >> Shift;
|
||||||
|
while (Shift) {
|
||||||
|
if ((Val & Mask) == 0) {
|
||||||
|
Val >>= Shift;
|
||||||
|
ZeroBits |= Shift;
|
||||||
|
}
|
||||||
|
Shift >>= 1;
|
||||||
|
Mask >>= Shift;
|
||||||
|
}
|
||||||
|
return ZeroBits;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if __GNUC__ >= 4 || defined(_MSC_VER)
|
||||||
|
template <typename T>
|
||||||
|
struct TrailingZerosCounter<T, 4> {
|
||||||
|
static std::size_t count(T Val, ZeroBehavior ZB) {
|
||||||
|
if (ZB != ZB_Undefined && Val == 0)
|
||||||
|
return 32;
|
||||||
|
|
||||||
|
#if __has_builtin(__builtin_ctz) || LLVM_GNUC_PREREQ(4, 0, 0)
|
||||||
|
return __builtin_ctz(Val);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
unsigned long Index;
|
||||||
|
_BitScanForward(&Index, Val);
|
||||||
|
return Index;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER) || defined(_M_X64)
|
||||||
|
template <typename T>
|
||||||
|
struct TrailingZerosCounter<T, 8> {
|
||||||
|
static std::size_t count(T Val, ZeroBehavior ZB) {
|
||||||
|
if (ZB != ZB_Undefined && Val == 0)
|
||||||
|
return 64;
|
||||||
|
|
||||||
|
#if __has_builtin(__builtin_ctzll) || LLVM_GNUC_PREREQ(4, 0, 0)
|
||||||
|
return __builtin_ctzll(Val);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
unsigned long Index;
|
||||||
|
_BitScanForward64(&Index, Val);
|
||||||
|
return Index;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// \brief Count number of 0's from the least significant bit to the most
|
||||||
|
/// stopping at the first 1.
|
||||||
|
///
|
||||||
|
/// Only unsigned integral types are allowed.
|
||||||
|
///
|
||||||
|
/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
|
||||||
|
/// valid arguments.
|
||||||
|
template <typename T>
|
||||||
|
std::size_t countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
|
||||||
|
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||||
|
"Only unsigned integral types are allowed.");
|
||||||
|
return detail::TrailingZerosCounter<T, sizeof(T)>::count(Val, ZB);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename T, std::size_t SizeOfT>
|
||||||
|
struct LeadingZerosCounter {
|
||||||
|
static std::size_t count(T Val, ZeroBehavior) {
|
||||||
|
if (!Val)
|
||||||
|
return std::numeric_limits<T>::digits;
|
||||||
|
|
||||||
|
// Bisection method.
|
||||||
|
std::size_t ZeroBits = 0;
|
||||||
|
for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
|
||||||
|
T Tmp = Val >> Shift;
|
||||||
|
if (Tmp)
|
||||||
|
Val = Tmp;
|
||||||
|
else
|
||||||
|
ZeroBits |= Shift;
|
||||||
|
}
|
||||||
|
return ZeroBits;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if __GNUC__ >= 4 || defined(_MSC_VER)
|
||||||
|
template <typename T>
|
||||||
|
struct LeadingZerosCounter<T, 4> {
|
||||||
|
static std::size_t count(T Val, ZeroBehavior ZB) {
|
||||||
|
if (ZB != ZB_Undefined && Val == 0)
|
||||||
|
return 32;
|
||||||
|
|
||||||
|
#if __has_builtin(__builtin_clz) || LLVM_GNUC_PREREQ(4, 0, 0)
|
||||||
|
return __builtin_clz(Val);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
unsigned long Index;
|
||||||
|
_BitScanReverse(&Index, Val);
|
||||||
|
return Index ^ 31;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER) || defined(_M_X64)
|
||||||
|
template <typename T>
|
||||||
|
struct LeadingZerosCounter<T, 8> {
|
||||||
|
static std::size_t count(T Val, ZeroBehavior ZB) {
|
||||||
|
if (ZB != ZB_Undefined && Val == 0)
|
||||||
|
return 64;
|
||||||
|
|
||||||
|
#if __has_builtin(__builtin_clzll) || LLVM_GNUC_PREREQ(4, 0, 0)
|
||||||
|
return __builtin_clzll(Val);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
unsigned long Index;
|
||||||
|
_BitScanReverse64(&Index, Val);
|
||||||
|
return Index ^ 63;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// \brief Count number of 0's from the most significant bit to the least
|
||||||
|
/// stopping at the first 1.
|
||||||
|
///
|
||||||
|
/// Only unsigned integral types are allowed.
|
||||||
|
///
|
||||||
|
/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
|
||||||
|
/// valid arguments.
|
||||||
|
template <typename T>
|
||||||
|
std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
|
||||||
|
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||||
|
"Only unsigned integral types are allowed.");
|
||||||
|
return detail::LeadingZerosCounter<T, sizeof(T)>::count(Val, ZB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Get the index of the first set bit starting from the least
|
||||||
|
/// significant bit.
|
||||||
|
///
|
||||||
|
/// Only unsigned integral types are allowed.
|
||||||
|
///
|
||||||
|
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
|
||||||
|
/// valid arguments.
|
||||||
|
template <typename T>
|
||||||
|
T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) {
|
||||||
|
if (ZB == ZB_Max && Val == 0)
|
||||||
|
return std::numeric_limits<T>::max();
|
||||||
|
|
||||||
|
return countTrailingZeros(Val, ZB_Undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Get the index of the last set bit starting from the least
|
||||||
|
/// significant bit.
|
||||||
|
///
|
||||||
|
/// Only unsigned integral types are allowed.
|
||||||
|
///
|
||||||
|
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
|
||||||
|
/// valid arguments.
|
||||||
|
template <typename T>
|
||||||
|
T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) {
|
||||||
|
if (ZB == ZB_Max && Val == 0)
|
||||||
|
return std::numeric_limits<T>::max();
|
||||||
|
|
||||||
|
// Use ^ instead of - because both gcc and llvm can remove the associated ^
|
||||||
|
// in the __builtin_clz intrinsic on x86.
|
||||||
|
return countLeadingZeros(Val, ZB_Undefined) ^ (std::numeric_limits<T>::digits - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Macro compressed bit reversal table for 256 bits.
|
||||||
|
///
|
||||||
|
/// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
|
||||||
|
static const unsigned char BitReverseTable256[256] = {
|
||||||
|
#define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64
|
||||||
|
#define R4(n) R2(n), R2(n + 2 * 16), R2(n + 1 * 16), R2(n + 3 * 16)
|
||||||
|
#define R6(n) R4(n), R4(n + 2 * 4), R4(n + 1 * 4), R4(n + 3 * 4)
|
||||||
|
R6(0), R6(2), R6(1), R6(3)
|
||||||
|
#undef R2
|
||||||
|
#undef R4
|
||||||
|
#undef R6
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Reverse the bits in \p Val.
|
||||||
|
template <typename T>
|
||||||
|
T reverseBits(T Val) {
|
||||||
|
unsigned char in[sizeof(Val)];
|
||||||
|
unsigned char out[sizeof(Val)];
|
||||||
|
std::memcpy(in, &Val, sizeof(Val));
|
||||||
|
for (unsigned i = 0; i < sizeof(Val); ++i)
|
||||||
|
out[(sizeof(Val) - i) - 1] = BitReverseTable256[in[i]];
|
||||||
|
std::memcpy(&Val, out, sizeof(Val));
|
||||||
|
return Val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: The following support functions use the _32/_64 extensions instead of
|
||||||
|
// type overloading so that signed and unsigned integers can be used without
|
||||||
|
// ambiguity.
|
||||||
|
|
||||||
|
/// Hi_32 - This function returns the high 32 bits of a 64 bit value.
|
||||||
|
constexpr inline uint32_t Hi_32(uint64_t Value) { return static_cast<uint32_t>(Value >> 32); }
|
||||||
|
|
||||||
|
/// Lo_32 - This function returns the low 32 bits of a 64 bit value.
|
||||||
|
constexpr inline uint32_t Lo_32(uint64_t Value) { return static_cast<uint32_t>(Value); }
|
||||||
|
|
||||||
|
/// Make_64 - This functions makes a 64-bit integer from a high / low pair of
|
||||||
|
/// 32-bit integers.
|
||||||
|
constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) { return ((uint64_t)High << 32) | (uint64_t)Low; }
|
||||||
|
|
||||||
|
/// isInt - Checks if an integer fits into the given bit width.
|
||||||
|
template <unsigned N>
|
||||||
|
constexpr inline bool isInt(int64_t x) {
|
||||||
|
return N >= 64 || (-(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1)));
|
||||||
|
}
|
||||||
|
// Template specializations to get better code for common cases.
|
||||||
|
template <>
|
||||||
|
constexpr inline bool isInt<8>(int64_t x) {
|
||||||
|
return static_cast<int8_t>(x) == x;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
constexpr inline bool isInt<16>(int64_t x) {
|
||||||
|
return static_cast<int16_t>(x) == x;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
constexpr inline bool isInt<32>(int64_t x) {
|
||||||
|
return static_cast<int32_t>(x) == x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// isShiftedInt<N,S> - Checks if a signed integer is an N bit number shifted
|
||||||
|
/// left by S.
|
||||||
|
template <unsigned N, unsigned S>
|
||||||
|
constexpr inline bool isShiftedInt(int64_t x) {
|
||||||
|
static_assert(N > 0, "isShiftedInt<0> doesn't make sense (refers to a 0-bit number.");
|
||||||
|
static_assert(N + S <= 64, "isShiftedInt<N, S> with N + S > 64 is too wide.");
|
||||||
|
return isInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// isUInt - Checks if an unsigned integer fits into the given bit width.
|
||||||
|
///
|
||||||
|
/// This is written as two functions rather than as simply
|
||||||
|
///
|
||||||
|
/// return N >= 64 || X < (UINT64_C(1) << N);
|
||||||
|
///
|
||||||
|
/// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting
|
||||||
|
/// left too many places.
|
||||||
|
template <unsigned N>
|
||||||
|
constexpr inline typename std::enable_if<(N < 64), bool>::type isUInt(uint64_t X) {
|
||||||
|
static_assert(N > 0, "isUInt<0> doesn't make sense");
|
||||||
|
return X < (UINT64_C(1) << (N));
|
||||||
|
}
|
||||||
|
template <unsigned N>
|
||||||
|
constexpr inline typename std::enable_if<N >= 64, bool>::type isUInt(uint64_t X) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template specializations to get better code for common cases.
|
||||||
|
template <>
|
||||||
|
constexpr inline bool isUInt<8>(uint64_t x) {
|
||||||
|
return static_cast<uint8_t>(x) == x;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
constexpr inline bool isUInt<16>(uint64_t x) {
|
||||||
|
return static_cast<uint16_t>(x) == x;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
constexpr inline bool isUInt<32>(uint64_t x) {
|
||||||
|
return static_cast<uint32_t>(x) == x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a unsigned integer is an N bit number shifted left by S.
|
||||||
|
template <unsigned N, unsigned S>
|
||||||
|
constexpr inline bool isShiftedUInt(uint64_t x) {
|
||||||
|
static_assert(N > 0, "isShiftedUInt<0> doesn't make sense (refers to a 0-bit number)");
|
||||||
|
static_assert(N + S <= 64, "isShiftedUInt<N, S> with N + S > 64 is too wide.");
|
||||||
|
// Per the two static_asserts above, S must be strictly less than 64. So
|
||||||
|
// 1 << S is not undefined behavior.
|
||||||
|
return isUInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the maximum value for a N-bit unsigned integer.
|
||||||
|
inline uint64_t maxUIntN(uint64_t N) {
|
||||||
|
assert(N > 0 && N <= 64 && "integer width out of range");
|
||||||
|
|
||||||
|
// uint64_t(1) << 64 is undefined behavior, so we can't do
|
||||||
|
// (uint64_t(1) << N) - 1
|
||||||
|
// without checking first that N != 64. But this works and doesn't have a
|
||||||
|
// branch.
|
||||||
|
return UINT64_MAX >> (64 - N);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the minimum value for a N-bit signed integer.
|
||||||
|
inline int64_t minIntN(int64_t N) {
|
||||||
|
assert(N > 0 && N <= 64 && "integer width out of range");
|
||||||
|
|
||||||
|
return -(UINT64_C(1) << (N - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the maximum value for a N-bit signed integer.
|
||||||
|
inline int64_t maxIntN(int64_t N) {
|
||||||
|
assert(N > 0 && N <= 64 && "integer width out of range");
|
||||||
|
|
||||||
|
// This relies on two's complement wraparound when N == 64, so we convert to
|
||||||
|
// int64_t only at the very end to avoid UB.
|
||||||
|
return (UINT64_C(1) << (N - 1)) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// isUIntN - Checks if an unsigned integer fits into the given (dynamic)
|
||||||
|
/// bit width.
|
||||||
|
inline bool isUIntN(unsigned N, uint64_t x) { return N >= 64 || x <= maxUIntN(N); }
|
||||||
|
|
||||||
|
/// isIntN - Checks if an signed integer fits into the given (dynamic)
|
||||||
|
/// bit width.
|
||||||
|
inline bool isIntN(unsigned N, int64_t x) { return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N)); }
|
||||||
|
|
||||||
|
/// isMask_32 - This function returns true if the argument is a non-empty
|
||||||
|
/// sequence of ones starting at the least significant bit with the remainder
|
||||||
|
/// zero (32 bit version). Ex. isMask_32(0x0000FFFFU) == true.
|
||||||
|
constexpr inline bool isMask_32(uint32_t Value) { return Value && ((Value + 1) & Value) == 0; }
|
||||||
|
|
||||||
|
/// isMask_64 - This function returns true if the argument is a non-empty
|
||||||
|
/// sequence of ones starting at the least significant bit with the remainder
|
||||||
|
/// zero (64 bit version).
|
||||||
|
constexpr inline bool isMask_64(uint64_t Value) { return Value && ((Value + 1) & Value) == 0; }
|
||||||
|
|
||||||
|
/// isShiftedMask_32 - This function returns true if the argument contains a
|
||||||
|
/// non-empty sequence of ones with the remainder zero (32 bit version.)
|
||||||
|
/// Ex. isShiftedMask_32(0x0000FF00U) == true.
|
||||||
|
constexpr inline bool isShiftedMask_32(uint32_t Value) { return Value && isMask_32((Value - 1) | Value); }
|
||||||
|
|
||||||
|
/// isShiftedMask_64 - This function returns true if the argument contains a
|
||||||
|
/// non-empty sequence of ones with the remainder zero (64 bit version.)
|
||||||
|
constexpr inline bool isShiftedMask_64(uint64_t Value) { return Value && isMask_64((Value - 1) | Value); }
|
||||||
|
|
||||||
|
/// isPowerOf2_32 - This function returns true if the argument is a power of
|
||||||
|
/// two > 0. Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
|
||||||
|
constexpr inline bool isPowerOf2_32(uint32_t Value) { return Value && !(Value & (Value - 1)); }
|
||||||
|
|
||||||
|
/// isPowerOf2_64 - This function returns true if the argument is a power of two
|
||||||
|
/// > 0 (64 bit edition.)
|
||||||
|
constexpr inline bool isPowerOf2_64(uint64_t Value) { return Value && !(Value & (Value - int64_t(1L))); }
|
||||||
|
|
||||||
|
/// ByteSwap_16 - This function returns a byte-swapped representation of the
|
||||||
|
/// 16-bit argument, Value.
|
||||||
|
inline uint16_t ByteSwap_16(uint16_t Value) { return hecl::bswap16(Value); }
|
||||||
|
|
||||||
|
/// ByteSwap_32 - This function returns a byte-swapped representation of the
|
||||||
|
/// 32-bit argument, Value.
|
||||||
|
inline uint32_t ByteSwap_32(uint32_t Value) { return hecl::bswap32(Value); }
|
||||||
|
|
||||||
|
/// ByteSwap_64 - This function returns a byte-swapped representation of the
|
||||||
|
/// 64-bit argument, Value.
|
||||||
|
inline uint64_t ByteSwap_64(uint64_t Value) { return hecl::bswap64(Value); }
|
||||||
|
|
||||||
|
/// \brief Count the number of ones from the most significant bit to the first
|
||||||
|
/// zero bit.
|
||||||
|
///
|
||||||
|
/// Ex. CountLeadingOnes(0xFF0FFF00) == 8.
|
||||||
|
/// Only unsigned integral types are allowed.
|
||||||
|
///
|
||||||
|
/// \param ZB the behavior on an input of all ones. Only ZB_Width and
|
||||||
|
/// ZB_Undefined are valid arguments.
|
||||||
|
template <typename T>
|
||||||
|
std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
|
||||||
|
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||||
|
"Only unsigned integral types are allowed.");
|
||||||
|
return countLeadingZeros(~Value, ZB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Count the number of ones from the least significant bit to the first
|
||||||
|
/// zero bit.
|
||||||
|
///
|
||||||
|
/// Ex. countTrailingOnes(0x00FF00FF) == 8.
|
||||||
|
/// Only unsigned integral types are allowed.
|
||||||
|
///
|
||||||
|
/// \param ZB the behavior on an input of all ones. Only ZB_Width and
|
||||||
|
/// ZB_Undefined are valid arguments.
|
||||||
|
template <typename T>
|
||||||
|
std::size_t countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
|
||||||
|
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||||
|
"Only unsigned integral types are allowed.");
|
||||||
|
return countTrailingZeros(~Value, ZB);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename T, std::size_t SizeOfT>
|
||||||
|
struct PopulationCounter {
|
||||||
|
static unsigned count(T Value) {
|
||||||
|
// Generic version, forward to 32 bits.
|
||||||
|
static_assert(SizeOfT <= 4, "Not implemented!");
|
||||||
|
#if __GNUC__ >= 4
|
||||||
|
return __builtin_popcount(Value);
|
||||||
|
#else
|
||||||
|
uint32_t v = Value;
|
||||||
|
v = v - ((v >> 1) & 0x55555555);
|
||||||
|
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
|
||||||
|
return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct PopulationCounter<T, 8> {
|
||||||
|
static unsigned count(T Value) {
|
||||||
|
#if __GNUC__ >= 4
|
||||||
|
return __builtin_popcountll(Value);
|
||||||
|
#else
|
||||||
|
uint64_t v = Value;
|
||||||
|
v = v - ((v >> 1) & 0x5555555555555555ULL);
|
||||||
|
v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
|
||||||
|
v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
|
||||||
|
return unsigned((uint64_t)(v * 0x0101010101010101ULL) >> 56);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// \brief Count the number of set bits in a value.
|
||||||
|
/// Ex. countPopulation(0xF000F000) = 8
|
||||||
|
/// Returns 0 if the word is zero.
|
||||||
|
template <typename T>
|
||||||
|
inline unsigned countPopulation(T Value) {
|
||||||
|
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||||
|
"Only unsigned integral types are allowed.");
|
||||||
|
return detail::PopulationCounter<T, sizeof(T)>::count(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Log2 - This function returns the log base 2 of the specified value
|
||||||
|
inline double Log2(double Value) {
|
||||||
|
#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
|
||||||
|
return __builtin_log(Value) / __builtin_log(2.0);
|
||||||
|
#else
|
||||||
|
return log2(Value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Log2_32 - This function returns the floor log base 2 of the specified value,
|
||||||
|
/// -1 if the value is zero. (32 bit edition.)
|
||||||
|
/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
|
||||||
|
inline unsigned Log2_32(uint32_t Value) { return 31 - countLeadingZeros(Value); }
|
||||||
|
|
||||||
|
/// Log2_64 - This function returns the floor log base 2 of the specified value,
|
||||||
|
/// -1 if the value is zero. (64 bit edition.)
|
||||||
|
inline unsigned Log2_64(uint64_t Value) { return 63 - countLeadingZeros(Value); }
|
||||||
|
|
||||||
|
/// Log2_32_Ceil - This function returns the ceil log base 2 of the specified
|
||||||
|
/// value, 32 if the value is zero. (32 bit edition).
|
||||||
|
/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
|
||||||
|
inline unsigned Log2_32_Ceil(uint32_t Value) { return 32 - countLeadingZeros(Value - 1); }
|
||||||
|
|
||||||
|
/// Log2_64_Ceil - This function returns the ceil log base 2 of the specified
|
||||||
|
/// value, 64 if the value is zero. (64 bit edition.)
|
||||||
|
inline unsigned Log2_64_Ceil(uint64_t Value) { return 64 - countLeadingZeros(Value - 1); }
|
||||||
|
|
||||||
|
/// GreatestCommonDivisor64 - Return the greatest common divisor of the two
|
||||||
|
/// values using Euclid's algorithm.
|
||||||
|
inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) {
|
||||||
|
while (B) {
|
||||||
|
uint64_t T = B;
|
||||||
|
B = A % B;
|
||||||
|
A = T;
|
||||||
|
}
|
||||||
|
return A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BitsToDouble - This function takes a 64-bit integer and returns the bit
|
||||||
|
/// equivalent double.
|
||||||
|
inline double BitsToDouble(uint64_t Bits) {
|
||||||
|
union {
|
||||||
|
uint64_t L;
|
||||||
|
double D;
|
||||||
|
} T;
|
||||||
|
T.L = Bits;
|
||||||
|
return T.D;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BitsToFloat - This function takes a 32-bit integer and returns the bit
|
||||||
|
/// equivalent float.
|
||||||
|
inline float BitsToFloat(uint32_t Bits) {
|
||||||
|
union {
|
||||||
|
uint32_t I;
|
||||||
|
float F;
|
||||||
|
} T;
|
||||||
|
T.I = Bits;
|
||||||
|
return T.F;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DoubleToBits - This function takes a double and returns the bit
|
||||||
|
/// equivalent 64-bit integer. Note that copying doubles around
|
||||||
|
/// changes the bits of NaNs on some hosts, notably x86, so this
|
||||||
|
/// routine cannot be used if these bits are needed.
|
||||||
|
inline uint64_t DoubleToBits(double Double) {
|
||||||
|
union {
|
||||||
|
uint64_t L;
|
||||||
|
double D;
|
||||||
|
} T;
|
||||||
|
T.D = Double;
|
||||||
|
return T.L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FloatToBits - This function takes a float and returns the bit
|
||||||
|
/// equivalent 32-bit integer. Note that copying floats around
|
||||||
|
/// changes the bits of NaNs on some hosts, notably x86, so this
|
||||||
|
/// routine cannot be used if these bits are needed.
|
||||||
|
inline uint32_t FloatToBits(float Float) {
|
||||||
|
union {
|
||||||
|
uint32_t I;
|
||||||
|
float F;
|
||||||
|
} T;
|
||||||
|
T.F = Float;
|
||||||
|
return T.I;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MinAlign - A and B are either alignments or offsets. Return the minimum
|
||||||
|
/// alignment that may be assumed after adding the two together.
|
||||||
|
constexpr inline uint64_t MinAlign(uint64_t A, uint64_t B) {
|
||||||
|
// The largest power of 2 that divides both A and B.
|
||||||
|
//
|
||||||
|
// Replace "-Value" by "1+~Value" in the following commented code to avoid
|
||||||
|
// MSVC warning C4146
|
||||||
|
// return (A | B) & -(A | B);
|
||||||
|
return (A | B) & (1 + ~(A | B));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Aligns \c Addr to \c Alignment bytes, rounding up.
|
||||||
|
///
|
||||||
|
/// Alignment should be a power of two. This method rounds up, so
|
||||||
|
/// alignAddr(7, 4) == 8 and alignAddr(8, 4) == 8.
|
||||||
|
inline uintptr_t alignAddr(const void* Addr, size_t Alignment) {
|
||||||
|
assert(Alignment && isPowerOf2_64((uint64_t)Alignment) && "Alignment is not a power of two!");
|
||||||
|
|
||||||
|
assert((uintptr_t)Addr + Alignment - 1 >= (uintptr_t)Addr);
|
||||||
|
|
||||||
|
return (((uintptr_t)Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Returns the necessary adjustment for aligning \c Ptr to \c Alignment
|
||||||
|
/// bytes, rounding up.
|
||||||
|
inline size_t alignmentAdjustment(const void* Ptr, size_t Alignment) {
|
||||||
|
return alignAddr(Ptr, Alignment) - (uintptr_t)Ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NextPowerOf2 - Returns the next power of two (in 64-bits)
|
||||||
|
/// that is strictly greater than A. Returns zero on overflow.
|
||||||
|
inline uint64_t NextPowerOf2(uint64_t A) {
|
||||||
|
A |= (A >> 1);
|
||||||
|
A |= (A >> 2);
|
||||||
|
A |= (A >> 4);
|
||||||
|
A |= (A >> 8);
|
||||||
|
A |= (A >> 16);
|
||||||
|
A |= (A >> 32);
|
||||||
|
return A + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the power of two which is less than or equal to the given value.
|
||||||
|
/// Essentially, it is a floor operation across the domain of powers of two.
|
||||||
|
inline uint64_t PowerOf2Floor(uint64_t A) {
|
||||||
|
if (!A)
|
||||||
|
return 0;
|
||||||
|
return 1ull << (63 - countLeadingZeros(A, ZB_Undefined));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the power of two which is greater than or equal to the given value.
|
||||||
|
/// Essentially, it is a ceil operation across the domain of powers of two.
|
||||||
|
inline uint64_t PowerOf2Ceil(uint64_t A) {
|
||||||
|
if (!A)
|
||||||
|
return 0;
|
||||||
|
return NextPowerOf2(A - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next integer (mod 2**64) that is greater than or equal to
|
||||||
|
/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
|
||||||
|
///
|
||||||
|
/// If non-zero \p Skew is specified, the return value will be a minimal
|
||||||
|
/// integer that is greater than or equal to \p Value and equal to
|
||||||
|
/// \p Align * N + \p Skew for some integer N. If \p Skew is larger than
|
||||||
|
/// \p Align, its value is adjusted to '\p Skew mod \p Align'.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
/// \code
|
||||||
|
/// alignTo(5, 8) = 8
|
||||||
|
/// alignTo(17, 8) = 24
|
||||||
|
/// alignTo(~0LL, 8) = 0
|
||||||
|
/// alignTo(321, 255) = 510
|
||||||
|
///
|
||||||
|
/// alignTo(5, 8, 7) = 7
|
||||||
|
/// alignTo(17, 8, 1) = 17
|
||||||
|
/// alignTo(~0LL, 8, 3) = 3
|
||||||
|
/// alignTo(321, 255, 42) = 552
|
||||||
|
/// \endcode
|
||||||
|
inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
|
||||||
|
assert(Align != 0u && "Align can't be 0.");
|
||||||
|
Skew %= Align;
|
||||||
|
return (Value + Align - 1 - Skew) / Align * Align + Skew;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next integer (mod 2**64) that is greater than or equal to
|
||||||
|
/// \p Value and is a multiple of \c Align. \c Align must be non-zero.
|
||||||
|
template <uint64_t Align>
|
||||||
|
constexpr inline uint64_t alignTo(uint64_t Value) {
|
||||||
|
static_assert(Align != 0u, "Align must be non-zero");
|
||||||
|
return (Value + Align - 1) / Align * Align;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \c alignTo for contexts where a constant expression is required.
|
||||||
|
/// \sa alignTo
|
||||||
|
///
|
||||||
|
/// \todo FIXME: remove when \c constexpr becomes really \c constexpr
|
||||||
|
template <uint64_t Align>
|
||||||
|
struct AlignTo {
|
||||||
|
static_assert(Align != 0u, "Align must be non-zero");
|
||||||
|
template <uint64_t Value>
|
||||||
|
struct from_value {
|
||||||
|
static const uint64_t value = (Value + Align - 1) / Align * Align;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns the largest uint64_t less than or equal to \p Value and is
|
||||||
|
/// \p Skew mod \p Align. \p Align must be non-zero
|
||||||
|
inline uint64_t alignDown(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
|
||||||
|
assert(Align != 0u && "Align can't be 0.");
|
||||||
|
Skew %= Align;
|
||||||
|
return (Value - Skew) / Align * Align + Skew;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the offset to the next integer (mod 2**64) that is greater than
|
||||||
|
/// or equal to \p Value and is a multiple of \p Align. \p Align must be
|
||||||
|
/// non-zero.
|
||||||
|
inline uint64_t OffsetToAlignment(uint64_t Value, uint64_t Align) { return alignTo(Value, Align) - Value; }
|
||||||
|
|
||||||
|
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
|
||||||
|
/// Requires 0 < B <= 32.
|
||||||
|
template <unsigned B>
|
||||||
|
constexpr inline int32_t SignExtend32(uint32_t X) {
|
||||||
|
static_assert(B > 0, "Bit width can't be 0.");
|
||||||
|
static_assert(B <= 32, "Bit width out of range.");
|
||||||
|
return int32_t(X << (32 - B)) >> (32 - B);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
|
||||||
|
/// Requires 0 < B < 32.
|
||||||
|
inline int32_t SignExtend32(uint32_t X, unsigned B) {
|
||||||
|
assert(B > 0 && "Bit width can't be 0.");
|
||||||
|
assert(B <= 32 && "Bit width out of range.");
|
||||||
|
return int32_t(X << (32 - B)) >> (32 - B);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
|
||||||
|
/// Requires 0 < B < 64.
|
||||||
|
template <unsigned B>
|
||||||
|
constexpr inline int64_t SignExtend64(uint64_t x) {
|
||||||
|
static_assert(B > 0, "Bit width can't be 0.");
|
||||||
|
static_assert(B <= 64, "Bit width out of range.");
|
||||||
|
return int64_t(x << (64 - B)) >> (64 - B);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
|
||||||
|
/// Requires 0 < B < 64.
|
||||||
|
inline int64_t SignExtend64(uint64_t X, unsigned B) {
|
||||||
|
assert(B > 0 && "Bit width can't be 0.");
|
||||||
|
assert(B <= 64 && "Bit width out of range.");
|
||||||
|
return int64_t(X << (64 - B)) >> (64 - B);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract two unsigned integers, X and Y, of type T and return the absolute
|
||||||
|
/// value of the result.
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value, T>::type AbsoluteDifference(T X, T Y) {
|
||||||
|
return std::max(X, Y) - std::min(X, Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add two unsigned integers, X and Y, of type T. Clamp the result to the
|
||||||
|
/// maximum representable value of T on overflow. ResultOverflowed indicates if
|
||||||
|
/// the result is larger than the maximum representable value of type T.
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingAdd(T X, T Y, bool* ResultOverflowed = nullptr) {
|
||||||
|
bool Dummy;
|
||||||
|
bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
|
||||||
|
// Hacker's Delight, p. 29
|
||||||
|
T Z = X + Y;
|
||||||
|
Overflowed = (Z < X || Z < Y);
|
||||||
|
if (Overflowed)
|
||||||
|
return std::numeric_limits<T>::max();
|
||||||
|
else
|
||||||
|
return Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the
|
||||||
|
/// maximum representable value of T on overflow. ResultOverflowed indicates if
|
||||||
|
/// the result is larger than the maximum representable value of type T.
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingMultiply(T X, T Y,
|
||||||
|
bool* ResultOverflowed = nullptr) {
|
||||||
|
bool Dummy;
|
||||||
|
bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
|
||||||
|
|
||||||
|
// Hacker's Delight, p. 30 has a different algorithm, but we don't use that
|
||||||
|
// because it fails for uint16_t (where multiplication can have undefined
|
||||||
|
// behavior due to promotion to int), and requires a division in addition
|
||||||
|
// to the multiplication.
|
||||||
|
|
||||||
|
Overflowed = false;
|
||||||
|
|
||||||
|
// Log2(Z) would be either Log2Z or Log2Z + 1.
|
||||||
|
// Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z
|
||||||
|
// will necessarily be less than Log2Max as desired.
|
||||||
|
int Log2Z = Log2_64(X) + Log2_64(Y);
|
||||||
|
const T Max = std::numeric_limits<T>::max();
|
||||||
|
int Log2Max = Log2_64(Max);
|
||||||
|
if (Log2Z < Log2Max) {
|
||||||
|
return X * Y;
|
||||||
|
}
|
||||||
|
if (Log2Z > Log2Max) {
|
||||||
|
Overflowed = true;
|
||||||
|
return Max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're going to use the top bit, and maybe overflow one
|
||||||
|
// bit past it. Multiply all but the bottom bit then add
|
||||||
|
// that on at the end.
|
||||||
|
T Z = (X >> 1) * Y;
|
||||||
|
if (Z & ~(Max >> 1)) {
|
||||||
|
Overflowed = true;
|
||||||
|
return Max;
|
||||||
|
}
|
||||||
|
Z <<= 1;
|
||||||
|
if (X & 1)
|
||||||
|
return SaturatingAdd(Z, Y, ResultOverflowed);
|
||||||
|
|
||||||
|
return Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply two unsigned integers, X and Y, and add the unsigned integer, A to
|
||||||
|
/// the product. Clamp the result to the maximum representable value of T on
|
||||||
|
/// overflow. ResultOverflowed indicates if the result is larger than the
|
||||||
|
/// maximum representable value of type T.
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingMultiplyAdd(T X, T Y, T A,
|
||||||
|
bool* ResultOverflowed = nullptr) {
|
||||||
|
bool Dummy;
|
||||||
|
bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
|
||||||
|
|
||||||
|
T Product = SaturatingMultiply(X, Y, &Overflowed);
|
||||||
|
if (Overflowed)
|
||||||
|
return Product;
|
||||||
|
|
||||||
|
return SaturatingAdd(A, Product, &Overflowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
|
||||||
|
extern const float huge_valf;
|
||||||
|
} // namespace llvm
|
||||||
|
} // namespace hecl
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue