fix_vs2017_warnings
kimkulling 2021-02-26 08:47:48 +01:00
commit 25abf8fb0d
515 changed files with 68319 additions and 1660 deletions

2
.github/FUNDING.yml vendored
View File

@ -1,2 +1,2 @@
patreon: assimp patreon: assimp
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4JRJVPXC4QJM4 open_collective: assimp

View File

@ -69,6 +69,12 @@ jobs:
repository: cpp-pm/polly repository: cpp-pm/polly
path: cmake/polly path: cmake/polly
- name: Remove contrib directory for Hunter builds
if: contains(matrix.name, 'hunter')
uses: JesseTG/rm@v1.0.2
with:
path: contrib
- name: Cache DX SDK - name: Cache DX SDK
id: dxcache id: dxcache
if: contains(matrix.name, 'windows') if: contains(matrix.name, 'windows')

3
.gitignore vendored
View File

@ -18,6 +18,9 @@ build
*.VC.db-wal *.VC.db-wal
*.VC.opendb *.VC.opendb
*.ipch *.ipch
.vs/
out/
CMakeSettings.json
# Output # Output
bin/ bin/

View File

@ -1,6 +1,6 @@
# Build Instructions # Build / Install Instructions
## Build on all platforms using vcpkg ## Install on all platforms using vcpkg
You can download and install assimp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: You can download and install assimp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
```bash ```bash
git clone https://github.com/Microsoft/vcpkg.git git clone https://github.com/Microsoft/vcpkg.git
@ -11,6 +11,18 @@ You can download and install assimp using the [vcpkg](https://github.com/Microso
``` ```
The assimp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. The assimp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
## Install on Ubuntu
You can install the Asset-Importer-Lib via apt:
```
sudo apt-get install assimp
```
## Install pyassimp
You need to have pip installed:
```
pip install pyassimp
```
## Manual build instructions ## Manual build instructions
### Install CMake ### Install CMake
@ -24,6 +36,12 @@ Make sure you have a working git-installation. Open a command prompt and clone t
```bash ```bash
git clone https://github.com/assimp/assimp.git git clone https://github.com/assimp/assimp.git
``` ```
### Build from source:
```bash
cd assimp
cmake CMakeLists.txt
cmake --build .
```
### Build instructions for Windows with Visual-Studio ### Build instructions for Windows with Visual-Studio

View File

@ -38,7 +38,7 @@ SET(CMAKE_POLICY_DEFAULT_CMP0012 NEW)
SET(CMAKE_POLICY_DEFAULT_CMP0074 NEW) SET(CMAKE_POLICY_DEFAULT_CMP0074 NEW)
SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW) SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW)
CMAKE_MINIMUM_REQUIRED( VERSION 3.0 ) CMAKE_MINIMUM_REQUIRED( VERSION 3.10 )
# Toggles the use of the hunter package manager # Toggles the use of the hunter package manager
option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
@ -46,8 +46,8 @@ option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
IF(ASSIMP_HUNTER_ENABLED) IF(ASSIMP_HUNTER_ENABLED)
include("cmake/HunterGate.cmake") include("cmake/HunterGate.cmake")
HunterGate( HunterGate(
URL "https://github.com/cpp-pm/hunter/archive/v0.23.269.tar.gz" URL "https://github.com/cpp-pm/hunter/archive/v0.23.293.tar.gz"
SHA1 "64024b7b95b4c86d50ae05b926814448c93a70a0" SHA1 "e8e5470652db77149d9b38656db2a6c0b7642693"
) )
add_definitions(-DASSIMP_USE_HUNTER) add_definitions(-DASSIMP_USE_HUNTER)
@ -61,7 +61,6 @@ OPTION( BUILD_SHARED_LIBS
"Build package with shared libraries." "Build package with shared libraries."
ON ON
) )
OPTION( ASSIMP_BUILD_FRAMEWORK OPTION( ASSIMP_BUILD_FRAMEWORK
"Build package as Mac OS X Framework bundle." "Build package as Mac OS X Framework bundle."
OFF OFF
@ -133,9 +132,22 @@ OPTION ( ASSIMP_IGNORE_GIT_HASH
) )
IF ( WIN32 ) IF ( WIN32 )
# Use subset of Windows.h
ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
OPTION ( ASSIMP_BUILD_ASSIMP_VIEW OPTION ( ASSIMP_BUILD_ASSIMP_VIEW
"If the Assimp view tool is built. (requires DirectX)" "If the Assimp view tool is built. (requires DirectX)"
OFF ) OFF )
IF(MSVC)
OPTION( ASSIMP_INSTALL_PDB
"Install MSVC debug files."
ON )
IF(NOT (MSVC_VERSION LESS 1900))
# Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
ENDIF()
ENDIF()
ENDIF() ENDIF()
IF (IOS AND NOT ASSIMP_HUNTER_ENABLED) IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
@ -145,21 +157,6 @@ IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
ADD_DEFINITIONS(-DENABLE_BITCODE) ADD_DEFINITIONS(-DENABLE_BITCODE)
ENDIF () ENDIF ()
# Use subset of Windows.h
if (WIN32)
ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
endif()
IF(MSVC)
OPTION( ASSIMP_INSTALL_PDB
"Install MSVC debug files."
ON
)
IF(NOT (MSVC_VERSION LESS 1900))
# Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
ENDIF()
ENDIF()
IF (ASSIMP_BUILD_FRAMEWORK) IF (ASSIMP_BUILD_FRAMEWORK)
SET (BUILD_SHARED_LIBS ON) SET (BUILD_SHARED_LIBS ON)
@ -455,6 +452,12 @@ IF(ASSIMP_HUNTER_ENABLED)
set(ZLIB_LIBRARIES ZLIB::zlib) set(ZLIB_LIBRARIES ZLIB::zlib)
set(ASSIMP_BUILD_MINIZIP TRUE) set(ASSIMP_BUILD_MINIZIP TRUE)
ELSE() ELSE()
# If the zlib is already found outside, add an export in case assimpTargets can't find it.
IF( ZLIB_FOUND )
INSTALL( TARGETS zlib
EXPORT "${TARGETS_EXPORT_NAME}")
ENDIF()
IF ( NOT ASSIMP_BUILD_ZLIB ) IF ( NOT ASSIMP_BUILD_ZLIB )
FIND_PACKAGE(ZLIB) FIND_PACKAGE(ZLIB)
ENDIF() ENDIF()
@ -570,6 +573,94 @@ ELSE ()
ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER ) ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
ENDIF () ENDIF ()
# Draco requires cmake 3.12
IF (DEFINED CMAKE_VERSION AND "${CMAKE_VERSION}" VERSION_LESS "3.12")
message(NOTICE "draco requires cmake 3.12 or newer, cmake is ${CMAKE_VERSION} . Draco is disabled")
SET ( ASSIMP_BUILD_DRACO OFF CACHE BOOL "Disabled: Draco requires newer cmake" FORCE )
ELSE()
OPTION ( ASSIMP_BUILD_DRACO "If the Draco libraries are to be built. Primarily for glTF" ON )
IF ( ASSIMP_BUILD_DRACO )
# Primarily for glTF v2
# Enable Draco glTF feature set
set(DRACO_GLTF ON CACHE BOOL "" FORCE)
# Disable unnecessary or omitted components
set(DRACO_JS_GLUE OFF CACHE BOOL "" FORCE)
set(DRACO_WASM OFF CACHE BOOL "" FORCE)
set(DRACO_MAYA_PLUGIN OFF CACHE BOOL "" FORCE)
set(DRACO_UNITY_PLUGIN OFF CACHE BOOL "" FORCE)
set(DRACO_TESTS OFF CACHE BOOL "" FORCE)
IF(ASSIMP_HUNTER_ENABLED)
hunter_add_package(draco)
find_package(draco CONFIG REQUIRED)
set(draco_LIBRARIES draco::draco)
ELSE()
# Draco 1.4.1 has many warnings and will not build with /WX or -Werror
# See https://github.com/google/draco/issues/672
# and https://github.com/google/draco/issues/673
IF(MSVC)
set(DRACO_CXX_FLAGS "/W0")
ELSE()
list(APPEND DRACO_CXX_FLAGS
"-Wno-bool-compare"
"-Wno-comment"
"-Wno-maybe-uninitialized"
"-Wno-sign-compare"
"-Wno-unused-local-typedefs"
)
# Draco 1.4.1 does not explicitly export any symbols under GCC/clang
list(APPEND DRACO_CXX_FLAGS
"-fvisibility=default"
)
ENDIF()
# Don't build or install all of Draco by default
ADD_SUBDIRECTORY( "contrib/draco" EXCLUDE_FROM_ALL )
if(MSVC OR WIN32)
set(draco_LIBRARIES "draco")
else()
if(BUILD_SHARED_LIBS)
set(draco_LIBRARIES "draco_shared")
else()
set(draco_LIBRARIES "draco_static")
endif()
endif()
# Don't build the draco command-line tools by default
set_target_properties(draco_encoder draco_decoder PROPERTIES
EXCLUDE_FROM_ALL TRUE
EXCLUDE_FROM_DEFAULT_BUILD TRUE
)
# Do build the draco shared library
set_target_properties(${draco_LIBRARIES} PROPERTIES
EXCLUDE_FROM_ALL FALSE
EXCLUDE_FROM_DEFAULT_BUILD FALSE
)
TARGET_USE_COMMON_OUTPUT_DIRECTORY(${draco_LIBRARIES})
TARGET_USE_COMMON_OUTPUT_DIRECTORY(draco_encoder)
TARGET_USE_COMMON_OUTPUT_DIRECTORY(draco_decoder)
set(draco_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/contrib/draco/src")
# This is probably wrong
INSTALL( TARGETS ${draco_LIBRARIES}
EXPORT "${TARGETS_EXPORT_NAME}"
LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR}
FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
COMPONENT ${LIBASSIMP_COMPONENT}
INCLUDES DESTINATION include
)
ENDIF()
ENDIF()
ENDIF()
# Main assimp code
ADD_SUBDIRECTORY( code/ ) ADD_SUBDIRECTORY( code/ )
IF ( ASSIMP_BUILD_ASSIMP_TOOLS ) IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
# The viewer for windows only # The viewer for windows only
@ -583,7 +674,7 @@ IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
ADD_SUBDIRECTORY( tools/assimp_cmd/ ) ADD_SUBDIRECTORY( tools/assimp_cmd/ )
ENDIF () ENDIF ()
IF ( ASSIMP_BUILD_SAMPLES) IF ( ASSIMP_BUILD_SAMPLES )
SET( SAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/samples ) SET( SAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/samples )
SET( SAMPLES_SHARED_CODE_DIR ${SAMPLES_DIR}/SharedCode ) SET( SAMPLES_SHARED_CODE_DIR ${SAMPLES_DIR}/SharedCode )
IF ( WIN32 ) IF ( WIN32 )

View File

@ -45,6 +45,7 @@ Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file.
* [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777) * [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777)
* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status)) * [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status))
* [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port. * [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
* [Rust](https://github.com/jkvargas/russimp)
### Other tools ### ### Other tools ###
[open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities. [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.
@ -105,12 +106,6 @@ Become a financial contributor and help us sustain our community. [[Contribute](
Monthly donations via Patreon: Monthly donations via Patreon:
<br>[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/assimp) <br>[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/assimp)
<br>
One-off donations via PayPal:
<br>[![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4JRJVPXC4QJM4)
<br>
#### Organizations #### Organizations

View File

@ -1,81 +0,0 @@
# AppVeyor file
# http://www.appveyor.com/docs/appveyor-yml
# clone directory
clone_folder: c:\projects\assimp
clone_depth: 1
# branches to build
branches:
# whitelist
only:
- master
matrix:
fast_finish: true
image:
- Visual Studio 2013
#- Visual Studio 2015
#- Visual Studio 2017
- Visual Studio 2019
#- MinGW
platform:
- Win32
- x64
configuration: Release
install:
- set PATH=C:\Ruby24-x64\bin;%PATH%
- set CMAKE_DEFINES -DASSIMP_WERROR=ON
- if [%COMPILER%]==[MinGW] set PATH=C:\MinGW\bin;%PATH%
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" set CMAKE_GENERATOR_NAME=Visual Studio 16 2019
- cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" -A %platform% .
# Rename sh.exe as sh.exe in PATH interferes with MinGW - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
- rename "C:\Program Files\Git\usr\bin\sh.exe" "sh2.exe"
- set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
- ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe
- ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe
cache:
- code\assimp.dir\%CONFIGURATION%
- contrib\zlib\zlibstatic.dir\%CONFIGURATION%
- contrib\zlib\zlib.dir\%CONFIGURATION%
- tools\assimp_cmd\assimp_cmd.dir\%CONFIGURATION%
- tools\assimp_view\assimp_viewer.dir\%CONFIGURATION%
- test\unit.dir\%CONFIGURATION%
- bin\.mtime_cache
before_build:
- echo NUMBER_OF_PROCESSORS=%NUMBER_OF_PROCESSORS%
- ruby scripts\AppVeyor\mtime_cache -g scripts\AppVeyor\cacheglobs.txt -c bin\.mtime_cache\cache.json
build_script:
cmake --build . --config Release -- /maxcpucount:2
after_build:
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (
if "%platform%"=="x64" (
iscc packaging\windows-innosetup\script_x64.iss
) else (
iscc packaging\windows-innosetup\script_x86.iss
)
)
- 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
test_script:
- cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml
on_finish:
- ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml))
artifacts:
- path: assimp.7z
name: assimp_lib

View File

@ -10,5 +10,9 @@ find_package(polyclipping CONFIG REQUIRED)
find_package(zip CONFIG REQUIRED) find_package(zip CONFIG REQUIRED)
find_package(pugixml CONFIG REQUIRED) find_package(pugixml CONFIG REQUIRED)
if(@ASSIMP_BUILD_DRACO@)
find_package(draco CONFIG REQUIRED)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
check_required_components("@PROJECT_NAME@") check_required_components("@PROJECT_NAME@")

View File

@ -266,6 +266,7 @@ void Discreet3DSImporter::ParseMainChunk() {
case Discreet3DS::CHUNK_PRJ: case Discreet3DS::CHUNK_PRJ:
bIsPrj = true; bIsPrj = true;
break;
case Discreet3DS::CHUNK_MAIN: case Discreet3DS::CHUNK_MAIN:
ParseEditorChunk(); ParseEditorChunk();
break; break;

View File

@ -427,7 +427,7 @@ private:
aiFace face = ReadTriangle(currentNode); aiFace face = ReadTriangle(currentNode);
faces.push_back(face); faces.push_back(face);
int pid, p1; int pid = 0, p1;
bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);

View File

@ -194,7 +194,7 @@ void AMFImporter::ParseNode_Coordinates(XmlNode &node) {
// <volume // <volume
// materialid="" - Which material to use. // materialid="" - Which material to use.
// type="" - What this volume describes can be “region” or “support”. If none specified, “object” is assumed. If support, then the geometric // type="" - What this volume describes can be "region" or "support". If none specified, "object" is assumed. If support, then the geometric
// requirements 1-8 listed in section 5 do not need to be maintained. // requirements 1-8 listed in section 5 do not need to be maintained.
// > // >
// </volume> // </volume>

View File

@ -240,7 +240,7 @@ struct AMFVertices : public AMFNodeElementBase {
/// Structure that define volume node. /// Structure that define volume node.
struct AMFVolume : public AMFNodeElementBase { struct AMFVolume : public AMFNodeElementBase {
std::string MaterialID; ///< Which material to use. std::string MaterialID; ///< Which material to use.
std::string Type; ///< What this volume describes can be “region” or “support”. If none specified, “object” is assumed. std::string Type; ///< What this volume describes can be "region" or "support". If none specified, "object" is assumed.
/// Constructor. /// Constructor.
/// \param [in] pParent - pointer to parent node. /// \param [in] pParent - pointer to parent node.

View File

@ -329,8 +329,8 @@ void AMFImporter::Postprocess_AddMetadata(const AMFMetaDataArray &metadataList,
sceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metadataList.size())); sceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metadataList.size()));
size_t meta_idx(0); size_t meta_idx(0);
for (const AMFMetadata &metadata : metadataList) { for (const AMFMetadata *metadata : metadataList) {
sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata.Type, aiString(metadata.Value)); sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata->Type, aiString(metadata->Value));
} }
} }

View File

@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER #ifndef ASSIMP_BUILD_NO_COB_IMPORTER
#include "AssetLib/COB/COBLoader.h" #include "AssetLib/COB/COBLoader.h"
#include "AssetLib/COB/COBScene.h" #include "AssetLib/COB/COBScene.h"
#include "PostProcessing/ConvertToLHProcess.h" #include "PostProcessing/ConvertToLHProcess.h"
@ -90,11 +91,15 @@ static const aiImporterDesc desc = {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
COBImporter::COBImporter() {} COBImporter::COBImporter() {
// empty
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
COBImporter::~COBImporter() {} COBImporter::~COBImporter() {
// empty
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
@ -466,8 +471,9 @@ void COBImporter::UnsupportedChunk_Ascii(LineSplitter &splitter, const ChunkInfo
// missing the next line. // missing the next line.
splitter.get_stream().IncPtr(nfo.size); splitter.get_stream().IncPtr(nfo.size);
splitter.swallow_next_increment(); splitter.swallow_next_increment();
} else } else {
ThrowException(error); ThrowException(error);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -790,25 +796,12 @@ void COBImporter::ReadBitM_Ascii(Scene & /*out*/, LineSplitter &splitter, const
if (nfo.version > 1) { if (nfo.version > 1) {
return UnsupportedChunk_Ascii(splitter, nfo, "BitM"); return UnsupportedChunk_Ascii(splitter, nfo, "BitM");
} }
/*
"\nThumbNailHdrSize %ld"
"\nThumbHeader: %02hx 02hx %02hx "
"\nColorBufSize %ld"
"\nColorBufZipSize %ld"
"\nZippedThumbnail: %02hx 02hx %02hx "
*/
const unsigned int head = strtoul10((++splitter)[1]); const unsigned int head = strtoul10((++splitter)[1]);
if (head != sizeof(Bitmap::BitmapHeader)) { if (head != sizeof(Bitmap::BitmapHeader)) {
ASSIMP_LOG_WARN("Unexpected ThumbNailHdrSize, skipping this chunk"); ASSIMP_LOG_WARN("Unexpected ThumbNailHdrSize, skipping this chunk");
return; return;
} }
/*union {
Bitmap::BitmapHeader data;
char opaq[sizeof Bitmap::BitmapHeader()];
};*/
// ReadHexOctets(opaq,head,(++splitter)[1]);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -884,7 +877,10 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) {
while (1) { while (1) {
std::string type; std::string type;
type += reader->GetI1(), type += reader->GetI1(), type += reader->GetI1(), type += reader->GetI1(); type += reader->GetI1();
type += reader->GetI1();
type += reader->GetI1();
type += reader->GetI1();
ChunkInfo nfo; ChunkInfo nfo;
nfo.version = reader->GetI2() * 10; nfo.version = reader->GetI2() * 10;
@ -906,14 +902,7 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) {
ReadCame_Binary(out, *reader, nfo); ReadCame_Binary(out, *reader, nfo);
} else if (type == "Mat1") { } else if (type == "Mat1") {
ReadMat1_Binary(out, *reader, nfo); ReadMat1_Binary(out, *reader, nfo);
} } else if (type == "Unit") {
/* else if (type == "Bone") {
ReadBone_Binary(out,*reader,nfo);
}
else if (type == "Chan") {
ReadChan_Binary(out,*reader,nfo);
}*/
else if (type == "Unit") {
ReadUnit_Binary(out, *reader, nfo); ReadUnit_Binary(out, *reader, nfo);
} else if (type == "OLay") { } else if (type == "OLay") {
// ignore layer index silently. // ignore layer index silently.
@ -923,9 +912,10 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) {
return UnsupportedChunk_Binary(*reader, nfo, type.c_str()); return UnsupportedChunk_Binary(*reader, nfo, type.c_str());
} else if (type == "END ") { } else if (type == "END ") {
return; return;
} else } else {
UnsupportedChunk_Binary(*reader, nfo, type.c_str()); UnsupportedChunk_Binary(*reader, nfo, type.c_str());
} }
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h> #include <assimp/material.h>
#include <assimp/mesh.h> #include <assimp/mesh.h>
#include <stdint.h> #include <cstdint>
#include <map> #include <map>
#include <set> #include <set>
#include <vector> #include <vector>
@ -206,7 +206,8 @@ struct SemanticMappingTable {
std::string mMatName; std::string mMatName;
/// List of semantic map commands, grouped by effect semantic name /// List of semantic map commands, grouped by effect semantic name
std::map<std::string, InputSemanticMapEntry> mMap; using InputSemanticMap = std::map<std::string, InputSemanticMapEntry>;
InputSemanticMap mMap;
/// For std::find /// For std::find
bool operator==(const std::string &s) const { bool operator==(const std::string &s) const {

View File

@ -63,6 +63,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp { namespace Assimp {
using namespace Assimp::Formatter; using namespace Assimp::Formatter;
using namespace Assimp::Collada;
static const aiImporterDesc desc = { static const aiImporterDesc desc = {
"Collada Importer", "Collada Importer",
@ -271,7 +272,7 @@ aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collad
node->mTransformation = pParser.CalculateResultTransform(pNode->mTransforms); node->mTransformation = pParser.CalculateResultTransform(pNode->mTransforms);
// now resolve node instances // now resolve node instances
std::vector<const Collada::Node *> instances; std::vector<const Node*> instances;
ResolveNodeInstances(pParser, pNode, instances); ResolveNodeInstances(pParser, pNode, instances);
// add children. first the *real* ones // add children. first the *real* ones
@ -298,8 +299,8 @@ aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collad
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Resolve node instances // Resolve node instances
void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode, void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Node *pNode,
std::vector<const Collada::Node *> &resolved) { std::vector<const Node*> &resolved) {
// reserve enough storage // reserve enough storage
resolved.reserve(pNode->mNodeInstances.size()); resolved.reserve(pNode->mNodeInstances.size());
@ -307,7 +308,7 @@ void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Col
for (const auto &nodeInst : pNode->mNodeInstances) { for (const auto &nodeInst : pNode->mNodeInstances) {
// find the corresponding node in the library // find the corresponding node in the library
const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find(nodeInst.mNode); const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find(nodeInst.mNode);
const Collada::Node *nd = itt == pParser.mNodeLibrary.end() ? nullptr : (*itt).second; const Node *nd = itt == pParser.mNodeLibrary.end() ? nullptr : (*itt).second;
// FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632 // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
// need to check for both name and ID to catch all. To avoid breaking valid files, // need to check for both name and ID to catch all. To avoid breaking valid files,
@ -326,13 +327,13 @@ void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Col
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Resolve UV channels // Resolve UV channels
void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler &sampler, const Collada::SemanticMappingTable &table) { void ColladaLoader::ApplyVertexToEffectSemanticMapping(Sampler &sampler, const SemanticMappingTable &table) {
std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel); SemanticMappingTable::InputSemanticMap::const_iterator it = table.mMap.find(sampler.mUVChannel);
if (it == table.mMap.end()) { if (it == table.mMap.end()) {
return; return;
} }
if (it->second.mType != Collada::IT_Texcoord) { if (it->second.mType != IT_Texcoord) {
ASSIMP_LOG_ERROR("Collada: Unexpected effect input mapping"); ASSIMP_LOG_ERROR("Collada: Unexpected effect input mapping");
} }
@ -341,8 +342,8 @@ void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler &sampler
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Builds lights for the given node and references them // Builds lights for the given node and references them
void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Collada::Node *pNode, aiNode *pTarget) { void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
for (const Collada::LightInstance &lid : pNode->mLights) { for (const LightInstance &lid : pNode->mLights) {
// find the referred light // find the referred light
ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find(lid.mLight); ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find(lid.mLight);
if (srcLightIt == pParser.mLightLibrary.end()) { if (srcLightIt == pParser.mLightLibrary.end()) {
@ -406,8 +407,8 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Colla
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Builds cameras for the given node and references them // Builds cameras for the given node and references them
void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Collada::Node *pNode, aiNode *pTarget) { void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
for (const Collada::CameraInstance &cid : pNode->mCameras) { for (const CameraInstance &cid : pNode->mCameras) {
// find the referred light // find the referred light
ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera); ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera);
if (srcCameraIt == pParser.mCameraLibrary.end()) { if (srcCameraIt == pParser.mCameraLibrary.end()) {
@ -461,15 +462,15 @@ void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Coll
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Builds meshes for the given node and references them // Builds meshes for the given node and references them
void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode, aiNode *pTarget) { void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
// accumulated mesh references by this node // accumulated mesh references by this node
std::vector<size_t> newMeshRefs; std::vector<size_t> newMeshRefs;
newMeshRefs.reserve(pNode->mMeshes.size()); newMeshRefs.reserve(pNode->mMeshes.size());
// add a mesh for each subgroup in each collada mesh // add a mesh for each subgroup in each collada mesh
for (const Collada::MeshInstance &mid : pNode->mMeshes) { for (const MeshInstance &mid : pNode->mMeshes) {
const Collada::Mesh *srcMesh = nullptr; const Mesh *srcMesh = nullptr;
const Collada::Controller *srcController = nullptr; const Controller *srcController = nullptr;
// find the referred mesh // find the referred mesh
ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController); ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController);
@ -503,7 +504,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Colla
// find material assigned to this submesh // find material assigned to this submesh
std::string meshMaterial; std::string meshMaterial;
std::map<std::string, Collada::SemanticMappingTable>::const_iterator meshMatIt = mid.mMaterials.find(submesh.mMaterial); std::map<std::string, SemanticMappingTable>::const_iterator meshMatIt = mid.mMaterials.find(submesh.mMaterial);
const Collada::SemanticMappingTable *table = nullptr; const Collada::SemanticMappingTable *table = nullptr;
if (meshMatIt != mid.mMaterials.end()) { if (meshMatIt != mid.mMaterials.end()) {
@ -557,7 +558,12 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Colla
faceStart += submesh.mNumFaces; faceStart += submesh.mNumFaces;
// assign the material index // assign the material index
std::map<std::string, size_t>::const_iterator subMatIt = mMaterialIndexByName.find(submesh.mMaterial);
if (subMatIt != mMaterialIndexByName.end()) {
dstMesh->mMaterialIndex = static_cast<unsigned int>(subMatIt->second);
} else {
dstMesh->mMaterialIndex = matIdx; dstMesh->mMaterialIndex = matIdx;
}
if (dstMesh->mName.length == 0) { if (dstMesh->mName.length == 0) {
dstMesh->mName = mid.mMeshOrController; dstMesh->mName = mid.mMeshOrController;
} }
@ -586,15 +592,15 @@ aiMesh *ColladaLoader::findMesh(const std::string &meshid) {
return nullptr; return nullptr;
} }
for (unsigned int i = 0; i < mMeshes.size(); ++i) { for (auto & mMeshe : mMeshes) {
if (std::string(mMeshes[i]->mName.data) == meshid) { if (std::string(mMeshe->mName.data) == meshid) {
return mMeshes[i]; return mMeshe;
} }
} }
for (unsigned int i = 0; i < mTargetMeshes.size(); ++i) { for (auto & mTargetMeshe : mTargetMeshes) {
if (std::string(mTargetMeshes[i]->mName.data) == meshid) { if (std::string(mTargetMeshe->mName.data) == meshid) {
return mTargetMeshes[i]; return mTargetMeshe;
} }
} }
@ -603,8 +609,8 @@ aiMesh *ColladaLoader::findMesh(const std::string &meshid) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::Mesh *pSrcMesh, const Collada::SubMesh &pSubMesh, aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrcMesh, const SubMesh &pSubMesh,
const Collada::Controller *pSrcController, size_t pStartVertex, size_t pStartFace) { const Controller *pSrcController, size_t pStartVertex, size_t pStartFace) {
std::unique_ptr<aiMesh> dstMesh(new aiMesh); std::unique_ptr<aiMesh> dstMesh(new aiMesh);
if (useColladaName) { if (useColladaName) {
@ -642,7 +648,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::M
std::copy(pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + pStartVertex + numVertices, dstMesh->mBitangents); std::copy(pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + pStartVertex + numVertices, dstMesh->mBitangents);
} }
// same for texturecoords, as many as we have // same for texture coords, as many as we have
// empty slots are not allowed, need to pack and adjust UV indexes accordingly // empty slots are not allowed, need to pack and adjust UV indexes accordingly
for (size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { for (size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
if (pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) { if (pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) {
@ -682,11 +688,11 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::M
// create morph target meshes if any // create morph target meshes if any
std::vector<aiMesh *> targetMeshes; std::vector<aiMesh *> targetMeshes;
std::vector<float> targetWeights; std::vector<float> targetWeights;
Collada::MorphMethod method = Collada::Normalized; Collada::MorphMethod method = Normalized;
for (std::map<std::string, Collada::Controller>::const_iterator it = pParser.mControllerLibrary.begin(); for (std::map<std::string, Controller>::const_iterator it = pParser.mControllerLibrary.begin();
it != pParser.mControllerLibrary.end(); ++it) { it != pParser.mControllerLibrary.end(); ++it) {
const Collada::Controller &c = it->second; const Controller &c = it->second;
const Collada::Mesh *baseMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, c.mMeshId); const Collada::Mesh *baseMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, c.mMeshId);
if (c.mType == Collada::Morph && baseMesh->mName == pSrcMesh->mName) { if (c.mType == Collada::Morph && baseMesh->mName == pSrcMesh->mName) {
@ -705,8 +711,8 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::M
throw DeadlyImportError("target weight data must not be textual "); throw DeadlyImportError("target weight data must not be textual ");
} }
for (unsigned int i = 0; i < targetData.mStrings.size(); ++i) { for (const auto & mString : targetData.mStrings) {
const Collada::Mesh *targetMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, targetData.mStrings.at(i)); const Mesh *targetMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, mString);
aiMesh *aimesh = findMesh(useColladaName ? targetMesh->mName : targetMesh->mId); aiMesh *aimesh = findMesh(useColladaName ? targetMesh->mName : targetMesh->mId);
if (!aimesh) { if (!aimesh) {
@ -718,12 +724,12 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::M
} }
targetMeshes.push_back(aimesh); targetMeshes.push_back(aimesh);
} }
for (unsigned int i = 0; i < weightData.mValues.size(); ++i) { for (float mValue : weightData.mValues) {
targetWeights.push_back(weightData.mValues.at(i)); targetWeights.push_back(mValue);
} }
} }
} }
if (targetMeshes.size() > 0 && targetWeights.size() == targetMeshes.size()) { if (!targetMeshes.empty() && targetWeights.size() == targetMeshes.size()) {
std::vector<aiAnimMesh *> animMeshes; std::vector<aiAnimMesh *> animMeshes;
for (unsigned int i = 0; i < targetMeshes.size(); ++i) { for (unsigned int i = 0; i < targetMeshes.size(); ++i) {
aiMesh *targetMesh = targetMeshes.at(i); aiMesh *targetMesh = targetMeshes.at(i);
@ -733,7 +739,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::M
animMesh->mName = targetMesh->mName; animMesh->mName = targetMesh->mName;
animMeshes.push_back(animMesh); animMeshes.push_back(animMesh);
} }
dstMesh->mMethod = (method == Collada::Relative) ? aiMorphingMethod_MORPH_RELATIVE : aiMorphingMethod_MORPH_NORMALIZED; dstMesh->mMethod = (method == Relative) ? aiMorphingMethod_MORPH_RELATIVE : aiMorphingMethod_MORPH_NORMALIZED;
dstMesh->mAnimMeshes = new aiAnimMesh *[animMeshes.size()]; dstMesh->mAnimMeshes = new aiAnimMesh *[animMeshes.size()];
dstMesh->mNumAnimMeshes = static_cast<unsigned int>(animMeshes.size()); dstMesh->mNumAnimMeshes = static_cast<unsigned int>(animMeshes.size());
for (unsigned int i = 0; i < animMeshes.size(); ++i) { for (unsigned int i = 0; i < animMeshes.size(); ++i) {
@ -757,18 +763,20 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::M
const Collada::Accessor &weightsAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor); const Collada::Accessor &weightsAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor);
const Collada::Data &weights = pParser.ResolveLibraryReference(pParser.mDataLibrary, weightsAcc.mSource); const Collada::Data &weights = pParser.ResolveLibraryReference(pParser.mDataLibrary, weightsAcc.mSource);
if (!jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) if (!jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) {
throw DeadlyImportError("Data type mismatch while resolving mesh joints"); throw DeadlyImportError("Data type mismatch while resolving mesh joints");
}
// sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex
if (pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) if (pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) {
throw DeadlyImportError("Unsupported vertex_weight addressing scheme. "); throw DeadlyImportError("Unsupported vertex_weight addressing scheme. ");
}
// create containers to collect the weights for each bone // create containers to collect the weights for each bone
size_t numBones = jointNames.mStrings.size(); size_t numBones = jointNames.mStrings.size();
std::vector<std::vector<aiVertexWeight>> dstBones(numBones); std::vector<std::vector<aiVertexWeight>> dstBones(numBones);
// build a temporary array of pointers to the start of each vertex's weights // build a temporary array of pointers to the start of each vertex's weights
typedef std::vector<std::pair<size_t, size_t>> IndexPairVector; using IndexPairVector = std::vector<std::pair<size_t, size_t>>;
std::vector<IndexPairVector::const_iterator> weightStartPerVertex; std::vector<IndexPairVector::const_iterator> weightStartPerVertex;
weightStartPerVertex.resize(pSrcController->mWeightCounts.size(), pSrcController->mWeights.end()); weightStartPerVertex.resize(pSrcController->mWeightCounts.size(), pSrcController->mWeights.end());
@ -807,8 +815,8 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::M
// count the number of bones which influence vertices of the current submesh // count the number of bones which influence vertices of the current submesh
size_t numRemainingBones = 0; size_t numRemainingBones = 0;
for (std::vector<std::vector<aiVertexWeight>>::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it) { for (const auto & dstBone : dstBones) {
if (it->size() > 0) { if (!dstBone.empty()) {
++numRemainingBones; ++numRemainingBones;
} }
} }
@ -867,12 +875,12 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Collada::M
// and replace the bone's name by the node's name so that the user can use the standard // and replace the bone's name by the node's name so that the user can use the standard
// find-by-name method to associate nodes with bones. // find-by-name method to associate nodes with bones.
const Collada::Node *bnode = FindNode(pParser.mRootNode, bone->mName.data); const Collada::Node *bnode = FindNode(pParser.mRootNode, bone->mName.data);
if (!bnode) { if (nullptr == bnode) {
bnode = FindNodeBySID(pParser.mRootNode, bone->mName.data); bnode = FindNodeBySID(pParser.mRootNode, bone->mName.data);
} }
// assign the name that we would have assigned for the source node // assign the name that we would have assigned for the source node
if (bnode) { if (nullptr != bnode) {
bone->mName.Set(FindNameForNode(bnode)); bone->mName.Set(FindNameForNode(bnode));
} else { } else {
ASSIMP_LOG_WARN_F("ColladaLoader::CreateMesh(): could not find corresponding node for joint \"", bone->mName.data, "\"."); ASSIMP_LOG_WARN_F("ColladaLoader::CreateMesh(): could not find corresponding node for joint \"", bone->mName.data, "\".");
@ -973,8 +981,8 @@ void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParse
std::set<std::string> animTargets; std::set<std::string> animTargets;
animTargets.insert(templateAnim->mChannels[0]->mNodeName.C_Str()); animTargets.insert(templateAnim->mChannels[0]->mNodeName.C_Str());
bool collectedAnimationsHaveDifferentChannels = true; bool collectedAnimationsHaveDifferentChannels = true;
for (size_t b = 0; b < collectedAnimIndices.size(); ++b) { for (unsigned long long collectedAnimIndice : collectedAnimIndices) {
aiAnimation *srcAnimation = mAnims[collectedAnimIndices[b]]; aiAnimation *srcAnimation = mAnims[(int)collectedAnimIndice];
std::string channelName = std::string(srcAnimation->mChannels[0]->mNodeName.C_Str()); std::string channelName = std::string(srcAnimation->mChannels[0]->mNodeName.C_Str());
if (animTargets.find(channelName) == animTargets.end()) { if (animTargets.find(channelName) == animTargets.end()) {
animTargets.insert(channelName); animTargets.insert(channelName);
@ -984,8 +992,9 @@ void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParse
} }
} }
if (!collectedAnimationsHaveDifferentChannels) if (!collectedAnimationsHaveDifferentChannels) {
continue; continue;
}
// if there are other animations which fit the template anim, combine all channels into a single anim // if there are other animations which fit the template anim, combine all channels into a single anim
if (!collectedAnimIndices.empty()) { if (!collectedAnimIndices.empty()) {
@ -1032,16 +1041,18 @@ void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParse
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructs the animations for the given source anim // Constructs the animations for the given source anim
void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParser, const Collada::Animation *pSrcAnim, const std::string &pPrefix) { void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParser, const Animation *pSrcAnim, const std::string &pPrefix) {
std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName; std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName;
// create nested animations, if given // create nested animations, if given
for (std::vector<Collada::Animation *>::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it) for (auto mSubAnim : pSrcAnim->mSubAnims) {
StoreAnimations(pScene, pParser, *it, animName); StoreAnimations(pScene, pParser, mSubAnim, animName);
}
// create animation channels, if any // create animation channels, if any
if (!pSrcAnim->mChannels.empty()) if (!pSrcAnim->mChannels.empty()) {
CreateAnimation(pScene, pParser, pSrcAnim, animName); CreateAnimation(pScene, pParser, pSrcAnim, animName);
}
} }
struct MorphTimeValues { struct MorphTimeValues {
@ -1057,7 +1068,7 @@ void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, floa
MorphTimeValues::key k; MorphTimeValues::key k;
k.mValue = value; k.mValue = value;
k.mWeight = weight; k.mWeight = weight;
if (values.size() == 0 || time < values[0].mTime) { if (values.empty() || time < values[0].mTime) {
MorphTimeValues val; MorphTimeValues val;
val.mTime = time; val.mTime = time;
val.mKeys.push_back(k); val.mKeys.push_back(k);
@ -1083,13 +1094,13 @@ void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, floa
return; return;
} }
} }
// should not get here
} }
float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key, unsigned int value) { static float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key, unsigned int value) {
for (unsigned int i = 0; i < values[key].mKeys.size(); i++) { for (auto mKey : values[key].mKeys) {
if (values[key].mKeys[i].mValue == value) if (mKey.mValue == value) {
return values[key].mKeys[i].mWeight; return mKey.mWeight;
}
} }
// no value at key found, try to interpolate if present at other keys. if not, return zero // no value at key found, try to interpolate if present at other keys. if not, return zero
// TODO: interpolation // TODO: interpolation
@ -1098,7 +1109,7 @@ float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key, unsign
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructs the animation for the given source anim // Constructs the animation for the given source anim
void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParser, const Collada::Animation *pSrcAnim, const std::string &pName) { void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParser, const Animation *pSrcAnim, const std::string &pName) {
// collect a list of animatable nodes // collect a list of animatable nodes
std::vector<const aiNode *> nodes; std::vector<const aiNode *> nodes;
CollectNodes(pScene->mRootNode, nodes); CollectNodes(pScene->mRootNode, nodes);
@ -1106,23 +1117,23 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
std::vector<aiNodeAnim *> anims; std::vector<aiNodeAnim *> anims;
std::vector<aiMeshMorphAnim *> morphAnims; std::vector<aiMeshMorphAnim *> morphAnims;
for (std::vector<const aiNode *>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit) { for (auto node : nodes) {
// find all the collada anim channels which refer to the current node // find all the collada anim channels which refer to the current node
std::vector<Collada::ChannelEntry> entries; std::vector<ChannelEntry> entries;
std::string nodeName = (*nit)->mName.data; std::string nodeName = node->mName.data;
// find the collada node corresponding to the aiNode // find the collada node corresponding to the aiNode
const Collada::Node *srcNode = FindNode(pParser.mRootNode, nodeName); const Node *srcNode = FindNode(pParser.mRootNode, nodeName);
if (!srcNode) { if (!srcNode) {
continue; continue;
} }
// now check all channels if they affect the current node // now check all channels if they affect the current node
std::string targetID, subElement; std::string targetID, subElement;
for (std::vector<Collada::AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin(); for (std::vector<AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
cit != pSrcAnim->mChannels.end(); ++cit) { cit != pSrcAnim->mChannels.end(); ++cit) {
const Collada::AnimationChannel &srcChannel = *cit; const AnimationChannel &srcChannel = *cit;
Collada::ChannelEntry entry; ChannelEntry entry;
// we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others
// find the slash that separates the node name - there should be only one // find the slash that separates the node name - there should be only one
@ -1137,24 +1148,28 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
entry.mChannel = &(*cit); entry.mChannel = &(*cit);
entry.mTargetId = srcChannel.mTarget.substr(targetPos + pSrcAnim->mName.length(), entry.mTargetId = srcChannel.mTarget.substr(targetPos + pSrcAnim->mName.length(),
srcChannel.mTarget.length() - targetPos - pSrcAnim->mName.length()); srcChannel.mTarget.length() - targetPos - pSrcAnim->mName.length());
if (entry.mTargetId.front() == '-') if (entry.mTargetId.front() == '-') {
entry.mTargetId = entry.mTargetId.substr(1); entry.mTargetId = entry.mTargetId.substr(1);
}
entries.push_back(entry); entries.push_back(entry);
continue; continue;
} }
if (srcChannel.mTarget.find('/', slashPos + 1) != std::string::npos) if (srcChannel.mTarget.find('/', slashPos + 1) != std::string::npos) {
continue; continue;
}
targetID.clear(); targetID.clear();
targetID = srcChannel.mTarget.substr(0, slashPos); targetID = srcChannel.mTarget.substr(0, slashPos);
if (targetID != srcNode->mID) if (targetID != srcNode->mID) {
continue; continue;
}
// find the dot that separates the transformID - there should be only one or zero // find the dot that separates the transformID - there should be only one or zero
std::string::size_type dotPos = srcChannel.mTarget.find('.'); std::string::size_type dotPos = srcChannel.mTarget.find('.');
if (dotPos != std::string::npos) { if (dotPos != std::string::npos) {
if (srcChannel.mTarget.find('.', dotPos + 1) != std::string::npos) if (srcChannel.mTarget.find('.', dotPos + 1) != std::string::npos) {
continue; continue;
}
entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, dotPos - slashPos - 1); entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, dotPos - slashPos - 1);
@ -1171,7 +1186,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
else else
ASSIMP_LOG_WARN_F("Unknown anim subelement <", subElement, ">. Ignoring"); ASSIMP_LOG_WARN_F("Unknown anim subelement <", subElement, ">. Ignoring");
} else { } else {
// no subelement following, transformId is remaining string // no sub-element following, transformId is remaining string
entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1); entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1);
} }
@ -1222,11 +1237,11 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
entry.mTransformIndex = a; entry.mTransformIndex = a;
if (entry.mTransformIndex == SIZE_MAX) { if (entry.mTransformIndex == SIZE_MAX) {
if (entry.mTransformId.find("morph-weights") != std::string::npos) { if (entry.mTransformId.find("morph-weights") == std::string::npos) {
continue;
}
entry.mTargetId = entry.mTransformId; entry.mTargetId = entry.mTransformId;
entry.mTransformId = ""; entry.mTransformId = "";
} else
continue;
} }
entry.mChannel = &(*cit); entry.mChannel = &(*cit);
@ -1234,21 +1249,22 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
} }
// if there's no channel affecting the current node, we skip it // if there's no channel affecting the current node, we skip it
if (entries.empty()) if (entries.empty()) {
continue; continue;
}
// resolve the data pointers for all anim channels. Find the minimum time while we're at it // resolve the data pointers for all anim channels. Find the minimum time while we're at it
ai_real startTime = ai_real(1e20), endTime = ai_real(-1e20); ai_real startTime = ai_real(1e20), endTime = ai_real(-1e20);
for (std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) { for (ChannelEntry & e : entries) {
Collada::ChannelEntry &e = *it;
e.mTimeAccessor = &pParser.ResolveLibraryReference(pParser.mAccessorLibrary, e.mChannel->mSourceTimes); e.mTimeAccessor = &pParser.ResolveLibraryReference(pParser.mAccessorLibrary, e.mChannel->mSourceTimes);
e.mTimeData = &pParser.ResolveLibraryReference(pParser.mDataLibrary, e.mTimeAccessor->mSource); e.mTimeData = &pParser.ResolveLibraryReference(pParser.mDataLibrary, e.mTimeAccessor->mSource);
e.mValueAccessor = &pParser.ResolveLibraryReference(pParser.mAccessorLibrary, e.mChannel->mSourceValues); e.mValueAccessor = &pParser.ResolveLibraryReference(pParser.mAccessorLibrary, e.mChannel->mSourceValues);
e.mValueData = &pParser.ResolveLibraryReference(pParser.mDataLibrary, e.mValueAccessor->mSource); e.mValueData = &pParser.ResolveLibraryReference(pParser.mDataLibrary, e.mValueAccessor->mSource);
// time count and value count must match // time count and value count must match
if (e.mTimeAccessor->mCount != e.mValueAccessor->mCount) if (e.mTimeAccessor->mCount != e.mValueAccessor->mCount) {
throw DeadlyImportError("Time count / value count mismatch in animation channel \"", e.mChannel->mTarget, "\"."); throw DeadlyImportError("Time count / value count mismatch in animation channel \"", e.mChannel->mTarget, "\".");
}
if (e.mTimeAccessor->mCount > 0) { if (e.mTimeAccessor->mCount > 0) {
// find bounding times // find bounding times
@ -1266,18 +1282,18 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
// and apply them to the transform chain. Then the node's present transformation can be calculated. // and apply them to the transform chain. Then the node's present transformation can be calculated.
ai_real time = startTime; ai_real time = startTime;
while (1) { while (1) {
for (std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) { for (ChannelEntry & e : entries) {
Collada::ChannelEntry &e = *it;
// find the keyframe behind the current point in time // find the keyframe behind the current point in time
size_t pos = 0; size_t pos = 0;
ai_real postTime = 0.0; ai_real postTime = 0.0;
while (1) { while (1) {
if (pos >= e.mTimeAccessor->mCount) if (pos >= e.mTimeAccessor->mCount) {
break; break;
}
postTime = ReadFloat(*e.mTimeAccessor, *e.mTimeData, pos, 0); postTime = ReadFloat(*e.mTimeAccessor, *e.mTimeData, pos, 0);
if (postTime >= time) if (postTime >= time) {
break; break;
}
++pos; ++pos;
} }
@ -1285,8 +1301,9 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
// read values from there // read values from there
ai_real temp[16]; ai_real temp[16];
for (size_t c = 0; c < e.mValueAccessor->mSize; ++c) for (size_t c = 0; c < e.mValueAccessor->mSize; ++c) {
temp[c] = ReadFloat(*e.mValueAccessor, *e.mValueData, pos, c); temp[c] = ReadFloat(*e.mValueAccessor, *e.mValueData, pos, c);
}
// if not exactly at the key time, interpolate with previous value set // if not exactly at the key time, interpolate with previous value set
if (postTime > time && pos > 0) { if (postTime > time && pos > 0) {
@ -1312,9 +1329,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
// find next point in time to evaluate. That's the closest frame larger than the current in any channel // find next point in time to evaluate. That's the closest frame larger than the current in any channel
ai_real nextTime = ai_real(1e20); ai_real nextTime = ai_real(1e20);
for (std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) { for (ChannelEntry & channelElement : entries) {
Collada::ChannelEntry &channelElement = *it;
// find the next time value larger than the current // find the next time value larger than the current
size_t pos = 0; size_t pos = 0;
while (pos < channelElement.mTimeAccessor->mCount) { while (pos < channelElement.mTimeAccessor->mCount) {
@ -1329,7 +1344,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
// https://github.com/assimp/assimp/issues/458 // https://github.com/assimp/assimp/issues/458
// Sub-sample axis-angle channels if the delta between two consecutive // Sub-sample axis-angle channels if the delta between two consecutive
// key-frame angles is >= 180 degrees. // key-frame angles is >= 180 degrees.
if (transforms[channelElement.mTransformIndex].mType == Collada::TF_ROTATE && channelElement.mSubElement == 3 && pos > 0 && pos < channelElement.mTimeAccessor->mCount) { if (transforms[channelElement.mTransformIndex].mType == TF_ROTATE && channelElement.mSubElement == 3 && pos > 0 && pos < channelElement.mTimeAccessor->mCount) {
const ai_real cur_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos, 0); const ai_real cur_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos, 0);
const ai_real last_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos - 1, 0); const ai_real last_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos - 1, 0);
const ai_real cur_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0); const ai_real cur_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0);
@ -1347,17 +1362,15 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
} }
// no more keys on any channel after the current time -> we're done // no more keys on any channel after the current time -> we're done
if (nextTime > 1e19) if (nextTime > 1e19) {
break; break;
}
// else construct next keyframe at this following time point // else construct next key-frame at this following time point
time = nextTime; time = nextTime;
} }
} }
// there should be some keyframes, but we aren't that fixated on valid input data
// ai_assert( resultTrafos.size() > 0);
// build an animation channel for the given node out of these trafo keys // build an animation channel for the given node out of these trafo keys
if (!resultTrafos.empty()) { if (!resultTrafos.empty()) {
aiNodeAnim *dstAnim = new aiNodeAnim; aiNodeAnim *dstAnim = new aiNodeAnim;
@ -1386,17 +1399,17 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
} }
if (!entries.empty() && entries.front().mTimeAccessor->mCount > 0) { if (!entries.empty() && entries.front().mTimeAccessor->mCount > 0) {
std::vector<Collada::ChannelEntry> morphChannels; std::vector<ChannelEntry> morphChannels;
for (std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) { for (ChannelEntry & e : entries) {
Collada::ChannelEntry &e = *it;
// skip non-transform types // skip non-transform types
if (e.mTargetId.empty()) if (e.mTargetId.empty()) {
continue; continue;
}
if (e.mTargetId.find("morph-weights") != std::string::npos) if (e.mTargetId.find("morph-weights") != std::string::npos) {
morphChannels.push_back(e); morphChannels.push_back(e);
} }
}
if (!morphChannels.empty()) { if (!morphChannels.empty()) {
// either 1) morph weight animation count should contain morph target count channels // either 1) morph weight animation count should contain morph target count channels
// or 2) one channel with morph target count arrays // or 2) one channel with morph target count arrays
@ -1407,13 +1420,14 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
std::vector<MorphTimeValues> morphTimeValues; std::vector<MorphTimeValues> morphTimeValues;
int morphAnimChannelIndex = 0; int morphAnimChannelIndex = 0;
for (std::vector<Collada::ChannelEntry>::iterator it = morphChannels.begin(); it != morphChannels.end(); ++it) { for (ChannelEntry & e : morphChannels) {
Collada::ChannelEntry &e = *it;
std::string::size_type apos = e.mTargetId.find('('); std::string::size_type apos = e.mTargetId.find('(');
std::string::size_type bpos = e.mTargetId.find(')'); std::string::size_type bpos = e.mTargetId.find(')');
if (apos == std::string::npos || bpos == std::string::npos)
// unknown way to specify weight -> ignore this animation // If unknown way to specify weight -> ignore this animation
if (apos == std::string::npos || bpos == std::string::npos) {
continue; continue;
}
// weight target can be in format Weight_M_N, Weight_N, WeightN, or some other way // weight target can be in format Weight_M_N, Weight_N, WeightN, or some other way
// we ignore the name and just assume the channels are in the right order // we ignore the name and just assume the channels are in the right order
@ -1457,13 +1471,13 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
std::copy(morphAnims.begin(), morphAnims.end(), anim->mMorphMeshChannels); std::copy(morphAnims.begin(), morphAnims.end(), anim->mMorphMeshChannels);
} }
anim->mDuration = 0.0f; anim->mDuration = 0.0f;
for (size_t a = 0; a < anims.size(); ++a) { for (auto & a : anims) {
anim->mDuration = std::max(anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys - 1].mTime); anim->mDuration = std::max(anim->mDuration, a->mPositionKeys[a->mNumPositionKeys - 1].mTime);
anim->mDuration = std::max(anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys - 1].mTime); anim->mDuration = std::max(anim->mDuration, a->mRotationKeys[a->mNumRotationKeys - 1].mTime);
anim->mDuration = std::max(anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys - 1].mTime); anim->mDuration = std::max(anim->mDuration, a->mScalingKeys[a->mNumScalingKeys - 1].mTime);
} }
for (size_t a = 0; a < morphAnims.size(); ++a) { for (auto & morphAnim : morphAnims) {
anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys - 1].mTime); anim->mDuration = std::max(anim->mDuration, morphAnim->mKeys[morphAnim->mNumKeys - 1].mTime);
} }
anim->mTicksPerSecond = 1000.0; anim->mTicksPerSecond = 1000.0;
mAnims.push_back(anim); mAnims.push_back(anim);
@ -1472,10 +1486,12 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Add a texture to a material structure // Add a texture to a material structure
void ColladaLoader::AddTexture(aiMaterial &mat, const ColladaParser &pParser, void ColladaLoader::AddTexture(aiMaterial &mat,
const Collada::Effect &effect, const ColladaParser &pParser,
const Collada::Sampler &sampler, const Effect &effect,
aiTextureType type, unsigned int idx) { const Sampler &sampler,
aiTextureType type,
unsigned int idx) {
// first of all, basic file name // first of all, basic file name
const aiString name = FindFilenameForEffectTexture(pParser, effect, sampler.mName); const aiString name = FindFilenameForEffectTexture(pParser, effect, sampler.mName);
mat.AddProperty(&name, _AI_MATKEY_TEXTURE_BASE, type, idx); mat.AddProperty(&name, _AI_MATKEY_TEXTURE_BASE, type, idx);
@ -1574,7 +1590,7 @@ void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pSce
shadeMode = effect.mDoubleSided; shadeMode = effect.mDoubleSided;
mat.AddProperty<int>(&shadeMode, 1, AI_MATKEY_TWOSIDED); mat.AddProperty<int>(&shadeMode, 1, AI_MATKEY_TWOSIDED);
// wireframe? // wire-frame?
shadeMode = effect.mWireframe; shadeMode = effect.mWireframe;
mat.AddProperty<int>(&shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME); mat.AddProperty<int>(&shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME);
@ -1652,12 +1668,12 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/)
for (ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); for (ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin();
matIt != pParser.mMaterialLibrary.end(); ++matIt) { matIt != pParser.mMaterialLibrary.end(); ++matIt) {
const Collada::Material &material = matIt->second; const Material &material = matIt->second;
// a material is only a reference to an effect // a material is only a reference to an effect
ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect); ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect);
if (effIt == pParser.mEffectLibrary.end()) if (effIt == pParser.mEffectLibrary.end())
continue; continue;
Collada::Effect &effect = effIt->second; Effect &effect = effIt->second;
// create material // create material
aiMaterial *mat = new aiMaterial; aiMaterial *mat = new aiMaterial;
@ -1666,7 +1682,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/)
// store the material // store the material
mMaterialIndexByName[matIt->first] = newMats.size(); mMaterialIndexByName[matIt->first] = newMats.size();
newMats.push_back(std::pair<Collada::Effect *, aiMaterial *>(&effect, mat)); newMats.push_back(std::pair<Effect *, aiMaterial *>(&effect, mat));
} }
// ScenePreprocessor generates a default material automatically if none is there. // ScenePreprocessor generates a default material automatically if none is there.
// All further code here in this loader works well without a valid material so // All further code here in this loader works well without a valid material so
@ -1674,17 +1690,16 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Resolves the texture name for the given effect texture entry // Resolves the texture name for the given effect texture entry and loads the texture data
// and loads the texture data
aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParser, aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParser,
const Collada::Effect &pEffect, const std::string &pName) { const Effect &pEffect, const std::string &pName) {
aiString result; aiString result;
// recurse through the param references until we end up at an image // recurse through the param references until we end up at an image
std::string name = pName; std::string name = pName;
while (1) { while (1) {
// the given string is a param entry. Find it // the given string is a param entry. Find it
Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find(name); Effect::ParamLibrary::const_iterator it = pEffect.mParams.find(name);
// if not found, we're at the end of the recursion. The resulting string should be the image ID // if not found, we're at the end of the recursion. The resulting string should be the image ID
if (it == pEffect.mParams.end()) if (it == pEffect.mParams.end())
break; break;
@ -1712,10 +1727,6 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
tex->mFilename.Set(imIt->second.mFileName.c_str()); tex->mFilename.Set(imIt->second.mFileName.c_str());
result.Set(imIt->second.mFileName); result.Set(imIt->second.mFileName);
// TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
// result.data[0] = '*';
// result.length = 1 + ASSIMP_itoa10(result.data + 1, static_cast<unsigned int>(MAXLEN - 1), static_cast<int32_t>(mTextures.size()));
// setup format hint // setup format hint
if (imIt->second.mEmbeddedFormat.length() >= HINTMAXTEXTURELEN) { if (imIt->second.mEmbeddedFormat.length() >= HINTMAXTEXTURELEN) {
ASSIMP_LOG_WARN("Collada: texture format hint is too long, truncating to 3 characters"); ASSIMP_LOG_WARN("Collada: texture format hint is too long, truncating to 3 characters");
@ -1744,7 +1755,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads a float value from an accessor and its data array. // Reads a float value from an accessor and its data array.
ai_real ColladaLoader::ReadFloat(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex, size_t pOffset) const { ai_real ColladaLoader::ReadFloat(const Accessor &pAccessor, const Data &pData, size_t pIndex, size_t pOffset) const {
size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset; size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
ai_assert(pos < pData.mValues.size()); ai_assert(pos < pData.mValues.size());
return pData.mValues[pos]; return pData.mValues[pos];
@ -1752,7 +1763,7 @@ ai_real ColladaLoader::ReadFloat(const Collada::Accessor &pAccessor, const Colla
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads a string value from an accessor and its data array. // Reads a string value from an accessor and its data array.
const std::string &ColladaLoader::ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const { const std::string &ColladaLoader::ReadString(const Accessor &pAccessor, const Data &pData, size_t pIndex) const {
size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset; size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset;
ai_assert(pos < pData.mStrings.size()); ai_assert(pos < pData.mStrings.size());
return pData.mStrings[pos]; return pData.mStrings[pos];
@ -1769,12 +1780,12 @@ void ColladaLoader::CollectNodes(const aiNode *pNode, std::vector<const aiNode *
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Finds a node in the collada scene by the given name // Finds a node in the collada scene by the given name
const Collada::Node *ColladaLoader::FindNode(const Collada::Node *pNode, const std::string &pName) const { const Node *ColladaLoader::FindNode(const Node *pNode, const std::string &pName) const {
if (pNode->mName == pName || pNode->mID == pName) if (pNode->mName == pName || pNode->mID == pName)
return pNode; return pNode;
for (size_t a = 0; a < pNode->mChildren.size(); ++a) { for (auto a : pNode->mChildren) {
const Collada::Node *node = FindNode(pNode->mChildren[a], pName); const Collada::Node *node = FindNode(a, pName);
if (node) { if (node) {
return node; return node;
} }
@ -1785,7 +1796,7 @@ const Collada::Node *ColladaLoader::FindNode(const Collada::Node *pNode, const s
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Finds a node in the collada scene by the given SID // Finds a node in the collada scene by the given SID
const Collada::Node *ColladaLoader::FindNodeBySID(const Collada::Node *pNode, const std::string &pSID) const { const Node *ColladaLoader::FindNodeBySID(const Node *pNode, const std::string &pSID) const {
if (nullptr == pNode) { if (nullptr == pNode) {
return nullptr; return nullptr;
} }
@ -1794,8 +1805,8 @@ const Collada::Node *ColladaLoader::FindNodeBySID(const Collada::Node *pNode, co
return pNode; return pNode;
} }
for (size_t a = 0; a < pNode->mChildren.size(); ++a) { for (auto a : pNode->mChildren) {
const Collada::Node *node = FindNodeBySID(pNode->mChildren[a], pSID); const Collada::Node *node = FindNodeBySID(a, pSID);
if (node) { if (node) {
return node; return node;
} }
@ -1807,7 +1818,7 @@ const Collada::Node *ColladaLoader::FindNodeBySID(const Collada::Node *pNode, co
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Finds a proper unique name for a node derived from the collada-node's properties. // Finds a proper unique name for a node derived from the collada-node's properties.
// The name must be unique for proper node-bone association. // The name must be unique for proper node-bone association.
std::string ColladaLoader::FindNameForNode(const Collada::Node *pNode) { std::string ColladaLoader::FindNameForNode(const Node *pNode) {
// If explicitly requested, just use the collada name. // If explicitly requested, just use the collada name.
if (useColladaName) { if (useColladaName) {
if (!pNode->mName.empty()) { if (!pNode->mName.empty()) {

View File

@ -4,7 +4,7 @@
Open Asset Import Library (assimp) Open Asset Import Library (assimp)
---------------------------------------------------------------------- ----------------------------------------------------------------------
Copyright (c) 2006-2020, assimp team Copyright (c) 2006-2021, assimp team
All rights reserved. All rights reserved.
@ -45,8 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_COLLADALOADER_H_INC #ifndef AI_COLLADALOADER_H_INC
#define AI_COLLADALOADER_H_INC #define AI_COLLADALOADER_H_INC
#include <assimp/BaseImporter.h>
#include "ColladaParser.h" #include "ColladaParser.h"
#include <assimp/BaseImporter.h>
struct aiNode; struct aiNode;
struct aiCamera; struct aiCamera;
@ -54,28 +54,24 @@ struct aiLight;
struct aiTexture; struct aiTexture;
struct aiAnimation; struct aiAnimation;
namespace Assimp namespace Assimp {
{
struct ColladaMeshIndex struct ColladaMeshIndex {
{
std::string mMeshID; std::string mMeshID;
size_t mSubMesh; size_t mSubMesh;
std::string mMaterial; std::string mMaterial;
ColladaMeshIndex( const std::string& pMeshID, size_t pSubMesh, const std::string& pMaterial) ColladaMeshIndex(const std::string &pMeshID, size_t pSubMesh, const std::string &pMaterial) :
: mMeshID( pMeshID), mSubMesh( pSubMesh), mMaterial( pMaterial) mMeshID(pMeshID), mSubMesh(pSubMesh), mMaterial(pMaterial) {
{ } ai_assert(!pMeshID.empty());
}
bool operator < (const ColladaMeshIndex& p) const bool operator<(const ColladaMeshIndex &p) const {
{ if (mMeshID == p.mMeshID) {
if( mMeshID == p.mMeshID) if (mSubMesh == p.mSubMesh)
{
if( mSubMesh == p.mSubMesh)
return mMaterial < p.mMaterial; return mMaterial < p.mMaterial;
else else
return mSubMesh < p.mSubMesh; return mSubMesh < p.mSubMesh;
} else } else {
{
return mMeshID < p.mMeshID; return mMeshID < p.mMeshID;
} }
} }
@ -84,105 +80,102 @@ struct ColladaMeshIndex
/** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing /** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing
* more useless stuff, so I limited the data to what I think is useful for games. * more useless stuff, so I limited the data to what I think is useful for games.
*/ */
class ColladaLoader : public BaseImporter class ColladaLoader : public BaseImporter {
{
public: public:
/// The class constructor.
ColladaLoader(); ColladaLoader();
~ColladaLoader();
/// The class destructor.
~ColladaLoader() override;
public: /// Returns whether the class can handle the format of the given file.
/** Returns whether the class can handle the format of the given file. /// @see BaseImporter::CanRead() for more details.
* See BaseImporter::CanRead() for details. */ bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
protected: protected:
/** Return importer meta information. /// See #BaseImporter::GetInfo for the details
* See #BaseImporter::GetInfo for the details const aiImporterDesc *GetInfo() const override;
*/
const aiImporterDesc* GetInfo () const override;
void SetupProperties(const Importer* pImp) override; /// See #BaseImporter::SetupProperties for the details
void SetupProperties(const Importer *pImp) override;
/** Imports the given file into the given scene structure. /// See #BaseImporter::InternReadFile for the details
* See BaseImporter::InternReadFile() for details void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
*/
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
/** Recursively constructs a scene node for the given parser node and returns it. */ /** Recursively constructs a scene node for the given parser node and returns it. */
aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode); aiNode *BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode);
/** Resolve node instances */ /** Resolve node instances */
void ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode, void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode,
std::vector<const Collada::Node*>& resolved); std::vector<const Collada::Node *> &resolved);
/** Builds meshes for the given node and references them */ /** Builds meshes for the given node and references them */
void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, void BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode,
aiNode* pTarget); aiNode *pTarget);
aiMesh *findMesh(const std::string& meshid); aiMesh *findMesh(const std::string &meshid);
/** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */ /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, aiMesh *CreateMesh(const ColladaParser &pParser, const Collada::Mesh *pSrcMesh, const Collada::SubMesh &pSubMesh,
const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace); const Collada::Controller *pSrcController, size_t pStartVertex, size_t pStartFace);
/** Builds cameras for the given node and references them */ /** Builds cameras for the given node and references them */
void BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, void BuildCamerasForNode(const ColladaParser &pParser, const Collada::Node *pNode,
aiNode* pTarget); aiNode *pTarget);
/** Builds lights for the given node and references them */ /** Builds lights for the given node and references them */
void BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, void BuildLightsForNode(const ColladaParser &pParser, const Collada::Node *pNode,
aiNode* pTarget); aiNode *pTarget);
/** Stores all meshes in the given scene */ /** Stores all meshes in the given scene */
void StoreSceneMeshes( aiScene* pScene); void StoreSceneMeshes(aiScene *pScene);
/** Stores all materials in the given scene */ /** Stores all materials in the given scene */
void StoreSceneMaterials( aiScene* pScene); void StoreSceneMaterials(aiScene *pScene);
/** Stores all lights in the given scene */ /** Stores all lights in the given scene */
void StoreSceneLights( aiScene* pScene); void StoreSceneLights(aiScene *pScene);
/** Stores all cameras in the given scene */ /** Stores all cameras in the given scene */
void StoreSceneCameras( aiScene* pScene); void StoreSceneCameras(aiScene *pScene);
/** Stores all textures in the given scene */ /** Stores all textures in the given scene */
void StoreSceneTextures( aiScene* pScene); void StoreSceneTextures(aiScene *pScene);
/** Stores all animations /** Stores all animations
* @param pScene target scene to store the anims * @param pScene target scene to store the anims
*/ */
void StoreAnimations( aiScene* pScene, const ColladaParser& pParser); void StoreAnimations(aiScene *pScene, const ColladaParser &pParser);
/** Stores all animations for the given source anim and its nested child animations /** Stores all animations for the given source anim and its nested child animations
* @param pScene target scene to store the anims * @param pScene target scene to store the anims
* @param pSrcAnim the source animation to process * @param pSrcAnim the source animation to process
* @param pPrefix Prefix to the name in case of nested animations * @param pPrefix Prefix to the name in case of nested animations
*/ */
void StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pPrefix); void StoreAnimations(aiScene *pScene, const ColladaParser &pParser, const Collada::Animation *pSrcAnim, const std::string &pPrefix);
/** Constructs the animation for the given source anim */ /** Constructs the animation for the given source anim */
void CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName); void CreateAnimation(aiScene *pScene, const ColladaParser &pParser, const Collada::Animation *pSrcAnim, const std::string &pName);
/** Constructs materials from the collada material definitions */ /** Constructs materials from the collada material definitions */
void BuildMaterials( ColladaParser& pParser, aiScene* pScene); void BuildMaterials(ColladaParser &pParser, aiScene *pScene);
/** Fill materials from the collada material definitions */ /** Fill materials from the collada material definitions */
void FillMaterials( const ColladaParser& pParser, aiScene* pScene); void FillMaterials(const ColladaParser &pParser, aiScene *pScene);
/** Resolve UV channel mappings*/ /** Resolve UV channel mappings*/
void ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler, void ApplyVertexToEffectSemanticMapping(Collada::Sampler &sampler,
const Collada::SemanticMappingTable& table); const Collada::SemanticMappingTable &table);
/** Add a texture and all of its sampling properties to a material*/ /** Add a texture and all of its sampling properties to a material*/
void AddTexture ( aiMaterial& mat, const ColladaParser& pParser, void AddTexture(aiMaterial &mat, const ColladaParser &pParser,
const Collada::Effect& effect, const Collada::Effect &effect,
const Collada::Sampler& sampler, const Collada::Sampler &sampler,
aiTextureType type, unsigned int idx = 0); aiTextureType type, unsigned int idx = 0);
/** Resolves the texture name for the given effect texture entry */ /** Resolves the texture name for the given effect texture entry */
aiString FindFilenameForEffectTexture( const ColladaParser& pParser, aiString FindFilenameForEffectTexture(const ColladaParser &pParser,
const Collada::Effect& pEffect, const std::string& pName); const Collada::Effect &pEffect, const std::string &pName);
/** Reads a float value from an accessor and its data array. /** Reads a float value from an accessor and its data array.
* @param pAccessor The accessor to use for reading * @param pAccessor The accessor to use for reading
@ -191,7 +184,7 @@ protected:
* @param pOffset Offset into the element, for multipart elements such as vectors or matrices * @param pOffset Offset into the element, for multipart elements such as vectors or matrices
* @return the specified value * @return the specified value
*/ */
ai_real ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const; ai_real ReadFloat(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex, size_t pOffset) const;
/** Reads a string value from an accessor and its data array. /** Reads a string value from an accessor and its data array.
* @param pAccessor The accessor to use for reading * @param pAccessor The accessor to use for reading
@ -199,18 +192,18 @@ protected:
* @param pIndex The index of the element to retrieve * @param pIndex The index of the element to retrieve
* @return the specified value * @return the specified value
*/ */
const std::string& ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const; const std::string &ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const;
/** Recursively collects all nodes into the given array */ /** Recursively collects all nodes into the given array */
void CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const; void CollectNodes(const aiNode *pNode, std::vector<const aiNode *> &poNodes) const;
/** Finds a node in the collada scene by the given name */ /** Finds a node in the collada scene by the given name */
const Collada::Node* FindNode( const Collada::Node* pNode, const std::string& pName) const; const Collada::Node *FindNode(const Collada::Node *pNode, const std::string &pName) const;
/** Finds a node in the collada scene by the given SID */ /** Finds a node in the collada scene by the given SID */
const Collada::Node* FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const; const Collada::Node *FindNodeBySID(const Collada::Node *pNode, const std::string &pSID) const;
/** Finds a proper name for a node derived from the collada-node's properties */ /** Finds a proper name for a node derived from the collada-node's properties */
std::string FindNameForNode( const Collada::Node* pNode); std::string FindNameForNode(const Collada::Node *pNode);
protected: protected:
/** Filename, for a verbose error message */ /** Filename, for a verbose error message */
@ -223,25 +216,25 @@ protected:
std::map<std::string, size_t> mMaterialIndexByName; std::map<std::string, size_t> mMaterialIndexByName;
/** Accumulated meshes for the target scene */ /** Accumulated meshes for the target scene */
std::vector<aiMesh*> mMeshes; std::vector<aiMesh *> mMeshes;
/** Accumulated morph target meshes */ /** Accumulated morph target meshes */
std::vector<aiMesh*> mTargetMeshes; std::vector<aiMesh *> mTargetMeshes;
/** Temporary material list */ /** Temporary material list */
std::vector<std::pair<Collada::Effect*, aiMaterial*> > newMats; std::vector<std::pair<Collada::Effect *, aiMaterial *>> newMats;
/** Temporary camera list */ /** Temporary camera list */
std::vector<aiCamera*> mCameras; std::vector<aiCamera *> mCameras;
/** Temporary light list */ /** Temporary light list */
std::vector<aiLight*> mLights; std::vector<aiLight *> mLights;
/** Temporary texture list */ /** Temporary texture list */
std::vector<aiTexture*> mTextures; std::vector<aiTexture *> mTextures;
/** Accumulated animations for the target scene */ /** Accumulated animations for the target scene */
std::vector<aiAnimation*> mAnims; std::vector<aiAnimation *> mAnims;
bool noSkeletonMesh; bool noSkeletonMesh;
bool ignoreUpDirection; bool ignoreUpDirection;

View File

@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/light.h> #include <assimp/light.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/IOSystem.hpp> #include <assimp/IOSystem.hpp>
#include <memory>
using namespace Assimp; using namespace Assimp;
using namespace Assimp::Collada; using namespace Assimp::Collada;
@ -158,9 +159,9 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
if (colladaNode.empty()) { if (colladaNode.empty()) {
return; return;
} }
ReadContents(colladaNode);
// read embedded textures // Read content and embedded textures
ReadContents(colladaNode);
if (zip_archive && zip_archive->isOpen()) { if (zip_archive && zip_archive->isOpen()) {
ReadEmbeddedTextures(*zip_archive); ReadEmbeddedTextures(*zip_archive);
} }
@ -169,11 +170,11 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
ColladaParser::~ColladaParser() { ColladaParser::~ColladaParser() {
for (NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it) { for (auto & it : mNodeLibrary) {
delete it->second; delete it.second;
} }
for (MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it) { for (auto & it : mMeshLibrary) {
delete it->second; delete it.second;
} }
} }
@ -289,7 +290,7 @@ void ColladaParser::ReadContents(XmlNode &node) {
// Reads the structure of the file // Reads the structure of the file
void ColladaParser::ReadStructure(XmlNode &node) { void ColladaParser::ReadStructure(XmlNode &node) {
for (XmlNode &currentNode : node.children()) { for (XmlNode &currentNode : node.children()) {
const std::string &currentName = std::string(currentNode.name()); const std::string &currentName = currentNode.name();
if (currentName == "asset") { if (currentName == "asset") {
ReadAssetInfo(currentNode); ReadAssetInfo(currentNode);
} else if (currentName == "library_animations") { } else if (currentName == "library_animations") {
@ -334,7 +335,7 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) {
const std::string &currentName = currentNode.name(); const std::string &currentName = currentNode.name();
if (currentName == "unit") { if (currentName == "unit") {
mUnitSize = 1.f; mUnitSize = 1.f;
XmlParser::getFloatAttribute(node, "meter", mUnitSize); XmlParser::getFloatAttribute(currentNode, "meter", mUnitSize);
} else if (currentName == "up_axis") { } else if (currentName == "up_axis") {
std::string v; std::string v;
if (!XmlParser::getValueAsString(currentNode, v)) { if (!XmlParser::getValueAsString(currentNode, v)) {
@ -407,7 +408,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) {
const std::string &currentName = currentNode.name(); const std::string &currentName = currentNode.name();
if (currentName == "instance_animation") { if (currentName == "instance_animation") {
std::string url; std::string url;
readUrlAttribute(node, url); readUrlAttribute(currentNode, url);
clip.second.push_back(url); clip.second.push_back(url);
} }
@ -419,8 +420,8 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) {
void ColladaParser::PostProcessControllers() { void ColladaParser::PostProcessControllers() {
std::string meshId; std::string meshId;
for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it) { for (auto & it : mControllerLibrary) {
meshId = it->second.mMeshId; meshId = it.second.mMeshId;
if (meshId.empty()) { if (meshId.empty()) {
continue; continue;
} }
@ -431,7 +432,7 @@ void ColladaParser::PostProcessControllers() {
findItr = mControllerLibrary.find(meshId); findItr = mControllerLibrary.find(meshId);
} }
it->second.mMeshId = meshId; it.second.mMeshId = meshId;
} }
} }
@ -444,22 +445,19 @@ void ColladaParser::PostProcessRootAnimations() {
} }
Animation temp; Animation temp;
for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it) { for (auto & it : mAnimationClipLibrary) {
std::string clipName = it->first; std::string clipName = it.first;
Animation *clip = new Animation(); Animation *clip = new Animation();
clip->mName = clipName; clip->mName = clipName;
temp.mSubAnims.push_back(clip); temp.mSubAnims.push_back(clip);
for (std::vector<std::string>::iterator a = it->second.begin(); a != it->second.end(); ++a) { for (std::string animationID : it.second) {
std::string animationID = *a;
AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
if (animation != mAnimationLibrary.end()) { if (animation != mAnimationLibrary.end()) {
Animation *pSourceAnimation = animation->second; Animation *pSourceAnimation = animation->second;
pSourceAnimation->CollectChannelsRecursively(clip->mChannels); pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
} }
} }
@ -495,7 +493,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) {
// an <animation> element may be a container for grouping sub-elements or an animation channel // an <animation> element may be a container for grouping sub-elements or an animation channel
// this is the channel collection by ID, in case it has channels // this is the channel collection by ID, in case it has channels
using ChannelMap = std::map<std::string, AnimationChannel> ; using ChannelMap = std::map<std::string, AnimationChannel>;
ChannelMap channels; ChannelMap channels;
// this is the anim container in case we're a container // this is the anim container in case we're a container
Animation *anim = nullptr; Animation *anim = nullptr;
@ -531,6 +529,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) {
// have it read into a channel // have it read into a channel
ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first; ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first;
ReadAnimationSampler(currentNode, newChannel->second); ReadAnimationSampler(currentNode, newChannel->second);
}
} else if (currentName == "channel") { } else if (currentName == "channel") {
std::string source_name, target; std::string source_name, target;
XmlParser::getStdStrAttribute(currentNode, "source", source_name); XmlParser::getStdStrAttribute(currentNode, "source", source_name);
@ -544,7 +543,6 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) {
} }
} }
} }
}
// it turned out to have channels - add them // it turned out to have channels - add them
if (!channels.empty()) { if (!channels.empty()) {
@ -554,8 +552,8 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) {
pParent->mSubAnims.push_back(anim); pParent->mSubAnims.push_back(anim);
} }
for (ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it) { for (const auto & channel : channels) {
anim->mChannels.push_back(it->second); anim->mChannels.push_back(channel.second);
} }
if (idAttr) { if (idAttr) {
@ -610,50 +608,62 @@ void ColladaParser::ReadControllerLibrary(XmlNode &node) {
if (currentName != "controller") { if (currentName != "controller") {
continue; continue;
} }
std::string id = node.attribute("id").as_string(); std::string id;
if (XmlParser::getStdStrAttribute(currentNode, "id", id)) {
mControllerLibrary[id] = Controller(); mControllerLibrary[id] = Controller();
ReadController(node, mControllerLibrary[id]); ReadController(currentNode, mControllerLibrary[id]);
}
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads a controller into the given mesh structure // Reads a controller into the given mesh structure
void ColladaParser::ReadController(XmlNode &node, Collada::Controller &pController) { void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controller) {
// initial values // initial values
pController.mType = Skin; controller.mType = Skin;
pController.mMethod = Normalized; controller.mMethod = Normalized;
for (XmlNode &currentNode : node.children()) {
XmlNodeIterator xmlIt(node);
xmlIt.collectChildrenPreOrder(node);
XmlNode currentNode;
while (xmlIt.getNext(currentNode)) {
//for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name(); const std::string &currentName = currentNode.name();
if (currentName == "morph") { if (currentName == "morph") {
pController.mType = Morph; controller.mType = Morph;
pController.mMeshId = currentNode.attribute("source").as_string(); controller.mMeshId = currentNode.attribute("source").as_string();
int methodIndex = currentNode.attribute("method").as_int(); int methodIndex = currentNode.attribute("method").as_int();
if (methodIndex > 0) { if (methodIndex > 0) {
std::string method; std::string method;
XmlParser::getValueAsString(currentNode, method); XmlParser::getValueAsString(currentNode, method);
if (method == "RELATIVE") { if (method == "RELATIVE") {
pController.mMethod = Relative; controller.mMethod = Relative;
} }
} }
} else if (currentName == "skin") { } else if (currentName == "skin") {
pController.mMeshId = currentNode.attribute("source").as_string(); std::string id;
if (XmlParser::getStdStrAttribute(currentNode, "source", id)) {
controller.mMeshId = id.substr(1, id.size()-1);
}
} else if (currentName == "bind_shape_matrix") { } else if (currentName == "bind_shape_matrix") {
std::string v; std::string v;
XmlParser::getValueAsString(currentNode, v); XmlParser::getValueAsString(currentNode, v);
const char *content = v.c_str(); const char *content = v.c_str();
for (unsigned int a = 0; a < 16; a++) { for (unsigned int a = 0; a < 16; a++) {
SkipSpacesAndLineEnd(&content);
// read a number // read a number
content = fast_atoreal_move<ai_real>(content, pController.mBindShapeMatrix[a]); content = fast_atoreal_move<ai_real>(content, controller.mBindShapeMatrix[a]);
// skip whitespace after it // skip whitespace after it
SkipSpacesAndLineEnd(&content); SkipSpacesAndLineEnd(&content);
} }
} else if (currentName == "source") { } else if (currentName == "source") {
ReadSource(currentNode); ReadSource(currentNode);
} else if (currentName == "joints") { } else if (currentName == "joints") {
ReadControllerJoints(currentNode, pController); ReadControllerJoints(currentNode, controller);
} else if (currentName == "vertex_weights") { } else if (currentName == "vertex_weights") {
ReadControllerWeights(currentNode, pController); ReadControllerWeights(currentNode, controller);
} else if (currentName == "targets") { } else if (currentName == "targets") {
for (XmlNode currentChildNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { for (XmlNode currentChildNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
const std::string &currentChildName = currentChildNode.name(); const std::string &currentChildName = currentChildNode.name();
@ -661,9 +671,9 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &pControll
const char *semantics = currentChildNode.attribute("semantic").as_string(); const char *semantics = currentChildNode.attribute("semantic").as_string();
const char *source = currentChildNode.attribute("source").as_string(); const char *source = currentChildNode.attribute("source").as_string();
if (strcmp(semantics, "MORPH_TARGET") == 0) { if (strcmp(semantics, "MORPH_TARGET") == 0) {
pController.mMorphTarget = source + 1; controller.mMorphTarget = source + 1;
} else if (strcmp(semantics, "MORPH_WEIGHT") == 0) { } else if (strcmp(semantics, "MORPH_WEIGHT") == 0) {
pController.mMorphWeight = source + 1; controller.mMorphWeight = source + 1;
} }
} }
} }
@ -701,6 +711,7 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
// Read vertex count from attributes and resize the array accordingly // Read vertex count from attributes and resize the array accordingly
int vertexCount=0; int vertexCount=0;
XmlParser::getIntAttribute(node, "count", vertexCount); XmlParser::getIntAttribute(node, "count", vertexCount);
pController.mWeightCounts.resize(vertexCount);
for (XmlNode &currentNode : node.children()) { for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name(); const std::string &currentName = currentNode.name();
@ -726,7 +737,7 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in <vertex_weights> data <input> element"); throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in <vertex_weights> data <input> element");
} }
} else if (currentName == "vcount" && vertexCount > 0) { } else if (currentName == "vcount" && vertexCount > 0) {
const char *text = currentNode.value(); const char *text = currentNode.text().as_string();
size_t numWeights = 0; size_t numWeights = 0;
for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) { for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) {
if (*text == 0) { if (*text == 0) {
@ -763,20 +774,17 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads the image library contents // Reads the image library contents
void ColladaParser::ReadImageLibrary(XmlNode &node) { void ColladaParser::ReadImageLibrary(XmlNode &node) {
if (node.empty()) {
return;
}
for (XmlNode &currentNode : node.children()) { for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name(); const std::string &currentName = currentNode.name();
if (currentName == "image") { if (currentName == "image") {
std::string id = currentNode.attribute("id").as_string(); std::string id;
if (XmlParser::getStdStrAttribute( currentNode, "id", id )) {
mImageLibrary[id] = Image(); mImageLibrary[id] = Image();
// read on from there // read on from there
ReadImage(currentNode, mImageLibrary[id]); ReadImage(currentNode, mImageLibrary[id]);
} }
} }
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -793,7 +801,7 @@ void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) {
if (!currentNode.empty()) { if (!currentNode.empty()) {
// element content is filename - hopefully // element content is filename - hopefully
const char *sz = currentNode.text().as_string(); const char *sz = currentNode.text().as_string();
if (sz) { if (nullptr != sz) {
aiString filepath(sz); aiString filepath(sz);
UriDecodePath(filepath); UriDecodePath(filepath);
pImage.mFileName = filepath.C_Str(); pImage.mFileName = filepath.C_Str();
@ -843,10 +851,6 @@ void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads the material library // Reads the material library
void ColladaParser::ReadMaterialLibrary(XmlNode &node) { void ColladaParser::ReadMaterialLibrary(XmlNode &node) {
if (node.empty()) {
return;
}
std::map<std::string, int> names; std::map<std::string, int> names;
for (XmlNode &currentNode : node.children()) { for (XmlNode &currentNode : node.children()) {
std::string id = currentNode.attribute("id").as_string(); std::string id = currentNode.attribute("id").as_string();
@ -873,10 +877,6 @@ void ColladaParser::ReadMaterialLibrary(XmlNode &node) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads the light library // Reads the light library
void ColladaParser::ReadLightLibrary(XmlNode &node) { void ColladaParser::ReadLightLibrary(XmlNode &node) {
if (node.empty()) {
return;
}
for (XmlNode &currentNode : node.children()) { for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name(); const std::string &currentName = currentNode.name();
if (currentName == "light") { if (currentName == "light") {
@ -891,10 +891,6 @@ void ColladaParser::ReadLightLibrary(XmlNode &node) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads the camera library // Reads the camera library
void ColladaParser::ReadCameraLibrary(XmlNode &node) { void ColladaParser::ReadCameraLibrary(XmlNode &node) {
if (node.empty()) {
return;
}
for (XmlNode &currentNode : node.children()) { for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name(); const std::string &currentName = currentNode.name();
if (currentName == "camera") { if (currentName == "camera") {
@ -1738,14 +1734,16 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
// and read all indices into a temporary array // and read all indices into a temporary array
std::vector<size_t> indices; std::vector<size_t> indices;
if (expectedPointCount > 0) if (expectedPointCount > 0) {
indices.reserve(expectedPointCount * numOffsets); indices.reserve(expectedPointCount * numOffsets);
}
if (pNumPrimitives > 0) // It is possible to not contain any indices // It is possible to not contain any indices
{ if (pNumPrimitives > 0) {
std::string v; std::string v;
XmlParser::getValueAsString(node, v); XmlParser::getValueAsString(node, v);
const char *content = v.c_str(); const char *content = v.c_str();
SkipSpacesAndLineEnd(&content);
while (*content != 0) { while (*content != 0) {
// read a value. // read a value.
// Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways. // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
@ -1772,21 +1770,24 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
// find the data for all sources // find the data for all sources
for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) { for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
InputChannel &input = *it; InputChannel &input = *it;
if (input.mResolved) if (input.mResolved) {
continue; continue;
}
// find accessor // find accessor
input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor); input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
// resolve accessor's data pointer as well, if necessary // resolve accessor's data pointer as well, if necessary
const Accessor *acc = input.mResolved; const Accessor *acc = input.mResolved;
if (!acc->mData) if (!acc->mData) {
acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource); acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
} }
}
// and the same for the per-index channels // and the same for the per-index channels
for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) { for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
InputChannel &input = *it; InputChannel &input = *it;
if (input.mResolved) if (input.mResolved) {
continue; continue;
}
// ignore vertex pointer, it doesn't refer to an accessor // ignore vertex pointer, it doesn't refer to an accessor
if (input.mType == IT_Vertex) { if (input.mType == IT_Vertex) {
@ -1801,9 +1802,10 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor); input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
// resolve accessor's data pointer as well, if necessary // resolve accessor's data pointer as well, if necessary
const Accessor *acc = input.mResolved; const Accessor *acc = input.mResolved;
if (!acc->mData) if (!acc->mData) {
acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource); acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
} }
}
// For continued primitives, the given count does not come all in one <p>, but only one primitive per <p> // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
size_t numPrimitives = pNumPrimitives; size_t numPrimitives = pNumPrimitives;
@ -1884,11 +1886,13 @@ void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t n
ai_assert((baseOffset + numOffsets - 1) < indices.size()); ai_assert((baseOffset + numOffsets - 1) < indices.size());
// extract per-vertex channels using the global per-vertex offset // extract per-vertex channels using the global per-vertex offset
for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh); ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
}
// and extract per-index channels using there specified offset // and extract per-index channels using there specified offset
for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh); ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
}
// store the vertex-data index for later assignment of bone vertex weights // store the vertex-data index for later assignment of bone vertex weights
pMesh.mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]); pMesh.mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
@ -1912,8 +1916,9 @@ void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset,
// Extracts a single object from an input channel and stores it in the appropriate mesh data array // Extracts a single object from an input channel and stores it in the appropriate mesh data array
void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, size_t pLocalIndex, Mesh &pMesh) { void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, size_t pLocalIndex, Mesh &pMesh) {
// ignore vertex referrer - we handle them that separate // ignore vertex referrer - we handle them that separate
if (pInput.mType == IT_Vertex) if (pInput.mType == IT_Vertex) {
return; return;
}
const Accessor &acc = *pInput.mResolved; const Accessor &acc = *pInput.mResolved;
if (pLocalIndex >= acc.mCount) { if (pLocalIndex >= acc.mCount) {
@ -1926,16 +1931,18 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz
// assemble according to the accessors component sub-offset list. We don't care, yet, // assemble according to the accessors component sub-offset list. We don't care, yet,
// what kind of object exactly we're extracting here // what kind of object exactly we're extracting here
ai_real obj[4]; ai_real obj[4];
for (size_t c = 0; c < 4; ++c) for (size_t c = 0; c < 4; ++c) {
obj[c] = dataObject[acc.mSubOffset[c]]; obj[c] = dataObject[acc.mSubOffset[c]];
}
// now we reinterpret it according to the type we're reading here // now we reinterpret it according to the type we're reading here
switch (pInput.mType) { switch (pInput.mType) {
case IT_Position: // ignore all position streams except 0 - there can be only one position case IT_Position: // ignore all position streams except 0 - there can be only one position
if (pInput.mIndex == 0) if (pInput.mIndex == 0) {
pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2])); pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2]));
else } else {
ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported"); ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported");
}
break; break;
case IT_Normal: case IT_Normal:
// pad to current vertex count if necessary // pad to current vertex count if necessary
@ -1943,10 +1950,11 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz
pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0)); pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0));
// ignore all normal streams except 0 - there can be only one normal // ignore all normal streams except 0 - there can be only one normal
if (pInput.mIndex == 0) if (pInput.mIndex == 0) {
pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2])); pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2]));
else } else {
ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported"); ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported");
}
break; break;
case IT_Tangent: case IT_Tangent:
// pad to current vertex count if necessary // pad to current vertex count if necessary
@ -1954,21 +1962,24 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz
pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0)); pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0));
// ignore all tangent streams except 0 - there can be only one tangent // ignore all tangent streams except 0 - there can be only one tangent
if (pInput.mIndex == 0) if (pInput.mIndex == 0) {
pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
else } else {
ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported"); ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported");
}
break; break;
case IT_Bitangent: case IT_Bitangent:
// pad to current vertex count if necessary // pad to current vertex count if necessary
if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) {
pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1)); pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1));
}
// ignore all bitangent streams except 0 - there can be only one bitangent // ignore all bitangent streams except 0 - there can be only one bitangent
if (pInput.mIndex == 0) if (pInput.mIndex == 0) {
pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
else } else {
ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported"); ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported");
}
break; break;
case IT_Texcoord: case IT_Texcoord:
// up to 4 texture coord sets are fine, ignore the others // up to 4 texture coord sets are fine, ignore the others
@ -1979,8 +1990,9 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz
pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0)); pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0));
pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2])); pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2]));
if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */ if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) {
pMesh.mNumUVComponents[pInput.mIndex] = 3; pMesh.mNumUVComponents[pInput.mIndex] = 3;
}
} else { } else {
ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping.");
} }

View File

@ -54,18 +54,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/ByteSwapper.h> #include <assimp/ByteSwapper.h>
#include <assimp/ParsingUtils.h> #include <assimp/ParsingUtils.h>
#include <algorithm> // std::transform
#include "FBXUtil.h" #include "FBXUtil.h"
namespace Assimp { namespace Assimp {
namespace FBX { namespace FBX {
using namespace Util; using namespace Util;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Material::Material(uint64_t id, const Element& element, const Document& doc, const std::string& name) Material::Material(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
: Object(id,element,name) Object(id,element,name) {
{
const Scope& sc = GetRequiredScope(element); const Scope& sc = GetRequiredScope(element);
const Element* const ShadingModel = sc["ShadingModel"]; const Element* const ShadingModel = sc["ShadingModel"];
@ -77,23 +75,21 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con
if(ShadingModel) { if(ShadingModel) {
shading = ParseTokenAsString(GetRequiredToken(*ShadingModel,0)); shading = ParseTokenAsString(GetRequiredToken(*ShadingModel,0));
} } else {
else {
DOMWarning("shading mode not specified, assuming phong",&element); DOMWarning("shading mode not specified, assuming phong",&element);
shading = "phong"; shading = "phong";
} }
std::string templateName;
// lower-case shading because Blender (for example) writes "Phong" // lower-case shading because Blender (for example) writes "Phong"
std::transform(shading.data(), shading.data() + shading.size(), std::addressof(shading[0]), Assimp::ToLower<char>); for (size_t i = 0; i < shading.length(); ++i) {
shading[i] = static_cast<char>(tolower(shading[i]));
}
std::string templateName;
if(shading == "phong") { if(shading == "phong") {
templateName = "Material.FbxSurfacePhong"; templateName = "Material.FbxSurfacePhong";
} } else if(shading == "lambert") {
else if(shading == "lambert") {
templateName = "Material.FbxSurfaceLambert"; templateName = "Material.FbxSurfaceLambert";
} } else {
else {
DOMWarning("shading mode not recognized: " + shading,&element); DOMWarning("shading mode not recognized: " + shading,&element);
} }
@ -102,20 +98,19 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con
// resolve texture links // resolve texture links
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID()); const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());
for(const Connection* con : conns) { for(const Connection* con : conns) {
// texture link to properties, not objects // texture link to properties, not objects
if (!con->PropertyName().length()) { if ( 0 == con->PropertyName().length()) {
continue; continue;
} }
const Object* const ob = con->SourceObject(); const Object* const ob = con->SourceObject();
if(!ob) { if(nullptr == ob) {
DOMWarning("failed to read source object for texture link, ignoring",&element); DOMWarning("failed to read source object for texture link, ignoring",&element);
continue; continue;
} }
const Texture* const tex = dynamic_cast<const Texture*>(ob); const Texture* const tex = dynamic_cast<const Texture*>(ob);
if(!tex) { if(nullptr == tex) {
const LayeredTexture* const layeredTexture = dynamic_cast<const LayeredTexture*>(ob); const LayeredTexture* const layeredTexture = dynamic_cast<const LayeredTexture*>(ob);
if(!layeredTexture) { if(!layeredTexture) {
DOMWarning("source object for texture link is not a texture or layered texture, ignoring",&element); DOMWarning("source object for texture link is not a texture or layered texture, ignoring",&element);
@ -128,9 +123,7 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con
layeredTextures[prop] = layeredTexture; layeredTextures[prop] = layeredTexture;
((LayeredTexture*)layeredTexture)->fillTexture(doc); ((LayeredTexture*)layeredTexture)->fillTexture(doc);
} } else {
else
{
const std::string& prop = con->PropertyName(); const std::string& prop = con->PropertyName();
if (textures.find(prop) != textures.end()) { if (textures.find(prop) != textures.end()) {
DOMWarning("duplicate texture link: " + prop,&element); DOMWarning("duplicate texture link: " + prop,&element);
@ -138,23 +131,20 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con
textures[prop] = tex; textures[prop] = tex;
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Material::~Material() Material::~Material() {
{ // empty
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
: Object(id,element,name) Object(id,element,name),
, uvScaling(1.0f,1.0f) uvScaling(1.0f,1.0f),
, media(0) media(0) {
{
const Scope& sc = GetRequiredScope(element); const Scope& sc = GetRequiredScope(element);
const Element* const Type = sc["Type"]; const Element* const Type = sc["Type"];
@ -194,8 +184,7 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const
crop[1] = ParseTokenAsInt(GetRequiredToken(*Cropping,1)); crop[1] = ParseTokenAsInt(GetRequiredToken(*Cropping,1));
crop[2] = ParseTokenAsInt(GetRequiredToken(*Cropping,2)); crop[2] = ParseTokenAsInt(GetRequiredToken(*Cropping,2));
crop[3] = ParseTokenAsInt(GetRequiredToken(*Cropping,3)); crop[3] = ParseTokenAsInt(GetRequiredToken(*Cropping,3));
} } else {
else {
// vc8 doesn't support the crop() syntax in initialization lists // vc8 doesn't support the crop() syntax in initialization lists
// (and vc9 WARNS about the new (i.e. compliant) behaviour). // (and vc9 WARNS about the new (i.e. compliant) behaviour).
crop[0] = crop[1] = crop[2] = crop[3] = 0; crop[0] = crop[1] = crop[2] = crop[3] = 0;
@ -226,7 +215,7 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID()); const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());
for(const Connection* con : conns) { for(const Connection* con : conns) {
const Object* const ob = con->SourceObject(); const Object* const ob = con->SourceObject();
if(!ob) { if (nullptr == ob) {
DOMWarning("failed to read source object for texture link, ignoring",&element); DOMWarning("failed to read source object for texture link, ignoring",&element);
continue; continue;
} }
@ -240,46 +229,38 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const
} }
Texture::~Texture() Texture::~Texture() {
{ // empty
} }
LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& /*doc*/, const std::string& name) LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& /*doc*/, const std::string& name) :
: Object(id,element,name) Object(id,element,name),
,blendMode(BlendMode_Modulate) blendMode(BlendMode_Modulate),
,alpha(1) alpha(1) {
{
const Scope& sc = GetRequiredScope(element); const Scope& sc = GetRequiredScope(element);
const Element* const BlendModes = sc["BlendModes"]; const Element* const BlendModes = sc["BlendModes"];
const Element* const Alphas = sc["Alphas"]; const Element* const Alphas = sc["Alphas"];
if (nullptr != BlendModes) {
if(BlendModes!=0)
{
blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(*BlendModes,0)); blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(*BlendModes,0));
} }
if(Alphas!=0) if (nullptr != Alphas) {
{
alpha = ParseTokenAsFloat(GetRequiredToken(*Alphas,0)); alpha = ParseTokenAsFloat(GetRequiredToken(*Alphas,0));
} }
} }
LayeredTexture::~LayeredTexture() LayeredTexture::~LayeredTexture() {
{ // empty
} }
void LayeredTexture::fillTexture(const Document& doc) void LayeredTexture::fillTexture(const Document& doc) {
{
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID()); const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());
for(size_t i = 0; i < conns.size();++i) for(size_t i = 0; i < conns.size();++i) {
{
const Connection* con = conns.at(i); const Connection* con = conns.at(i);
const Object* const ob = con->SourceObject(); const Object* const ob = con->SourceObject();
if(!ob) { if (nullptr == ob) {
DOMWarning("failed to read source object for texture link, ignoring",&element); DOMWarning("failed to read source object for texture link, ignoring",&element);
continue; continue;
} }
@ -290,13 +271,11 @@ void LayeredTexture::fillTexture(const Document& doc)
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
: Object(id,element,name) Object(id,element,name),
, contentLength(0) contentLength(0),
, content(0) content(0) {
{
const Scope& sc = GetRequiredScope(element); const Scope& sc = GetRequiredScope(element);
const Element* const Type = sc["Type"]; const Element* const Type = sc["Type"];
@ -324,52 +303,43 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
if (!token.IsBinary()) { if (!token.IsBinary()) {
if (*data != '"') { if (*data != '"') {
DOMError("embedded content is not surrounded by quotation marks", &element); DOMError("embedded content is not surrounded by quotation marks", &element);
} } else {
else {
size_t targetLength = 0; size_t targetLength = 0;
auto numTokens = Content->Tokens().size(); auto numTokens = Content->Tokens().size();
// First time compute size (it could be large like 64Gb and it is good to allocate it once) // First time compute size (it could be large like 64Gb and it is good to allocate it once)
for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
{
const Token& dataToken = GetRequiredToken(*Content, tokenIdx); const Token& dataToken = GetRequiredToken(*Content, tokenIdx);
size_t tokenLength = dataToken.end() - dataToken.begin() - 2; // ignore double quotes size_t tokenLength = dataToken.end() - dataToken.begin() - 2; // ignore double quotes
const char* base64data = dataToken.begin() + 1; const char* base64data = dataToken.begin() + 1;
const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength); const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength);
if (outLength == 0) if (outLength == 0) {
{
DOMError("Corrupted embedded content found", &element); DOMError("Corrupted embedded content found", &element);
} }
targetLength += outLength; targetLength += outLength;
} }
if (targetLength == 0) if (targetLength == 0) {
{
DOMError("Corrupted embedded content found", &element); DOMError("Corrupted embedded content found", &element);
} }
content = new uint8_t[targetLength]; content = new uint8_t[targetLength];
contentLength = static_cast<uint64_t>(targetLength); contentLength = static_cast<uint64_t>(targetLength);
size_t dst_offset = 0; size_t dst_offset = 0;
for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
{
const Token& dataToken = GetRequiredToken(*Content, tokenIdx); const Token& dataToken = GetRequiredToken(*Content, tokenIdx);
size_t tokenLength = dataToken.end() - dataToken.begin() - 2; // ignore double quotes size_t tokenLength = dataToken.end() - dataToken.begin() - 2; // ignore double quotes
const char* base64data = dataToken.begin() + 1; const char* base64data = dataToken.begin() + 1;
dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset); dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset);
} }
if (targetLength != dst_offset) if (targetLength != dst_offset) {
{
delete[] content; delete[] content;
contentLength = 0; contentLength = 0;
DOMError("Corrupted embedded content found", &element); DOMError("Corrupted embedded content found", &element);
} }
} }
} } else if (static_cast<size_t>(token.end() - data) < 5) {
else if (static_cast<size_t>(token.end() - data) < 5) {
DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element); DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element);
} } else if (*data != 'R') {
else if (*data != 'R') {
DOMWarning("video content is not raw binary data, ignoring", &element); DOMWarning("video content is not raw binary data, ignoring", &element);
} } else {
else {
// read number of elements // read number of elements
uint32_t len = 0; uint32_t len = 0;
::memcpy(&len, data + 1, sizeof(len)); ::memcpy(&len, data + 1, sizeof(len));
@ -380,8 +350,7 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
content = new uint8_t[len]; content = new uint8_t[len];
::memcpy(content, data + 5, len); ::memcpy(content, data + 5, len);
} }
} catch (const runtime_error& runtimeError) } catch (const runtime_error& runtimeError) {
{
//we don't need the content data for contents that has already been loaded //we don't need the content data for contents that has already been loaded
ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ", ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ",
runtimeError.what()); runtimeError.what());
@ -392,14 +361,11 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
} }
Video::~Video() Video::~Video() {
{
if(content) {
delete[] content; delete[] content;
}
} }
} //!FBX } //!FBX
} //!Assimp } //!Assimp
#endif #endif // ASSIMP_BUILD_NO_FBX_IMPORTER

View File

@ -656,7 +656,7 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
} }
} }
if( openings && ((sides_with_openings == 1 && sides_with_openings) || (sides_with_v_openings == 2 && sides_with_v_openings)) ) { if( openings && (sides_with_openings == 1 || sides_with_v_openings == 2 ) ) {
IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp"); IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp");
} }

View File

@ -1189,22 +1189,11 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
TempMesh* profile_data = opening.profileMesh.get(); TempMesh* profile_data = opening.profileMesh.get();
bool is_2d_source = false; bool is_2d_source = false;
if (opening.profileMesh2D && norm_extrusion_dir.SquareLength() > 0) { if (opening.profileMesh2D && norm_extrusion_dir.SquareLength() > 0) {
if(std::fabs(norm_extrusion_dir * wall_extrusion_axis_norm) < 0.1) {
// horizontal extrusion
if (std::fabs(norm_extrusion_dir * nor) > 0.9) { if (std::fabs(norm_extrusion_dir * nor) > 0.9) {
profile_data = opening.profileMesh2D.get(); profile_data = opening.profileMesh2D.get();
is_2d_source = true; is_2d_source = true;
} }
} }
else {
// vertical extrusion
if (std::fabs(norm_extrusion_dir * nor) > 0.9) {
profile_data = opening.profileMesh2D.get();
is_2d_source = true;
}
}
}
std::vector<IfcVector3> profile_verts = profile_data->mVerts; std::vector<IfcVector3> profile_verts = profile_data->mVerts;
std::vector<unsigned int> profile_vertcnts = profile_data->mVertcnt; std::vector<unsigned int> profile_vertcnts = profile_data->mVertcnt;
if(profile_verts.size() <= 2) { if(profile_verts.size() <= 2) {

View File

@ -1642,7 +1642,7 @@ static int _m3dstbi__expand_png_palette(_m3dstbi__png *a, unsigned char *palette
static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp) { static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp) {
unsigned char palette[1024], pal_img_n = 0; unsigned char palette[1024], pal_img_n = 0;
unsigned char has_trans = 0, tc[3] = {}; unsigned char has_trans = 0, tc[3] = {};
_m3dstbi__uint16 tc16[3]; _m3dstbi__uint16 tc16[3] = {};
_m3dstbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0; _m3dstbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0;
int first = 1, k, interlace = 0, color = 0; int first = 1, k, interlace = 0, color = 0;
_m3dstbi__context *s = z->s; _m3dstbi__context *s = z->s;

View File

@ -122,8 +122,8 @@ void ObjFileMtlImporter::load() {
{ {
++m_DataIt; ++m_DataIt;
getColorRGBA(&m_pModel->m_pCurrentMaterial->ambient); getColorRGBA(&m_pModel->m_pCurrentMaterial->ambient);
} else if (*m_DataIt == 'd') // Diffuse color } else if (*m_DataIt == 'd') {
{ // Diffuse color
++m_DataIt; ++m_DataIt;
getColorRGBA(&m_pModel->m_pCurrentMaterial->diffuse); getColorRGBA(&m_pModel->m_pCurrentMaterial->diffuse);
} else if (*m_DataIt == 's') { } else if (*m_DataIt == 's') {
@ -144,7 +144,9 @@ void ObjFileMtlImporter::load() {
} else if (*m_DataIt == 'r') { } else if (*m_DataIt == 'r') {
// Material transmission alpha value // Material transmission alpha value
++m_DataIt; ++m_DataIt;
getFloatValue(m_pModel->m_pCurrentMaterial->alpha); ai_real d;
getFloatValue(d);
m_pModel->m_pCurrentMaterial->alpha = static_cast<ai_real>(1.0) - d;
} }
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
} break; } break;

View File

@ -147,7 +147,7 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo
for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
WriteMesh(pScene->mMeshes[ i ]); WriteMesh(pScene->mMeshes[ i ]);
} }
mOutput << EndSolidToken << name << endl; mOutput << EndSolidToken << " " << name << endl;
} }
} }

View File

@ -54,7 +54,7 @@ size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out) {
} }
if (inLength < 4) { if (inLength < 4) {
out = 0; out = nullptr;
return 0; return 0;
} }

View File

@ -107,7 +107,6 @@ public:
f(file) {} f(file) {}
~IOStream() { ~IOStream() {
fclose(f); fclose(f);
f = 0;
} }
size_t Read(void *b, size_t sz, size_t n) { return fread(b, sz, n, f); } size_t Read(void *b, size_t sz, size_t n) { return fread(b, sz, n, f); }

View File

@ -376,87 +376,6 @@ struct Object {
// Classes for each glTF top-level object type // Classes for each glTF top-level object type
// //
//! A typed view into a BufferView. A BufferView contains raw binary data.
//! An accessor provides a typed view into a BufferView or a subset of a BufferView
//! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer.
struct Accessor : public Object {
struct Sparse;
Ref<BufferView> bufferView; //!< The ID of the bufferView. (required)
size_t byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required)
ComponentType componentType; //!< The datatype of components in the attribute. (required)
size_t count; //!< The number of attributes referenced by this accessor. (required)
AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
std::vector<double> max; //!< Maximum value of each component in this attribute.
std::vector<double> min; //!< Minimum value of each component in this attribute.
std::unique_ptr<Sparse> sparse;
unsigned int GetNumComponents();
unsigned int GetBytesPerComponent();
unsigned int GetElementSize();
inline uint8_t *GetPointer();
template <class T>
void ExtractData(T *&outData);
void WriteData(size_t count, const void *src_buffer, size_t src_stride);
void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride);
void WriteSparseIndices(size_t count, const void *src_idx, size_t src_idxStride);
//! Helper class to iterate the data
class Indexer {
friend struct Accessor;
// This field is reported as not used, making it protectd is the easiest way to work around it without going to the bottom of what the problem is:
// ../code/glTF2/glTF2Asset.h:392:19: error: private field 'accessor' is not used [-Werror,-Wunused-private-field]
protected:
Accessor &accessor;
private:
uint8_t *data;
size_t elemSize, stride;
Indexer(Accessor &acc);
public:
//! Accesses the i-th value as defined by the accessor
template <class T>
T GetValue(int i);
//! Accesses the i-th value as defined by the accessor
inline unsigned int GetUInt(int i) {
return GetValue<unsigned int>(i);
}
inline bool IsValid() const {
return data != 0;
}
};
inline Indexer GetIndexer() {
return Indexer(*this);
}
Accessor() {}
void Read(Value &obj, Asset &r);
//sparse
struct Sparse {
size_t count;
ComponentType indicesType;
Ref<BufferView> indices;
size_t indicesByteOffset;
Ref<BufferView> values;
size_t valuesByteOffset;
std::vector<uint8_t> data; //!< Actual data, which may be defaulted to an array of zeros or the original data, with the sparse buffer view applied on top of it.
void PopulateData(size_t numBytes, uint8_t *bytes);
void PatchData(unsigned int elementSize);
};
};
//! A buffer points to binary geometry, animation, or skins. //! A buffer points to binary geometry, animation, or skins.
struct Buffer : public Object { struct Buffer : public Object {
/********************* Types *********************/ /********************* Types *********************/
@ -594,6 +513,90 @@ struct BufferView : public Object {
uint8_t *GetPointer(size_t accOffset); uint8_t *GetPointer(size_t accOffset);
}; };
//! A typed view into a BufferView. A BufferView contains raw binary data.
//! An accessor provides a typed view into a BufferView or a subset of a BufferView
//! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer.
struct Accessor : public Object {
struct Sparse;
Ref<BufferView> bufferView; //!< The ID of the bufferView. (required)
size_t byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required)
ComponentType componentType; //!< The datatype of components in the attribute. (required)
size_t count; //!< The number of attributes referenced by this accessor. (required)
AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
std::vector<double> max; //!< Maximum value of each component in this attribute.
std::vector<double> min; //!< Minimum value of each component in this attribute.
std::unique_ptr<Sparse> sparse;
std::unique_ptr<Buffer> decodedBuffer; // Packed decoded data, returned instead of original bufferView if present
unsigned int GetNumComponents();
unsigned int GetBytesPerComponent();
unsigned int GetElementSize();
inline uint8_t *GetPointer();
inline size_t GetStride();
inline size_t GetMaxByteSize();
template <class T>
void ExtractData(T *&outData);
void WriteData(size_t count, const void *src_buffer, size_t src_stride);
void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride);
void WriteSparseIndices(size_t count, const void *src_idx, size_t src_idxStride);
//! Helper class to iterate the data
class Indexer {
friend struct Accessor;
// This field is reported as not used, making it protectd is the easiest way to work around it without going to the bottom of what the problem is:
// ../code/glTF2/glTF2Asset.h:392:19: error: private field 'accessor' is not used [-Werror,-Wunused-private-field]
protected:
Accessor &accessor;
private:
uint8_t *data;
size_t elemSize, stride;
Indexer(Accessor &acc);
public:
//! Accesses the i-th value as defined by the accessor
template <class T>
T GetValue(int i);
//! Accesses the i-th value as defined by the accessor
inline unsigned int GetUInt(int i) {
return GetValue<unsigned int>(i);
}
inline bool IsValid() const {
return data != nullptr;
}
};
inline Indexer GetIndexer() {
return Indexer(*this);
}
Accessor() {}
void Read(Value &obj, Asset &r);
//sparse
struct Sparse {
size_t count;
ComponentType indicesType;
Ref<BufferView> indices;
size_t indicesByteOffset;
Ref<BufferView> values;
size_t valuesByteOffset;
std::vector<uint8_t> data; //!< Actual data, which may be defaulted to an array of zeros or the original data, with the sparse buffer view applied on top of it.
void PopulateData(size_t numBytes, uint8_t *bytes);
void PatchData(unsigned int elementSize);
};
};
struct Camera : public Object { struct Camera : public Object {
enum Type { enum Type {
Perspective, Perspective,
@ -846,7 +849,7 @@ struct CustomExtension : public Object {
CustomExtension() = default; CustomExtension() = default;
CustomExtension(const CustomExtension& other) CustomExtension(const CustomExtension &other)
: Object(other) : Object(other)
, mStringValue(other.mStringValue) , mStringValue(other.mStringValue)
, mDoubleValue(other.mDoubleValue) , mDoubleValue(other.mDoubleValue)
@ -1092,6 +1095,7 @@ public:
bool KHR_materials_sheen; bool KHR_materials_sheen;
bool KHR_materials_clearcoat; bool KHR_materials_clearcoat;
bool KHR_materials_transmission; bool KHR_materials_transmission;
bool KHR_draco_mesh_compression;
} extensionsUsed; } extensionsUsed;
//! Keeps info about the required extensions //! Keeps info about the required extensions
@ -1100,7 +1104,7 @@ public:
} extensionsRequired; } extensionsRequired;
AssetMetadata asset; AssetMetadata asset;
Value* extras = nullptr; Value *extras = nullptr;
// Dictionaries for each type of object // Dictionaries for each type of object
@ -1122,7 +1126,7 @@ public:
Ref<Scene> scene; Ref<Scene> scene;
public: public:
Asset(IOSystem *io = 0) : Asset(IOSystem *io = nullptr) :
mIOSystem(io), mIOSystem(io),
asset(), asset(),
accessors(*this, "accessors"), accessors(*this, "accessors"),

View File

@ -42,9 +42,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "AssetLib/glTF/glTFCommon.h" #include "AssetLib/glTF/glTFCommon.h"
#include <assimp/MemoryIOWrapper.h>
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/MemoryIOWrapper.h>
#ifdef ASSIMP_ENABLE_DRACO
// Google draco library headers spew many warnings. Bad Google, no cookie
#if _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4018) // Signed/unsigned mismatch
#pragma warning(disable : 4804) // Unsafe use of type 'bool'
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wbool-compare"
#pragma GCC diagnostic ignored "-Wsign-compare"
#endif
#include "draco/compression/decode.h"
#include "draco/core/decoder_buffer.h"
#if _MSC_VER
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#ifndef DRACO_MESH_COMPRESSION_SUPPORTED
#error glTF: KHR_draco_mesh_compression: draco library must have DRACO_MESH_COMPRESSION_SUPPORTED
#endif
#endif
using namespace Assimp; using namespace Assimp;
@ -146,35 +177,155 @@ inline static T MemberOrDefault(Value &obj, const char *id, T defaultValue) {
inline Value *FindMember(Value &val, const char *id) { inline Value *FindMember(Value &val, const char *id) {
Value::MemberIterator it = val.FindMember(id); Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd()) ? &it->value : 0; return (it != val.MemberEnd()) ? &it->value : nullptr;
} }
inline Value *FindString(Value &val, const char *id) { inline Value *FindString(Value &val, const char *id) {
Value::MemberIterator it = val.FindMember(id); Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0; return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : nullptr;
} }
inline Value *FindNumber(Value &val, const char *id) { inline Value *FindNumber(Value &val, const char *id) {
Value::MemberIterator it = val.FindMember(id); Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : 0; return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : nullptr;
} }
inline Value *FindUInt(Value &val, const char *id) { inline Value *FindUInt(Value &val, const char *id) {
Value::MemberIterator it = val.FindMember(id); Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd() && it->value.IsUint()) ? &it->value : 0; return (it != val.MemberEnd() && it->value.IsUint()) ? &it->value : nullptr;
} }
inline Value *FindArray(Value &val, const char *id) { inline Value *FindArray(Value &val, const char *id) {
Value::MemberIterator it = val.FindMember(id); Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd() && it->value.IsArray()) ? &it->value : 0; return (it != val.MemberEnd() && it->value.IsArray()) ? &it->value : nullptr;
} }
inline Value *FindObject(Value &val, const char *id) { inline Value *FindObject(Value &val, const char *id) {
Value::MemberIterator it = val.FindMember(id); Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd() && it->value.IsObject()) ? &it->value : 0; return (it != val.MemberEnd() && it->value.IsObject()) ? &it->value : nullptr;
}
inline Value *FindExtension(Value &val, const char *extensionId) {
if (Value *extensionList = FindObject(val, "extensions")) {
if (Value *extension = FindObject(*extensionList, extensionId)) {
return extension;
}
}
return nullptr;
} }
} // namespace } // namespace
#ifdef ASSIMP_ENABLE_DRACO
template <typename T>
inline void CopyFaceIndex_Draco(Buffer &decodedIndexBuffer, const draco::Mesh &draco_mesh) {
const size_t faceStride = sizeof(T) * 3;
for (draco::FaceIndex f(0); f < draco_mesh.num_faces(); ++f) {
const draco::Mesh::Face &face = draco_mesh.face(f);
T indices[3] = { static_cast<T>(face[0].value()), static_cast<T>(face[1].value()), static_cast<T>(face[2].value()) };
memcpy(decodedIndexBuffer.GetPointer() + (f.value() * faceStride), &indices[0], faceStride);
}
}
inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Primitive &prim) {
if (!prim.indices || dracoMesh.num_faces() == 0)
return;
// Create a decoded Index buffer (if there is one)
size_t componentBytes = prim.indices->GetBytesPerComponent();
std::unique_ptr<Buffer> decodedIndexBuffer(new Buffer());
decodedIndexBuffer->Grow(dracoMesh.num_faces() * 3 * componentBytes);
// If accessor uses the same size as draco implementation, copy the draco buffer directly
// Usually uint32_t but shouldn't assume
if (sizeof(dracoMesh.face(draco::FaceIndex(0))[0]) == componentBytes) {
memcpy(decodedIndexBuffer->GetPointer(), &dracoMesh.face(draco::FaceIndex(0))[0], decodedIndexBuffer->byteLength);
return;
}
// Not same size, convert
switch (componentBytes) {
case sizeof(uint32_t):
CopyFaceIndex_Draco<uint32_t>(*decodedIndexBuffer, dracoMesh);
break;
case sizeof(uint16_t):
CopyFaceIndex_Draco<uint16_t>(*decodedIndexBuffer, dracoMesh);
break;
case sizeof(uint8_t):
CopyFaceIndex_Draco<uint8_t>(*decodedIndexBuffer, dracoMesh);
break;
default:
ai_assert(false);
break;
}
// Assign this alternate data buffer to the accessor
prim.indices->decodedBuffer.swap(decodedIndexBuffer);
}
template <typename T>
static bool GetAttributeForAllPoints_Draco(const draco::Mesh &dracoMesh,
const draco::PointAttribute &dracoAttribute,
Buffer &outBuffer) {
size_t byteOffset = 0;
T values[4] = { 0, 0, 0, 0 };
for (draco::PointIndex i(0); i < dracoMesh.num_points(); ++i) {
const draco::AttributeValueIndex val_index = dracoAttribute.mapped_index(i);
if (!dracoAttribute.ConvertValue<T>(val_index, dracoAttribute.num_components(), values)) {
return false;
}
memcpy(outBuffer.GetPointer() + byteOffset, &values[0], sizeof(T) * dracoAttribute.num_components());
byteOffset += sizeof(T) * dracoAttribute.num_components();
}
return true;
}
inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32_t dracoAttribId, Accessor &accessor) {
// Create decoded buffer
const draco::PointAttribute *pDracoAttribute = dracoMesh.GetAttributeByUniqueId(dracoAttribId);
if (pDracoAttribute == nullptr) {
throw DeadlyImportError("GLTF: Invalid draco attribute id: ", dracoAttribId);
}
size_t componentBytes = accessor.GetBytesPerComponent();
std::unique_ptr<Buffer> decodedAttribBuffer(new Buffer());
decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes);
switch (accessor.componentType) {
case ComponentType_BYTE:
GetAttributeForAllPoints_Draco<int8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
break;
case ComponentType_UNSIGNED_BYTE:
GetAttributeForAllPoints_Draco<uint8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
break;
case ComponentType_SHORT:
GetAttributeForAllPoints_Draco<int16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
break;
case ComponentType_UNSIGNED_SHORT:
GetAttributeForAllPoints_Draco<uint16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
break;
case ComponentType_UNSIGNED_INT:
GetAttributeForAllPoints_Draco<uint32_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
break;
case ComponentType_FLOAT:
GetAttributeForAllPoints_Draco<float>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
break;
default:
ai_assert(false);
break;
}
// Assign this alternate data buffer to the accessor
accessor.decodedBuffer.swap(decodedAttribBuffer);
}
#endif // ASSIMP_ENABLE_DRACO
// //
// LazyDict methods // LazyDict methods
// //
@ -197,7 +348,7 @@ inline LazyDict<T>::~LazyDict() {
template <class T> template <class T>
inline void LazyDict<T>::AttachToDocument(Document &doc) { inline void LazyDict<T>::AttachToDocument(Document &doc) {
Value *container = 0; Value *container = nullptr;
if (mExtId) { if (mExtId) {
if (Value *exts = FindObject(doc, "extensions")) { if (Value *exts = FindObject(doc, "extensions")) {
@ -214,7 +365,7 @@ inline void LazyDict<T>::AttachToDocument(Document &doc) {
template <class T> template <class T>
inline void LazyDict<T>::DetachFromDocument() { inline void LazyDict<T>::DetachFromDocument() {
mDict = 0; mDict = nullptr;
} }
template <class T> template <class T>
@ -382,7 +533,7 @@ inline void Buffer::Read(Value &obj, Asset &r) {
glTFCommon::Util::DataURI dataURI; glTFCommon::Util::DataURI dataURI;
if (ParseDataURI(uri, it->GetStringLength(), dataURI)) { if (ParseDataURI(uri, it->GetStringLength(), dataURI)) {
if (dataURI.base64) { if (dataURI.base64) {
uint8_t *data = 0; uint8_t *data = nullptr;
this->byteLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, data); this->byteLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, data);
this->mData.reset(data, std::default_delete<uint8_t[]>()); this->mData.reset(data, std::default_delete<uint8_t[]>());
@ -401,10 +552,7 @@ inline void Buffer::Read(Value &obj, Asset &r) {
} }
} else { // Local file } else { // Local file
if (byteLength > 0) { if (byteLength > 0) {
std::string dir = !r.mCurrentAssetDir.empty() ? ( std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir.back() == '/' ? r.mCurrentAssetDir : r.mCurrentAssetDir + '/') : "";
r.mCurrentAssetDir.back() == '/' ?
r.mCurrentAssetDir : r.mCurrentAssetDir + '/'
) : "";
IOStream *file = r.OpenFile(dir + uri, "rb"); IOStream *file = r.OpenFile(dir + uri, "rb");
if (file) { if (file) {
@ -575,9 +723,9 @@ inline void BufferView::Read(Value &obj, Asset &r) {
} }
inline uint8_t *BufferView::GetPointer(size_t accOffset) { inline uint8_t *BufferView::GetPointer(size_t accOffset) {
if (!buffer) return 0; if (!buffer) return nullptr;
uint8_t *basePtr = buffer->GetPointer(); uint8_t *basePtr = buffer->GetPointer();
if (!basePtr) return 0; if (!basePtr) return nullptr;
size_t offset = accOffset + byteOffset; size_t offset = accOffset + byteOffset;
if (buffer->EncodedRegion_Current != nullptr) { if (buffer->EncodedRegion_Current != nullptr) {
@ -709,12 +857,15 @@ inline unsigned int Accessor::GetElementSize() {
} }
inline uint8_t *Accessor::GetPointer() { inline uint8_t *Accessor::GetPointer() {
if (decodedBuffer)
return decodedBuffer->GetPointer();
if (sparse) if (sparse)
return sparse->data.data(); return sparse->data.data();
if (!bufferView || !bufferView->buffer) return 0; if (!bufferView || !bufferView->buffer) return nullptr;
uint8_t *basePtr = bufferView->buffer->GetPointer(); uint8_t *basePtr = bufferView->buffer->GetPointer();
if (!basePtr) return 0; if (!basePtr) return nullptr;
size_t offset = byteOffset + bufferView->byteOffset; size_t offset = byteOffset + bufferView->byteOffset;
@ -730,6 +881,22 @@ inline uint8_t *Accessor::GetPointer() {
return basePtr + offset; return basePtr + offset;
} }
inline size_t Accessor::GetStride() {
// Decoded buffer is always packed
if (decodedBuffer)
return GetElementSize();
// Sparse and normal bufferView
return (bufferView && bufferView->byteStride ? bufferView->byteStride : GetElementSize());
}
inline size_t Accessor::GetMaxByteSize() {
if (decodedBuffer)
return decodedBuffer->byteLength;
return (bufferView ? bufferView->byteLength : sparse->data.size());
}
namespace { namespace {
inline void CopyData(size_t count, inline void CopyData(size_t count,
const uint8_t *src, size_t src_stride, const uint8_t *src, size_t src_stride,
@ -761,7 +928,7 @@ void Accessor::ExtractData(T *&outData) {
const size_t elemSize = GetElementSize(); const size_t elemSize = GetElementSize();
const size_t totalSize = elemSize * count; const size_t totalSize = elemSize * count;
const size_t stride = bufferView && bufferView->byteStride ? bufferView->byteStride : elemSize; const size_t stride = GetStride();
const size_t targetElemSize = sizeof(T); const size_t targetElemSize = sizeof(T);
@ -769,8 +936,8 @@ void Accessor::ExtractData(T *&outData) {
throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name)); throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name));
} }
const size_t maxSize = (bufferView ? bufferView->byteLength : sparse->data.size()); const size_t maxSize = GetMaxByteSize();
if (count*stride > maxSize) { if (count * stride > maxSize) {
throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
} }
@ -828,14 +995,14 @@ inline Accessor::Indexer::Indexer(Accessor &acc) :
accessor(acc), accessor(acc),
data(acc.GetPointer()), data(acc.GetPointer()),
elemSize(acc.GetElementSize()), elemSize(acc.GetElementSize()),
stride(acc.bufferView && acc.bufferView->byteStride ? acc.bufferView->byteStride : elemSize) { stride(acc.GetStride()) {
} }
//! Accesses the i-th value as defined by the accessor //! Accesses the i-th value as defined by the accessor
template <class T> template <class T>
T Accessor::Indexer::GetValue(int i) { T Accessor::Indexer::GetValue(int i) {
ai_assert(data); ai_assert(data);
ai_assert(i * stride < accessor.bufferView->byteLength); ai_assert(i * stride < accessor.GetMaxByteSize());
// Ensure that the memcpy doesn't overwrite the local. // Ensure that the memcpy doesn't overwrite the local.
const size_t sizeToCopy = std::min(elemSize, sizeof(T)); const size_t sizeToCopy = std::min(elemSize, sizeof(T));
T value = T(); T value = T();
@ -872,8 +1039,7 @@ inline void Image::Read(Value &obj, Asset &r) {
if (Value *mtype = FindString(obj, "mimeType")) { if (Value *mtype = FindString(obj, "mimeType")) {
this->mimeType = mtype->GetString(); this->mimeType = mtype->GetString();
} }
if (!this->bufferView || this->mimeType.empty()) if (!this->bufferView || this->mimeType.empty()) {
{
throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " does not have a URI, so it must have a valid bufferView and mimetype"); throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " does not have a URI, so it must have a valid bufferView and mimetype");
} }
@ -884,10 +1050,8 @@ inline void Image::Read(Value &obj, Asset &r) {
this->mData.reset(new uint8_t[this->mDataLength]); this->mData.reset(new uint8_t[this->mDataLength]);
memcpy(this->mData.get(), buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength); memcpy(this->mData.get(), buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength);
} } else {
else throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " should have either a URI of a bufferView and mimetype");
{
throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " should have either a URI of a bufferView and mimetype" );
} }
} }
} }
@ -946,9 +1110,8 @@ inline void Texture::Read(Value &obj, Asset &r) {
namespace { namespace {
inline void SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) { inline void SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) {
if (r.extensionsUsed.KHR_texture_transform) { if (r.extensionsUsed.KHR_texture_transform) {
if (Value *extensions = FindObject(*prop, "extensions")) { if (Value *pKHR_texture_transform = FindExtension(*prop, "KHR_texture_transform")) {
out.textureTransformSupported = true; out.textureTransformSupported = true;
if (Value *pKHR_texture_transform = FindObject(*extensions, "KHR_texture_transform")) {
if (Value *array = FindArray(*pKHR_texture_transform, "offset")) { if (Value *array = FindArray(*pKHR_texture_transform, "offset")) {
out.TextureTransformExt_t.offset[0] = (*array)[0].GetFloat(); out.TextureTransformExt_t.offset[0] = (*array)[0].GetFloat();
out.TextureTransformExt_t.offset[1] = (*array)[1].GetFloat(); out.TextureTransformExt_t.offset[1] = (*array)[1].GetFloat();
@ -970,7 +1133,6 @@ inline void SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) {
} }
} }
} }
}
if (Value *index = FindUInt(*prop, "index")) { if (Value *index = FindUInt(*prop, "index")) {
out.texture = r.textures.Retrieve(index->GetUint()); out.texture = r.textures.Retrieve(index->GetUint());
@ -1043,8 +1205,7 @@ inline void Material::Read(Value &material, Asset &r) {
} }
} }
if (r.extensionsUsed.KHR_texture_transform) { // Extension KHR_texture_transform is handled in ReadTextureProperty
}
if (r.extensionsUsed.KHR_materials_sheen) { if (r.extensionsUsed.KHR_materials_sheen) {
if (Value *curMaterialSheen = FindObject(*extensions, "KHR_materials_sheen")) { if (Value *curMaterialSheen = FindObject(*extensions, "KHR_materials_sheen")) {
@ -1106,12 +1267,12 @@ void SetVector(vec3 &v, const float (&in)[3]) {
inline void Material::SetDefaults() { inline void Material::SetDefaults() {
//pbr materials //pbr materials
SetVector(pbrMetallicRoughness.baseColorFactor, defaultBaseColor); SetVector(pbrMetallicRoughness.baseColorFactor, defaultBaseColor);
pbrMetallicRoughness.metallicFactor = 1.0; pbrMetallicRoughness.metallicFactor = 1.0f;
pbrMetallicRoughness.roughnessFactor = 1.0; pbrMetallicRoughness.roughnessFactor = 1.0f;
SetVector(emissiveFactor, defaultEmissiveFactor); SetVector(emissiveFactor, defaultEmissiveFactor);
alphaMode = "OPAQUE"; alphaMode = "OPAQUE";
alphaCutoff = 0.5; alphaCutoff = 0.5f;
doubleSided = false; doubleSided = false;
unlit = false; unlit = false;
} }
@ -1120,7 +1281,7 @@ inline void PbrSpecularGlossiness::SetDefaults() {
//pbrSpecularGlossiness properties //pbrSpecularGlossiness properties
SetVector(diffuseFactor, defaultDiffuseFactor); SetVector(diffuseFactor, defaultDiffuseFactor);
SetVector(specularFactor, defaultSpecularFactor); SetVector(specularFactor, defaultSpecularFactor);
glossinessFactor = 1.0; glossinessFactor = 1.0f;
} }
inline void MaterialSheen::SetDefaults() { inline void MaterialSheen::SetDefaults() {
@ -1192,6 +1353,14 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
Primitive &prim = this->primitives[i]; Primitive &prim = this->primitives[i];
prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES); prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES);
if (Value *indices = FindUInt(primitive, "indices")) {
prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint());
}
if (Value *material = FindUInt(primitive, "material")) {
prim.material = pAsset_Root.materials.Retrieve(material->GetUint());
}
if (Value *attrs = FindObject(primitive, "attributes")) { if (Value *attrs = FindObject(primitive, "attributes")) {
for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) { for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
if (!it->value.IsUint()) continue; if (!it->value.IsUint()) continue;
@ -1200,11 +1369,12 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
// and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc. // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc.
int undPos = 0; int undPos = 0;
Mesh::AccessorList *vec = 0; Mesh::AccessorList *vec = nullptr;
if (GetAttribVector(prim, attr, vec, undPos)) { if (GetAttribVector(prim, attr, vec, undPos)) {
size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
if ((*vec).size() != idx) { if ((*vec).size() != idx) {
throw DeadlyImportError("GLTF: Invalid attribute: ", attr, ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc."); throw DeadlyImportError("GLTF: Invalid attribute in mesh: ", name, " primitive: ", i, "attrib: ", attr,
". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc.");
} }
(*vec).resize(idx + 1); (*vec).resize(idx + 1);
(*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint()); (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint());
@ -1212,6 +1382,69 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
} }
} }
#ifdef ASSIMP_ENABLE_DRACO
// KHR_draco_mesh_compression spec: Draco can only be used for glTF Triangles or Triangle Strips
if (pAsset_Root.extensionsUsed.KHR_draco_mesh_compression && (prim.mode == PrimitiveMode_TRIANGLES || prim.mode == PrimitiveMode_TRIANGLE_STRIP)) {
// Look for draco mesh compression extension and bufferView
// Skip if any missing
if (Value *dracoExt = FindExtension(primitive, "KHR_draco_mesh_compression")) {
if (Value *bufView = FindUInt(*dracoExt, "bufferView")) {
// Attempt to load indices and attributes using draco compression
auto bufferView = pAsset_Root.bufferViews.Retrieve(bufView->GetUint());
// Attempt to perform the draco decode on the buffer data
const char *bufferViewData = reinterpret_cast<const char *>(bufferView->buffer->GetPointer() + bufferView->byteOffset);
draco::DecoderBuffer decoderBuffer;
decoderBuffer.Init(bufferViewData, bufferView->byteLength);
draco::Decoder decoder;
auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
if (!decodeResult.ok()) {
// A corrupt Draco isn't actually fatal if the primitive data is also provided in a standard buffer, but does anyone do that?
throw DeadlyImportError("GLTF: Invalid Draco mesh compression in mesh: ", name, " primitive: ", i, ": ", decodeResult.status().error_msg_string());
}
// Now we have a draco mesh
const std::unique_ptr<draco::Mesh> &pDracoMesh = decodeResult.value();
// Redirect the accessors to the decoded data
// Indices
SetDecodedIndexBuffer_Draco(*pDracoMesh, prim);
// Vertex attributes
if (Value *attrs = FindObject(*dracoExt, "attributes")) {
for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
if (!it->value.IsUint()) continue;
const char *attr = it->name.GetString();
int undPos = 0;
Mesh::AccessorList *vec = nullptr;
if (GetAttribVector(prim, attr, vec, undPos)) {
size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
if (idx >= (*vec).size()) {
throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr,
". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc.");
}
if (!(*vec)[idx]) {
throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr,
". All draco-encoded attributes must also define an accessor.");
}
Accessor &attribAccessor = *(*vec)[idx];
if (attribAccessor.count == 0)
throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr);
// Redirect this accessor to the appropriate Draco vertex attribute data
const uint32_t dracoAttribId = it->value.GetUint();
SetDecodedAttributeBuffer_Draco(*pDracoMesh, dracoAttribId, attribAccessor);
}
}
}
}
}
}
#endif
Value *targetsArray = FindArray(primitive, "targets"); Value *targetsArray = FindArray(primitive, "targets");
if (nullptr != targetsArray) { if (nullptr != targetsArray) {
prim.targets.resize(targetsArray->Size()); prim.targets.resize(targetsArray->Size());
@ -1227,7 +1460,7 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
const char *attr = it->name.GetString(); const char *attr = it->name.GetString();
// Valid attribute semantics include POSITION, NORMAL, TANGENT // Valid attribute semantics include POSITION, NORMAL, TANGENT
int undPos = 0; int undPos = 0;
Mesh::AccessorList *vec = 0; Mesh::AccessorList *vec = nullptr;
if (GetAttribTargetVector(prim, j, attr, vec, undPos)) { if (GetAttribTargetVector(prim, j, attr, vec, undPos)) {
size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
if ((*vec).size() <= idx) { if ((*vec).size() <= idx) {
@ -1238,14 +1471,6 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
} }
} }
} }
if (Value *indices = FindUInt(primitive, "indices")) {
prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint());
}
if (Value *material = FindUInt(primitive, "material")) {
prim.material = pAsset_Root.materials.Retrieve(material->GetUint());
}
} }
} }
@ -1331,25 +1556,22 @@ inline void Light::Read(Value &obj, Asset & /*r*/) {
} }
} }
inline CustomExtension ReadExtensions(const char *name, Value& obj) { inline CustomExtension ReadExtensions(const char *name, Value &obj) {
CustomExtension ret; CustomExtension ret;
ret.name = name; ret.name = name;
if (obj.IsObject()) { if (obj.IsObject()) {
ret.mValues.isPresent = true; ret.mValues.isPresent = true;
for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
auto& val = it->value; auto &val = it->value;
ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val));
} }
} } else if (obj.IsArray()) {
else if (obj.IsArray()) {
ret.mValues.value.reserve(obj.Size()); ret.mValues.value.reserve(obj.Size());
ret.mValues.isPresent = true; ret.mValues.isPresent = true;
for (unsigned int i = 0; i < obj.Size(); ++i) for (unsigned int i = 0; i < obj.Size(); ++i) {
{
ret.mValues.value.push_back(ReadExtensions(name, obj[i])); ret.mValues.value.push_back(ReadExtensions(name, obj[i]));
} }
} } else if (obj.IsNumber()) {
else if (obj.IsNumber()) {
if (obj.IsUint64()) { if (obj.IsUint64()) {
ret.mUint64Value.value = obj.GetUint64(); ret.mUint64Value.value = obj.GetUint64();
ret.mUint64Value.isPresent = true; ret.mUint64Value.isPresent = true;
@ -1360,12 +1582,10 @@ inline CustomExtension ReadExtensions(const char *name, Value& obj) {
ret.mDoubleValue.value = obj.GetDouble(); ret.mDoubleValue.value = obj.GetDouble();
ret.mDoubleValue.isPresent = true; ret.mDoubleValue.isPresent = true;
} }
} } else if (obj.IsString()) {
else if (obj.IsString()) {
ReadValue(obj, ret.mStringValue); ReadValue(obj, ret.mStringValue);
ret.mStringValue.isPresent = true; ret.mStringValue.isPresent = true;
} } else if (obj.IsBool()) {
else if (obj.IsBool()) {
ret.mBoolValue.value = obj.GetBool(); ret.mBoolValue.value = obj.GetBool();
ret.mBoolValue.isPresent = true; ret.mBoolValue.isPresent = true;
} }
@ -1693,10 +1913,12 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
ReadExtensionsUsed(doc); ReadExtensionsUsed(doc);
ReadExtensionsRequired(doc); ReadExtensionsRequired(doc);
// Currently Draco is not supported #ifndef ASSIMP_ENABLE_DRACO
// Is Draco required?
if (extensionsRequired.KHR_draco_mesh_compression) { if (extensionsRequired.KHR_draco_mesh_compression) {
throw DeadlyImportError("GLTF: Draco mesh compression not currently supported."); throw DeadlyImportError("GLTF: Draco mesh compression not supported.");
} }
#endif
// Prepare the dictionaries // Prepare the dictionaries
for (size_t i = 0; i < mDicts.size(); ++i) { for (size_t i = 0; i < mDicts.size(); ++i) {
@ -1784,6 +2006,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
CHECK_EXT(KHR_materials_sheen); CHECK_EXT(KHR_materials_sheen);
CHECK_EXT(KHR_materials_clearcoat); CHECK_EXT(KHR_materials_clearcoat);
CHECK_EXT(KHR_materials_transmission); CHECK_EXT(KHR_materials_transmission);
CHECK_EXT(KHR_draco_mesh_compression);
#undef CHECK_EXT #undef CHECK_EXT
} }
@ -1792,12 +2015,12 @@ inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool /*abso
#ifdef ASSIMP_API #ifdef ASSIMP_API
return mIOSystem->Open(path, mode); return mIOSystem->Open(path, mode);
#else #else
if (path.size() < 2) return 0; if (path.size() < 2) return nullptr;
if (!absolute && path[1] != ':' && path[0] != '/') { // relative? if (!absolute && path[1] != ':' && path[0] != '/') { // relative?
path = mCurrentAssetDir + path; path = mCurrentAssetDir + path;
} }
FILE *f = fopen(path.c_str(), mode); FILE *f = fopen(path.c_str(), mode);
return f ? new IOStream(f) : 0; return f ? new IOStream(f) : nullptr;
#endif #endif
} }
@ -1831,7 +2054,7 @@ inline std::string Asset::FindUniqueID(const std::string &str, const char *suffi
} }
#if _MSC_VER #if _MSC_VER
# pragma warning(pop) #pragma warning(pop)
#endif // _MSC_VER #endif // _MSC_VER
} // namespace glTF2 } // namespace glTF2

View File

@ -571,7 +571,6 @@ namespace glTF2 {
inline void Write(Value& obj, Node& n, AssetWriter& w) inline void Write(Value& obj, Node& n, AssetWriter& w)
{ {
if (n.matrix.isPresent) { if (n.matrix.isPresent) {
Value val; Value val;
obj.AddMember("matrix", MakeValue(val, n.matrix.value, w.mAl).Move(), w.mAl); obj.AddMember("matrix", MakeValue(val, n.matrix.value, w.mAl).Move(), w.mAl);
@ -597,14 +596,13 @@ namespace glTF2 {
obj.AddMember("mesh", n.meshes[0]->index, w.mAl); obj.AddMember("mesh", n.meshes[0]->index, w.mAl);
} }
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
if (n.skin) { if (n.skin) {
obj.AddMember("skin", n.skin->index, w.mAl); obj.AddMember("skin", n.skin->index, w.mAl);
} }
if (!n.jointName.empty()) { //gltf2 spec does not support "skeletons" under node
obj.AddMember("jointName", n.jointName, w.mAl); if(n.skeletons.size()) {
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
} }
} }

View File

@ -1095,6 +1095,7 @@ void glTF2Exporter::ExportMeshes()
//---------------------------------------- //----------------------------------------
// Finish the skin // Finish the skin
// Create the Accessor for skinRef->inverseBindMatrices // Create the Accessor for skinRef->inverseBindMatrices
bool bAddCustomizedProperty = this->mProperties->HasPropertyBool("GLTF2_CUSTOMIZE_PROPERTY");
if (createSkin) { if (createSkin) {
mat4* invBindMatrixData = new mat4[inverseBindMatricesData.size()]; mat4* invBindMatrixData = new mat4[inverseBindMatricesData.size()];
for ( unsigned int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) { for ( unsigned int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) {
@ -1110,7 +1111,7 @@ void glTF2Exporter::ExportMeshes()
// Identity Matrix =====> skinRef->bindShapeMatrix // Identity Matrix =====> skinRef->bindShapeMatrix
// Temporary. Hard-coded identity matrix here // Temporary. Hard-coded identity matrix here
skinRef->bindShapeMatrix.isPresent = true; skinRef->bindShapeMatrix.isPresent = bAddCustomizedProperty;
IdentityMatrix4(skinRef->bindShapeMatrix.value); IdentityMatrix4(skinRef->bindShapeMatrix.value);
// Find nodes that contain a mesh with bones and add "skeletons" and "skin" attributes to those nodes. // Find nodes that contain a mesh with bones and add "skeletons" and "skin" attributes to those nodes.
@ -1131,6 +1132,7 @@ void glTF2Exporter::ExportMeshes()
std::string meshID = mesh->id; std::string meshID = mesh->id;
FindMeshNode(rootNode, meshNode, meshID); FindMeshNode(rootNode, meshNode, meshID);
Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef); Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef);
if(bAddCustomizedProperty)
meshNode->skeletons.push_back(rootJoint); meshNode->skeletons.push_back(rootJoint);
meshNode->skin = skinRef; meshNode->skin = skinRef;
} }
@ -1229,7 +1231,7 @@ unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref<Node>& parent)
node->name = name; node->name = name;
if (!n->mTransformation.IsIdentity()) { if (!n->mTransformation.IsIdentity()) {
if (mScene->mNumAnimations > 0) { if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) {
aiQuaternion quaternion; aiQuaternion quaternion;
n->mTransformation.Decompose(*reinterpret_cast<aiVector3D *>(&node->scale.value), quaternion, *reinterpret_cast<aiVector3D *>(&node->translation.value)); n->mTransformation.Decompose(*reinterpret_cast<aiVector3D *>(&node->scale.value), quaternion, *reinterpret_cast<aiVector3D *>(&node->translation.value));
@ -1386,6 +1388,7 @@ void glTF2Exporter::ExportAnimations()
nameAnim = anim->mName.C_Str(); nameAnim = anim->mName.C_Str();
} }
Ref<Animation> animRef = mAsset->animations.Create(nameAnim); Ref<Animation> animRef = mAsset->animations.Create(nameAnim);
animRef->name = nameAnim;
for (unsigned int channelIndex = 0; channelIndex < anim->mNumChannels; ++channelIndex) { for (unsigned int channelIndex = 0; channelIndex < anim->mNumChannels; ++channelIndex) {
const aiNodeAnim* nodeChannel = anim->mChannels[channelIndex]; const aiNodeAnim* nodeChannel = anim->mChannels[channelIndex];

View File

@ -383,6 +383,22 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
} }
#endif // ASSIMP_BUILD_DEBUG #endif // ASSIMP_BUILD_DEBUG
template<typename T>
aiColor4D* GetVertexColorsForType(glTF2::Ref<glTF2::Accessor> input) {
float max = std::numeric_limits<T>::max();
aiColor4t<T>* colors;
input->ExtractData(colors);
auto output = new aiColor4D[input->count];
for (size_t i = 0; i < input->count; i++) {
output[i] = aiColor4D(
colors[i].r / max, colors[i].g / max,
colors[i].b / max, colors[i].a / max
);
}
delete[] colors;
return output;
}
void glTF2Importer::ImportMeshes(glTF2::Asset &r) { void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
ASSIMP_LOG_DEBUG_F("Importing ", r.meshes.Size(), " meshes"); ASSIMP_LOG_DEBUG_F("Importing ", r.meshes.Size(), " meshes");
std::vector<std::unique_ptr<aiMesh>> meshes; std::vector<std::unique_ptr<aiMesh>> meshes;
@ -436,10 +452,16 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
} }
if (attr.normal.size() > 0 && attr.normal[0]) { if (attr.normal.size() > 0 && attr.normal[0]) {
if (attr.normal[0]->count != aim->mNumVertices) {
DefaultLogger::get()->warn("Normal count in mesh \"" + mesh.name + "\" does not match the vertex count, normals ignored.");
} else {
attr.normal[0]->ExtractData(aim->mNormals); attr.normal[0]->ExtractData(aim->mNormals);
// only extract tangents if normals are present // only extract tangents if normals are present
if (attr.tangent.size() > 0 && attr.tangent[0]) { if (attr.tangent.size() > 0 && attr.tangent[0]) {
if (attr.tangent[0]->count != aim->mNumVertices) {
DefaultLogger::get()->warn("Tangent count in mesh \"" + mesh.name + "\" does not match the vertex count, tangents ignored.");
} else {
// generate bitangents from normals and tangents according to spec // generate bitangents from normals and tangents according to spec
Tangent *tangents = nullptr; Tangent *tangents = nullptr;
@ -456,6 +478,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
delete[] tangents; delete[] tangents;
} }
} }
}
}
for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) { for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) {
if (attr.color[c]->count != aim->mNumVertices) { if (attr.color[c]->count != aim->mNumVertices) {
@ -463,7 +487,17 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
"\" does not match the vertex count"); "\" does not match the vertex count");
continue; continue;
} }
auto componentType = attr.color[c]->componentType;
if (componentType == glTF2::ComponentType_FLOAT) {
attr.color[c]->ExtractData(aim->mColors[c]); attr.color[c]->ExtractData(aim->mColors[c]);
} else {
if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]);
} else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) {
aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c]);
}
}
} }
for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) { for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
if (!attr.texcoord[tc]) { if (!attr.texcoord[tc]) {

View File

@ -43,7 +43,7 @@
# 3) Add libassimp using the file lists (eliminates duplication of file names between # 3) Add libassimp using the file lists (eliminates duplication of file names between
# source groups and library command) # source groups and library command)
# #
cmake_minimum_required( VERSION 3.0 ) cmake_minimum_required( VERSION 3.10 )
SET( HEADER_PATH ../include/assimp ) SET( HEADER_PATH ../include/assimp )
if(NOT ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM) if(NOT ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
@ -665,6 +665,10 @@ if (NOT ASSIMP_NO_EXPORT)
AssetLib/3MF/D3MFExporter.h AssetLib/3MF/D3MFExporter.h
AssetLib/3MF/D3MFExporter.cpp) AssetLib/3MF/D3MFExporter.cpp)
ADD_ASSIMP_EXPORTER( PBRT
Pbrt/PbrtExporter.h
Pbrt/PbrtExporter.cpp)
ADD_ASSIMP_EXPORTER( ASSJSON ADD_ASSIMP_EXPORTER( ASSJSON
AssetLib/Assjson/cencode.c AssetLib/Assjson/cencode.c
AssetLib/Assjson/cencode.h AssetLib/Assjson/cencode.h
@ -1117,6 +1121,11 @@ IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
INCLUDE_DIRECTORIES(${C4D_INCLUDES}) INCLUDE_DIRECTORIES(${C4D_INCLUDES})
ENDIF () ENDIF ()
IF (ASSIMP_BUILD_DRACO)
INCLUDE_DIRECTORIES(${draco_INCLUDE_DIRS})
ADD_DEFINITIONS( -DASSIMP_ENABLE_DRACO )
ENDIF()
ADD_LIBRARY( assimp ${assimp_src} ) ADD_LIBRARY( assimp ${assimp_src} )
ADD_LIBRARY(assimp::assimp ALIAS assimp) ADD_LIBRARY(assimp::assimp ALIAS assimp)
@ -1148,8 +1157,15 @@ IF(ASSIMP_HUNTER_ENABLED)
zip::zip zip::zip
pugixml pugixml
) )
if (ASSIMP_BUILD_DRACO)
target_link_libraries(assimp PUBLIC ${draco_LIBRARIES})
endif()
ELSE() ELSE()
TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES} ${OPENDDL_PARSER_LIBRARIES} ) TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES} ${OPENDDL_PARSER_LIBRARIES})
if (ASSIMP_BUILD_DRACO)
target_link_libraries(assimp ${draco_LIBRARIES})
endif()
ENDIF() ENDIF()
if(ASSIMP_ANDROID_JNIIOSYSTEM) if(ASSIMP_ANDROID_JNIIOSYSTEM)

View File

@ -138,6 +138,9 @@ void ExportSceneM3DA(const char*, IOSystem*, const aiScene*, const ExportPropert
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*); void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
#endif #endif
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
void ExportScenePbrt(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) { static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) {
(void)exporters; (void)exporters;
@ -221,6 +224,10 @@ static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporte
exporters.push_back(Exporter::ExportFormatEntry("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0)); exporters.push_back(Exporter::ExportFormatEntry("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0));
#endif #endif
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
exporters.push_back(Exporter::ExportFormatEntry("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_Triangulate | aiProcess_SortByPType));
#endif
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
exporters.push_back(Exporter::ExportFormatEntry("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0)); exporters.push_back(Exporter::ExportFormatEntry("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0));
#endif #endif

View File

@ -78,6 +78,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/Profiler.h> #include <assimp/Profiler.h>
#include <assimp/commonMetaData.h> #include <assimp/commonMetaData.h>
#include <exception>
#include <set> #include <set>
#include <memory> #include <memory>
#include <cctype> #include <cctype>

View File

@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef INCLUDED_AI_IMPORTER_H #ifndef INCLUDED_AI_IMPORTER_H
#define INCLUDED_AI_IMPORTER_H #define INCLUDED_AI_IMPORTER_H
#include <exception>
#include <map> #include <map>
#include <vector> #include <vector>
#include <string> #include <string>

View File

@ -0,0 +1,949 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/* TODO:
Material improvements:
- don't export embedded textures that we're not going to use
- diffuse roughness
- what is with the uv mapping, uv transform not coming through??
- metal? glass? mirror? detect these better?
- eta/k from RGB?
- emissive textures: warn at least
Other:
- use aiProcess_GenUVCoords if needed to handle spherical/planar uv mapping?
- don't build up a big string in memory but write directly to a file
- aiProcess_Triangulate meshes to get triangles only?
- animation (allow specifying a time)
*/
#ifndef ASSIMP_BUILD_NO_EXPORT
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
#include "PbrtExporter.h"
#include <assimp/version.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOSystem.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/StreamWriter.h>
#include <assimp/Exceptional.h>
#include <assimp/material.h>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace Assimp;
namespace Assimp {
void ExportScenePbrt (
const char* pFile,
IOSystem* pIOSystem,
const aiScene* pScene,
const ExportProperties* /*pProperties*/
){
std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
// initialize the exporter
PbrtExporter exporter(pScene, pIOSystem, path, file);
}
} // end of namespace Assimp
// Constructor
PbrtExporter::PbrtExporter (
const aiScene* pScene, IOSystem* pIOSystem,
const std::string path, const std::string file)
: mScene(pScene),
mIOSystem(pIOSystem),
mPath(path),
mFile(file)
{
// Export embedded textures.
if (mScene->mNumTextures > 0)
if (!mIOSystem->CreateDirectory("textures"))
throw DeadlyExportError("Could not create textures/ directory.");
for (unsigned int i = 0; i < mScene->mNumTextures; ++i) {
aiTexture* tex = mScene->mTextures[i];
std::string fn = CleanTextureFilename(tex->mFilename, false);
std::cerr << "Writing embedded texture: " << tex->mFilename.C_Str() << " -> "
<< fn << "\n";
std::unique_ptr<IOStream> outfile(mIOSystem->Open(fn, "wb"));
if (!outfile) {
throw DeadlyExportError("could not open output texture file: " + fn);
}
if (tex->mHeight == 0) {
// It's binary data
outfile->Write(tex->pcData, tex->mWidth, 1);
} else {
std::cerr << fn << ": TODO handle uncompressed embedded textures.\n";
}
}
#if 0
// Debugging: print the full node hierarchy
std::function<void(aiNode*, int)> visitNode;
visitNode = [&](aiNode* node, int depth) {
for (int i = 0; i < depth; ++i) std::cerr << " ";
std::cerr << node->mName.C_Str() << "\n";
for (int i = 0; i < node->mNumChildren; ++i)
visitNode(node->mChildren[i], depth + 1);
};
visitNode(mScene->mRootNode, 0);
#endif
mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
// Write everything out
WriteMetaData();
WriteCameras();
WriteWorldDefinition();
// And write the file to disk...
std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath,"wt"));
if (!outfile) {
throw DeadlyExportError("could not open output .pbrt file: " + std::string(mFile));
}
outfile->Write(mOutput.str().c_str(), mOutput.str().length(), 1);
}
// Destructor
PbrtExporter::~PbrtExporter() {
// Empty
}
void PbrtExporter::WriteMetaData() {
mOutput << "#############################\n";
mOutput << "# Scene metadata:\n";
aiMetadata* pMetaData = mScene->mMetaData;
for (unsigned int i = 0; i < pMetaData->mNumProperties; i++) {
mOutput << "# - ";
mOutput << pMetaData->mKeys[i].C_Str() << " :";
switch(pMetaData->mValues[i].mType) {
case AI_BOOL : {
mOutput << " ";
if (*static_cast<bool*>(pMetaData->mValues[i].mData))
mOutput << "TRUE\n";
else
mOutput << "FALSE\n";
break;
}
case AI_INT32 : {
mOutput << " " <<
*static_cast<int32_t*>(pMetaData->mValues[i].mData) <<
std::endl;
break;
}
case AI_UINT64 :
mOutput << " " <<
*static_cast<uint64_t*>(pMetaData->mValues[i].mData) <<
std::endl;
break;
case AI_FLOAT :
mOutput << " " <<
*static_cast<float*>(pMetaData->mValues[i].mData) <<
std::endl;
break;
case AI_DOUBLE :
mOutput << " " <<
*static_cast<double*>(pMetaData->mValues[i].mData) <<
std::endl;
break;
case AI_AISTRING : {
aiString* value =
static_cast<aiString*>(pMetaData->mValues[i].mData);
std::string svalue = value->C_Str();
std::size_t found = svalue.find_first_of("\n");
mOutput << "\n";
while (found != std::string::npos) {
mOutput << "# " << svalue.substr(0, found) << "\n";
svalue = svalue.substr(found + 1);
found = svalue.find_first_of("\n");
}
mOutput << "# " << svalue << "\n";
break;
}
case AI_AIVECTOR3D :
// TODO
mOutput << " Vector3D (unable to print)\n";
break;
default:
// AI_META_MAX and FORCE_32BIT
mOutput << " META_MAX or FORCE_32Bit (unable to print)\n";
break;
}
}
}
void PbrtExporter::WriteCameras() {
mOutput << "\n";
mOutput << "###############################\n";
mOutput << "# Cameras (" << mScene->mNumCameras << ") total\n\n";
if (mScene->mNumCameras == 0) {
std::cerr << "Warning: No cameras found in scene file.\n";
return;
}
if (mScene->mNumCameras > 1) {
std::cerr << "Multiple cameras found in scene file; defaulting to first one specified.\n";
}
for (unsigned int i = 0; i < mScene->mNumCameras; i++) {
WriteCamera(i);
}
}
aiMatrix4x4 PbrtExporter::GetNodeTransform(const aiString &name) const {
aiMatrix4x4 m;
auto node = mScene->mRootNode->FindNode(name);
if (!node) {
std::cerr << '"' << name.C_Str() << "\": node not found in scene tree.\n";
throw DeadlyExportError("Could not find node");
}
else {
while (node) {
m = node->mTransformation * m;
node = node->mParent;
}
}
return m;
}
std::string PbrtExporter::TransformAsString(const aiMatrix4x4 &m) {
// Transpose on the way out to match pbrt's expected layout (sanity
// check: the translation component should be the last 3 entries
// before a '1' as the final entry in the matrix, assuming it's
// non-projective.)
std::stringstream s;
s << m.a1 << " " << m.b1 << " " << m.c1 << " " << m.d1 << " "
<< m.a2 << " " << m.b2 << " " << m.c2 << " " << m.d2 << " "
<< m.a3 << " " << m.b3 << " " << m.c3 << " " << m.d3 << " "
<< m.a4 << " " << m.b4 << " " << m.c4 << " " << m.d4;
return s.str();
}
void PbrtExporter::WriteCamera(int i) {
auto camera = mScene->mCameras[i];
bool cameraActive = i == 0;
mOutput << "# - Camera " << i+1 << ": "
<< camera->mName.C_Str() << "\n";
// Get camera aspect ratio
float aspect = camera->mAspect;
if (aspect == 0) {
aspect = 4.f/3.f;
mOutput << "# - Aspect ratio : 1.33333 (no aspect found, defaulting to 4/3)\n";
} else {
mOutput << "# - Aspect ratio : " << aspect << "\n";
}
// Get Film xres and yres
int xres = 1920;
int yres = (int)round(xres/aspect);
// Print Film for this camera
if (!cameraActive)
mOutput << "# ";
mOutput << "Film \"rgb\" \"string filename\" \"" << mFile << ".exr\"\n";
if (!cameraActive)
mOutput << "# ";
mOutput << " \"integer xresolution\" [" << xres << "]\n";
if (!cameraActive)
mOutput << "# ";
mOutput << " \"integer yresolution\" [" << yres << "]\n";
// Get camera fov
float hfov = AI_RAD_TO_DEG(camera->mHorizontalFOV);
float fov = (aspect >= 1.0) ? hfov : (hfov * aspect);
if (fov < 5) {
std::cerr << fov << ": suspiciously low field of view specified by camera. Setting to 45 degrees.\n";
fov = 45;
}
// Get camera transform
aiMatrix4x4 worldFromCamera = GetNodeTransform(camera->mName);
// Print Camera LookAt
auto position = worldFromCamera * camera->mPosition;
auto lookAt = worldFromCamera * (camera->mPosition + camera->mLookAt);
aiMatrix3x3 worldFromCamera3(worldFromCamera);
auto up = worldFromCamera3 * camera->mUp;
up.Normalize();
if (!cameraActive)
mOutput << "# ";
mOutput << "Scale -1 1 1\n"; // right handed -> left handed
if (!cameraActive)
mOutput << "# ";
mOutput << "LookAt "
<< position.x << " " << position.y << " " << position.z << "\n";
if (!cameraActive)
mOutput << "# ";
mOutput << " "
<< lookAt.x << " " << lookAt.y << " " << lookAt.z << "\n";
if (!cameraActive)
mOutput << "# ";
mOutput << " "
<< up.x << " " << up.y << " " << up.z << "\n";
// Print camera descriptor
if (!cameraActive)
mOutput << "# ";
mOutput << "Camera \"perspective\" \"float fov\" " << "[" << fov << "]\n\n";
}
void PbrtExporter::WriteWorldDefinition() {
// Figure out which meshes are referenced multiple times; those will be
// emitted as object instances and the rest will be emitted directly.
std::map<int, int> meshUses;
std::function<void(aiNode*)> visitNode;
visitNode = [&](aiNode* node) {
for (unsigned int i = 0; i < node->mNumMeshes; ++i)
++meshUses[node->mMeshes[i]];
for (unsigned int i = 0; i < node->mNumChildren; ++i)
visitNode(node->mChildren[i]);
};
visitNode(mScene->mRootNode);
int nInstanced = 0, nUnused = 0;
for (const auto &u : meshUses) {
if (u.second == 0) ++nUnused;
else if (u.second > 1) ++nInstanced;
}
std::cerr << nInstanced << " / " << mScene->mNumMeshes << " meshes instanced.\n";
if (nUnused)
std::cerr << nUnused << " meshes defined but not used in scene.\n";
mOutput << "WorldBegin\n";
WriteLights();
WriteTextures();
WriteMaterials();
// Object instance definitions
mOutput << "# Object instance definitions\n\n";
for (const auto &mu : meshUses) {
if (mu.second > 1) {
WriteInstanceDefinition(mu.first);
}
}
mOutput << "# Geometry\n\n";
aiMatrix4x4 worldFromObject;
WriteGeometricObjects(mScene->mRootNode, worldFromObject, meshUses);
}
void PbrtExporter::WriteTextures() {
mOutput << "###################\n";
mOutput << "# Textures\n\n";
C_STRUCT aiString path;
aiTextureMapping mapping;
unsigned int uvIndex;
ai_real blend;
aiTextureOp op;
aiTextureMapMode mapMode[3];
// For every material in the scene,
for (unsigned int m = 0 ; m < mScene->mNumMaterials; m++) {
auto material = mScene->mMaterials[m];
// Parse through all texture types,
for (int tt = 1; tt <= aiTextureType_UNKNOWN; tt++) {
int ttCount = material->GetTextureCount(aiTextureType(tt));
// ... and get every texture
for (int t = 0; t < ttCount; t++) {
// TODO write out texture specifics
// TODO UV transforms may be material specific
// so those may need to be baked into unique tex name
if (material->GetTexture(aiTextureType(tt), t, &path, &mapping,
&uvIndex, &blend, &op, mapMode) != AI_SUCCESS) {
std::cerr << "Error getting texture! " << m << " " << tt << " " << t << "\n";
continue;
}
std::string filename = CleanTextureFilename(path);
if (uvIndex != 0)
std::cerr << "Warning: texture \"" << filename << "\" uses uv set #" <<
uvIndex << " but the pbrt converter only exports uv set 0.\n";
#if 0
if (op != aiTextureOp_Multiply)
std::cerr << "Warning: unexpected texture op " << (int)op <<
" encountered for texture \"" <<
filename << "\". The resulting scene may have issues...\n";
if (blend != 1)
std::cerr << "Blend value of " << blend << " found for texture \"" << filename
<< "\" but not handled in converter.\n";
#endif
std::string mappingString;
#if 0
if (mapMode[0] != mapMode[1])
std::cerr << "Different texture boundary mode for u and v for texture \"" <<
filename << "\". Using u for both.\n";
switch (mapMode[0]) {
case aiTextureMapMode_Wrap:
// pbrt's default
break;
case aiTextureMapMode_Clamp:
mappingString = "\"string wrap\" \"clamp\"";
break;
case aiTextureMapMode_Decal:
std::cerr << "Decal texture boundary mode not supported by pbrt for texture \"" <<
filename << "\"\n";
break;
case aiTextureMapMode_Mirror:
std::cerr << "Mirror texture boundary mode not supported by pbrt for texture \"" <<
filename << "\"\n";
break;
default:
std::cerr << "Unexpected map mode " << (int)mapMode[0] << " for texture \"" <<
filename << "\"\n";
//throw DeadlyExportError("Unexpected aiTextureMapMode");
}
#endif
#if 0
aiUVTransform uvTransform;
if (material->Get(AI_MATKEY_TEXTURE(tt, t), uvTransform) == AI_SUCCESS) {
mOutput << "# UV transform " << uvTransform.mTranslation.x << " "
<< uvTransform.mTranslation.y << " " << uvTransform.mScaling.x << " "
<< uvTransform.mScaling.y << " " << uvTransform.mRotation << "\n";
}
#endif
std::string texName, texType, texOptions;
if (aiTextureType(tt) == aiTextureType_SHININESS ||
aiTextureType(tt) == aiTextureType_OPACITY ||
aiTextureType(tt) == aiTextureType_HEIGHT ||
aiTextureType(tt) == aiTextureType_DISPLACEMENT ||
aiTextureType(tt) == aiTextureType_METALNESS ||
aiTextureType(tt) == aiTextureType_DIFFUSE_ROUGHNESS) {
texType = "float";
texName = std::string("float:") + RemoveSuffix(filename);
if (aiTextureType(tt) == aiTextureType_SHININESS) {
texOptions = " \"bool invert\" true\n";
texName += "_Roughness";
}
} else if (aiTextureType(tt) == aiTextureType_DIFFUSE ||
aiTextureType(tt) == aiTextureType_BASE_COLOR) {
texType = "spectrum";
texName = std::string("rgb:") + RemoveSuffix(filename);
}
// Don't export textures we're not actually going to use...
if (texName.empty())
continue;
if (mTextureSet.find(texName) == mTextureSet.end()) {
mOutput << "Texture \"" << texName << "\" \"" << texType << "\" \"imagemap\"\n"
<< texOptions
<< " \"string filename\" \"" << filename << "\" " << mappingString << '\n';
mTextureSet.insert(texName);
}
// Also emit a float version for use with alpha testing...
if ((aiTextureType(tt) == aiTextureType_DIFFUSE ||
aiTextureType(tt) == aiTextureType_BASE_COLOR) &&
TextureHasAlphaMask(filename)) {
texType = "float";
texName = std::string("alpha:") + filename;
if (mTextureSet.find(texName) == mTextureSet.end()) {
mOutput << "Texture \"" << texName << "\" \"" << texType << "\" \"imagemap\"\n"
<< " \"string filename\" \"" << filename << "\" " << mappingString << '\n';
mTextureSet.insert(texName);
}
}
}
}
}
}
bool PbrtExporter::TextureHasAlphaMask(const std::string &filename) {
// TODO: STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
// quick return if it's 3
int xSize, ySize, nComponents;
unsigned char *data = stbi_load(filename.c_str(), &xSize, &ySize, &nComponents, 0);
if (!data) {
std::cerr << filename << ": unable to load texture and check for alpha mask in texture. "
"Geometry will not be alpha masked with this texture.\n";
return false;
}
bool hasMask = false;
switch (nComponents) {
case 1:
for (int i = 0; i < xSize * ySize; ++i)
if (data[i] != 255) {
hasMask = true;
break;
}
break;
case 2:
for (int y = 0; y < ySize; ++y)
for (int x = 0; x < xSize; ++x)
if (data[2 * (x + y * xSize) + 1] != 255) {
hasMask = true;
break;
}
break;
case 3:
break;
case 4:
for (int y = 0; y < ySize; ++y)
for (int x = 0; x < xSize; ++x)
if (data[4 * (x + y * xSize) + 3] != 255) {
hasMask = true;
break;
}
break;
default:
std::cerr << filename << ": unexpected number of image channels, " <<
nComponents << ".\n";
}
stbi_image_free(data);
return hasMask;
}
void PbrtExporter::WriteMaterials() {
mOutput << "\n";
mOutput << "####################\n";
mOutput << "# Materials (" << mScene->mNumMaterials << ") total\n\n";
for (unsigned int i = 0; i < mScene->mNumMaterials; i++) {
WriteMaterial(i);
}
mOutput << "\n\n";
}
void PbrtExporter::WriteMaterial(int m) {
aiMaterial* material = mScene->mMaterials[m];
// get material name
auto materialName = material->GetName();
mOutput << std::endl << "# - Material " << m+1 << ": " << materialName.C_Str() << "\n";
// Print out number of properties
mOutput << "# - Number of Material Properties: " << material->mNumProperties << "\n";
// Print out texture type counts
mOutput << "# - Non-Zero Texture Type Counts: ";
for (int i = 1; i <= aiTextureType_UNKNOWN; i++) {
int count = material->GetTextureCount(aiTextureType(i));
if (count > 0)
mOutput << TextureTypeToString(aiTextureType(i)) << ": " << count << " ";
}
mOutput << "\n";
auto White = [](aiColor3D c) { return c.r == 1 && c.g == 1 && c.b == 1; };
auto Black = [](aiColor3D c) { return c.r == 0 && c.g == 0 && c.b == 0; };
aiColor3D diffuse, specular, transparency;
bool constantDiffuse = (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS &&
!White(diffuse));
bool constantSpecular = (material->Get(AI_MATKEY_COLOR_SPECULAR, specular) == AI_SUCCESS &&
!White(specular));
bool constantTransparency = (material->Get(AI_MATKEY_COLOR_TRANSPARENT, transparency) == AI_SUCCESS &&
!Black(transparency));
float opacity, shininess, shininessStrength, eta;
bool constantOpacity = (material->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS &&
opacity != 0);
bool constantShininess = material->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS;
bool constantShininessStrength = material->Get(AI_MATKEY_SHININESS_STRENGTH, shininessStrength) == AI_SUCCESS;
bool constantEta = (material->Get(AI_MATKEY_REFRACTI, eta) == AI_SUCCESS &&
eta != 1);
mOutput << "# - Constants: diffuse " << constantDiffuse << " specular " << constantSpecular <<
" transprency " << constantTransparency << " opacity " << constantOpacity <<
" shininess " << constantShininess << " shininess strength " << constantShininessStrength <<
" eta " << constantEta << "\n";
aiString roughnessMap;
if (material->Get(AI_MATKEY_TEXTURE_SHININESS(0), roughnessMap) == AI_SUCCESS) {
std::string roughnessTexture = std::string("float:") +
RemoveSuffix(CleanTextureFilename(roughnessMap)) + "_Roughness";
mOutput << "MakeNamedMaterial \"" << materialName.C_Str() << "\""
<< " \"string type\" \"coateddiffuse\"\n"
<< " \"texture roughness\" \"" << roughnessTexture << "\"\n";
} else if (constantShininess) {
// Assume plastic for now at least
float roughness = std::max(0.f, 1.f - shininess);
mOutput << "MakeNamedMaterial \"" << materialName.C_Str() << "\""
<< " \"string type\" \"coateddiffuse\"\n"
<< " \"float roughness\" " << roughness << "\n";
} else
// Diffuse
mOutput << "MakeNamedMaterial \"" << materialName.C_Str() << "\""
<< " \"string type\" \"diffuse\"\n";
aiString diffuseTexture;
if (material->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), diffuseTexture) == AI_SUCCESS)
mOutput << " \"texture reflectance\" \"rgb:" << RemoveSuffix(CleanTextureFilename(diffuseTexture)) << "\"\n";
else
mOutput << " \"rgb reflectance\" [ " << diffuse.r << " " << diffuse.g <<
" " << diffuse.b << " ]\n";
aiString displacementTexture, normalMap;
if (material->Get(AI_MATKEY_TEXTURE_NORMALS(0), displacementTexture) == AI_SUCCESS)
mOutput << " \"string normalmap\" \"" << CleanTextureFilename(displacementTexture) << "\"\n";
else if (material->Get(AI_MATKEY_TEXTURE_HEIGHT(0), displacementTexture) == AI_SUCCESS)
mOutput << " \"texture displacement\" \"float:" <<
RemoveSuffix(CleanTextureFilename(displacementTexture)) << "\"\n";
else if (material->Get(AI_MATKEY_TEXTURE_DISPLACEMENT(0), displacementTexture) == AI_SUCCESS)
mOutput << " \"texture displacement\" \"float:" <<
RemoveSuffix(CleanTextureFilename(displacementTexture)) << "\"\n";
}
std::string PbrtExporter::CleanTextureFilename(const aiString &f, bool rewriteExtension) const {
std::string fn = f.C_Str();
// Remove directory name
size_t offset = fn.find_last_of("/\\");
if (offset != std::string::npos) {
fn.erase(0, offset + 1);
}
// Expect all textures in textures
fn = std::string("textures") + mIOSystem->getOsSeparator() + fn;
// Rewrite extension for unsupported file formats.
if (rewriteExtension) {
offset = fn.rfind('.');
if (offset != std::string::npos) {
std::string extension = fn;
extension.erase(0, offset + 1);
std::transform(extension.begin(), extension.end(), extension.begin(),
[](unsigned char c) { return (char)std::tolower(c); });
if (extension != "tga" && extension != "exr" && extension != "png" &&
extension != "pfm" && extension != "hdr") {
std::string orig = fn;
fn.erase(offset + 1);
fn += "png";
// Does it already exist? Warn if not.
std::ifstream filestream(fn);
if (!filestream.good())
std::cerr << orig << ": must convert this texture to PNG.\n";
}
}
}
return fn;
}
std::string PbrtExporter::RemoveSuffix(std::string filename) {
size_t offset = filename.rfind('.');
if (offset != std::string::npos)
filename.erase(offset);
return filename;
}
void PbrtExporter::WriteLights() {
mOutput << "\n";
mOutput << "#################\n";
mOutput << "# Lights\n\n";
if (mScene->mNumLights == 0) {
// Skip the default light if no cameras and this is flat up geometry
if (mScene->mNumCameras > 0) {
std::cerr << "No lights specified. Using default infinite light.\n";
mOutput << "AttributeBegin\n";
mOutput << " # default light\n";
mOutput << " LightSource \"infinite\" \"blackbody L\" [6000 1]\n";
mOutput << "AttributeEnd\n\n";
}
} else {
for (unsigned int i = 0; i < mScene->mNumLights; ++i) {
const aiLight *light = mScene->mLights[i];
mOutput << "# Light " << light->mName.C_Str() << "\n";
mOutput << "AttributeBegin\n";
aiMatrix4x4 worldFromLight = GetNodeTransform(light->mName);
mOutput << " Transform [ " << TransformAsString(worldFromLight) << " ]\n";
aiColor3D color = light->mColorDiffuse + light->mColorSpecular;
if (light->mAttenuationConstant != 0)
color = color * (ai_real)(1. / light->mAttenuationConstant);
switch (light->mType) {
case aiLightSource_DIRECTIONAL: {
mOutput << " LightSource \"distant\"\n";
mOutput << " \"point3 from\" [ " << light->mPosition.x << " " <<
light->mPosition.y << " " << light->mPosition.z << " ]\n";
aiVector3D to = light->mPosition + light->mDirection;
mOutput << " \"point3 to\" [ " << to.x << " " << to.y << " " << to.z << " ]\n";
mOutput << " \"rgb L\" [ " << color.r << " " << color.g << " " << color.b << " ]\n";
break;
} case aiLightSource_POINT:
mOutput << " LightSource \"distant\"\n";
mOutput << " \"point3 from\" [ " << light->mPosition.x << " " <<
light->mPosition.y << " " << light->mPosition.z << " ]\n";
mOutput << " \"rgb L\" [ " << color.r << " " << color.g << " " << color.b << " ]\n";
break;
case aiLightSource_SPOT: {
mOutput << " LightSource \"spot\"\n";
mOutput << " \"point3 from\" [ " << light->mPosition.x << " " <<
light->mPosition.y << " " << light->mPosition.z << " ]\n";
aiVector3D to = light->mPosition + light->mDirection;
mOutput << " \"point3 to\" [ " << to.x << " " << to.y << " " << to.z << " ]\n";
mOutput << " \"rgb L\" [ " << color.r << " " << color.g << " " << color.b << " ]\n";
mOutput << " \"float coneangle\" [ " << AI_RAD_TO_DEG(light->mAngleOuterCone) << " ]\n";
mOutput << " \"float conedeltaangle\" [ " << AI_RAD_TO_DEG(light->mAngleOuterCone -
light->mAngleInnerCone) << " ]\n";
break;
} case aiLightSource_AMBIENT:
mOutput << "# ignored ambient light source\n";
break;
case aiLightSource_AREA: {
aiVector3D left = light->mDirection ^ light->mUp;
// rectangle. center at position, direction is normal vector
float dLeft = light->mSize.x / 2, dUp = light->mSize.y / 2;
aiVector3D vertices[4] = {
light->mPosition - dLeft * left - dUp * light->mUp,
light->mPosition + dLeft * left - dUp * light->mUp,
light->mPosition - dLeft * left + dUp * light->mUp,
light->mPosition + dLeft * left + dUp * light->mUp };
mOutput << " AreaLightSource \"diffuse\"\n";
mOutput << " \"rgb L\" [ " << color.r << " " << color.g << " " << color.b << " ]\n";
mOutput << " Shape \"bilinearmesh\"\n";
mOutput << " \"point3 p\" [ ";
for (int j = 0; j < 4; ++j)
mOutput << vertices[j].x << " " << vertices[j].y << " " << vertices[j].z;
mOutput << " ]\n";
mOutput << " \"integer indices\" [ 0 1 2 3 ]\n";
break;
} default:
mOutput << "# ignored undefined light source type\n";
break;
}
mOutput << "AttributeEnd\n\n";
}
}
}
void PbrtExporter::WriteMesh(aiMesh* mesh) {
mOutput << "# - Mesh: ";
if (mesh->mName == aiString(""))
mOutput << "<No Name>\n";
else
mOutput << mesh->mName.C_Str() << "\n";
mOutput << "AttributeBegin\n";
aiMaterial* material = mScene->mMaterials[mesh->mMaterialIndex];
mOutput << " NamedMaterial \"" << material->GetName().C_Str() << "\"\n";
// Handle area lights
aiColor3D emission;
if (material->Get(AI_MATKEY_COLOR_EMISSIVE, emission) == AI_SUCCESS &&
(emission.r > 0 || emission.g > 0 || emission.b > 0))
mOutput << " AreaLightSource \"diffuse\" \"rgb L\" [ " << emission.r <<
" " << emission.g << " " << emission.b << " ]\n";
// Check if any types other than tri
if ( (mesh->mPrimitiveTypes & aiPrimitiveType_POINT)
|| (mesh->mPrimitiveTypes & aiPrimitiveType_LINE)
|| (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
std::cerr << "Error: ignoring point / line / polygon mesh " << mesh->mName.C_Str() << ".\n";
return;
}
// Alpha mask
std::string alpha;
aiString opacityTexture;
if (material->Get(AI_MATKEY_TEXTURE_OPACITY(0), opacityTexture) == AI_SUCCESS ||
material->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), opacityTexture) == AI_SUCCESS) {
// material->Get(AI_MATKEY_TEXTURE_BASE_COLOR(0), opacityTexture) == AI_SUCCESS)
std::string texName = std::string("alpha:") + CleanTextureFilename(opacityTexture);
if (mTextureSet.find(texName) != mTextureSet.end())
alpha = std::string(" \"texture alpha\" \"") + texName + "\"\n";
} else {
float opacity = 1;
if (material->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS && opacity < 1)
alpha = std::string(" \"float alpha\" [ ") + std::to_string(opacity) + " ]\n";
}
// Output the shape specification
mOutput << "Shape \"trianglemesh\"\n" <<
alpha <<
" \"integer indices\" [";
// Start with faces (which hold indices)
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
auto face = mesh->mFaces[i];
if (face.mNumIndices != 3) throw DeadlyExportError("oh no not a tri!");
for (unsigned int j = 0; j < face.mNumIndices; j++) {
mOutput << face.mIndices[j] << " ";
}
if ((i % 7) == 6) mOutput << "\n ";
}
mOutput << "]\n";
// Then go to vertices
mOutput << " \"point3 P\" [";
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
auto vector = mesh->mVertices[i];
mOutput << vector.x << " " << vector.y << " " << vector.z << " ";
if ((i % 4) == 3) mOutput << "\n ";
}
mOutput << "]\n";
// Normals (if present)
if (mesh->mNormals) {
mOutput << " \"normal N\" [";
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
auto normal = mesh->mNormals[i];
mOutput << normal.x << " " << normal.y << " " << normal.z << " ";
if ((i % 4) == 3) mOutput << "\n ";
}
mOutput << "]\n";
}
// Tangents (if present)
if (mesh->mTangents) {
mOutput << " \"vector3 S\" [";
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
auto tangent = mesh->mTangents[i];
mOutput << tangent.x << " " << tangent.y << " " << tangent.z << " ";
if ((i % 4) == 3) mOutput << "\n ";
}
mOutput << "]\n";
}
// Texture Coords (if present)
// Find the first set of 2D texture coordinates..
for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (mesh->mNumUVComponents[i] == 2) {
// assert(mesh->mTextureCoords[i] != nullptr);
aiVector3D* uv = mesh->mTextureCoords[i];
mOutput << " \"point2 uv\" [";
for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
mOutput << uv[j].x << " " << uv[j].y << " ";
if ((j % 6) == 5) mOutput << "\n ";
}
mOutput << "]\n";
break;
}
}
// TODO: issue warning if there are additional UV sets?
mOutput << "AttributeEnd\n";
}
void PbrtExporter::WriteInstanceDefinition(int i) {
aiMesh* mesh = mScene->mMeshes[i];
mOutput << "ObjectBegin \"";
if (mesh->mName == aiString(""))
mOutput << "mesh_" << i+1 << "\"\n";
else
mOutput << mesh->mName.C_Str() << "_" << i+1 << "\"\n";
WriteMesh(mesh);
mOutput << "ObjectEnd\n";
}
void PbrtExporter::WriteGeometricObjects(aiNode* node, aiMatrix4x4 worldFromObject,
std::map<int, int> &meshUses) {
// Sometimes interior nodes have degenerate matrices??
if (node->mTransformation.Determinant() != 0)
worldFromObject = worldFromObject * node->mTransformation;
if (node->mNumMeshes > 0) {
mOutput << "AttributeBegin\n";
mOutput << " Transform [ " << TransformAsString(worldFromObject) << "]\n";
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
aiMesh* mesh = mScene->mMeshes[node->mMeshes[i]];
if (meshUses[node->mMeshes[i]] == 1) {
// If it's only used once in the scene, emit it directly as
// a triangle mesh.
mOutput << " # " << mesh->mName.C_Str();
WriteMesh(mesh);
} else {
// If it's used multiple times, there will be an object
// instance for it, so emit a reference to that.
mOutput << " ObjectInstance \"";
if (mesh->mName == aiString(""))
mOutput << "mesh_" << node->mMeshes[i] + 1 << "\"\n";
else
mOutput << mesh->mName.C_Str() << "_" << node->mMeshes[i] + 1 << "\"\n";
}
}
mOutput << "AttributeEnd\n\n";
}
// Recurse through children
for (unsigned int i = 0; i < node->mNumChildren; i++) {
WriteGeometricObjects(node->mChildren[i], worldFromObject, meshUses);
}
}
#endif // ASSIMP_BUILD_NO_PBRT_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -0,0 +1,135 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file PbrtExporter.h
* Declares the exporter class to write a scene to a pbrt file
*/
#ifndef AI_PBRTEXPORTER_H_INC
#define AI_PBRTEXPORTER_H_INC
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
#include <assimp/types.h>
#include <assimp/StreamWriter.h>
#include <assimp/Exceptional.h>
#include <map>
#include <set>
#include <string>
#include <sstream>
struct aiScene;
struct aiNode;
struct aiMaterial;
struct aiMesh;
namespace Assimp {
class IOSystem;
class IOStream;
class ExportProperties;
// ---------------------------------------------------------------------
/** Helper class to export a given scene to a Pbrt file. */
// ---------------------------------------------------------------------
class PbrtExporter
{
public:
/// Constructor for a specific scene to export
PbrtExporter(const aiScene* pScene, IOSystem* pIOSystem,
const std::string path, const std::string file);
/// Destructor
virtual ~PbrtExporter();
private:
// the scene to export
const aiScene* mScene;
/// Stringstream to write all output into
std::stringstream mOutput;
/// The IOSystem for output
IOSystem* mIOSystem;
/// Path of the directory where the scene will be exported
const std::string mPath;
/// Name of the file (without extension) where the scene will be exported
const std::string mFile;
private:
// A private set to keep track of which textures have been declared
std::set<std::string> mTextureSet;
aiMatrix4x4 GetNodeTransform(const aiString& name) const;
static std::string TransformAsString(const aiMatrix4x4& m);
static std::string RemoveSuffix(std::string filename);
std::string CleanTextureFilename(const aiString &f, bool rewriteExtension = true) const;
void WriteMetaData();
void WriteWorldDefinition();
void WriteCameras();
void WriteCamera(int i);
void WriteLights();
void WriteTextures();
static bool TextureHasAlphaMask(const std::string &filename);
void WriteMaterials();
void WriteMaterial(int i);
void WriteMesh(aiMesh* mesh);
void WriteInstanceDefinition(int i);
void WriteGeometricObjects(aiNode* node, aiMatrix4x4 parentTransform,
std::map<int, int> &meshUses);
};
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_PBRT_EXPORTER
#endif // AI_PBRTEXPORTER_H_INC

7756
code/Pbrt/stb_image.h 100644

File diff suppressed because it is too large Load Diff

View File

@ -124,13 +124,13 @@ void ArmaturePopulate::BuildBoneList(aiNode *current_node,
for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) { for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) {
aiBone *bone = mesh->mBones[boneId]; aiBone *bone = mesh->mBones[boneId];
ai_assert(bone); ai_assert(nullptr != bone);
// duplicate mehes exist with the same bones sometimes :) // duplicate mehes exist with the same bones sometimes :)
// so this must be detected // so this must be detected
if (std::find(bones.begin(), bones.end(), bone) == bones.end()) { if (std::find(bones.begin(), bones.end(), bone) == bones.end()) {
// add the element once // add the element once
bones.push_back(bone); bones.emplace_back(bone);
} }
} }
@ -145,14 +145,14 @@ void ArmaturePopulate::BuildBoneList(aiNode *current_node,
// Prepare flat node list which can be used for non recursive lookups later // Prepare flat node list which can be used for non recursive lookups later
void ArmaturePopulate::BuildNodeList(const aiNode *current_node, void ArmaturePopulate::BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes) { std::vector<aiNode *> &nodes) {
ai_assert(current_node); ai_assert(nullptr != current_node);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId]; aiNode *child = current_node->mChildren[nodeId];
ai_assert(child); ai_assert(child);
if (child->mNumMeshes == 0) { if (child->mNumMeshes == 0) {
nodes.push_back(child); nodes.emplace_back(child);
} }
BuildNodeList(child, nodes); BuildNodeList(child, nodes);
@ -168,8 +168,10 @@ void ArmaturePopulate::BuildBoneStack(aiNode *,
const std::vector<aiBone *> &bones, const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack, std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack) { std::vector<aiNode *> &node_stack) {
ai_assert(root_node); if (node_stack.empty()) {
ai_assert(!node_stack.empty()); return;
}
ai_assert(nullptr != root_node);
for (aiBone *bone : bones) { for (aiBone *bone : bones) {
ai_assert(bone); ai_assert(bone);
@ -181,7 +183,7 @@ void ArmaturePopulate::BuildBoneStack(aiNode *,
node = GetNodeFromStack(bone->mName, node_stack); node = GetNodeFromStack(bone->mName, node_stack);
if (!node) { if (nullptr == node) {
ASSIMP_LOG_ERROR("serious import issue node for bone was not detected"); ASSIMP_LOG_ERROR("serious import issue node for bone was not detected");
continue; continue;
} }
@ -199,7 +201,7 @@ void ArmaturePopulate::BuildBoneStack(aiNode *,
// points. (yet) // points. (yet)
aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list) { std::vector<aiBone *> &bone_list) {
while (bone_node) { while (nullptr != bone_node) {
if (!IsBoneNode(bone_node->mName, bone_list)) { if (!IsBoneNode(bone_node->mName, bone_list)) {
ASSIMP_LOG_VERBOSE_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str()); ASSIMP_LOG_VERBOSE_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str());
return bone_node; return bone_node;
@ -236,7 +238,7 @@ aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name,
aiNode *found = nullptr; aiNode *found = nullptr;
for (iter = nodes.begin(); iter < nodes.end(); ++iter) { for (iter = nodes.begin(); iter < nodes.end(); ++iter) {
aiNode *element = *iter; aiNode *element = *iter;
ai_assert(element); ai_assert(nullptr != element);
// node valid and node name matches // node valid and node name matches
if (element->mName == node_name) { if (element->mName == node_name) {
found = element; found = element;

View File

@ -844,7 +844,8 @@ void ValidateDSProcess::Validate(const aiAnimation *pAnimation,
Validate(&pMeshMorphAnim->mName); Validate(&pMeshMorphAnim->mName);
if (!pMeshMorphAnim->mNumKeys) { if (!pMeshMorphAnim->mNumKeys) {
ReportError("Empty mesh morph animation channel"); ReportWarning("Empty mesh morph animation channel");
return;
} }
// otherwise check whether one of the keys exceeds the total duration of the animation // otherwise check whether one of the keys exceeds the total duration of the animation

View File

@ -0,0 +1,5 @@
---
Language: Cpp
BasedOnStyle: Google
PointerAlignment: Right
...

View File

@ -0,0 +1,102 @@
# Generated with cmake-format 0.5.1
# How wide to allow formatted cmake files
line_width = 80
# How many spaces to tab for indent
tab_size = 2
# If arglists are longer than this, break them always
max_subargs_per_line = 10
# If true, separate flow control names from their parentheses with a space
separate_ctrl_name_with_space = False
# If true, separate function names from parentheses with a space
separate_fn_name_with_space = False
# If a statement is wrapped to more than one line, than dangle the closing
# parenthesis on its own line
dangle_parens = False
# What character to use for bulleted lists
bullet_char = '*'
# What character to use as punctuation after numerals in an enumerated list
enum_char = '.'
# What style line endings to use in the output.
line_ending = u'unix'
# Format command names consistently as 'lower' or 'upper' case
command_case = u'lower'
# Format keywords consistently as 'lower' or 'upper' case
keyword_case = u'unchanged'
# Specify structure for custom cmake functions
additional_commands = {
"foo": {
"flags": [
"BAR",
"BAZ"
],
"kwargs": {
"HEADERS": "*",
"DEPENDS": "*",
"SOURCES": "*"
}
}
}
# A list of command names which should always be wrapped
always_wrap = []
# Specify the order of wrapping algorithms during successive reflow attempts
algorithm_order = [0, 1, 2, 3, 4]
# If true, the argument lists which are known to be sortable will be sorted
# lexicographicall
autosort = False
# enable comment markup parsing and reflow
enable_markup = True
# If comment markup is enabled, don't reflow the first comment block in
# eachlistfile. Use this to preserve formatting of your
# copyright/licensestatements.
first_comment_is_literal = False
# If comment markup is enabled, don't reflow any comment block which matchesthis
# (regex) pattern. Default is `None` (disabled).
literal_comment_pattern = None
# Regular expression to match preformat fences in comments
# default=r'^\s*([`~]{3}[`~]*)(.*)$'
fence_pattern = u'^\\s*([`~]{3}[`~]*)(.*)$'
# Regular expression to match rulers in comments
# default=r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'
ruler_pattern = u'^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$'
# If true, emit the unicode byte-order mark (BOM) at the start of the file
emit_byteorder_mark = False
# If a comment line starts with at least this many consecutive hash characters,
# then don't lstrip() them off. This allows for lazy hash rulers where the first
# hash char is not separated by space
hashruler_min_length = 10
# If true, then insert a space between the first hash char and remaining hash
# chars in a hash ruler, and normalize its length to fill the column
canonicalize_hashrulers = True
# Specify the encoding of the input file. Defaults to utf-8.
input_encoding = u'utf-8'
# Specify the encoding of the output file. Defaults to utf-8. Note that cmake
# only claims to support utf-8 so be careful when using anything else
output_encoding = u'utf-8'
# A dictionary containing any per-command configuration overrides. Currently
# only `command_case` is supported.
per_command = {}

1
contrib/draco/.gitignore vendored 100644
View File

@ -0,0 +1 @@
docs/_site

View File

@ -0,0 +1 @@
2.3.0

View File

@ -0,0 +1,31 @@
cache: ccache
language: cpp
matrix:
include:
- os: linux
dist: xenial
compiler: clang
- os: linux
dist: xenial
compiler: gcc
- os: osx
compiler: clang
addons:
apt:
packages:
- cmake
script:
# Output version info for compilers, cmake, and make
- ${CC} -v
- ${CXX} -v
- cmake --version
- make --version
# Clone googletest
- pushd .. && git clone https://github.com/google/googletest.git && popd
# Configure and build
- mkdir _travis_build && cd _travis_build
- cmake -G "Unix Makefiles" -DENABLE_TESTS=ON ..
- make -j10
- ./draco_tests

View File

@ -0,0 +1,7 @@
# This is the list of Draco authors for copyright purposes.
#
# This does not necessarily list everyone who has contributed code, since in
# some cases, their employer may be the copyright holder. To see the full list
# of contributors, see the revision history in source control.
Google Inc.
and other contributors

View File

@ -0,0 +1,301 @@
_**Contents**_
* [CMake Basics](#cmake-basics)
* [Mac OS X](#mac-os-x)
* [Windows](#windows)
* [CMake Build Configuration](#cmake-build-configuration)
* [Debugging and Optimization](#debugging-and-optimization)
* [Googletest Integration](#googletest-integration)
* [Javascript Encoder/Decoder](#javascript-encoderdecoder)
* [WebAssembly Decoder](#webassembly-decoder)
* [WebAssembly Mesh Only Decoder](#webassembly-mesh-only-decoder)
* [WebAssembly Point Cloud Only Decoder](#webassembly-point-cloud-only-decoder)
* [iOS Builds](#ios-builds)
* [Android Studio Project Integration](#android-studio-project-integration)
* [Native Android Builds](#native-android-builds)
* [vcpkg](#vcpkg)
Building
========
For all platforms, you must first generate the project/make files and then
compile the examples.
CMake Basics
------------
To generate project/make files for the default toolchain on your system, run
`cmake` from a directory where you would like to generate build files, and pass
it the path to your Draco repository.
E.g. Starting from Draco root.
~~~~~ bash
$ mkdir build_dir && cd build_dir
$ cmake ../
~~~~~
On Windows, the above command will produce Visual Studio project files for the
newest Visual Studio detected on the system. On Mac OS X and Linux systems,
the above command will produce a `makefile`.
To control what types of projects are generated, add the `-G` parameter to the
`cmake` command. This argument must be followed by the name of a generator.
Running `cmake` with the `--help` argument will list the available
generators for your system.
Mac OS X
---------
On Mac OS X, run the following command to generate Xcode projects:
~~~~~ bash
$ cmake ../ -G Xcode
~~~~~
Windows
-------
On a Windows box you would run the following command to generate Visual Studio
2019 projects:
~~~~~ bash
C:\Users\nobody> cmake ../ -G "Visual Studio 16 2019" -A Win32
~~~~~
To generate 64-bit Windows Visual Studio 2019 projects:
~~~~~ bash
C:\Users\nobody> cmake ../ -G "Visual Studio 16 2019" -A x64
~~~~~
CMake Build Configuration
-------------------------
Debugging and Optimization
--------------------------
Unlike Visual Studio and Xcode projects, the build configuration for make
builds is controlled when you run `cmake`. The following examples demonstrate
various build configurations.
Omitting the build type produces makefiles that use release build flags
by default:
~~~~~ bash
$ cmake ../
~~~~~
A makefile using release (optimized) flags is produced like this:
~~~~~ bash
$ cmake ../ -DCMAKE_BUILD_TYPE=Release
~~~~~
A release build with debug info can be produced as well:
~~~~~ bash
$ cmake ../ -DCMAKE_BUILD_TYPE=RelWithDebInfo
~~~~~
And your standard debug build will be produced using:
~~~~~ bash
$ cmake ../ -DCMAKE_BUILD_TYPE=Debug
~~~~~
To enable the use of sanitizers when the compiler in use supports them, set the
sanitizer type when running CMake:
~~~~~ bash
$ cmake ../ -DDRACO_SANITIZE=address
~~~~~
Googletest Integration
----------------------
Draco includes testing support built using Googletest. To enable Googletest unit
test support the DRACO_TESTS cmake variable must be turned on at cmake
generation time:
~~~~~ bash
$ cmake ../ -DDRACO_TESTS=ON
~~~~~
When cmake is used as shown in the above example the googletest directory must
be a sibling of the Draco repository root directory. To run the tests execute
`draco_tests` from your build output directory.
WebAssembly Decoder
-------------------
The WebAssembly decoder can be built using the existing cmake build file by
passing the path the Emscripten's cmake toolchain file at cmake generation time
in the CMAKE_TOOLCHAIN_FILE variable and enabling the WASM build option.
In addition, the EMSCRIPTEN environment variable must be set to the local path
of the parent directory of the Emscripten tools directory.
~~~~~ bash
# Make the path to emscripten available to cmake.
$ export EMSCRIPTEN=/path/to/emscripten/tools/parent
# Emscripten.cmake can be found within your Emscripten installation directory,
# it should be the subdir: cmake/Modules/Platform/Emscripten.cmake
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=/path/to/Emscripten.cmake -DDRACO_WASM=ON
# Build the WebAssembly decoder.
$ make
# Run the Javascript wrapper through Closure.
$ java -jar closure.jar --compilation_level SIMPLE --js draco_decoder.js --js_output_file draco_wasm_wrapper.js
~~~~~
WebAssembly Mesh Only Decoder
-----------------------------
~~~~~ bash
# cmake command line for mesh only WebAssembly decoder.
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=/path/to/Emscripten.cmake -DDRACO_WASM=ON -DDRACO_POINT_CLOUD_COMPRESSION=OFF
~~~~~
WebAssembly Point Cloud Only Decoder
-----------------------------
~~~~~ bash
# cmake command line for point cloud only WebAssembly decoder.
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=/path/to/Emscripten.cmake -DDRACO_WASM=ON -DDRACO_MESH_COMPRESSION=OFF
~~~~~
Javascript Encoder/Decoder
------------------
The javascript encoder and decoder can be built using the existing cmake build
file by passing the path the Emscripten's cmake toolchain file at cmake
generation time in the CMAKE_TOOLCHAIN_FILE variable.
In addition, the EMSCRIPTEN environment variable must be set to the local path
of the parent directory of the Emscripten tools directory.
*Note* The WebAssembly decoder should be favored over the JavaScript decoder.
~~~~~ bash
# Make the path to emscripten available to cmake.
$ export EMSCRIPTEN=/path/to/emscripten/tools/parent
# Emscripten.cmake can be found within your Emscripten installation directory,
# it should be the subdir: cmake/Modules/Platform/Emscripten.cmake
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=/path/to/Emscripten.cmake
# Build the Javascript encoder and decoder.
$ make
~~~~~
iOS Builds
---------------------
These are the basic commands needed to build Draco for iOS targets.
~~~~~ bash
#arm64
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/arm64-ios.cmake
$ make
#x86_64
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/x86_64-ios.cmake
$ make
#armv7
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/armv7-ios.cmake
$ make
#i386
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/i386-ios.cmake
$ make
~~~~~~
After building for each target the libraries can be merged into a single
universal/fat library using lipo, and then used in iOS applications.
Native Android Builds
---------------------
It's sometimes useful to build Draco command line tools and run them directly on
Android devices via adb.
~~~~~ bash
# This example is for armeabi-v7a.
$ cmake ../ -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/android.cmake \
-DDRACO_ANDROID_NDK_PATH=path/to/ndk -DANDROID_ABI=armeabi-v7a
$ make
# See the android.cmake toolchain file for additional ANDROID_ABI options and
# other configurable Android variables.
~~~~~
After building the tools they can be moved to an android device via the use of
`adb push`, and then run within an `adb shell` instance.
Android Studio Project Integration
----------------------------------
Tested on Android Studio 3.5.3.
Draco - Static Library
----------------------
To include Draco in an existing or new Android Studio project, reference it
from the `cmake` file of an existing native project that has a minimum SDK
version of 18 or higher. The project must support C++11.
To add Draco to your project:
1. Create a new "Native C++" project.
2. Add the following somewhere within the `CMakeLists.txt` for your project
before the `add_library()` for your project's native-lib:
~~~~~ cmake
# Note "/path/to/draco" must be changed to the path where you have cloned
# the Draco sources.
add_subdirectory(/path/to/draco
${CMAKE_BINARY_DIR}/draco_build)
include_directories("${CMAKE_BINARY_DIR}" /path/to/draco)
~~~~~
3. Add the library target "draco" to the `target_link_libraries()` call for
your project's native-lib. The `target_link_libraries()` call for an
empty activity native project looks like this after the addition of
Draco:
~~~~~ cmake
target_link_libraries( # Specifies the target library.
native-lib
# Tells cmake this build depends on libdraco.
draco
# Links the target library to the log library
# included in the NDK.
${log-lib} )
vcpkg
---------------------
You can download and install Draco using the
[vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
vcpkg install draco
The Draco port in vcpkg is kept up to date by Microsoft team members and
community contributors. If the version is out of date, please
[create an issue or pull request](https://github.com/Microsoft/vcpkg) on the
vcpkg repository.

View File

@ -0,0 +1,106 @@
# CMake Build System Overview
[TOC]
This document provides a general layout of the Draco CMake build system.
## Core Build System Files
These files are listed in order of interest to maintainers of the build system.
- `CMakeLists.txt` is the main driver of the build system. It's responsible
for defining targets and source lists, surfacing build system options, and
tying the components of the build system together.
- `cmake/draco_build_definitions.cmake` defines the macro
`draco_set_build_definitions()`, which is called from `CMakeLists.txt` to
configure include paths, compiler and linker flags, library settings,
platform speficic configuration, and other build system settings that
depend on optional build configurations.
- `cmake/draco_targets.cmake` defines the macros `draco_add_library()` and
`draco_add_executable()` which are used to create all targets in the CMake
build. These macros attempt to behave in a manner that loosely mirrors the
blaze `cc_library()` and `cc_binary()` commands. Note that
`draco_add_executable()` is also used for tests.
- `cmake/draco_emscripten.cmake` handles Emscripten SDK integration. It
defines several Emscripten specific macros that are required to build the
Emscripten specific targets defined in `CMakeLists.txt`.
- `cmake/draco_flags.cmake` defines macros related to compiler and linker
flags. Testing macros, macros for isolating flags to specific source files,
and the main flag configuration function for the library are defined here.
- `cmake/draco_options.cmake` defines macros that control optional features
of draco, and help track draco library and build system options.
- `cmake/draco_install.cmake` defines the draco install target.
- `cmake/draco_cpu_detection.cmake` determines the optimization types to
enable based on target system processor as reported by CMake.
- `cmake/draco_intrinsics.cmake` manages flags for source files that use
intrinsics. It handles detection of whether flags are necessary, and the
application of the flags to the sources that need them when they are
required.
## Helper and Utility Files
- `.cmake-format.py` Defines coding style for cmake-format.
- `cmake/draco_helpers.cmake` defines utility macros.
- `cmake/draco_sanitizer.cmake` defines the `draco_configure_sanitizer()`
macro, which implements support for `DRACO_SANITIZE`. It handles the
compiler and linker flags necessary for using sanitizers like asan and msan.
- `cmake/draco_variables.cmake` defines macros for tracking and control of
draco build system variables.
## Toolchain Files
These files help facilitate cross compiling of draco for various targets.
- `cmake/toolchains/aarch64-linux-gnu.cmake` provides cross compilation
support for arm64 targets.
- `cmake/toolchains/android.cmake` provides cross compilation support for
Android targets.
- `cmake/toolchains/arm-linux-gnueabihf.cmake` provides cross compilation
support for armv7 targets.
- `cmake/toolchains/arm64-ios.cmake`, `cmake/toolchains/armv7-ios.cmake`,
and `cmake/toolchains/armv7s-ios.cmake` provide support for iOS.
- `cmake/toolchains/arm64-linux-gcc.cmake` and
`cmake/toolchains/armv7-linux-gcc.cmake` are deprecated, but remain for
compatibility. `cmake/toolchains/android.cmake` should be used instead.
- `cmake/toolchains/arm64-android-ndk-libcpp.cmake`,
`cmake/toolchains/armv7-android-ndk-libcpp.cmake`,
`cmake/toolchains/x86-android-ndk-libcpp.cmake`, and
`cmake/toolchains/x86_64-android-ndk-libcpp.cmake` are deprecated, but
remain for compatibility. `cmake/toolchains/android.cmake` should be used
instead.
- `cmake/toolchains/i386-ios.cmake` and `cmake/toolchains/x86_64-ios.cmake`
provide support for the iOS simulator.
- `cmake/toolchains/android-ndk-common.cmake` and
`cmake/toolchains/arm-ios-common.cmake` are support files used by other
toolchain files.
## Template Files
These files are inputs to the CMake build and are used to generate inputs to the
build system output by CMake.
- `cmake/draco-config.cmake.template` is used to produce
draco-config.cmake. draco-config.cmake can be used by CMake to find draco
when another CMake project depends on draco.
- `cmake/draco.pc.template` is used to produce draco's pkg-config file.
Some build systems use pkg-config to configure include and library paths
when they depend upon third party libraries like draco.

View File

@ -0,0 +1,958 @@
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
# Draco requires C++11.
set(CMAKE_CXX_STANDARD 11)
project(draco C CXX)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(draco_root "${CMAKE_CURRENT_SOURCE_DIR}")
set(draco_src_root "${draco_root}/src/draco")
set(draco_build "${CMAKE_BINARY_DIR}")
if("${draco_root}" STREQUAL "${draco_build}")
message(
FATAL_ERROR "Building from within the Draco source tree is not supported.\n"
"Hint: Run these commands\n"
"$ rm -rf CMakeCache.txt CMakeFiles\n"
"$ mkdir -p ../draco_build\n" "$ cd ../draco_build\n"
"And re-run CMake from the draco_build directory.")
endif()
include(CMakePackageConfigHelpers)
include(FindPythonInterp)
include("${draco_root}/cmake/draco_build_definitions.cmake")
include("${draco_root}/cmake/draco_cpu_detection.cmake")
include("${draco_root}/cmake/draco_emscripten.cmake")
include("${draco_root}/cmake/draco_flags.cmake")
include("${draco_root}/cmake/draco_helpers.cmake")
include("${draco_root}/cmake/draco_install.cmake")
include("${draco_root}/cmake/draco_intrinsics.cmake")
include("${draco_root}/cmake/draco_options.cmake")
include("${draco_root}/cmake/draco_sanitizer.cmake")
include("${draco_root}/cmake/draco_targets.cmake")
include("${draco_root}/cmake/draco_tests.cmake")
include("${draco_root}/cmake/draco_variables.cmake")
# C++ and linker flags.
draco_track_configuration_variable(DRACO_CXX_FLAGS)
draco_track_configuration_variable(DRACO_EXE_LINKER_FLAGS)
# Sanitizer integration.
draco_track_configuration_variable(DRACO_SANITIZE)
# Generated source file directory.
draco_track_configuration_variable(DRACO_GENERATED_SOURCES_DIRECTORY)
# Controls use of std::mutex and absl::Mutex in ThreadPool.
draco_track_configuration_variable(DRACO_THREADPOOL_USE_STD_MUTEX)
if(DRACO_VERBOSE)
draco_dump_cmake_flag_variables()
draco_dump_tracked_configuration_variables()
draco_dump_options()
endif()
# Compiler/linker flags must be lists, but come in from the environment as
# strings. Break them up:
if(NOT "${DRACO_CXX_FLAGS}" STREQUAL "")
separate_arguments(DRACO_CXX_FLAGS)
endif()
if(NOT "${DRACO_EXE_LINKER_FLAGS}" STREQUAL "")
separate_arguments(DRACO_EXE_LINKER_FLAGS)
endif()
draco_reset_target_lists()
draco_setup_options()
draco_set_build_definitions()
draco_set_cxx_flags()
draco_generate_features_h()
# Draco source file listing variables.
list(APPEND draco_attributes_sources
"${draco_src_root}/attributes/attribute_octahedron_transform.cc"
"${draco_src_root}/attributes/attribute_octahedron_transform.h"
"${draco_src_root}/attributes/attribute_quantization_transform.cc"
"${draco_src_root}/attributes/attribute_quantization_transform.h"
"${draco_src_root}/attributes/attribute_transform.cc"
"${draco_src_root}/attributes/attribute_transform.h"
"${draco_src_root}/attributes/attribute_transform_data.h"
"${draco_src_root}/attributes/attribute_transform_type.h"
"${draco_src_root}/attributes/geometry_attribute.cc"
"${draco_src_root}/attributes/geometry_attribute.h"
"${draco_src_root}/attributes/geometry_indices.h"
"${draco_src_root}/attributes/point_attribute.cc"
"${draco_src_root}/attributes/point_attribute.h")
list(
APPEND
draco_compression_attributes_dec_sources
"${draco_src_root}/compression/attributes/attributes_decoder.cc"
"${draco_src_root}/compression/attributes/attributes_decoder.h"
"${draco_src_root}/compression/attributes/kd_tree_attributes_decoder.cc"
"${draco_src_root}/compression/attributes/kd_tree_attributes_decoder.h"
"${draco_src_root}/compression/attributes/kd_tree_attributes_shared.h"
"${draco_src_root}/compression/attributes/mesh_attribute_indices_encoding_data.h"
"${draco_src_root}/compression/attributes/normal_compression_utils.h"
"${draco_src_root}/compression/attributes/point_d_vector.h"
"${draco_src_root}/compression/attributes/sequential_attribute_decoder.cc"
"${draco_src_root}/compression/attributes/sequential_attribute_decoder.h"
"${draco_src_root}/compression/attributes/sequential_attribute_decoders_controller.cc"
"${draco_src_root}/compression/attributes/sequential_attribute_decoders_controller.h"
"${draco_src_root}/compression/attributes/sequential_integer_attribute_decoder.cc"
"${draco_src_root}/compression/attributes/sequential_integer_attribute_decoder.h"
"${draco_src_root}/compression/attributes/sequential_normal_attribute_decoder.cc"
"${draco_src_root}/compression/attributes/sequential_normal_attribute_decoder.h"
"${draco_src_root}/compression/attributes/sequential_quantization_attribute_decoder.cc"
"${draco_src_root}/compression/attributes/sequential_quantization_attribute_decoder.h"
)
list(
APPEND
draco_compression_attributes_enc_sources
"${draco_src_root}/compression/attributes/attributes_encoder.cc"
"${draco_src_root}/compression/attributes/attributes_encoder.h"
"${draco_src_root}/compression/attributes/kd_tree_attributes_encoder.cc"
"${draco_src_root}/compression/attributes/kd_tree_attributes_encoder.h"
"${draco_src_root}/compression/attributes/linear_sequencer.h"
"${draco_src_root}/compression/attributes/points_sequencer.h"
"${draco_src_root}/compression/attributes/sequential_attribute_encoder.cc"
"${draco_src_root}/compression/attributes/sequential_attribute_encoder.h"
"${draco_src_root}/compression/attributes/sequential_attribute_encoders_controller.cc"
"${draco_src_root}/compression/attributes/sequential_attribute_encoders_controller.h"
"${draco_src_root}/compression/attributes/sequential_integer_attribute_encoder.cc"
"${draco_src_root}/compression/attributes/sequential_integer_attribute_encoder.h"
"${draco_src_root}/compression/attributes/sequential_normal_attribute_encoder.cc"
"${draco_src_root}/compression/attributes/sequential_normal_attribute_encoder.h"
"${draco_src_root}/compression/attributes/sequential_quantization_attribute_encoder.cc"
"${draco_src_root}/compression/attributes/sequential_quantization_attribute_encoder.h"
)
list(
APPEND
draco_compression_attributes_pred_schemes_dec_sources
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_decoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder_interface.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoding_transform.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_delta_decoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_factory.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_interface.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_decoding_transform.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h"
)
list(
APPEND
draco_compression_attributes_pred_schemes_enc_sources
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_factory.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_interface.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h"
)
list(
APPEND
draco_compression_bit_coders_sources
"${draco_src_root}/compression/bit_coders/adaptive_rans_bit_coding_shared.h"
"${draco_src_root}/compression/bit_coders/adaptive_rans_bit_decoder.cc"
"${draco_src_root}/compression/bit_coders/adaptive_rans_bit_decoder.h"
"${draco_src_root}/compression/bit_coders/adaptive_rans_bit_encoder.cc"
"${draco_src_root}/compression/bit_coders/adaptive_rans_bit_encoder.h"
"${draco_src_root}/compression/bit_coders/direct_bit_decoder.cc"
"${draco_src_root}/compression/bit_coders/direct_bit_decoder.h"
"${draco_src_root}/compression/bit_coders/direct_bit_encoder.cc"
"${draco_src_root}/compression/bit_coders/direct_bit_encoder.h"
"${draco_src_root}/compression/bit_coders/folded_integer_bit_decoder.h"
"${draco_src_root}/compression/bit_coders/folded_integer_bit_encoder.h"
"${draco_src_root}/compression/bit_coders/rans_bit_decoder.cc"
"${draco_src_root}/compression/bit_coders/rans_bit_decoder.h"
"${draco_src_root}/compression/bit_coders/rans_bit_encoder.cc"
"${draco_src_root}/compression/bit_coders/rans_bit_encoder.h"
"${draco_src_root}/compression/bit_coders/symbol_bit_decoder.cc"
"${draco_src_root}/compression/bit_coders/symbol_bit_decoder.h"
"${draco_src_root}/compression/bit_coders/symbol_bit_encoder.cc"
"${draco_src_root}/compression/bit_coders/symbol_bit_encoder.h")
list(APPEND draco_enc_config_sources
"${draco_src_root}/compression/config/compression_shared.h"
"${draco_src_root}/compression/config/draco_options.h"
"${draco_src_root}/compression/config/encoder_options.h"
"${draco_src_root}/compression/config/encoding_features.h")
list(APPEND draco_dec_config_sources
"${draco_src_root}/compression/config/compression_shared.h"
"${draco_src_root}/compression/config/decoder_options.h"
"${draco_src_root}/compression/config/draco_options.h")
list(APPEND draco_compression_decode_sources
"${draco_src_root}/compression/decode.cc"
"${draco_src_root}/compression/decode.h")
list(APPEND draco_compression_encode_sources
"${draco_src_root}/compression/encode.cc"
"${draco_src_root}/compression/encode.h"
"${draco_src_root}/compression/encode_base.h"
"${draco_src_root}/compression/expert_encode.cc"
"${draco_src_root}/compression/expert_encode.h")
list(
APPEND
draco_compression_mesh_traverser_sources
"${draco_src_root}/compression/mesh/traverser/depth_first_traverser.h"
"${draco_src_root}/compression/mesh/traverser/max_prediction_degree_traverser.h"
"${draco_src_root}/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h"
"${draco_src_root}/compression/mesh/traverser/mesh_traversal_sequencer.h"
"${draco_src_root}/compression/mesh/traverser/traverser_base.h")
list(
APPEND
draco_compression_mesh_dec_sources
"${draco_src_root}/compression/mesh/mesh_decoder.cc"
"${draco_src_root}/compression/mesh/mesh_decoder.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder.cc"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl.cc"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_shared.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_decoder.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h"
"${draco_src_root}/compression/mesh/mesh_sequential_decoder.cc"
"${draco_src_root}/compression/mesh/mesh_sequential_decoder.h")
list(
APPEND
draco_compression_mesh_enc_sources
"${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder.cc"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl.cc"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_shared.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_encoder.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h"
"${draco_src_root}/compression/mesh/mesh_encoder.cc"
"${draco_src_root}/compression/mesh/mesh_encoder.h"
"${draco_src_root}/compression/mesh/mesh_sequential_encoder.cc"
"${draco_src_root}/compression/mesh/mesh_sequential_encoder.h")
list(
APPEND
draco_compression_point_cloud_dec_sources
"${draco_src_root}/compression/point_cloud/point_cloud_decoder.cc"
"${draco_src_root}/compression/point_cloud/point_cloud_decoder.h"
"${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_decoder.cc"
"${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_decoder.h"
"${draco_src_root}/compression/point_cloud/point_cloud_sequential_decoder.cc"
"${draco_src_root}/compression/point_cloud/point_cloud_sequential_decoder.h"
)
list(
APPEND
draco_compression_point_cloud_enc_sources
"${draco_src_root}/compression/point_cloud/point_cloud_encoder.cc"
"${draco_src_root}/compression/point_cloud/point_cloud_encoder.h"
"${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoder.cc"
"${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoder.h"
"${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoder.cc"
"${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoder.h"
)
list(APPEND draco_compression_entropy_sources
"${draco_src_root}/compression/entropy/ans.h"
"${draco_src_root}/compression/entropy/rans_symbol_coding.h"
"${draco_src_root}/compression/entropy/rans_symbol_decoder.h"
"${draco_src_root}/compression/entropy/rans_symbol_encoder.h"
"${draco_src_root}/compression/entropy/shannon_entropy.cc"
"${draco_src_root}/compression/entropy/shannon_entropy.h"
"${draco_src_root}/compression/entropy/symbol_decoding.cc"
"${draco_src_root}/compression/entropy/symbol_decoding.h"
"${draco_src_root}/compression/entropy/symbol_encoding.cc"
"${draco_src_root}/compression/entropy/symbol_encoding.h")
list(APPEND draco_core_sources
"${draco_src_root}/core/bit_utils.cc"
"${draco_src_root}/core/bit_utils.h"
"${draco_src_root}/core/bounding_box.cc"
"${draco_src_root}/core/bounding_box.h"
"${draco_src_root}/core/cycle_timer.cc"
"${draco_src_root}/core/cycle_timer.h"
"${draco_src_root}/core/data_buffer.cc"
"${draco_src_root}/core/data_buffer.h"
"${draco_src_root}/core/decoder_buffer.cc"
"${draco_src_root}/core/decoder_buffer.h"
"${draco_src_root}/core/divide.cc"
"${draco_src_root}/core/divide.h"
"${draco_src_root}/core/draco_index_type.h"
"${draco_src_root}/core/draco_index_type_vector.h"
"${draco_src_root}/core/draco_types.cc"
"${draco_src_root}/core/draco_types.h"
"${draco_src_root}/core/encoder_buffer.cc"
"${draco_src_root}/core/encoder_buffer.h"
"${draco_src_root}/core/hash_utils.cc"
"${draco_src_root}/core/hash_utils.h"
"${draco_src_root}/core/macros.h"
"${draco_src_root}/core/math_utils.h"
"${draco_src_root}/core/options.cc"
"${draco_src_root}/core/options.h"
"${draco_src_root}/core/quantization_utils.cc"
"${draco_src_root}/core/quantization_utils.h"
"${draco_src_root}/core/status.h"
"${draco_src_root}/core/status_or.h"
"${draco_src_root}/core/varint_decoding.h"
"${draco_src_root}/core/varint_encoding.h"
"${draco_src_root}/core/vector_d.h")
list(APPEND draco_io_sources
"${draco_src_root}/io/file_reader_factory.cc"
"${draco_src_root}/io/file_reader_factory.h"
"${draco_src_root}/io/file_reader_interface.h"
"${draco_src_root}/io/file_utils.cc"
"${draco_src_root}/io/file_utils.h"
"${draco_src_root}/io/file_writer_factory.cc"
"${draco_src_root}/io/file_writer_factory.h"
"${draco_src_root}/io/file_writer_interface.h"
"${draco_src_root}/io/file_writer_utils.h"
"${draco_src_root}/io/file_writer_utils.cc"
"${draco_src_root}/io/mesh_io.cc"
"${draco_src_root}/io/mesh_io.h"
"${draco_src_root}/io/obj_decoder.cc"
"${draco_src_root}/io/obj_decoder.h"
"${draco_src_root}/io/obj_encoder.cc"
"${draco_src_root}/io/obj_encoder.h"
"${draco_src_root}/io/parser_utils.cc"
"${draco_src_root}/io/parser_utils.h"
"${draco_src_root}/io/ply_decoder.cc"
"${draco_src_root}/io/ply_decoder.h"
"${draco_src_root}/io/ply_encoder.cc"
"${draco_src_root}/io/ply_encoder.h"
"${draco_src_root}/io/ply_property_reader.h"
"${draco_src_root}/io/ply_property_writer.h"
"${draco_src_root}/io/ply_reader.cc"
"${draco_src_root}/io/ply_reader.h"
"${draco_src_root}/io/point_cloud_io.cc"
"${draco_src_root}/io/point_cloud_io.h"
"${draco_src_root}/io/stdio_file_reader.cc"
"${draco_src_root}/io/stdio_file_reader.h"
"${draco_src_root}/io/stdio_file_writer.cc"
"${draco_src_root}/io/stdio_file_writer.h")
list(APPEND draco_mesh_sources
"${draco_src_root}/mesh/corner_table.cc"
"${draco_src_root}/mesh/corner_table.h"
"${draco_src_root}/mesh/corner_table_iterators.h"
"${draco_src_root}/mesh/mesh.cc"
"${draco_src_root}/mesh/mesh.h"
"${draco_src_root}/mesh/mesh_are_equivalent.cc"
"${draco_src_root}/mesh/mesh_are_equivalent.h"
"${draco_src_root}/mesh/mesh_attribute_corner_table.cc"
"${draco_src_root}/mesh/mesh_attribute_corner_table.h"
"${draco_src_root}/mesh/mesh_cleanup.cc"
"${draco_src_root}/mesh/mesh_cleanup.h"
"${draco_src_root}/mesh/mesh_misc_functions.cc"
"${draco_src_root}/mesh/mesh_misc_functions.h"
"${draco_src_root}/mesh/mesh_stripifier.cc"
"${draco_src_root}/mesh/mesh_stripifier.h"
"${draco_src_root}/mesh/triangle_soup_mesh_builder.cc"
"${draco_src_root}/mesh/triangle_soup_mesh_builder.h"
"${draco_src_root}/mesh/valence_cache.h")
list(APPEND draco_point_cloud_sources
"${draco_src_root}/point_cloud/point_cloud.cc"
"${draco_src_root}/point_cloud/point_cloud.h"
"${draco_src_root}/point_cloud/point_cloud_builder.cc"
"${draco_src_root}/point_cloud/point_cloud_builder.h")
list(
APPEND
draco_points_common_sources
"${draco_src_root}/compression/point_cloud/algorithms/point_cloud_compression_method.h"
"${draco_src_root}/compression/point_cloud/algorithms/point_cloud_types.h"
"${draco_src_root}/compression/point_cloud/algorithms/quantize_points_3.h"
"${draco_src_root}/compression/point_cloud/algorithms/queuing_policy.h")
list(
APPEND
draco_points_dec_sources
"${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.cc"
"${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h"
"${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_decoder.cc"
"${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_decoder.h"
)
list(
APPEND
draco_points_enc_sources
"${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.cc"
"${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h"
"${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_encoder.cc"
"${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_encoder.h"
)
list(APPEND draco_metadata_sources
"${draco_src_root}/metadata/geometry_metadata.cc"
"${draco_src_root}/metadata/geometry_metadata.h"
"${draco_src_root}/metadata/metadata.cc"
"${draco_src_root}/metadata/metadata.h")
list(APPEND draco_metadata_enc_sources
"${draco_src_root}/metadata/metadata_encoder.cc"
"${draco_src_root}/metadata/metadata_encoder.h")
list(APPEND draco_metadata_dec_sources
"${draco_src_root}/metadata/metadata_decoder.cc"
"${draco_src_root}/metadata/metadata_decoder.h")
list(APPEND draco_animation_sources
"${draco_src_root}/animation/keyframe_animation.cc"
"${draco_src_root}/animation/keyframe_animation.h")
list(APPEND draco_animation_enc_sources
"${draco_src_root}/animation/keyframe_animation_encoder.cc"
"${draco_src_root}/animation/keyframe_animation_encoder.h")
list(APPEND draco_animation_dec_sources
"${draco_src_root}/animation/keyframe_animation_decoder.cc"
"${draco_src_root}/animation/keyframe_animation_decoder.h")
list(
APPEND draco_js_dec_sources
"${draco_src_root}/javascript/emscripten/decoder_webidl_wrapper.cc"
"${draco_src_root}/javascript/emscripten/draco_decoder_glue_wrapper.cc"
)
list(
APPEND draco_js_enc_sources
"${draco_src_root}/javascript/emscripten/draco_encoder_glue_wrapper.cc"
"${draco_src_root}/javascript/emscripten/encoder_webidl_wrapper.cc")
list(
APPEND
draco_animation_js_dec_sources
"${draco_src_root}/javascript/emscripten/animation_decoder_webidl_wrapper.cc"
"${draco_src_root}/javascript/emscripten/draco_animation_decoder_glue_wrapper.cc"
)
list(
APPEND
draco_animation_js_enc_sources
"${draco_src_root}/javascript/emscripten/animation_encoder_webidl_wrapper.cc"
"${draco_src_root}/javascript/emscripten/draco_animation_encoder_glue_wrapper.cc"
)
list(APPEND draco_unity_plug_sources
"${draco_src_root}/unity/draco_unity_plugin.cc"
"${draco_src_root}/unity/draco_unity_plugin.h")
list(APPEND draco_maya_plug_sources
"${draco_src_root}/maya/draco_maya_plugin.cc"
"${draco_src_root}/maya/draco_maya_plugin.h")
#
# Draco targets.
#
if(EMSCRIPTEN AND DRACO_JS_GLUE)
# Draco decoder and encoder "executable" targets in various flavors for
# Emsscripten.
list(APPEND draco_decoder_src
${draco_attributes_sources}
${draco_compression_attributes_dec_sources}
${draco_compression_attributes_pred_schemes_dec_sources}
${draco_compression_bit_coders_sources}
${draco_compression_decode_sources}
${draco_compression_entropy_sources}
${draco_compression_mesh_traverser_sources}
${draco_compression_mesh_dec_sources}
${draco_compression_point_cloud_dec_sources}
${draco_core_sources}
${draco_dec_config_sources}
${draco_js_dec_sources}
${draco_mesh_sources}
${draco_metadata_dec_sources}
${draco_metadata_sources}
${draco_point_cloud_sources}
${draco_points_dec_sources})
list(APPEND draco_encoder_src
${draco_attributes_sources}
${draco_compression_attributes_enc_sources}
${draco_compression_attributes_pred_schemes_enc_sources}
${draco_compression_bit_coders_sources}
${draco_compression_encode_sources}
${draco_compression_entropy_sources}
${draco_compression_mesh_traverser_sources}
${draco_compression_mesh_enc_sources}
${draco_compression_point_cloud_enc_sources}
${draco_core_sources}
${draco_enc_config_sources}
${draco_js_enc_sources}
${draco_mesh_sources}
${draco_metadata_enc_sources}
${draco_metadata_sources}
${draco_point_cloud_sources}
${draco_points_enc_sources})
list(APPEND draco_js_dec_idl
"${draco_src_root}/javascript/emscripten/draco_web_decoder.idl")
list(APPEND draco_js_enc_idl
"${draco_src_root}/javascript/emscripten/draco_web_encoder.idl")
list(
APPEND
draco_animation_js_dec_idl
"${draco_src_root}/javascript/emscripten/draco_animation_web_decoder.idl")
list(
APPEND
draco_animation_js_enc_idl
"${draco_src_root}/javascript/emscripten/draco_animation_web_encoder.idl")
list(APPEND draco_pre_link_js_sources
"${draco_src_root}/javascript/emscripten/prepareCallbacks.js"
"${draco_src_root}/javascript/emscripten/version.js")
list(APPEND draco_post_link_js_sources
"${draco_src_root}/javascript/emscripten/finalize.js")
list(APPEND draco_post_link_js_decoder_sources ${draco_post_link_js_sources}
"${draco_src_root}/javascript/emscripten/decoder_functions.js")
set(draco_decoder_glue_path "${draco_build}/glue_decoder")
set(draco_encoder_glue_path "${draco_build}/glue_encoder")
draco_generate_emscripten_glue(INPUT_IDL ${draco_js_dec_idl} OUTPUT_PATH
${draco_decoder_glue_path})
draco_generate_emscripten_glue(INPUT_IDL ${draco_js_enc_idl} OUTPUT_PATH
${draco_encoder_glue_path})
if(DRACO_DECODER_ATTRIBUTE_DEDUPLICATION)
list(APPEND draco_decoder_features
"DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED"
"DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED")
endif()
draco_add_emscripten_executable(NAME
draco_decoder
SOURCES
${draco_decoder_src}
DEFINES
${draco_defines}
FEATURES
${draco_decoder_features}
INCLUDES
${draco_include_paths}
LINK_FLAGS
"-sEXPORT_NAME=\"DracoDecoderModule\""
GLUE_PATH
${draco_decoder_glue_path}
PRE_LINK_JS_SOURCES
${draco_pre_link_js_sources}
POST_LINK_JS_SOURCES
${draco_post_link_js_decoder_sources})
draco_add_emscripten_executable(
NAME
draco_encoder
SOURCES
${draco_encoder_src}
DEFINES
${draco_defines}
FEATURES
DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
INCLUDES
${draco_include_paths}
LINK_FLAGS
"-sEXPORT_NAME=\"DracoEncoderModule\""
GLUE_PATH
${draco_encoder_glue_path}
PRE_LINK_JS_SOURCES
${draco_pre_link_js_sources}
POST_LINK_JS_SOURCES
${draco_post_link_js_sources})
if(DRACO_ANIMATION_ENCODING)
set(draco_anim_decoder_glue_path "${draco_build}/glue_animation_decoder")
set(draco_anim_encoder_glue_path "${draco_build}/glue_animation_encoder")
draco_generate_emscripten_glue(INPUT_IDL ${draco_animation_js_dec_idl}
OUTPUT_PATH ${draco_anim_decoder_glue_path})
draco_generate_emscripten_glue(INPUT_IDL ${draco_animation_js_enc_idl}
OUTPUT_PATH ${draco_anim_encoder_glue_path})
draco_add_emscripten_executable(
NAME
draco_animation_decoder
SOURCES
${draco_animation_dec_sources}
${draco_animation_js_dec_sources}
${draco_animation_sources}
${draco_decoder_src}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
LINK_FLAGS
"-sEXPORT_NAME=\"DracoAnimationDecoderModule\""
GLUE_PATH
${draco_anim_decoder_glue_path}
PRE_LINK_JS_SOURCES
${draco_pre_link_js_sources}
POST_LINK_JS_SOURCES
${draco_post_link_js_decoder_sources})
draco_add_emscripten_executable(
NAME
draco_animation_encoder
SOURCES
${draco_animation_enc_sources}
${draco_animation_js_enc_sources}
${draco_animation_sources}
${draco_encoder_src}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
LINK_FLAGS
"-sEXPORT_NAME=\"DracoAnimationEncoderModule\""
GLUE_PATH
${draco_anim_encoder_glue_path}
PRE_LINK_JS_SOURCES
${draco_pre_link_js_sources}
POST_LINK_JS_SOURCES
${draco_post_link_js_sources})
endif()
else()
# Standard Draco libs, encoder and decoder. Object collections that mirror the
# Draco directory structure.
draco_add_library(NAME draco_attributes TYPE OBJECT SOURCES
${draco_attributes_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME
draco_compression_attributes_dec
OBJECT
${draco_compression_attributes_dec_sources}
TYPE
OBJECT
SOURCES
${draco_compression_attributes_dec_sources}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths})
draco_add_library(NAME draco_compression_attributes_enc TYPE OBJECT SOURCES
${draco_compression_attributes_enc_sources} DEFINES
${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_attributes_pred_schemes_dec TYPE
OBJECT SOURCES
${draco_compression_attributes_pred_schemes_dec_sources})
draco_add_library(NAME draco_compression_attributes_pred_schemes_enc TYPE
OBJECT SOURCES
${draco_compression_attributes_pred_schemes_enc_sources}
DEFINES ${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_bit_coders TYPE OBJECT SOURCES
${draco_compression_bit_coders_sources} DEFINES
${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_enc_config TYPE OBJECT SOURCES
${draco_enc_config_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_dec_config TYPE OBJECT SOURCES
${draco_dec_config_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_decode TYPE OBJECT SOURCES
${draco_compression_decode_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_encode TYPE OBJECT SOURCES
${draco_compression_encode_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_entropy TYPE OBJECT SOURCES
${draco_compression_entropy_sources} DEFINES
${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_mesh_traverser TYPE OBJECT SOURCES
${draco_compression_mesh_traverser_sources} DEFINES
${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_mesh_dec TYPE OBJECT SOURCES
${draco_compression_mesh_dec_sources} DEFINES
${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_mesh_enc TYPE OBJECT SOURCES
${draco_compression_mesh_enc_sources} DEFINES
${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_point_cloud_dec TYPE OBJECT SOURCES
${draco_compression_point_cloud_dec_sources} DEFINES
${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_compression_point_cloud_enc TYPE OBJECT SOURCES
${draco_compression_point_cloud_enc_sources} DEFINES
${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_core TYPE OBJECT SOURCES ${draco_core_sources}
DEFINES ${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_io TYPE OBJECT SOURCES ${draco_io_sources}
DEFINES ${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_mesh TYPE OBJECT SOURCES ${draco_mesh_sources}
DEFINES ${draco_defines} INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_metadata_dec TYPE OBJECT SOURCES
${draco_metadata_dec_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_metadata_enc TYPE OBJECT SOURCES
${draco_metadata_enc_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_metadata TYPE OBJECT SOURCES
${draco_metadata_sources} DEFINES ${draco_defines} INCLUDES
${draco_include_paths})
draco_add_library(NAME draco_animation_dec TYPE OBJECT SOURCES
${draco_animation_dec_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_animation_enc TYPE OBJECT SOURCES
${draco_animation_enc_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME draco_animation TYPE OBJECT SOURCES
${draco_animation_sources} DEFINES ${draco_defines} INCLUDES
${draco_include_paths})
draco_add_library(NAME draco_point_cloud TYPE OBJECT SOURCES
${draco_point_cloud_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME
draco_points_dec
TYPE
OBJECT
SOURCES
${draco_points_common_sources}
${draco_points_dec_sources}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths})
draco_add_library(NAME
draco_points_enc
TYPE
OBJECT
SOURCES
${draco_points_common_sources}
${draco_points_enc_sources}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths})
set(draco_object_library_deps
draco_attributes
draco_compression_attributes_dec
draco_compression_attributes_enc
draco_compression_attributes_pred_schemes_dec
draco_compression_attributes_pred_schemes_enc
draco_compression_bit_coders
draco_compression_decode
draco_compression_encode
draco_compression_entropy
draco_compression_mesh_dec
draco_compression_mesh_enc
draco_compression_point_cloud_dec
draco_compression_point_cloud_enc
draco_core
draco_dec_config
draco_enc_config
draco_io
draco_mesh
draco_metadata
draco_metadata_dec
draco_metadata_enc
draco_animation
draco_animation_dec
draco_animation_enc
draco_point_cloud
draco_points_dec
draco_points_enc)
# Library targets that consume the object collections.
if(MSVC OR WIN32)
# In order to produce a DLL and import library the Windows tools require
# that the exported symbols are part of the DLL target. The unfortunate side
# effect of this is that a single configuration cannot output both the
# static library and the DLL: This results in an either/or situation.
# Windows users of the draco build can have a DLL and an import library,
# or they can have a static library; they cannot have both from a single
# configuration of the build.
if(BUILD_SHARED_LIBS)
set(draco_lib_type SHARED)
else()
set(draco_lib_type STATIC)
endif()
draco_add_library(NAME
draco
OUTPUT_NAME
draco
TYPE
${draco_lib_type}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
OBJLIB_DEPS
${draco_object_library_deps})
else()
draco_add_library(NAME
draco_static
OUTPUT_NAME
draco
TYPE
STATIC
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
OBJLIB_DEPS
${draco_object_library_deps})
if(BUILD_SHARED_LIBS)
draco_add_library(NAME
draco_shared
SOURCES
"${draco_src_root}/core/draco_version.h"
OUTPUT_NAME
draco
TYPE
SHARED
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
LIB_DEPS
draco_static)
endif()
endif()
if(DRACO_UNITY_PLUGIN)
if(IOS)
set(unity_decoder_lib_type STATIC)
else()
set(unity_decoder_lib_type MODULE)
endif()
draco_add_library(NAME draco_unity_plugin TYPE OBJECT SOURCES
${draco_unity_plug_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME
dracodec_unity
TYPE
${unity_decoder_lib_type}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
OBJLIB_DEPS
draco_unity_plugin
LIB_DEPS
${draco_plugin_dependency})
# For Mac, we need to build a .bundle for the unity plugin.
if(APPLE)
set_target_properties(dracodec_unity PROPERTIES BUNDLE true)
elseif(NOT unity_decoder_lib_type STREQUAL STATIC)
set_target_properties(dracodec_unity
PROPERTIES SOVERSION ${DRACO_SOVERSION})
endif()
endif()
if(DRACO_MAYA_PLUGIN)
draco_add_library(NAME draco_maya_plugin TYPE OBJECT SOURCES
${draco_maya_plug_sources} DEFINES ${draco_defines}
INCLUDES ${draco_include_paths})
draco_add_library(NAME
draco_maya_wrapper
TYPE
MODULE
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
OBJLIB_DEPS
draco_maya_plugin
LIB_DEPS
${draco_plugin_dependency})
# For Mac, we need to build a .bundle for the plugin.
if(APPLE)
set_target_properties(draco_maya_wrapper PROPERTIES BUNDLE true)
else()
set_target_properties(draco_maya_wrapper
PROPERTIES SOVERSION ${DRACO_SOVERSION})
endif()
endif()
# Draco app targets.
draco_add_executable(NAME
draco_decoder
SOURCES
"${draco_src_root}/tools/draco_decoder.cc"
${draco_io_sources}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
LIB_DEPS
${draco_dependency})
draco_add_executable(NAME
draco_encoder
SOURCES
"${draco_src_root}/tools/draco_encoder.cc"
${draco_io_sources}
DEFINES
${draco_defines}
INCLUDES
${draco_include_paths}
LIB_DEPS
${draco_dependency})
draco_setup_install_target()
draco_setup_test_targets()
endif()
if(DRACO_VERBOSE)
draco_dump_cmake_flag_variables()
draco_dump_tracked_configuration_variables()
draco_dump_options()
endif()

View File

@ -0,0 +1,27 @@
Want to contribute? Great! First, read this page (including the small print at the end).
### Before you contribute
Before we can use your code, you must sign the
[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)
(CLA), which you can do online. The CLA is necessary mainly because you own the
copyright to your changes, even after your contribution becomes part of our
codebase, so we need your permission to use and distribute your code. We also
need to be sure of various other things—for instance that you'll tell us if you
know that your code infringes on other people's patents. You don't have to sign
the CLA until after you've submitted your code for review and a member has
approved it, but you must do it before we can put your code into our codebase.
Before you start working on a larger contribution, you should get in touch with
us first through the issue tracker with your idea so that we can help out and
possibly guide you. Coordinating up front makes it much easier to avoid
frustration later on.
### Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose.
Please make sure that your code conforms with our
[coding style guidelines](https://google.github.io/styleguide/cppguide.html).
### The small print
Contributions made by corporations are covered by a different agreement than
the one above, the
[Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).

View File

@ -0,0 +1,252 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
Files: docs/assets/js/ASCIIMathML.js
Copyright (c) 2014 Peter Jipsen and other ASCIIMathML.js contributors
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.
--------------------------------------------------------------------------------
Files: docs/assets/css/pygments/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org>

View File

@ -0,0 +1,478 @@
<p align="center">
<img width="350px" src="docs/artwork/draco3d-vert.svg" />
</p>
![Build Status: master](https://travis-ci.org/google/draco.svg?branch=master)
News
=======
### Version 1.4.1 release
* Using the versioned gstatic.com WASM and Javascript decoders is now
recommended. To use v1.4.1, use this URL:
* https://www.gstatic.com/draco/versioned/decoders/1.4.1/*
* Replace the * with the files to load. E.g.
* https://gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js
* This works with the v1.3.6 and v1.4.0 releases, and will work with future
Draco releases.
* Bug fixes
### Version 1.4.0 release
* WASM and JavaScript decoders are hosted from a static URL.
* It is recommended to always pull your Draco WASM and JavaScript decoders from this URL:
* https://www.gstatic.com/draco/v1/decoders/*
* Replace * with the files to load. E.g.
* https://www.gstatic.com/draco/v1/decoders/draco_decoder_gltf.wasm
* Users will benefit from having the Draco decoder in cache as more sites start using the static URL
* Changed npm modules to use WASM, which increased performance by ~200%.
* Updated Emscripten to 2.0.
* This causes the Draco codec modules to return a promise instead of the module directly.
* Please see the example code on how to handle the promise.
* Changed NORMAL quantization default to 8.
* Added new array API to decoder and deprecated DecoderBuffer.
* See PR https://github.com/google/draco/issues/513 for more information.
* Changed WASM/JavaScript behavior of catching exceptions.
* See issue https://github.com/google/draco/issues/629 for more information.
* Code cleanup.
* Emscripten builds now disable NODEJS_CATCH_EXIT and NODEJS_CATCH_REJECTION.
* Authors of a CLI tool might want to add their own error handlers.
* Added Maya plugin builds.
* Unity plugin builds updated.
* Builds are now stored as archives.
* Added iOS build.
* Unity users may want to look into https://github.com/atteneder/DracoUnity.
* Bug fixes.
### Version 1.3.6 release
* WASM and JavaScript decoders are now hosted from a static URL
* It is recommended to always pull your Draco WASM and JavaScript decoders from this URL:
* https://www.gstatic.com/draco/v1/decoders/*
* Replace * with the files to load. E.g.
* https://www.gstatic.com/draco/v1/decoders/draco_decoder_gltf.wasm
* Users will benefit from having the Draco decoder in cache as more sites start using the static URL
* Changed web examples to pull Draco decoders from static URL
* Added new API to Draco WASM decoder, which increased performance by ~15%
* Decreased Draco WASM decoder size by ~20%
* Added support for generic and multiple attributes to Draco Unity plug-ins
* Added new API to Draco Unity, which increased decoder performance by ~15%
* Changed quantization defaults:
* POSITION: 11
* NORMAL: 7
* TEX_COORD: 10
* COLOR: 8
* GENERIC: 8
* Code cleanup
* Bug fixes
### Version 1.3.5 release
* Added option to build Draco for Universal Scene Description
* Code cleanup
* Bug fixes
### Version 1.3.4 release
* Released Draco Animation code
* Fixes for Unity
* Various file location and name changes
### Version 1.3.3 release
* Added ExpertEncoder to the Javascript API
* Allows developers to set quantization options per attribute id
* Bug fixes
### Version 1.3.2 release
* Bug fixes
### Version 1.3.1 release
* Fix issue with multiple attributes when skipping an attribute transform
### Version 1.3.0 release
* Improved kD-tree based point cloud encoding
* Now applicable to point clouds with any number of attributes
* Support for all integer attribute types and quantized floating point types
* Improved mesh compression up to 10% (on average ~2%)
* For meshes, the 1.3.0 bitstream is fully compatible with 1.2.x decoders
* Improved Javascript API
* Added support for all signed and unsigned integer types
* Added support for point clouds to our Javascript encoder API
* Added support for integer properties to the PLY decoder
* Bug fixes
### Previous releases
https://github.com/google/draco/releases
Description
===========
Draco is a library for compressing and decompressing 3D geometric [meshes] and
[point clouds]. It is intended to improve the storage and transmission of 3D
graphics.
Draco was designed and built for compression efficiency and speed. The code
supports compressing points, connectivity information, texture coordinates,
color information, normals, and any other generic attributes associated with
geometry. With Draco, applications using 3D graphics can be significantly
smaller without compromising visual fidelity. For users, this means apps can
now be downloaded faster, 3D graphics in the browser can load quicker, and VR
and AR scenes can now be transmitted with a fraction of the bandwidth and
rendered quickly.
Draco is released as C++ source code that can be used to compress 3D graphics
as well as C++ and Javascript decoders for the encoded data.
_**Contents**_
* [Building](#building)
* [Usage](#usage)
* [Unity](#unity)
* [WASM and JavaScript Decoders](#WASM-and-JavaScript-Decoders)
* [Command Line Applications](#command-line-applications)
* [Encoding Tool](#encoding-tool)
* [Encoding Point Clouds](#encoding-point-clouds)
* [Decoding Tool](#decoding-tool)
* [C++ Decoder API](#c-decoder-api)
* [Javascript Encoder API](#javascript-encoder-api)
* [Javascript Decoder API](#javascript-decoder-api)
* [Javascript Decoder Performance](#javascript-decoder-performance)
* [Metadata API](#metadata-api)
* [NPM Package](#npm-package)
* [three.js Renderer Example](#threejs-renderer-example)
* [Support](#support)
* [License](#license)
* [References](#references)
Building
========
See [BUILDING](BUILDING.md) for building instructions.
Usage
======
Unity
-----
For the best information about using Unity with Draco please visit https://github.com/atteneder/DracoUnity
For a simple example of using Unity with Draco see [README](unity/README.md) in the unity folder.
WASM and JavaScript Decoders
----------------------------
It is recommended to always pull your Draco WASM and JavaScript decoders from:
~~~~~ bash
https://www.gstatic.com/draco/v1/decoders/
~~~~~
Users will benefit from having the Draco decoder in cache as more sites start using the static URL.
Command Line Applications
------------------------
The default target created from the build files will be the `draco_encoder`
and `draco_decoder` command line applications. For both applications, if you
run them without any arguments or `-h`, the applications will output usage and
options.
Encoding Tool
-------------
`draco_encoder` will read OBJ or PLY files as input, and output Draco-encoded
files. We have included Stanford's [Bunny] mesh for testing. The basic command
line looks like this:
~~~~~ bash
./draco_encoder -i testdata/bun_zipper.ply -o out.drc
~~~~~
A value of `0` for the quantization parameter will not perform any quantization
on the specified attribute. Any value other than `0` will quantize the input
values for the specified attribute to that number of bits. For example:
~~~~~ bash
./draco_encoder -i testdata/bun_zipper.ply -o out.drc -qp 14
~~~~~
will quantize the positions to 14 bits (default is 11 for the position
coordinates).
In general, the more you quantize your attributes the better compression rate
you will get. It is up to your project to decide how much deviation it will
tolerate. In general, most projects can set quantization values of about `11`
without any noticeable difference in quality.
The compression level (`-cl`) parameter turns on/off different compression
features.
~~~~~ bash
./draco_encoder -i testdata/bun_zipper.ply -o out.drc -cl 8
~~~~~
In general, the highest setting, `10`, will have the most compression but
worst decompression speed. `0` will have the least compression, but best
decompression speed. The default setting is `7`.
Encoding Point Clouds
---------------------
You can encode point cloud data with `draco_encoder` by specifying the
`-point_cloud` parameter. If you specify the `-point_cloud` parameter with a
mesh input file, `draco_encoder` will ignore the connectivity data and encode
the positions from the mesh file.
~~~~~ bash
./draco_encoder -point_cloud -i testdata/bun_zipper.ply -o out.drc
~~~~~
This command line will encode the mesh input as a point cloud, even though the
input might not produce compression that is representative of other point
clouds. Specifically, one can expect much better compression rates for larger
and denser point clouds.
Decoding Tool
-------------
`draco_decoder` will read Draco files as input, and output OBJ or PLY files.
The basic command line looks like this:
~~~~~ bash
./draco_decoder -i in.drc -o out.obj
~~~~~
C++ Decoder API
-------------
If you'd like to add decoding to your applications you will need to include
the `draco_dec` library. In order to use the Draco decoder you need to
initialize a `DecoderBuffer` with the compressed data. Then call
`DecodeMeshFromBuffer()` to return a decoded mesh object or call
`DecodePointCloudFromBuffer()` to return a decoded `PointCloud` object. For
example:
~~~~~ cpp
draco::DecoderBuffer buffer;
buffer.Init(data.data(), data.size());
const draco::EncodedGeometryType geom_type =
draco::GetEncodedGeometryType(&buffer);
if (geom_type == draco::TRIANGULAR_MESH) {
unique_ptr<draco::Mesh> mesh = draco::DecodeMeshFromBuffer(&buffer);
} else if (geom_type == draco::POINT_CLOUD) {
unique_ptr<draco::PointCloud> pc = draco::DecodePointCloudFromBuffer(&buffer);
}
~~~~~
Please see [src/draco/mesh/mesh.h](src/draco/mesh/mesh.h) for the full `Mesh` class interface and
[src/draco/point_cloud/point_cloud.h](src/draco/point_cloud/point_cloud.h) for the full `PointCloud` class interface.
Javascript Encoder API
----------------------
The Javascript encoder is located in `javascript/draco_encoder.js`. The encoder
API can be used to compress mesh and point cloud. In order to use the encoder,
you need to first create an instance of `DracoEncoderModule`. Then use this
instance to create `MeshBuilder` and `Encoder` objects. `MeshBuilder` is used
to construct a mesh from geometry data that could be later compressed by
`Encoder`. First create a mesh object using `new encoderModule.Mesh()` . Then,
use `AddFacesToMesh()` to add indices to the mesh and use
`AddFloatAttributeToMesh()` to add attribute data to the mesh, e.g. position,
normal, color and texture coordinates. After a mesh is constructed, you could
then use `EncodeMeshToDracoBuffer()` to compress the mesh. For example:
~~~~~ js
const mesh = {
indices : new Uint32Array(indices),
vertices : new Float32Array(vertices),
normals : new Float32Array(normals)
};
const encoderModule = DracoEncoderModule();
const encoder = new encoderModule.Encoder();
const meshBuilder = new encoderModule.MeshBuilder();
const dracoMesh = new encoderModule.Mesh();
const numFaces = mesh.indices.length / 3;
const numPoints = mesh.vertices.length;
meshBuilder.AddFacesToMesh(dracoMesh, numFaces, mesh.indices);
meshBuilder.AddFloatAttributeToMesh(dracoMesh, encoderModule.POSITION,
numPoints, 3, mesh.vertices);
if (mesh.hasOwnProperty('normals')) {
meshBuilder.AddFloatAttributeToMesh(
dracoMesh, encoderModule.NORMAL, numPoints, 3, mesh.normals);
}
if (mesh.hasOwnProperty('colors')) {
meshBuilder.AddFloatAttributeToMesh(
dracoMesh, encoderModule.COLOR, numPoints, 3, mesh.colors);
}
if (mesh.hasOwnProperty('texcoords')) {
meshBuilder.AddFloatAttributeToMesh(
dracoMesh, encoderModule.TEX_COORD, numPoints, 3, mesh.texcoords);
}
if (method === "edgebreaker") {
encoder.SetEncodingMethod(encoderModule.MESH_EDGEBREAKER_ENCODING);
} else if (method === "sequential") {
encoder.SetEncodingMethod(encoderModule.MESH_SEQUENTIAL_ENCODING);
}
const encodedData = new encoderModule.DracoInt8Array();
// Use default encoding setting.
const encodedLen = encoder.EncodeMeshToDracoBuffer(dracoMesh,
encodedData);
encoderModule.destroy(dracoMesh);
encoderModule.destroy(encoder);
encoderModule.destroy(meshBuilder);
~~~~~
Please see [src/draco/javascript/emscripten/draco_web_encoder.idl](src/draco/javascript/emscripten/draco_web_encoder.idl) for the full API.
Javascript Decoder API
----------------------
The Javascript decoder is located in [javascript/draco_decoder.js](javascript/draco_decoder.js). The
Javascript decoder can decode mesh and point cloud. In order to use the
decoder, you must first create an instance of `DracoDecoderModule`. The
instance is then used to create `DecoderBuffer` and `Decoder` objects. Set
the encoded data in the `DecoderBuffer`. Then call `GetEncodedGeometryType()`
to identify the type of geometry, e.g. mesh or point cloud. Then call either
`DecodeBufferToMesh()` or `DecodeBufferToPointCloud()`, which will return
a Mesh object or a point cloud. For example:
~~~~~ js
// Create the Draco decoder.
const decoderModule = DracoDecoderModule();
const buffer = new decoderModule.DecoderBuffer();
buffer.Init(byteArray, byteArray.length);
// Create a buffer to hold the encoded data.
const decoder = new decoderModule.Decoder();
const geometryType = decoder.GetEncodedGeometryType(buffer);
// Decode the encoded geometry.
let outputGeometry;
let status;
if (geometryType == decoderModule.TRIANGULAR_MESH) {
outputGeometry = new decoderModule.Mesh();
status = decoder.DecodeBufferToMesh(buffer, outputGeometry);
} else {
outputGeometry = new decoderModule.PointCloud();
status = decoder.DecodeBufferToPointCloud(buffer, outputGeometry);
}
// You must explicitly delete objects created from the DracoDecoderModule
// or Decoder.
decoderModule.destroy(outputGeometry);
decoderModule.destroy(decoder);
decoderModule.destroy(buffer);
~~~~~
Please see [src/draco/javascript/emscripten/draco_web_decoder.idl](src/draco/javascript/emscripten/draco_web_decoder.idl) for the full API.
Javascript Decoder Performance
------------------------------
The Javascript decoder is built with dynamic memory. This will let the decoder
work with all of the compressed data. But this option is not the fastest.
Pre-allocating the memory sees about a 2x decoder speed improvement. If you
know all of your project's memory requirements, you can turn on static memory
by changing `CMakeLists.txt` accordingly.
Metadata API
------------
Starting from v1.0, Draco provides metadata functionality for encoding data
other than geometry. It could be used to encode any custom data along with the
geometry. For example, we can enable metadata functionality to encode the name
of attributes, name of sub-objects and customized information.
For one mesh and point cloud, it can have one top-level geometry metadata class.
The top-level metadata then can have hierarchical metadata. Other than that,
the top-level metadata can have metadata for each attribute which is called
attribute metadata. The attribute metadata should be initialized with the
correspondent attribute id within the mesh. The metadata API is provided both
in C++ and Javascript.
For example, to add metadata in C++:
~~~~~ cpp
draco::PointCloud pc;
// Add metadata for the geometry.
std::unique_ptr<draco::GeometryMetadata> metadata =
std::unique_ptr<draco::GeometryMetadata>(new draco::GeometryMetadata());
metadata->AddEntryString("description", "This is an example.");
pc.AddMetadata(std::move(metadata));
// Add metadata for attributes.
draco::GeometryAttribute pos_att;
pos_att.Init(draco::GeometryAttribute::POSITION, nullptr, 3,
draco::DT_FLOAT32, false, 12, 0);
const uint32_t pos_att_id = pc.AddAttribute(pos_att, false, 0);
std::unique_ptr<draco::AttributeMetadata> pos_metadata =
std::unique_ptr<draco::AttributeMetadata>(
new draco::AttributeMetadata(pos_att_id));
pos_metadata->AddEntryString("name", "position");
// Directly add attribute metadata to geometry.
// You can do this without explicitly add |GeometryMetadata| to mesh.
pc.AddAttributeMetadata(pos_att_id, std::move(pos_metadata));
~~~~~
To read metadata from a geometry in C++:
~~~~~ cpp
// Get metadata for the geometry.
const draco::GeometryMetadata *pc_metadata = pc.GetMetadata();
// Request metadata for a specific attribute.
const draco::AttributeMetadata *requested_pos_metadata =
pc.GetAttributeMetadataByStringEntry("name", "position");
~~~~~
Please see [src/draco/metadata](src/draco/metadata) and [src/draco/point_cloud](src/draco/point_cloud) for the full API.
NPM Package
-----------
Draco NPM NodeJS package is located in [javascript/npm/draco3d](javascript/npm/draco3d). Please see the
doc in the folder for detailed usage.
three.js Renderer Example
-------------------------
Here's an [example] of a geometric compressed with Draco loaded via a
Javascript decoder using the `three.js` renderer.
Please see the [javascript/example/README.md](javascript/example/README.md) file for more information.
Support
=======
For questions/comments please email <draco-3d-discuss@googlegroups.com>
If you have found an error in this library, please file an issue at
<https://github.com/google/draco/issues>
Patches are encouraged, and may be submitted by forking this project and
submitting a pull request through GitHub. See [CONTRIBUTING] for more detail.
License
=======
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
<http://www.apache.org/licenses/LICENSE-2.0>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
References
==========
[example]:https://storage.googleapis.com/demos.webmproject.org/draco/draco_loader_throw.html
[meshes]: https://en.wikipedia.org/wiki/Polygon_mesh
[point clouds]: https://en.wikipedia.org/wiki/Point_cloud
[Bunny]: https://graphics.stanford.edu/data/3Dscanrep/
[CONTRIBUTING]: https://raw.githubusercontent.com/google/draco/master/CONTRIBUTING.md
Bunny model from Stanford's graphic department <https://graphics.stanford.edu/data/3Dscanrep/>

View File

@ -0,0 +1,3 @@
@PACKAGE_INIT@
set_and_check(draco_INCLUDE_DIR "@PACKAGE_draco_include_install_dir@")
set_and_check(draco_LIBRARY_DIR "@PACKAGE_draco_lib_install_dir@")

View File

@ -0,0 +1,56 @@
# Finddraco
#
# Locates draco and sets the following variables:
#
# draco_FOUND draco_INCLUDE_DIRS draco_LIBARY_DIRS draco_LIBRARIES
# draco_VERSION_STRING
#
# draco_FOUND is set to YES only when all other variables are successfully
# configured.
unset(draco_FOUND)
unset(draco_INCLUDE_DIRS)
unset(draco_LIBRARY_DIRS)
unset(draco_LIBRARIES)
unset(draco_VERSION_STRING)
mark_as_advanced(draco_FOUND)
mark_as_advanced(draco_INCLUDE_DIRS)
mark_as_advanced(draco_LIBRARY_DIRS)
mark_as_advanced(draco_LIBRARIES)
mark_as_advanced(draco_VERSION_STRING)
set(draco_version_file_no_prefix "draco/src/draco/core/draco_version.h")
# Set draco_INCLUDE_DIRS
find_path(draco_INCLUDE_DIRS NAMES "${draco_version_file_no_prefix}")
# Extract the version string from draco_version.h.
if(draco_INCLUDE_DIRS)
set(draco_version_file
"${draco_INCLUDE_DIRS}/draco/src/draco/core/draco_version.h")
file(STRINGS "${draco_version_file}" draco_version REGEX "kdracoVersion")
list(GET draco_version 0 draco_version)
string(REPLACE "static const char kdracoVersion[] = " "" draco_version
"${draco_version}")
string(REPLACE ";" "" draco_version "${draco_version}")
string(REPLACE "\"" "" draco_version "${draco_version}")
set(draco_VERSION_STRING ${draco_version})
endif()
# Find the library.
if(BUILD_SHARED_LIBS)
find_library(draco_LIBRARIES NAMES draco.dll libdraco.dylib libdraco.so)
else()
find_library(draco_LIBRARIES NAMES draco.lib libdraco.a)
endif()
# Store path to library.
get_filename_component(draco_LIBRARY_DIRS ${draco_LIBRARIES} DIRECTORY)
if(draco_INCLUDE_DIRS
AND draco_LIBRARY_DIRS
AND draco_LIBRARIES
AND draco_VERSION_STRING)
set(draco_FOUND YES)
endif()

View File

@ -0,0 +1,220 @@
if(DRACO_CMAKE_COMPILER_FLAGS_CMAKE_)
return()
endif()
set(DRACO_CMAKE_COMPILER_FLAGS_CMAKE_ 1)
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include("${draco_root}/cmake/compiler_tests.cmake")
# Strings used to cache failed C/CXX flags.
set(DRACO_FAILED_C_FLAGS)
set(DRACO_FAILED_CXX_FLAGS)
# Checks C compiler for support of $c_flag. Adds $c_flag to $CMAKE_C_FLAGS when
# the compile test passes. Caches $c_flag in $DRACO_FAILED_C_FLAGS when the test
# fails.
macro(add_c_flag_if_supported c_flag)
unset(C_FLAG_FOUND CACHE)
string(FIND "${CMAKE_C_FLAGS}" "${c_flag}" C_FLAG_FOUND)
unset(C_FLAG_FAILED CACHE)
string(FIND "${DRACO_FAILED_C_FLAGS}" "${c_flag}" C_FLAG_FAILED)
if(${C_FLAG_FOUND} EQUAL -1 AND ${C_FLAG_FAILED} EQUAL -1)
unset(C_FLAG_SUPPORTED CACHE)
message("Checking C compiler flag support for: " ${c_flag})
check_c_compiler_flag("${c_flag}" C_FLAG_SUPPORTED)
if(${C_FLAG_SUPPORTED})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${c_flag}" CACHE STRING "")
else()
set(DRACO_FAILED_C_FLAGS
"${DRACO_FAILED_C_FLAGS} ${c_flag}"
CACHE STRING "" FORCE)
endif()
endif()
endmacro()
# Checks C++ compiler for support of $cxx_flag. Adds $cxx_flag to
# $CMAKE_CXX_FLAGS when the compile test passes. Caches $c_flag in
# $DRACO_FAILED_CXX_FLAGS when the test fails.
macro(add_cxx_flag_if_supported cxx_flag)
unset(CXX_FLAG_FOUND CACHE)
string(FIND "${CMAKE_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FOUND)
unset(CXX_FLAG_FAILED CACHE)
string(FIND "${DRACO_FAILED_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FAILED)
if(${CXX_FLAG_FOUND} EQUAL -1 AND ${CXX_FLAG_FAILED} EQUAL -1)
unset(CXX_FLAG_SUPPORTED CACHE)
message("Checking CXX compiler flag support for: " ${cxx_flag})
check_cxx_compiler_flag("${cxx_flag}" CXX_FLAG_SUPPORTED)
if(${CXX_FLAG_SUPPORTED})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${cxx_flag}" CACHE STRING "")
else()
set(DRACO_FAILED_CXX_FLAGS
"${DRACO_FAILED_CXX_FLAGS} ${cxx_flag}"
CACHE STRING "" FORCE)
endif()
endif()
endmacro()
# Convenience method for adding a flag to both the C and C++ compiler command
# lines.
macro(add_compiler_flag_if_supported flag)
add_c_flag_if_supported(${flag})
add_cxx_flag_if_supported(${flag})
endmacro()
# Checks C compiler for support of $c_flag and terminates generation when
# support is not present.
macro(require_c_flag c_flag update_c_flags)
unset(C_FLAG_FOUND CACHE)
string(FIND "${CMAKE_C_FLAGS}" "${c_flag}" C_FLAG_FOUND)
if(${C_FLAG_FOUND} EQUAL -1)
unset(HAVE_C_FLAG CACHE)
message("Checking C compiler flag support for: " ${c_flag})
check_c_compiler_flag("${c_flag}" HAVE_C_FLAG)
if(NOT ${HAVE_C_FLAG})
message(
FATAL_ERROR "${PROJECT_NAME} requires support for C flag: ${c_flag}.")
endif()
if(${update_c_flags})
set(CMAKE_C_FLAGS "${c_flag} ${CMAKE_C_FLAGS}" CACHE STRING "" FORCE)
endif()
endif()
endmacro()
# Checks CXX compiler for support of $cxx_flag and terminates generation when
# support is not present.
macro(require_cxx_flag cxx_flag update_cxx_flags)
unset(CXX_FLAG_FOUND CACHE)
string(FIND "${CMAKE_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FOUND)
if(${CXX_FLAG_FOUND} EQUAL -1)
unset(HAVE_CXX_FLAG CACHE)
message("Checking CXX compiler flag support for: " ${cxx_flag})
check_cxx_compiler_flag("${cxx_flag}" HAVE_CXX_FLAG)
if(NOT ${HAVE_CXX_FLAG})
message(
FATAL_ERROR
"${PROJECT_NAME} requires support for CXX flag: ${cxx_flag}.")
endif()
if(${update_cxx_flags})
set(CMAKE_CXX_FLAGS
"${cxx_flag} ${CMAKE_CXX_FLAGS}"
CACHE STRING "" FORCE)
endif()
endif()
endmacro()
# Checks for support of $flag by both the C and CXX compilers. Terminates
# generation when support is not present in both compilers.
macro(require_compiler_flag flag update_cmake_flags)
require_c_flag(${flag} ${update_cmake_flags})
require_cxx_flag(${flag} ${update_cmake_flags})
endmacro()
# Checks only non-MSVC targets for support of $c_flag and terminates generation
# when support is not present.
macro(require_c_flag_nomsvc c_flag update_c_flags)
if(NOT MSVC)
require_c_flag(${c_flag} ${update_c_flags})
endif()
endmacro()
# Checks only non-MSVC targets for support of $cxx_flag and terminates
# generation when support is not present.
macro(require_cxx_flag_nomsvc cxx_flag update_cxx_flags)
if(NOT MSVC)
require_cxx_flag(${cxx_flag} ${update_cxx_flags})
endif()
endmacro()
# Checks only non-MSVC targets for support of $flag by both the C and CXX
# compilers. Terminates generation when support is not present in both
# compilers.
macro(require_compiler_flag_nomsvc flag update_cmake_flags)
require_c_flag_nomsvc(${flag} ${update_cmake_flags})
require_cxx_flag_nomsvc(${flag} ${update_cmake_flags})
endmacro()
# Adds $flag to assembler command line.
macro(append_as_flag flag)
unset(AS_FLAG_FOUND CACHE)
string(FIND "${DRACO_AS_FLAGS}" "${flag}" AS_FLAG_FOUND)
if(${AS_FLAG_FOUND} EQUAL -1)
set(DRACO_AS_FLAGS "${DRACO_AS_FLAGS} ${flag}")
endif()
endmacro()
# Adds $flag to the C compiler command line.
macro(append_c_flag flag)
unset(C_FLAG_FOUND CACHE)
string(FIND "${CMAKE_C_FLAGS}" "${flag}" C_FLAG_FOUND)
if(${C_FLAG_FOUND} EQUAL -1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}")
endif()
endmacro()
# Adds $flag to the CXX compiler command line.
macro(append_cxx_flag flag)
unset(CXX_FLAG_FOUND CACHE)
string(FIND "${CMAKE_CXX_FLAGS}" "${flag}" CXX_FLAG_FOUND)
if(${CXX_FLAG_FOUND} EQUAL -1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}")
endif()
endmacro()
# Adds $flag to the C and CXX compiler command lines.
macro(append_compiler_flag flag)
append_c_flag(${flag})
append_cxx_flag(${flag})
endmacro()
# Adds $flag to the executable linker command line.
macro(append_exe_linker_flag flag)
unset(LINKER_FLAG_FOUND CACHE)
string(FIND "${CMAKE_EXE_LINKER_FLAGS}" "${flag}" LINKER_FLAG_FOUND)
if(${LINKER_FLAG_FOUND} EQUAL -1)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${flag}")
endif()
endmacro()
# Adds $flag to the link flags for $target.
function(append_link_flag_to_target target flags)
unset(target_link_flags)
get_target_property(target_link_flags ${target} LINK_FLAGS)
if(target_link_flags)
unset(link_flag_found)
string(FIND "${target_link_flags}" "${flags}" link_flag_found)
if(NOT ${link_flag_found} EQUAL -1)
return()
endif()
set(target_link_flags "${target_link_flags} ${flags}")
else()
set(target_link_flags "${flags}")
endif()
set_target_properties(${target} PROPERTIES LINK_FLAGS ${target_link_flags})
endfunction()
# Adds $flag to executable linker flags, and makes sure C/CXX builds still work.
macro(require_linker_flag flag)
append_exe_linker_flag(${flag})
unset(c_passed)
draco_check_c_compiles("LINKER_FLAG_C_TEST(${flag})" "" c_passed)
unset(cxx_passed)
draco_check_cxx_compiles("LINKER_FLAG_CXX_TEST(${flag})" "" cxx_passed)
if(NOT c_passed OR NOT cxx_passed)
message(FATAL_ERROR "Linker flag test for ${flag} failed.")
endif()
endmacro()

View File

@ -0,0 +1,103 @@
if(DRACO_CMAKE_COMPILER_TESTS_CMAKE_)
return()
endif()
set(DRACO_CMAKE_COMPILER_TESTS_CMAKE_ 1)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)
# The basic main() macro used in all compile tests.
set(DRACO_C_MAIN "\nint main(void) { return 0; }")
set(DRACO_CXX_MAIN "\nint main() { return 0; }")
# Strings containing the names of passed and failed tests.
set(DRACO_C_PASSED_TESTS)
set(DRACO_C_FAILED_TESTS)
set(DRACO_CXX_PASSED_TESTS)
set(DRACO_CXX_FAILED_TESTS)
macro(draco_push_var var new_value)
set(SAVED_${var} ${var})
set(${var} ${new_value})
endmacro()
macro(draco_pop_var var)
set(var ${SAVED_${var}})
unset(SAVED_${var})
endmacro()
# Confirms $test_source compiles and stores $test_name in one of
# $DRACO_C_PASSED_TESTS or $DRACO_C_FAILED_TESTS depending on out come. When the
# test passes $result_var is set to 1. When it fails $result_var is unset. The
# test is not run if the test name is found in either of the passed or failed
# test variables.
macro(draco_check_c_compiles test_name test_source result_var)
unset(C_TEST_PASSED CACHE)
unset(C_TEST_FAILED CACHE)
string(FIND "${DRACO_C_PASSED_TESTS}" "${test_name}" C_TEST_PASSED)
string(FIND "${DRACO_C_FAILED_TESTS}" "${test_name}" C_TEST_FAILED)
if(${C_TEST_PASSED} EQUAL -1 AND ${C_TEST_FAILED} EQUAL -1)
unset(C_TEST_COMPILED CACHE)
message("Running C compiler test: ${test_name}")
check_c_source_compiles("${test_source} ${DRACO_C_MAIN}" C_TEST_COMPILED)
set(${result_var} ${C_TEST_COMPILED})
if(${C_TEST_COMPILED})
set(DRACO_C_PASSED_TESTS "${DRACO_C_PASSED_TESTS} ${test_name}")
else()
set(DRACO_C_FAILED_TESTS "${DRACO_C_FAILED_TESTS} ${test_name}")
message("C Compiler test ${test_name} failed.")
endif()
elseif(NOT ${C_TEST_PASSED} EQUAL -1)
set(${result_var} 1)
else() # ${C_TEST_FAILED} NOT EQUAL -1
unset(${result_var})
endif()
endmacro()
# Confirms $test_source compiles and stores $test_name in one of
# $DRACO_CXX_PASSED_TESTS or $DRACO_CXX_FAILED_TESTS depending on out come. When
# the test passes $result_var is set to 1. When it fails $result_var is unset.
# The test is not run if the test name is found in either of the passed or
# failed test variables.
macro(draco_check_cxx_compiles test_name test_source result_var)
unset(CXX_TEST_PASSED CACHE)
unset(CXX_TEST_FAILED CACHE)
string(FIND "${DRACO_CXX_PASSED_TESTS}" "${test_name}" CXX_TEST_PASSED)
string(FIND "${DRACO_CXX_FAILED_TESTS}" "${test_name}" CXX_TEST_FAILED)
if(${CXX_TEST_PASSED} EQUAL -1 AND ${CXX_TEST_FAILED} EQUAL -1)
unset(CXX_TEST_COMPILED CACHE)
message("Running CXX compiler test: ${test_name}")
check_cxx_source_compiles("${test_source} ${DRACO_CXX_MAIN}"
CXX_TEST_COMPILED)
set(${result_var} ${CXX_TEST_COMPILED})
if(${CXX_TEST_COMPILED})
set(DRACO_CXX_PASSED_TESTS "${DRACO_CXX_PASSED_TESTS} ${test_name}")
else()
set(DRACO_CXX_FAILED_TESTS "${DRACO_CXX_FAILED_TESTS} ${test_name}")
message("CXX Compiler test ${test_name} failed.")
endif()
elseif(NOT ${CXX_TEST_PASSED} EQUAL -1)
set(${result_var} 1)
else() # ${CXX_TEST_FAILED} NOT EQUAL -1
unset(${result_var})
endif()
endmacro()
# Convenience macro that confirms $test_source compiles as C and C++.
# $result_var is set to 1 when both tests are successful, and 0 when one or both
# tests fail. Note: This macro is intended to be used to write to result
# variables that are expanded via configure_file(). $result_var is set to 1 or 0
# to allow direct usage of the value in generated source files.
macro(draco_check_source_compiles test_name test_source result_var)
unset(C_PASSED)
unset(CXX_PASSED)
draco_check_c_compiles(${test_name} ${test_source} C_PASSED)
draco_check_cxx_compiles(${test_name} ${test_source} CXX_PASSED)
if(${C_PASSED} AND ${CXX_PASSED})
set(${result_var} 1)
else()
set(${result_var} 0)
endif()
endmacro()

View File

@ -0,0 +1,2 @@
set(DRACO_INCLUDE_DIRS "@DRACO_INCLUDE_DIRS@")
set(DRACO_LIBRARIES "draco")

View File

@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: @PROJECT_NAME@
Description: Draco geometry de(com)pression library.
Version: @DRACO_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -ldraco
Libs.private: @CMAKE_THREAD_LIBS_INIT@

View File

@ -0,0 +1,117 @@
if(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_
set(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_ 1)
# Utility for controlling the main draco library dependency. This changes in
# shared builds, and when an optional target requires a shared library build.
macro(set_draco_target)
if(MSVC OR WIN32)
set(draco_dependency draco)
set(draco_plugin_dependency ${draco_dependency})
else()
if(BUILD_SHARED_LIBS)
set(draco_dependency draco_shared)
else()
set(draco_dependency draco_static)
endif()
set(draco_plugin_dependency draco_static)
endif()
if(BUILD_SHARED_LIBS)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
endmacro()
# Configures flags and sets build system globals.
macro(draco_set_build_definitions)
string(TOLOWER "${CMAKE_BUILD_TYPE}" build_type_lowercase)
if(build_type_lowercase MATCHES "rel" AND DRACO_FAST)
if(MSVC)
list(APPEND draco_msvc_cxx_flags "/Ox")
else()
list(APPEND draco_base_cxx_flags "-O3")
endif()
endif()
draco_load_version_info()
set(DRACO_SOVERSION 1)
list(APPEND draco_include_paths "${draco_root}" "${draco_root}/src"
"${draco_build}")
if(DRACO_ABSL)
list(APPEND draco_include_path "${draco_root}/third_party/abseil-cpp")
endif()
list(APPEND draco_gtest_include_paths
"${draco_root}/../googletest/googlemock/include"
"${draco_root}/../googletest/googlemock"
"${draco_root}/../googletest/googletest/include"
"${draco_root}/../googletest/googletest")
list(APPEND draco_test_include_paths ${draco_include_paths}
${draco_gtest_include_paths})
list(APPEND draco_defines "DRACO_CMAKE=1"
"DRACO_FLAGS_SRCDIR=\"${draco_root}\""
"DRACO_FLAGS_TMPDIR=\"/tmp\"")
if(MSVC OR WIN32)
list(APPEND draco_defines "_CRT_SECURE_NO_DEPRECATE=1" "NOMINMAX=1")
if(BUILD_SHARED_LIBS)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif()
endif()
if(ANDROID)
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a")
set(CMAKE_ANDROID_ARM_MODE ON)
endif()
endif()
set_draco_target()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6")
# Quiet warnings in copy-list-initialization where {} elision has always
# been allowed.
list(APPEND draco_clang_cxx_flags "-Wno-missing-braces")
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "7")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7")
# Quiet gcc 6 vs 7 abi warnings:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77728
list(APPEND draco_base_cxx_flags "-Wno-psabi")
list(APPEND ABSL_GCC_FLAGS "-Wno-psabi")
endif()
endif()
endif()
# Source file names ending in these suffixes will have the appropriate
# compiler flags added to their compile commands to enable intrinsics.
set(draco_neon_source_file_suffix "neon.cc")
set(draco_sse4_source_file_suffix "sse4.cc")
if((${CMAKE_CXX_COMPILER_ID}
STREQUAL
"GNU"
AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 5)
OR (${CMAKE_CXX_COMPILER_ID}
STREQUAL
"Clang"
AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4))
message(
WARNING "GNU/GCC < v5 or Clang/LLVM < v4, ENABLING COMPATIBILITY MODE.")
draco_enable_feature(FEATURE "DRACO_OLD_GCC")
endif()
if(EMSCRIPTEN)
draco_check_emscripten_environment()
draco_get_required_emscripten_flags(FLAG_LIST_VAR draco_base_cxx_flags)
endif()
endmacro()

View File

@ -0,0 +1,28 @@
if(DRACO_CMAKE_DRACO_CPU_DETECTION_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_CPU_DETECTION_CMAKE_
set(DRACO_CMAKE_DRACO_CPU_DETECTION_CMAKE_ 1)
# Detect optimizations available for the current target CPU.
macro(draco_optimization_detect)
if(DRACO_ENABLE_OPTIMIZATIONS)
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" cpu_lowercase)
if(cpu_lowercase MATCHES "^arm|^aarch64")
set(draco_have_neon ON)
elseif(cpu_lowercase MATCHES "^x86|amd64")
set(draco_have_sse4 ON)
endif()
endif()
if(draco_have_neon AND DRACO_ENABLE_NEON)
list(APPEND draco_defines "DRACO_ENABLE_NEON=1")
else()
list(APPEND draco_defines "DRACO_ENABLE_NEON=0")
endif()
if(draco_have_sse4 AND DRACO_ENABLE_SSE4_1)
list(APPEND draco_defines "DRACO_ENABLE_SSE4_1=1")
else()
list(APPEND draco_defines "DRACO_ENABLE_SSE4_1=0")
endif()
endmacro()

View File

@ -0,0 +1,185 @@
if(DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_
# Checks environment for Emscripten prerequisites.
macro(draco_check_emscripten_environment)
if(NOT PYTHONINTERP_FOUND)
message(
FATAL_ERROR
"Python required for Emscripten builds, but cmake cannot find it.")
endif()
if(NOT EXISTS "$ENV{EMSCRIPTEN}")
message(
FATAL_ERROR
"The EMSCRIPTEN environment variable must be set. See README.md.")
endif()
endmacro()
# Obtains the required Emscripten flags for Draco targets.
macro(draco_get_required_emscripten_flags)
set(em_FLAG_LIST_VAR)
set(em_flags)
set(em_single_arg_opts FLAG_LIST_VAR)
set(em_multi_arg_opts)
cmake_parse_arguments(em "${em_flags}" "${em_single_arg_opts}"
"${em_multi_arg_opts}" ${ARGN})
if(NOT em_FLAG_LIST_VAR)
message(FATAL "draco_get_required_emscripten_flags: FLAG_LIST_VAR required")
endif()
if(DRACO_JS_GLUE)
unset(required_flags)
list(APPEND ${em_FLAG_LIST_VAR} "-sALLOW_MEMORY_GROWTH=1")
list(APPEND ${em_FLAG_LIST_VAR} "-Wno-almost-asm")
list(APPEND ${em_FLAG_LIST_VAR} "--memory-init-file" "0")
list(APPEND ${em_FLAG_LIST_VAR} "-fno-omit-frame-pointer")
list(APPEND ${em_FLAG_LIST_VAR} "-sMODULARIZE=1")
list(APPEND ${em_FLAG_LIST_VAR} "-sNO_FILESYSTEM=1")
list(APPEND ${em_FLAG_LIST_VAR} "-sEXPORTED_RUNTIME_METHODS=[]")
list(APPEND ${em_FLAG_LIST_VAR} "-sPRECISE_F32=1")
list(APPEND ${em_FLAG_LIST_VAR} "-sNODEJS_CATCH_EXIT=0")
list(APPEND ${em_FLAG_LIST_VAR} "-sNODEJS_CATCH_REJECTION=0")
if(DRACO_FAST)
list(APPEND ${em_FLAG_LIST_VAR} "--llvm-lto" "1")
endif()
if(DRACO_WASM)
list(APPEND ${em_FLAG_LIST_VAR} "-sWASM=1")
else()
list(APPEND ${em_FLAG_LIST_VAR} "-sWASM=0")
endif()
if(DRACO_IE_COMPATIBLE)
list(APPEND ${em_FLAG_LIST_VAR} "-sLEGACY_VM_SUPPORT=1")
endif()
endif()
endmacro()
# Macro for generating C++ glue code from IDL for Emscripten targets. Executes
# python to generate the C++ binding, and establishes dendency: $OUTPUT_PATH.cpp
# on $INPUT_IDL.
macro(draco_generate_emscripten_glue)
set(glue_flags)
set(glue_single_arg_opts INPUT_IDL OUTPUT_PATH)
set(glue_multi_arg_opts)
cmake_parse_arguments(glue "${glue_flags}" "${glue_single_arg_opts}"
"${glue_multi_arg_opts}" ${ARGN})
if(DRACO_VERBOSE GREATER 1)
message("--------- draco_generate_emscripten_glue -----------\n"
"glue_INPUT_IDL=${glue_INPUT_IDL}\n"
"glue_OUTPUT_PATH=${glue_OUTPUT_PATH}\n" ]
"----------------------------------------------------\n")
endif()
if(NOT glue_INPUT_IDL OR NOT glue_OUTPUT_PATH)
message(
FATAL_ERROR
"draco_generate_emscripten_glue: INPUT_IDL and OUTPUT_PATH required.")
endif()
# Generate the glue source.
execute_process(COMMAND ${PYTHON_EXECUTABLE}
$ENV{EMSCRIPTEN}/tools/webidl_binder.py
${glue_INPUT_IDL} ${glue_OUTPUT_PATH})
if(NOT EXISTS "${glue_OUTPUT_PATH}.cpp")
message(FATAL_ERROR "JS glue generation failed for ${glue_INPUT_IDL}.")
endif()
# Create a dependency so that it regenerated on edits.
add_custom_command(OUTPUT "${glue_OUTPUT_PATH}.cpp"
COMMAND ${PYTHON_EXECUTABLE}
$ENV{EMSCRIPTEN}/tools/webidl_binder.py
${glue_INPUT_IDL} ${glue_OUTPUT_PATH}
DEPENDS ${draco_js_dec_idl}
COMMENT "Generating ${glue_OUTPUT_PATH}.cpp."
WORKING_DIRECTORY ${draco_build}
VERBATIM)
endmacro()
# Wrapper for draco_add_executable() that handles the extra work necessary for
# emscripten targets when generating JS glue:
#
# ~~~
# - Set source level dependency on the C++ binding.
# - Pre/Post link emscripten magic.
#
# Required args:
# - GLUE_PATH: Base path for glue file. Used to generate .cpp and .js files.
# - PRE_LINK_JS_SOURCES: em_link_pre_js() source files.
# - POST_LINK_JS_SOURCES: em_link_post_js() source files.
# Optional args:
# - FEATURES:
# ~~~
macro(draco_add_emscripten_executable)
unset(emexe_NAME)
unset(emexe_FEATURES)
unset(emexe_SOURCES)
unset(emexe_DEFINES)
unset(emexe_INCLUDES)
unset(emexe_LINK_FLAGS)
set(optional_args)
set(single_value_args NAME GLUE_PATH)
set(multi_value_args SOURCES DEFINES FEATURES INCLUDES LINK_FLAGS
PRE_LINK_JS_SOURCES POST_LINK_JS_SOURCES)
cmake_parse_arguments(emexe "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT
(emexe_GLUE_PATH
AND emexe_POST_LINK_JS_SOURCES
AND emexe_PRE_LINK_JS_SOURCES))
message(FATAL
"draco_add_emscripten_executable: GLUE_PATH PRE_LINK_JS_SOURCES "
"POST_LINK_JS_SOURCES args required.")
endif()
if(DRACO_VERBOSE GREATER 1)
message("--------- draco_add_emscripten_executable ---------\n"
"emexe_NAME=${emexe_NAME}\n"
"emexe_SOURCES=${emexe_SOURCES}\n"
"emexe_DEFINES=${emexe_DEFINES}\n"
"emexe_INCLUDES=${emexe_INCLUDES}\n"
"emexe_LINK_FLAGS=${emexe_LINK_FLAGS}\n"
"emexe_GLUE_PATH=${emexe_GLUE_PATH}\n"
"emexe_FEATURES=${emexe_FEATURES}\n"
"emexe_PRE_LINK_JS_SOURCES=${emexe_PRE_LINK_JS_SOURCES}\n"
"emexe_POST_LINK_JS_SOURCES=${emexe_POST_LINK_JS_SOURCES}\n"
"----------------------------------------------------\n")
endif()
# The Emscripten linker needs the C++ flags in addition to whatever has been
# passed in with the target.
list(APPEND emexe_LINK_FLAGS ${DRACO_CXX_FLAGS})
if(DRACO_GLTF)
draco_add_executable(NAME
${emexe_NAME}
OUTPUT_NAME
${emexe_NAME}_gltf
SOURCES
${emexe_SOURCES}
DEFINES
${emexe_DEFINES}
INCLUDES
${emexe_INCLUDES}
LINK_FLAGS
${emexe_LINK_FLAGS})
else()
draco_add_executable(NAME ${emexe_NAME} SOURCES ${emexe_SOURCES} DEFINES
${emexe_DEFINES} INCLUDES ${emexe_INCLUDES} LINK_FLAGS
${emexe_LINK_FLAGS})
endif()
foreach(feature ${emexe_FEATURES})
draco_enable_feature(FEATURE ${feature} TARGETS ${emexe_NAME})
endforeach()
set_property(SOURCE ${emexe_SOURCES}
APPEND
PROPERTY OBJECT_DEPENDS "${emexe_GLUE_PATH}.cpp")
em_link_pre_js(${emexe_NAME} ${emexe_PRE_LINK_JS_SOURCES})
em_link_post_js(${emexe_NAME} "${emexe_GLUE_PATH}.js"
${emexe_POST_LINK_JS_SOURCES})
endmacro()

View File

@ -0,0 +1,63 @@
if(DRACO_CMAKE_DRACO_FEATURES_CMAKE_)
return()
endif()
set(DRACO_CMAKE_DRACO_FEATURES_CMAKE_ 1)
set(draco_features_file_name "${draco_build_dir}/draco/draco_features.h")
set(draco_features_list)
# Macro that handles tracking of Draco preprocessor symbols for the purpose of
# producing draco_features.h.
#
# draco_enable_feature(FEATURE <feature_name> [TARGETS <target_name>]) FEATURE
# is required. It should be a Draco preprocessor symbol. TARGETS is optional. It
# can be one or more draco targets.
#
# When the TARGETS argument is not present the preproc symbol is added to
# draco_features.h. When it is draco_features.h is unchanged, and
# target_compile_options() is called for each target specified.
macro(draco_enable_feature)
set(def_flags)
set(def_single_arg_opts FEATURE)
set(def_multi_arg_opts TARGETS)
cmake_parse_arguments(DEF "${def_flags}" "${def_single_arg_opts}"
"${def_multi_arg_opts}" ${ARGN})
if("${DEF_FEATURE}" STREQUAL "")
message(FATAL_ERROR "Empty FEATURE passed to draco_enable_feature().")
endif()
# Do nothing/return early if $DEF_FEATURE is already in the list.
list(FIND draco_features_list ${DEF_FEATURE} df_index)
if(NOT df_index EQUAL -1)
return()
endif()
list(LENGTH DEF_TARGETS df_targets_list_length)
if(${df_targets_list_length} EQUAL 0)
list(APPEND draco_features_list ${DEF_FEATURE})
else()
foreach(target ${DEF_TARGETS})
target_compile_definitions(${target} PRIVATE ${DEF_FEATURE})
endforeach()
endif()
endmacro()
# Function for generating draco_features.h.
function(draco_generate_features_h)
file(WRITE "${draco_features_file_name}.new"
"// GENERATED FILE -- DO NOT EDIT\n\n" "#ifndef DRACO_FEATURES_H_\n"
"#define DRACO_FEATURES_H_\n\n")
foreach(feature ${draco_features_list})
file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n")
endforeach()
file(APPEND "${draco_features_file_name}.new"
"\n#endif // DRACO_FEATURES_H_")
# Will replace ${draco_features_file_name} only if the file content has
# changed. This prevents forced Draco rebuilds after CMake runs.
configure_file("${draco_features_file_name}.new"
"${draco_features_file_name}")
file(REMOVE "${draco_features_file_name}.new")
endfunction()

View File

@ -0,0 +1,238 @@
if(DRACO_CMAKE_DRACO_FLAGS_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_FLAGS_CMAKE_
set(DRACO_CMAKE_DRACO_FLAGS_CMAKE_ 1)
include(CheckCXXCompilerFlag)
include(CheckCXXSourceCompiles)
# Adds compiler flags specified by FLAGS to the sources specified by SOURCES:
#
# draco_set_compiler_flags_for_sources(SOURCES <sources> FLAGS <flags>)
macro(draco_set_compiler_flags_for_sources)
unset(compiler_SOURCES)
unset(compiler_FLAGS)
unset(optional_args)
unset(single_value_args)
set(multi_value_args SOURCES FLAGS)
cmake_parse_arguments(compiler "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT (compiler_SOURCES AND compiler_FLAGS))
draco_die("draco_set_compiler_flags_for_sources: SOURCES and "
"FLAGS required.")
endif()
set_source_files_properties(${compiler_SOURCES} PROPERTIES COMPILE_FLAGS
${compiler_FLAGS})
if(DRACO_VERBOSE GREATER 1)
foreach(source ${compiler_SOURCES})
foreach(flag ${compiler_FLAGS})
message("draco_set_compiler_flags_for_sources: source:${source} "
"flag:${flag}")
endforeach()
endforeach()
endif()
endmacro()
# Tests compiler flags stored in list(s) specified by FLAG_LIST_VAR_NAMES, adds
# flags to $DRACO_CXX_FLAGS when tests pass. Terminates configuration if
# FLAG_REQUIRED is specified and any flag check fails.
#
# ~~~
# draco_test_cxx_flag(<FLAG_LIST_VAR_NAMES <flag list variable(s)>>
# [FLAG_REQUIRED])
# ~~~
macro(draco_test_cxx_flag)
unset(cxx_test_FLAG_LIST_VAR_NAMES)
unset(cxx_test_FLAG_REQUIRED)
unset(single_value_args)
set(optional_args FLAG_REQUIRED)
set(multi_value_args FLAG_LIST_VAR_NAMES)
cmake_parse_arguments(cxx_test "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT cxx_test_FLAG_LIST_VAR_NAMES)
draco_die("draco_test_cxx_flag: FLAG_LIST_VAR_NAMES required")
endif()
unset(cxx_flags)
foreach(list_var ${cxx_test_FLAG_LIST_VAR_NAMES})
if(DRACO_VERBOSE)
message("draco_test_cxx_flag: adding ${list_var} to cxx_flags")
endif()
list(APPEND cxx_flags ${${list_var}})
endforeach()
if(DRACO_VERBOSE)
message("CXX test: all flags: ${cxx_flags}")
endif()
unset(all_cxx_flags)
list(APPEND all_cxx_flags ${DRACO_CXX_FLAGS} ${cxx_flags})
# Turn off output from check_cxx_source_compiles. Print status directly
# instead since the logging messages from check_cxx_source_compiles can be
# quite confusing.
set(CMAKE_REQUIRED_QUIET TRUE)
# Run the actual compile test.
unset(draco_all_cxx_flags_pass CACHE)
message("--- Running combined CXX flags test, flags: ${all_cxx_flags}")
check_cxx_compiler_flag("${all_cxx_flags}" draco_all_cxx_flags_pass)
if(cxx_test_FLAG_REQUIRED AND NOT draco_all_cxx_flags_pass)
draco_die("Flag test failed for required flag(s): "
"${all_cxx_flags} and FLAG_REQUIRED specified.")
endif()
if(draco_all_cxx_flags_pass)
# Test passed: update the global flag list used by the draco target creation
# wrappers.
set(DRACO_CXX_FLAGS ${cxx_flags})
list(REMOVE_DUPLICATES DRACO_CXX_FLAGS)
if(DRACO_VERBOSE)
message("DRACO_CXX_FLAGS=${DRACO_CXX_FLAGS}")
endif()
message("--- Passed combined CXX flags test")
else()
message("--- Failed combined CXX flags test, testing flags individually.")
if(cxx_flags)
message("--- Testing flags from $cxx_flags: " "${cxx_flags}")
foreach(cxx_flag ${cxx_flags})
# Since 3.17.0 check_cxx_compiler_flag() sets a normal variable at
# parent scope while check_cxx_source_compiles() continues to set an
# internal cache variable, so we unset both to avoid the failure /
# success state persisting between checks. This has been fixed in newer
# CMake releases, but 3.17 is pretty common: we will need this to avoid
# weird build breakages while the fix propagates.
unset(cxx_flag_test_passed)
unset(cxx_flag_test_passed CACHE)
message("--- Testing flag: ${cxx_flag}")
check_cxx_compiler_flag("${cxx_flag}" cxx_flag_test_passed)
if(cxx_flag_test_passed)
message("--- Passed test for ${cxx_flag}")
else()
list(REMOVE_ITEM cxx_flags ${cxx_flag})
message("--- Failed test for ${cxx_flag}, flag removed.")
endif()
endforeach()
set(DRACO_CXX_FLAGS ${cxx_flags})
endif()
endif()
if(DRACO_CXX_FLAGS)
list(REMOVE_DUPLICATES DRACO_CXX_FLAGS)
endif()
endmacro()
# Tests executable linker flags stored in list specified by FLAG_LIST_VAR_NAME,
# adds flags to $DRACO_EXE_LINKER_FLAGS when test passes. Terminates
# configuration when flag check fails. draco_set_cxx_flags() must be called
# before calling this macro because it assumes $DRACO_CXX_FLAGS contains only
# valid CXX flags.
#
# draco_test_exe_linker_flag(<FLAG_LIST_VAR_NAME <flag list variable)>)
macro(draco_test_exe_linker_flag)
unset(link_FLAG_LIST_VAR_NAME)
unset(optional_args)
unset(multi_value_args)
set(single_value_args FLAG_LIST_VAR_NAME)
cmake_parse_arguments(link "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT link_FLAG_LIST_VAR_NAME)
draco_die("draco_test_link_flag: FLAG_LIST_VAR_NAME required")
endif()
draco_set_and_stringify(DEST linker_flags SOURCE_VARS
${link_FLAG_LIST_VAR_NAME})
if(DRACO_VERBOSE)
message("EXE LINKER test: all flags: ${linker_flags}")
endif()
# Tests of $DRACO_CXX_FLAGS have already passed. Include them with the linker
# test.
draco_set_and_stringify(DEST CMAKE_REQUIRED_FLAGS SOURCE_VARS DRACO_CXX_FLAGS)
# Cache the global exe linker flags.
if(CMAKE_EXE_LINKER_FLAGS)
set(cached_CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS})
draco_set_and_stringify(DEST CMAKE_EXE_LINKER_FLAGS SOURCE ${linker_flags})
endif()
draco_set_and_stringify(DEST CMAKE_EXE_LINKER_FLAGS SOURCE ${linker_flags}
${CMAKE_EXE_LINKER_FLAGS})
# Turn off output from check_cxx_source_compiles. Print status directly
# instead since the logging messages from check_cxx_source_compiles can be
# quite confusing.
set(CMAKE_REQUIRED_QUIET TRUE)
message("--- Running EXE LINKER test for flags: ${linker_flags}")
unset(linker_flag_test_passed CACHE)
set(draco_cxx_main "\nint main() { return 0; }")
check_cxx_source_compiles("${draco_cxx_main}" linker_flag_test_passed)
if(NOT linker_flag_test_passed)
draco_die("EXE LINKER test failed.")
endif()
message("--- Passed EXE LINKER flag test.")
# Restore cached global exe linker flags.
if(cached_CMAKE_EXE_LINKER_FLAGS)
set(CMAKE_EXE_LINKER_FLAGS ${cached_CMAKE_EXE_LINKER_FLAGS})
else()
unset(CMAKE_EXE_LINKER_FLAGS)
endif()
endmacro()
# Runs the draco compiler tests. This macro builds up the list of list var(s)
# that is passed to draco_test_cxx_flag().
#
# Note: draco_set_build_definitions() must be called before this macro.
macro(draco_set_cxx_flags)
unset(cxx_flag_lists)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
list(APPEND cxx_flag_lists draco_base_cxx_flags)
endif()
# Append clang flags after the base set to allow -Wno* overrides to take
# effect. Some of the base flags may enable a large set of warnings, e.g.,
# -Wall.
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND cxx_flag_lists draco_clang_cxx_flags)
endif()
if(MSVC)
list(APPEND cxx_flag_lists draco_msvc_cxx_flags)
endif()
draco_set_and_stringify(DEST cxx_flags SOURCE_VARS ${cxx_flag_lists})
if(DRACO_VERBOSE)
message("draco_set_cxx_flags: internal CXX flags: ${cxx_flags}")
endif()
if(DRACO_CXX_FLAGS)
list(APPEND cxx_flag_lists DRACO_CXX_FLAGS)
if(DRACO_VERBOSE)
message("draco_set_cxx_flags: user CXX flags: ${DRACO_CXX_FLAGS}")
endif()
endif()
draco_set_and_stringify(DEST cxx_flags SOURCE_VARS ${cxx_flag_lists})
if(cxx_flags)
draco_test_cxx_flag(FLAG_LIST_VAR_NAMES ${cxx_flag_lists})
endif()
endmacro()

View File

@ -0,0 +1,110 @@
if(DRACO_CMAKE_DRACO_HELPERS_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_HELPERS_CMAKE_
set(DRACO_CMAKE_DRACO_HELPERS_CMAKE_ 1)
# Kills build generation using message(FATAL_ERROR) and outputs all data passed
# to the console via use of $ARGN.
macro(draco_die)
message(FATAL_ERROR ${ARGN})
endmacro()
# Converts semi-colon delimited list variable(s) to string. Output is written to
# variable supplied via the DEST parameter. Input is from an expanded variable
# referenced by SOURCE and/or variable(s) referenced by SOURCE_VARS.
macro(draco_set_and_stringify)
set(optional_args)
set(single_value_args DEST SOURCE_VAR)
set(multi_value_args SOURCE SOURCE_VARS)
cmake_parse_arguments(sas "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT sas_DEST OR NOT (sas_SOURCE OR sas_SOURCE_VARS))
draco_die("draco_set_and_stringify: DEST and at least one of SOURCE "
"SOURCE_VARS required.")
endif()
unset(${sas_DEST})
if(sas_SOURCE)
# $sas_SOURCE is one or more expanded variables, just copy the values to
# $sas_DEST.
set(${sas_DEST} "${sas_SOURCE}")
endif()
if(sas_SOURCE_VARS)
# $sas_SOURCE_VARS is one or more variable names. Each iteration expands a
# variable and appends it to $sas_DEST.
foreach(source_var ${sas_SOURCE_VARS})
set(${sas_DEST} "${${sas_DEST}} ${${source_var}}")
endforeach()
# Because $sas_DEST can be empty when entering this scope leading whitespace
# can be introduced to $sas_DEST on the first iteration of the above loop.
# Remove it:
string(STRIP "${${sas_DEST}}" ${sas_DEST})
endif()
# Lists in CMake are simply semicolon delimited strings, so stringification is
# just a find and replace of the semicolon.
string(REPLACE ";" " " ${sas_DEST} "${${sas_DEST}}")
if(DRACO_VERBOSE GREATER 1)
message("draco_set_and_stringify: ${sas_DEST}=${${sas_DEST}}")
endif()
endmacro()
# Creates a dummy source file in $DRACO_GENERATED_SOURCES_DIRECTORY and adds it
# to the specified target. Optionally adds its path to a list variable.
#
# draco_create_dummy_source_file(<TARGET <target> BASENAME <basename of file>>
# [LISTVAR <list variable>])
macro(draco_create_dummy_source_file)
set(optional_args)
set(single_value_args TARGET BASENAME LISTVAR)
set(multi_value_args)
cmake_parse_arguments(cdsf "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT cdsf_TARGET OR NOT cdsf_BASENAME)
draco_die("draco_create_dummy_source_file: TARGET and BASENAME required.")
endif()
if(NOT DRACO_GENERATED_SOURCES_DIRECTORY)
set(DRACO_GENERATED_SOURCES_DIRECTORY "${draco_build}/gen_src")
endif()
set(dummy_source_dir "${DRACO_GENERATED_SOURCES_DIRECTORY}")
set(dummy_source_file
"${dummy_source_dir}/draco_${cdsf_TARGET}_${cdsf_BASENAME}.cc")
set(dummy_source_code
"// Generated file. DO NOT EDIT!\n"
"// C++ source file created for target ${cdsf_TARGET}.\n"
"void draco_${cdsf_TARGET}_${cdsf_BASENAME}_dummy_function(void)\;\n"
"void draco_${cdsf_TARGET}_${cdsf_BASENAME}_dummy_function(void) {}\n")
file(WRITE "${dummy_source_file}" ${dummy_source_code})
target_sources(${cdsf_TARGET} PRIVATE ${dummy_source_file})
if(cdsf_LISTVAR)
list(APPEND ${cdsf_LISTVAR} "${dummy_source_file}")
endif()
endmacro()
# Loads the version string from $draco_source/draco/version.h and sets
# $DRACO_VERSION.
macro(draco_load_version_info)
file(STRINGS "${draco_src_root}/core/draco_version.h" version_file_strings)
foreach(str ${version_file_strings})
if(str MATCHES "char kDracoVersion")
string(FIND "${str}" "\"" open_quote_pos)
string(FIND "${str}" ";" semicolon_pos)
math(EXPR open_quote_pos "${open_quote_pos} + 1")
math(EXPR close_quote_pos "${semicolon_pos} - 1")
math(EXPR version_string_length "${close_quote_pos} - ${open_quote_pos}")
string(SUBSTRING "${str}" ${open_quote_pos} ${version_string_length}
DRACO_VERSION)
break()
endif()
endforeach()
endmacro()

View File

@ -0,0 +1,79 @@
if(DRACO_CMAKE_DRACO_INSTALL_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_INSTALL_CMAKE_
set(DRACO_CMAKE_DRACO_INSTALL_CMAKE_ 1)
# Sets up the draco install targets. Must be called after the static library
# target is created.
macro(draco_setup_install_target)
include(GNUInstallDirs)
# pkg-config: draco.pc
set(prefix "${CMAKE_INSTALL_PREFIX}")
set(exec_prefix "\${prefix}")
set(libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
set(draco_lib_name "draco")
configure_file("${draco_root}/cmake/draco.pc.template"
"${draco_build}/draco.pc" @ONLY NEWLINE_STYLE UNIX)
install(FILES "${draco_build}/draco.pc"
DESTINATION "${prefix}/${CMAKE_INSTALL_LIBDIR}/pkgconfig")
# CMake config: draco-config.cmake
set(DRACO_INCLUDE_DIRS "${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
configure_file("${draco_root}/cmake/draco-config.cmake.template"
"${draco_build}/draco-config.cmake" @ONLY NEWLINE_STYLE UNIX)
install(
FILES "${draco_build}/draco-config.cmake"
DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/cmake")
foreach(file ${draco_sources})
if(file MATCHES "h$")
list(APPEND draco_api_includes ${file})
endif()
endforeach()
# Strip $draco_src_root from the file paths: we need to install relative to
# $include_directory.
list(TRANSFORM draco_api_includes REPLACE "${draco_src_root}/" "")
set(include_directory "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
foreach(draco_api_include ${draco_api_includes})
get_filename_component(file_directory ${draco_api_include} DIRECTORY)
set(target_directory "${include_directory}/draco/${file_directory}")
install(FILES ${draco_src_root}/${draco_api_include}
DESTINATION "${target_directory}")
endforeach()
install(
FILES "${draco_build}/draco/draco_features.h"
DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/draco/")
install(TARGETS draco_decoder DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
install(TARGETS draco_encoder DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
if(WIN32)
install(TARGETS draco DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
else()
install(TARGETS draco_static DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
if(BUILD_SHARED_LIBS)
install(TARGETS draco_shared DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()
endif()
if(DRACO_UNITY_PLUGIN)
install(TARGETS dracodec_unity DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()
if(DRACO_MAYA_PLUGIN)
install(TARGETS draco_maya_wrapper DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()
endmacro()

View File

@ -0,0 +1,96 @@
if(DRACO_CMAKE_DRACO_INTRINSICS_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_INTRINSICS_CMAKE_
set(DRACO_CMAKE_DRACO_INTRINSICS_CMAKE_ 1)
# Returns the compiler flag for the SIMD intrinsics suffix specified by the
# SUFFIX argument via the variable specified by the VARIABLE argument:
# draco_get_intrinsics_flag_for_suffix(SUFFIX <suffix> VARIABLE <var name>)
macro(draco_get_intrinsics_flag_for_suffix)
unset(intrinsics_SUFFIX)
unset(intrinsics_VARIABLE)
unset(optional_args)
unset(multi_value_args)
set(single_value_args SUFFIX VARIABLE)
cmake_parse_arguments(intrinsics "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT (intrinsics_SUFFIX AND intrinsics_VARIABLE))
message(FATAL_ERROR "draco_get_intrinsics_flag_for_suffix: SUFFIX and "
"VARIABLE required.")
endif()
if(intrinsics_SUFFIX MATCHES "neon")
if(NOT MSVC)
set(${intrinsics_VARIABLE} "${DRACO_NEON_INTRINSICS_FLAG}")
endif()
elseif(intrinsics_SUFFIX MATCHES "sse4")
if(NOT MSVC)
set(${intrinsics_VARIABLE} "-msse4.1")
endif()
else()
message(FATAL_ERROR "draco_get_intrinsics_flag_for_suffix: Unknown "
"instrinics suffix: ${intrinsics_SUFFIX}")
endif()
if(DRACO_VERBOSE GREATER 1)
message("draco_get_intrinsics_flag_for_suffix: "
"suffix:${intrinsics_SUFFIX} flag:${${intrinsics_VARIABLE}}")
endif()
endmacro()
# Processes source files specified by SOURCES and adds intrinsics flags as
# necessary: draco_process_intrinsics_sources(SOURCES <sources>)
#
# Detects requirement for intrinsics flags using source file name suffix.
# Currently supports only SSE4.1.
macro(draco_process_intrinsics_sources)
unset(arg_TARGET)
unset(arg_SOURCES)
unset(optional_args)
set(single_value_args TARGET)
set(multi_value_args SOURCES)
cmake_parse_arguments(arg "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT (arg_TARGET AND arg_SOURCES))
message(FATAL_ERROR "draco_process_intrinsics_sources: TARGET and "
"SOURCES required.")
endif()
if(DRACO_ENABLE_SSE4_1 AND draco_have_sse4)
unset(sse4_sources)
list(APPEND sse4_sources ${arg_SOURCES})
list(FILTER sse4_sources INCLUDE REGEX
"${draco_sse4_source_file_suffix}$")
if(sse4_sources)
unset(sse4_flags)
draco_get_intrinsics_flag_for_suffix(SUFFIX
${draco_sse4_source_file_suffix}
VARIABLE sse4_flags)
if(sse4_flags)
draco_set_compiler_flags_for_sources(SOURCES ${sse4_sources} FLAGS
${sse4_flags})
endif()
endif()
endif()
if(DRACO_ENABLE_NEON AND draco_have_neon)
unset(neon_sources)
list(APPEND neon_sources ${arg_SOURCES})
list(FILTER neon_sources INCLUDE REGEX
"${draco_neon_source_file_suffix}$")
if(neon_sources AND DRACO_NEON_INTRINSICS_FLAG)
unset(neon_flags)
draco_get_intrinsics_flag_for_suffix(SUFFIX
${draco_neon_source_file_suffix}
VARIABLE neon_flags)
if(neon_flags)
draco_set_compiler_flags_for_sources(SOURCES ${neon_sources} FLAGS
${neon_flags})
endif()
endif()
endif()
endmacro()

View File

@ -0,0 +1,239 @@
if(DRACO_CMAKE_DRACO_OPTIONS_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_OPTIONS_CMAKE_
set(DRACO_CMAKE_DRACO_OPTIONS_CMAKE_)
set(draco_features_file_name "${draco_build}/draco/draco_features.h")
set(draco_features_list)
# Simple wrapper for CMake's builtin option command that tracks draco's build
# options in the list variable $draco_options.
macro(draco_option)
unset(option_NAME)
unset(option_HELPSTRING)
unset(option_VALUE)
unset(optional_args)
unset(multi_value_args)
set(single_value_args NAME HELPSTRING VALUE)
cmake_parse_arguments(option "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(NOT (option_NAME AND option_HELPSTRING AND DEFINED option_VALUE))
message(FATAL_ERROR "draco_option: NAME HELPSTRING and VALUE required.")
endif()
option(${option_NAME} ${option_HELPSTRING} ${option_VALUE})
if(DRACO_VERBOSE GREATER 2)
message("--------- draco_option ---------\n" "option_NAME=${option_NAME}\n"
"option_HELPSTRING=${option_HELPSTRING}\n"
"option_VALUE=${option_VALUE}\n"
"------------------------------------------\n")
endif()
list(APPEND draco_options ${option_NAME})
list(REMOVE_DUPLICATES draco_options)
endmacro()
# Dumps the $draco_options list via CMake message command.
macro(draco_dump_options)
foreach(option_name ${draco_options})
message("${option_name}: ${${option_name}}")
endforeach()
endmacro()
# Set default options.
macro(draco_set_default_options)
draco_option(NAME DRACO_FAST HELPSTRING "Try to build faster libs." VALUE OFF)
draco_option(NAME DRACO_JS_GLUE HELPSTRING
"Enable JS Glue and JS targets when using Emscripten." VALUE ON)
draco_option(NAME DRACO_IE_COMPATIBLE HELPSTRING
"Enable support for older IE builds when using Emscripten." VALUE
OFF)
draco_option(NAME DRACO_MESH_COMPRESSION HELPSTRING "Enable mesh compression."
VALUE ON)
draco_option(NAME DRACO_POINT_CLOUD_COMPRESSION HELPSTRING
"Enable point cloud compression." VALUE ON)
draco_option(NAME DRACO_PREDICTIVE_EDGEBREAKER HELPSTRING
"Enable predictive edgebreaker." VALUE ON)
draco_option(NAME DRACO_STANDARD_EDGEBREAKER HELPSTRING
"Enable stand edgebreaker." VALUE ON)
draco_option(NAME DRACO_BACKWARDS_COMPATIBILITY HELPSTRING
"Enable backwards compatibility." VALUE ON)
draco_option(NAME DRACO_DECODER_ATTRIBUTE_DEDUPLICATION HELPSTRING
"Enable attribute deduping." VALUE OFF)
draco_option(NAME DRACO_TESTS HELPSTRING "Enables tests." VALUE OFF)
draco_option(NAME DRACO_WASM HELPSTRING "Enables WASM support." VALUE OFF)
draco_option(NAME DRACO_UNITY_PLUGIN HELPSTRING
"Build plugin library for Unity." VALUE OFF)
draco_option(NAME DRACO_ANIMATION_ENCODING HELPSTRING "Enable animation."
VALUE OFF)
draco_option(NAME DRACO_GLTF HELPSTRING "Support GLTF." VALUE OFF)
draco_option(NAME DRACO_MAYA_PLUGIN HELPSTRING
"Build plugin library for Maya." VALUE OFF)
draco_check_deprecated_options()
endmacro()
# Warns when a deprecated option is used and sets the option that replaced it.
macro(draco_handle_deprecated_option)
unset(option_OLDNAME)
unset(option_NEWNAME)
unset(optional_args)
unset(multi_value_args)
set(single_value_args OLDNAME NEWNAME)
cmake_parse_arguments(option "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if("${${option_OLDNAME}}")
message(WARNING "${option_OLDNAME} is deprecated. Use ${option_NEWNAME}.")
set(${option_NEWNAME} ${${option_OLDNAME}})
endif()
endmacro()
# Checks for use of deprecated options.
macro(draco_check_deprecated_options)
draco_handle_deprecated_option(OLDNAME ENABLE_EXTRA_SPEED NEWNAME DRACO_FAST)
draco_handle_deprecated_option(OLDNAME ENABLE_JS_GLUE NEWNAME DRACO_JS_GLUE)
draco_handle_deprecated_option(OLDNAME ENABLE_MESH_COMPRESSION NEWNAME
DRACO_MESH_COMPRESSION)
draco_handle_deprecated_option(OLDNAME ENABLE_POINT_CLOUD_COMPRESSION NEWNAME
DRACO_POINT_CLOUD_COMPRESSION)
draco_handle_deprecated_option(OLDNAME ENABLE_PREDICTIVE_EDGEBREAKER NEWNAME
DRACO_PREDICTIVE_EDGEBREAKER)
draco_handle_deprecated_option(OLDNAME ENABLE_STANDARD_EDGEBREAKER NEWNAME
DRACO_STANDARD_EDGEBREAKER)
draco_handle_deprecated_option(OLDNAME ENABLE_BACKWARDS_COMPATIBILITY NEWNAME
DRACO_BACKWARDS_COMPATIBILITY)
draco_handle_deprecated_option(OLDNAME ENABLE_DECODER_ATTRIBUTE_DEDUPLICATION
NEWNAME DRACO_DECODER_ATTRIBUTE_DEDUPLICATION)
draco_handle_deprecated_option(OLDNAME ENABLE_TESTS NEWNAME DRACO_TESTS)
draco_handle_deprecated_option(OLDNAME ENABLE_WASM NEWNAME DRACO_WASM)
draco_handle_deprecated_option(OLDNAME BUILD_UNITY_PLUGIN NEWNAME
DRACO_UNITY_PLUGIN)
draco_handle_deprecated_option(OLDNAME BUILD_ANIMATION_ENCODING NEWNAME
DRACO_ANIMATION_ENCODING)
draco_handle_deprecated_option(OLDNAME BUILD_FOR_GLTF NEWNAME DRACO_GLTF)
draco_handle_deprecated_option(OLDNAME BUILD_MAYA_PLUGIN NEWNAME
DRACO_MAYA_PLUGIN)
draco_handle_deprecated_option(OLDNAME BUILD_USD_PLUGIN NEWNAME
BUILD_SHARED_LIBS)
endmacro()
# Macro for setting Draco features based on user configuration. Features enabled
# by this macro are Draco global.
macro(draco_set_optional_features)
if(DRACO_GLTF)
# Override settings when building for GLTF.
draco_enable_feature(FEATURE "DRACO_MESH_COMPRESSION_SUPPORTED")
draco_enable_feature(FEATURE "DRACO_NORMAL_ENCODING_SUPPORTED")
draco_enable_feature(FEATURE "DRACO_STANDARD_EDGEBREAKER_SUPPORTED")
else()
if(DRACO_POINT_CLOUD_COMPRESSION)
draco_enable_feature(FEATURE "DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED")
endif()
if(DRACO_MESH_COMPRESSION)
draco_enable_feature(FEATURE "DRACO_MESH_COMPRESSION_SUPPORTED")
draco_enable_feature(FEATURE "DRACO_NORMAL_ENCODING_SUPPORTED")
if(DRACO_STANDARD_EDGEBREAKER)
draco_enable_feature(FEATURE "DRACO_STANDARD_EDGEBREAKER_SUPPORTED")
endif()
if(DRACO_PREDICTIVE_EDGEBREAKER)
draco_enable_feature(FEATURE "DRACO_PREDICTIVE_EDGEBREAKER_SUPPORTED")
endif()
endif()
if(DRACO_BACKWARDS_COMPATIBILITY)
draco_enable_feature(FEATURE "DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED")
endif()
if(NOT EMSCRIPTEN)
# For now, enable deduplication for both encoder and decoder.
# TODO(ostava): Support for disabling attribute deduplication for the C++
# decoder is planned in future releases.
draco_enable_feature(FEATURE
DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED)
draco_enable_feature(FEATURE
DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED)
endif()
endif()
if(DRACO_UNITY_PLUGIN)
draco_enable_feature(FEATURE "DRACO_UNITY_PLUGIN")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
if(DRACO_MAYA_PLUGIN)
draco_enable_feature(FEATURE "DRACO_MAYA_PLUGIN")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
endmacro()
# Macro that handles tracking of Draco preprocessor symbols for the purpose of
# producing draco_features.h.
#
# ~~~
# draco_enable_feature(FEATURE <feature_name> [TARGETS <target_name>])
# ~~~
#
# FEATURE is required. It should be a Draco preprocessor symbol. TARGETS is
# optional. It can be one or more draco targets.
#
# When the TARGETS argument is not present the preproc symbol is added to
# draco_features.h. When it is draco_features.h is unchanged, and
# target_compile_options() is called for each target specified.
macro(draco_enable_feature)
set(def_flags)
set(def_single_arg_opts FEATURE)
set(def_multi_arg_opts TARGETS)
cmake_parse_arguments(DEF "${def_flags}" "${def_single_arg_opts}"
"${def_multi_arg_opts}" ${ARGN})
if("${DEF_FEATURE}" STREQUAL "")
message(FATAL_ERROR "Empty FEATURE passed to draco_enable_feature().")
endif()
# Do nothing/return early if $DEF_FEATURE is already in the list.
list(FIND draco_features_list ${DEF_FEATURE} df_index)
if(NOT df_index EQUAL -1)
return()
endif()
list(LENGTH DEF_TARGETS df_targets_list_length)
if(${df_targets_list_length} EQUAL 0)
list(APPEND draco_features_list ${DEF_FEATURE})
else()
foreach(target ${DEF_TARGETS})
target_compile_definitions(${target} PRIVATE ${DEF_FEATURE})
endforeach()
endif()
endmacro()
# Function for generating draco_features.h.
function(draco_generate_features_h)
file(WRITE "${draco_features_file_name}.new"
"// GENERATED FILE -- DO NOT EDIT\n\n" "#ifndef DRACO_FEATURES_H_\n"
"#define DRACO_FEATURES_H_\n\n")
foreach(feature ${draco_features_list})
file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n")
endforeach()
file(APPEND "${draco_features_file_name}.new"
"\n#endif // DRACO_FEATURES_H_")
# Will replace ${draco_features_file_name} only if the file content has
# changed. This prevents forced Draco rebuilds after CMake runs.
configure_file("${draco_features_file_name}.new"
"${draco_features_file_name}")
file(REMOVE "${draco_features_file_name}.new")
endfunction()
# Sets default options for the build and processes user controlled options to
# compute enabled features.
macro(draco_setup_options)
draco_set_default_options()
draco_set_optional_features()
endmacro()

View File

@ -0,0 +1,32 @@
if(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_SANITIZER_CMAKE_
set(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_ 1)
# Handles the details of enabling sanitizers.
macro(draco_configure_sanitizer)
if(DRACO_SANITIZE AND NOT MSVC)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(DRACO_SANITIZE MATCHES "cfi")
list(APPEND DRACO_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi")
list(APPEND DRACO_EXE_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi"
"-fuse-ld=gold")
endif()
if(${CMAKE_SIZEOF_VOID_P} EQUAL 4
AND DRACO_SANITIZE MATCHES "integer|undefined")
list(APPEND DRACO_EXE_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s")
endif()
endif()
list(APPEND DRACO_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}")
list(APPEND DRACO_EXE_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}")
# Make sanitizer callstacks accurate.
list(APPEND DRACO_CXX_FLAGS "-fno-omit-frame-pointer"
"-fno-optimize-sibling-calls")
draco_test_cxx_flag(FLAG_LIST_VAR_NAMES DRACO_CXX_FLAGS FLAG_REQUIRED)
draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME DRACO_EXE_LINKER_FLAGS)
endif()
endmacro()

View File

@ -0,0 +1,349 @@
if(DRACO_CMAKE_DRACO_TARGETS_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_TARGETS_CMAKE_
set(DRACO_CMAKE_DRACO_TARGETS_CMAKE_ 1)
# Resets list variables used to track draco targets.
macro(draco_reset_target_lists)
unset(draco_targets)
unset(draco_exe_targets)
unset(draco_lib_targets)
unset(draco_objlib_targets)
unset(draco_module_targets)
unset(draco_sources)
unset(draco_test_targets)
endmacro()
# Creates an executable target. The target name is passed as a parameter to the
# NAME argument, and the sources passed as a parameter to the SOURCES argument:
# draco_add_executable(NAME <name> SOURCES <sources> [optional args])
#
# Optional args:
# cmake-format: off
# - OUTPUT_NAME: Override output file basename. Target basename defaults to
# NAME.
# - TEST: Flag. Presence means treat executable as a test.
# - DEFINES: List of preprocessor macro definitions.
# - INCLUDES: list of include directories for the target.
# - COMPILE_FLAGS: list of compiler flags for the target.
# - LINK_FLAGS: List of linker flags for the target.
# - OBJLIB_DEPS: List of CMake object library target dependencies.
# - LIB_DEPS: List of CMake library dependencies.
# cmake-format: on
#
# Sources passed to this macro are added to $draco_test_sources when TEST is
# specified. Otherwise sources are added to $draco_sources.
#
# Targets passed to this macro are always added to the $draco_targets list. When
# TEST is specified targets are also added to the $draco_test_targets list.
# Otherwise targets are added to $draco_exe_targets.
macro(draco_add_executable)
unset(exe_TEST)
unset(exe_TEST_DEFINES_MAIN)
unset(exe_NAME)
unset(exe_OUTPUT_NAME)
unset(exe_SOURCES)
unset(exe_DEFINES)
unset(exe_INCLUDES)
unset(exe_COMPILE_FLAGS)
unset(exe_LINK_FLAGS)
unset(exe_OBJLIB_DEPS)
unset(exe_LIB_DEPS)
set(optional_args TEST)
set(single_value_args NAME OUTPUT_NAME)
set(multi_value_args SOURCES DEFINES INCLUDES COMPILE_FLAGS LINK_FLAGS
OBJLIB_DEPS LIB_DEPS)
cmake_parse_arguments(exe "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(DRACO_VERBOSE GREATER 1)
message("--------- draco_add_executable ---------\n"
"exe_TEST=${exe_TEST}\n"
"exe_TEST_DEFINES_MAIN=${exe_TEST_DEFINES_MAIN}\n"
"exe_NAME=${exe_NAME}\n"
"exe_OUTPUT_NAME=${exe_OUTPUT_NAME}\n"
"exe_SOURCES=${exe_SOURCES}\n"
"exe_DEFINES=${exe_DEFINES}\n"
"exe_INCLUDES=${exe_INCLUDES}\n"
"exe_COMPILE_FLAGS=${exe_COMPILE_FLAGS}\n"
"exe_LINK_FLAGS=${exe_LINK_FLAGS}\n"
"exe_OBJLIB_DEPS=${exe_OBJLIB_DEPS}\n"
"exe_LIB_DEPS=${exe_LIB_DEPS}\n"
"------------------------------------------\n")
endif()
if(NOT (exe_NAME AND exe_SOURCES))
message(FATAL_ERROR "draco_add_executable: NAME and SOURCES required.")
endif()
list(APPEND draco_targets ${exe_NAME})
if(exe_TEST)
list(APPEND draco_test_targets ${exe_NAME})
list(APPEND draco_test_sources ${exe_SOURCES})
else()
list(APPEND draco_exe_targets ${exe_NAME})
list(APPEND draco_sources ${exe_SOURCES})
endif()
add_executable(${exe_NAME} ${exe_SOURCES})
if(exe_OUTPUT_NAME)
set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME})
endif()
draco_process_intrinsics_sources(TARGET ${exe_NAME} SOURCES ${exe_SOURCES})
if(exe_DEFINES)
target_compile_definitions(${exe_NAME} PRIVATE ${exe_DEFINES})
endif()
if(exe_INCLUDES)
target_include_directories(${exe_NAME} PRIVATE ${exe_INCLUDES})
endif()
if(exe_COMPILE_FLAGS OR DRACO_CXX_FLAGS)
target_compile_options(${exe_NAME}
PRIVATE ${exe_COMPILE_FLAGS} ${DRACO_CXX_FLAGS})
endif()
if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS)
if(${CMAKE_VERSION} VERSION_LESS "3.13")
set(link_flags ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS})
set_target_properties(${exe_NAME}
PROPERTIES LINK_FLAGS ${exe_LINK_FLAGS}
${DRACO_EXE_LINKER_FLAGS})
else()
target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS}
${DRACO_EXE_LINKER_FLAGS})
endif()
endif()
if(exe_OBJLIB_DEPS)
foreach(objlib_dep ${exe_OBJLIB_DEPS})
target_sources(${exe_NAME} PRIVATE $<TARGET_OBJECTS:${objlib_dep}>)
endforeach()
endif()
if(CMAKE_THREAD_LIBS_INIT)
list(APPEND exe_LIB_DEPS ${CMAKE_THREAD_LIBS_INIT})
endif()
if(BUILD_SHARED_LIBS AND (MSVC OR WIN32))
target_compile_definitions(${lib_NAME} PRIVATE "DRACO_BUILDING_DLL=0")
endif()
if(exe_LIB_DEPS)
unset(exe_static)
if("${CMAKE_EXE_LINKER_FLAGS} ${DRACO_EXE_LINKER_FLAGS}" MATCHES "static")
set(exe_static ON)
endif()
if(exe_static AND CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
# Third party dependencies can introduce dependencies on system and test
# libraries. Since the target created here is an executable, and CMake
# does not provide a method of controlling order of link dependencies,
# wrap all of the dependencies of this target in start/end group flags to
# ensure that dependencies of third party targets can be resolved when
# those dependencies happen to be resolved by dependencies of the current
# target.
list(INSERT exe_LIB_DEPS 0 -Wl,--start-group)
list(APPEND exe_LIB_DEPS -Wl,--end-group)
endif()
target_link_libraries(${exe_NAME} PRIVATE ${exe_LIB_DEPS})
endif()
endmacro()
# Creates a library target of the specified type. The target name is passed as a
# parameter to the NAME argument, the type as a parameter to the TYPE argument,
# and the sources passed as a parameter to the SOURCES argument:
# draco_add_library(NAME <name> TYPE <type> SOURCES <sources> [optional args])
#
# Optional args:
# cmake-format: off
# - OUTPUT_NAME: Override output file basename. Target basename defaults to
# NAME. OUTPUT_NAME is ignored when BUILD_SHARED_LIBS is enabled and CMake
# is generating a build for which MSVC or WIN32 are true. This is to avoid
# output basename collisions with DLL import libraries.
# - TEST: Flag. Presence means treat library as a test.
# - DEFINES: List of preprocessor macro definitions.
# - INCLUDES: list of include directories for the target.
# - COMPILE_FLAGS: list of compiler flags for the target.
# - LINK_FLAGS: List of linker flags for the target.
# - OBJLIB_DEPS: List of CMake object library target dependencies.
# - LIB_DEPS: List of CMake library dependencies.
# - PUBLIC_INCLUDES: List of include paths to export to dependents.
# cmake-format: on
#
# Sources passed to the macro are added to the lists tracking draco sources:
# cmake-format: off
# - When TEST is specified sources are added to $draco_test_sources.
# - Otherwise sources are added to $draco_sources.
# cmake-format: on
#
# Targets passed to this macro are added to the lists tracking draco targets:
# cmake-format: off
# - Targets are always added to $draco_targets.
# - When the TEST flag is specified, targets are added to
# $draco_test_targets.
# - When TEST is not specified:
# - Libraries of type SHARED are added to $draco_dylib_targets.
# - Libraries of type OBJECT are added to $draco_objlib_targets.
# - Libraries of type STATIC are added to $draco_lib_targets.
# cmake-format: on
macro(draco_add_library)
unset(lib_TEST)
unset(lib_NAME)
unset(lib_OUTPUT_NAME)
unset(lib_TYPE)
unset(lib_SOURCES)
unset(lib_DEFINES)
unset(lib_INCLUDES)
unset(lib_COMPILE_FLAGS)
unset(lib_LINK_FLAGS)
unset(lib_OBJLIB_DEPS)
unset(lib_LIB_DEPS)
unset(lib_PUBLIC_INCLUDES)
unset(lib_TARGET_PROPERTIES)
set(optional_args TEST)
set(single_value_args NAME OUTPUT_NAME TYPE)
set(multi_value_args SOURCES DEFINES INCLUDES COMPILE_FLAGS LINK_FLAGS
OBJLIB_DEPS LIB_DEPS PUBLIC_INCLUDES TARGET_PROPERTIES)
cmake_parse_arguments(lib "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN})
if(DRACO_VERBOSE GREATER 1)
message("--------- draco_add_library ---------\n"
"lib_TEST=${lib_TEST}\n"
"lib_NAME=${lib_NAME}\n"
"lib_OUTPUT_NAME=${lib_OUTPUT_NAME}\n"
"lib_TYPE=${lib_TYPE}\n"
"lib_SOURCES=${lib_SOURCES}\n"
"lib_DEFINES=${lib_DEFINES}\n"
"lib_INCLUDES=${lib_INCLUDES}\n"
"lib_COMPILE_FLAGS=${lib_COMPILE_FLAGS}\n"
"lib_LINK_FLAGS=${lib_LINK_FLAGS}\n"
"lib_OBJLIB_DEPS=${lib_OBJLIB_DEPS}\n"
"lib_LIB_DEPS=${lib_LIB_DEPS}\n"
"lib_PUBLIC_INCLUDES=${lib_PUBLIC_INCLUDES}\n"
"---------------------------------------\n")
endif()
if(NOT (lib_NAME AND lib_TYPE))
message(FATAL_ERROR "draco_add_library: NAME and TYPE required.")
endif()
list(APPEND draco_targets ${lib_NAME})
if(lib_TEST)
list(APPEND draco_test_targets ${lib_NAME})
list(APPEND draco_test_sources ${lib_SOURCES})
else()
list(APPEND draco_sources ${lib_SOURCES})
if(lib_TYPE STREQUAL MODULE)
list(APPEND draco_module_targets ${lib_NAME})
elseif(lib_TYPE STREQUAL OBJECT)
list(APPEND draco_objlib_targets ${lib_NAME})
elseif(lib_TYPE STREQUAL SHARED)
list(APPEND draco_dylib_targets ${lib_NAME})
elseif(lib_TYPE STREQUAL STATIC)
list(APPEND draco_lib_targets ${lib_NAME})
else()
message(WARNING "draco_add_library: Unhandled type: ${lib_TYPE}")
endif()
endif()
add_library(${lib_NAME} ${lib_TYPE} ${lib_SOURCES})
if(lib_SOURCES)
draco_process_intrinsics_sources(TARGET ${lib_NAME} SOURCES ${lib_SOURCES})
endif()
if(lib_OUTPUT_NAME)
if(NOT (BUILD_SHARED_LIBS AND (MSVC OR WIN32)))
set_target_properties(${lib_NAME}
PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME})
endif()
endif()
if(lib_DEFINES)
target_compile_definitions(${lib_NAME} PRIVATE ${lib_DEFINES})
endif()
if(lib_INCLUDES)
target_include_directories(${lib_NAME} PRIVATE ${lib_INCLUDES})
endif()
if(lib_PUBLIC_INCLUDES)
target_include_directories(${lib_NAME} PUBLIC ${lib_PUBLIC_INCLUDES})
endif()
if(lib_COMPILE_FLAGS OR DRACO_CXX_FLAGS)
target_compile_options(${lib_NAME}
PRIVATE ${lib_COMPILE_FLAGS} ${DRACO_CXX_FLAGS})
endif()
if(lib_LINK_FLAGS)
set_target_properties(${lib_NAME} PROPERTIES LINK_FLAGS ${lib_LINK_FLAGS})
endif()
if(lib_OBJLIB_DEPS)
foreach(objlib_dep ${lib_OBJLIB_DEPS})
target_sources(${lib_NAME} PRIVATE $<TARGET_OBJECTS:${objlib_dep}>)
endforeach()
endif()
if(lib_LIB_DEPS)
if(lib_TYPE STREQUAL STATIC)
set(link_type PUBLIC)
else()
set(link_type PRIVATE)
if(lib_TYPE STREQUAL SHARED AND CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
# The draco shared object uses the static draco as input to turn it into
# a shared object. Include everything from the static library in the
# shared object.
if(APPLE)
list(INSERT lib_LIB_DEPS 0 -Wl,-force_load)
else()
list(INSERT lib_LIB_DEPS 0 -Wl,--whole-archive)
list(APPEND lib_LIB_DEPS -Wl,--no-whole-archive)
endif()
endif()
endif()
target_link_libraries(${lib_NAME} ${link_type} ${lib_LIB_DEPS})
endif()
if(NOT MSVC AND lib_NAME MATCHES "^lib")
# Non-MSVC generators prepend lib to static lib target file names. Libdraco
# already includes lib in its name. Avoid naming output files liblib*.
set_target_properties(${lib_NAME} PROPERTIES PREFIX "")
endif()
if(lib_TYPE STREQUAL SHARED AND NOT MSVC)
set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION})
endif()
if(BUILD_SHARED_LIBS AND (MSVC OR WIN32))
if(lib_TYPE STREQUAL SHARED)
target_compile_definitions(${lib_NAME} PRIVATE "DRACO_BUILDING_DLL=1")
else()
target_compile_definitions(${lib_NAME} PRIVATE "DRACO_BUILDING_DLL=0")
endif()
endif()
# Determine if $lib_NAME is a header only target.
unset(sources_list)
if(lib_SOURCES)
set(sources_list ${lib_SOURCES})
list(FILTER sources_list INCLUDE REGEX cc$)
endif()
if(NOT sources_list)
if(NOT XCODE)
# This is a header only target. Tell CMake the link language.
set_target_properties(${lib_NAME} PROPERTIES LINKER_LANGUAGE CXX)
else()
# The Xcode generator ignores LINKER_LANGUAGE. Add a dummy cc file.
draco_create_dummy_source_file(TARGET ${lib_NAME} BASENAME ${lib_NAME})
endif()
endif()
endmacro()

View File

@ -0,0 +1,13 @@
#ifndef DRACO_TESTING_DRACO_TEST_CONFIG_H_
#define DRACO_TESTING_DRACO_TEST_CONFIG_H_
// If this file is named draco_test_config.h.cmake:
// This file is used as input at cmake generation time.
// If this file is named draco_test_config.h:
// GENERATED FILE, DO NOT EDIT. SEE ABOVE.
#define DRACO_TEST_DATA_DIR "${DRACO_TEST_DATA_DIR}"
#define DRACO_TEST_TEMP_DIR "${DRACO_TEST_TEMP_DIR}"
#endif // DRACO_TESTING_DRACO_TEST_CONFIG_H_

View File

@ -0,0 +1,133 @@
if(DRACO_CMAKE_DRACO_TESTS_CMAKE)
return()
endif()
set(DRACO_CMAKE_DRACO_TESTS_CMAKE 1)
# The factory tests are in a separate target to avoid breaking tests that rely
# on file I/O via the factories. The fake reader and writer implementations
# interfere with normal file I/O function.
set(draco_factory_test_sources
"${draco_src_root}/io/file_reader_factory_test.cc"
"${draco_src_root}/io/file_writer_factory_test.cc")
list(
APPEND
draco_test_sources
"${draco_src_root}/animation/keyframe_animation_encoding_test.cc"
"${draco_src_root}/animation/keyframe_animation_test.cc"
"${draco_src_root}/attributes/point_attribute_test.cc"
"${draco_src_root}/compression/attributes/point_d_vector_test.cc"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_test.cc"
"${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc"
"${draco_src_root}/compression/attributes/sequential_integer_attribute_encoding_test.cc"
"${draco_src_root}/compression/bit_coders/rans_coding_test.cc"
"${draco_src_root}/compression/decode_test.cc"
"${draco_src_root}/compression/encode_test.cc"
"${draco_src_root}/compression/entropy/shannon_entropy_test.cc"
"${draco_src_root}/compression/entropy/symbol_coding_test.cc"
"${draco_src_root}/compression/mesh/mesh_edgebreaker_encoding_test.cc"
"${draco_src_root}/compression/mesh/mesh_encoder_test.cc"
"${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc"
"${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoding_test.cc"
"${draco_src_root}/core/buffer_bit_coding_test.cc"
"${draco_src_root}/core/draco_test_base.h"
"${draco_src_root}/core/draco_test_utils.cc"
"${draco_src_root}/core/draco_test_utils.h"
"${draco_src_root}/core/math_utils_test.cc"
"${draco_src_root}/core/quantization_utils_test.cc"
"${draco_src_root}/core/status_test.cc"
"${draco_src_root}/core/vector_d_test.cc"
"${draco_src_root}/io/file_reader_test_common.h"
"${draco_src_root}/io/file_utils_test.cc"
"${draco_src_root}/io/stdio_file_reader_test.cc"
"${draco_src_root}/io/stdio_file_writer_test.cc"
"${draco_src_root}/io/obj_decoder_test.cc"
"${draco_src_root}/io/obj_encoder_test.cc"
"${draco_src_root}/io/ply_decoder_test.cc"
"${draco_src_root}/io/ply_reader_test.cc"
"${draco_src_root}/io/point_cloud_io_test.cc"
"${draco_src_root}/mesh/mesh_are_equivalent_test.cc"
"${draco_src_root}/mesh/mesh_cleanup_test.cc"
"${draco_src_root}/mesh/triangle_soup_mesh_builder_test.cc"
"${draco_src_root}/metadata/metadata_encoder_test.cc"
"${draco_src_root}/metadata/metadata_test.cc"
"${draco_src_root}/point_cloud/point_cloud_builder_test.cc"
"${draco_src_root}/point_cloud/point_cloud_test.cc")
list(APPEND draco_gtest_all
"${draco_root}/../googletest/googletest/src/gtest-all.cc")
list(APPEND draco_gtest_main
"${draco_root}/../googletest/googletest/src/gtest_main.cc")
macro(draco_setup_test_targets)
if(DRACO_TESTS)
if(NOT (EXISTS ${draco_gtest_all} AND EXISTS ${draco_gtest_main}))
message(FATAL "googletest must be a sibling directory of ${draco_root}.")
endif()
list(APPEND draco_test_defines GTEST_HAS_PTHREAD=0)
draco_add_library(TEST
NAME
draco_gtest
TYPE
STATIC
SOURCES
${draco_gtest_all}
DEFINES
${draco_defines}
${draco_test_defines}
INCLUDES
${draco_test_include_paths})
draco_add_library(TEST
NAME
draco_gtest_main
TYPE
STATIC
SOURCES
${draco_gtest_main}
DEFINES
${draco_defines}
${draco_test_defines}
INCLUDES
${draco_test_include_paths})
set(DRACO_TEST_DATA_DIR "${draco_root}/testdata")
set(DRACO_TEST_TEMP_DIR "${draco_build}/draco_test_temp")
file(MAKE_DIRECTORY "${DRACO_TEST_TEMP_DIR}")
# Sets DRACO_TEST_DATA_DIR and DRACO_TEST_TEMP_DIR.
configure_file("${draco_root}/cmake/draco_test_config.h.cmake"
"${draco_build}/testing/draco_test_config.h")
# Create the test targets.
draco_add_executable(NAME
draco_tests
SOURCES
${draco_test_sources}
DEFINES
${draco_defines}
${draco_test_defines}
INCLUDES
${draco_test_include_paths}
LIB_DEPS
draco_static
draco_gtest
draco_gtest_main)
draco_add_executable(NAME
draco_factory_tests
SOURCES
${draco_factory_test_sources}
DEFINES
${draco_defines}
${draco_test_defines}
INCLUDES
${draco_test_include_paths}
LIB_DEPS
draco_static
draco_gtest
draco_gtest_main)
endif()
endmacro()

View File

@ -0,0 +1,64 @@
if(DRACO_CMAKE_DRACO_VARIABLES_CMAKE_)
return()
endif() # DRACO_CMAKE_DRACO_VARIABLES_CMAKE_
set(DRACO_CMAKE_DRACO_VARIABLES_CMAKE_ 1)
# Halts generation when $variable_name does not refer to a directory that
# exists.
macro(draco_variable_must_be_directory variable_name)
if("${variable_name}" STREQUAL "")
message(
FATAL_ERROR
"Empty variable_name passed to draco_variable_must_be_directory.")
endif()
if("${${variable_name}}" STREQUAL "")
message(
FATAL_ERROR
"Empty variable ${variable_name} is required to build draco.")
endif()
if(NOT IS_DIRECTORY "${${variable_name}}")
message(
FATAL_ERROR
"${variable_name}, which is ${${variable_name}}, does not refer to a\n"
"directory.")
endif()
endmacro()
# Adds $var_name to the tracked variables list.
macro(draco_track_configuration_variable var_name)
if(DRACO_VERBOSE GREATER 2)
message("---- draco_track_configuration_variable ----\n"
"var_name=${var_name}\n"
"----------------------------------------------\n")
endif()
list(APPEND draco_configuration_variables ${var_name})
list(REMOVE_DUPLICATES draco_configuration_variables)
endmacro()
# Logs current C++ and executable linker flags via the CMake message command.
macro(draco_dump_cmake_flag_variables)
unset(flag_variables)
list(APPEND flag_variables "CMAKE_CXX_FLAGS_INIT" "CMAKE_CXX_FLAGS"
"CMAKE_EXE_LINKER_FLAGS_INIT" "CMAKE_EXE_LINKER_FLAGS")
if(CMAKE_BUILD_TYPE)
list(APPEND flag_variables "CMAKE_BUILD_TYPE"
"CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}_INIT"
"CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}"
"CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE}_INIT"
"CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE}")
endif()
foreach(flag_variable ${flag_variables})
message("${flag_variable}:${${flag_variable}}")
endforeach()
endmacro()
# Dumps the variables tracked in $draco_configuration_variables via the CMake
# message command.
macro(draco_dump_tracked_configuration_variables)
foreach(config_variable ${draco_configuration_variables})
message("${config_variable}:${${config_variable}}")
endforeach()
endmacro()

View File

@ -0,0 +1,19 @@
if(DRACO_CMAKE_SANITIZERS_CMAKE_)
return()
endif()
set(DRACO_CMAKE_SANITIZERS_CMAKE_ 1)
if(MSVC OR NOT SANITIZE)
return()
endif()
include("${draco_root}/cmake/compiler_flags.cmake")
string(TOLOWER ${SANITIZE} SANITIZE)
# Require the sanitizer requested.
require_linker_flag("-fsanitize=${SANITIZE}")
require_compiler_flag("-fsanitize=${SANITIZE}" YES)
# Make callstacks accurate.
require_compiler_flag("-fno-omit-frame-pointer -fno-optimize-sibling-calls" YES)

View File

@ -0,0 +1,14 @@
if(DRACO_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_)
return()
endif() # DRACO_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_
set(DRACO_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_ 1)
set(CMAKE_SYSTEM_NAME "Linux")
if("${CROSS}" STREQUAL "")
set(CROSS aarch64-linux-gnu-)
endif()
set(CMAKE_CXX_COMPILER ${CROSS}g++)
set(CMAKE_CXX_FLAGS_INIT "-march=armv8-a")
set(CMAKE_SYSTEM_PROCESSOR "aarch64")

View File

@ -0,0 +1,23 @@
if(DRACO_CMAKE_TOOLCHAINS_ANDROID_NDK_COMMON_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_ANDROID_NDK_COMMON_CMAKE_ 1)
# Toolchain files do not have access to cached variables:
# https://gitlab.kitware.com/cmake/cmake/issues/16170. Set an intermediate
# environment variable when loaded the first time.
if(DRACO_ANDROID_NDK_PATH)
set(ENV{DRACO_ANDROID_NDK_PATH} "${DRACO_ANDROID_NDK_PATH}")
else()
set(DRACO_ANDROID_NDK_PATH "$ENV{DRACO_ANDROID_NDK_PATH}")
endif()
set(CMAKE_SYSTEM_NAME Android)
if(NOT CMAKE_ANDROID_STL_TYPE)
set(CMAKE_ANDROID_STL_TYPE c++_static)
endif()
if(NOT CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION)
set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang)
endif()

View File

@ -0,0 +1,39 @@
if(DRACO_CMAKE_TOOLCHAINS_ANDROID_CMAKE_)
return()
endif() # DRACO_CMAKE_TOOLCHAINS_ANDROID_CMAKE_
# Additional ANDROID_* settings are available, see:
# https://developer.android.com/ndk/guides/cmake#variables
if(NOT ANDROID_PLATFORM)
set(ANDROID_PLATFORM android-21)
endif()
# Choose target architecture with:
#
# -DANDROID_ABI={armeabi-v7a,armeabi-v7a with NEON,arm64-v8a,x86,x86_64}
if(NOT ANDROID_ABI)
set(ANDROID_ABI arm64-v8a)
endif()
# Force arm mode for 32-bit targets (instead of the default thumb) to improve
# performance.
if(NOT ANDROID_ARM_MODE)
set(ANDROID_ARM_MODE arm)
endif()
# Toolchain files do not have access to cached variables:
# https://gitlab.kitware.com/cmake/cmake/issues/16170. Set an intermediate
# environment variable when loaded the first time.
if(DRACO_ANDROID_NDK_PATH)
set(ENV{DRACO_ANDROID_NDK_PATH} "${DRACO_ANDROID_NDK_PATH}")
else()
set(DRACO_ANDROID_NDK_PATH "$ENV{DRACO_ANDROID_NDK_PATH}")
endif()
if(NOT DRACO_ANDROID_NDK_PATH)
message(FATAL_ERROR "DRACO_ANDROID_NDK_PATH not set.")
return()
endif()
include("${DRACO_ANDROID_NDK_PATH}/build/cmake/android.toolchain.cmake")

View File

@ -0,0 +1,17 @@
if(DRACO_CMAKE_TOOLCHAINS_ARM_IOS_COMMON_CMAKE_)
return()
endif()
set(DRACO_CMAKE_ARM_IOS_COMMON_CMAKE_ 1)
set(CMAKE_SYSTEM_NAME "Darwin")
if(CMAKE_OSX_SDK)
set(CMAKE_OSX_SYSROOT ${CMAKE_OSX_SDK})
else()
set(CMAKE_OSX_SYSROOT iphoneos)
endif()
set(CMAKE_C_COMPILER clang)
set(CMAKE_C_COMPILER_ARG1 "-arch ${CMAKE_SYSTEM_PROCESSOR}")
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_COMPILER_ARG1 "-arch ${CMAKE_SYSTEM_PROCESSOR}")
# TODO(tomfinegan): Handle bit code embedding.

View File

@ -0,0 +1,15 @@
if(DRACO_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_)
return()
endif() # DRACO_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_
set(DRACO_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_ 1)
set(CMAKE_SYSTEM_NAME "Linux")
if("${CROSS}" STREQUAL "")
set(CROSS arm-linux-gnueabihf-)
endif()
set(CMAKE_CXX_COMPILER ${CROSS}g++)
set(CMAKE_CXX_FLAGS_INIT "-march=armv7-a -marm")
set(CMAKE_SYSTEM_PROCESSOR "armv7")
set(DRACO_NEON_INTRINSICS_FLAG "-mfpu=neon")

View File

@ -0,0 +1,16 @@
if(DRACO_CMAKE_TOOLCHAINS_ARM64_ANDROID_NDK_LIBCPP_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_ARM64_ANDROID_NDK_LIBCPP_CMAKE_ 1)
include("${CMAKE_CURRENT_LIST_DIR}/android-ndk-common.cmake")
if(NOT ANDROID_PLATFORM)
set(ANROID_PLATFORM android-21)
endif()
if(NOT ANDROID_ABI)
set(ANDROID_ABI arm64-v8a)
endif()
include("${DRACO_ANDROID_NDK_PATH}/build/cmake/android.toolchain.cmake")

View File

@ -0,0 +1,14 @@
if(DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_ 1)
if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.")
endif()
set(CMAKE_SYSTEM_PROCESSOR "arm64")
set(CMAKE_OSX_ARCHITECTURES "arm64")
include("${CMAKE_CURRENT_LIST_DIR}/arm-ios-common.cmake")

View File

@ -0,0 +1,18 @@
if(DRACO_CMAKE_TOOLCHAINS_ARM64_LINUX_GCC_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_ARM64_LINUX_GCC_CMAKE_ 1)
set(CMAKE_SYSTEM_NAME "Linux")
if("${CROSS}" STREQUAL "")
# Default the cross compiler prefix to something known to work.
set(CROSS aarch64-linux-gnu-)
endif()
set(CMAKE_C_COMPILER ${CROSS}gcc)
set(CMAKE_CXX_COMPILER ${CROSS}g++)
set(AS_EXECUTABLE ${CROSS}as)
set(CMAKE_C_COMPILER_ARG1 "-march=armv8-a")
set(CMAKE_CXX_COMPILER_ARG1 "-march=armv8-a")
set(CMAKE_SYSTEM_PROCESSOR "arm64")

View File

@ -0,0 +1,16 @@
if(DRACO_CMAKE_TOOLCHAINS_ARMV7_ANDROID_NDK_LIBCPP_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_ARMV7_ANDROID_NDK_LIBCPP_CMAKE_ 1)
include("${CMAKE_CURRENT_LIST_DIR}/android-ndk-common.cmake")
if(NOT ANDROID_PLATFORM)
set(ANDROID_PLATFORM android-18)
endif()
if(NOT ANDROID_ABI)
set(ANDROID_ABI armeabi-v7a)
endif()
include("${DRACO_ANDROID_NDK_PATH}/build/cmake/android.toolchain.cmake")

View File

@ -0,0 +1,14 @@
if(DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_ 1)
if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.")
endif()
set(CMAKE_SYSTEM_PROCESSOR "armv7")
set(CMAKE_OSX_ARCHITECTURES "armv7")
include("${CMAKE_CURRENT_LIST_DIR}/arm-ios-common.cmake")

View File

@ -0,0 +1,24 @@
if(DRACO_CMAKE_TOOLCHAINS_ARMV7_LINUX_GCC_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_ARMV7_LINUX_GCC_CMAKE_ 1)
set(CMAKE_SYSTEM_NAME "Linux")
if("${CROSS}" STREQUAL "")
# Default the cross compiler prefix to something known to work.
set(CROSS arm-linux-gnueabihf-)
endif()
if(NOT ${CROSS} MATCHES hf-$)
set(DRACO_EXTRA_TOOLCHAIN_FLAGS "-mfloat-abi=softfp")
endif()
set(CMAKE_C_COMPILER ${CROSS}gcc)
set(CMAKE_CXX_COMPILER ${CROSS}g++)
set(AS_EXECUTABLE ${CROSS}as)
set(CMAKE_C_COMPILER_ARG1
"-march=armv7-a -mfpu=neon ${DRACO_EXTRA_TOOLCHAIN_FLAGS}")
set(CMAKE_CXX_COMPILER_ARG1
"-march=armv7-a -mfpu=neon ${DRACO_EXTRA_TOOLCHAIN_FLAGS}")
set(CMAKE_SYSTEM_PROCESSOR "armv7")

View File

@ -0,0 +1,14 @@
if(DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_ 1)
if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.")
endif()
set(CMAKE_SYSTEM_PROCESSOR "armv7s")
set(CMAKE_OSX_ARCHITECTURES "armv7s")
include("${CMAKE_CURRENT_LIST_DIR}/arm-ios-common.cmake")

View File

@ -0,0 +1,15 @@
if(DRACO_CMAKE_TOOLCHAINS_i386_IOS_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_i386_IOS_CMAKE_ 1)
if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.")
endif()
set(CMAKE_SYSTEM_PROCESSOR "i386")
set(CMAKE_OSX_ARCHITECTURES "i386")
set(CMAKE_OSX_SDK "iphonesimulator")
include("${CMAKE_CURRENT_LIST_DIR}/arm-ios-common.cmake")

View File

@ -0,0 +1,16 @@
if(DRACO_CMAKE_TOOLCHAINS_X86_ANDROID_NDK_LIBCPP_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_X86_ANDROID_NDK_LIBCPP_CMAKE_ 1)
include("${CMAKE_CURRENT_LIST_DIR}/android-ndk-common.cmake")
if(NOT ANDROID_PLATFORM)
set(ANDROID_PLATFORM android-18)
endif()
if(NOT ANDROID_ABI)
set(ANDROID_ABI x86)
endif()
include("${DRACO_ANDROID_NDK_PATH}/build/cmake/android.toolchain.cmake")

View File

@ -0,0 +1,16 @@
if(DRACO_CMAKE_TOOLCHAINS_X86_64_ANDROID_NDK_LIBCPP_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_X86_64_ANDROID_NDK_LIBCPP_CMAKE_ 1)
include("${CMAKE_CURRENT_LIST_DIR}/android-ndk-common.cmake")
if(NOT ANDROID_PLATFORM)
set(ANDROID_PLATFORM android-21)
endif()
if(NOT ANDROID_ABI)
set(ANDROID_ABI x86_64)
endif()
include("${DRACO_ANDROID_NDK_PATH}/build/cmake/android.toolchain.cmake")

View File

@ -0,0 +1,15 @@
if(DRACO_CMAKE_TOOLCHAINS_X86_64_IOS_CMAKE_)
return()
endif()
set(DRACO_CMAKE_TOOLCHAINS_X86_64_IOS_CMAKE_ 1)
if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.")
endif()
set(CMAKE_SYSTEM_PROCESSOR "x86_64")
set(CMAKE_OSX_ARCHITECTURES "x86_64")
set(CMAKE_OSX_SDK "iphonesimulator")
include("${CMAKE_CURRENT_LIST_DIR}/arm-ios-common.cmake")

View File

@ -0,0 +1,79 @@
if(DRACO_CMAKE_UTIL_CMAKE_)
return()
endif()
set(DRACO_CMAKE_UTIL_CMAKE_ 1)
# Creates dummy source file in $draco_build_dir named $basename.$extension and
# returns the full path to the dummy source file via the $out_file_path
# parameter.
function(create_dummy_source_file basename extension out_file_path)
set(dummy_source_file "${draco_build_dir}/${basename}.${extension}")
file(WRITE "${dummy_source_file}.new"
"// Generated file. DO NOT EDIT!\n"
"// ${target_name} needs a ${extension} file to force link language, \n"
"// or to silence a harmless CMake warning: Ignore me.\n"
"void ${target_name}_dummy_function(void) {}\n")
# Will replace ${dummy_source_file} only if the file content has changed.
# This prevents forced Draco rebuilds after CMake runs.
configure_file("${dummy_source_file}.new" "${dummy_source_file}")
file(REMOVE "${dummy_source_file}.new")
set(${out_file_path} ${dummy_source_file} PARENT_SCOPE)
endfunction()
# Convenience function for adding a dummy source file to $target_name using
# $extension as the file extension. Wraps create_dummy_source_file().
function(add_dummy_source_file_to_target target_name extension)
create_dummy_source_file("${target_name}" "${extension}" "dummy_source_file")
target_sources(${target_name} PRIVATE ${dummy_source_file})
endfunction()
# Extracts the version number from $version_file and returns it to the user via
# $version_string_out_var. This is achieved by finding the first instance of the
# kDracoVersion variable and then removing everything but the string literal
# assigned to the variable. Quotes and semicolon are stripped from the returned
# string.
function(extract_version_string version_file version_string_out_var)
file(STRINGS "${version_file}" draco_version REGEX "kDracoVersion")
list(GET draco_version 0 draco_version)
string(REPLACE "static const char kDracoVersion[] = " "" draco_version
"${draco_version}")
string(REPLACE ";" "" draco_version "${draco_version}")
string(REPLACE "\"" "" draco_version "${draco_version}")
set("${version_string_out_var}" "${draco_version}" PARENT_SCOPE)
endfunction()
# Sets CMake compiler launcher to $launcher_name when $launcher_name is found in
# $PATH. Warns user about ignoring build flag $launcher_flag when $launcher_name
# is not found in $PATH.
function(set_compiler_launcher launcher_flag launcher_name)
find_program(launcher_path "${launcher_name}")
if(launcher_path)
set(CMAKE_C_COMPILER_LAUNCHER "${launcher_path}" PARENT_SCOPE)
set(CMAKE_CXX_COMPILER_LAUNCHER "${launcher_path}" PARENT_SCOPE)
message("--- Using ${launcher_name} as compiler launcher.")
else()
message(
WARNING "--- Cannot find ${launcher_name}, ${launcher_flag} ignored.")
endif()
endfunction()
# Terminates CMake execution when $var_name is unset in the environment. Sets
# CMake variable to the value of the environment variable when the variable is
# present in the environment.
macro(require_variable var_name)
if("$ENV{${var_name}}" STREQUAL "")
message(FATAL_ERROR "${var_name} must be set in environment.")
endif()
set_variable_if_unset(${var_name} "")
endmacro()
# Sets $var_name to $default_value if not already set.
macro(set_variable_if_unset var_name default_value)
if(NOT "$ENV{${var_name}}" STREQUAL "")
set(${var_name} $ENV{${var_name}})
elseif(NOT ${var_name})
set(${var_name} ${default_value})
endif()
endmacro()

View File

@ -0,0 +1,54 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "draco/animation/keyframe_animation.h"
namespace draco {
KeyframeAnimation::KeyframeAnimation() {}
bool KeyframeAnimation::SetTimestamps(
const std::vector<TimestampType> &timestamp) {
// Already added attributes.
const int32_t num_frames = timestamp.size();
if (num_attributes() > 0) {
// Timestamp attribute could be added only once.
if (timestamps()->size()) {
return false;
} else {
// Check if the number of frames is consistent with
// the existing keyframes.
if (num_frames != num_points()) {
return false;
}
}
} else {
// This is the first attribute.
set_num_frames(num_frames);
}
// Add attribute for time stamp data.
std::unique_ptr<PointAttribute> timestamp_att =
std::unique_ptr<PointAttribute>(new PointAttribute());
timestamp_att->Init(GeometryAttribute::GENERIC, 1, DT_FLOAT32, false,
num_frames);
for (PointIndex i(0); i < num_frames; ++i) {
timestamp_att->SetAttributeValue(timestamp_att->mapped_index(i),
&timestamp[i.value()]);
}
this->SetAttribute(kTimestampId, std::move(timestamp_att));
return true;
}
} // namespace draco

View File

@ -0,0 +1,107 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef DRACO_ANIMATION_KEYFRAME_ANIMATION_H_
#define DRACO_ANIMATION_KEYFRAME_ANIMATION_H_
#include <vector>
#include "draco/point_cloud/point_cloud.h"
namespace draco {
// Class for holding keyframe animation data. It will have two or more
// attributes as a point cloud. The first attribute is always the timestamp
// of the animation. Each KeyframeAnimation could have multiple animations with
// the same number of frames. Each animation will be treated as a point
// attribute.
class KeyframeAnimation : public PointCloud {
public:
// Force time stamp to be float type.
using TimestampType = float;
KeyframeAnimation();
// Animation must have only one timestamp attribute.
// This function must be called before adding any animation data.
// Returns false if timestamp already exists.
bool SetTimestamps(const std::vector<TimestampType> &timestamp);
// Returns an id for the added animation data. This id will be used to
// identify this animation.
// Returns -1 if error, e.g. number of frames is not consistent.
// Type |T| should be consistent with |DataType|, e.g:
// float - DT_FLOAT32,
// int32_t - DT_INT32, ...
template <typename T>
int32_t AddKeyframes(DataType data_type, uint32_t num_components,
const std::vector<T> &data);
const PointAttribute *timestamps() const {
return GetAttributeByUniqueId(kTimestampId);
}
const PointAttribute *keyframes(int32_t animation_id) const {
return GetAttributeByUniqueId(animation_id);
}
// Number of frames should be equal to number points in the point cloud.
void set_num_frames(int32_t num_frames) { set_num_points(num_frames); }
int32_t num_frames() const { return static_cast<int32_t>(num_points()); }
int32_t num_animations() const { return num_attributes() - 1; }
private:
// Attribute id of timestamp is fixed to 0.
static constexpr int32_t kTimestampId = 0;
};
template <typename T>
int32_t KeyframeAnimation::AddKeyframes(DataType data_type,
uint32_t num_components,
const std::vector<T> &data) {
// TODO(draco-eng): Verify T is consistent with |data_type|.
if (num_components == 0) {
return -1;
}
// If timestamps is not added yet, then reserve attribute 0 for timestamps.
if (!num_attributes()) {
// Add a temporary attribute with 0 points to fill attribute id 0.
std::unique_ptr<PointAttribute> temp_att =
std::unique_ptr<PointAttribute>(new PointAttribute());
temp_att->Init(GeometryAttribute::GENERIC, num_components, data_type, false,
0);
this->AddAttribute(std::move(temp_att));
set_num_frames(data.size() / num_components);
}
if (data.size() != num_components * num_frames()) {
return -1;
}
std::unique_ptr<PointAttribute> keyframe_att =
std::unique_ptr<PointAttribute>(new PointAttribute());
keyframe_att->Init(GeometryAttribute::GENERIC, num_components, data_type,
false, num_frames());
const size_t stride = num_components;
for (PointIndex i(0); i < num_frames(); ++i) {
keyframe_att->SetAttributeValue(keyframe_att->mapped_index(i),
&data[i.value() * stride]);
}
return this->AddAttribute(std::move(keyframe_att));
}
} // namespace draco
#endif // DRACO_ANIMATION_KEYFRAME_ANIMATION_H_

View File

@ -0,0 +1,30 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "draco/animation/keyframe_animation_decoder.h"
namespace draco {
Status KeyframeAnimationDecoder::Decode(const DecoderOptions &options,
DecoderBuffer *in_buffer,
KeyframeAnimation *animation) {
const auto status = PointCloudSequentialDecoder::Decode(
options, in_buffer, static_cast<PointCloud *>(animation));
if (!status.ok()) {
return status;
}
return OkStatus();
}
} // namespace draco

View File

@ -0,0 +1,34 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef DRACO_ANIMATION_KEYFRAME_ANIMATION_DECODER_H_
#define DRACO_ANIMATION_KEYFRAME_ANIMATION_DECODER_H_
#include "draco/animation/keyframe_animation.h"
#include "draco/compression/point_cloud/point_cloud_sequential_decoder.h"
namespace draco {
// Class for decoding keyframe animation.
class KeyframeAnimationDecoder : private PointCloudSequentialDecoder {
public:
KeyframeAnimationDecoder(){};
Status Decode(const DecoderOptions &options, DecoderBuffer *in_buffer,
KeyframeAnimation *animation);
};
} // namespace draco
#endif // DRACO_ANIMATION_KEYFRAME_ANIMATION_DECODER_H_

View File

@ -0,0 +1,28 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "draco/animation/keyframe_animation_encoder.h"
namespace draco {
KeyframeAnimationEncoder::KeyframeAnimationEncoder() {}
Status KeyframeAnimationEncoder::EncodeKeyframeAnimation(
const KeyframeAnimation &animation, const EncoderOptions &options,
EncoderBuffer *out_buffer) {
SetPointCloud(animation);
return Encode(options, out_buffer);
}
} // namespace draco

View File

@ -0,0 +1,39 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef DRACO_ANIMATION_KEYFRAME_ANIMATION_ENCODER_H_
#define DRACO_ANIMATION_KEYFRAME_ANIMATION_ENCODER_H_
#include "draco/animation/keyframe_animation.h"
#include "draco/compression/point_cloud/point_cloud_sequential_encoder.h"
namespace draco {
// Class for encoding keyframe animation. It takes KeyframeAnimation as a
// PointCloud and compress it. It's mostly a wrapper around PointCloudEncoder so
// that the animation module could be separated from geometry compression when
// exposed to developers.
class KeyframeAnimationEncoder : private PointCloudSequentialEncoder {
public:
KeyframeAnimationEncoder();
// Encode an animation to a buffer.
Status EncodeKeyframeAnimation(const KeyframeAnimation &animation,
const EncoderOptions &options,
EncoderBuffer *out_buffer);
};
} // namespace draco
#endif // DRACO_ANIMATION_KEYFRAME_ANIMATION_ENCODER_H_

View File

@ -0,0 +1,168 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "draco/animation/keyframe_animation.h"
#include "draco/animation/keyframe_animation_decoder.h"
#include "draco/animation/keyframe_animation_encoder.h"
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
namespace draco {
class KeyframeAnimationEncodingTest : public ::testing::Test {
protected:
KeyframeAnimationEncodingTest() {}
bool CreateAndAddTimestamps(int32_t num_frames) {
timestamps_.resize(num_frames);
for (int i = 0; i < timestamps_.size(); ++i)
timestamps_[i] = static_cast<draco::KeyframeAnimation::TimestampType>(i);
return keyframe_animation_.SetTimestamps(timestamps_);
}
int32_t CreateAndAddAnimationData(int32_t num_frames,
uint32_t num_components) {
// Create and add animation data with.
animation_data_.resize(num_frames * num_components);
for (int i = 0; i < animation_data_.size(); ++i)
animation_data_[i] = static_cast<float>(i);
return keyframe_animation_.AddKeyframes(draco::DT_FLOAT32, num_components,
animation_data_);
}
template <int num_components_t>
void CompareAnimationData(const KeyframeAnimation &animation0,
const KeyframeAnimation &animation1,
bool quantized) {
ASSERT_EQ(animation0.num_frames(), animation1.num_frames());
ASSERT_EQ(animation0.num_animations(), animation1.num_animations());
if (quantized) {
// TODO(hemmer) : Add test for stable quantization.
// Quantization will result in slightly different values.
// Skip comparing values.
return;
}
// Compare time stamp.
const auto timestamp_att0 = animation0.timestamps();
const auto timestamp_att1 = animation0.timestamps();
for (int i = 0; i < animation0.num_frames(); ++i) {
std::array<float, 1> att_value0;
std::array<float, 1> att_value1;
ASSERT_TRUE((timestamp_att0->GetValue<float, 1>(
draco::AttributeValueIndex(i), &att_value0)));
ASSERT_TRUE((timestamp_att1->GetValue<float, 1>(
draco::AttributeValueIndex(i), &att_value1)));
ASSERT_FLOAT_EQ(att_value0[0], att_value1[0]);
}
for (int animation_id = 1; animation_id < animation0.num_animations();
++animation_id) {
// Compare keyframe data.
const auto keyframe_att0 = animation0.keyframes(animation_id);
const auto keyframe_att1 = animation1.keyframes(animation_id);
ASSERT_EQ(keyframe_att0->num_components(),
keyframe_att1->num_components());
for (int i = 0; i < animation0.num_frames(); ++i) {
std::array<float, num_components_t> att_value0;
std::array<float, num_components_t> att_value1;
ASSERT_TRUE((keyframe_att0->GetValue<float, num_components_t>(
draco::AttributeValueIndex(i), &att_value0)));
ASSERT_TRUE((keyframe_att1->GetValue<float, num_components_t>(
draco::AttributeValueIndex(i), &att_value1)));
for (int j = 0; j < att_value0.size(); ++j) {
ASSERT_FLOAT_EQ(att_value0[j], att_value1[j]);
}
}
}
}
template <int num_components_t>
void TestKeyframeAnimationEncoding() {
TestKeyframeAnimationEncoding<num_components_t>(false);
}
template <int num_components_t>
void TestKeyframeAnimationEncoding(bool quantized) {
// Encode animation class.
draco::EncoderBuffer buffer;
draco::KeyframeAnimationEncoder encoder;
EncoderOptions options = EncoderOptions::CreateDefaultOptions();
if (quantized) {
// Set quantization for timestamps.
options.SetAttributeInt(0, "quantization_bits", 20);
// Set quantization for keyframes.
for (int i = 1; i <= keyframe_animation_.num_animations(); ++i) {
options.SetAttributeInt(i, "quantization_bits", 20);
}
}
ASSERT_TRUE(
encoder.EncodeKeyframeAnimation(keyframe_animation_, options, &buffer)
.ok());
draco::DecoderBuffer dec_decoder;
draco::KeyframeAnimationDecoder decoder;
DecoderBuffer dec_buffer;
dec_buffer.Init(buffer.data(), buffer.size());
// Decode animation class.
std::unique_ptr<KeyframeAnimation> decoded_animation(
new KeyframeAnimation());
DecoderOptions dec_options;
ASSERT_TRUE(
decoder.Decode(dec_options, &dec_buffer, decoded_animation.get()).ok());
// Verify if animation before and after compression is identical.
CompareAnimationData<num_components_t>(keyframe_animation_,
*decoded_animation, quantized);
}
draco::KeyframeAnimation keyframe_animation_;
std::vector<draco::KeyframeAnimation::TimestampType> timestamps_;
std::vector<float> animation_data_;
};
TEST_F(KeyframeAnimationEncodingTest, OneComponent) {
const int num_frames = 1;
ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
ASSERT_EQ(CreateAndAddAnimationData(num_frames, 1), 1);
TestKeyframeAnimationEncoding<1>();
}
TEST_F(KeyframeAnimationEncodingTest, ManyComponents) {
const int num_frames = 100;
ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
ASSERT_EQ(CreateAndAddAnimationData(num_frames, 100), 1);
TestKeyframeAnimationEncoding<100>();
}
TEST_F(KeyframeAnimationEncodingTest, ManyComponentsWithQuantization) {
const int num_frames = 100;
ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
ASSERT_EQ(CreateAndAddAnimationData(num_frames, 4), 1);
// Test compression with quantization.
TestKeyframeAnimationEncoding<4>(true);
}
TEST_F(KeyframeAnimationEncodingTest, MultipleAnimations) {
const int num_frames = 5;
ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
ASSERT_EQ(CreateAndAddAnimationData(num_frames, 3), 1);
ASSERT_EQ(CreateAndAddAnimationData(num_frames, 3), 2);
TestKeyframeAnimationEncoding<3>();
}
} // namespace draco

View File

@ -0,0 +1,102 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "draco/animation/keyframe_animation.h"
#include "draco/core/draco_test_base.h"
namespace {
class KeyframeAnimationTest : public ::testing::Test {
protected:
KeyframeAnimationTest() {}
bool CreateAndAddTimestamps(int32_t num_frames) {
timestamps_.resize(num_frames);
for (int i = 0; i < timestamps_.size(); ++i)
timestamps_[i] = static_cast<draco::KeyframeAnimation::TimestampType>(i);
return keyframe_animation_.SetTimestamps(timestamps_);
}
int32_t CreateAndAddAnimationData(int32_t num_frames,
uint32_t num_components) {
// Create and add animation data with.
animation_data_.resize(num_frames * num_components);
for (int i = 0; i < animation_data_.size(); ++i)
animation_data_[i] = static_cast<float>(i);
return keyframe_animation_.AddKeyframes(draco::DT_FLOAT32, num_components,
animation_data_);
}
template <int num_components_t>
void CompareAnimationData() {
// Compare time stamp.
const auto timestamp_att = keyframe_animation_.timestamps();
for (int i = 0; i < timestamps_.size(); ++i) {
std::array<float, 1> att_value;
ASSERT_TRUE((timestamp_att->GetValue<float, 1>(
draco::AttributeValueIndex(i), &att_value)));
ASSERT_FLOAT_EQ(att_value[0], i);
}
// Compare keyframe data.
const auto keyframe_att = keyframe_animation_.keyframes(1);
for (int i = 0; i < animation_data_.size() / num_components_t; ++i) {
std::array<float, num_components_t> att_value;
ASSERT_TRUE((keyframe_att->GetValue<float, num_components_t>(
draco::AttributeValueIndex(i), &att_value)));
for (int j = 0; j < num_components_t; ++j) {
ASSERT_FLOAT_EQ(att_value[j], i * num_components_t + j);
}
}
}
template <int num_components_t>
void TestKeyframeAnimation(int32_t num_frames) {
ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
ASSERT_EQ(CreateAndAddAnimationData(num_frames, num_components_t), 1);
CompareAnimationData<num_components_t>();
}
draco::KeyframeAnimation keyframe_animation_;
std::vector<draco::KeyframeAnimation::TimestampType> timestamps_;
std::vector<float> animation_data_;
};
// Test animation with 1 component and 10 frames.
TEST_F(KeyframeAnimationTest, OneComponent) { TestKeyframeAnimation<1>(10); }
// Test animation with 4 component and 10 frames.
TEST_F(KeyframeAnimationTest, FourComponent) { TestKeyframeAnimation<4>(10); }
// Test adding animation data before timestamp.
TEST_F(KeyframeAnimationTest, AddingAnimationFirst) {
ASSERT_EQ(CreateAndAddAnimationData(5, 1), 1);
ASSERT_TRUE(CreateAndAddTimestamps(5));
}
// Test adding timestamp more than once.
TEST_F(KeyframeAnimationTest, ErrorAddingTimestampsTwice) {
ASSERT_TRUE(CreateAndAddTimestamps(5));
ASSERT_FALSE(CreateAndAddTimestamps(5));
}
// Test animation with multiple animation data.
TEST_F(KeyframeAnimationTest, MultipleAnimationData) {
const int num_frames = 5;
ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
ASSERT_EQ(CreateAndAddAnimationData(num_frames, 1), 1);
ASSERT_EQ(CreateAndAddAnimationData(num_frames, 2), 2);
}
} // namespace

View File

@ -0,0 +1,145 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "draco/attributes/attribute_octahedron_transform.h"
#include "draco/attributes/attribute_transform_type.h"
#include "draco/compression/attributes/normal_compression_utils.h"
namespace draco {
bool AttributeOctahedronTransform::InitFromAttribute(
const PointAttribute &attribute) {
const AttributeTransformData *const transform_data =
attribute.GetAttributeTransformData();
if (!transform_data ||
transform_data->transform_type() != ATTRIBUTE_OCTAHEDRON_TRANSFORM) {
return false; // Wrong transform type.
}
quantization_bits_ = transform_data->GetParameterValue<int32_t>(0);
return true;
}
void AttributeOctahedronTransform::CopyToAttributeTransformData(
AttributeTransformData *out_data) const {
out_data->set_transform_type(ATTRIBUTE_OCTAHEDRON_TRANSFORM);
out_data->AppendParameterValue(quantization_bits_);
}
bool AttributeOctahedronTransform::TransformAttribute(
const PointAttribute &attribute, const std::vector<PointIndex> &point_ids,
PointAttribute *target_attribute) {
return GeneratePortableAttribute(attribute, point_ids,
target_attribute->size(), target_attribute);
}
bool AttributeOctahedronTransform::InverseTransformAttribute(
const PointAttribute &attribute, PointAttribute *target_attribute) {
if (target_attribute->data_type() != DT_FLOAT32) {
return false;
}
const int num_points = target_attribute->size();
const int num_components = target_attribute->num_components();
if (num_components != 3) {
return false;
}
constexpr int kEntrySize = sizeof(float) * 3;
float att_val[3];
const int32_t *source_attribute_data = reinterpret_cast<const int32_t *>(
attribute.GetAddress(AttributeValueIndex(0)));
uint8_t *target_address =
target_attribute->GetAddress(AttributeValueIndex(0));
OctahedronToolBox octahedron_tool_box;
if (!octahedron_tool_box.SetQuantizationBits(quantization_bits_)) {
return false;
}
for (uint32_t i = 0; i < num_points; ++i) {
const int32_t s = *source_attribute_data++;
const int32_t t = *source_attribute_data++;
octahedron_tool_box.QuantizedOctahedralCoordsToUnitVector(s, t, att_val);
// Store the decoded floating point values into the attribute buffer.
std::memcpy(target_address, att_val, kEntrySize);
target_address += kEntrySize;
}
return true;
}
void AttributeOctahedronTransform::SetParameters(int quantization_bits) {
quantization_bits_ = quantization_bits;
}
bool AttributeOctahedronTransform::EncodeParameters(
EncoderBuffer *encoder_buffer) const {
if (is_initialized()) {
encoder_buffer->Encode(static_cast<uint8_t>(quantization_bits_));
return true;
}
return false;
}
bool AttributeOctahedronTransform::DecodeParameters(
const PointAttribute &attribute, DecoderBuffer *decoder_buffer) {
uint8_t quantization_bits;
if (!decoder_buffer->Decode(&quantization_bits)) {
return false;
}
quantization_bits_ = quantization_bits;
return true;
}
bool AttributeOctahedronTransform::GeneratePortableAttribute(
const PointAttribute &attribute, const std::vector<PointIndex> &point_ids,
int num_points, PointAttribute *target_attribute) const {
DRACO_DCHECK(is_initialized());
// Quantize all values in the order given by point_ids into portable
// attribute.
int32_t *const portable_attribute_data = reinterpret_cast<int32_t *>(
target_attribute->GetAddress(AttributeValueIndex(0)));
float att_val[3];
int32_t dst_index = 0;
OctahedronToolBox converter;
if (!converter.SetQuantizationBits(quantization_bits_)) {
return false;
}
if (!point_ids.empty()) {
for (uint32_t i = 0; i < point_ids.size(); ++i) {
const AttributeValueIndex att_val_id =
attribute.mapped_index(point_ids[i]);
attribute.GetValue(att_val_id, att_val);
// Encode the vector into a s and t octahedral coordinates.
int32_t s, t;
converter.FloatVectorToQuantizedOctahedralCoords(att_val, &s, &t);
portable_attribute_data[dst_index++] = s;
portable_attribute_data[dst_index++] = t;
}
} else {
for (PointIndex i(0); i < num_points; ++i) {
const AttributeValueIndex att_val_id = attribute.mapped_index(i);
attribute.GetValue(att_val_id, att_val);
// Encode the vector into a s and t octahedral coordinates.
int32_t s, t;
converter.FloatVectorToQuantizedOctahedralCoords(att_val, &s, &t);
portable_attribute_data[dst_index++] = s;
portable_attribute_data[dst_index++] = t;
}
}
return true;
}
} // namespace draco

View File

@ -0,0 +1,81 @@
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef DRACO_ATTRIBUTES_ATTRIBUTE_OCTAHEDRON_TRANSFORM_H_
#define DRACO_ATTRIBUTES_ATTRIBUTE_OCTAHEDRON_TRANSFORM_H_
#include "draco/attributes/attribute_transform.h"
#include "draco/attributes/point_attribute.h"
#include "draco/core/encoder_buffer.h"
namespace draco {
// Attribute transform for attributes transformed to octahedral coordinates.
class AttributeOctahedronTransform : public AttributeTransform {
public:
AttributeOctahedronTransform() : quantization_bits_(-1) {}
// Return attribute transform type.
AttributeTransformType Type() const override {
return ATTRIBUTE_OCTAHEDRON_TRANSFORM;
}
// Try to init transform from attribute.
bool InitFromAttribute(const PointAttribute &attribute) override;
// Copy parameter values into the provided AttributeTransformData instance.
void CopyToAttributeTransformData(
AttributeTransformData *out_data) const override;
bool TransformAttribute(const PointAttribute &attribute,
const std::vector<PointIndex> &point_ids,
PointAttribute *target_attribute) override;
bool InverseTransformAttribute(const PointAttribute &attribute,
PointAttribute *target_attribute) override;
// Set number of quantization bits.
void SetParameters(int quantization_bits);
// Encode relevant parameters into buffer.
bool EncodeParameters(EncoderBuffer *encoder_buffer) const override;
bool DecodeParameters(const PointAttribute &attribute,
DecoderBuffer *decoder_buffer) override;
bool is_initialized() const { return quantization_bits_ != -1; }
int32_t quantization_bits() const { return quantization_bits_; }
protected:
DataType GetTransformedDataType(
const PointAttribute &attribute) const override {
return DT_UINT32;
}
int GetTransformedNumComponents(
const PointAttribute &attribute) const override {
return 2;
}
// Perform the actual transformation.
bool GeneratePortableAttribute(const PointAttribute &attribute,
const std::vector<PointIndex> &point_ids,
int num_points,
PointAttribute *target_attribute) const;
private:
int32_t quantization_bits_;
};
} // namespace draco
#endif // DRACO_ATTRIBUTES_ATTRIBUTE_OCTAHEDRON_TRANSFORM_H_

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