upgrade draco to 1.5.6

Signed-off-by: Jackie9527 <80555200+Jackie9527@users.noreply.github.com>
pull/4978/head
Jackie9527 2023-02-25 10:11:35 +08:00
parent 90333340d6
commit 39efe4c832
290 changed files with 30940 additions and 2350 deletions

View File

@ -1,13 +1,119 @@
# Generated with cmake-format 0.5.1 with section('parse'):
# Specify structure for custom cmake functions
additional_commands = {
'draco_add_emscripten_executable': {
'kwargs': {
'NAME': '*',
'SOURCES': '*',
'OUTPUT_NAME': '*',
'DEFINES': '*',
'INCLUDES': '*',
'COMPILE_FLAGS': '*',
'LINK_FLAGS': '*',
'OBJLIB_DEPS': '*',
'LIB_DEPS': '*',
'GLUE_PATH': '*',
'PRE_LINK_JS_SOURCES': '*',
'POST_LINK_JS_SOURCES': '*',
'FEATURES': '*',
},
'pargs': 0,
},
'draco_add_executable': {
'kwargs': {
'NAME': '*',
'SOURCES': '*',
'OUTPUT_NAME': '*',
'TEST': 0,
'DEFINES': '*',
'INCLUDES': '*',
'COMPILE_FLAGS': '*',
'LINK_FLAGS': '*',
'OBJLIB_DEPS': '*',
'LIB_DEPS': '*',
},
'pargs': 0,
},
'draco_add_library': {
'kwargs': {
'NAME': '*',
'TYPE': '*',
'SOURCES': '*',
'TEST': 0,
'OUTPUT_NAME': '*',
'DEFINES': '*',
'INCLUDES': '*',
'COMPILE_FLAGS': '*',
'LINK_FLAGS': '*',
'OBJLIB_DEPS': '*',
'LIB_DEPS': '*',
'PUBLIC_INCLUDES': '*',
},
'pargs': 0,
},
'draco_generate_emscripten_glue': {
'kwargs': {
'INPUT_IDL': '*',
'OUTPUT_PATH': '*',
},
'pargs': 0,
},
'draco_get_required_emscripten_flags': {
'kwargs': {
'FLAG_LIST_VAR_COMPILER': '*',
'FLAG_LIST_VAR_LINKER': '*',
},
'pargs': 0,
},
'draco_option': {
'kwargs': {
'NAME': '*',
'HELPSTRING': '*',
'VALUE': '*',
},
'pargs': 0,
},
# Rules for built in CMake commands and those from dependencies.
'list': {
'kwargs': {
'APPEND': '*',
'FILTER': '*',
'FIND': '*',
'GET': '*',
'INSERT': '*',
'JOIN': '*',
'LENGTH': '*',
'POP_BACK': '*',
'POP_FRONT': '*',
'PREPEND': '*',
'REMOVE_DUPLICATES': '*',
'REMOVE_ITEM': '*',
'REVERSE': '*',
'SORT': '*',
'SUBLIST': '*',
'TRANSFORM': '*',
},
},
'protobuf_generate': {
'kwargs': {
'IMPORT_DIRS': '*',
'LANGUAGE': '*',
'OUT_VAR': '*',
'PROTOC_OUT_DIR': '*',
'PROTOS': '*',
},
},
}
with section('format'):
# Formatting options.
# How wide to allow formatted cmake files # How wide to allow formatted cmake files
line_width = 80 line_width = 80
# How many spaces to tab for indent # How many spaces to tab for indent
tab_size = 2 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 # If true, separate flow control names from their parentheses with a space
separate_ctrl_name_with_space = False separate_ctrl_name_with_space = False
@ -15,88 +121,17 @@ separate_ctrl_name_with_space = False
separate_fn_name_with_space = False separate_fn_name_with_space = False
# If a statement is wrapped to more than one line, than dangle the closing # If a statement is wrapped to more than one line, than dangle the closing
# parenthesis on its own line # parenthesis on its own line.
dangle_parens = False dangle_parens = False
# What character to use for bulleted lists # Do not sort argument lists.
bullet_char = '*' enable_sort = False
# What character to use as punctuation after numerals in an enumerated list
enum_char = '.'
# What style line endings to use in the output. # What style line endings to use in the output.
line_ending = u'unix' line_ending = 'unix'
# Format command names consistently as 'lower' or 'upper' case # Format command names consistently as 'lower' or 'upper' case
command_case = u'lower' command_case = 'canonical'
# Format keywords consistently as 'lower' or 'upper' case # Format keywords consistently as 'lower' or 'upper' case
keyword_case = u'unchanged' keyword_case = 'upper'
# 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/.gitattributes vendored 100644
View File

@ -0,0 +1 @@
*.obj eol=lf

12
contrib/draco/.gitmodules vendored 100644
View File

@ -0,0 +1,12 @@
[submodule "third_party/googletest"]
path = third_party/googletest
url = https://github.com/google/googletest.git
[submodule "third_party/eigen"]
path = third_party/eigen
url = https://gitlab.com/libeigen/eigen.git
[submodule "third_party/tinygltf"]
path = third_party/tinygltf
url = https://github.com/syoyo/tinygltf.git
[submodule "third_party/filesystem"]
path = third_party/filesystem
url = https://github.com/gulrak/filesystem

View File

@ -4,8 +4,10 @@ _**Contents**_
* [Mac OS X](#mac-os-x) * [Mac OS X](#mac-os-x)
* [Windows](#windows) * [Windows](#windows)
* [CMake Build Configuration](#cmake-build-configuration) * [CMake Build Configuration](#cmake-build-configuration)
* [Transcoder](#transcoder)
* [Debugging and Optimization](#debugging-and-optimization) * [Debugging and Optimization](#debugging-and-optimization)
* [Googletest Integration](#googletest-integration) * [Googletest Integration](#googletest-integration)
* [Third Party Libraries](#third-party-libraries)
* [Javascript Encoder/Decoder](#javascript-encoderdecoder) * [Javascript Encoder/Decoder](#javascript-encoderdecoder)
* [WebAssembly Decoder](#webassembly-decoder) * [WebAssembly Decoder](#webassembly-decoder)
* [WebAssembly Mesh Only Decoder](#webassembly-mesh-only-decoder) * [WebAssembly Mesh Only Decoder](#webassembly-mesh-only-decoder)
@ -72,6 +74,43 @@ C:\Users\nobody> cmake ../ -G "Visual Studio 16 2019" -A x64
CMake Build Configuration CMake Build Configuration
------------------------- -------------------------
Transcoder
----------
Before attempting to build Draco with transcoding support you must run an
additional Git command to obtain the submodules:
~~~~~ bash
# Run this command from within your Draco clone.
$ git submodule update --init
# See below if you prefer to use existing versions of Draco dependencies.
~~~~~
In order to build the `draco_transcoder` target, the transcoding support needs
to be explicitly enabled when you run `cmake`, for example:
~~~~~ bash
$ cmake ../ -DDRACO_TRANSCODER_SUPPORTED=ON
~~~~~
The above option is currently not compatible with our Javascript or WebAssembly
builds but all other use cases are supported. Note that binaries and libraries
built with the transcoder support may result in increased binary sizes of the
produced libraries and executables compared to the default CMake settings.
The following CMake variables can be used to configure Draco to use local
copies of third party dependencies instead of git submodules.
- `DRACO_EIGEN_PATH`: this path must contain an Eigen directory that includes
the Eigen sources.
- `DRACO_FILESYSTEM_PATH`: this path must contain the ghc directory where the
filesystem includes are located.
- `DRACO_TINYGLTF_PATH`: this path must contain tiny_gltf.h and its
dependencies.
When not specified the Draco build requires the presence of the submodules that
are stored within `draco/third_party`.
Debugging and Optimization Debugging and Optimization
-------------------------- --------------------------
@ -114,17 +153,52 @@ $ cmake ../ -DDRACO_SANITIZE=address
Googletest Integration Googletest Integration
---------------------- ----------------------
Draco includes testing support built using Googletest. To enable Googletest unit Draco includes testing support built using Googletest. The Googletest repository
test support the DRACO_TESTS cmake variable must be turned on at cmake is included as a submodule of the Draco git repository. Run the following
generation time: command to clone the Googletest repository:
~~~~~ bash
$ git submodule update --init
~~~~~
To enable Googletest unit test support the DRACO_TESTS cmake variable must be
turned on at cmake generation time:
~~~~~ bash ~~~~~ bash
$ cmake ../ -DDRACO_TESTS=ON $ cmake ../ -DDRACO_TESTS=ON
~~~~~ ~~~~~
When cmake is used as shown in the above example the googletest directory must To run the tests execute `draco_tests` from your build output directory:
be a sibling of the Draco repository root directory. To run the tests execute
`draco_tests` from your build output directory. ~~~~~ bash
$ ./draco_tests
~~~~~
Draco can be configured to use a local Googletest installation. The
`DRACO_GOOGLETEST_PATH` variable overrides the behavior described above and
configures Draco to use the Googletest at the specified path.
Third Party Libraries
---------------------
When Draco is built with transcoding and/or testing support enabled the project
has dependencies on third party libraries:
- [Eigen](https://eigen.tuxfamily.org/)
- Provides various math utilites.
- [Googletest](https://github.com/google/googletest)
- Provides testing support.
- [Gulrak/filesystem](https://github.com/gulrak/filesystem)
- Provides C++17 std::filesystem emulation for pre-C++17 environments.
- [TinyGLTF](https://github.com/syoyo/tinygltf)
- Provides GLTF I/O support.
These dependencies are managed as Git submodules. To obtain the dependencies
run the following command in your Draco repository:
~~~~~ bash
$ git submodule update --init
~~~~~
WebAssembly Decoder WebAssembly Decoder
------------------- -------------------

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,93 @@
<img width="350px" src="docs/artwork/draco3d-vert.svg" /> <img width="350px" src="docs/artwork/draco3d-vert.svg" />
</p> </p>
[![Build Status](https://github.com/google/draco/workflows/Build/badge.svg)](https://github.com/google/draco/actions?query=workflow%3ABuild) [![draco-ci](https://github.com/google/draco/workflows/draco-ci/badge.svg?branch=master)](https://github.com/google/draco/actions/workflows/ci.yml)
News News
======= =======
Attention GStatic users: the Draco team strongly recommends using the versioned
URLs for accessing Draco GStatic content. If you are using the URLs that include
the `v1/decoders` substring within the URL, edge caching and GStatic propagation
delays can result in transient errors that can be difficult to diagnose when
new Draco releases are launched. To avoid the issue pin your sites to a
versioned release.
### Version 1.5.6 release:
* Using the versioned www.gstatic.com WASM and Javascript decoders continues
to be recommended. To use v1.5.6, use this URL:
* https://www.gstatic.com/draco/versioned/decoders/1.5.6/*
* The CMake flag DRACO_DEBUG_MSVC_WARNINGS has been replaced with
DRACO_DEBUG_COMPILER_WARNINGS, and the behavior has changed. It is now a
boolean flag defined in draco_options.cmake.
* Bug fixes.
* Security fixes.
### Version 1.5.5 release:
* Using the versioned www.gstatic.com WASM and Javascript decoders continues
to be recommended. To use v1.5.5, use this URL:
* https://www.gstatic.com/draco/versioned/decoders/1.5.5/*
* Bug fix: https://github.com/google/draco/issues/935
### Version 1.5.4 release:
* Using the versioned www.gstatic.com WASM and Javascript decoders continues
to be recommended. To use v1.5.4, use this URL:
* https://www.gstatic.com/draco/versioned/decoders/1.5.4/*
* Added partial support for glTF extensions EXT_mesh_features and
EXT_structural_metadata.
* Bug fixes.
* Security fixes.
### Version 1.5.3 release:
* Using the versioned www.gstatic.com WASM and Javascript decoders continues
to be recommended. To use v1.5.3, use this URL:
* https://www.gstatic.com/draco/versioned/decoders/1.5.3/*
* Bug fixes.
### Version 1.5.2 release
* This is the same as v1.5.1 with the following two bug fixes:
* Fixes DRACO_TRANSCODER_SUPPORTED enabled builds.
* ABI version updated.
### Version 1.5.1 release
* Adds assertion enabled Emscripten builds to the release, and a subset of the
assertion enabled builds to GStatic. See the file listing below.
* Custom paths to third party dependencies are now supported. See BUILDING.md
for more information.
* The CMake configuration file draco-config.cmake is now tested and known to
work for using Draco in Linux, MacOS, and Windows CMake projects. See the
`install_test` subdirectory of `src/draco/tools` for more information.
* Bug fixes.
### Version 1.5.0 release
* Adds the draco_transcoder tool. See the section below on the glTF transcoding
tool, and BUILDING.md for build and dependency information.
* Some changes to configuration variables have been made for this release:
- The DRACO_GLTF flag has been renamed to DRACO_GLTF_BITSTREAM to help
increase understanding of its purpose, which is to limit Draco features to
those included in the Draco glTF specification.
- Variables exported in CMake via draco-config.cmake and find-draco.cmake
(formerly FindDraco.cmake) have been renamed. It's unlikely that this
impacts any existing projects as the aforementioned files were not formed
correctly. See [PR775](https://github.com/google/draco/pull/775) for full
details of the changes.
* A CMake version file has been added.
* The CMake install target now uses absolute paths direct from CMake instead
of building them using CMAKE_INSTALL_PREFIX. This was done to make Draco
easier to use for downstream packagers and should have little to no impact on
users picking up Draco from source.
* Certain MSVC warnings have had their levels changed via compiler flag to
reduce the amount of noise output by the MSVC compilers. Set MSVC warning
level to 4, or define DRACO_DEBUG_MSVC_WARNINGS at CMake configuration time
to restore previous behavior.
* Bug fixes.
### Version 1.4.3 release
* Using the versioned www.gstatic.com WASM and Javascript decoders continues
to be recommended. To use v1.4.3, use this URL:
* https://www.gstatic.com/draco/versioned/decoders/1.4.3/*
* Bug fixes
### Version 1.4.1 release ### Version 1.4.1 release
* Using the versioned www.gstatic.com WASM and Javascript decoders is now * Using the versioned www.gstatic.com WASM and Javascript decoders is now
recommended. To use v1.4.1, use this URL: recommended. To use v1.4.1, use this URL:
@ -129,6 +212,7 @@ _**Contents**_
* [Encoding Tool](#encoding-tool) * [Encoding Tool](#encoding-tool)
* [Encoding Point Clouds](#encoding-point-clouds) * [Encoding Point Clouds](#encoding-point-clouds)
* [Decoding Tool](#decoding-tool) * [Decoding Tool](#decoding-tool)
* [glTF Transcoding Tool](#gltf-transcoding-tool)
* [C++ Decoder API](#c-decoder-api) * [C++ Decoder API](#c-decoder-api)
* [Javascript Encoder API](#javascript-encoder-api) * [Javascript Encoder API](#javascript-encoder-api)
* [Javascript Decoder API](#javascript-decoder-api) * [Javascript Decoder API](#javascript-decoder-api)
@ -136,6 +220,7 @@ _**Contents**_
* [Metadata API](#metadata-api) * [Metadata API](#metadata-api)
* [NPM Package](#npm-package) * [NPM Package](#npm-package)
* [three.js Renderer Example](#threejs-renderer-example) * [three.js Renderer Example](#threejs-renderer-example)
* [GStatic Javascript Builds](#gstatic-javascript-builds)
* [Support](#support) * [Support](#support)
* [License](#license) * [License](#license)
* [References](#references) * [References](#references)
@ -170,16 +255,18 @@ Command Line Applications
------------------------ ------------------------
The default target created from the build files will be the `draco_encoder` The default target created from the build files will be the `draco_encoder`
and `draco_decoder` command line applications. For both applications, if you and `draco_decoder` command line applications. Additionally, `draco_transcoder`
run them without any arguments or `-h`, the applications will output usage and is generated when CMake is run with the DRACO_TRANSCODER_SUPPORTED variable set
options. to ON (see [BUILDING](BUILDING.md#transcoder) for more details). For all
applications, if you run them without any arguments or `-h`, the applications
will output usage and options.
Encoding Tool Encoding Tool
------------- -------------
`draco_encoder` will read OBJ or PLY files as input, and output Draco-encoded `draco_encoder` will read OBJ, STL or PLY files as input, and output
files. We have included Stanford's [Bunny] mesh for testing. The basic command Draco-encoded files. We have included Stanford's [Bunny] mesh for testing. The
line looks like this: basic command line looks like this:
~~~~~ bash ~~~~~ bash
./draco_encoder -i testdata/bun_zipper.ply -o out.drc ./draco_encoder -i testdata/bun_zipper.ply -o out.drc
@ -232,15 +319,34 @@ and denser point clouds.
Decoding Tool Decoding Tool
------------- -------------
`draco_decoder` will read Draco files as input, and output OBJ or PLY files. `draco_decoder` will read Draco files as input, and output OBJ, STL or PLY
The basic command line looks like this: files. The basic command line looks like this:
~~~~~ bash ~~~~~ bash
./draco_decoder -i in.drc -o out.obj ./draco_decoder -i in.drc -o out.obj
~~~~~ ~~~~~
glTF Transcoding Tool
---------------------
`draco_transcoder` can be used to add Draco compression to glTF assets. The
basic command line looks like this:
~~~~~ bash
./draco_transcoder -i in.glb -o out.glb
~~~~~
This command line will add geometry compression to all meshes in the `in.glb`
file. Quantization values for different glTF attributes can be specified
similarly to the `draco_encoder` tool. For example `-qp` can be used to define
quantization of the position attribute:
~~~~~ bash
./draco_transcoder -i in.glb -o out.glb -qp 12
~~~~~
C++ Decoder API C++ Decoder API
------------- ---------------
If you'd like to add decoding to your applications you will need to include 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 the `draco_dec` library. In order to use the Draco decoder you need to
@ -442,6 +548,30 @@ Javascript decoder using the `three.js` renderer.
Please see the [javascript/example/README.md](javascript/example/README.md) file for more information. Please see the [javascript/example/README.md](javascript/example/README.md) file for more information.
GStatic Javascript Builds
=========================
Prebuilt versions of the Emscripten-built Draco javascript decoders are hosted
on www.gstatic.com in version labeled directories:
https://www.gstatic.com/draco/versioned/decoders/VERSION/*
As of the v1.4.3 release the files available are:
- [draco_decoder.js](https://www.gstatic.com/draco/versioned/decoders/1.4.3/draco_decoder.js)
- [draco_decoder.wasm](https://www.gstatic.com/draco/versioned/decoders/1.4.3/draco_decoder.wasm)
- [draco_decoder_gltf.js](https://www.gstatic.com/draco/versioned/decoders/1.4.3/draco_decoder_gltf.js)
- [draco_decoder_gltf.wasm](https://www.gstatic.com/draco/versioned/decoders/1.4.3/draco_decoder_gltf.wasm)
- [draco_wasm_wrapper.js](https://www.gstatic.com/draco/versioned/decoders/1.4.3/draco_wasm_wrapper.js)
- [draco_wasm_wrapper_gltf.js](https://www.gstatic.com/draco/versioned/decoders/1.4.3/draco_wasm_wrapper_gltf.js)
Beginning with the v1.5.1 release assertion enabled builds of the following
files are available:
- [draco_decoder.js](https://www.gstatic.com/draco/versioned/decoders/1.5.1/with_asserts/draco_decoder.js)
- [draco_decoder.wasm](https://www.gstatic.com/draco/versioned/decoders/1.5.1/with_asserts/draco_decoder.wasm)
- [draco_wasm_wrapper.js](https://www.gstatic.com/draco/versioned/decoders/1.5.1/with_asserts/draco_wasm_wrapper.js)
Support Support
======= =======

View File

@ -1,3 +0,0 @@
@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

@ -1,56 +0,0 @@
# 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

@ -1,220 +0,0 @@
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

@ -1,103 +0,0 @@
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

@ -1,2 +1,3 @@
set(DRACO_INCLUDE_DIRS "@DRACO_INCLUDE_DIRS@") @PACKAGE_INIT@
set(DRACO_LIBRARIES "draco")
include("${CMAKE_CURRENT_LIST_DIR}/draco-targets.cmake")

View File

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

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_) if(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_ endif() # DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_
@ -17,10 +31,6 @@ macro(set_draco_target)
endif() endif()
set(draco_plugin_dependency draco_static) set(draco_plugin_dependency draco_static)
endif() endif()
if(BUILD_SHARED_LIBS)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
endmacro() endmacro()
# Configures flags and sets build system globals. # Configures flags and sets build system globals.
@ -36,23 +46,37 @@ macro(draco_set_build_definitions)
endif() endif()
draco_load_version_info() draco_load_version_info()
set(DRACO_SOVERSION 1)
# Library version info. See the libtool docs for updating the values:
# https://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
#
# c=<current>, r=<revision>, a=<age>
#
# libtool generates a .so file as .so.[c-a].a.r, while -version-info c:r:a is
# passed to libtool.
#
# We set DRACO_SOVERSION = [c-a].a.r
set(LT_CURRENT 8)
set(LT_REVISION 0)
set(LT_AGE 0)
math(EXPR DRACO_SOVERSION_MAJOR "${LT_CURRENT} - ${LT_AGE}")
set(DRACO_SOVERSION "${DRACO_SOVERSION_MAJOR}.${LT_AGE}.${LT_REVISION}")
unset(LT_CURRENT)
unset(LT_REVISION)
unset(LT_AGE)
list(APPEND draco_include_paths "${draco_root}" "${draco_root}/src" list(APPEND draco_include_paths "${draco_root}" "${draco_root}/src"
"${draco_build}") "${draco_build}")
if(DRACO_ABSL) if(DRACO_TRANSCODER_SUPPORTED)
list(APPEND draco_include_path "${draco_root}/third_party/abseil-cpp") draco_setup_eigen()
draco_setup_filesystem()
draco_setup_tinygltf()
endif() 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" list(APPEND draco_defines "DRACO_CMAKE=1"
"DRACO_FLAGS_SRCDIR=\"${draco_root}\"" "DRACO_FLAGS_SRCDIR=\"${draco_root}\""
"DRACO_FLAGS_TMPDIR=\"/tmp\"") "DRACO_FLAGS_TMPDIR=\"/tmp\"")
@ -63,11 +87,22 @@ macro(draco_set_build_definitions)
if(BUILD_SHARED_LIBS) if(BUILD_SHARED_LIBS)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif() endif()
else() endif()
if(NOT MSVC)
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
# Ensure 64-bit platforms can support large files. # Ensure 64-bit platforms can support large files.
list(APPEND draco_defines "_LARGEFILE_SOURCE" "_FILE_OFFSET_BITS=64") list(APPEND draco_defines "_LARGEFILE_SOURCE" "_FILE_OFFSET_BITS=64")
endif() endif()
if(NOT DRACO_DEBUG_COMPILER_WARNINGS)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
list(APPEND draco_clang_cxx_flags
"-Wno-implicit-const-int-float-conversion")
else()
list(APPEND draco_base_cxx_flags "-Wno-deprecated-declarations")
endif()
endif()
endif() endif()
if(ANDROID) if(ANDROID)
@ -102,13 +137,9 @@ macro(draco_set_build_definitions)
set(draco_neon_source_file_suffix "neon.cc") set(draco_neon_source_file_suffix "neon.cc")
set(draco_sse4_source_file_suffix "sse4.cc") set(draco_sse4_source_file_suffix "sse4.cc")
if((${CMAKE_CXX_COMPILER_ID} if((${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" AND ${CMAKE_CXX_COMPILER_VERSION}
STREQUAL VERSION_LESS 5)
"GNU" OR (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"
AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 5)
OR (${CMAKE_CXX_COMPILER_ID}
STREQUAL
"Clang"
AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4)) AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4))
message( message(
WARNING "GNU/GCC < v5 or Clang/LLVM < v4, ENABLING COMPATIBILITY MODE.") WARNING "GNU/GCC < v5 or Clang/LLVM < v4, ENABLING COMPATIBILITY MODE.")
@ -117,7 +148,9 @@ macro(draco_set_build_definitions)
if(EMSCRIPTEN) if(EMSCRIPTEN)
draco_check_emscripten_environment() draco_check_emscripten_environment()
draco_get_required_emscripten_flags(FLAG_LIST_VAR draco_base_cxx_flags) draco_get_required_emscripten_flags(
FLAG_LIST_VAR_COMPILER draco_base_cxx_flags
FLAG_LIST_VAR_LINKER draco_base_exe_linker_flags)
endif() endif()
draco_configure_sanitizer() draco_configure_sanitizer()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_CPU_DETECTION_CMAKE_) if(DRACO_CMAKE_DRACO_CPU_DETECTION_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_CPU_DETECTION_CMAKE_ endif() # DRACO_CMAKE_DRACO_CPU_DETECTION_CMAKE_

View File

@ -0,0 +1,136 @@
# Copyright 2022 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.
if(DRACO_CMAKE_DRACO_DEPENDENCIES_CMAKE)
return()
endif()
set(DRACO_CMAKE_DRACO_DEPENDENCIES_CMAKE 1)
include("${draco_root}/cmake/draco_variables.cmake")
# Each variable holds a user specified custom path to a local copy of the
# sources that belong to each project that Draco depends on. When paths are
# empty the build will be generated pointing to the Draco git submodules.
# Otherwise the paths specified by the user will be used in the build
# configuration.
# Path to the Eigen. The path must contain the Eigen directory.
set(DRACO_EIGEN_PATH)
draco_track_configuration_variable(DRACO_EIGEN_PATH)
# Path to the gulrak/filesystem installation. The path specified must contain
# the ghc subdirectory that houses the filesystem includes.
set(DRACO_FILESYSTEM_PATH)
draco_track_configuration_variable(DRACO_FILESYSTEM_PATH)
# Path to the googletest installation. The path must be to the root of the
# Googletest project directory.
set(DRACO_GOOGLETEST_PATH)
draco_track_configuration_variable(DRACO_GOOGLETEST_PATH)
# Path to the syoyo/tinygltf installation. The path must be to the root of the
# project directory.
set(DRACO_TINYGLTF_PATH)
draco_track_configuration_variable(DRACO_TINYGLTF_PATH)
# Utility macro for killing the build due to a missing submodule directory.
macro(draco_die_missing_submodule dir)
message(FATAL_ERROR "${dir} missing, run git submodule update --init")
endmacro()
# Determines the Eigen location and updates the build configuration accordingly.
macro(draco_setup_eigen)
if(DRACO_EIGEN_PATH)
set(eigen_path "${DRACO_EIGEN_PATH}")
if(NOT IS_DIRECTORY "${eigen_path}")
message(FATAL_ERROR "DRACO_EIGEN_PATH does not exist.")
endif()
else()
set(eigen_path "${draco_root}/third_party/eigen")
if(NOT IS_DIRECTORY "${eigen_path}")
draco_die_missing_submodule("${eigen_path}")
endif()
endif()
set(eigen_include_path "${eigen_path}/Eigen")
if(NOT EXISTS "${eigen_path}/Eigen")
message(FATAL_ERROR "The eigen path does not contain an Eigen directory.")
endif()
list(APPEND draco_include_paths "${eigen_path}")
endmacro()
# Determines the gulrak/filesystem location and updates the build configuration
# accordingly.
macro(draco_setup_filesystem)
if(DRACO_FILESYSTEM_PATH)
set(fs_path "${DRACO_FILESYSTEM_PATH}")
if(NOT IS_DIRECTORY "${fs_path}")
message(FATAL_ERROR "DRACO_FILESYSTEM_PATH does not exist.")
endif()
else()
set(fs_path "${draco_root}/third_party/filesystem/include")
if(NOT IS_DIRECTORY "${fs_path}")
draco_die_missing_submodule("${fs_path}")
endif()
endif()
list(APPEND draco_include_paths "${fs_path}")
endmacro()
# Determines the Googletest location and sets up include and source list vars
# for the draco_tests build.
macro(draco_setup_googletest)
if(DRACO_GOOGLETEST_PATH)
set(gtest_path "${DRACO_GOOGLETEST_PATH}")
if(NOT IS_DIRECTORY "${gtest_path}")
message(FATAL_ERROR "DRACO_GOOGLETEST_PATH does not exist.")
endif()
else()
set(gtest_path "${draco_root}/third_party/googletest")
endif()
list(APPEND draco_test_include_paths ${draco_include_paths}
"${gtest_path}/include" "${gtest_path}/googlemock"
"${gtest_path}/googletest/include" "${gtest_path}/googletest")
list(APPEND draco_gtest_all "${gtest_path}/googletest/src/gtest-all.cc")
list(APPEND draco_gtest_main "${gtest_path}/googletest/src/gtest_main.cc")
endmacro()
# Determines the location of TinyGLTF and updates the build configuration
# accordingly.
macro(draco_setup_tinygltf)
if(DRACO_TINYGLTF_PATH)
set(tinygltf_path "${DRACO_TINYGLTF_PATH}")
if(NOT IS_DIRECTORY "${tinygltf_path}")
message(FATAL_ERROR "DRACO_TINYGLTF_PATH does not exist.")
endif()
else()
set(tinygltf_path "${draco_root}/third_party/tinygltf")
if(NOT IS_DIRECTORY "${tinygltf_path}")
draco_die_missing_submodule("${tinygltf_path}")
endif()
endif()
list(APPEND draco_include_paths "${tinygltf_path}")
endmacro()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_) if(DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_ endif() # DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_
@ -18,39 +32,64 @@ endmacro()
# Obtains the required Emscripten flags for Draco targets. # Obtains the required Emscripten flags for Draco targets.
macro(draco_get_required_emscripten_flags) macro(draco_get_required_emscripten_flags)
set(em_FLAG_LIST_VAR) set(em_FLAG_LIST_VAR_COMPILER)
set(em_FLAG_LIST_VAR_LINKER)
set(em_flags) set(em_flags)
set(em_single_arg_opts FLAG_LIST_VAR) set(em_single_arg_opts FLAG_LIST_VAR_COMPILER FLAG_LIST_VAR_LINKER)
set(em_multi_arg_opts) set(em_multi_arg_opts)
cmake_parse_arguments(em "${em_flags}" "${em_single_arg_opts}" cmake_parse_arguments(em "${em_flags}" "${em_single_arg_opts}"
"${em_multi_arg_opts}" ${ARGN}) "${em_multi_arg_opts}" ${ARGN})
if(NOT em_FLAG_LIST_VAR) if(NOT em_FLAG_LIST_VAR_COMPILER)
message(FATAL "draco_get_required_emscripten_flags: FLAG_LIST_VAR required") message(
FATAL
"draco_get_required_emscripten_flags: FLAG_LIST_VAR_COMPILER required")
endif()
if(NOT em_FLAG_LIST_VAR_LINKER)
message(
FATAL
"draco_get_required_emscripten_flags: FLAG_LIST_VAR_LINKER required")
endif() endif()
if(DRACO_JS_GLUE) if(DRACO_JS_GLUE)
unset(required_flags) unset(required_flags)
list(APPEND ${em_FLAG_LIST_VAR} "-sALLOW_MEMORY_GROWTH=1") # TODO(tomfinegan): Revisit splitting of compile/link flags for Emscripten,
list(APPEND ${em_FLAG_LIST_VAR} "-Wno-almost-asm") # and drop -Wno-unused-command-line-argument. Emscripten complains about
list(APPEND ${em_FLAG_LIST_VAR} "--memory-init-file" "0") # what are supposedly link-only flags sent with compile commands, but then
list(APPEND ${em_FLAG_LIST_VAR} "-fno-omit-frame-pointer") # proceeds to produce broken code if the warnings are heeded.
list(APPEND ${em_FLAG_LIST_VAR} "-sMODULARIZE=1") list(APPEND ${em_FLAG_LIST_VAR_COMPILER}
list(APPEND ${em_FLAG_LIST_VAR} "-sNO_FILESYSTEM=1") "-Wno-unused-command-line-argument")
list(APPEND ${em_FLAG_LIST_VAR} "-sEXPORTED_RUNTIME_METHODS=[]")
list(APPEND ${em_FLAG_LIST_VAR} "-sPRECISE_F32=1") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-Wno-almost-asm")
list(APPEND ${em_FLAG_LIST_VAR} "-sNODEJS_CATCH_EXIT=0") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "--memory-init-file" "0")
list(APPEND ${em_FLAG_LIST_VAR} "-sNODEJS_CATCH_REJECTION=0") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-fno-omit-frame-pointer")
# According to Emscripten the following flags are linker only, but sending
# these flags (en masse) to only the linker results in a broken Emscripten
# build with an empty DracoDecoderModule.
list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sALLOW_MEMORY_GROWTH=1")
list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sMODULARIZE=1")
list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sFILESYSTEM=0")
list(APPEND ${em_FLAG_LIST_VAR_COMPILER}
"-sEXPORTED_FUNCTIONS=[\"_free\",\"_malloc\"]")
list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sPRECISE_F32=1")
list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sNODEJS_CATCH_EXIT=0")
list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sNODEJS_CATCH_REJECTION=0")
if(DRACO_FAST) if(DRACO_FAST)
list(APPEND ${em_FLAG_LIST_VAR} "--llvm-lto" "1") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "--llvm-lto" "1")
endif() endif()
# The WASM flag is reported as linker only.
if(DRACO_WASM) if(DRACO_WASM)
list(APPEND ${em_FLAG_LIST_VAR} "-sWASM=1") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sWASM=1")
else() else()
list(APPEND ${em_FLAG_LIST_VAR} "-sWASM=0") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sWASM=0")
endif() endif()
# The LEGACY_VM_SUPPORT flag is reported as linker only.
if(DRACO_IE_COMPATIBLE) if(DRACO_IE_COMPATIBLE)
list(APPEND ${em_FLAG_LIST_VAR} "-sLEGACY_VM_SUPPORT=1") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sLEGACY_VM_SUPPORT=1")
endif() endif()
endif() endif()
endmacro() endmacro()
@ -66,9 +105,10 @@ macro(draco_generate_emscripten_glue)
"${glue_multi_arg_opts}" ${ARGN}) "${glue_multi_arg_opts}" ${ARGN})
if(DRACO_VERBOSE GREATER 1) if(DRACO_VERBOSE GREATER 1)
message("--------- draco_generate_emscripten_glue -----------\n" message(
"--------- draco_generate_emscripten_glue -----------\n"
"glue_INPUT_IDL=${glue_INPUT_IDL}\n" "glue_INPUT_IDL=${glue_INPUT_IDL}\n"
"glue_OUTPUT_PATH=${glue_OUTPUT_PATH}\n" ] "glue_OUTPUT_PATH=${glue_OUTPUT_PATH}\n"
"----------------------------------------------------\n") "----------------------------------------------------\n")
endif() endif()
@ -79,17 +119,17 @@ macro(draco_generate_emscripten_glue)
endif() endif()
# Generate the glue source. # Generate the glue source.
execute_process(COMMAND ${PYTHON_EXECUTABLE} execute_process(
$ENV{EMSCRIPTEN}/tools/webidl_binder.py COMMAND ${PYTHON_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py
${glue_INPUT_IDL} ${glue_OUTPUT_PATH}) ${glue_INPUT_IDL} ${glue_OUTPUT_PATH})
if(NOT EXISTS "${glue_OUTPUT_PATH}.cpp") if(NOT EXISTS "${glue_OUTPUT_PATH}.cpp")
message(FATAL_ERROR "JS glue generation failed for ${glue_INPUT_IDL}.") message(FATAL_ERROR "JS glue generation failed for ${glue_INPUT_IDL}.")
endif() endif()
# Create a dependency so that it regenerated on edits. # Create a dependency so that it regenerated on edits.
add_custom_command(OUTPUT "${glue_OUTPUT_PATH}.cpp" add_custom_command(
COMMAND ${PYTHON_EXECUTABLE} OUTPUT "${glue_OUTPUT_PATH}.cpp"
$ENV{EMSCRIPTEN}/tools/webidl_binder.py COMMAND ${PYTHON_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py
${glue_INPUT_IDL} ${glue_OUTPUT_PATH} ${glue_INPUT_IDL} ${glue_OUTPUT_PATH}
DEPENDS ${draco_js_dec_idl} DEPENDS ${draco_js_dec_idl}
COMMENT "Generating ${glue_OUTPUT_PATH}.cpp." COMMENT "Generating ${glue_OUTPUT_PATH}.cpp."
@ -120,8 +160,14 @@ macro(draco_add_emscripten_executable)
unset(emexe_LINK_FLAGS) unset(emexe_LINK_FLAGS)
set(optional_args) set(optional_args)
set(single_value_args NAME GLUE_PATH) set(single_value_args NAME GLUE_PATH)
set(multi_value_args SOURCES DEFINES FEATURES INCLUDES LINK_FLAGS set(multi_value_args
PRE_LINK_JS_SOURCES POST_LINK_JS_SOURCES) SOURCES
DEFINES
FEATURES
INCLUDES
LINK_FLAGS
PRE_LINK_JS_SOURCES
POST_LINK_JS_SOURCES)
cmake_parse_arguments(emexe "${optional_args}" "${single_value_args}" cmake_parse_arguments(emexe "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN}) "${multi_value_args}" ${ARGN})
@ -136,7 +182,8 @@ macro(draco_add_emscripten_executable)
endif() endif()
if(DRACO_VERBOSE GREATER 1) if(DRACO_VERBOSE GREATER 1)
message("--------- draco_add_emscripten_executable ---------\n" message(
"--------- draco_add_emscripten_executable ---------\n"
"emexe_NAME=${emexe_NAME}\n" "emexe_NAME=${emexe_NAME}\n"
"emexe_SOURCES=${emexe_SOURCES}\n" "emexe_SOURCES=${emexe_SOURCES}\n"
"emexe_DEFINES=${emexe_DEFINES}\n" "emexe_DEFINES=${emexe_DEFINES}\n"
@ -153,30 +200,30 @@ macro(draco_add_emscripten_executable)
# passed in with the target. # passed in with the target.
list(APPEND emexe_LINK_FLAGS ${DRACO_CXX_FLAGS}) list(APPEND emexe_LINK_FLAGS ${DRACO_CXX_FLAGS})
if(DRACO_GLTF) if(DRACO_GLTF_BITSTREAM)
draco_add_executable(NAME # Add "_gltf" suffix to target output name.
${emexe_NAME} draco_add_executable(
OUTPUT_NAME NAME ${emexe_NAME}
${emexe_NAME}_gltf OUTPUT_NAME ${emexe_NAME}_gltf
SOURCES SOURCES ${emexe_SOURCES}
${emexe_SOURCES} DEFINES ${emexe_DEFINES}
DEFINES INCLUDES ${emexe_INCLUDES}
${emexe_DEFINES} LINK_FLAGS ${emexe_LINK_FLAGS})
INCLUDES
${emexe_INCLUDES}
LINK_FLAGS
${emexe_LINK_FLAGS})
else() else()
draco_add_executable(NAME ${emexe_NAME} SOURCES ${emexe_SOURCES} DEFINES draco_add_executable(
${emexe_DEFINES} INCLUDES ${emexe_INCLUDES} LINK_FLAGS NAME ${emexe_NAME}
${emexe_LINK_FLAGS}) SOURCES ${emexe_SOURCES}
DEFINES ${emexe_DEFINES}
INCLUDES ${emexe_INCLUDES}
LINK_FLAGS ${emexe_LINK_FLAGS})
endif() endif()
foreach(feature ${emexe_FEATURES}) foreach(feature ${emexe_FEATURES})
draco_enable_feature(FEATURE ${feature} TARGETS ${emexe_NAME}) draco_enable_feature(FEATURE ${feature} TARGETS ${emexe_NAME})
endforeach() endforeach()
set_property(SOURCE ${emexe_SOURCES} set_property(
SOURCE ${emexe_SOURCES}
APPEND APPEND
PROPERTY OBJECT_DEPENDS "${emexe_GLUE_PATH}.cpp") PROPERTY OBJECT_DEPENDS "${emexe_GLUE_PATH}.cpp")
em_link_pre_js(${emexe_NAME} ${emexe_PRE_LINK_JS_SOURCES}) em_link_pre_js(${emexe_NAME} ${emexe_PRE_LINK_JS_SOURCES})

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_FLAGS_CMAKE_) if(DRACO_CMAKE_DRACO_FLAGS_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_FLAGS_CMAKE_ endif() # DRACO_CMAKE_DRACO_FLAGS_CMAKE_
@ -85,8 +99,8 @@ macro(draco_test_cxx_flag)
# are passed as a list it will remove the list separators, and attempt to run # are passed as a list it will remove the list separators, and attempt to run
# a compile command using list entries concatenated together as a single # a compile command using list entries concatenated together as a single
# argument. Avoid the problem by forcing the argument to be a string. # argument. Avoid the problem by forcing the argument to be a string.
draco_set_and_stringify(SOURCE_VARS all_cxx_flags DEST all_cxx_flags) draco_set_and_stringify(SOURCE_VARS all_cxx_flags DEST all_cxx_flags_string)
check_cxx_compiler_flag("${all_cxx_flags}" draco_all_cxx_flags_pass) check_cxx_compiler_flag("${all_cxx_flags_string}" draco_all_cxx_flags_pass)
if(cxx_test_FLAG_REQUIRED AND NOT 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): " draco_die("Flag test failed for required flag(s): "
@ -245,3 +259,34 @@ macro(draco_set_cxx_flags)
draco_test_cxx_flag(FLAG_LIST_VAR_NAMES ${cxx_flag_lists}) draco_test_cxx_flag(FLAG_LIST_VAR_NAMES ${cxx_flag_lists})
endif() endif()
endmacro() endmacro()
# Collects Draco built-in and user-specified linker flags and tests them. Halts
# configuration and reports the error when any flags cause the build to fail.
#
# Note: draco_test_exe_linker_flag() does the real work of setting the flags and
# running the test compile commands.
macro(draco_set_exe_linker_flags)
unset(linker_flag_lists)
if(DRACO_VERBOSE)
message("draco_set_exe_linker_flags: "
"draco_base_exe_linker_flags=${draco_base_exe_linker_flags}")
endif()
if(draco_base_exe_linker_flags)
list(APPEND linker_flag_lists draco_base_exe_linker_flags)
endif()
if(linker_flag_lists)
unset(test_linker_flags)
if(DRACO_VERBOSE)
message("draco_set_exe_linker_flags: "
"linker_flag_lists=${linker_flag_lists}")
endif()
draco_set_and_stringify(DEST test_linker_flags SOURCE_VARS
${linker_flag_lists})
draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME test_linker_flags)
endif()
endmacro()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_HELPERS_CMAKE_) if(DRACO_CMAKE_DRACO_HELPERS_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_HELPERS_CMAKE_ endif() # DRACO_CMAKE_DRACO_HELPERS_CMAKE_

View File

@ -1,32 +1,32 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_INSTALL_CMAKE_) if(DRACO_CMAKE_DRACO_INSTALL_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_INSTALL_CMAKE_ endif() # DRACO_CMAKE_DRACO_INSTALL_CMAKE_
set(DRACO_CMAKE_DRACO_INSTALL_CMAKE_ 1) set(DRACO_CMAKE_DRACO_INSTALL_CMAKE_ 1)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
# Sets up the draco install targets. Must be called after the static library # Sets up the draco install targets. Must be called after the static library
# target is created. # target is created.
macro(draco_setup_install_target) macro(draco_setup_install_target)
include(GNUInstallDirs) set(bin_path "${CMAKE_INSTALL_BINDIR}")
set(data_path "${CMAKE_INSTALL_DATAROOTDIR}")
# pkg-config: draco.pc set(includes_path "${CMAKE_INSTALL_INCLUDEDIR}")
set(prefix "${CMAKE_INSTALL_PREFIX}") set(libs_path "${CMAKE_INSTALL_LIBDIR}")
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}) foreach(file ${draco_sources})
if(file MATCHES "h$") if(file MATCHES "h$")
@ -34,46 +34,88 @@ macro(draco_setup_install_target)
endif() endif()
endforeach() endforeach()
list(REMOVE_DUPLICATES draco_api_includes)
# Strip $draco_src_root from the file paths: we need to install relative to # Strip $draco_src_root from the file paths: we need to install relative to
# $include_directory. # $include_directory.
list(TRANSFORM draco_api_includes REPLACE "${draco_src_root}/" "") 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}) foreach(draco_api_include ${draco_api_includes})
get_filename_component(file_directory ${draco_api_include} DIRECTORY) get_filename_component(file_directory ${draco_api_include} DIRECTORY)
set(target_directory "${include_directory}/draco/${file_directory}") set(target_directory "${includes_path}/draco/${file_directory}")
install(FILES ${draco_src_root}/${draco_api_include} install(FILES ${draco_src_root}/${draco_api_include}
DESTINATION "${target_directory}") DESTINATION "${target_directory}")
endforeach() endforeach()
install( install(FILES "${draco_build}/draco/draco_features.h"
FILES "${draco_build}/draco/draco_features.h" DESTINATION "${includes_path}/draco/")
DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/draco/")
install(TARGETS draco_decoder DESTINATION install(TARGETS draco_decoder DESTINATION "${bin_path}")
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") install(TARGETS draco_encoder DESTINATION "${bin_path}")
install(TARGETS draco_encoder DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") if(DRACO_TRANSCODER_SUPPORTED)
install(TARGETS draco_transcoder DESTINATION "${bin_path}")
endif()
if(MSVC) if(MSVC)
install(TARGETS draco DESTINATION install(
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") TARGETS draco
EXPORT dracoExport
RUNTIME DESTINATION "${bin_path}"
ARCHIVE DESTINATION "${libs_path}"
LIBRARY DESTINATION "${libs_path}")
else() else()
install(TARGETS draco_static DESTINATION install(
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") TARGETS draco_static
EXPORT dracoExport
DESTINATION "${libs_path}")
if(BUILD_SHARED_LIBS) if(BUILD_SHARED_LIBS)
install(TARGETS draco_shared DESTINATION install(
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") TARGETS draco_shared
EXPORT dracoExport
RUNTIME DESTINATION "${bin_path}"
ARCHIVE DESTINATION "${libs_path}"
LIBRARY DESTINATION "${libs_path}")
endif() endif()
endif() endif()
if(DRACO_UNITY_PLUGIN) if(DRACO_UNITY_PLUGIN)
install(TARGETS dracodec_unity DESTINATION install(TARGETS dracodec_unity DESTINATION "${libs_path}")
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()
if(DRACO_MAYA_PLUGIN)
install(TARGETS draco_maya_wrapper DESTINATION
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif() endif()
if(DRACO_MAYA_PLUGIN)
install(TARGETS draco_maya_wrapper DESTINATION "${libs_path}")
endif()
# pkg-config: draco.pc
configure_file("${draco_root}/cmake/draco.pc.template"
"${draco_build}/draco.pc" @ONLY NEWLINE_STYLE UNIX)
install(FILES "${draco_build}/draco.pc" DESTINATION "${libs_path}/pkgconfig")
# CMake config: draco-config.cmake
configure_package_config_file(
"${draco_root}/cmake/draco-config.cmake.template"
"${draco_build}/draco-config.cmake"
INSTALL_DESTINATION "${data_path}/cmake/draco")
write_basic_package_version_file(
"${draco_build}/draco-config-version.cmake"
VERSION ${DRACO_VERSION}
COMPATIBILITY AnyNewerVersion)
export(
EXPORT dracoExport
NAMESPACE draco::
FILE "${draco_build}/draco-targets.cmake")
install(
EXPORT dracoExport
NAMESPACE draco::
FILE draco-targets.cmake
DESTINATION "${data_path}/cmake/draco")
install(FILES "${draco_build}/draco-config.cmake"
"${draco_build}/draco-config-version.cmake"
DESTINATION "${data_path}/cmake/draco")
endmacro() endmacro()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_INTRINSICS_CMAKE_) if(DRACO_CMAKE_DRACO_INTRINSICS_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_INTRINSICS_CMAKE_ endif() # DRACO_CMAKE_DRACO_INTRINSICS_CMAKE_
@ -61,14 +75,12 @@ macro(draco_process_intrinsics_sources)
unset(sse4_sources) unset(sse4_sources)
list(APPEND sse4_sources ${arg_SOURCES}) list(APPEND sse4_sources ${arg_SOURCES})
list(FILTER sse4_sources INCLUDE REGEX list(FILTER sse4_sources INCLUDE REGEX "${draco_sse4_source_file_suffix}$")
"${draco_sse4_source_file_suffix}$")
if(sse4_sources) if(sse4_sources)
unset(sse4_flags) unset(sse4_flags)
draco_get_intrinsics_flag_for_suffix(SUFFIX draco_get_intrinsics_flag_for_suffix(
${draco_sse4_source_file_suffix} SUFFIX ${draco_sse4_source_file_suffix} VARIABLE sse4_flags)
VARIABLE sse4_flags)
if(sse4_flags) if(sse4_flags)
draco_set_compiler_flags_for_sources(SOURCES ${sse4_sources} FLAGS draco_set_compiler_flags_for_sources(SOURCES ${sse4_sources} FLAGS
${sse4_flags}) ${sse4_flags})
@ -79,14 +91,12 @@ macro(draco_process_intrinsics_sources)
if(DRACO_ENABLE_NEON AND draco_have_neon) if(DRACO_ENABLE_NEON AND draco_have_neon)
unset(neon_sources) unset(neon_sources)
list(APPEND neon_sources ${arg_SOURCES}) list(APPEND neon_sources ${arg_SOURCES})
list(FILTER neon_sources INCLUDE REGEX list(FILTER neon_sources INCLUDE REGEX "${draco_neon_source_file_suffix}$")
"${draco_neon_source_file_suffix}$")
if(neon_sources AND DRACO_NEON_INTRINSICS_FLAG) if(neon_sources AND DRACO_NEON_INTRINSICS_FLAG)
unset(neon_flags) unset(neon_flags)
draco_get_intrinsics_flag_for_suffix(SUFFIX draco_get_intrinsics_flag_for_suffix(
${draco_neon_source_file_suffix} SUFFIX ${draco_neon_source_file_suffix} VARIABLE neon_flags)
VARIABLE neon_flags)
if(neon_flags) if(neon_flags)
draco_set_compiler_flags_for_sources(SOURCES ${neon_sources} FLAGS draco_set_compiler_flags_for_sources(SOURCES ${neon_sources} FLAGS
${neon_flags}) ${neon_flags})

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_OPTIONS_CMAKE_) if(DRACO_CMAKE_DRACO_OPTIONS_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_OPTIONS_CMAKE_ endif() # DRACO_CMAKE_DRACO_OPTIONS_CMAKE_
@ -18,14 +32,19 @@ macro(draco_option)
cmake_parse_arguments(option "${optional_args}" "${single_value_args}" cmake_parse_arguments(option "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN}) "${multi_value_args}" ${ARGN})
if(NOT (option_NAME AND option_HELPSTRING AND DEFINED option_VALUE)) if(NOT
(option_NAME
AND option_HELPSTRING
AND DEFINED option_VALUE))
message(FATAL_ERROR "draco_option: NAME HELPSTRING and VALUE required.") message(FATAL_ERROR "draco_option: NAME HELPSTRING and VALUE required.")
endif() endif()
option(${option_NAME} ${option_HELPSTRING} ${option_VALUE}) option(${option_NAME} ${option_HELPSTRING} ${option_VALUE})
if(DRACO_VERBOSE GREATER 2) if(DRACO_VERBOSE GREATER 2)
message("--------- draco_option ---------\n" "option_NAME=${option_NAME}\n" message(
"--------- draco_option ---------\n"
"option_NAME=${option_NAME}\n"
"option_HELPSTRING=${option_HELPSTRING}\n" "option_HELPSTRING=${option_HELPSTRING}\n"
"option_VALUE=${option_VALUE}\n" "option_VALUE=${option_VALUE}\n"
"------------------------------------------\n") "------------------------------------------\n")
@ -44,33 +63,74 @@ endmacro()
# Set default options. # Set default options.
macro(draco_set_default_options) macro(draco_set_default_options)
draco_option(NAME DRACO_FAST HELPSTRING "Try to build faster libs." VALUE OFF) draco_option(
draco_option(NAME DRACO_JS_GLUE HELPSTRING NAME DRACO_FAST
"Enable JS Glue and JS targets when using Emscripten." VALUE ON) HELPSTRING "Try to build faster libs."
draco_option(NAME DRACO_IE_COMPATIBLE HELPSTRING VALUE OFF)
"Enable support for older IE builds when using Emscripten." VALUE draco_option(
OFF) NAME DRACO_JS_GLUE
draco_option(NAME DRACO_MESH_COMPRESSION HELPSTRING "Enable mesh compression." HELPSTRING "Enable JS Glue and JS targets when using Emscripten."
VALUE ON) VALUE ON)
draco_option(NAME DRACO_POINT_CLOUD_COMPRESSION HELPSTRING draco_option(
"Enable point cloud compression." VALUE ON) NAME DRACO_IE_COMPATIBLE
draco_option(NAME DRACO_PREDICTIVE_EDGEBREAKER HELPSTRING HELPSTRING "Enable support for older IE builds when using Emscripten."
"Enable predictive edgebreaker." VALUE ON) VALUE OFF)
draco_option(NAME DRACO_STANDARD_EDGEBREAKER HELPSTRING draco_option(
"Enable stand edgebreaker." VALUE ON) NAME DRACO_MESH_COMPRESSION
draco_option(NAME DRACO_BACKWARDS_COMPATIBILITY HELPSTRING HELPSTRING "Enable mesh compression."
"Enable backwards compatibility." VALUE ON) VALUE ON)
draco_option(NAME DRACO_DECODER_ATTRIBUTE_DEDUPLICATION HELPSTRING draco_option(
"Enable attribute deduping." VALUE OFF) NAME DRACO_POINT_CLOUD_COMPRESSION
draco_option(NAME DRACO_TESTS HELPSTRING "Enables tests." VALUE OFF) HELPSTRING "Enable point cloud compression."
draco_option(NAME DRACO_WASM HELPSTRING "Enables WASM support." VALUE OFF) VALUE ON)
draco_option(NAME DRACO_UNITY_PLUGIN HELPSTRING draco_option(
"Build plugin library for Unity." VALUE OFF) NAME DRACO_PREDICTIVE_EDGEBREAKER
draco_option(NAME DRACO_ANIMATION_ENCODING HELPSTRING "Enable animation." 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_BITSTREAM
HELPSTRING "Draco GLTF extension bitstream specified features only."
VALUE OFF)
draco_option(
NAME DRACO_MAYA_PLUGIN
HELPSTRING "Build plugin library for Maya."
VALUE OFF)
draco_option(
NAME DRACO_TRANSCODER_SUPPORTED
HELPSTRING "Enable the Draco transcoder."
VALUE OFF)
draco_option(
NAME DRACO_DEBUG_COMPILER_WARNINGS
HELPSTRING "Turn on more warnings."
VALUE OFF) 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() draco_check_deprecated_options()
endmacro() endmacro()
@ -117,14 +177,16 @@ macro(draco_check_deprecated_options)
DRACO_MAYA_PLUGIN) DRACO_MAYA_PLUGIN)
draco_handle_deprecated_option(OLDNAME BUILD_USD_PLUGIN NEWNAME draco_handle_deprecated_option(OLDNAME BUILD_USD_PLUGIN NEWNAME
BUILD_SHARED_LIBS) BUILD_SHARED_LIBS)
draco_handle_deprecated_option(OLDNAME DRACO_GLTF NEWNAME
DRACO_GLTF_BITSTREAM)
endmacro() endmacro()
# Macro for setting Draco features based on user configuration. Features enabled # Macro for setting Draco features based on user configuration. Features enabled
# by this macro are Draco global. # by this macro are Draco global.
macro(draco_set_optional_features) macro(draco_set_optional_features)
if(DRACO_GLTF) if(DRACO_GLTF_BITSTREAM)
# Override settings when building for GLTF. # Enable only the features included in the Draco GLTF bitstream spec.
draco_enable_feature(FEATURE "DRACO_MESH_COMPRESSION_SUPPORTED") draco_enable_feature(FEATURE "DRACO_MESH_COMPRESSION_SUPPORTED")
draco_enable_feature(FEATURE "DRACO_NORMAL_ENCODING_SUPPORTED") draco_enable_feature(FEATURE "DRACO_NORMAL_ENCODING_SUPPORTED")
draco_enable_feature(FEATURE "DRACO_STANDARD_EDGEBREAKER_SUPPORTED") draco_enable_feature(FEATURE "DRACO_STANDARD_EDGEBREAKER_SUPPORTED")
@ -170,6 +232,11 @@ macro(draco_set_optional_features)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif() endif()
if(DRACO_TRANSCODER_SUPPORTED)
draco_enable_feature(FEATURE "DRACO_TRANSCODER_SUPPORTED")
endif()
endmacro() endmacro()
# Macro that handles tracking of Draco preprocessor symbols for the purpose of # Macro that handles tracking of Draco preprocessor symbols for the purpose of
@ -221,8 +288,56 @@ function(draco_generate_features_h)
file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n") file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n")
endforeach() endforeach()
if(MSVC)
if(NOT DRACO_DEBUG_COMPILER_WARNINGS)
file(APPEND "${draco_features_file_name}.new" file(APPEND "${draco_features_file_name}.new"
"\n#endif // DRACO_FEATURES_H_") "// Enable DRACO_DEBUG_COMPILER_WARNINGS at CMake generation \n"
"// time to remove these pragmas.\n")
# warning C4018: '<operator>': signed/unsigned mismatch.
file(APPEND "${draco_features_file_name}.new"
"#pragma warning(disable:4018)\n")
# warning C4146: unary minus operator applied to unsigned type, result
# still unsigned
file(APPEND "${draco_features_file_name}.new"
"#pragma warning(disable:4146)\n")
# warning C4244: 'return': conversion from '<type>' to '<type>', possible
# loss of data.
file(APPEND "${draco_features_file_name}.new"
"#pragma warning(disable:4244)\n")
# warning C4267: 'initializing' conversion from '<type>' to '<type>',
# possible loss of data.
file(APPEND "${draco_features_file_name}.new"
"#pragma warning(disable:4267)\n")
# warning C4305: 'context' : truncation from 'type1' to 'type2'.
file(APPEND "${draco_features_file_name}.new"
"#pragma warning(disable:4305)\n")
# warning C4661: 'identifier' : no suitable definition provided for
# explicit template instantiation request.
file(APPEND "${draco_features_file_name}.new"
"#pragma warning(disable:4661)\n")
# warning C4800: Implicit conversion from 'type' to bool. Possible
# information loss.
# Also, in older MSVC releases:
# warning C4800: 'type' : forcing value to bool 'true' or 'false'
# (performance warning).
file(APPEND "${draco_features_file_name}.new"
"#pragma warning(disable:4800)\n")
# warning C4804: '<operator>': unsafe use of type '<type>' in operation.
file(APPEND "${draco_features_file_name}.new"
"#pragma warning(disable:4804)\n")
endif()
endif()
file(APPEND "${draco_features_file_name}.new"
"\n#endif // DRACO_FEATURES_H_\n")
# Will replace ${draco_features_file_name} only if the file content has # Will replace ${draco_features_file_name} only if the file content has
# changed. This prevents forced Draco rebuilds after CMake runs. # changed. This prevents forced Draco rebuilds after CMake runs.

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_) if(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_SANITIZER_CMAKE_ endif() # DRACO_CMAKE_DRACO_SANITIZER_CMAKE_
@ -5,7 +19,9 @@ set(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_ 1)
# Handles the details of enabling sanitizers. # Handles the details of enabling sanitizers.
macro(draco_configure_sanitizer) macro(draco_configure_sanitizer)
if(DRACO_SANITIZE AND NOT EMSCRIPTEN AND NOT MSVC) if(DRACO_SANITIZE
AND NOT EMSCRIPTEN
AND NOT MSVC)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(DRACO_SANITIZE MATCHES "cfi") if(DRACO_SANITIZE MATCHES "cfi")
list(APPEND SAN_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") list(APPEND SAN_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi")
@ -13,8 +29,8 @@ macro(draco_configure_sanitizer)
"-fuse-ld=gold") "-fuse-ld=gold")
endif() endif()
if(${CMAKE_SIZEOF_VOID_P} EQUAL 4 if(${CMAKE_SIZEOF_VOID_P} EQUAL 4 AND DRACO_SANITIZE MATCHES
AND DRACO_SANITIZE MATCHES "integer|undefined") "integer|undefined")
list(APPEND SAN_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") list(APPEND SAN_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s")
endif() endif()
endif() endif()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_TARGETS_CMAKE_) if(DRACO_CMAKE_DRACO_TARGETS_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_TARGETS_CMAKE_ endif() # DRACO_CMAKE_DRACO_TARGETS_CMAKE_
@ -51,14 +65,21 @@ macro(draco_add_executable)
unset(exe_LIB_DEPS) unset(exe_LIB_DEPS)
set(optional_args TEST) set(optional_args TEST)
set(single_value_args NAME OUTPUT_NAME) set(single_value_args NAME OUTPUT_NAME)
set(multi_value_args SOURCES DEFINES INCLUDES COMPILE_FLAGS LINK_FLAGS set(multi_value_args
OBJLIB_DEPS LIB_DEPS) SOURCES
DEFINES
INCLUDES
COMPILE_FLAGS
LINK_FLAGS
OBJLIB_DEPS
LIB_DEPS)
cmake_parse_arguments(exe "${optional_args}" "${single_value_args}" cmake_parse_arguments(exe "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN}) "${multi_value_args}" ${ARGN})
if(DRACO_VERBOSE GREATER 1) if(DRACO_VERBOSE GREATER 1)
message("--------- draco_add_executable ---------\n" message(
"--------- draco_add_executable ---------\n"
"exe_TEST=${exe_TEST}\n" "exe_TEST=${exe_TEST}\n"
"exe_TEST_DEFINES_MAIN=${exe_TEST_DEFINES_MAIN}\n" "exe_TEST_DEFINES_MAIN=${exe_TEST_DEFINES_MAIN}\n"
"exe_NAME=${exe_NAME}\n" "exe_NAME=${exe_NAME}\n"
@ -87,7 +108,12 @@ macro(draco_add_executable)
endif() endif()
add_executable(${exe_NAME} ${exe_SOURCES}) add_executable(${exe_NAME} ${exe_SOURCES})
target_compile_features(${exe_NAME} PUBLIC cxx_std_11)
if(NOT EMSCRIPTEN)
set_target_properties(${exe_NAME} PROPERTIES VERSION ${DRACO_VERSION}) set_target_properties(${exe_NAME} PROPERTIES VERSION ${DRACO_VERSION})
endif()
if(exe_OUTPUT_NAME) if(exe_OUTPUT_NAME)
set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME}) set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME})
@ -104,8 +130,8 @@ macro(draco_add_executable)
endif() endif()
if(exe_COMPILE_FLAGS OR DRACO_CXX_FLAGS) if(exe_COMPILE_FLAGS OR DRACO_CXX_FLAGS)
target_compile_options(${exe_NAME} target_compile_options(${exe_NAME} PRIVATE ${exe_COMPILE_FLAGS}
PRIVATE ${exe_COMPILE_FLAGS} ${DRACO_CXX_FLAGS}) ${DRACO_CXX_FLAGS})
endif() endif()
if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS) if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS)
@ -113,8 +139,8 @@ macro(draco_add_executable)
list(APPEND exe_LINK_FLAGS "${DRACO_EXE_LINKER_FLAGS}") list(APPEND exe_LINK_FLAGS "${DRACO_EXE_LINKER_FLAGS}")
# LINK_FLAGS is managed as a string. # LINK_FLAGS is managed as a string.
draco_set_and_stringify(SOURCE "${exe_LINK_FLAGS}" DEST exe_LINK_FLAGS) draco_set_and_stringify(SOURCE "${exe_LINK_FLAGS}" DEST exe_LINK_FLAGS)
set_target_properties(${exe_NAME} set_target_properties(${exe_NAME} PROPERTIES LINK_FLAGS
PROPERTIES LINK_FLAGS "${exe_LINK_FLAGS}") "${exe_LINK_FLAGS}")
else() else()
target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS} target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS}
${DRACO_EXE_LINKER_FLAGS}) ${DRACO_EXE_LINKER_FLAGS})
@ -136,12 +162,7 @@ macro(draco_add_executable)
endif() endif()
if(exe_LIB_DEPS) if(exe_LIB_DEPS)
unset(exe_static) if(CMAKE_CXX_COMPILER_ID MATCHES "^Clang|^GNU")
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 # Third party dependencies can introduce dependencies on system and test
# libraries. Since the target created here is an executable, and CMake # libraries. Since the target created here is an executable, and CMake
# does not provide a method of controlling order of link dependencies, # does not provide a method of controlling order of link dependencies,
@ -149,6 +170,10 @@ macro(draco_add_executable)
# ensure that dependencies of third party targets can be resolved when # ensure that dependencies of third party targets can be resolved when
# those dependencies happen to be resolved by dependencies of the current # those dependencies happen to be resolved by dependencies of the current
# target. # target.
# TODO(tomfinegan): For portability use LINK_GROUP with RESCAN instead of
# directly (ab)using compiler/linker specific flags once CMake v3.24 is in
# wider use. See:
# https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#genex:LINK_GROUP
list(INSERT exe_LIB_DEPS 0 -Wl,--start-group) list(INSERT exe_LIB_DEPS 0 -Wl,--start-group)
list(APPEND exe_LIB_DEPS -Wl,--end-group) list(APPEND exe_LIB_DEPS -Wl,--end-group)
endif() endif()
@ -209,14 +234,23 @@ macro(draco_add_library)
unset(lib_TARGET_PROPERTIES) unset(lib_TARGET_PROPERTIES)
set(optional_args TEST) set(optional_args TEST)
set(single_value_args NAME OUTPUT_NAME TYPE) set(single_value_args NAME OUTPUT_NAME TYPE)
set(multi_value_args SOURCES DEFINES INCLUDES COMPILE_FLAGS LINK_FLAGS set(multi_value_args
OBJLIB_DEPS LIB_DEPS PUBLIC_INCLUDES TARGET_PROPERTIES) SOURCES
DEFINES
INCLUDES
COMPILE_FLAGS
LINK_FLAGS
OBJLIB_DEPS
LIB_DEPS
PUBLIC_INCLUDES
TARGET_PROPERTIES)
cmake_parse_arguments(lib "${optional_args}" "${single_value_args}" cmake_parse_arguments(lib "${optional_args}" "${single_value_args}"
"${multi_value_args}" ${ARGN}) "${multi_value_args}" ${ARGN})
if(DRACO_VERBOSE GREATER 1) if(DRACO_VERBOSE GREATER 1)
message("--------- draco_add_library ---------\n" message(
"--------- draco_add_library ---------\n"
"lib_TEST=${lib_TEST}\n" "lib_TEST=${lib_TEST}\n"
"lib_NAME=${lib_NAME}\n" "lib_NAME=${lib_NAME}\n"
"lib_OUTPUT_NAME=${lib_OUTPUT_NAME}\n" "lib_OUTPUT_NAME=${lib_OUTPUT_NAME}\n"
@ -256,14 +290,24 @@ macro(draco_add_library)
endif() endif()
add_library(${lib_NAME} ${lib_TYPE} ${lib_SOURCES}) add_library(${lib_NAME} ${lib_TYPE} ${lib_SOURCES})
target_compile_features(${lib_NAME} PUBLIC cxx_std_11)
target_include_directories(${lib_NAME} PUBLIC $<INSTALL_INTERFACE:include>)
if(BUILD_SHARED_LIBS)
# Enable PIC for all targets in shared configurations.
set_target_properties(${lib_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
if(lib_SOURCES) if(lib_SOURCES)
draco_process_intrinsics_sources(TARGET ${lib_NAME} SOURCES ${lib_SOURCES}) draco_process_intrinsics_sources(TARGET ${lib_NAME} SOURCES ${lib_SOURCES})
endif() endif()
if(lib_OUTPUT_NAME) if(lib_OUTPUT_NAME)
if(NOT (BUILD_SHARED_LIBS AND MSVC)) if(NOT (BUILD_SHARED_LIBS AND MSVC))
set_target_properties(${lib_NAME} set_target_properties(${lib_NAME} PROPERTIES OUTPUT_NAME
PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME}) ${lib_OUTPUT_NAME})
endif() endif()
endif() endif()
@ -280,8 +324,8 @@ macro(draco_add_library)
endif() endif()
if(lib_COMPILE_FLAGS OR DRACO_CXX_FLAGS) if(lib_COMPILE_FLAGS OR DRACO_CXX_FLAGS)
target_compile_options(${lib_NAME} target_compile_options(${lib_NAME} PRIVATE ${lib_COMPILE_FLAGS}
PRIVATE ${lib_COMPILE_FLAGS} ${DRACO_CXX_FLAGS}) ${DRACO_CXX_FLAGS})
endif() endif()
if(lib_LINK_FLAGS) if(lib_LINK_FLAGS)
@ -320,11 +364,12 @@ macro(draco_add_library)
set_target_properties(${lib_NAME} PROPERTIES PREFIX "") set_target_properties(${lib_NAME} PROPERTIES PREFIX "")
endif() endif()
if(NOT EMSCRIPTEN)
# VERSION and SOVERSION as necessary # VERSION and SOVERSION as necessary
if(NOT lib_TYPE STREQUAL STATIC AND NOT lib_TYPE STREQUAL MODULE) if((lib_TYPE STREQUAL BUNDLE OR lib_TYPE STREQUAL SHARED) AND NOT MSVC)
set_target_properties(${lib_NAME} PROPERTIES VERSION ${DRACO_VERSION}) set_target_properties(
if(NOT MSVC) ${lib_NAME} PROPERTIES VERSION ${DRACO_SOVERSION}
set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) SOVERSION ${DRACO_SOVERSION_MAJOR})
endif() endif()
endif() endif()

View File

@ -1,3 +1,17 @@
// Copyright 2021 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_TESTING_DRACO_TEST_CONFIG_H_ #ifndef DRACO_TESTING_DRACO_TEST_CONFIG_H_
#define DRACO_TESTING_DRACO_TEST_CONFIG_H_ #define DRACO_TESTING_DRACO_TEST_CONFIG_H_
@ -9,5 +23,6 @@
#define DRACO_TEST_DATA_DIR "${DRACO_TEST_DATA_DIR}" #define DRACO_TEST_DATA_DIR "${DRACO_TEST_DATA_DIR}"
#define DRACO_TEST_TEMP_DIR "${DRACO_TEST_TEMP_DIR}" #define DRACO_TEST_TEMP_DIR "${DRACO_TEST_TEMP_DIR}"
#define DRACO_TEST_ROOT_DIR "${DRACO_TEST_ROOT_DIR}"
#endif // DRACO_TESTING_DRACO_TEST_CONFIG_H_ #endif // DRACO_TESTING_DRACO_TEST_CONFIG_H_

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_TESTS_CMAKE) if(DRACO_CMAKE_DRACO_TESTS_CMAKE)
return() return()
endif() endif()
@ -10,6 +24,13 @@ set(draco_factory_test_sources
"${draco_src_root}/io/file_reader_factory_test.cc" "${draco_src_root}/io/file_reader_factory_test.cc"
"${draco_src_root}/io/file_writer_factory_test.cc") "${draco_src_root}/io/file_writer_factory_test.cc")
list(
APPEND draco_test_common_sources
"${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/status.cc")
list( list(
APPEND APPEND
draco_test_sources draco_test_sources
@ -30,22 +51,23 @@ list(
"${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoding_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}/compression/point_cloud/point_cloud_sequential_encoding_test.cc"
"${draco_src_root}/core/buffer_bit_coding_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/math_utils_test.cc"
"${draco_src_root}/core/quantization_utils_test.cc" "${draco_src_root}/core/quantization_utils_test.cc"
"${draco_src_root}/core/status_test.cc" "${draco_src_root}/core/status_test.cc"
"${draco_src_root}/core/vector_d_test.cc" "${draco_src_root}/core/vector_d_test.cc"
"${draco_src_root}/io/file_reader_test_common.h" "${draco_src_root}/io/file_reader_test_common.h"
"${draco_src_root}/io/file_utils_test.cc" "${draco_src_root}/io/file_utils_test.cc"
"${draco_src_root}/io/file_writer_utils_test.cc"
"${draco_src_root}/io/stdio_file_reader_test.cc" "${draco_src_root}/io/stdio_file_reader_test.cc"
"${draco_src_root}/io/stdio_file_writer_test.cc" "${draco_src_root}/io/stdio_file_writer_test.cc"
"${draco_src_root}/io/obj_decoder_test.cc" "${draco_src_root}/io/obj_decoder_test.cc"
"${draco_src_root}/io/obj_encoder_test.cc" "${draco_src_root}/io/obj_encoder_test.cc"
"${draco_src_root}/io/ply_decoder_test.cc" "${draco_src_root}/io/ply_decoder_test.cc"
"${draco_src_root}/io/ply_reader_test.cc" "${draco_src_root}/io/ply_reader_test.cc"
"${draco_src_root}/io/stl_decoder_test.cc"
"${draco_src_root}/io/stl_encoder_test.cc"
"${draco_src_root}/io/point_cloud_io_test.cc" "${draco_src_root}/io/point_cloud_io_test.cc"
"${draco_src_root}/mesh/corner_table_test.cc"
"${draco_src_root}/mesh/mesh_are_equivalent_test.cc" "${draco_src_root}/mesh/mesh_are_equivalent_test.cc"
"${draco_src_root}/mesh/mesh_cleanup_test.cc" "${draco_src_root}/mesh/mesh_cleanup_test.cc"
"${draco_src_root}/mesh/triangle_soup_mesh_builder_test.cc" "${draco_src_root}/mesh/triangle_soup_mesh_builder_test.cc"
@ -54,47 +76,71 @@ list(
"${draco_src_root}/point_cloud/point_cloud_builder_test.cc" "${draco_src_root}/point_cloud/point_cloud_builder_test.cc"
"${draco_src_root}/point_cloud/point_cloud_test.cc") "${draco_src_root}/point_cloud/point_cloud_test.cc")
list(APPEND draco_gtest_all if(DRACO_TRANSCODER_SUPPORTED)
"${draco_root}/../googletest/googletest/src/gtest-all.cc") list(
list(APPEND draco_gtest_main APPEND draco_test_sources
"${draco_root}/../googletest/googletest/src/gtest_main.cc") "${draco_src_root}/animation/animation_test.cc"
"${draco_src_root}/io/gltf_decoder_test.cc"
"${draco_src_root}/io/gltf_encoder_test.cc"
"${draco_src_root}/io/gltf_utils_test.cc"
"${draco_src_root}/io/gltf_test_helper.cc"
"${draco_src_root}/io/gltf_test_helper.h"
"${draco_src_root}/io/scene_io_test.cc"
"${draco_src_root}/io/texture_io_test.cc"
"${draco_src_root}/material/material_library_test.cc"
"${draco_src_root}/material/material_test.cc"
"${draco_src_root}/metadata/property_table_test.cc"
"${draco_src_root}/metadata/structural_metadata_test.cc"
"${draco_src_root}/scene/instance_array_test.cc"
"${draco_src_root}/scene/light_test.cc"
"${draco_src_root}/scene/mesh_group_test.cc"
"${draco_src_root}/scene/scene_test.cc"
"${draco_src_root}/scene/scene_are_equivalent_test.cc"
"${draco_src_root}/scene/scene_utils_test.cc"
"${draco_src_root}/scene/trs_matrix_test.cc"
"${draco_src_root}/texture/texture_library_test.cc"
"${draco_src_root}/texture/texture_map_test.cc"
"${draco_src_root}/texture/texture_transform_test.cc")
endif()
macro(draco_setup_test_targets) macro(draco_setup_test_targets)
if(DRACO_TESTS) if(DRACO_TESTS)
draco_setup_googletest()
if(NOT (EXISTS ${draco_gtest_all} AND EXISTS ${draco_gtest_main})) if(NOT (EXISTS ${draco_gtest_all} AND EXISTS ${draco_gtest_main}))
message(FATAL "googletest must be a sibling directory of ${draco_root}.") message(FATAL_ERROR "googletest missing, run git submodule update --init")
endif() endif()
list(APPEND draco_test_defines GTEST_HAS_PTHREAD=0) list(APPEND draco_test_defines GTEST_HAS_PTHREAD=0)
draco_add_library(TEST draco_add_library(
NAME TEST
draco_gtest NAME draco_test_common
TYPE TYPE STATIC
STATIC SOURCES ${draco_test_common_sources}
SOURCES DEFINES ${draco_defines} ${draco_test_defines}
${draco_gtest_all} INCLUDES ${draco_test_include_paths})
DEFINES
${draco_defines}
${draco_test_defines}
INCLUDES
${draco_test_include_paths})
draco_add_library(TEST draco_add_library(
NAME TEST
draco_gtest_main NAME draco_gtest
TYPE TYPE STATIC
STATIC SOURCES ${draco_gtest_all}
SOURCES DEFINES ${draco_defines} ${draco_test_defines}
${draco_gtest_main} INCLUDES ${draco_test_include_paths})
DEFINES
${draco_defines} draco_add_library(
${draco_test_defines} TEST
INCLUDES NAME draco_gtest_main
${draco_test_include_paths}) 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_DATA_DIR "${draco_root}/testdata")
set(DRACO_TEST_TEMP_DIR "${draco_build}/draco_test_temp") set(DRACO_TEST_TEMP_DIR "${draco_build}/draco_test_temp")
set(DRACO_TEST_ROOT_DIR "${draco_root}")
file(MAKE_DIRECTORY "${DRACO_TEST_TEMP_DIR}") file(MAKE_DIRECTORY "${DRACO_TEST_TEMP_DIR}")
# Sets DRACO_TEST_DATA_DIR and DRACO_TEST_TEMP_DIR. # Sets DRACO_TEST_DATA_DIR and DRACO_TEST_TEMP_DIR.
@ -102,32 +148,24 @@ macro(draco_setup_test_targets)
"${draco_build}/testing/draco_test_config.h") "${draco_build}/testing/draco_test_config.h")
# Create the test targets. # Create the test targets.
draco_add_executable(NAME draco_add_executable(
draco_tests TEST
SOURCES NAME draco_tests
${draco_test_sources} SOURCES ${draco_test_sources}
DEFINES DEFINES ${draco_defines} ${draco_test_defines}
${draco_defines} INCLUDES ${draco_test_include_paths}
${draco_test_defines} LIB_DEPS ${draco_dependency} draco_gtest draco_gtest_main
INCLUDES draco_test_common)
${draco_test_include_paths}
LIB_DEPS draco_add_executable(
draco_static TEST
draco_gtest NAME draco_factory_tests
draco_gtest_main) SOURCES ${draco_factory_test_sources}
DEFINES ${draco_defines} ${draco_test_defines}
INCLUDES ${draco_test_include_paths}
LIB_DEPS ${draco_dependency} draco_gtest draco_gtest_main
draco_test_common)
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() endif()
endmacro() endmacro()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_DRACO_VARIABLES_CMAKE_) if(DRACO_CMAKE_DRACO_VARIABLES_CMAKE_)
return() return()
endif() # DRACO_CMAKE_DRACO_VARIABLES_CMAKE_ endif() # DRACO_CMAKE_DRACO_VARIABLES_CMAKE_
@ -14,8 +28,7 @@ macro(draco_variable_must_be_directory variable_name)
if("${${variable_name}}" STREQUAL "") if("${${variable_name}}" STREQUAL "")
message( message(
FATAL_ERROR FATAL_ERROR "Empty variable ${variable_name} is required to build draco.")
"Empty variable ${variable_name} is required to build draco.")
endif() endif()
if(NOT IS_DIRECTORY "${${variable_name}}") if(NOT IS_DIRECTORY "${${variable_name}}")
@ -44,7 +57,9 @@ macro(draco_dump_cmake_flag_variables)
list(APPEND flag_variables "CMAKE_CXX_FLAGS_INIT" "CMAKE_CXX_FLAGS" list(APPEND flag_variables "CMAKE_CXX_FLAGS_INIT" "CMAKE_CXX_FLAGS"
"CMAKE_EXE_LINKER_FLAGS_INIT" "CMAKE_EXE_LINKER_FLAGS") "CMAKE_EXE_LINKER_FLAGS_INIT" "CMAKE_EXE_LINKER_FLAGS")
if(CMAKE_BUILD_TYPE) if(CMAKE_BUILD_TYPE)
list(APPEND flag_variables "CMAKE_BUILD_TYPE" list(
APPEND flag_variables
"CMAKE_BUILD_TYPE"
"CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}_INIT" "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}_INIT"
"CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}"
"CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE}_INIT" "CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE}_INIT"

View File

@ -1,19 +0,0 @@
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

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_)
return() return()
endif() # DRACO_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_ endif() # DRACO_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ANDROID_NDK_COMMON_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ANDROID_NDK_COMMON_CMAKE_)
return() return()
endif() endif()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ANDROID_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ANDROID_CMAKE_)
return() return()
endif() # DRACO_CMAKE_TOOLCHAINS_ANDROID_CMAKE_ endif() # DRACO_CMAKE_TOOLCHAINS_ANDROID_CMAKE_
@ -16,9 +30,9 @@ if(NOT ANDROID_ABI)
set(ANDROID_ABI arm64-v8a) set(ANDROID_ABI arm64-v8a)
endif() endif()
# Force arm mode for 32-bit targets (instead of the default thumb) to improve # Force arm mode for 32-bit arm targets (instead of the default thumb) to
# performance. # improve performance.
if(NOT ANDROID_ARM_MODE) if(ANDROID_ABI MATCHES "^armeabi" AND NOT ANDROID_ARM_MODE)
set(ANDROID_ARM_MODE arm) set(ANDROID_ARM_MODE arm)
endif() endif()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARM_IOS_COMMON_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARM_IOS_COMMON_CMAKE_)
return() return()
endif() endif()
@ -13,5 +27,3 @@ set(CMAKE_C_COMPILER clang)
set(CMAKE_C_COMPILER_ARG1 "-arch ${CMAKE_SYSTEM_PROCESSOR}") set(CMAKE_C_COMPILER_ARG1 "-arch ${CMAKE_SYSTEM_PROCESSOR}")
set(CMAKE_CXX_COMPILER clang++) set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_COMPILER_ARG1 "-arch ${CMAKE_SYSTEM_PROCESSOR}") set(CMAKE_CXX_COMPILER_ARG1 "-arch ${CMAKE_SYSTEM_PROCESSOR}")
# TODO(tomfinegan): Handle bit code embedding.

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_)
return() return()
endif() # DRACO_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_ endif() # DRACO_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARM64_ANDROID_NDK_LIBCPP_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARM64_ANDROID_NDK_LIBCPP_CMAKE_)
return() return()
endif() endif()

View File

@ -1,10 +1,23 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_)
return() return()
endif() endif()
set(DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_ 1) set(DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_ 1)
if(XCODE) if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.") message(FATAL_ERROR "This toolchain does not support Xcode.")
endif() endif()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARM64_LINUX_GCC_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARM64_LINUX_GCC_CMAKE_)
return() return()
endif() endif()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARMV7_ANDROID_NDK_LIBCPP_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARMV7_ANDROID_NDK_LIBCPP_CMAKE_)
return() return()
endif() endif()

View File

@ -1,10 +1,23 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_)
return() return()
endif() endif()
set(DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_ 1) set(DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_ 1)
if(XCODE) if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.") message(FATAL_ERROR "This toolchain does not support Xcode.")
endif() endif()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARMV7_LINUX_GCC_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARMV7_LINUX_GCC_CMAKE_)
return() return()
endif() endif()

View File

@ -1,10 +1,23 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_)
return() return()
endif() endif()
set(DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_ 1) set(DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_ 1)
if(XCODE) if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.") message(FATAL_ERROR "This toolchain does not support Xcode.")
endif() endif()

View File

@ -1,10 +1,23 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_i386_IOS_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_i386_IOS_CMAKE_)
return() return()
endif() endif()
set(DRACO_CMAKE_TOOLCHAINS_i386_IOS_CMAKE_ 1) set(DRACO_CMAKE_TOOLCHAINS_i386_IOS_CMAKE_ 1)
if(XCODE) if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.") message(FATAL_ERROR "This toolchain does not support Xcode.")
endif() endif()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_X86_ANDROID_NDK_LIBCPP_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_X86_ANDROID_NDK_LIBCPP_CMAKE_)
return() return()
endif() endif()

View File

@ -1,3 +1,17 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_X86_64_ANDROID_NDK_LIBCPP_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_X86_64_ANDROID_NDK_LIBCPP_CMAKE_)
return() return()
endif() endif()

View File

@ -1,10 +1,23 @@
# Copyright 2021 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.
if(DRACO_CMAKE_TOOLCHAINS_X86_64_IOS_CMAKE_) if(DRACO_CMAKE_TOOLCHAINS_X86_64_IOS_CMAKE_)
return() return()
endif() endif()
set(DRACO_CMAKE_TOOLCHAINS_X86_64_IOS_CMAKE_ 1) set(DRACO_CMAKE_TOOLCHAINS_X86_64_IOS_CMAKE_ 1)
if(XCODE) if(XCODE)
# TODO(tomfinegan): Handle arm builds in Xcode.
message(FATAL_ERROR "This toolchain does not support Xcode.") message(FATAL_ERROR "This toolchain does not support Xcode.")
endif() endif()

View File

@ -1,79 +0,0 @@
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,47 @@
// Copyright 2019 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/animation.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
namespace draco {
void Animation::Copy(const Animation &src) {
name_ = src.name_;
channels_.clear();
for (int i = 0; i < src.NumChannels(); ++i) {
std::unique_ptr<AnimationChannel> new_channel(new AnimationChannel());
new_channel->Copy(*src.GetChannel(i));
channels_.push_back(std::move(new_channel));
}
samplers_.clear();
for (int i = 0; i < src.NumSamplers(); ++i) {
std::unique_ptr<AnimationSampler> new_sampler(new AnimationSampler());
new_sampler->Copy(*src.GetSampler(i));
samplers_.push_back(std::move(new_sampler));
}
node_animation_data_.clear();
for (int i = 0; i < src.NumNodeAnimationData(); ++i) {
std::unique_ptr<NodeAnimationData> new_data(new NodeAnimationData());
new_data->Copy(*src.GetNodeAnimationData(i));
node_animation_data_.push_back(std::move(new_data));
}
}
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -0,0 +1,149 @@
// Copyright 2019 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_ANIMATION_H_
#define DRACO_ANIMATION_ANIMATION_H_
#include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <memory>
#include <vector>
#include "draco/animation/node_animation_data.h"
#include "draco/core/status.h"
namespace draco {
// Struct to hold information about an animation's sampler.
struct AnimationSampler {
enum class SamplerInterpolation { LINEAR, STEP, CUBICSPLINE };
static std::string InterpolationToString(SamplerInterpolation value) {
switch (value) {
case SamplerInterpolation::STEP:
return "STEP";
case SamplerInterpolation::CUBICSPLINE:
return "CUBICSPLINE";
default:
return "LINEAR";
}
}
AnimationSampler()
: input_index(-1),
interpolation_type(SamplerInterpolation::LINEAR),
output_index(-1) {}
void Copy(const AnimationSampler &src) {
input_index = src.input_index;
interpolation_type = src.interpolation_type;
output_index = src.output_index;
}
int input_index;
SamplerInterpolation interpolation_type;
int output_index;
};
// Struct to hold information about an animation's channel.
struct AnimationChannel {
enum class ChannelTransformation { TRANSLATION, ROTATION, SCALE, WEIGHTS };
static std::string TransformationToString(ChannelTransformation value) {
switch (value) {
case ChannelTransformation::ROTATION:
return "rotation";
case ChannelTransformation::SCALE:
return "scale";
case ChannelTransformation::WEIGHTS:
return "weights";
default:
return "translation";
}
}
AnimationChannel()
: target_index(-1),
transformation_type(ChannelTransformation::TRANSLATION),
sampler_index(-1) {}
void Copy(const AnimationChannel &src) {
target_index = src.target_index;
transformation_type = src.transformation_type;
sampler_index = src.sampler_index;
}
int target_index;
ChannelTransformation transformation_type;
int sampler_index;
};
// This class is used to hold data and information of glTF animations.
class Animation {
public:
Animation() {}
void Copy(const Animation &src);
const std::string &GetName() const { return name_; }
void SetName(const std::string &name) { name_ = name; }
// Returns the number of channels in an animation.
int NumChannels() const { return channels_.size(); }
// Returns the number of samplers in an animation.
int NumSamplers() const { return samplers_.size(); }
// Returns the number of accessors in an animation.
int NumNodeAnimationData() const { return node_animation_data_.size(); }
// Returns a channel in the animation.
AnimationChannel *GetChannel(int index) { return channels_[index].get(); }
const AnimationChannel *GetChannel(int index) const {
return channels_[index].get();
}
// Returns a sampler in the animation.
AnimationSampler *GetSampler(int index) { return samplers_[index].get(); }
const AnimationSampler *GetSampler(int index) const {
return samplers_[index].get();
}
// Returns an accessor in the animation.
NodeAnimationData *GetNodeAnimationData(int index) {
return node_animation_data_[index].get();
}
const NodeAnimationData *GetNodeAnimationData(int index) const {
return node_animation_data_[index].get();
}
void AddNodeAnimationData(
std::unique_ptr<NodeAnimationData> node_animation_data) {
node_animation_data_.push_back(std::move(node_animation_data));
}
void AddSampler(std::unique_ptr<AnimationSampler> sampler) {
samplers_.push_back(std::move(sampler));
}
void AddChannel(std::unique_ptr<AnimationChannel> channel) {
channels_.push_back(std::move(channel));
}
private:
std::string name_;
std::vector<std::unique_ptr<AnimationSampler>> samplers_;
std::vector<std::unique_ptr<AnimationChannel>> channels_;
std::vector<std::unique_ptr<NodeAnimationData>> node_animation_data_;
};
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED
#endif // DRACO_ANIMATION_ANIMATION_H_

View File

@ -0,0 +1,71 @@
// Copyright 2021 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/animation.h"
#include "draco/core/draco_test_base.h"
#include "draco/draco_features.h"
namespace {
#ifdef DRACO_TRANSCODER_SUPPORTED
TEST(AnimationTest, TestCopy) {
// Test copying of animation data.
draco::Animation src_anim;
ASSERT_TRUE(src_anim.GetName().empty());
src_anim.SetName("Walking");
ASSERT_EQ(src_anim.GetName(), "Walking");
std::unique_ptr<draco::AnimationSampler> src_sampler_0(
new draco::AnimationSampler());
src_sampler_0->interpolation_type =
draco::AnimationSampler::SamplerInterpolation::CUBICSPLINE;
std::unique_ptr<draco::AnimationSampler> src_sampler_1(
new draco::AnimationSampler());
src_sampler_1->Copy(*src_sampler_0);
ASSERT_EQ(src_sampler_0->interpolation_type,
src_sampler_1->interpolation_type);
src_sampler_1->interpolation_type =
draco::AnimationSampler::SamplerInterpolation::STEP;
src_anim.AddSampler(std::move(src_sampler_0));
src_anim.AddSampler(std::move(src_sampler_1));
ASSERT_EQ(src_anim.NumSamplers(), 2);
std::unique_ptr<draco::AnimationChannel> src_channel(
new draco::AnimationChannel());
src_channel->transformation_type =
draco::AnimationChannel::ChannelTransformation::WEIGHTS;
src_anim.AddChannel(std::move(src_channel));
ASSERT_EQ(src_anim.NumChannels(), 1);
draco::Animation dst_anim;
dst_anim.Copy(src_anim);
ASSERT_EQ(dst_anim.GetName(), src_anim.GetName());
ASSERT_EQ(dst_anim.NumSamplers(), 2);
ASSERT_EQ(dst_anim.NumChannels(), 1);
ASSERT_EQ(dst_anim.GetSampler(0)->interpolation_type,
src_anim.GetSampler(0)->interpolation_type);
ASSERT_EQ(dst_anim.GetSampler(1)->interpolation_type,
src_anim.GetSampler(1)->interpolation_type);
ASSERT_EQ(dst_anim.GetChannel(0)->transformation_type,
src_anim.GetChannel(0)->transformation_type);
}
#endif // DRACO_TRANSCODER_SUPPORTED
} // namespace

View File

@ -26,8 +26,9 @@ class KeyframeAnimationEncodingTest : public ::testing::Test {
bool CreateAndAddTimestamps(int32_t num_frames) { bool CreateAndAddTimestamps(int32_t num_frames) {
timestamps_.resize(num_frames); timestamps_.resize(num_frames);
for (int i = 0; i < timestamps_.size(); ++i) for (int i = 0; i < timestamps_.size(); ++i) {
timestamps_[i] = static_cast<draco::KeyframeAnimation::TimestampType>(i); timestamps_[i] = static_cast<draco::KeyframeAnimation::TimestampType>(i);
}
return keyframe_animation_.SetTimestamps(timestamps_); return keyframe_animation_.SetTimestamps(timestamps_);
} }
@ -35,8 +36,9 @@ class KeyframeAnimationEncodingTest : public ::testing::Test {
uint32_t num_components) { uint32_t num_components) {
// Create and add animation data with. // Create and add animation data with.
animation_data_.resize(num_frames * num_components); animation_data_.resize(num_frames * num_components);
for (int i = 0; i < animation_data_.size(); ++i) for (int i = 0; i < animation_data_.size(); ++i) {
animation_data_[i] = static_cast<float>(i); animation_data_[i] = static_cast<float>(i);
}
return keyframe_animation_.AddKeyframes(draco::DT_FLOAT32, num_components, return keyframe_animation_.AddKeyframes(draco::DT_FLOAT32, num_components,
animation_data_); animation_data_);
} }
@ -49,7 +51,7 @@ class KeyframeAnimationEncodingTest : public ::testing::Test {
ASSERT_EQ(animation0.num_animations(), animation1.num_animations()); ASSERT_EQ(animation0.num_animations(), animation1.num_animations());
if (quantized) { if (quantized) {
// TODO(hemmer) : Add test for stable quantization. // TODO(b/199760123) : Add test for stable quantization.
// Quantization will result in slightly different values. // Quantization will result in slightly different values.
// Skip comparing values. // Skip comparing values.
return; return;
@ -109,9 +111,8 @@ class KeyframeAnimationEncodingTest : public ::testing::Test {
} }
} }
ASSERT_TRUE( DRACO_ASSERT_OK(
encoder.EncodeKeyframeAnimation(keyframe_animation_, options, &buffer) encoder.EncodeKeyframeAnimation(keyframe_animation_, options, &buffer));
.ok());
draco::DecoderBuffer dec_decoder; draco::DecoderBuffer dec_decoder;
draco::KeyframeAnimationDecoder decoder; draco::KeyframeAnimationDecoder decoder;
@ -122,8 +123,8 @@ class KeyframeAnimationEncodingTest : public ::testing::Test {
std::unique_ptr<KeyframeAnimation> decoded_animation( std::unique_ptr<KeyframeAnimation> decoded_animation(
new KeyframeAnimation()); new KeyframeAnimation());
DecoderOptions dec_options; DecoderOptions dec_options;
ASSERT_TRUE( DRACO_ASSERT_OK(
decoder.Decode(dec_options, &dec_buffer, decoded_animation.get()).ok()); decoder.Decode(dec_options, &dec_buffer, decoded_animation.get()));
// Verify if animation before and after compression is identical. // Verify if animation before and after compression is identical.
CompareAnimationData<num_components_t>(keyframe_animation_, CompareAnimationData<num_components_t>(keyframe_animation_,

View File

@ -24,8 +24,9 @@ class KeyframeAnimationTest : public ::testing::Test {
bool CreateAndAddTimestamps(int32_t num_frames) { bool CreateAndAddTimestamps(int32_t num_frames) {
timestamps_.resize(num_frames); timestamps_.resize(num_frames);
for (int i = 0; i < timestamps_.size(); ++i) for (int i = 0; i < timestamps_.size(); ++i) {
timestamps_[i] = static_cast<draco::KeyframeAnimation::TimestampType>(i); timestamps_[i] = static_cast<draco::KeyframeAnimation::TimestampType>(i);
}
return keyframe_animation_.SetTimestamps(timestamps_); return keyframe_animation_.SetTimestamps(timestamps_);
} }
@ -33,8 +34,9 @@ class KeyframeAnimationTest : public ::testing::Test {
uint32_t num_components) { uint32_t num_components) {
// Create and add animation data with. // Create and add animation data with.
animation_data_.resize(num_frames * num_components); animation_data_.resize(num_frames * num_components);
for (int i = 0; i < animation_data_.size(); ++i) for (int i = 0; i < animation_data_.size(); ++i) {
animation_data_[i] = static_cast<float>(i); animation_data_[i] = static_cast<float>(i);
}
return keyframe_animation_.AddKeyframes(draco::DT_FLOAT32, num_components, return keyframe_animation_.AddKeyframes(draco::DT_FLOAT32, num_components,
animation_data_); animation_data_);
} }

View File

@ -0,0 +1,150 @@
// Copyright 2019 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_NODE_ANIMATION_DATA_H_
#define DRACO_ANIMATION_NODE_ANIMATION_DATA_H_
#include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include "draco/core/hash_utils.h"
#include "draco/core/status.h"
#include "draco/core/status_or.h"
namespace draco {
// This class is used to store information and data for animations that only
// affect the nodes.
// TODO(fgalligan): Think about changing the name of this class now that Skin
// is using it.
class NodeAnimationData {
public:
enum class Type { SCALAR, VEC3, VEC4, MAT4 };
NodeAnimationData() : type_(Type::SCALAR), count_(0), normalized_(false) {}
void Copy(const NodeAnimationData &src) {
type_ = src.type_;
count_ = src.count_;
normalized_ = src.normalized_;
data_ = src.data_;
}
Type type() const { return type_; }
int count() const { return count_; }
bool normalized() const { return normalized_; }
std::vector<float> *GetMutableData() { return &data_; }
const std::vector<float> *GetData() const { return &data_; }
void SetType(Type type) { type_ = type; }
void SetCount(int count) { count_ = count; }
void SetNormalized(bool normalized) { normalized_ = normalized; }
int ComponentSize() const { return sizeof(float); }
int NumComponents() const {
switch (type_) {
case Type::SCALAR:
return 1;
case Type::VEC3:
return 3;
case Type::MAT4:
return 16;
default:
return 4;
}
}
std::string TypeAsString() const {
switch (type_) {
case Type::SCALAR:
return "SCALAR";
case Type::VEC3:
return "VEC3";
case Type::MAT4:
return "MAT4";
default:
return "VEC4";
}
}
bool operator==(const NodeAnimationData &nad) const {
return type_ == nad.type_ && count_ == nad.count_ &&
normalized_ == nad.normalized_ && data_ == nad.data_;
}
private:
Type type_;
int count_;
bool normalized_;
std::vector<float> data_;
};
// Wrapper class for hashing NodeAnimationData. When using different containers,
// this class is preferable instead of copying the data in NodeAnimationData
// every time.
class NodeAnimationDataHash {
public:
NodeAnimationDataHash() = delete;
NodeAnimationDataHash &operator=(const NodeAnimationDataHash &) = delete;
NodeAnimationDataHash(NodeAnimationDataHash &&) = delete;
NodeAnimationDataHash &operator=(NodeAnimationDataHash &&) = delete;
explicit NodeAnimationDataHash(const NodeAnimationData *nad)
: node_animation_data_(nad) {
hash_ = NodeAnimationDataHash::HashNodeAnimationData(*node_animation_data_);
}
NodeAnimationDataHash(const NodeAnimationDataHash &nadh) {
node_animation_data_ = nadh.node_animation_data_;
hash_ = nadh.hash_;
}
bool operator==(const NodeAnimationDataHash &nadh) const {
return *node_animation_data_ == *nadh.node_animation_data_;
}
struct Hash {
size_t operator()(const NodeAnimationDataHash &nadh) const {
return nadh.hash_;
}
};
const NodeAnimationData *GetNodeAnimationData() {
return node_animation_data_;
}
private:
// Returns a hash of |nad|.
static size_t HashNodeAnimationData(const NodeAnimationData &nad) {
size_t hash = 79; // Magic number.
hash = HashCombine(static_cast<int>(nad.type()), hash);
hash = HashCombine(nad.count(), hash);
hash = HashCombine(nad.normalized(), hash);
const uint64_t data_hash =
FingerprintString(reinterpret_cast<const char *>(nad.GetData()->data()),
nad.GetData()->size() * sizeof(float));
hash = HashCombine(data_hash, hash);
return hash;
}
const NodeAnimationData *node_animation_data_;
size_t hash_;
};
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED
#endif // DRACO_ANIMATION_NODE_ANIMATION_DATA_H_

View File

@ -0,0 +1,29 @@
// Copyright 2019 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/skin.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
namespace draco {
void Skin::Copy(const Skin &s) {
inverse_bind_matrices_.Copy(s.GetInverseBindMatrices());
joints_ = s.GetJoints();
joint_root_index_ = s.GetJointRoot();
}
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -0,0 +1,64 @@
// Copyright 2019 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_SKIN_H_
#define DRACO_ANIMATION_SKIN_H_
#include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <vector>
#include "draco/animation/node_animation_data.h"
#include "draco/scene/scene_indices.h"
namespace draco {
// This class is used to store information on animation skins.
class Skin {
public:
Skin() : joint_root_index_(-1) {}
void Copy(const Skin &s);
NodeAnimationData &GetInverseBindMatrices() { return inverse_bind_matrices_; }
const NodeAnimationData &GetInverseBindMatrices() const {
return inverse_bind_matrices_;
}
int AddJoint(SceneNodeIndex index) {
joints_.push_back(index);
return joints_.size() - 1;
}
int NumJoints() const { return joints_.size(); }
SceneNodeIndex GetJoint(int index) const { return joints_[index]; }
SceneNodeIndex &GetJoint(int index) { return joints_[index]; }
const std::vector<SceneNodeIndex> &GetJoints() const { return joints_; }
void SetJointRoot(SceneNodeIndex index) { joint_root_index_ = index; }
SceneNodeIndex GetJointRoot() const { return joint_root_index_; }
private:
NodeAnimationData inverse_bind_matrices_;
// List of node indices that make up the joint hierarchy.
std::vector<SceneNodeIndex> joints_;
SceneNodeIndex joint_root_index_;
};
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED
#endif // DRACO_ANIMATION_SKIN_H_

View File

@ -28,12 +28,13 @@ std::unique_ptr<PointAttribute> AttributeTransform::InitTransformedAttribute(
const PointAttribute &src_attribute, int num_entries) { const PointAttribute &src_attribute, int num_entries) {
const int num_components = GetTransformedNumComponents(src_attribute); const int num_components = GetTransformedNumComponents(src_attribute);
const DataType dt = GetTransformedDataType(src_attribute); const DataType dt = GetTransformedDataType(src_attribute);
GeometryAttribute va; GeometryAttribute ga;
va.Init(src_attribute.attribute_type(), nullptr, num_components, dt, false, ga.Init(src_attribute.attribute_type(), nullptr, num_components, dt, false,
num_components * DataTypeLength(dt), 0); num_components * DataTypeLength(dt), 0);
std::unique_ptr<PointAttribute> transformed_attribute(new PointAttribute(va)); std::unique_ptr<PointAttribute> transformed_attribute(new PointAttribute(ga));
transformed_attribute->Reset(num_entries); transformed_attribute->Reset(num_entries);
transformed_attribute->SetIdentityMapping(); transformed_attribute->SetIdentityMapping();
transformed_attribute->set_unique_id(src_attribute.unique_id());
return transformed_attribute; return transformed_attribute;
} }

View File

@ -26,7 +26,7 @@ GeometryAttribute::GeometryAttribute()
unique_id_(0) {} unique_id_(0) {}
void GeometryAttribute::Init(GeometryAttribute::Type attribute_type, void GeometryAttribute::Init(GeometryAttribute::Type attribute_type,
DataBuffer *buffer, int8_t num_components, DataBuffer *buffer, uint8_t num_components,
DataType data_type, bool normalized, DataType data_type, bool normalized,
int64_t byte_stride, int64_t byte_offset) { int64_t byte_stride, int64_t byte_offset) {
buffer_ = buffer; buffer_ = buffer;

View File

@ -15,12 +15,18 @@
#ifndef DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_ #ifndef DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_
#define DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_ #define DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_
#include <algorithm>
#include <array> #include <array>
#include <cmath>
#include <limits> #include <limits>
#include "draco/attributes/geometry_indices.h" #include "draco/attributes/geometry_indices.h"
#include "draco/core/data_buffer.h" #include "draco/core/data_buffer.h"
#include "draco/core/hash_utils.h" #include "draco/core/hash_utils.h"
#include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include "draco/core/status.h"
#endif
namespace draco { namespace draco {
@ -51,6 +57,16 @@ class GeometryAttribute {
// predefined use case. Such attributes are often used for a shader specific // predefined use case. Such attributes are often used for a shader specific
// data. // data.
GENERIC, GENERIC,
#ifdef DRACO_TRANSCODER_SUPPORTED
// TODO(ostava): Adding a new attribute would be bit-stream change for GLTF.
// Older decoders wouldn't know what to do with this attribute type. This
// should be open-sourced only when we are ready to increase our bit-stream
// version.
TANGENT,
MATERIAL,
JOINTS,
WEIGHTS,
#endif
// Total number of different attribute types. // Total number of different attribute types.
// Always keep behind all named attributes. // Always keep behind all named attributes.
NAMED_ATTRIBUTES_COUNT, NAMED_ATTRIBUTES_COUNT,
@ -58,7 +74,7 @@ class GeometryAttribute {
GeometryAttribute(); GeometryAttribute();
// Initializes and enables the attribute. // Initializes and enables the attribute.
void Init(Type attribute_type, DataBuffer *buffer, int8_t num_components, void Init(Type attribute_type, DataBuffer *buffer, uint8_t num_components,
DataType data_type, bool normalized, int64_t byte_stride, DataType data_type, bool normalized, int64_t byte_stride,
int64_t byte_offset); int64_t byte_offset);
bool IsValid() const { return buffer_ != nullptr; } bool IsValid() const { return buffer_ != nullptr; }
@ -129,6 +145,17 @@ class GeometryAttribute {
buffer_->Write(byte_pos, value, byte_stride()); buffer_->Write(byte_pos, value, byte_stride());
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
// Sets a value of an attribute entry. The input |value| must have
// |input_num_components| entries and it will be automatically converted to
// the internal format used by the geometry attribute. If the conversion is
// not possible, an error status will be returned.
template <typename InputT>
Status ConvertAndSetAttributeValue(AttributeValueIndex avi,
int input_num_components,
const InputT *value);
#endif
// DEPRECATED: Use // DEPRECATED: Use
// ConvertValue(AttributeValueIndex att_id, // ConvertValue(AttributeValueIndex att_id,
// int out_num_components, // int out_num_components,
@ -233,10 +260,11 @@ class GeometryAttribute {
// Returns the number of components that are stored for each entry. // Returns the number of components that are stored for each entry.
// For position attribute this is usually three (x,y,z), // For position attribute this is usually three (x,y,z),
// while texture coordinates have two components (u,v). // while texture coordinates have two components (u,v).
int8_t num_components() const { return num_components_; } uint8_t num_components() const { return num_components_; }
// Indicates whether the data type should be normalized before interpretation, // Indicates whether the data type should be normalized before interpretation,
// that is, it should be divided by the max value of the data type. // that is, it should be divided by the max value of the data type.
bool normalized() const { return normalized_; } bool normalized() const { return normalized_; }
void set_normalized(bool normalized) { normalized_ = normalized; }
// The buffer storing the entire data of the attribute. // The buffer storing the entire data of the attribute.
const DataBuffer *buffer() const { return buffer_; } const DataBuffer *buffer() const { return buffer_; }
// Returns the number of bytes between two attribute entries, this is, at // Returns the number of bytes between two attribute entries, this is, at
@ -260,7 +288,7 @@ class GeometryAttribute {
// T is the stored attribute data type. // T is the stored attribute data type.
// OutT is the desired data type of the attribute. // OutT is the desired data type of the attribute.
template <typename T, typename OutT> template <typename T, typename OutT>
bool ConvertTypedValue(AttributeValueIndex att_id, int8_t out_num_components, bool ConvertTypedValue(AttributeValueIndex att_id, uint8_t out_num_components,
OutT *out_value) const { OutT *out_value) const {
const uint8_t *src_address = GetAddress(att_id); const uint8_t *src_address = GetAddress(att_id);
@ -270,29 +298,10 @@ class GeometryAttribute {
return false; return false;
} }
const T in_value = *reinterpret_cast<const T *>(src_address); const T in_value = *reinterpret_cast<const T *>(src_address);
if (!ConvertComponentValue<T, OutT>(in_value, normalized_,
// Make sure the in_value fits within the range of values that OutT out_value + i)) {
// is able to represent. Perform the check only for integral types.
if (std::is_integral<T>::value && std::is_integral<OutT>::value) {
static constexpr OutT kOutMin =
std::is_signed<T>::value ? std::numeric_limits<OutT>::lowest() : 0;
if (in_value < kOutMin || in_value > std::numeric_limits<OutT>::max()) {
return false; return false;
} }
}
out_value[i] = static_cast<OutT>(in_value);
// When converting integer to floating point, normalize the value if
// necessary.
if (std::is_integral<T>::value && std::is_floating_point<OutT>::value &&
normalized_) {
out_value[i] /= static_cast<OutT>(std::numeric_limits<T>::max());
}
// TODO(ostava): Add handling of normalized attributes when converting
// between different integer representations. If the attribute is
// normalized, integer values should be converted as if they represent 0-1
// range. E.g. when we convert uint16 to uint8, the range <0, 2^16 - 1>
// should be converted to range <0, 2^8 - 1>.
src_address += sizeof(T); src_address += sizeof(T);
} }
// Fill empty data for unused output components if needed. // Fill empty data for unused output components if needed.
@ -302,12 +311,128 @@ class GeometryAttribute {
return true; return true;
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
// Function that converts input |value| from type T to the internal attribute
// representation defined by OutT and |num_components_|.
template <typename T, typename OutT>
Status ConvertAndSetAttributeTypedValue(AttributeValueIndex avi,
int8_t input_num_components,
const T *value) {
uint8_t *address = GetAddress(avi);
// Convert all components available in both the original and output formats.
for (int i = 0; i < num_components_; ++i) {
if (!IsAddressValid(address)) {
return ErrorStatus("GeometryAttribute: Invalid address.");
}
OutT *const out_value = reinterpret_cast<OutT *>(address);
if (i < input_num_components) {
if (!ConvertComponentValue<T, OutT>(*(value + i), normalized_,
out_value)) {
return ErrorStatus(
"GeometryAttribute: Failed to convert component value.");
}
} else {
*out_value = static_cast<OutT>(0);
}
address += sizeof(OutT);
}
return OkStatus();
}
#endif // DRACO_TRANSCODER_SUPPORTED
// Converts |in_value| of type T into |out_value| of type OutT. If
// |normalized| is true, any conversion between floating point and integer
// values will be treating integers as normalized types (the entire integer
// range will be used to represent 0-1 floating point range).
template <typename T, typename OutT>
static bool ConvertComponentValue(const T &in_value, bool normalized,
OutT *out_value) {
// Make sure the |in_value| can be represented as an integral type OutT.
if (std::is_integral<OutT>::value) {
// Make sure the |in_value| fits within the range of values that OutT
// is able to represent. Perform the check only for integral types.
if (!std::is_same<T, bool>::value && std::is_integral<T>::value) {
static constexpr OutT kOutMin =
std::is_signed<T>::value ? std::numeric_limits<OutT>::min() : 0;
if (in_value < kOutMin || in_value > std::numeric_limits<OutT>::max()) {
return false;
}
}
// Check conversion of floating point |in_value| to integral value OutT.
if (std::is_floating_point<T>::value) {
// Make sure the floating point |in_value| is not NaN and not Inf as
// integral type OutT is unable to represent these values.
if (sizeof(in_value) > sizeof(double)) {
if (std::isnan(static_cast<long double>(in_value)) ||
std::isinf(static_cast<long double>(in_value))) {
return false;
}
} else if (sizeof(in_value) > sizeof(float)) {
if (std::isnan(static_cast<double>(in_value)) ||
std::isinf(static_cast<double>(in_value))) {
return false;
}
} else {
if (std::isnan(static_cast<float>(in_value)) ||
std::isinf(static_cast<float>(in_value))) {
return false;
}
}
// Make sure the floating point |in_value| fits within the range of
// values that integral type OutT is able to represent.
if (in_value < std::numeric_limits<OutT>::min() ||
in_value >= std::numeric_limits<OutT>::max()) {
return false;
}
}
}
if (std::is_integral<T>::value && std::is_floating_point<OutT>::value &&
normalized) {
// When converting integer to floating point, normalize the value if
// necessary.
*out_value = static_cast<OutT>(in_value);
*out_value /= static_cast<OutT>(std::numeric_limits<T>::max());
} else if (std::is_floating_point<T>::value &&
std::is_integral<OutT>::value && normalized) {
// Converting from floating point to a normalized integer.
if (in_value > 1 || in_value < 0) {
// Normalized float values need to be between 0 and 1.
return false;
}
// TODO(ostava): Consider allowing float to normalized integer conversion
// for 64-bit integer types. Currently it doesn't work because we don't
// have a floating point type that could store all 64 bit integers.
if (sizeof(OutT) > 4) {
return false;
}
// Expand the float to the range of the output integer and round it to the
// nearest representable value. Use doubles for the math to ensure the
// integer values are represented properly during the conversion process.
*out_value = static_cast<OutT>(std::floor(
in_value * static_cast<double>(std::numeric_limits<OutT>::max()) +
0.5));
} else {
*out_value = static_cast<OutT>(in_value);
}
// TODO(ostava): Add handling of normalized attributes when converting
// between different integer representations. If the attribute is
// normalized, integer values should be converted as if they represent 0-1
// range. E.g. when we convert uint16 to uint8, the range <0, 2^16 - 1>
// should be converted to range <0, 2^8 - 1>.
return true;
}
DataBuffer *buffer_; DataBuffer *buffer_;
// The buffer descriptor is stored at the time the buffer is attached to this // The buffer descriptor is stored at the time the buffer is attached to this
// attribute. The purpose is to detect if any changes happened to the buffer // attribute. The purpose is to detect if any changes happened to the buffer
// since the time it was attached. // since the time it was attached.
DataBufferDescriptor buffer_descriptor_; DataBufferDescriptor buffer_descriptor_;
int8_t num_components_; uint8_t num_components_;
DataType data_type_; DataType data_type_;
bool normalized_; bool normalized_;
int64_t byte_stride_; int64_t byte_stride_;
@ -323,6 +448,54 @@ class GeometryAttribute {
friend struct GeometryAttributeHasher; friend struct GeometryAttributeHasher;
}; };
#ifdef DRACO_TRANSCODER_SUPPORTED
template <typename InputT>
Status GeometryAttribute::ConvertAndSetAttributeValue(AttributeValueIndex avi,
int input_num_components,
const InputT *value) {
switch (this->data_type()) {
case DT_INT8:
return ConvertAndSetAttributeTypedValue<InputT, int8_t>(
avi, input_num_components, value);
case DT_UINT8:
return ConvertAndSetAttributeTypedValue<InputT, uint8_t>(
avi, input_num_components, value);
case DT_INT16:
return ConvertAndSetAttributeTypedValue<InputT, int16_t>(
avi, input_num_components, value);
case DT_UINT16:
return ConvertAndSetAttributeTypedValue<InputT, uint16_t>(
avi, input_num_components, value);
case DT_INT32:
return ConvertAndSetAttributeTypedValue<InputT, int32_t>(
avi, input_num_components, value);
case DT_UINT32:
return ConvertAndSetAttributeTypedValue<InputT, uint32_t>(
avi, input_num_components, value);
case DT_INT64:
return ConvertAndSetAttributeTypedValue<InputT, int64_t>(
avi, input_num_components, value);
case DT_UINT64:
return ConvertAndSetAttributeTypedValue<InputT, uint64_t>(
avi, input_num_components, value);
case DT_FLOAT32:
return ConvertAndSetAttributeTypedValue<InputT, float>(
avi, input_num_components, value);
case DT_FLOAT64:
return ConvertAndSetAttributeTypedValue<InputT, double>(
avi, input_num_components, value);
case DT_BOOL:
return ConvertAndSetAttributeTypedValue<InputT, bool>(
avi, input_num_components, value);
default:
break;
}
return ErrorStatus(
"GeometryAttribute::SetAndConvertAttributeValue: Unsupported "
"attribute type.");
}
#endif
// Hashing support // Hashing support
// Function object for using Attribute as a hash key. // Function object for using Attribute as a hash key.

View File

@ -222,4 +222,47 @@ AttributeValueIndex::ValueType PointAttribute::DeduplicateFormattedValues(
} }
#endif #endif
#ifdef DRACO_TRANSCODER_SUPPORTED
void PointAttribute::RemoveUnusedValues() {
if (is_mapping_identity()) {
return; // For identity mapping, all values are always used.
}
// For explicit mapping we need to check if any point is mapped to a value.
// If not we can delete the value.
IndexTypeVector<AttributeValueIndex, bool> is_value_used(size(), false);
int num_used_values = 0;
for (PointIndex pi(0); pi < indices_map_.size(); ++pi) {
const AttributeValueIndex avi = indices_map_[pi];
if (!is_value_used[avi]) {
is_value_used[avi] = true;
num_used_values++;
}
}
if (num_used_values == size()) {
return; // All values are used.
}
// Remap the values and update the point to value mapping.
IndexTypeVector<AttributeValueIndex, AttributeValueIndex>
old_to_new_value_map(size(), kInvalidAttributeValueIndex);
AttributeValueIndex new_avi(0);
for (AttributeValueIndex avi(0); avi < size(); ++avi) {
if (!is_value_used[avi]) {
continue;
}
if (avi != new_avi) {
SetAttributeValue(new_avi, GetAddress(avi));
}
old_to_new_value_map[avi] = new_avi++;
}
// Remap all points to the new attribute values.
for (PointIndex pi(0); pi < indices_map_.size(); ++pi) {
indices_map_[pi] = old_to_new_value_map[indices_map_[pi]];
}
num_unique_entries_ = num_used_values;
}
#endif
} // namespace draco } // namespace draco

View File

@ -133,6 +133,12 @@ class PointAttribute : public GeometryAttribute {
return attribute_transform_data_.get(); return attribute_transform_data_.get();
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
// Removes unused values from the attribute. Value is unused when no point
// is mapped to the value. Only applicable when the mapping is not identity.
void RemoveUnusedValues();
#endif
private: private:
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED #ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
template <typename T> template <typename T>

View File

@ -15,14 +15,16 @@
#include "draco/compression/attributes/attributes_encoder.h" #include "draco/compression/attributes/attributes_encoder.h"
#include "draco/core/varint_encoding.h" #include "draco/core/varint_encoding.h"
#include "draco/draco_features.h"
namespace draco { namespace draco {
AttributesEncoder::AttributesEncoder() AttributesEncoder::AttributesEncoder()
: point_cloud_encoder_(nullptr), point_cloud_(nullptr) {} : point_cloud_encoder_(nullptr), point_cloud_(nullptr) {}
AttributesEncoder::AttributesEncoder(int att_id) : AttributesEncoder() { AttributesEncoder::AttributesEncoder(int point_attrib_id)
AddAttributeId(att_id); : AttributesEncoder() {
AddAttributeId(point_attrib_id);
} }
bool AttributesEncoder::Init(PointCloudEncoder *encoder, const PointCloud *pc) { bool AttributesEncoder::Init(PointCloudEncoder *encoder, const PointCloud *pc) {
@ -37,7 +39,15 @@ bool AttributesEncoder::EncodeAttributesEncoderData(EncoderBuffer *out_buffer) {
for (uint32_t i = 0; i < num_attributes(); ++i) { for (uint32_t i = 0; i < num_attributes(); ++i) {
const int32_t att_id = point_attribute_ids_[i]; const int32_t att_id = point_attribute_ids_[i];
const PointAttribute *const pa = point_cloud_->attribute(att_id); const PointAttribute *const pa = point_cloud_->attribute(att_id);
out_buffer->Encode(static_cast<uint8_t>(pa->attribute_type())); GeometryAttribute::Type type = pa->attribute_type();
#ifdef DRACO_TRANSCODER_SUPPORTED
// Attribute types TANGENT, MATERIAL, JOINTS, and WEIGHTS are not supported
// in the official bitstream. They will be encoded as GENERIC.
if (type > GeometryAttribute::GENERIC) {
type = GeometryAttribute::GENERIC;
}
#endif
out_buffer->Encode(static_cast<uint8_t>(type));
out_buffer->Encode(static_cast<uint8_t>(pa->data_type())); out_buffer->Encode(static_cast<uint8_t>(pa->data_type()));
out_buffer->Encode(static_cast<uint8_t>(pa->num_components())); out_buffer->Encode(static_cast<uint8_t>(pa->num_components()));
out_buffer->Encode(static_cast<uint8_t>(pa->normalized())); out_buffer->Encode(static_cast<uint8_t>(pa->normalized()));

View File

@ -72,16 +72,19 @@ class PointAttributeVectorOutputIterator {
Self &operator*() { return *this; } Self &operator*() { return *this; }
// Still needed in some cases. // Still needed in some cases.
// TODO(hemmer): remove. // TODO(b/199760123): Remove.
// hardcoded to 3 based on legacy usage. // hardcoded to 3 based on legacy usage.
const Self &operator=(const VectorD<CoeffT, 3> &val) { const Self &operator=(const VectorD<CoeffT, 3> &val) {
DRACO_DCHECK_EQ(attributes_.size(), 1); // Expect only ONE attribute. DRACO_DCHECK_EQ(attributes_.size(), 1); // Expect only ONE attribute.
AttributeTuple &att = attributes_[0]; AttributeTuple &att = attributes_[0];
PointAttribute *attribute = std::get<0>(att); PointAttribute *attribute = std::get<0>(att);
const AttributeValueIndex avi = attribute->mapped_index(point_id_);
if (avi >= static_cast<uint32_t>(attribute->size())) {
return *this;
}
const uint32_t &offset = std::get<1>(att); const uint32_t &offset = std::get<1>(att);
DRACO_DCHECK_EQ(offset, 0); // expected to be zero DRACO_DCHECK_EQ(offset, 0); // expected to be zero
attribute->SetAttributeValue(attribute->mapped_index(point_id_), attribute->SetAttributeValue(avi, &val[0] + offset);
&val[0] + offset);
return *this; return *this;
} }
// Additional operator taking std::vector as argument. // Additional operator taking std::vector as argument.
@ -89,6 +92,10 @@ class PointAttributeVectorOutputIterator {
for (auto index = 0; index < attributes_.size(); index++) { for (auto index = 0; index < attributes_.size(); index++) {
AttributeTuple &att = attributes_[index]; AttributeTuple &att = attributes_[index];
PointAttribute *attribute = std::get<0>(att); PointAttribute *attribute = std::get<0>(att);
const AttributeValueIndex avi = attribute->mapped_index(point_id_);
if (avi >= static_cast<uint32_t>(attribute->size())) {
return *this;
}
const uint32_t &offset = std::get<1>(att); const uint32_t &offset = std::get<1>(att);
const uint32_t &data_size = std::get<3>(att); const uint32_t &data_size = std::get<3>(att);
const uint32_t &num_components = std::get<4>(att); const uint32_t &num_components = std::get<4>(att);
@ -103,10 +110,6 @@ class PointAttributeVectorOutputIterator {
// redirect to copied data // redirect to copied data
data_source = reinterpret_cast<uint32_t *>(data_); data_source = reinterpret_cast<uint32_t *>(data_);
} }
const AttributeValueIndex avi = attribute->mapped_index(point_id_);
if (avi >= static_cast<uint32_t>(attribute->size())) {
return *this;
}
attribute->SetAttributeValue(avi, data_source); attribute->SetAttributeValue(avi, data_source);
} }
return *this; return *this;
@ -195,54 +198,55 @@ bool KdTreeAttributesDecoder::DecodePortableAttributes(
data_size, num_components); data_size, num_components);
total_dimensionality += num_components; total_dimensionality += num_components;
} }
PointAttributeVectorOutputIterator<uint32_t> out_it(atts); typedef PointAttributeVectorOutputIterator<uint32_t> OutIt;
OutIt out_it(atts);
switch (compression_level) { switch (compression_level) {
case 0: { case 0: {
DynamicIntegerPointsKdTreeDecoder<0> decoder(total_dimensionality); if (!DecodePoints<0, OutIt>(total_dimensionality, num_points, in_buffer,
if (!decoder.DecodePoints(in_buffer, out_it)) { &out_it)) {
return false; return false;
} }
break; break;
} }
case 1: { case 1: {
DynamicIntegerPointsKdTreeDecoder<1> decoder(total_dimensionality); if (!DecodePoints<1, OutIt>(total_dimensionality, num_points, in_buffer,
if (!decoder.DecodePoints(in_buffer, out_it)) { &out_it)) {
return false; return false;
} }
break; break;
} }
case 2: { case 2: {
DynamicIntegerPointsKdTreeDecoder<2> decoder(total_dimensionality); if (!DecodePoints<2, OutIt>(total_dimensionality, num_points, in_buffer,
if (!decoder.DecodePoints(in_buffer, out_it)) { &out_it)) {
return false; return false;
} }
break; break;
} }
case 3: { case 3: {
DynamicIntegerPointsKdTreeDecoder<3> decoder(total_dimensionality); if (!DecodePoints<3, OutIt>(total_dimensionality, num_points, in_buffer,
if (!decoder.DecodePoints(in_buffer, out_it)) { &out_it)) {
return false; return false;
} }
break; break;
} }
case 4: { case 4: {
DynamicIntegerPointsKdTreeDecoder<4> decoder(total_dimensionality); if (!DecodePoints<4, OutIt>(total_dimensionality, num_points, in_buffer,
if (!decoder.DecodePoints(in_buffer, out_it)) { &out_it)) {
return false; return false;
} }
break; break;
} }
case 5: { case 5: {
DynamicIntegerPointsKdTreeDecoder<5> decoder(total_dimensionality); if (!DecodePoints<5, OutIt>(total_dimensionality, num_points, in_buffer,
if (!decoder.DecodePoints(in_buffer, out_it)) { &out_it)) {
return false; return false;
} }
break; break;
} }
case 6: { case 6: {
DynamicIntegerPointsKdTreeDecoder<6> decoder(total_dimensionality); if (!DecodePoints<6, OutIt>(total_dimensionality, num_points, in_buffer,
if (!decoder.DecodePoints(in_buffer, out_it)) { &out_it)) {
return false; return false;
} }
break; break;
@ -253,6 +257,19 @@ bool KdTreeAttributesDecoder::DecodePortableAttributes(
return true; return true;
} }
template <int level_t, typename OutIteratorT>
bool KdTreeAttributesDecoder::DecodePoints(int total_dimensionality,
int num_expected_points,
DecoderBuffer *in_buffer,
OutIteratorT *out_iterator) {
DynamicIntegerPointsKdTreeDecoder<level_t> decoder(total_dimensionality);
if (!decoder.DecodePoints(in_buffer, *out_iterator, num_expected_points) ||
decoder.num_decoded_points() != num_expected_points) {
return false;
}
return true;
}
bool KdTreeAttributesDecoder::DecodeDataNeededByPortableTransforms( bool KdTreeAttributesDecoder::DecodeDataNeededByPortableTransforms(
DecoderBuffer *in_buffer) { DecoderBuffer *in_buffer) {
if (in_buffer->bitstream_version() >= DRACO_BITSTREAM_VERSION(2, 3)) { if (in_buffer->bitstream_version() >= DRACO_BITSTREAM_VERSION(2, 3)) {
@ -336,6 +353,10 @@ bool KdTreeAttributesDecoder::DecodeDataNeededByPortableTransforms(
return false; return false;
} }
if (method == KdTreeAttributesEncodingMethod::kKdTreeQuantizationEncoding) { if (method == KdTreeAttributesEncodingMethod::kKdTreeQuantizationEncoding) {
// This method only supports one attribute with exactly three components.
if (atts.size() != 1 || std::get<4>(atts[0]) != 3) {
return false;
}
uint8_t compression_level = 0; uint8_t compression_level = 0;
if (!in_buffer->Decode(&compression_level)) { if (!in_buffer->Decode(&compression_level)) {
return false; return false;
@ -376,7 +397,7 @@ bool KdTreeAttributesDecoder::DecodeDataNeededByPortableTransforms(
GetDecoder()->point_cloud()->attribute(att_id); GetDecoder()->point_cloud()->attribute(att_id);
attr->Reset(num_points); attr->Reset(num_points);
attr->SetIdentityMapping(); attr->SetIdentityMapping();
}; }
PointAttributeVectorOutputIterator<uint32_t> out_it(atts); PointAttributeVectorOutputIterator<uint32_t> out_it(atts);
@ -455,7 +476,11 @@ bool KdTreeAttributesDecoder::TransformAttributeBackToSignedType(
att->GetValue(avi, &unsigned_val[0]); att->GetValue(avi, &unsigned_val[0]);
for (int c = 0; c < att->num_components(); ++c) { for (int c = 0; c < att->num_components(); ++c) {
// Up-cast |unsigned_val| to int32_t to ensure we don't overflow it for // Up-cast |unsigned_val| to int32_t to ensure we don't overflow it for
// smaller data types. // smaller data types. But first check that the up-casting does not cause
// signed integer overflow.
if (unsigned_val[c] > std::numeric_limits<int32_t>::max()) {
return false;
}
signed_val[c] = static_cast<SignedDataTypeT>( signed_val[c] = static_cast<SignedDataTypeT>(
static_cast<int32_t>(unsigned_val[c]) + static_cast<int32_t>(unsigned_val[c]) +
min_signed_values_[num_processed_signed_components + c]); min_signed_values_[num_processed_signed_components + c]);

View File

@ -31,6 +31,10 @@ class KdTreeAttributesDecoder : public AttributesDecoder {
bool TransformAttributesToOriginalFormat() override; bool TransformAttributesToOriginalFormat() override;
private: private:
template <int level_t, typename OutIteratorT>
bool DecodePoints(int total_dimensionality, int num_expected_points,
DecoderBuffer *in_buffer, OutIteratorT *out_iterator);
template <typename SignedDataTypeT> template <typename SignedDataTypeT>
bool TransformAttributeBackToSignedType(PointAttribute *att, bool TransformAttributeBackToSignedType(PointAttribute *att,
int num_processed_signed_components); int num_processed_signed_components);

View File

@ -61,7 +61,7 @@ class OctahedronToolBox {
return false; return false;
} }
quantization_bits_ = q; quantization_bits_ = q;
max_quantized_value_ = (1 << quantization_bits_) - 1; max_quantized_value_ = (1u << quantization_bits_) - 1;
max_value_ = max_quantized_value_ - 1; max_value_ = max_quantized_value_ - 1;
dequantization_scale_ = 2.f / max_value_; dequantization_scale_ = 2.f / max_value_;
center_value_ = max_value_ / 2; center_value_ = max_value_ / 2;
@ -208,7 +208,9 @@ class OctahedronToolBox {
DRACO_DCHECK_LE(t, center_value_); DRACO_DCHECK_LE(t, center_value_);
DRACO_DCHECK_GE(s, -center_value_); DRACO_DCHECK_GE(s, -center_value_);
DRACO_DCHECK_GE(t, -center_value_); DRACO_DCHECK_GE(t, -center_value_);
return std::abs(s) + std::abs(t) <= center_value_; const uint32_t st =
static_cast<uint32_t>(std::abs(s)) + static_cast<uint32_t>(std::abs(t));
return st <= center_value_;
} }
void InvertDiamond(int32_t *s, int32_t *t) const { void InvertDiamond(int32_t *s, int32_t *t) const {
@ -230,19 +232,29 @@ class OctahedronToolBox {
sign_t = (*t > 0) ? 1 : -1; sign_t = (*t > 0) ? 1 : -1;
} }
const int32_t corner_point_s = sign_s * center_value_; // Perform the addition and subtraction using unsigned integers to avoid
const int32_t corner_point_t = sign_t * center_value_; // signed integer overflows for bad data. Note that the result will be
*s = 2 * *s - corner_point_s; // unchanged for non-overflowing cases.
*t = 2 * *t - corner_point_t; const uint32_t corner_point_s = sign_s * center_value_;
const uint32_t corner_point_t = sign_t * center_value_;
uint32_t us = *s;
uint32_t ut = *t;
us = us + us - corner_point_s;
ut = ut + ut - corner_point_t;
if (sign_s * sign_t >= 0) { if (sign_s * sign_t >= 0) {
int32_t temp = *s; uint32_t temp = us;
*s = -*t; us = -ut;
*t = -temp; ut = -temp;
} else { } else {
std::swap(*s, *t); std::swap(us, ut);
} }
*s = (*s + corner_point_s) / 2; us = us + corner_point_s;
*t = (*t + corner_point_t) / 2; ut = ut + corner_point_t;
*s = us;
*t = ut;
*s /= 2;
*t /= 2;
} }
void InvertDirection(int32_t *s, int32_t *t) const { void InvertDirection(int32_t *s, int32_t *t) const {
@ -318,7 +330,7 @@ class OctahedronToolBox {
// Remaining coordinate can be computed by projecting the (y, z) values onto // Remaining coordinate can be computed by projecting the (y, z) values onto
// the surface of the octahedron. // the surface of the octahedron.
const float x = 1.f - abs(y) - abs(z); const float x = 1.f - std::abs(y) - std::abs(z);
// |x| is essentially a signed distance from the diagonal edges of the // |x| is essentially a signed distance from the diagonal edges of the
// diamond shown on the figure above. It is positive for all points in the // diamond shown on the figure above. It is positive for all points in the

View File

@ -16,7 +16,9 @@
#ifndef DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_ #ifndef DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_
#define DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_ #define DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_
#include <cstddef>
#include <cstring> #include <cstring>
#include <iterator>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -99,11 +101,17 @@ class PointDVector {
data_(n_items * dimensionality), data_(n_items * dimensionality),
data0_(data_.data()) {} data0_(data_.data()) {}
// random access iterator // random access iterator
class PointDVectorIterator class PointDVectorIterator {
: public std::iterator<std::random_access_iterator_tag, size_t, size_t> {
friend class PointDVector; friend class PointDVector;
public: public:
// Iterator traits expected by std libraries.
using iterator_category = std::random_access_iterator_tag;
using value_type = size_t;
using difference_type = size_t;
using pointer = PointDVector *;
using reference = PointDVector &;
// std::iter_swap is called inside of std::partition and needs this // std::iter_swap is called inside of std::partition and needs this
// specialized support // specialized support
PseudoPointD<internal_t> operator*() const { PseudoPointD<internal_t> operator*() const {

View File

@ -22,6 +22,7 @@
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h" #include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h" #include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
#include "draco/compression/bit_coders/rans_bit_decoder.h" #include "draco/compression/bit_coders/rans_bit_decoder.h"
#include "draco/core/math_utils.h"
#include "draco/core/varint_decoding.h" #include "draco/core/varint_decoding.h"
#include "draco/draco_features.h" #include "draco/draco_features.h"
@ -161,7 +162,8 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramDecoder<
if (!is_crease) { if (!is_crease) {
++num_used_parallelograms; ++num_used_parallelograms;
for (int j = 0; j < num_components; ++j) { for (int j = 0; j < num_components; ++j) {
multi_pred_vals[j] += pred_vals[i][j]; multi_pred_vals[j] =
AddAsUnsigned(multi_pred_vals[j], pred_vals[i][j]);
} }
} }
} }
@ -210,6 +212,9 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramDecoder<
if (!DecodeVarint<uint32_t>(&num_flags, buffer)) { if (!DecodeVarint<uint32_t>(&num_flags, buffer)) {
return false; return false;
} }
if (num_flags > this->mesh_data().corner_table()->num_corners()) {
return false;
}
if (num_flags > 0) { if (num_flags > 0) {
is_crease_edge_[i].resize(num_flags); is_crease_edge_[i].resize(num_flags);
RAnsBitDecoder decoder; RAnsBitDecoder decoder;

View File

@ -392,7 +392,7 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
RAnsBitEncoder encoder; RAnsBitEncoder encoder;
encoder.StartEncoding(); encoder.StartEncoding();
// Encode the crease edge flags in the reverse vertex order that is needed // Encode the crease edge flags in the reverse vertex order that is needed
// be the decoder. Note that for the currently supported mode, each vertex // by the decoder. Note that for the currently supported mode, each vertex
// has exactly |num_used_parallelograms| edges that need to be encoded. // has exactly |num_used_parallelograms| edges that need to be encoded.
for (int j = static_cast<int>(is_crease_edge_[i].size()) - for (int j = static_cast<int>(is_crease_edge_[i].size()) -
num_used_parallelograms; num_used_parallelograms;

View File

@ -18,6 +18,7 @@
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h" #include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h" #include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
#include "draco/core/math_utils.h"
#include "draco/draco_features.h" #include "draco/draco_features.h"
namespace draco { namespace draco {
@ -89,7 +90,8 @@ bool MeshPredictionSchemeMultiParallelogramDecoder<DataTypeT, TransformT,
p, corner_id, table, *vertex_to_data_map, out_data, p, corner_id, table, *vertex_to_data_map, out_data,
num_components, parallelogram_pred_vals.get())) { num_components, parallelogram_pred_vals.get())) {
for (int c = 0; c < num_components; ++c) { for (int c = 0; c < num_components; ++c) {
pred_vals[c] += parallelogram_pred_vals[c]; pred_vals[c] =
AddAsUnsigned(pred_vals[c], parallelogram_pred_vals[c]);
} }
++num_parallelograms; ++num_parallelograms;
} }

View File

@ -105,7 +105,7 @@ class MeshPredictionSchemeTexCoordsDecoder
static_cast<float>(data[data_offset + 1])); static_cast<float>(data[data_offset + 1]));
} }
void ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data, bool ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data,
int data_id); int data_id);
private: private:
@ -123,6 +123,10 @@ bool MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data, ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
int /* size */, int num_components, int /* size */, int num_components,
const PointIndex *entry_to_point_id_map) { const PointIndex *entry_to_point_id_map) {
if (num_components != 2) {
// Corrupt/malformed input. Two output components are req'd.
return false;
}
num_components_ = num_components; num_components_ = num_components;
entry_to_point_id_map_ = entry_to_point_id_map; entry_to_point_id_map_ = entry_to_point_id_map;
predicted_value_ = predicted_value_ =
@ -133,7 +137,9 @@ bool MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
static_cast<int>(this->mesh_data().data_to_corner_map()->size()); static_cast<int>(this->mesh_data().data_to_corner_map()->size());
for (int p = 0; p < corner_map_size; ++p) { for (int p = 0; p < corner_map_size; ++p) {
const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p); const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p);
ComputePredictedValue(corner_id, out_data, p); if (!ComputePredictedValue(corner_id, out_data, p)) {
return false;
}
const int dst_offset = p * num_components; const int dst_offset = p * num_components;
this->transform().ComputeOriginalValue( this->transform().ComputeOriginalValue(
@ -159,6 +165,11 @@ bool MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
if (num_orientations == 0) { if (num_orientations == 0) {
return false; return false;
} }
if (num_orientations > this->mesh_data().corner_table()->num_corners()) {
// We can't have more orientations than the maximum number of decoded
// values.
return false;
}
orientations_.resize(num_orientations); orientations_.resize(num_orientations);
bool last_orientation = true; bool last_orientation = true;
RAnsBitDecoder decoder; RAnsBitDecoder decoder;
@ -177,7 +188,7 @@ bool MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
} }
template <typename DataTypeT, class TransformT, class MeshDataT> template <typename DataTypeT, class TransformT, class MeshDataT>
void MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>:: bool MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data, ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data,
int data_id) { int data_id) {
// Compute the predicted UV coordinate from the positions on all corners // Compute the predicted UV coordinate from the positions on all corners
@ -206,9 +217,17 @@ void MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
const Vector2f p_uv = GetTexCoordForEntryId(prev_data_id, data); const Vector2f p_uv = GetTexCoordForEntryId(prev_data_id, data);
if (p_uv == n_uv) { if (p_uv == n_uv) {
// We cannot do a reliable prediction on degenerated UV triangles. // We cannot do a reliable prediction on degenerated UV triangles.
predicted_value_[0] = static_cast<int>(p_uv[0]); // Technically floats > INT_MAX are undefined, but compilers will
predicted_value_[1] = static_cast<int>(p_uv[1]); // convert those values to INT_MIN. We are being explicit here for asan.
return; for (const int i : {0, 1}) {
if (std::isnan(p_uv[i]) || static_cast<double>(p_uv[i]) > INT_MAX ||
static_cast<double>(p_uv[i]) < INT_MIN) {
predicted_value_[i] = INT_MIN;
} else {
predicted_value_[i] = static_cast<int>(p_uv[i]);
}
}
return true;
} }
// Get positions at all corners. // Get positions at all corners.
@ -282,32 +301,40 @@ void MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
const float pnvs = pn_uv[1] * s + n_uv[1]; const float pnvs = pn_uv[1] * s + n_uv[1];
const float pnvt = pn_uv[1] * t; const float pnvt = pn_uv[1] * t;
Vector2f predicted_uv; Vector2f predicted_uv;
if (orientations_.empty()) {
return false;
}
// When decoding the data, we already know which orientation to use. // When decoding the data, we already know which orientation to use.
const bool orientation = orientations_.back(); const bool orientation = orientations_.back();
orientations_.pop_back(); orientations_.pop_back();
if (orientation) if (orientation) {
predicted_uv = Vector2f(pnus - pnvt, pnvs + pnut); predicted_uv = Vector2f(pnus - pnvt, pnvs + pnut);
else } else {
predicted_uv = Vector2f(pnus + pnvt, pnvs - pnut); predicted_uv = Vector2f(pnus + pnvt, pnvs - pnut);
}
if (std::is_integral<DataTypeT>::value) { if (std::is_integral<DataTypeT>::value) {
// Round the predicted value for integer types. // Round the predicted value for integer types.
if (std::isnan(predicted_uv[0])) { // Technically floats > INT_MAX are undefined, but compilers will
// convert those values to INT_MIN. We are being explicit here for asan.
const double u = floor(predicted_uv[0] + 0.5);
if (std::isnan(u) || u > INT_MAX || u < INT_MIN) {
predicted_value_[0] = INT_MIN; predicted_value_[0] = INT_MIN;
} else { } else {
predicted_value_[0] = static_cast<int>(floor(predicted_uv[0] + 0.5)); predicted_value_[0] = static_cast<int>(u);
} }
if (std::isnan(predicted_uv[1])) { const double v = floor(predicted_uv[1] + 0.5);
if (std::isnan(v) || v > INT_MAX || v < INT_MIN) {
predicted_value_[1] = INT_MIN; predicted_value_[1] = INT_MIN;
} else { } else {
predicted_value_[1] = static_cast<int>(floor(predicted_uv[1] + 0.5)); predicted_value_[1] = static_cast<int>(v);
} }
} else { } else {
predicted_value_[0] = static_cast<int>(predicted_uv[0]); predicted_value_[0] = static_cast<int>(predicted_uv[0]);
predicted_value_[1] = static_cast<int>(predicted_uv[1]); predicted_value_[1] = static_cast<int>(predicted_uv[1]);
} }
return;
return true;
} }
// Else we don't have available textures on both corners. For such case we // Else we don't have available textures on both corners. For such case we
// can't use positions for predicting the uv value and we resort to delta // can't use positions for predicting the uv value and we resort to delta
@ -330,12 +357,13 @@ void MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
for (int i = 0; i < num_components_; ++i) { for (int i = 0; i < num_components_; ++i) {
predicted_value_[i] = 0; predicted_value_[i] = 0;
} }
return; return true;
} }
} }
for (int i = 0; i < num_components_; ++i) { for (int i = 0; i < num_components_; ++i) {
predicted_value_[i] = data[data_offset + i]; predicted_value_[i] = data[data_offset + i];
} }
return true;
} }
} // namespace draco } // namespace draco

View File

@ -98,7 +98,10 @@ bool MeshPredictionSchemeTexCoordsPortableEncoder<DataTypeT, TransformT,
static_cast<int>(this->mesh_data().data_to_corner_map()->size() - 1); static_cast<int>(this->mesh_data().data_to_corner_map()->size() - 1);
p >= 0; --p) { p >= 0; --p) {
const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p); const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p);
predictor_.template ComputePredictedValue<true>(corner_id, in_data, p); if (!predictor_.template ComputePredictedValue<true>(corner_id, in_data,
p)) {
return false;
}
const int dst_offset = p * num_components; const int dst_offset = p * num_components;
this->transform().ComputeCorrection(in_data + dst_offset, this->transform().ComputeCorrection(in_data + dst_offset,

View File

@ -17,6 +17,9 @@
#include <math.h> #include <math.h>
#include <algorithm>
#include <limits>
#include "draco/attributes/point_attribute.h" #include "draco/attributes/point_attribute.h"
#include "draco/core/math_utils.h" #include "draco/core/math_utils.h"
#include "draco/core/vector_d.h" #include "draco/core/vector_d.h"
@ -105,10 +108,14 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
next_data_id = mesh_data_.vertex_to_data_map()->at(next_vert_id); next_data_id = mesh_data_.vertex_to_data_map()->at(next_vert_id);
prev_data_id = mesh_data_.vertex_to_data_map()->at(prev_vert_id); prev_data_id = mesh_data_.vertex_to_data_map()->at(prev_vert_id);
typedef VectorD<int64_t, 2> Vec2;
typedef VectorD<int64_t, 3> Vec3;
typedef VectorD<uint64_t, 2> Vec2u;
if (prev_data_id < data_id && next_data_id < data_id) { if (prev_data_id < data_id && next_data_id < data_id) {
// Both other corners have available UV coordinates for prediction. // Both other corners have available UV coordinates for prediction.
const VectorD<int64_t, 2> n_uv = GetTexCoordForEntryId(next_data_id, data); const Vec2 n_uv = GetTexCoordForEntryId(next_data_id, data);
const VectorD<int64_t, 2> p_uv = GetTexCoordForEntryId(prev_data_id, data); const Vec2 p_uv = GetTexCoordForEntryId(prev_data_id, data);
if (p_uv == n_uv) { if (p_uv == n_uv) {
// We cannot do a reliable prediction on degenerated UV triangles. // We cannot do a reliable prediction on degenerated UV triangles.
predicted_value_[0] = p_uv[0]; predicted_value_[0] = p_uv[0];
@ -117,9 +124,9 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
} }
// Get positions at all corners. // Get positions at all corners.
const VectorD<int64_t, 3> tip_pos = GetPositionForEntryId(data_id); const Vec3 tip_pos = GetPositionForEntryId(data_id);
const VectorD<int64_t, 3> next_pos = GetPositionForEntryId(next_data_id); const Vec3 next_pos = GetPositionForEntryId(next_data_id);
const VectorD<int64_t, 3> prev_pos = GetPositionForEntryId(prev_data_id); const Vec3 prev_pos = GetPositionForEntryId(prev_data_id);
// We use the positions of the above triangle to predict the texture // We use the positions of the above triangle to predict the texture
// coordinate on the tip corner C. // coordinate on the tip corner C.
// To convert the triangle into the UV coordinate system we first compute // To convert the triangle into the UV coordinate system we first compute
@ -135,17 +142,17 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
// Where next_pos is point (N), prev_pos is point (P) and tip_pos is the // Where next_pos is point (N), prev_pos is point (P) and tip_pos is the
// position of predicted coordinate (C). // position of predicted coordinate (C).
// //
const VectorD<int64_t, 3> pn = prev_pos - next_pos; const Vec3 pn = prev_pos - next_pos;
const uint64_t pn_norm2_squared = pn.SquaredNorm(); const uint64_t pn_norm2_squared = pn.SquaredNorm();
if (pn_norm2_squared != 0) { if (pn_norm2_squared != 0) {
// Compute the projection of C onto PN by computing dot product of CN with // Compute the projection of C onto PN by computing dot product of CN with
// PN and normalizing it by length of PN. This gives us a factor |s| where // PN and normalizing it by length of PN. This gives us a factor |s| where
// |s = PN.Dot(CN) / PN.SquaredNorm2()|. This factor can be used to // |s = PN.Dot(CN) / PN.SquaredNorm2()|. This factor can be used to
// compute X in UV space |X_UV| as |X_UV = N_UV + s * PN_UV|. // compute X in UV space |X_UV| as |X_UV = N_UV + s * PN_UV|.
const VectorD<int64_t, 3> cn = tip_pos - next_pos; const Vec3 cn = tip_pos - next_pos;
const int64_t cn_dot_pn = pn.Dot(cn); const int64_t cn_dot_pn = pn.Dot(cn);
const VectorD<int64_t, 2> pn_uv = p_uv - n_uv; const Vec2 pn_uv = p_uv - n_uv;
// Because we perform all computations with integers, we don't explicitly // Because we perform all computations with integers, we don't explicitly
// compute the normalized factor |s|, but rather we perform all operations // compute the normalized factor |s|, but rather we perform all operations
// over UV vectors in a non-normalized coordinate system scaled with a // over UV vectors in a non-normalized coordinate system scaled with a
@ -153,19 +160,30 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
// //
// x_uv = X_UV * PN.Norm2Squared() // x_uv = X_UV * PN.Norm2Squared()
// //
const VectorD<int64_t, 2> x_uv = const int64_t n_uv_absmax_element =
n_uv * pn_norm2_squared + (cn_dot_pn * pn_uv); std::max(std::abs(n_uv[0]), std::abs(n_uv[1]));
if (n_uv_absmax_element >
std::numeric_limits<int64_t>::max() / pn_norm2_squared) {
// Return false if the below multiplication would overflow.
return false;
}
const int64_t pn_uv_absmax_element =
std::max(std::abs(pn_uv[0]), std::abs(pn_uv[1]));
if (cn_dot_pn >
std::numeric_limits<int64_t>::max() / pn_uv_absmax_element) {
// Return false if squared length calculation would overflow.
return false;
}
const Vec2 x_uv = n_uv * pn_norm2_squared + (cn_dot_pn * pn_uv);
const int64_t pn_absmax_element = const int64_t pn_absmax_element =
std::max(std::max(std::abs(pn[0]), std::abs(pn[1])), std::abs(pn[2])); std::max(std::max(std::abs(pn[0]), std::abs(pn[1])), std::abs(pn[2]));
if (cn_dot_pn > std::numeric_limits<int64_t>::max() / pn_absmax_element) { if (cn_dot_pn > std::numeric_limits<int64_t>::max() / pn_absmax_element) {
// return false if squared length calculation would overflow. // Return false if squared length calculation would overflow.
return false; return false;
} }
// Compute squared length of vector CX in position coordinate system: // Compute squared length of vector CX in position coordinate system:
const VectorD<int64_t, 3> x_pos = const Vec3 x_pos = next_pos + (cn_dot_pn * pn) / pn_norm2_squared;
next_pos + (cn_dot_pn * pn) / pn_norm2_squared;
const uint64_t cx_norm2_squared = (tip_pos - x_pos).SquaredNorm(); const uint64_t cx_norm2_squared = (tip_pos - x_pos).SquaredNorm();
// Compute vector CX_UV in the uv space by rotating vector PN_UV by 90 // Compute vector CX_UV in the uv space by rotating vector PN_UV by 90
@ -182,7 +200,7 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
// //
// cx_uv = CX.Norm2() * PN.Norm2() * Rot(PN_UV) // cx_uv = CX.Norm2() * PN.Norm2() * Rot(PN_UV)
// //
VectorD<int64_t, 2> cx_uv(pn_uv[1], -pn_uv[0]); // Rotated PN_UV. Vec2 cx_uv(pn_uv[1], -pn_uv[0]); // Rotated PN_UV.
// Compute CX.Norm2() * PN.Norm2() // Compute CX.Norm2() * PN.Norm2()
const uint64_t norm_squared = const uint64_t norm_squared =
IntSqrt(cx_norm2_squared * pn_norm2_squared); IntSqrt(cx_norm2_squared * pn_norm2_squared);
@ -191,17 +209,15 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
// Predicted uv coordinate is then computed by either adding or // Predicted uv coordinate is then computed by either adding or
// subtracting CX_UV to/from X_UV. // subtracting CX_UV to/from X_UV.
VectorD<int64_t, 2> predicted_uv; Vec2 predicted_uv;
if (is_encoder_t) { if (is_encoder_t) {
// When encoding, compute both possible vectors and determine which one // When encoding, compute both possible vectors and determine which one
// results in a better prediction. // results in a better prediction.
// Both vectors need to be transformed back from the scaled space to // Both vectors need to be transformed back from the scaled space to
// the real UV coordinate space. // the real UV coordinate space.
const VectorD<int64_t, 2> predicted_uv_0((x_uv + cx_uv) / const Vec2 predicted_uv_0((x_uv + cx_uv) / pn_norm2_squared);
pn_norm2_squared); const Vec2 predicted_uv_1((x_uv - cx_uv) / pn_norm2_squared);
const VectorD<int64_t, 2> predicted_uv_1((x_uv - cx_uv) / const Vec2 c_uv = GetTexCoordForEntryId(data_id, data);
pn_norm2_squared);
const VectorD<int64_t, 2> c_uv = GetTexCoordForEntryId(data_id, data);
if ((c_uv - predicted_uv_0).SquaredNorm() < if ((c_uv - predicted_uv_0).SquaredNorm() <
(c_uv - predicted_uv_1).SquaredNorm()) { (c_uv - predicted_uv_1).SquaredNorm()) {
predicted_uv = predicted_uv_0; predicted_uv = predicted_uv_0;
@ -217,10 +233,12 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
} }
const bool orientation = orientations_.back(); const bool orientation = orientations_.back();
orientations_.pop_back(); orientations_.pop_back();
// Perform operations in unsigned type to avoid signed integer overflow.
// Note that the result will be the same (for non-overflowing values).
if (orientation) { if (orientation) {
predicted_uv = (x_uv + cx_uv) / pn_norm2_squared; predicted_uv = Vec2(Vec2u(x_uv) + Vec2u(cx_uv)) / pn_norm2_squared;
} else { } else {
predicted_uv = (x_uv - cx_uv) / pn_norm2_squared; predicted_uv = Vec2(Vec2u(x_uv) - Vec2u(cx_uv)) / pn_norm2_squared;
} }
} }
predicted_value_[0] = static_cast<int>(predicted_uv[0]); predicted_value_[0] = static_cast<int>(predicted_uv[0]);

View File

@ -18,22 +18,58 @@ namespace draco {
PredictionSchemeMethod SelectPredictionMethod( PredictionSchemeMethod SelectPredictionMethod(
int att_id, const PointCloudEncoder *encoder) { int att_id, const PointCloudEncoder *encoder) {
if (encoder->options()->GetSpeed() >= 10) { return SelectPredictionMethod(att_id, *encoder->options(), encoder);
}
PredictionSchemeMethod SelectPredictionMethod(
int att_id, const EncoderOptions &options,
const PointCloudEncoder *encoder) {
if (options.GetSpeed() >= 10) {
// Selected fastest, though still doing some compression. // Selected fastest, though still doing some compression.
return PREDICTION_DIFFERENCE; return PREDICTION_DIFFERENCE;
} }
if (encoder->GetGeometryType() == TRIANGULAR_MESH) { if (encoder->GetGeometryType() == TRIANGULAR_MESH) {
// Use speed setting to select the best encoding method. // Use speed setting to select the best encoding method.
const int att_quant =
options.GetAttributeInt(att_id, "quantization_bits", -1);
const PointAttribute *const att = encoder->point_cloud()->attribute(att_id); const PointAttribute *const att = encoder->point_cloud()->attribute(att_id);
if (att->attribute_type() == GeometryAttribute::TEX_COORD) { if (att_quant != -1 &&
if (encoder->options()->GetSpeed() < 4) { att->attribute_type() == GeometryAttribute::TEX_COORD &&
att->num_components() == 2) {
// Texture coordinate predictor needs a position attribute that is either
// integer or quantized. For numerical reasons, we require the position
// quantization to be at most 21 bits and the 2*position_quantization +
// uv_quantization < 64 (TODO(b/231259902)).
const PointAttribute *const pos_att =
encoder->point_cloud()->GetNamedAttribute(
GeometryAttribute::POSITION);
bool is_pos_att_valid = false;
if (pos_att) {
if (IsDataTypeIntegral(pos_att->data_type())) {
is_pos_att_valid = true;
} else {
// Check quantization of the position attribute.
const int pos_att_id = encoder->point_cloud()->GetNamedAttributeId(
GeometryAttribute::POSITION);
const int pos_quant =
options.GetAttributeInt(pos_att_id, "quantization_bits", -1);
// Must be quantized but the quantization is restricted to 21 bits and
// 2*|pos_quant|+|att_quant| must be smaller than 64 bits.
if (pos_quant > 0 && pos_quant <= 21 &&
2 * pos_quant + att_quant < 64) {
is_pos_att_valid = true;
}
}
}
if (is_pos_att_valid && options.GetSpeed() < 4) {
// Use texture coordinate prediction for speeds 0, 1, 2, 3. // Use texture coordinate prediction for speeds 0, 1, 2, 3.
return MESH_PREDICTION_TEX_COORDS_PORTABLE; return MESH_PREDICTION_TEX_COORDS_PORTABLE;
} }
} }
if (att->attribute_type() == GeometryAttribute::NORMAL) { if (att->attribute_type() == GeometryAttribute::NORMAL) {
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED #ifdef DRACO_NORMAL_ENCODING_SUPPORTED
if (encoder->options()->GetSpeed() < 4) { if (options.GetSpeed() < 4) {
// Use geometric normal prediction for speeds 0, 1, 2, 3. // Use geometric normal prediction for speeds 0, 1, 2, 3.
// For this prediction, the position attribute needs to be either // For this prediction, the position attribute needs to be either
// integer or quantized as well. // integer or quantized as well.
@ -43,8 +79,8 @@ PredictionSchemeMethod SelectPredictionMethod(
encoder->point_cloud()->GetNamedAttribute( encoder->point_cloud()->GetNamedAttribute(
GeometryAttribute::POSITION); GeometryAttribute::POSITION);
if (pos_att && (IsDataTypeIntegral(pos_att->data_type()) || if (pos_att && (IsDataTypeIntegral(pos_att->data_type()) ||
encoder->options()->GetAttributeInt( options.GetAttributeInt(pos_att_id, "quantization_bits",
pos_att_id, "quantization_bits", -1) > 0)) { -1) > 0)) {
return MESH_PREDICTION_GEOMETRIC_NORMAL; return MESH_PREDICTION_GEOMETRIC_NORMAL;
} }
} }
@ -52,11 +88,10 @@ PredictionSchemeMethod SelectPredictionMethod(
return PREDICTION_DIFFERENCE; // default return PREDICTION_DIFFERENCE; // default
} }
// Handle other attribute types. // Handle other attribute types.
if (encoder->options()->GetSpeed() >= 8) { if (options.GetSpeed() >= 8) {
return PREDICTION_DIFFERENCE; return PREDICTION_DIFFERENCE;
} }
if (encoder->options()->GetSpeed() >= 2 || if (options.GetSpeed() >= 2 || encoder->point_cloud()->num_points() < 40) {
encoder->point_cloud()->num_points() < 40) {
// Parallelogram prediction is used for speeds 2 - 7 or when the overhead // Parallelogram prediction is used for speeds 2 - 7 or when the overhead
// of using constrained multi-parallelogram would be too high. // of using constrained multi-parallelogram would be too high.
return MESH_PREDICTION_PARALLELOGRAM; return MESH_PREDICTION_PARALLELOGRAM;

View File

@ -38,6 +38,10 @@ namespace draco {
PredictionSchemeMethod SelectPredictionMethod(int att_id, PredictionSchemeMethod SelectPredictionMethod(int att_id,
const PointCloudEncoder *encoder); const PointCloudEncoder *encoder);
PredictionSchemeMethod SelectPredictionMethod(int att_id,
const EncoderOptions &options,
const PointCloudEncoder *encoder);
// Factory class for creating mesh prediction schemes. // Factory class for creating mesh prediction schemes.
template <typename DataTypeT> template <typename DataTypeT>
struct MeshPredictionSchemeEncoderFactory { struct MeshPredictionSchemeEncoderFactory {
@ -97,10 +101,11 @@ CreatePredictionSchemeForEncoder(PredictionSchemeMethod method, int att_id,
// template nature of the prediction schemes). // template nature of the prediction schemes).
const MeshEncoder *const mesh_encoder = const MeshEncoder *const mesh_encoder =
static_cast<const MeshEncoder *>(encoder); static_cast<const MeshEncoder *>(encoder);
const uint16_t bitstream_version = kDracoMeshBitstreamVersion;
auto ret = CreateMeshPredictionScheme< auto ret = CreateMeshPredictionScheme<
MeshEncoder, PredictionSchemeEncoder<DataTypeT, TransformT>, MeshEncoder, PredictionSchemeEncoder<DataTypeT, TransformT>,
MeshPredictionSchemeEncoderFactory<DataTypeT>>( MeshPredictionSchemeEncoderFactory<DataTypeT>>(
mesh_encoder, method, att_id, transform, kDracoMeshBitstreamVersion); mesh_encoder, method, att_id, transform, bitstream_version);
if (ret) { if (ret) {
return ret; return ret;
} }

View File

@ -21,6 +21,7 @@
#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h" #include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h"
#include "draco/core/decoder_buffer.h" #include "draco/core/decoder_buffer.h"
#include "draco/core/macros.h" #include "draco/core/macros.h"
#include "draco/core/math_utils.h"
#include "draco/core/vector_d.h" #include "draco/core/vector_d.h"
namespace draco { namespace draco {
@ -98,9 +99,8 @@ class PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform
if (!pred_is_in_bottom_left) { if (!pred_is_in_bottom_left) {
pred = this->RotatePoint(pred, rotation_count); pred = this->RotatePoint(pred, rotation_count);
} }
Point2 orig = pred + corr; Point2 orig(this->ModMax(AddAsUnsigned(pred[0], corr[0])),
orig[0] = this->ModMax(orig[0]); this->ModMax(AddAsUnsigned(pred[1], corr[1])));
orig[1] = this->ModMax(orig[1]);
if (!pred_is_in_bottom_left) { if (!pred_is_in_bottom_left) {
const int32_t reverse_rotation_count = (4 - rotation_count) % 4; const int32_t reverse_rotation_count = (4 - rotation_count) % 4;
orig = this->RotatePoint(orig, reverse_rotation_count); orig = this->RotatePoint(orig, reverse_rotation_count);

View File

@ -25,10 +25,10 @@ class PredictionSchemeNormalOctahedronCanonicalizedTransformTest
Transform; Transform;
typedef Transform::Point2 Point2; typedef Transform::Point2 Point2;
void TestComputeCorrection(const Transform &transform, const int32_t &ox, void TestComputeCorrection(const Transform &transform, const int32_t ox,
const int32_t &oy, const int32_t &px, const int32_t oy, const int32_t px,
const int32_t &py, const int32_t &cx, const int32_t py, const int32_t cx,
const int32_t &cy) { const int32_t cy) {
const int32_t o[2] = {ox + 7, oy + 7}; const int32_t o[2] = {ox + 7, oy + 7};
const int32_t p[2] = {px + 7, py + 7}; const int32_t p[2] = {px + 7, py + 7};
int32_t corr[2] = {500, 500}; int32_t corr[2] = {500, 500};
@ -38,7 +38,7 @@ class PredictionSchemeNormalOctahedronCanonicalizedTransformTest
} }
void TestGetRotationCount(const Transform &transform, const Point2 &pred, void TestGetRotationCount(const Transform &transform, const Point2 &pred,
const int32_t &rot_dir) { const int32_t rot_dir) {
const int32_t rotation_count = transform.GetRotationCount(pred); const int32_t rotation_count = transform.GetRotationCount(pred);
ASSERT_EQ(rot_dir, rotation_count); ASSERT_EQ(rot_dir, rotation_count);
} }

View File

@ -80,19 +80,31 @@ class PredictionSchemeNormalOctahedronDecodingTransform
private: private:
Point2 ComputeOriginalValue(Point2 pred, const Point2 &corr) const { Point2 ComputeOriginalValue(Point2 pred, const Point2 &corr) const {
const Point2 t(this->center_value(), this->center_value()); const Point2 t(this->center_value(), this->center_value());
pred = pred - t; typedef typename std::make_unsigned<DataTypeT>::type UnsignedDataTypeT;
typedef VectorD<UnsignedDataTypeT, 2> Point2u;
// Perform the addition in unsigned type to avoid signed integer overflow.
// Note that the result will be the same (for non-overflowing values).
pred = Point2(Point2u(pred) - Point2u(t));
const bool pred_is_in_diamond = this->IsInDiamond(pred[0], pred[1]); const bool pred_is_in_diamond = this->IsInDiamond(pred[0], pred[1]);
if (!pred_is_in_diamond) { if (!pred_is_in_diamond) {
this->InvertDiamond(&pred[0], &pred[1]); this->InvertDiamond(&pred[0], &pred[1]);
} }
Point2 orig = pred + corr;
// Perform the addition in unsigned type to avoid signed integer overflow.
// Note that the result will be the same (for non-overflowing values).
Point2 orig(Point2u(pred) + Point2u(corr));
orig[0] = this->ModMax(orig[0]); orig[0] = this->ModMax(orig[0]);
orig[1] = this->ModMax(orig[1]); orig[1] = this->ModMax(orig[1]);
if (!pred_is_in_diamond) { if (!pred_is_in_diamond) {
this->InvertDiamond(&orig[0], &orig[1]); this->InvertDiamond(&orig[0], &orig[1]);
} }
orig = orig + t;
// Perform the addition in unsigned type to avoid signed integer overflow.
// Note that the result will be the same (for non-overflowing values).
orig = Point2(Point2u(orig) + Point2u(t));
return orig; return orig;
} }
}; };

View File

@ -23,10 +23,10 @@ class PredictionSchemeNormalOctahedronTransformTest : public ::testing::Test {
Transform; Transform;
typedef Transform::Point2 Point2; typedef Transform::Point2 Point2;
void TestComputeCorrection(const Transform &transform, const int32_t &ox, void TestComputeCorrection(const Transform &transform, const int32_t ox,
const int32_t &oy, const int32_t &px, const int32_t oy, const int32_t px,
const int32_t &py, const int32_t &cx, const int32_t py, const int32_t cx,
const int32_t &cy) { const int32_t cy) {
const int32_t o[2] = {ox + 7, oy + 7}; const int32_t o[2] = {ox + 7, oy + 7};
const int32_t p[2] = {px + 7, py + 7}; const int32_t p[2] = {px + 7, py + 7};
int32_t corr[2] = {500, 500}; int32_t corr[2] = {500, 500};

View File

@ -70,10 +70,10 @@ class PredictionSchemeWrapTransformBase {
clamped_value_[i] = predicted_val[i]; clamped_value_[i] = predicted_val[i];
} }
} }
return &clamped_value_[0]; return clamped_value_.data();
} }
// TODO(hemmer): Consider refactoring to avoid this dummy. // TODO(b/199760123): Consider refactoring to avoid this dummy.
int quantization_bits() const { int quantization_bits() const {
DRACO_DCHECK(false); DRACO_DCHECK(false);
return -1; return -1;

View File

@ -148,11 +148,12 @@ bool SequentialIntegerAttributeDecoder::DecodeIntegerValues(
return false; return false;
} }
for (size_t i = 0; i < num_values; ++i) { for (size_t i = 0; i < num_values; ++i) {
if (!in_buffer->Decode(portable_attribute_data + i, num_bytes)) if (!in_buffer->Decode(portable_attribute_data + i, num_bytes)) {
return false; return false;
} }
} }
} }
}
if (num_values > 0 && (prediction_scheme_ == nullptr || if (num_values > 0 && (prediction_scheme_ == nullptr ||
!prediction_scheme_->AreCorrectionsPositive())) { !prediction_scheme_->AreCorrectionsPositive())) {
@ -228,12 +229,13 @@ void SequentialIntegerAttributeDecoder::StoreTypedValues(uint32_t num_values) {
void SequentialIntegerAttributeDecoder::PreparePortableAttribute( void SequentialIntegerAttributeDecoder::PreparePortableAttribute(
int num_entries, int num_components) { int num_entries, int num_components) {
GeometryAttribute va; GeometryAttribute ga;
va.Init(attribute()->attribute_type(), nullptr, num_components, DT_INT32, ga.Init(attribute()->attribute_type(), nullptr, num_components, DT_INT32,
false, num_components * DataTypeLength(DT_INT32), 0); false, num_components * DataTypeLength(DT_INT32), 0);
std::unique_ptr<PointAttribute> port_att(new PointAttribute(va)); std::unique_ptr<PointAttribute> port_att(new PointAttribute(ga));
port_att->SetIdentityMapping(); port_att->SetIdentityMapping();
port_att->Reset(num_entries); port_att->Reset(num_entries);
port_att->set_unique_id(attribute()->unique_id());
SetPortableAttribute(std::move(port_att)); SetPortableAttribute(std::move(port_att));
} }

View File

@ -138,9 +138,11 @@ bool SequentialIntegerAttributeEncoder::EncodeValues(
// All integer values are initialized. Process them using the prediction // All integer values are initialized. Process them using the prediction
// scheme if we have one. // scheme if we have one.
if (prediction_scheme_) { if (prediction_scheme_) {
prediction_scheme_->ComputeCorrectionValues( if (!prediction_scheme_->ComputeCorrectionValues(
portable_attribute_data, &encoded_data[0], num_values, num_components, portable_attribute_data, &encoded_data[0], num_values,
point_ids.data()); num_components, point_ids.data())) {
return false;
}
} }
if (prediction_scheme_ == nullptr || if (prediction_scheme_ == nullptr ||

View File

@ -20,8 +20,9 @@ namespace draco {
bool SequentialNormalAttributeEncoder::Init(PointCloudEncoder *encoder, bool SequentialNormalAttributeEncoder::Init(PointCloudEncoder *encoder,
int attribute_id) { int attribute_id) {
if (!SequentialIntegerAttributeEncoder::Init(encoder, attribute_id)) if (!SequentialIntegerAttributeEncoder::Init(encoder, attribute_id)) {
return false; return false;
}
// Currently this encoder works only for 3-component normal vectors. // Currently this encoder works only for 3-component normal vectors.
if (attribute()->num_components() != 3) { if (attribute()->num_components() != 3) {
return false; return false;

View File

@ -47,14 +47,13 @@ class DirectBitDecoder {
// Decode the next |nbits| and return the sequence in |value|. |nbits| must be // Decode the next |nbits| and return the sequence in |value|. |nbits| must be
// > 0 and <= 32. // > 0 and <= 32.
void DecodeLeastSignificantBits32(int nbits, uint32_t *value) { bool DecodeLeastSignificantBits32(int nbits, uint32_t *value) {
DRACO_DCHECK_EQ(true, nbits <= 32); DRACO_DCHECK_EQ(true, nbits <= 32);
DRACO_DCHECK_EQ(true, nbits > 0); DRACO_DCHECK_EQ(true, nbits > 0);
const int remaining = 32 - num_used_bits_; const int remaining = 32 - num_used_bits_;
if (nbits <= remaining) { if (nbits <= remaining) {
if (pos_ == bits_.end()) { if (pos_ == bits_.end()) {
*value = 0; return false;
return;
} }
*value = (*pos_ << num_used_bits_) >> (32 - nbits); *value = (*pos_ << num_used_bits_) >> (32 - nbits);
num_used_bits_ += nbits; num_used_bits_ += nbits;
@ -64,8 +63,7 @@ class DirectBitDecoder {
} }
} else { } else {
if (pos_ + 1 == bits_.end()) { if (pos_ + 1 == bits_.end()) {
*value = 0; return false;
return;
} }
const uint32_t value_l = ((*pos_) << num_used_bits_); const uint32_t value_l = ((*pos_) << num_used_bits_);
num_used_bits_ = nbits - remaining; num_used_bits_ = nbits - remaining;
@ -73,6 +71,7 @@ class DirectBitDecoder {
const uint32_t value_r = (*pos_) >> (32 - num_used_bits_); const uint32_t value_r = (*pos_) >> (32 - num_used_bits_);
*value = (value_l >> (32 - num_used_bits_ - remaining)) | value_r; *value = (value_l >> (32 - num_used_bits_ - remaining)) | value_r;
} }
return true;
} }
void EndDecoding() {} void EndDecoding() {}

View File

@ -65,6 +65,10 @@ class EncoderOptionsBase : public DracoOptions<AttributeKeyT> {
this->SetGlobalInt("encoding_speed", encoding_speed); this->SetGlobalInt("encoding_speed", encoding_speed);
this->SetGlobalInt("decoding_speed", decoding_speed); this->SetGlobalInt("decoding_speed", decoding_speed);
} }
bool IsSpeedSet() const {
return this->IsGlobalOptionSet("encoding_speed") ||
this->IsGlobalOptionSet("decoding_speed");
}
// Sets a given feature as supported or unsupported by the target decoder. // Sets a given feature as supported or unsupported by the target decoder.
// Encoder will always use only supported features when encoding the input // Encoder will always use only supported features when encoding the input

View File

@ -17,9 +17,11 @@
#include <cinttypes> #include <cinttypes>
#include <sstream> #include <sstream>
#include "draco/compression/encode.h"
#include "draco/core/draco_test_base.h" #include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h" #include "draco/core/draco_test_utils.h"
#include "draco/io/file_utils.h" #include "draco/io/file_utils.h"
#include "draco/io/obj_encoder.h"
namespace { namespace {
@ -166,4 +168,78 @@ TEST_F(DecodeTest, TestSkipAttributeTransformWithNoQuantization) {
ASSERT_EQ(pos_att->GetAttributeTransformData(), nullptr); ASSERT_EQ(pos_att->GetAttributeTransformData(), nullptr);
} }
TEST_F(DecodeTest, TestSkipAttributeTransformUniqueId) {
// Tests that decoders preserve unique id of attributes even when their
// attribute transforms are skipped.
const std::string file_name = "cube_att.obj";
auto src_mesh = draco::ReadMeshFromTestFile(file_name);
ASSERT_NE(src_mesh, nullptr);
constexpr int kPosUniqueId = 7;
constexpr int kNormUniqueId = 42;
// Set unique ids for some of the attributes.
src_mesh
->attribute(
src_mesh->GetNamedAttributeId(draco::GeometryAttribute::POSITION))
->set_unique_id(kPosUniqueId);
src_mesh
->attribute(
src_mesh->GetNamedAttributeId(draco::GeometryAttribute::NORMAL))
->set_unique_id(kNormUniqueId);
draco::EncoderBuffer encoder_buffer;
draco::Encoder encoder;
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 10);
encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 11);
encoder.EncodeMeshToBuffer(*src_mesh, &encoder_buffer);
// Create a draco decoding buffer.
draco::DecoderBuffer buffer;
buffer.Init(encoder_buffer.data(), encoder_buffer.size());
// First we decode the mesh without skipping the attribute transforms.
draco::Decoder decoder_no_skip;
std::unique_ptr<draco::Mesh> mesh_no_skip =
decoder_no_skip.DecodeMeshFromBuffer(&buffer).value();
ASSERT_NE(mesh_no_skip, nullptr);
// Now we decode it again while skipping some attributes.
draco::Decoder decoder_skip;
// Make sure we skip dequantization for the position and normal attribute.
decoder_skip.SetSkipAttributeTransform(draco::GeometryAttribute::POSITION);
decoder_skip.SetSkipAttributeTransform(draco::GeometryAttribute::NORMAL);
// Decode the input data into a geometry.
buffer.Init(encoder_buffer.data(), encoder_buffer.size());
std::unique_ptr<draco::Mesh> mesh_skip =
decoder_skip.DecodeMeshFromBuffer(&buffer).value();
ASSERT_NE(mesh_skip, nullptr);
// Compare the unique ids.
const draco::PointAttribute *const pos_att_no_skip =
mesh_no_skip->GetNamedAttribute(draco::GeometryAttribute::POSITION);
ASSERT_NE(pos_att_no_skip, nullptr);
ASSERT_EQ(pos_att_no_skip->data_type(), draco::DataType::DT_FLOAT32);
const draco::PointAttribute *const pos_att_skip =
mesh_skip->GetNamedAttribute(draco::GeometryAttribute::POSITION);
ASSERT_NE(pos_att_skip, nullptr);
ASSERT_EQ(pos_att_skip->data_type(), draco::DataType::DT_INT32);
const draco::PointAttribute *const norm_att_no_skip =
mesh_no_skip->GetNamedAttribute(draco::GeometryAttribute::NORMAL);
ASSERT_NE(norm_att_no_skip, nullptr);
ASSERT_EQ(norm_att_no_skip->data_type(), draco::DataType::DT_FLOAT32);
const draco::PointAttribute *const norm_att_skip =
mesh_skip->GetNamedAttribute(draco::GeometryAttribute::NORMAL);
ASSERT_NE(norm_att_skip, nullptr);
ASSERT_EQ(norm_att_skip->data_type(), draco::DataType::DT_INT32);
ASSERT_EQ(pos_att_skip->unique_id(), pos_att_no_skip->unique_id());
ASSERT_EQ(norm_att_skip->unique_id(), norm_att_no_skip->unique_id());
std::cout << pos_att_skip->unique_id() << " " << norm_att_skip->unique_id()
<< std::endl;
}
} // namespace } // namespace

View File

@ -0,0 +1,59 @@
// Copyright 2022 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/compression/draco_compression_options.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
namespace draco {
SpatialQuantizationOptions::SpatialQuantizationOptions(int quantization_bits) {
SetQuantizationBits(quantization_bits);
}
void SpatialQuantizationOptions::SetQuantizationBits(int quantization_bits) {
mode_ = LOCAL_QUANTIZATION_BITS;
quantization_bits_ = quantization_bits;
}
bool SpatialQuantizationOptions::AreQuantizationBitsDefined() const {
return mode_ == LOCAL_QUANTIZATION_BITS;
}
SpatialQuantizationOptions &SpatialQuantizationOptions::SetGrid(float spacing) {
mode_ = GLOBAL_GRID;
spacing_ = spacing;
return *this;
}
bool SpatialQuantizationOptions::operator==(
const SpatialQuantizationOptions &other) const {
if (mode_ != other.mode_) {
return false;
}
if (mode_ == LOCAL_QUANTIZATION_BITS) {
if (quantization_bits_ != other.quantization_bits_) {
return false;
}
} else if (mode_ == GLOBAL_GRID) {
if (spacing_ != other.spacing_) {
return false;
}
}
return true;
}
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -0,0 +1,141 @@
// Copyright 2019 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_COMPRESSION_DRACO_COMPRESSION_OPTIONS_H_
#define DRACO_COMPRESSION_DRACO_COMPRESSION_OPTIONS_H_
#include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include "draco/core/status.h"
namespace draco {
// Quantization options for positions. Currently there are two modes for
// quantizing positions:
//
// 1. Quantization bits:
// - User defined number of quantization bits that is evenly distributed
// to cover the compressed geometry.
// 2. Grid:
// - Positions are snapped to a global grid defined by grid spacing.
// - This method is primarily intended to be used when the location of
// quantized vertices needs to be consistent between multiple
// geometries.
class SpatialQuantizationOptions {
public:
explicit SpatialQuantizationOptions(int quantization_bits);
// Sets quantization bits that are going to be used for the compressed
// geometry. If the geometry is a scene, the same number of quantization bits
// is going to be applied to each mesh of the scene. Quantized values are
// going to be distributed within the bounds of individual meshes.
void SetQuantizationBits(int quantization_bits);
// If this returns true, quantization_bits() should be used to get the
// desired number of quantization bits for compression. Otherwise the grid
// mode is selected and spacing() should be used to get the desired grid
// spacing.
bool AreQuantizationBitsDefined() const;
const int quantization_bits() const { return quantization_bits_; }
// Defines quantization grid used for the compressed geometry. All vertices
// are going to be snapped to the nearest grid vertex that corresponds to an
// integer quantized position. |spacing| defines the distance between two grid
// vertices. E.g. a grid with |spacing| = 10 would have grid vertices at
// locations {10 * i, 10 * j, 10 * k} where i, j, k are integer numbers.
SpatialQuantizationOptions &SetGrid(float spacing);
const float spacing() const { return spacing_; }
bool operator==(const SpatialQuantizationOptions &other) const;
private:
enum Mode { LOCAL_QUANTIZATION_BITS, GLOBAL_GRID };
Mode mode_ = LOCAL_QUANTIZATION_BITS;
int quantization_bits_; // Default quantization bits for positions.
float spacing_ = 0.f;
};
// TODO(fgalligan): Add support for unified_position_quantization.
// Struct to hold Draco compression options.
struct DracoCompressionOptions {
int compression_level = 7; // compression level [0-10], most=10, least=0.
SpatialQuantizationOptions quantization_position{11};
int quantization_bits_normal = 8;
int quantization_bits_tex_coord = 10;
int quantization_bits_color = 8;
int quantization_bits_generic = 8;
int quantization_bits_tangent = 8;
int quantization_bits_weight = 8;
bool find_non_degenerate_texture_quantization = false;
bool operator==(const DracoCompressionOptions &other) const {
return compression_level == other.compression_level &&
quantization_position == other.quantization_position &&
quantization_bits_normal == other.quantization_bits_normal &&
quantization_bits_tex_coord == other.quantization_bits_tex_coord &&
quantization_bits_color == other.quantization_bits_color &&
quantization_bits_generic == other.quantization_bits_generic &&
quantization_bits_tangent == other.quantization_bits_tangent &&
quantization_bits_weight == other.quantization_bits_weight &&
find_non_degenerate_texture_quantization ==
other.find_non_degenerate_texture_quantization;
}
bool operator!=(const DracoCompressionOptions &other) const {
return !(*this == other);
}
Status Check() const {
DRACO_RETURN_IF_ERROR(
Validate("Compression level", compression_level, 0, 10));
if (quantization_position.AreQuantizationBitsDefined()) {
DRACO_RETURN_IF_ERROR(Validate("Position quantization",
quantization_position.quantization_bits(),
0, 30));
} else {
if (quantization_position.spacing() <= 0.f) {
return ErrorStatus("Position quantization spacing is invalid.");
}
}
DRACO_RETURN_IF_ERROR(
Validate("Normals quantization", quantization_bits_normal, 0, 30));
DRACO_RETURN_IF_ERROR(
Validate("Tex coord quantization", quantization_bits_tex_coord, 0, 30));
DRACO_RETURN_IF_ERROR(
Validate("Color quantization", quantization_bits_color, 0, 30));
DRACO_RETURN_IF_ERROR(
Validate("Generic quantization", quantization_bits_generic, 0, 30));
DRACO_RETURN_IF_ERROR(
Validate("Tangent quantization", quantization_bits_tangent, 0, 30));
DRACO_RETURN_IF_ERROR(
Validate("Weights quantization", quantization_bits_weight, 0, 30));
return OkStatus();
}
static Status Validate(const std::string &name, int value, int min, int max) {
if (value < min || value > max) {
const std::string range =
"[" + std::to_string(min) + "-" + std::to_string(max) + "].";
return Status(Status::DRACO_ERROR, name + " is out of range " + range);
}
return OkStatus();
}
};
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED
#endif // DRACO_COMPRESSION_DRACO_COMPRESSION_OPTIONS_H_

View File

@ -0,0 +1,45 @@
#include "draco/compression/draco_compression_options.h"
#include "draco/core/draco_test_utils.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
namespace {
TEST(DracoCompressionOptionsTest, TestPositionQuantizationBits) {
// Test verifies that we can define draco compression options using
// quantization bits.
draco::SpatialQuantizationOptions options(10);
// Quantization bits should be used by default.
ASSERT_TRUE(options.AreQuantizationBitsDefined());
ASSERT_EQ(options.quantization_bits(), 10);
// Change the quantization bits.
options.SetQuantizationBits(9);
ASSERT_TRUE(options.AreQuantizationBitsDefined());
ASSERT_EQ(options.quantization_bits(), 9);
// If we select the grid, quantization bits should not be used.
options.SetGrid(0.5f);
ASSERT_FALSE(options.AreQuantizationBitsDefined());
}
TEST(DracoCompressionOptionsTest, TestPositionQuantizationGrid) {
// Test verifies that we can define draco compression options using
// quantization grid.
draco::SpatialQuantizationOptions options(10);
// Quantization bits should be used by default.
ASSERT_TRUE(options.AreQuantizationBitsDefined());
// Set the grid parameters.
options.SetGrid(0.25f);
ASSERT_FALSE(options.AreQuantizationBitsDefined());
ASSERT_EQ(options.spacing(), 0.25f);
}
} // namespace
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -129,7 +129,6 @@ class Encoder
// call of EncodePointCloudToBuffer or EncodeMeshToBuffer is going to fail. // call of EncodePointCloudToBuffer or EncodeMeshToBuffer is going to fail.
void SetEncodingMethod(int encoding_method); void SetEncodingMethod(int encoding_method);
protected:
// Creates encoder options for the expert encoder used during the actual // Creates encoder options for the expert encoder used during the actual
// encoding. // encoding.
EncoderOptions CreateExpertEncoderOptions(const PointCloud &pc) const; EncoderOptions CreateExpertEncoderOptions(const PointCloud &pc) const;

View File

@ -98,7 +98,7 @@ class EncoderBase {
"Invalid prediction scheme for attribute type."); "Invalid prediction scheme for attribute type.");
} }
} }
// TODO(hemmer): Try to enable more prediction schemes for normals. // TODO(b/199760123): Try to enable more prediction schemes for normals.
if (att_type == GeometryAttribute::NORMAL) { if (att_type == GeometryAttribute::NORMAL) {
if (!(prediction_scheme == PREDICTION_DIFFERENCE || if (!(prediction_scheme == PREDICTION_DIFFERENCE ||
prediction_scheme == MESH_PREDICTION_GEOMETRIC_NORMAL)) { prediction_scheme == MESH_PREDICTION_GEOMETRIC_NORMAL)) {

View File

@ -26,6 +26,7 @@
#include "draco/core/draco_test_base.h" #include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h" #include "draco/core/draco_test_utils.h"
#include "draco/core/vector_d.h" #include "draco/core/vector_d.h"
#include "draco/io/file_utils.h"
#include "draco/io/obj_decoder.h" #include "draco/io/obj_decoder.h"
#include "draco/mesh/triangle_soup_mesh_builder.h" #include "draco/mesh/triangle_soup_mesh_builder.h"
#include "draco/point_cloud/point_cloud_builder.h" #include "draco/point_cloud/point_cloud_builder.h"
@ -213,16 +214,14 @@ class EncodeTest : public ::testing::Test {
draco::Decoder decoder; draco::Decoder decoder;
if (mesh) { if (mesh) {
auto maybe_mesh = decoder.DecodeMeshFromBuffer(&decoder_buffer); DRACO_ASSIGN_OR_ASSERT(auto decoded_mesh,
ASSERT_TRUE(maybe_mesh.ok()); decoder.DecodeMeshFromBuffer(&decoder_buffer));
auto decoded_mesh = std::move(maybe_mesh).value();
ASSERT_NE(decoded_mesh, nullptr); ASSERT_NE(decoded_mesh, nullptr);
ASSERT_EQ(decoded_mesh->num_points(), encoder.num_encoded_points()); ASSERT_EQ(decoded_mesh->num_points(), encoder.num_encoded_points());
ASSERT_EQ(decoded_mesh->num_faces(), encoder.num_encoded_faces()); ASSERT_EQ(decoded_mesh->num_faces(), encoder.num_encoded_faces());
} else { } else {
auto maybe_pc = decoder.DecodePointCloudFromBuffer(&decoder_buffer); DRACO_ASSIGN_OR_ASSERT(
ASSERT_TRUE(maybe_pc.ok()); auto decoded_pc, decoder.DecodePointCloudFromBuffer(&decoder_buffer));
auto decoded_pc = std::move(maybe_pc).value();
ASSERT_EQ(decoded_pc->num_points(), encoder.num_encoded_points()); ASSERT_EQ(decoded_pc->num_points(), encoder.num_encoded_points());
} }
} }
@ -274,7 +273,7 @@ TEST_F(EncodeTest, TestLinesObj) {
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 16); encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 16);
draco::EncoderBuffer buffer; draco::EncoderBuffer buffer;
ASSERT_TRUE(encoder.EncodePointCloudToBuffer(*pc, &buffer).ok()); DRACO_ASSERT_OK(encoder.EncodePointCloudToBuffer(*pc, &buffer));
} }
TEST_F(EncodeTest, TestQuantizedInfinity) { TEST_F(EncodeTest, TestQuantizedInfinity) {
@ -315,7 +314,7 @@ TEST_F(EncodeTest, TestUnquantizedInfinity) {
encoder.SetEncodingMethod(draco::POINT_CLOUD_SEQUENTIAL_ENCODING); encoder.SetEncodingMethod(draco::POINT_CLOUD_SEQUENTIAL_ENCODING);
draco::EncoderBuffer buffer; draco::EncoderBuffer buffer;
ASSERT_TRUE(encoder.EncodePointCloudToBuffer(*pc, &buffer).ok()); DRACO_ASSERT_OK(encoder.EncodePointCloudToBuffer(*pc, &buffer));
} }
TEST_F(EncodeTest, TestQuantizedAndUnquantizedAttributes) { TEST_F(EncodeTest, TestQuantizedAndUnquantizedAttributes) {
@ -330,7 +329,7 @@ TEST_F(EncodeTest, TestQuantizedAndUnquantizedAttributes) {
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 11); encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 11);
encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 0); encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 0);
draco::EncoderBuffer buffer; draco::EncoderBuffer buffer;
ASSERT_TRUE(encoder.EncodePointCloudToBuffer(*pc, &buffer).ok()); DRACO_ASSERT_OK(encoder.EncodePointCloudToBuffer(*pc, &buffer));
} }
TEST_F(EncodeTest, TestKdTreeEncoding) { TEST_F(EncodeTest, TestKdTreeEncoding) {
@ -348,7 +347,7 @@ TEST_F(EncodeTest, TestKdTreeEncoding) {
// Now set quantization for the position attribute which should make // Now set quantization for the position attribute which should make
// the encoder happy. // the encoder happy.
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 16); encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 16);
ASSERT_TRUE(encoder.EncodePointCloudToBuffer(*pc, &buffer).ok()); DRACO_ASSERT_OK(encoder.EncodePointCloudToBuffer(*pc, &buffer));
} }
TEST_F(EncodeTest, TestTrackingOfNumberOfEncodedEntries) { TEST_F(EncodeTest, TestTrackingOfNumberOfEncodedEntries) {
@ -373,7 +372,7 @@ TEST_F(EncodeTest, TestTrackingOfNumberOfEncodedEntriesNotSet) {
draco::EncoderBuffer buffer; draco::EncoderBuffer buffer;
draco::Encoder encoder; draco::Encoder encoder;
ASSERT_TRUE(encoder.EncodeMeshToBuffer(*mesh, &buffer).ok()); DRACO_ASSERT_OK(encoder.EncodeMeshToBuffer(*mesh, &buffer));
ASSERT_EQ(encoder.num_encoded_points(), 0); ASSERT_EQ(encoder.num_encoded_points(), 0);
ASSERT_EQ(encoder.num_encoded_faces(), 0); ASSERT_EQ(encoder.num_encoded_faces(), 0);
} }
@ -404,4 +403,170 @@ TEST_F(EncodeTest, TestNoPosQuantizationNormalCoding) {
ASSERT_NE(decoded_mesh, nullptr); ASSERT_NE(decoded_mesh, nullptr);
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
TEST_F(EncodeTest, TestDracoCompressionOptions) {
// This test verifies that we can set the encoder's compression options via
// draco::Mesh's compression options.
const auto mesh = draco::ReadMeshFromTestFile("test_nm.obj");
ASSERT_NE(mesh, nullptr);
// First set compression level and quantization manually.
draco::Encoder encoder_manual;
draco::EncoderBuffer buffer_manual;
encoder_manual.SetAttributeQuantization(draco::GeometryAttribute::POSITION,
8);
encoder_manual.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 7);
encoder_manual.SetSpeedOptions(4, 4);
DRACO_ASSERT_OK(encoder_manual.EncodeMeshToBuffer(*mesh, &buffer_manual));
// Now do the same with options provided via DracoCompressionOptions.
draco::DracoCompressionOptions compression_options;
compression_options.compression_level = 6;
compression_options.quantization_position.SetQuantizationBits(8);
compression_options.quantization_bits_normal = 7;
mesh->SetCompressionOptions(compression_options);
mesh->SetCompressionEnabled(true);
draco::Encoder encoder_auto;
draco::EncoderBuffer buffer_auto;
DRACO_ASSERT_OK(encoder_auto.EncodeMeshToBuffer(*mesh, &buffer_auto));
// Ensure that both encoders produce the same result.
ASSERT_EQ(buffer_manual.size(), buffer_auto.size());
// Now change some of the mesh's compression settings and ensure the
// compression changes as well.
compression_options.compression_level = 7;
mesh->SetCompressionOptions(compression_options);
buffer_auto.Clear();
DRACO_ASSERT_OK(encoder_auto.EncodeMeshToBuffer(*mesh, &buffer_auto));
ASSERT_NE(buffer_manual.size(), buffer_auto.size());
// Check that |mesh| compression options do not override the encoder options.
mesh->GetCompressionOptions().compression_level = 10;
mesh->GetCompressionOptions().quantization_position.SetQuantizationBits(10);
mesh->GetCompressionOptions().quantization_bits_normal = 10;
draco::EncoderBuffer buffer;
DRACO_ASSERT_OK(encoder_manual.EncodeMeshToBuffer(*mesh, &buffer));
ASSERT_EQ(buffer.size(), buffer_manual.size());
}
TEST_F(EncodeTest, TestDracoCompressionOptionsManualOverride) {
// This test verifies that we can use encoder's option to override compression
// options provided in draco::Mesh's compression options.
const auto mesh = draco::ReadMeshFromTestFile("test_nm.obj");
ASSERT_NE(mesh, nullptr);
// Set some compression options.
draco::DracoCompressionOptions compression_options;
compression_options.compression_level = 6;
compression_options.quantization_position.SetQuantizationBits(8);
compression_options.quantization_bits_normal = 7;
mesh->SetCompressionOptions(compression_options);
mesh->SetCompressionEnabled(true);
draco::Encoder encoder;
draco::EncoderBuffer buffer_no_override;
DRACO_ASSERT_OK(encoder.EncodeMeshToBuffer(*mesh, &buffer_no_override));
// Now override some options and ensure the compression is different.
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 5);
draco::EncoderBuffer buffer_with_override;
DRACO_ASSERT_OK(encoder.EncodeMeshToBuffer(*mesh, &buffer_with_override));
ASSERT_LT(buffer_with_override.size(), buffer_no_override.size());
}
TEST_F(EncodeTest, TestDracoCompressionOptionsGridQuantization) {
// Test verifies that we can set position quantization via grid spacing.
// 1x1x1 cube.
const auto mesh = draco::ReadMeshFromTestFile("cube_att.obj");
ASSERT_NE(mesh, nullptr);
mesh->SetCompressionEnabled(true);
// Set grid quantization for positions.
draco::DracoCompressionOptions compression_options;
// This should result in 10x10x10 quantization.
compression_options.quantization_position.SetGrid(0.1);
mesh->SetCompressionOptions(compression_options);
draco::ExpertEncoder encoder(*mesh);
draco::EncoderBuffer buffer;
DRACO_ASSERT_OK(encoder.EncodeToBuffer(&buffer));
// The grid options should be reflected in the |encoder|. Check that the
// computed values are correct.
const int pos_att_id =
mesh->GetNamedAttributeId(draco::GeometryAttribute::POSITION);
draco::Vector3f origin;
encoder.options().GetAttributeVector(pos_att_id, "quantization_origin", 3,
&origin[0]);
ASSERT_EQ(origin, draco::Vector3f(0.f, 0.f, 0.f));
// We need 4 quantization bits (for 10 values).
ASSERT_EQ(
encoder.options().GetAttributeInt(pos_att_id, "quantization_bits", -1),
4);
// The quantization range should be ((1 << quantization_bits) - 1) * spacing.
ASSERT_NEAR(encoder.options().GetAttributeFloat(pos_att_id,
"quantization_range", 0.f),
15.f * 0.1f, 1e-6f);
}
TEST_F(EncodeTest, TestDracoCompressionOptionsGridQuantizationWithOffset) {
// Test verifies that we can set position quantization via grid spacing when
// the geometry is not perfectly aligned with the quantization grid.
// 1x1x1 cube.
const auto mesh = draco::ReadMeshFromTestFile("cube_att.obj");
ASSERT_NE(mesh, nullptr);
// Move all positions a bit.
auto *pos_att = mesh->attribute(
mesh->GetNamedAttributeId(draco::GeometryAttribute::POSITION));
for (draco::AttributeValueIndex avi(0); avi < pos_att->size(); ++avi) {
draco::Vector3f pos;
pos_att->GetValue(avi, &pos[0]);
pos = pos + draco::Vector3f(-0.55f, 0.65f, 10.75f);
pos_att->SetAttributeValue(avi, &pos[0]);
}
mesh->SetCompressionEnabled(true);
// Set grid quantization for positions.
draco::DracoCompressionOptions compression_options;
// This should result in 16x16x16 quantization if the grid was perfectly
// aligned but since it is not we should expect 17 or 18 values per component.
compression_options.quantization_position.SetGrid(0.0625f);
mesh->SetCompressionOptions(compression_options);
draco::ExpertEncoder encoder(*mesh);
draco::EncoderBuffer buffer;
DRACO_ASSERT_OK(encoder.EncodeToBuffer(&buffer));
// The grid options should be reflected in the |encoder|. Check that the
// computed values are correct.
const int pos_att_id =
mesh->GetNamedAttributeId(draco::GeometryAttribute::POSITION);
draco::Vector3f origin;
encoder.options().GetAttributeVector(pos_att_id, "quantization_origin", 3,
&origin[0]);
// The origin is the first lower value on the quantization grid for each
// component of the mesh.
ASSERT_EQ(origin, draco::Vector3f(-0.5625f, 0.625f, 10.75f));
// We need 5 quantization bits (for 17-18 values).
ASSERT_EQ(
encoder.options().GetAttributeInt(pos_att_id, "quantization_bits", -1),
5);
// The quantization range should be ((1 << quantization_bits) - 1) * spacing.
ASSERT_NEAR(encoder.options().GetAttributeFloat(pos_att_id,
"quantization_range", 0.f),
31.f * 0.0625f, 1e-6f);
}
#endif // DRACO_TRANSCODER_SUPPORTED
} // namespace } // namespace

View File

@ -391,7 +391,6 @@ class RAnsEncoder {
ans_.buf[ans_.buf_offset++] = ans_.state % DRACO_ANS_IO_BASE; ans_.buf[ans_.buf_offset++] = ans_.state % DRACO_ANS_IO_BASE;
ans_.state /= DRACO_ANS_IO_BASE; ans_.state /= DRACO_ANS_IO_BASE;
} }
// TODO(ostava): The division and multiplication should be optimized.
ans_.state = ans_.state =
(ans_.state / p) * rans_precision + ans_.state % p + sym->cum_prob; (ans_.state / p) * rans_precision + ans_.state % p + sym->cum_prob;
} }

View File

@ -75,6 +75,13 @@ bool RAnsSymbolDecoder<unique_symbols_bit_length_t>::Create(
return false; return false;
} }
} }
// Check that decoded number of symbols is not unreasonably high. Remaining
// buffer size must be at least |num_symbols| / 64 bytes to contain the
// probability table. The |prob_data| below is one byte but it can be
// theoretically stored for each 64th symbol.
if (num_symbols_ / 64 > buffer->remaining_size()) {
return false;
}
probability_table_.resize(num_symbols_); probability_table_.resize(num_symbols_);
if (num_symbols_ == 0) { if (num_symbols_ == 0) {
return true; return true;

View File

@ -125,7 +125,7 @@ bool RAnsSymbolEncoder<unique_symbols_bit_length_t>::Create(
for (int i = 0; i < num_symbols; ++i) { for (int i = 0; i < num_symbols; ++i) {
sorted_probabilities[i] = i; sorted_probabilities[i] = i;
} }
std::sort(sorted_probabilities.begin(), sorted_probabilities.end(), std::stable_sort(sorted_probabilities.begin(), sorted_probabilities.end(),
ProbabilityLess(&probability_table_)); ProbabilityLess(&probability_table_));
if (total_rans_prob < rans_precision_) { if (total_rans_prob < rans_precision_) {
// This happens rather infrequently, just add the extra needed precision // This happens rather infrequently, just add the extra needed precision

View File

@ -72,7 +72,7 @@ bool DecodeTaggedSymbols(uint32_t num_values, int num_components,
int value_id = 0; int value_id = 0;
for (uint32_t i = 0; i < num_values; i += num_components) { for (uint32_t i = 0; i < num_values; i += num_components) {
// Decode the tag. // Decode the tag.
const int bit_length = tag_decoder.DecodeSymbol(); const uint32_t bit_length = tag_decoder.DecodeSymbol();
// Decode the actual value. // Decode the actual value.
for (int j = 0; j < num_components; ++j) { for (int j = 0; j < num_components; ++j) {
uint32_t val; uint32_t val;

View File

@ -14,6 +14,12 @@
// //
#include "draco/compression/expert_encode.h" #include "draco/compression/expert_encode.h"
#include <iostream>
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include "draco/compression/mesh/mesh_edgebreaker_encoder.h" #include "draco/compression/mesh/mesh_edgebreaker_encoder.h"
#include "draco/compression/mesh/mesh_sequential_encoder.h" #include "draco/compression/mesh/mesh_sequential_encoder.h"
#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED #ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED
@ -21,6 +27,9 @@
#include "draco/compression/point_cloud/point_cloud_sequential_encoder.h" #include "draco/compression/point_cloud/point_cloud_sequential_encoder.h"
#endif #endif
#ifdef DRACO_TRANSCODER_SUPPORTED
#include "draco/core/bit_utils.h"
#endif
namespace draco { namespace draco {
ExpertEncoder::ExpertEncoder(const PointCloud &point_cloud) ExpertEncoder::ExpertEncoder(const PointCloud &point_cloud)
@ -101,6 +110,11 @@ Status ExpertEncoder::EncodePointCloudToBuffer(const PointCloud &pc,
Status ExpertEncoder::EncodeMeshToBuffer(const Mesh &m, Status ExpertEncoder::EncodeMeshToBuffer(const Mesh &m,
EncoderBuffer *out_buffer) { EncoderBuffer *out_buffer) {
#ifdef DRACO_TRANSCODER_SUPPORTED
// Apply DracoCompressionOptions associated with the mesh.
DRACO_RETURN_IF_ERROR(ApplyCompressionOptions(m));
#endif // DRACO_TRANSCODER_SUPPORTED
std::unique_ptr<MeshEncoder> encoder; std::unique_ptr<MeshEncoder> encoder;
// Select the encoding method only based on the provided options. // Select the encoding method only based on the provided options.
int encoding_method = options().GetGlobalInt("encoding_method", -1); int encoding_method = options().GetGlobalInt("encoding_method", -1);
@ -118,6 +132,7 @@ Status ExpertEncoder::EncodeMeshToBuffer(const Mesh &m,
encoder = std::unique_ptr<MeshEncoder>(new MeshSequentialEncoder()); encoder = std::unique_ptr<MeshEncoder>(new MeshSequentialEncoder());
} }
encoder->SetMesh(m); encoder->SetMesh(m);
DRACO_RETURN_IF_ERROR(encoder->Encode(options(), out_buffer)); DRACO_RETURN_IF_ERROR(encoder->Encode(options(), out_buffer));
set_num_encoded_points(encoder->num_encoded_points()); set_num_encoded_points(encoder->num_encoded_points());
@ -179,4 +194,107 @@ Status ExpertEncoder::SetAttributePredictionScheme(
return status; return status;
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) {
if (!mesh.IsCompressionEnabled()) {
return OkStatus();
}
const auto &compression_options = mesh.GetCompressionOptions();
// Set any encoder options that haven't been explicitly set by users (don't
// override existing options).
if (!options().IsSpeedSet()) {
options().SetSpeed(10 - compression_options.compression_level,
10 - compression_options.compression_level);
}
for (int ai = 0; ai < mesh.num_attributes(); ++ai) {
if (options().IsAttributeOptionSet(ai, "quantization_bits")) {
continue; // Don't override options that have been set.
}
int quantization_bits = 0;
const auto type = mesh.attribute(ai)->attribute_type();
switch (type) {
case GeometryAttribute::POSITION:
if (compression_options.quantization_position
.AreQuantizationBitsDefined()) {
quantization_bits =
compression_options.quantization_position.quantization_bits();
} else {
DRACO_RETURN_IF_ERROR(ApplyGridQuantization(mesh, ai));
}
break;
case GeometryAttribute::TEX_COORD:
quantization_bits = compression_options.quantization_bits_tex_coord;
break;
case GeometryAttribute::NORMAL:
quantization_bits = compression_options.quantization_bits_normal;
break;
case GeometryAttribute::COLOR:
quantization_bits = compression_options.quantization_bits_color;
break;
case GeometryAttribute::TANGENT:
quantization_bits = compression_options.quantization_bits_tangent;
break;
case GeometryAttribute::WEIGHTS:
quantization_bits = compression_options.quantization_bits_weight;
break;
case GeometryAttribute::GENERIC:
quantization_bits = compression_options.quantization_bits_generic;
break;
default:
break;
}
if (quantization_bits > 0) {
options().SetAttributeInt(ai, "quantization_bits", quantization_bits);
}
}
return OkStatus();
}
Status ExpertEncoder::ApplyGridQuantization(const Mesh &mesh,
int attribute_index) {
const auto compression_options = mesh.GetCompressionOptions();
if (mesh.attribute(attribute_index)->num_components() != 3) {
return ErrorStatus(
"Invalid number of components: Grid quantization is currently "
"supported only for 3D positions.");
}
const float spacing = compression_options.quantization_position.spacing();
// Compute quantization properties based on the grid spacing.
const auto &bbox = mesh.ComputeBoundingBox();
// Snap min and max points of the |bbox| to the quantization grid vertices.
Vector3f min_pos;
int num_values = 0; // Number of values that we need to encode.
for (int c = 0; c < 3; ++c) {
// Min / max position on grid vertices in grid coordinates.
const float min_grid_pos = floor(bbox.GetMinPoint()[c] / spacing);
const float max_grid_pos = ceil(bbox.GetMaxPoint()[c] / spacing);
// Min pos on grid vertex in mesh coordinates.
min_pos[c] = min_grid_pos * spacing;
const float component_num_values =
static_cast<int>(max_grid_pos) - static_cast<int>(min_grid_pos) + 1;
if (component_num_values > num_values) {
num_values = component_num_values;
}
}
// Now compute the number of bits needed to encode |num_values|.
int bits = MostSignificantBit(num_values);
if ((1 << bits) < num_values) {
// If the |num_values| is larger than number of values representable by
// |bits|, we need to use one more bit. This will be almost always true
// unless |num_values| was equal to 1 << |bits|.
bits++;
}
// Compute the range in mesh coordinates that matches the quantization bits.
// Note there are n-1 intervals between the |n| quantization values.
const float range = ((1 << bits) - 1) * spacing;
SetAttributeExplicitQuantization(attribute_index, bits, 3, min_pos.data(),
range);
return OkStatus();
}
#endif // DRACO_TRANSCODER_SUPPORTED
} // namespace draco } // namespace draco

View File

@ -138,6 +138,12 @@ class ExpertEncoder : public EncoderBase<EncoderOptions> {
Status EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer); Status EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer);
#ifdef DRACO_TRANSCODER_SUPPORTED
// Applies compression options stored in |mesh|.
Status ApplyCompressionOptions(const Mesh &mesh);
Status ApplyGridQuantization(const Mesh &mesh, int attribute_index);
#endif // DRACO_TRANSCODER_SUPPORTED
const PointCloud *point_cloud_; const PointCloud *point_cloud_;
const Mesh *mesh_; const Mesh *mesh_;
}; };

View File

@ -454,7 +454,7 @@ bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity() {
#endif #endif
// Decode connectivity of non-position attributes. // Decode connectivity of non-position attributes.
if (attribute_data_.size() > 0) { if (!attribute_data_.empty()) {
#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED #ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 1)) { if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 1)) {
for (CornerIndex ci(0); ci < corner_table_->num_corners(); ci += 3) { for (CornerIndex ci(0); ci < corner_table_->num_corners(); ci += 3) {
@ -484,7 +484,10 @@ bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity() {
attribute_data_[i].connectivity_data.AddSeamEdge(CornerIndex(c)); attribute_data_[i].connectivity_data.AddSeamEdge(CornerIndex(c));
} }
// Recompute vertices from the newly added seam edges. // Recompute vertices from the newly added seam edges.
attribute_data_[i].connectivity_data.RecomputeVertices(nullptr, nullptr); if (!attribute_data_[i].connectivity_data.RecomputeVertices(nullptr,
nullptr)) {
return false;
}
} }
pos_encoding_data_.Init(corner_table_->num_vertices()); pos_encoding_data_.Init(corner_table_->num_vertices());
@ -574,6 +577,17 @@ int MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity(
const CornerIndex corner_b = const CornerIndex corner_b =
corner_table_->Next(corner_table_->LeftMostCorner(vertex_x)); corner_table_->Next(corner_table_->LeftMostCorner(vertex_x));
if (corner_a == corner_b) {
// All matched corners must be different.
return -1;
}
if (corner_table_->Opposite(corner_a) != kInvalidCornerIndex ||
corner_table_->Opposite(corner_b) != kInvalidCornerIndex) {
// One of the corners is already opposite to an existing face, which
// should not happen unless the input was tampered with.
return -1;
}
// New tip corner. // New tip corner.
const CornerIndex corner(3 * face.value()); const CornerIndex corner(3 * face.value());
// Update opposite corner mappings. // Update opposite corner mappings.
@ -616,6 +630,11 @@ int MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity(
return -1; return -1;
} }
const CornerIndex corner_a = active_corner_stack.back(); const CornerIndex corner_a = active_corner_stack.back();
if (corner_table_->Opposite(corner_a) != kInvalidCornerIndex) {
// Active corner is already opposite to an existing face, which should
// not happen unless the input was tampered with.
return -1;
}
// First corner on the new face is either corner "l" or "r". // First corner on the new face is either corner "l" or "r".
const CornerIndex corner(3 * face.value()); const CornerIndex corner(3 * face.value());
@ -681,10 +700,14 @@ int MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity(
} }
const CornerIndex corner_a = active_corner_stack.back(); const CornerIndex corner_a = active_corner_stack.back();
if (corner_a == corner_b) {
// All matched corners must be different.
return -1;
}
if (corner_table_->Opposite(corner_a) != kInvalidCornerIndex || if (corner_table_->Opposite(corner_a) != kInvalidCornerIndex ||
corner_table_->Opposite(corner_b) != kInvalidCornerIndex) { corner_table_->Opposite(corner_b) != kInvalidCornerIndex) {
// One of the corners is already opposite to an existing face, which // One of the corners is already opposite to an existing face, which
// should not happen unless the input was tempered with. // should not happen unless the input was tampered with.
return -1; return -1;
} }
@ -713,9 +736,15 @@ int MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity(
// Also update the vertex id at corner "n" and all corners that are // Also update the vertex id at corner "n" and all corners that are
// connected to it in the CCW direction. // connected to it in the CCW direction.
const CornerIndex first_corner = corner_n;
while (corner_n != kInvalidCornerIndex) { while (corner_n != kInvalidCornerIndex) {
corner_table_->MapCornerToVertex(corner_n, vertex_p); corner_table_->MapCornerToVertex(corner_n, vertex_p);
corner_n = corner_table_->SwingLeft(corner_n); corner_n = corner_table_->SwingLeft(corner_n);
if (corner_n == first_corner) {
// We reached the start again which should not happen for split
// symbols.
return -1;
}
} }
// Make sure the old vertex n is now mapped to an invalid corner (make it // Make sure the old vertex n is now mapped to an invalid corner (make it
// isolated). // isolated).
@ -800,7 +829,7 @@ int MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity(
return -1; // Unexpected number of decoded vertices. return -1; // Unexpected number of decoded vertices.
} }
// Decode start faces and connect them to the faces from the active stack. // Decode start faces and connect them to the faces from the active stack.
while (active_corner_stack.size() > 0) { while (!active_corner_stack.empty()) {
const CornerIndex corner = active_corner_stack.back(); const CornerIndex corner = active_corner_stack.back();
active_corner_stack.pop_back(); active_corner_stack.pop_back();
const bool interior_face = const bool interior_face =
@ -842,6 +871,18 @@ int MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity(
const CornerIndex corner_c = const CornerIndex corner_c =
corner_table_->Next(corner_table_->LeftMostCorner(vert_x)); corner_table_->Next(corner_table_->LeftMostCorner(vert_x));
if (corner == corner_b || corner == corner_c || corner_b == corner_c) {
// All matched corners must be different.
return -1;
}
if (corner_table_->Opposite(corner) != kInvalidCornerIndex ||
corner_table_->Opposite(corner_b) != kInvalidCornerIndex ||
corner_table_->Opposite(corner_c) != kInvalidCornerIndex) {
// One of the corners is already opposite to an existing face, which
// should not happen unless the input was tampered with.
return -1;
}
const VertexIndex vert_p = const VertexIndex vert_p =
corner_table_->Vertex(corner_table_->Next(corner_c)); corner_table_->Vertex(corner_table_->Next(corner_c));
@ -894,6 +935,11 @@ int MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity(
VertexCornersIterator<CornerTable> vcit(corner_table_.get(), src_vert); VertexCornersIterator<CornerTable> vcit(corner_table_.get(), src_vert);
for (; !vcit.End(); ++vcit) { for (; !vcit.End(); ++vcit) {
const CornerIndex cid = vcit.Corner(); const CornerIndex cid = vcit.Corner();
if (corner_table_->Vertex(cid) != src_vert) {
// Vertex mapped to |cid| was not |src_vert|. This indicates corrupted
// data and we should terminate the decoding.
return -1;
}
corner_table_->MapCornerToVertex(cid, invalid_vert); corner_table_->MapCornerToVertex(cid, invalid_vert);
} }
corner_table_->SetLeftMostCorner(invalid_vert, corner_table_->SetLeftMostCorner(invalid_vert,

View File

@ -31,7 +31,6 @@ bool MeshEdgebreakerEncoder::InitializeEncoder() {
impl_ = nullptr; impl_ = nullptr;
// For tiny meshes it's usually better to use the basic edgebreaker as the // For tiny meshes it's usually better to use the basic edgebreaker as the
// overhead of the predictive one may turn out to be too big. // overhead of the predictive one may turn out to be too big.
// TODO(b/111065939): Check if this can be improved.
const bool is_tiny_mesh = mesh()->num_faces() < 1000; const bool is_tiny_mesh = mesh()->num_faces() < 1000;
int selected_edgebreaker_method = int selected_edgebreaker_method =

View File

@ -408,7 +408,7 @@ Status MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
init_face_connectivity_corners.begin(), init_face_connectivity_corners.begin(),
init_face_connectivity_corners.end()); init_face_connectivity_corners.end());
// Encode connectivity for all non-position attributes. // Encode connectivity for all non-position attributes.
if (attribute_data_.size() > 0) { if (!attribute_data_.empty()) {
// Use the same order of corner that will be used by the decoder. // Use the same order of corner that will be used by the decoder.
visited_faces_.assign(mesh_->num_faces(), false); visited_faces_.assign(mesh_->num_faces(), false);
for (CornerIndex ci : processed_connectivity_corners_) { for (CornerIndex ci : processed_connectivity_corners_) {

View File

@ -177,7 +177,6 @@ class MeshEdgebreakerEncoderImpl : public MeshEdgebreakerEncoderImplInterface {
uint32_t num_split_symbols_; uint32_t num_split_symbols_;
// Struct holding data used for encoding each non-position attribute. // Struct holding data used for encoding each non-position attribute.
// TODO(ostava): This should be probably renamed to something better.
struct AttributeData { struct AttributeData {
AttributeData() : attribute_index(-1), is_connectivity_used(true) {} AttributeData() : attribute_index(-1), is_connectivity_used(true) {}
int attribute_index; int attribute_index;

View File

@ -44,7 +44,7 @@ class MeshEdgebreakerEncodingTest : public ::testing::Test {
EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions(); EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions();
encoder_options.SetSpeed(10 - compression_level, 10 - compression_level); encoder_options.SetSpeed(10 - compression_level, 10 - compression_level);
encoder.SetMesh(*mesh); encoder.SetMesh(*mesh);
ASSERT_TRUE(encoder.Encode(encoder_options, &buffer).ok()); DRACO_ASSERT_OK(encoder.Encode(encoder_options, &buffer));
DecoderBuffer dec_buffer; DecoderBuffer dec_buffer;
dec_buffer.Init(buffer.data(), buffer.size()); dec_buffer.Init(buffer.data(), buffer.size());
@ -52,15 +52,14 @@ class MeshEdgebreakerEncodingTest : public ::testing::Test {
std::unique_ptr<Mesh> decoded_mesh(new Mesh()); std::unique_ptr<Mesh> decoded_mesh(new Mesh());
DecoderOptions dec_options; DecoderOptions dec_options;
ASSERT_TRUE( DRACO_ASSERT_OK(
decoder.Decode(dec_options, &dec_buffer, decoded_mesh.get()).ok()); decoder.Decode(dec_options, &dec_buffer, decoded_mesh.get()));
// Cleanup the input mesh to make sure that input and output can be // Cleanup the input mesh to make sure that input and output can be
// compared (edgebreaker method discards degenerated triangles and isolated // compared (edgebreaker method discards degenerated triangles and isolated
// vertices). // vertices).
const MeshCleanupOptions options; const MeshCleanupOptions options;
MeshCleanup cleanup; DRACO_ASSERT_OK(MeshCleanup::Cleanup(mesh, options));
ASSERT_TRUE(cleanup(mesh, options)) << "Failed to clean the input mesh.";
MeshAreEquivalent eq; MeshAreEquivalent eq;
ASSERT_TRUE(eq(*mesh, *decoded_mesh.get())) ASSERT_TRUE(eq(*mesh, *decoded_mesh.get()))
@ -102,8 +101,8 @@ TEST_F(MeshEdgebreakerEncodingTest, TestEncoderReuse) {
EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions(); EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions();
encoder.SetMesh(*mesh); encoder.SetMesh(*mesh);
EncoderBuffer buffer_0, buffer_1; EncoderBuffer buffer_0, buffer_1;
ASSERT_TRUE(encoder.Encode(encoder_options, &buffer_0).ok()); DRACO_ASSERT_OK(encoder.Encode(encoder_options, &buffer_0));
ASSERT_TRUE(encoder.Encode(encoder_options, &buffer_1).ok()); DRACO_ASSERT_OK(encoder.Encode(encoder_options, &buffer_1));
// Make sure both buffer are identical. // Make sure both buffer are identical.
ASSERT_EQ(buffer_0.size(), buffer_1.size()); ASSERT_EQ(buffer_0.size(), buffer_1.size());
@ -123,7 +122,7 @@ TEST_F(MeshEdgebreakerEncodingTest, TestDecoderReuse) {
EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions(); EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions();
encoder.SetMesh(*mesh); encoder.SetMesh(*mesh);
EncoderBuffer buffer; EncoderBuffer buffer;
ASSERT_TRUE(encoder.Encode(encoder_options, &buffer).ok()); DRACO_ASSERT_OK(encoder.Encode(encoder_options, &buffer));
DecoderBuffer dec_buffer; DecoderBuffer dec_buffer;
dec_buffer.Init(buffer.data(), buffer.size()); dec_buffer.Init(buffer.data(), buffer.size());
@ -133,13 +132,13 @@ TEST_F(MeshEdgebreakerEncodingTest, TestDecoderReuse) {
// Decode the mesh two times. // Decode the mesh two times.
std::unique_ptr<Mesh> decoded_mesh_0(new Mesh()); std::unique_ptr<Mesh> decoded_mesh_0(new Mesh());
DecoderOptions dec_options; DecoderOptions dec_options;
ASSERT_TRUE( DRACO_ASSERT_OK(
decoder.Decode(dec_options, &dec_buffer, decoded_mesh_0.get()).ok()); decoder.Decode(dec_options, &dec_buffer, decoded_mesh_0.get()));
dec_buffer.Init(buffer.data(), buffer.size()); dec_buffer.Init(buffer.data(), buffer.size());
std::unique_ptr<Mesh> decoded_mesh_1(new Mesh()); std::unique_ptr<Mesh> decoded_mesh_1(new Mesh());
ASSERT_TRUE( DRACO_ASSERT_OK(
decoder.Decode(dec_options, &dec_buffer, decoded_mesh_1.get()).ok()); decoder.Decode(dec_options, &dec_buffer, decoded_mesh_1.get()));
// Make sure both of the meshes are identical. // Make sure both of the meshes are identical.
MeshAreEquivalent eq; MeshAreEquivalent eq;
@ -169,7 +168,7 @@ TEST_F(MeshEdgebreakerEncodingTest, TestSingleConnectivityEncoding) {
encoder.SetAttributeQuantization(GeometryAttribute::TEX_COORD, 8); encoder.SetAttributeQuantization(GeometryAttribute::TEX_COORD, 8);
encoder.SetAttributeQuantization(GeometryAttribute::NORMAL, 8); encoder.SetAttributeQuantization(GeometryAttribute::NORMAL, 8);
encoder.SetEncodingMethod(MESH_EDGEBREAKER_ENCODING); encoder.SetEncodingMethod(MESH_EDGEBREAKER_ENCODING);
ASSERT_TRUE(encoder.EncodeMeshToBuffer(*mesh, &buffer).ok()); DRACO_ASSERT_OK(encoder.EncodeMeshToBuffer(*mesh, &buffer));
DecoderBuffer dec_buffer; DecoderBuffer dec_buffer;
dec_buffer.Init(buffer.data(), buffer.size()); dec_buffer.Init(buffer.data(), buffer.size());
@ -216,7 +215,7 @@ TEST_F(MeshEdgebreakerEncodingTest, TestWrongAttributeOrder) {
encoder.SetAttributeQuantization(GeometryAttribute::POSITION, 8); encoder.SetAttributeQuantization(GeometryAttribute::POSITION, 8);
encoder.SetAttributeQuantization(GeometryAttribute::NORMAL, 8); encoder.SetAttributeQuantization(GeometryAttribute::NORMAL, 8);
encoder.SetEncodingMethod(MESH_EDGEBREAKER_ENCODING); encoder.SetEncodingMethod(MESH_EDGEBREAKER_ENCODING);
ASSERT_TRUE(encoder.EncodeMeshToBuffer(*mesh, &buffer).ok()); DRACO_ASSERT_OK(encoder.EncodeMeshToBuffer(*mesh, &buffer));
DecoderBuffer dec_buffer; DecoderBuffer dec_buffer;
dec_buffer.Init(buffer.data(), buffer.size()); dec_buffer.Init(buffer.data(), buffer.size());

View File

@ -50,8 +50,6 @@ namespace draco {
// \ / S \ / / E \ // \ / S \ / / E \
// *-------* *-------* // *-------* *-------*
// //
// TODO(ostava): Get rid of the topology bit pattern. It's important only for
// encoding but the algorithms should use EdgebreakerSymbol instead.
enum EdgebreakerTopologyBitPattern { enum EdgebreakerTopologyBitPattern {
TOPOLOGY_C = 0x0, // 0 TOPOLOGY_C = 0x0, // 0
TOPOLOGY_S = 0x1, // 1 0 0 TOPOLOGY_S = 0x1, // 1 0 0

View File

@ -129,7 +129,11 @@ class MeshEdgebreakerTraversalValenceDecoder
if (context_counter < 0) { if (context_counter < 0) {
return TOPOLOGY_INVALID; return TOPOLOGY_INVALID;
} }
const int symbol_id = context_symbols_[active_context_][context_counter]; const uint32_t symbol_id =
context_symbols_[active_context_][context_counter];
if (symbol_id > 4) {
return TOPOLOGY_INVALID;
}
last_symbol_ = edge_breaker_symbol_to_topology_id[symbol_id]; last_symbol_ = edge_breaker_symbol_to_topology_id[symbol_id];
} else { } else {
#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED #ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED

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