Merge branch 'master' of https://github.com/assimp/assimp
commit
d65049a657
|
@ -74,12 +74,6 @@ jobs:
|
||||||
repository: cpp-pm/polly
|
repository: cpp-pm/polly
|
||||||
path: cmake/polly
|
path: cmake/polly
|
||||||
|
|
||||||
- name: Remove contrib directory for Hunter builds
|
|
||||||
if: contains(matrix.name, 'hunter')
|
|
||||||
uses: JesseTG/rm@v1.0.3
|
|
||||||
with:
|
|
||||||
path: contrib
|
|
||||||
|
|
||||||
- name: Cache DX SDK
|
- name: Cache DX SDK
|
||||||
id: dxcache
|
id: dxcache
|
||||||
if: contains(matrix.name, 'windows')
|
if: contains(matrix.name, 'windows')
|
||||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
||||||
name: adress-sanitizer
|
name: adress-sanitizer
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: lukka/get-cmake@latest
|
- uses: lukka/get-cmake@latest
|
||||||
- uses: lukka/set-shell-env@v1
|
- uses: lukka/set-shell-env@v1
|
||||||
with:
|
with:
|
||||||
|
@ -38,7 +38,7 @@ jobs:
|
||||||
name: undefined-behavior-sanitizer
|
name: undefined-behavior-sanitizer
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: lukka/get-cmake@latest
|
- uses: lukka/get-cmake@latest
|
||||||
- uses: lukka/set-shell-env@v1
|
- uses: lukka/set-shell-env@v1
|
||||||
with:
|
with:
|
||||||
|
@ -46,7 +46,7 @@ jobs:
|
||||||
CC: clang
|
CC: clang
|
||||||
|
|
||||||
- name: configure and build
|
- name: configure and build
|
||||||
uses: lukka/run-cmake@v2
|
uses: lukka/run-cmake@v3
|
||||||
with:
|
with:
|
||||||
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
|
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
|
||||||
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
|
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
|
||||||
|
@ -57,3 +57,13 @@ jobs:
|
||||||
- name: test
|
- name: test
|
||||||
run: cd build/bin && ./unit
|
run: cd build/bin && ./unit
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
job3:
|
||||||
|
name: printf-sanitizer
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: run scan_printf script
|
||||||
|
run: ./scripts/scan_printf.sh
|
||||||
|
shell: bash
|
||||||
|
|
|
@ -49,10 +49,9 @@ option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
|
||||||
IF(ASSIMP_HUNTER_ENABLED)
|
IF(ASSIMP_HUNTER_ENABLED)
|
||||||
include("cmake-modules/HunterGate.cmake")
|
include("cmake-modules/HunterGate.cmake")
|
||||||
HunterGate(
|
HunterGate(
|
||||||
URL "https://github.com/cpp-pm/hunter/archive/v0.24.0.tar.gz"
|
URL "https://github.com/cpp-pm/hunter/archive/v0.24.17.tar.gz"
|
||||||
SHA1 "a3d7f4372b1dcd52faa6ff4a3bd5358e1d0e5efd"
|
SHA1 "e6396699e414120e32557fe92db097b7655b760b"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(-DASSIMP_USE_HUNTER)
|
add_definitions(-DASSIMP_USE_HUNTER)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
|
@ -84,10 +83,6 @@ OPTION( ASSIMP_NO_EXPORT
|
||||||
"Disable Assimp's export functionality."
|
"Disable Assimp's export functionality."
|
||||||
OFF
|
OFF
|
||||||
)
|
)
|
||||||
OPTION( ASSIMP_BUILD_ZLIB
|
|
||||||
"Build your own zlib"
|
|
||||||
OFF
|
|
||||||
)
|
|
||||||
OPTION( ASSIMP_BUILD_ASSIMP_TOOLS
|
OPTION( ASSIMP_BUILD_ASSIMP_TOOLS
|
||||||
"If the supplementary tools for Assimp are built in addition to the library."
|
"If the supplementary tools for Assimp are built in addition to the library."
|
||||||
OFF
|
OFF
|
||||||
|
@ -134,6 +129,18 @@ OPTION ( ASSIMP_IGNORE_GIT_HASH
|
||||||
OFF
|
OFF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
IF (WIN32)
|
||||||
|
OPTION( ASSIMP_BUILD_ZLIB
|
||||||
|
"Build your own zlib"
|
||||||
|
ON
|
||||||
|
)
|
||||||
|
ELSE()
|
||||||
|
OPTION( ASSIMP_BUILD_ZLIB
|
||||||
|
"Build your own zlib"
|
||||||
|
ON
|
||||||
|
)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
IF (WIN32)
|
IF (WIN32)
|
||||||
# Use subset of Windows.h
|
# Use subset of Windows.h
|
||||||
ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
|
ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
|
||||||
|
@ -193,12 +200,9 @@ SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VER
|
||||||
SET (ASSIMP_SOVERSION 5)
|
SET (ASSIMP_SOVERSION 5)
|
||||||
|
|
||||||
SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
|
SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
|
||||||
if(NOT ASSIMP_HUNTER_ENABLED)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
# Enable C++17 support globally
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
IF(NOT ASSIMP_IGNORE_GIT_HASH)
|
IF(NOT ASSIMP_IGNORE_GIT_HASH)
|
||||||
# Get the current working branch
|
# Get the current working branch
|
||||||
|
@ -246,8 +250,7 @@ IF( UNIX )
|
||||||
# Use GNUInstallDirs for Unix predefined directories
|
# Use GNUInstallDirs for Unix predefined directories
|
||||||
INCLUDE(GNUInstallDirs)
|
INCLUDE(GNUInstallDirs)
|
||||||
# Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux
|
# Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux
|
||||||
IF( ${OPERATING_SYSTEM} MATCHES "Android")
|
IF(NOT ${OPERATING_SYSTEM} MATCHES "Android")
|
||||||
ELSE()
|
|
||||||
IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux
|
IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux
|
||||||
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 )
|
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 )
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
@ -257,9 +260,13 @@ ENDIF()
|
||||||
# Grouped compiler settings ########################################
|
# Grouped compiler settings ########################################
|
||||||
IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW)
|
IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW)
|
||||||
IF(NOT ASSIMP_HUNTER_ENABLED)
|
IF(NOT ASSIMP_HUNTER_ENABLED)
|
||||||
SET(CMAKE_CXX_STANDARD 17)
|
|
||||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
|
IF(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 13)
|
||||||
|
MESSAGE(STATUS "GCC13 detected disabling \"-Wdangling-reference\" in Cpp files as it appears to be a false positive")
|
||||||
|
ADD_COMPILE_OPTIONS("$<$<COMPILE_LANGUAGE:CXX>:-Wno-dangling-reference>")
|
||||||
|
ENDIF()
|
||||||
# hide all not-exported symbols
|
# hide all not-exported symbols
|
||||||
IF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "mips64" )
|
IF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "mips64" )
|
||||||
SET(CMAKE_CXX_FLAGS "-mxgot -fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}")
|
SET(CMAKE_CXX_FLAGS "-mxgot -fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}")
|
||||||
|
@ -273,9 +280,9 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW)
|
||||||
ELSEIF(MSVC)
|
ELSEIF(MSVC)
|
||||||
# enable multi-core compilation with MSVC
|
# enable multi-core compilation with MSVC
|
||||||
IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) # clang-cl
|
IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) # clang-cl
|
||||||
ADD_COMPILE_OPTIONS(/bigobj /W4 /WX )
|
ADD_COMPILE_OPTIONS(/bigobj)
|
||||||
ELSE() # msvc
|
ELSE() # msvc
|
||||||
ADD_COMPILE_OPTIONS(/MP /bigobj /W4 /WX)
|
ADD_COMPILE_OPTIONS(/MP /bigobj)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# disable "elements of array '' will be default initialized" warning on MSVC2013
|
# disable "elements of array '' will be default initialized" warning on MSVC2013
|
||||||
|
@ -289,7 +296,6 @@ ELSEIF(MSVC)
|
||||||
SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF")
|
SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF")
|
||||||
ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
|
ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
|
||||||
IF(NOT ASSIMP_HUNTER_ENABLED)
|
IF(NOT ASSIMP_HUNTER_ENABLED)
|
||||||
SET(CMAKE_CXX_STANDARD 17)
|
|
||||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
|
SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
|
||||||
|
@ -319,12 +325,12 @@ IF ( IOS AND NOT ASSIMP_HUNTER_ENABLED)
|
||||||
ELSE()
|
ELSE()
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
|
||||||
# Experimental for pdb generation
|
|
||||||
ENDIF()
|
ENDIF()
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
IF (ASSIMP_COVERALLS)
|
IF (ASSIMP_COVERALLS)
|
||||||
MESSAGE(STATUS "Coveralls enabled")
|
MESSAGE(STATUS "Coveralls enabled")
|
||||||
|
|
||||||
INCLUDE(Coveralls)
|
INCLUDE(Coveralls)
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
|
||||||
|
@ -332,12 +338,14 @@ ENDIF()
|
||||||
|
|
||||||
IF (ASSIMP_ASAN)
|
IF (ASSIMP_ASAN)
|
||||||
MESSAGE(STATUS "AddressSanitizer enabled")
|
MESSAGE(STATUS "AddressSanitizer enabled")
|
||||||
|
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
IF (ASSIMP_UBSAN)
|
IF (ASSIMP_UBSAN)
|
||||||
MESSAGE(STATUS "Undefined Behavior sanitizer enabled")
|
MESSAGE(STATUS "Undefined Behavior sanitizer enabled")
|
||||||
|
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin -fno-sanitize-recover=all")
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin -fno-sanitize-recover=all")
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin -fno-sanitize-recover=all")
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin -fno-sanitize-recover=all")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
@ -684,7 +692,6 @@ ELSE()
|
||||||
COMPONENT ${LIBASSIMP_COMPONENT}
|
COMPONENT ${LIBASSIMP_COMPONENT}
|
||||||
INCLUDES DESTINATION include
|
INCLUDES DESTINATION include
|
||||||
)
|
)
|
||||||
|
|
||||||
ENDIF()
|
ENDIF()
|
||||||
ENDIF()
|
ENDIF()
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
16
Dockerfile
16
Dockerfile
|
@ -1,14 +1,9 @@
|
||||||
FROM ubuntu:14.04
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y ninja-build \
|
||||||
git cmake build-essential software-properties-common
|
git cmake build-essential software-properties-common
|
||||||
|
|
||||||
RUN add-apt-repository ppa:ubuntu-toolchain-r/test && apt-get update && apt-get install -y gcc-4.9 g++-4.9 && \
|
RUN add-apt-repository ppa:ubuntu-toolchain-r/test && apt-get update
|
||||||
cd /usr/bin && \
|
|
||||||
rm gcc g++ cpp && \
|
|
||||||
ln -s gcc-4.9 gcc && \
|
|
||||||
ln -s g++-4.9 g++ && \
|
|
||||||
ln -s cpp-4.9 cpp
|
|
||||||
|
|
||||||
WORKDIR /opt
|
WORKDIR /opt
|
||||||
|
|
||||||
|
@ -19,7 +14,8 @@ WORKDIR /opt/assimp
|
||||||
|
|
||||||
RUN git checkout master \
|
RUN git checkout master \
|
||||||
&& mkdir build && cd build && \
|
&& mkdir build && cd build && \
|
||||||
cmake \
|
cmake -G 'Ninja' \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DASSIMP_BUILD_ASSIMP_TOOLS=ON \
|
||||||
.. && \
|
.. && \
|
||||||
make && make install
|
ninja -j4 && ninja install
|
||||||
|
|
41
Readme.md
41
Readme.md
|
@ -1,6 +1,8 @@
|
||||||
Open Asset Import Library (assimp)
|
Open Asset Import Library (assimp)
|
||||||
==================================
|
==================================
|
||||||
A library to import and export various 3d-model-formats including scene-post-processing to generate missing render data.
|
|
||||||
|
Open Asset Import Library is a library to load various 3d file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
|
||||||
|
|
||||||
### Current project status ###
|
### Current project status ###
|
||||||
[![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp)
|
[![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp)
|
||||||
![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
|
![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
|
||||||
|
@ -14,7 +16,6 @@ A library to import and export various 3d-model-formats including scene-post-pro
|
||||||
[![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Average time to resolve an issue")
|
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Average time to resolve an issue")
|
||||||
[![Percentage of issues still open](http://isitmaintained.com/badge/open/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Percentage of issues still open")
|
[![Percentage of issues still open](http://isitmaintained.com/badge/open/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Percentage of issues still open")
|
||||||
[![Total alerts](https://img.shields.io/lgtm/alerts/g/assimp/assimp.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/assimp/assimp/alerts/)
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
|
APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
|
||||||
|
@ -23,15 +24,19 @@ Additionally, assimp features various __mesh post processing tools__: normals an
|
||||||
### Latest Doc's ###
|
### Latest Doc's ###
|
||||||
Please check the latest documents at [Asset-Importer-Lib-Doc](https://assimp-docs.readthedocs.io/en/latest/).
|
Please check the latest documents at [Asset-Importer-Lib-Doc](https://assimp-docs.readthedocs.io/en/latest/).
|
||||||
|
|
||||||
### Get involved ###
|
### Prebuild binaries ###
|
||||||
This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
|
Please check our [Itchi Projectspace](https://kimkulling.itch.io/the-asset-importer-lib)
|
||||||
<br>
|
|
||||||
You find a bug in the docs? Use [Doc-Repo](https://github.com/assimp/assimp-docs).
|
|
||||||
<br>
|
|
||||||
Please check our Wiki as well: https://github.com/assimp/assimp/wiki
|
|
||||||
|
|
||||||
If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
|
If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
|
||||||
|
|
||||||
|
### Communities ###
|
||||||
|
- Ask a question at [The Assimp-Discussion Board](https://github.com/assimp/assimp/discussions)
|
||||||
|
- Ask on [Assimp-Community on Reddit](https://www.reddit.com/r/Assimp/)
|
||||||
|
- Ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest).
|
||||||
|
- Nothing has worked? File a question or an issue-report at [The Assimp-Issue Tracker](https://github.com/assimp/assimp/issues)
|
||||||
|
|
||||||
|
And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)<br>
|
||||||
|
|
||||||
#### Supported file formats ####
|
#### Supported file formats ####
|
||||||
You can find the complete list of supported file-formats [here](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md)
|
You can find the complete list of supported file-formats [here](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md)
|
||||||
|
|
||||||
|
@ -66,28 +71,18 @@ Open Asset Import Library is implemented in C++. The directory structure looks l
|
||||||
/port Ports to other languages and scripts to maintain those.
|
/port Ports to other languages and scripts to maintain those.
|
||||||
/test Unit- and regression tests, test suite of models
|
/test Unit- and regression tests, test suite of models
|
||||||
/tools Tools (old assimp viewer, command line `assimp`)
|
/tools Tools (old assimp viewer, command line `assimp`)
|
||||||
/samples A small number of samples to illustrate possible
|
/samples A small number of samples to illustrate possible use-cases for Assimp
|
||||||
use cases for Assimp
|
|
||||||
|
|
||||||
The source code is organized in the following way:
|
The source code is organized in the following way:
|
||||||
|
|
||||||
code/Common The base implementation for importers and the infrastructure
|
code/Common The base implementation for importers and the infrastructure
|
||||||
|
code/CApi Special implementations which are only used for the C-API
|
||||||
|
code/Geometry A collection of geometry tools
|
||||||
|
code/Material The material system
|
||||||
|
code/PBR An exporter for physical based models
|
||||||
code/PostProcessing The post-processing steps
|
code/PostProcessing The post-processing steps
|
||||||
code/AssetLib/<FormatName> Implementation for import and export for the format
|
code/AssetLib/<FormatName> Implementation for import and export for the format
|
||||||
|
|
||||||
### Where to get help ###
|
|
||||||
To find our documentation, visit [our website](https://assimp.org/) or check out [Wiki](https://github.com/assimp/assimp/wiki)
|
|
||||||
|
|
||||||
If the docs don't solve your problem, you can:
|
|
||||||
- Ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest).
|
|
||||||
- Ask on [Assimp-Community on Reddit](https://www.reddit.com/r/Assimp/)
|
|
||||||
- Ask a question at [The Assimp-Discussion Board](https://github.com/assimp/assimp/discussions)
|
|
||||||
- Nothing has worked? File a question or an issue-report at [The Assimp-Issue Tracker](https://github.com/assimp/assimp/issues)
|
|
||||||
|
|
||||||
Open Asset Import Library is a library to load various 3d file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
|
|
||||||
|
|
||||||
And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)<br>
|
|
||||||
|
|
||||||
### Contributing ###
|
### Contributing ###
|
||||||
Contributions to assimp are highly appreciated. The easiest way to get involved is to submit
|
Contributions to assimp are highly appreciated. The easiest way to get involved is to submit
|
||||||
a pull request with your changes against the main repository's `master` branch.
|
a pull request with your changes against the main repository's `master` branch.
|
||||||
|
|
|
@ -397,10 +397,6 @@ struct Material {
|
||||||
|
|
||||||
Material(const Material &other) = default;
|
Material(const Material &other) = default;
|
||||||
|
|
||||||
Material(Material &&other) AI_NO_EXCEPT = default;
|
|
||||||
|
|
||||||
Material &operator=(Material &&other) AI_NO_EXCEPT = default;
|
|
||||||
|
|
||||||
virtual ~Material() = default;
|
virtual ~Material() = default;
|
||||||
|
|
||||||
//! Name of the material
|
//! Name of the material
|
||||||
|
|
|
@ -266,8 +266,15 @@ void Discreet3DSImporter::ParseMainChunk() {
|
||||||
};
|
};
|
||||||
|
|
||||||
ASSIMP_3DS_END_CHUNK();
|
ASSIMP_3DS_END_CHUNK();
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wunreachable-code-return"
|
||||||
|
#endif
|
||||||
// recursively continue processing this hierarchy level
|
// recursively continue processing this hierarchy level
|
||||||
return ParseMainChunk();
|
return ParseMainChunk();
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -68,7 +68,7 @@ using namespace D3DS;
|
||||||
class Discreet3DSImporter : public BaseImporter {
|
class Discreet3DSImporter : public BaseImporter {
|
||||||
public:
|
public:
|
||||||
Discreet3DSImporter();
|
Discreet3DSImporter();
|
||||||
~Discreet3DSImporter();
|
~Discreet3DSImporter() override;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
|
|
|
@ -93,7 +93,7 @@ public:
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
~EmbeddedTexture() = default;
|
~EmbeddedTexture() override = default;
|
||||||
|
|
||||||
ResourceType getType() const override {
|
ResourceType getType() const override {
|
||||||
return ResourceType::RT_EmbeddedTexture2D;
|
return ResourceType::RT_EmbeddedTexture2D;
|
||||||
|
@ -110,7 +110,7 @@ public:
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
~Texture2DGroup() = default;
|
~Texture2DGroup() override = default;
|
||||||
|
|
||||||
ResourceType getType() const override {
|
ResourceType getType() const override {
|
||||||
return ResourceType::RT_Texture2DGroup;
|
return ResourceType::RT_Texture2DGroup;
|
||||||
|
@ -127,7 +127,7 @@ public:
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
~BaseMaterials() = default;
|
~BaseMaterials() override = default;
|
||||||
|
|
||||||
ResourceType getType() const override {
|
ResourceType getType() const override {
|
||||||
return ResourceType::RT_BaseMaterials;
|
return ResourceType::RT_BaseMaterials;
|
||||||
|
@ -152,7 +152,7 @@ public:
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
~Object() = default;
|
~Object() override = default;
|
||||||
|
|
||||||
ResourceType getType() const override {
|
ResourceType getType() const override {
|
||||||
return ResourceType::RT_Object;
|
return ResourceType::RT_Object;
|
||||||
|
|
|
@ -282,11 +282,11 @@ public:
|
||||||
bool Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const;
|
bool Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const;
|
||||||
bool Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const;
|
bool Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const;
|
||||||
bool Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const;
|
bool Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const;
|
||||||
void Throw_CloseNotFound(const std::string &nodeName);
|
AI_WONT_RETURN void Throw_CloseNotFound(const std::string &nodeName) AI_WONT_RETURN_SUFFIX;
|
||||||
void Throw_IncorrectAttr(const std::string &nodeName, const std::string &pAttrName);
|
AI_WONT_RETURN void Throw_IncorrectAttr(const std::string &nodeName, const std::string &pAttrName) AI_WONT_RETURN_SUFFIX;
|
||||||
void Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &pAttrName);
|
AI_WONT_RETURN void Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &pAttrName) AI_WONT_RETURN_SUFFIX;
|
||||||
void Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription);
|
AI_WONT_RETURN void Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription) AI_WONT_RETURN_SUFFIX;
|
||||||
void Throw_ID_NotFound(const std::string &pID) const;
|
AI_WONT_RETURN void Throw_ID_NotFound(const std::string &pID) const AI_WONT_RETURN_SUFFIX;
|
||||||
void XML_CheckNode_MustHaveChildren(pugi::xml_node &node);
|
void XML_CheckNode_MustHaveChildren(pugi::xml_node &node);
|
||||||
bool XML_SearchNode(const std::string &nodeName);
|
bool XML_SearchNode(const std::string &nodeName);
|
||||||
void ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString);
|
void ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString);
|
||||||
|
|
|
@ -815,6 +815,7 @@ nl_clean_loop:
|
||||||
for (; next_it != nodeArray.end(); ++next_it) {
|
for (; next_it != nodeArray.end(); ++next_it) {
|
||||||
if ((*next_it)->FindNode((*nl_it)->mName) != nullptr) {
|
if ((*next_it)->FindNode((*nl_it)->mName) != nullptr) {
|
||||||
// if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
|
// if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
|
||||||
|
// FIXME: this leaks memory on test models test8.amf and test9.amf
|
||||||
nodeArray.erase(nl_it);
|
nodeArray.erase(nl_it);
|
||||||
|
|
||||||
goto nl_clean_loop;
|
goto nl_clean_loop;
|
||||||
|
|
|
@ -44,7 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
|
||||||
|
|
||||||
// internal headers
|
// internal headers
|
||||||
|
@ -322,21 +321,6 @@ void ASEImporter::BuildAnimations(const std::vector<BaseNode *> &nodes) {
|
||||||
aiNodeAnim *nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
|
aiNodeAnim *nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
|
||||||
nd->mNodeName.Set(me->mName + ".Target");
|
nd->mNodeName.Set(me->mName + ".Target");
|
||||||
|
|
||||||
// If there is no input position channel we will need
|
|
||||||
// to supply the default position from the node's
|
|
||||||
// local transformation matrix.
|
|
||||||
/*TargetAnimationHelper helper;
|
|
||||||
if (me->mAnim.akeyPositions.empty())
|
|
||||||
{
|
|
||||||
aiMatrix4x4& mat = (*i)->mTransform;
|
|
||||||
helper.SetFixedMainAnimationChannel(aiVector3D(
|
|
||||||
mat.a4, mat.b4, mat.c4));
|
|
||||||
}
|
|
||||||
else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions);
|
|
||||||
helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions);
|
|
||||||
|
|
||||||
helper.Process(&me->mTargetAnim.akeyPositions);*/
|
|
||||||
|
|
||||||
// Allocate the key array and fill it
|
// Allocate the key array and fill it
|
||||||
nd->mNumPositionKeys = (unsigned int)me->mTargetAnim.akeyPositions.size();
|
nd->mNumPositionKeys = (unsigned int)me->mTargetAnim.akeyPositions.size();
|
||||||
nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
|
nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
|
||||||
|
|
|
@ -304,7 +304,6 @@ void Parser::Parse() {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_TOP_LEVEL_SECTION();
|
AI_ASE_HANDLE_TOP_LEVEL_SECTION();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -480,6 +479,11 @@ void Parser::ParseLV1MaterialListBlock() {
|
||||||
if (TokenMatch(filePtr, "MATERIAL_COUNT", 14)) {
|
if (TokenMatch(filePtr, "MATERIAL_COUNT", 14)) {
|
||||||
ParseLV4MeshLong(iMaterialCount);
|
ParseLV4MeshLong(iMaterialCount);
|
||||||
|
|
||||||
|
if (UINT_MAX - iOldMaterialCount < iMaterialCount) {
|
||||||
|
LogWarning("Out of range: material index is too large");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// now allocate enough storage to hold all materials
|
// now allocate enough storage to hold all materials
|
||||||
m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID"));
|
m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID"));
|
||||||
continue;
|
continue;
|
||||||
|
@ -734,7 +738,6 @@ void Parser::ParseLV3MapBlock(Texture &map) {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MAP_XXXXXX");
|
AI_ASE_HANDLE_SECTION("3", "*MAP_XXXXXX");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -859,7 +862,6 @@ void Parser::ParseLV1ObjectBlock(ASE::BaseNode &node) {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_TOP_LEVEL_SECTION();
|
AI_ASE_HANDLE_TOP_LEVEL_SECTION();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -883,7 +885,6 @@ void Parser::ParseLV2CameraSettingsBlock(ASE::Camera &camera) {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("2", "CAMERA_SETTINGS");
|
AI_ASE_HANDLE_SECTION("2", "CAMERA_SETTINGS");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -1189,7 +1190,6 @@ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("2", "*NODE_TM");
|
AI_ASE_HANDLE_SECTION("2", "*NODE_TM");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV2MeshBlock(ASE::Mesh &mesh) {
|
void Parser::ParseLV2MeshBlock(ASE::Mesh &mesh) {
|
||||||
|
@ -1310,7 +1310,6 @@ void Parser::ParseLV2MeshBlock(ASE::Mesh &mesh) {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("2", "*MESH");
|
AI_ASE_HANDLE_SECTION("2", "*MESH");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh &mesh) {
|
void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh &mesh) {
|
||||||
|
@ -1344,7 +1343,6 @@ void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh &mesh) {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_WEIGHTS");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_WEIGHTS");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV4MeshBones(unsigned int iNumBones, ASE::Mesh &mesh) {
|
void Parser::ParseLV4MeshBones(unsigned int iNumBones, ASE::Mesh &mesh) {
|
||||||
|
@ -1414,7 +1412,6 @@ void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices, ASE::Mesh &mes
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("4", "*MESH_BONE_VERTEX");
|
AI_ASE_HANDLE_SECTION("4", "*MESH_BONE_VERTEX");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MeshVertexListBlock(
|
void Parser::ParseLV3MeshVertexListBlock(
|
||||||
|
@ -1443,7 +1440,6 @@ void Parser::ParseLV3MeshVertexListBlock(
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_VERTEX_LIST");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_VERTEX_LIST");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) {
|
void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) {
|
||||||
|
@ -1470,7 +1466,6 @@ void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh)
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_FACE_LIST");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_FACE_LIST");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices,
|
void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices,
|
||||||
|
@ -1503,7 +1498,6 @@ void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices,
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_TVERT_LIST");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_TVERT_LIST");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces,
|
void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces,
|
||||||
|
@ -1532,7 +1526,6 @@ void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces,
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_TFACE_LIST");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_TFACE_LIST");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh &mesh) {
|
void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh &mesh) {
|
||||||
|
@ -1567,7 +1560,6 @@ void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh &mesh) {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_MAPPING_CHANNEL");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_MAPPING_CHANNEL");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh &mesh) {
|
void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh &mesh) {
|
||||||
|
@ -1595,7 +1587,6 @@ void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh &mesh)
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_CVERTEX_LIST");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_CVERTEX_LIST");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) {
|
void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) {
|
||||||
|
@ -1623,7 +1614,6 @@ void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh)
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_CFACE_LIST");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_CFACE_LIST");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) {
|
void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) {
|
||||||
|
@ -1681,7 +1671,6 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) {
|
||||||
}
|
}
|
||||||
AI_ASE_HANDLE_SECTION("3", "*MESH_NORMALS");
|
AI_ASE_HANDLE_SECTION("3", "*MESH_NORMALS");
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Parser::ParseLV4MeshFace(ASE::Face &out) {
|
void Parser::ParseLV4MeshFace(ASE::Face &out) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ For details, see http://sourceforge.net/projects/libb64
|
||||||
|
|
||||||
#include "cencode.h" // changed from <B64/cencode.h>
|
#include "cencode.h" // changed from <B64/cencode.h>
|
||||||
|
|
||||||
const int CHARS_PER_LINE = 72;
|
static const int CHARS_PER_LINE = 72;
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
|
|
|
@ -150,7 +150,7 @@ AI_WONT_RETURN void B3DImporter::Fail(const string &str) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
int B3DImporter::ReadByte() {
|
int B3DImporter::ReadByte() {
|
||||||
if (_pos > _buf.size()) {
|
if (_pos >= _buf.size()) {
|
||||||
Fail("EOF");
|
Fail("EOF");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,7 +418,6 @@ void B3DImporter::ReadTRIS(int v0) {
|
||||||
ASSIMP_LOG_ERROR("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
|
ASSIMP_LOG_ERROR("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
|
||||||
#endif
|
#endif
|
||||||
Fail("Bad triangle index");
|
Fail("Bad triangle index");
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
face->mNumIndices = 3;
|
face->mNumIndices = 3;
|
||||||
face->mIndices = new unsigned[3];
|
face->mIndices = new unsigned[3];
|
||||||
|
|
|
@ -96,7 +96,8 @@ struct CustomDataTypeDescription {
|
||||||
* other (like CD_ORCO, ...) uses arrays of rawtypes or even arrays of Structures
|
* other (like CD_ORCO, ...) uses arrays of rawtypes or even arrays of Structures
|
||||||
* use a special readfunction for that cases
|
* use a special readfunction for that cases
|
||||||
*/
|
*/
|
||||||
std::array<CustomDataTypeDescription, CD_NUMTYPES> customDataTypeDescriptions = { { DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MVert),
|
static std::array<CustomDataTypeDescription, CD_NUMTYPES> customDataTypeDescriptions = { {
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MVert),
|
||||||
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MEdge),
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MEdge),
|
||||||
|
|
|
@ -115,15 +115,12 @@ BlenderImporter::~BlenderImporter() {
|
||||||
delete modifier_cache;
|
delete modifier_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * const Tokens[] = { "BLENDER" };
|
static const char Token[] = "BLENDER";
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// Returns whether the class can handle the format of the given file.
|
||||||
bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
|
bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
|
||||||
// note: this won't catch compressed files
|
return ParseMagicToken(pFile, pIOHandler).error.empty();
|
||||||
static const char *tokens[] = { "<BLENDER", "blender" };
|
|
||||||
|
|
||||||
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -142,63 +139,21 @@ void BlenderImporter::SetupProperties(const Importer * /*pImp*/) {
|
||||||
// Imports the given file into the given scene structure.
|
// Imports the given file into the given scene structure.
|
||||||
void BlenderImporter::InternReadFile(const std::string &pFile,
|
void BlenderImporter::InternReadFile(const std::string &pFile,
|
||||||
aiScene *pScene, IOSystem *pIOHandler) {
|
aiScene *pScene, IOSystem *pIOHandler) {
|
||||||
#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
|
|
||||||
std::vector<char> uncompressed;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FileDatabase file;
|
FileDatabase file;
|
||||||
std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
|
StreamOrError streamOrError = ParseMagicToken(pFile, pIOHandler);
|
||||||
if (!stream) {
|
if (!streamOrError.error.empty()) {
|
||||||
ThrowException("Could not open file for reading");
|
ThrowException(streamOrError.error);
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<IOStream> stream = std::move(streamOrError.stream);
|
||||||
|
|
||||||
char magic[8] = { 0 };
|
char version[4] = { 0 };
|
||||||
stream->Read(magic, 7, 1);
|
file.i64bit = (stream->Read(version, 1, 1), version[0] == '-');
|
||||||
if (strcmp(magic, Tokens[0])) {
|
file.little = (stream->Read(version, 1, 1), version[0] == 'v');
|
||||||
// Check for presence of the gzip header. If yes, assume it is a
|
|
||||||
// compressed blend file and try uncompressing it, else fail. This is to
|
|
||||||
// avoid uncompressing random files which our loader might end up with.
|
|
||||||
#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
|
|
||||||
ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
|
|
||||||
#else
|
|
||||||
if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
|
|
||||||
ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either");
|
|
||||||
}
|
|
||||||
|
|
||||||
LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file");
|
stream->Read(version, 3, 1);
|
||||||
if (magic[2] != 8) {
|
version[3] = '\0';
|
||||||
ThrowException("Unsupported GZIP compression method");
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
|
LogInfo("Blender version is ", version[0], ".", version + 1,
|
||||||
stream->Seek(0L, aiOrigin_SET);
|
|
||||||
std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
|
|
||||||
|
|
||||||
size_t total = 0;
|
|
||||||
Compression compression;
|
|
||||||
if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) {
|
|
||||||
total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), uncompressed);
|
|
||||||
compression.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace the input stream with a memory stream
|
|
||||||
stream = std::make_shared<MemoryIOStream>(reinterpret_cast<uint8_t *>(uncompressed.data()), total);
|
|
||||||
|
|
||||||
// .. and retry
|
|
||||||
stream->Read(magic, 7, 1);
|
|
||||||
if (strcmp(magic, "BLENDER")) {
|
|
||||||
ThrowException("Found no BLENDER magic word in decompressed GZIP file");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
file.i64bit = (stream->Read(magic, 1, 1), magic[0] == '-');
|
|
||||||
file.little = (stream->Read(magic, 1, 1), magic[0] == 'v');
|
|
||||||
|
|
||||||
stream->Read(magic, 3, 1);
|
|
||||||
magic[3] = '\0';
|
|
||||||
|
|
||||||
LogInfo("Blender version is ", magic[0], ".", magic + 1,
|
|
||||||
" (64bit: ", file.i64bit ? "true" : "false",
|
" (64bit: ", file.i64bit ? "true" : "false",
|
||||||
", little endian: ", file.little ? "true" : "false", ")");
|
", little endian: ", file.little ? "true" : "false", ")");
|
||||||
|
|
||||||
|
@ -1338,4 +1293,55 @@ aiNode *BlenderImporter::ConvertNode(const Scene &in, const Object *obj, Convers
|
||||||
return node.release();
|
return node.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlenderImporter::StreamOrError BlenderImporter::ParseMagicToken(const std::string &pFile, IOSystem *pIOHandler) const {
|
||||||
|
std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
|
||||||
|
if (stream == nullptr) {
|
||||||
|
return {{}, {}, "Could not open file for reading"};
|
||||||
|
}
|
||||||
|
|
||||||
|
char magic[8] = { 0 };
|
||||||
|
stream->Read(magic, 7, 1);
|
||||||
|
if (strcmp(magic, Token) == 0) {
|
||||||
|
return {stream, {}, {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for presence of the gzip header. If yes, assume it is a
|
||||||
|
// compressed blend file and try uncompressing it, else fail. This is to
|
||||||
|
// avoid uncompressing random files which our loader might end up with.
|
||||||
|
#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
|
||||||
|
return {{}, {}, "BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?"};
|
||||||
|
#else
|
||||||
|
if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
|
||||||
|
return {{}, {}, "BLENDER magic bytes are missing, couldn't find GZIP header either"};
|
||||||
|
}
|
||||||
|
|
||||||
|
LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file");
|
||||||
|
if (magic[2] != 8) {
|
||||||
|
return {{}, {}, "Unsupported GZIP compression method"};
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
|
||||||
|
stream->Seek(0L, aiOrigin_SET);
|
||||||
|
std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
|
||||||
|
|
||||||
|
size_t total = 0;
|
||||||
|
Compression compression;
|
||||||
|
auto uncompressed = std::make_shared<std::vector<char>>();
|
||||||
|
if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) {
|
||||||
|
total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), *uncompressed);
|
||||||
|
compression.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace the input stream with a memory stream
|
||||||
|
stream = std::make_shared<MemoryIOStream>(reinterpret_cast<uint8_t *>(uncompressed->data()), total);
|
||||||
|
|
||||||
|
// .. and retry
|
||||||
|
stream->Read(magic, 7, 1);
|
||||||
|
if (strcmp(magic, Token) == 0) {
|
||||||
|
return {stream, uncompressed, {}};
|
||||||
|
}
|
||||||
|
return {{}, {}, "Found no BLENDER magic word in decompressed GZIP file"};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
|
#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
|
||||||
|
|
|
@ -180,6 +180,19 @@ private:
|
||||||
const Blender::MTex *tex,
|
const Blender::MTex *tex,
|
||||||
Blender::ConversionData &conv_data);
|
Blender::ConversionData &conv_data);
|
||||||
|
|
||||||
|
// TODO: Move to a std::variant, once c++17 is supported.
|
||||||
|
struct StreamOrError {
|
||||||
|
std::shared_ptr<IOStream> stream;
|
||||||
|
std::shared_ptr<std::vector<char>> input;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns either a stream (and optional input data for the stream) or
|
||||||
|
// an error if it can't parse the magic token.
|
||||||
|
StreamOrError ParseMagicToken(
|
||||||
|
const std::string &pFile,
|
||||||
|
IOSystem *pIOHandler) const;
|
||||||
|
|
||||||
private: // static stuff, mostly logging and error reporting.
|
private: // static stuff, mostly logging and error reporting.
|
||||||
// --------------------
|
// --------------------
|
||||||
static void CheckActualType(const Blender::ElemBase *dt,
|
static void CheckActualType(const Blender::ElemBase *dt,
|
||||||
|
|
|
@ -102,10 +102,6 @@ void Structure::Convert<CollectionObject>(
|
||||||
|
|
||||||
ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
|
ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
|
||||||
{
|
{
|
||||||
//std::shared_ptr<CollectionObject> prev;
|
|
||||||
//ReadFieldPtr<ErrorPolicy_Fail>(prev, "*prev", db);
|
|
||||||
//dest.prev = prev.get();
|
|
||||||
|
|
||||||
std::shared_ptr<Object> ob;
|
std::shared_ptr<Object> ob;
|
||||||
ReadFieldPtr<ErrorPolicy_Igno>(ob, "*ob", db);
|
ReadFieldPtr<ErrorPolicy_Igno>(ob, "*ob", db);
|
||||||
dest.ob = ob.get();
|
dest.ob = ob.get();
|
||||||
|
@ -569,7 +565,7 @@ void Structure ::Convert<MVert>(
|
||||||
const FileDatabase &db) const {
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
ReadFieldArray<ErrorPolicy_Fail>(dest.co, "co", db);
|
ReadFieldArray<ErrorPolicy_Fail>(dest.co, "co", db);
|
||||||
ReadFieldArray<ErrorPolicy_Fail>(dest.no, "no", db);
|
ReadFieldArray<ErrorPolicy_Warn>(dest.no, "no", db);
|
||||||
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
//ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
|
//ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
|
||||||
ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
|
ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
|
||||||
|
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -40,10 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file BlenderTessellator.cpp
|
/// @file BlenderTessellator.cpp
|
||||||
* @brief A simple tessellation wrapper
|
/// @brief A simple tessellation wrapper
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -144,11 +143,7 @@ namespace Assimp
|
||||||
|
|
||||||
#if ASSIMP_BLEND_WITH_POLY_2_TRI
|
#if ASSIMP_BLEND_WITH_POLY_2_TRI
|
||||||
|
|
||||||
#ifdef ASSIMP_USE_HUNTER
|
#include "contrib/poly2tri/poly2tri/poly2tri.h"
|
||||||
# include <poly2tri/poly2tri.h>
|
|
||||||
#else
|
|
||||||
# include "../contrib/poly2tri/poly2tri/poly2tri.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp
|
||||||
{
|
{
|
||||||
|
|
|
@ -95,6 +95,7 @@ ColladaLoader::ColladaLoader() :
|
||||||
noSkeletonMesh(false),
|
noSkeletonMesh(false),
|
||||||
removeEmptyBones(false),
|
removeEmptyBones(false),
|
||||||
ignoreUpDirection(false),
|
ignoreUpDirection(false),
|
||||||
|
ignoreUnitSize(false),
|
||||||
useColladaName(false),
|
useColladaName(false),
|
||||||
mNodeNameCounter(0) {
|
mNodeNameCounter(0) {
|
||||||
// empty
|
// empty
|
||||||
|
@ -122,6 +123,7 @@ void ColladaLoader::SetupProperties(const Importer *pImp) {
|
||||||
noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
|
noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
|
||||||
removeEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true) != 0;
|
removeEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true) != 0;
|
||||||
ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0;
|
ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0;
|
||||||
|
ignoreUnitSize = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UNIT_SIZE, 0) != 0;
|
||||||
useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0;
|
useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,12 +172,15 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO
|
||||||
// ... then fill the materials with the now adjusted settings
|
// ... then fill the materials with the now adjusted settings
|
||||||
FillMaterials(parser, pScene);
|
FillMaterials(parser, pScene);
|
||||||
|
|
||||||
|
if (!ignoreUnitSize) {
|
||||||
// Apply unit-size scale calculation
|
// Apply unit-size scale calculation
|
||||||
|
pScene->mRootNode->mTransformation *= aiMatrix4x4(
|
||||||
pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0,
|
parser.mUnitSize, 0, 0, 0,
|
||||||
0, parser.mUnitSize, 0, 0,
|
0, parser.mUnitSize, 0, 0,
|
||||||
0, 0, parser.mUnitSize, 0,
|
0, 0, parser.mUnitSize, 0,
|
||||||
0, 0, 0, 1);
|
0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ignoreUpDirection) {
|
if (!ignoreUpDirection) {
|
||||||
// Convert to Y_UP, if different orientation
|
// Convert to Y_UP, if different orientation
|
||||||
if (parser.mUpDirection == ColladaParser::UP_X) {
|
if (parser.mUpDirection == ColladaParser::UP_X) {
|
||||||
|
|
|
@ -239,6 +239,7 @@ protected:
|
||||||
bool noSkeletonMesh;
|
bool noSkeletonMesh;
|
||||||
bool removeEmptyBones;
|
bool removeEmptyBones;
|
||||||
bool ignoreUpDirection;
|
bool ignoreUpDirection;
|
||||||
|
bool ignoreUnitSize;
|
||||||
bool useColladaName;
|
bool useColladaName;
|
||||||
|
|
||||||
/** Used by FindNameForNode() to generate unique node names */
|
/** Used by FindNameForNode() to generate unique node names */
|
||||||
|
|
|
@ -1855,7 +1855,6 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
|
||||||
default:
|
default:
|
||||||
// LineStrip is not supported due to expected index unmangling
|
// LineStrip is not supported due to expected index unmangling
|
||||||
throw DeadlyImportError("Unsupported primitive type.");
|
throw DeadlyImportError("Unsupported primitive type.");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// store the face size to later reconstruct the face from
|
// store the face size to later reconstruct the face from
|
||||||
|
|
|
@ -71,7 +71,7 @@ static const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f));
|
||||||
// color indices for DXF - 16 are supported, the table is
|
// color indices for DXF - 16 are supported, the table is
|
||||||
// taken directly from the DXF spec.
|
// taken directly from the DXF spec.
|
||||||
static aiColor4D g_aclrDxfIndexColors[] = {
|
static aiColor4D g_aclrDxfIndexColors[] = {
|
||||||
aiColor4D (0.6f, 0.6f, 0.6f, 1.0f),
|
aiColor4D(0.6f, 0.6f, 0.6f, 1.0f),
|
||||||
aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red
|
aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red
|
||||||
aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green
|
aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green
|
||||||
aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue
|
aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue
|
||||||
|
@ -88,6 +88,7 @@ static aiColor4D g_aclrDxfIndexColors[] = {
|
||||||
aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white
|
aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white
|
||||||
aiColor4D (0.6f, 0.0f, 1.0f, 1.0f) // violet
|
aiColor4D (0.6f, 0.0f, 1.0f, 1.0f) // violet
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
|
#define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
|
||||||
#define AI_DXF_ENTITIES_MAGIC_BLOCK "$ASSIMP_ENTITIES_MAGIC"
|
#define AI_DXF_ENTITIES_MAGIC_BLOCK "$ASSIMP_ENTITIES_MAGIC"
|
||||||
|
|
||||||
|
@ -109,14 +110,6 @@ static const aiImporterDesc desc = {
|
||||||
"dxf"
|
"dxf"
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
// Constructor to be privately used by Importer
|
|
||||||
DXFImporter::DXFImporter() = default;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
// Destructor, private as well
|
|
||||||
DXFImporter::~DXFImporter() = default;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// Returns whether the class can handle the format of the given file.
|
||||||
bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
|
bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
|
||||||
|
@ -229,7 +222,7 @@ void DXFImporter::ConvertMeshes(aiScene* pScene, DXF::FileData& output) {
|
||||||
ASSIMP_LOG_VERBOSE_DEBUG("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount);
|
ASSIMP_LOG_VERBOSE_DEBUG("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! output.blocks.size() ) {
|
if (output.blocks.empty()) {
|
||||||
throw DeadlyImportError("DXF: no data blocks loaded");
|
throw DeadlyImportError("DXF: no data blocks loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,10 +580,11 @@ void DXFImporter::ParseInsertion(DXF::LineReader& reader, DXF::FileData& output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DXF_POLYLINE_FLAG_CLOSED 0x1
|
static constexpr unsigned int DXF_POLYLINE_FLAG_CLOSED = 0x1;
|
||||||
#define DXF_POLYLINE_FLAG_3D_POLYLINE 0x8
|
// Currently unused
|
||||||
#define DXF_POLYLINE_FLAG_3D_POLYMESH 0x10
|
//static constexpr unsigned int DXF_POLYLINE_FLAG_3D_POLYLINE = 0x8;
|
||||||
#define DXF_POLYLINE_FLAG_POLYFACEMESH 0x40
|
//static constexpr unsigned int DXF_POLYLINE_FLAG_3D_POLYMESH = 0x10;
|
||||||
|
static constexpr unsigned int DXF_POLYLINE_FLAG_POLYFACEMESH = 0x40;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output) {
|
void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output) {
|
||||||
|
@ -639,12 +633,6 @@ void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output)
|
||||||
reader++;
|
reader++;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (!(line.flags & DXF_POLYLINE_FLAG_POLYFACEMESH)) {
|
|
||||||
// DefaultLogger::get()->warn((Formatter::format("DXF: polyline not currently supported: "),line.flags));
|
|
||||||
// output.blocks.back().lines.pop_back();
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (vguess && line.positions.size() != vguess) {
|
if (vguess && line.positions.size() != vguess) {
|
||||||
ASSIMP_LOG_WARN("DXF: unexpected vertex count in polymesh: ",
|
ASSIMP_LOG_WARN("DXF: unexpected vertex count in polymesh: ",
|
||||||
line.positions.size(),", expected ", vguess );
|
line.positions.size(),", expected ", vguess );
|
||||||
|
@ -734,12 +722,18 @@ void DXFImporter::ParsePolyLineVertex(DXF::LineReader& reader, DXF::PolyLine& li
|
||||||
case 71:
|
case 71:
|
||||||
case 72:
|
case 72:
|
||||||
case 73:
|
case 73:
|
||||||
case 74:
|
case 74: {
|
||||||
if (cnti == 4) {
|
if (cnti == 4) {
|
||||||
ASSIMP_LOG_WARN("DXF: more than 4 indices per face not supported; ignoring");
|
ASSIMP_LOG_WARN("DXF: more than 4 indices per face not supported; ignoring");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
indices[cnti++] = reader.ValueAsUnsignedInt();
|
const int index = reader.ValueAsSignedInt();
|
||||||
|
if (index >= 0) {
|
||||||
|
indices[cnti++] = static_cast<unsigned int>(index);
|
||||||
|
} else {
|
||||||
|
indices[cnti++] = static_cast<unsigned int>(-index);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// color
|
// color
|
||||||
|
@ -777,8 +771,7 @@ void DXFImporter::ParsePolyLineVertex(DXF::LineReader& reader, DXF::PolyLine& li
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void DXFImporter::Parse3DFace(DXF::LineReader& reader, DXF::FileData& output)
|
void DXFImporter::Parse3DFace(DXF::LineReader& reader, DXF::FileData& output) {
|
||||||
{
|
|
||||||
// (note) this is also used for for parsing line entities, so we
|
// (note) this is also used for for parsing line entities, so we
|
||||||
// must handle the vertex_count == 2 case as well.
|
// must handle the vertex_count == 2 case as well.
|
||||||
|
|
||||||
|
@ -795,8 +788,7 @@ void DXFImporter::Parse3DFace(DXF::LineReader& reader, DXF::FileData& output)
|
||||||
if (reader.GroupCode() == 0) {
|
if (reader.GroupCode() == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (reader.GroupCode())
|
switch (reader.GroupCode()) {
|
||||||
{
|
|
||||||
|
|
||||||
// 8 specifies the layer
|
// 8 specifies the layer
|
||||||
case 8:
|
case 8:
|
||||||
|
|
|
@ -68,8 +68,8 @@ namespace DXF {
|
||||||
*/
|
*/
|
||||||
class DXFImporter : public BaseImporter {
|
class DXFImporter : public BaseImporter {
|
||||||
public:
|
public:
|
||||||
DXFImporter();
|
DXFImporter() = default;
|
||||||
~DXFImporter() override;
|
~DXFImporter() override = default;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
|
|
|
@ -139,6 +139,7 @@ size_t Offset(const char* begin, const char* cursor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
AI_WONT_RETURN void TokenizeError(const std::string& message, const char* begin, const char* cursor) AI_WONT_RETURN_SUFFIX;
|
||||||
void TokenizeError(const std::string& message, const char* begin, const char* cursor) {
|
void TokenizeError(const std::string& message, const char* begin, const char* cursor) {
|
||||||
TokenizeError(message, Offset(begin, cursor));
|
TokenizeError(message, Offset(begin, cursor));
|
||||||
}
|
}
|
||||||
|
@ -341,8 +342,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end, bool const is64bits)
|
bool ReadScope(TokenList &output_tokens, StackAllocator &token_allocator, const char *input, const char *&cursor, const char *end, bool const is64bits) {
|
||||||
{
|
|
||||||
// the first word contains the offset at which this block ends
|
// the first word contains the offset at which this block ends
|
||||||
const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
|
const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
|
||||||
|
|
||||||
|
@ -408,7 +408,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
|
||||||
|
|
||||||
// XXX this is vulnerable to stack overflowing ..
|
// XXX this is vulnerable to stack overflowing ..
|
||||||
while(Offset(input, cursor) < end_offset - sentinel_block_length) {
|
while(Offset(input, cursor) < end_offset - sentinel_block_length) {
|
||||||
ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits);
|
ReadScope(output_tokens, token_allocator, input, cursor, input + end_offset - sentinel_block_length, is64bits);
|
||||||
}
|
}
|
||||||
output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) ));
|
output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) ));
|
||||||
|
|
||||||
|
@ -431,8 +431,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
|
// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
|
||||||
void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
|
void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, StackAllocator &token_allocator) {
|
||||||
{
|
|
||||||
ai_assert(input);
|
ai_assert(input);
|
||||||
ASSIMP_LOG_DEBUG("Tokenizing binary FBX file");
|
ASSIMP_LOG_DEBUG("Tokenizing binary FBX file");
|
||||||
|
|
||||||
|
@ -465,7 +464,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (cursor < end ) {
|
while (cursor < end ) {
|
||||||
if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
|
if (!ReadScope(output_tokens, token_allocator, input, cursor, input + length, is64bits)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,8 @@ FBXConverter::FBXConverter(aiScene *out, const Document &doc, bool removeEmptyBo
|
||||||
mSceneOut(out),
|
mSceneOut(out),
|
||||||
doc(doc),
|
doc(doc),
|
||||||
mRemoveEmptyBones(removeEmptyBones) {
|
mRemoveEmptyBones(removeEmptyBones) {
|
||||||
|
|
||||||
|
|
||||||
// animations need to be converted first since this will
|
// animations need to be converted first since this will
|
||||||
// populate the node_anim_chain_bits map, which is needed
|
// populate the node_anim_chain_bits map, which is needed
|
||||||
// to determine which nodes need to be generated.
|
// to determine which nodes need to be generated.
|
||||||
|
@ -421,16 +423,32 @@ void FBXConverter::ConvertCamera(const Camera &cam, const std::string &orig_name
|
||||||
|
|
||||||
out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
|
out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
|
||||||
|
|
||||||
|
// NOTE: Camera mPosition, mLookAt and mUp must be set to default here.
|
||||||
|
// All transformations to the camera will be handled by its node in the scenegraph.
|
||||||
out_camera->mPosition = aiVector3D(0.0f);
|
out_camera->mPosition = aiVector3D(0.0f);
|
||||||
out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f);
|
out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f);
|
||||||
out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
|
out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
|
||||||
|
|
||||||
out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
|
// NOTE: Some software (maya) does not put FieldOfView in FBX, so we compute
|
||||||
|
// mHorizontalFOV from FocalLength and FilmWidth with unit conversion.
|
||||||
|
|
||||||
out_camera->mClipPlaneNear = cam.NearPlane();
|
// TODO: This is not a complete solution for how FBX cameras can be stored.
|
||||||
out_camera->mClipPlaneFar = cam.FarPlane();
|
// TODO: Incorporate non-square pixel aspect ratio.
|
||||||
|
// TODO: FBX aperture mode might be storing vertical FOV in need of conversion with aspect ratio.
|
||||||
|
|
||||||
|
float fov_deg = cam.FieldOfView();
|
||||||
|
// If FOV not specified in file, compute using FilmWidth and FocalLength.
|
||||||
|
if (fov_deg == kFovUnknown) {
|
||||||
|
float film_width_inches = cam.FilmWidth();
|
||||||
|
float focal_length_mm = cam.FocalLength();
|
||||||
|
ASSIMP_LOG_VERBOSE_DEBUG("FBX FOV unspecified. Computing from FilmWidth (", film_width_inches, "inches) and FocalLength (", focal_length_mm, "mm).");
|
||||||
|
double half_fov_rad = std::atan2(film_width_inches * 25.4 * 0.5, focal_length_mm);
|
||||||
|
out_camera->mHorizontalFOV = static_cast<float>(half_fov_rad);
|
||||||
|
} else {
|
||||||
|
// FBX fov is full-view degrees. We want half-view radians.
|
||||||
|
out_camera->mHorizontalFOV = AI_DEG_TO_RAD(fov_deg) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
|
|
||||||
out_camera->mClipPlaneNear = cam.NearPlane();
|
out_camera->mClipPlaneNear = cam.NearPlane();
|
||||||
out_camera->mClipPlaneFar = cam.FarPlane();
|
out_camera->mClipPlaneFar = cam.FarPlane();
|
||||||
}
|
}
|
||||||
|
@ -640,7 +658,7 @@ void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D &rot
|
||||||
bool FBXConverter::NeedsComplexTransformationChain(const Model &model) {
|
bool FBXConverter::NeedsComplexTransformationChain(const Model &model) {
|
||||||
const PropertyTable &props = model.Props();
|
const PropertyTable &props = model.Props();
|
||||||
|
|
||||||
const auto zero_epsilon = ai_epsilon;
|
const auto zero_epsilon = Math::getEpsilon<ai_real>();
|
||||||
const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
|
const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
|
||||||
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
||||||
const TransformationComp comp = static_cast<TransformationComp>(i);
|
const TransformationComp comp = static_cast<TransformationComp>(i);
|
||||||
|
@ -873,8 +891,12 @@ void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) {
|
||||||
data->Set(index++, prop.first, interpretedBool->Value());
|
data->Set(index++, prop.first, interpretedBool->Value());
|
||||||
} else if (const TypedProperty<int> *interpretedInt = prop.second->As<TypedProperty<int>>()) {
|
} else if (const TypedProperty<int> *interpretedInt = prop.second->As<TypedProperty<int>>()) {
|
||||||
data->Set(index++, prop.first, interpretedInt->Value());
|
data->Set(index++, prop.first, interpretedInt->Value());
|
||||||
|
} else if (const TypedProperty<uint32_t> *interpretedUInt = prop.second->As<TypedProperty<uint32_t>>()) {
|
||||||
|
data->Set(index++, prop.first, interpretedUInt->Value());
|
||||||
} else if (const TypedProperty<uint64_t> *interpretedUint64 = prop.second->As<TypedProperty<uint64_t>>()) {
|
} else if (const TypedProperty<uint64_t> *interpretedUint64 = prop.second->As<TypedProperty<uint64_t>>()) {
|
||||||
data->Set(index++, prop.first, interpretedUint64->Value());
|
data->Set(index++, prop.first, interpretedUint64->Value());
|
||||||
|
} else if (const TypedProperty<int64_t> *interpretedint64 = prop.second->As<TypedProperty<int64_t>>()) {
|
||||||
|
data->Set(index++, prop.first, interpretedint64->Value());
|
||||||
} else if (const TypedProperty<float> *interpretedFloat = prop.second->As<TypedProperty<float>>()) {
|
} else if (const TypedProperty<float> *interpretedFloat = prop.second->As<TypedProperty<float>>()) {
|
||||||
data->Set(index++, prop.first, interpretedFloat->Value());
|
data->Set(index++, prop.first, interpretedFloat->Value());
|
||||||
} else if (const TypedProperty<std::string> *interpretedString = prop.second->As<TypedProperty<std::string>>()) {
|
} else if (const TypedProperty<std::string> *interpretedString = prop.second->As<TypedProperty<std::string>>()) {
|
||||||
|
@ -1176,15 +1198,23 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
|
||||||
std::vector<aiAnimMesh *> animMeshes;
|
std::vector<aiAnimMesh *> animMeshes;
|
||||||
for (const BlendShape *blendShape : mesh.GetBlendShapes()) {
|
for (const BlendShape *blendShape : mesh.GetBlendShapes()) {
|
||||||
for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
|
for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
|
||||||
const std::vector<const ShapeGeometry *> &shapeGeometries = blendShapeChannel->GetShapeGeometries();
|
const auto& shapeGeometries = blendShapeChannel->GetShapeGeometries();
|
||||||
for (size_t i = 0; i < shapeGeometries.size(); i++) {
|
for (const ShapeGeometry *shapeGeometry : shapeGeometries) {
|
||||||
aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
|
aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
|
||||||
const ShapeGeometry *shapeGeometry = shapeGeometries.at(i);
|
const auto &curVertices = shapeGeometry->GetVertices();
|
||||||
const std::vector<aiVector3D> &curVertices = shapeGeometry->GetVertices();
|
const auto &curNormals = shapeGeometry->GetNormals();
|
||||||
const std::vector<aiVector3D> &curNormals = shapeGeometry->GetNormals();
|
const auto &curIndices = shapeGeometry->GetIndices();
|
||||||
const std::vector<unsigned int> &curIndices = shapeGeometry->GetIndices();
|
|
||||||
//losing channel name if using shapeGeometry->Name()
|
//losing channel name if using shapeGeometry->Name()
|
||||||
animMesh->mName.Set(FixAnimMeshName(blendShapeChannel->Name()));
|
// if blendShapeChannel Name is empty or don't have a ".", add geoMetryName;
|
||||||
|
auto aniName = FixAnimMeshName(blendShapeChannel->Name());
|
||||||
|
auto geoMetryName = FixAnimMeshName(shapeGeometry->Name());
|
||||||
|
if (aniName.empty()) {
|
||||||
|
aniName = geoMetryName;
|
||||||
|
}
|
||||||
|
else if (aniName.find('.') == aniName.npos) {
|
||||||
|
aniName += "." + geoMetryName;
|
||||||
|
}
|
||||||
|
animMesh->mName.Set(aniName);
|
||||||
for (size_t j = 0; j < curIndices.size(); j++) {
|
for (size_t j = 0; j < curIndices.size(); j++) {
|
||||||
const unsigned int curIndex = curIndices.at(j);
|
const unsigned int curIndex = curIndices.at(j);
|
||||||
aiVector3D vertex = curVertices.at(j);
|
aiVector3D vertex = curVertices.at(j);
|
||||||
|
@ -1406,13 +1436,12 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
|
||||||
std::vector<aiAnimMesh *> animMeshes;
|
std::vector<aiAnimMesh *> animMeshes;
|
||||||
for (const BlendShape *blendShape : mesh.GetBlendShapes()) {
|
for (const BlendShape *blendShape : mesh.GetBlendShapes()) {
|
||||||
for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
|
for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
|
||||||
const std::vector<const ShapeGeometry *> &shapeGeometries = blendShapeChannel->GetShapeGeometries();
|
const auto& shapeGeometries = blendShapeChannel->GetShapeGeometries();
|
||||||
for (size_t i = 0; i < shapeGeometries.size(); i++) {
|
for (const ShapeGeometry *shapeGeometry : shapeGeometries) {
|
||||||
aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
|
aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
|
||||||
const ShapeGeometry *shapeGeometry = shapeGeometries.at(i);
|
const auto& curVertices = shapeGeometry->GetVertices();
|
||||||
const std::vector<aiVector3D> &curVertices = shapeGeometry->GetVertices();
|
const auto& curNormals = shapeGeometry->GetNormals();
|
||||||
const std::vector<aiVector3D> &curNormals = shapeGeometry->GetNormals();
|
const auto& curIndices = shapeGeometry->GetIndices();
|
||||||
const std::vector<unsigned int> &curIndices = shapeGeometry->GetIndices();
|
|
||||||
animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
|
animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
|
||||||
for (size_t j = 0; j < curIndices.size(); j++) {
|
for (size_t j = 0; j < curIndices.size(); j++) {
|
||||||
unsigned int curIndex = curIndices.at(j);
|
unsigned int curIndex = curIndices.at(j);
|
||||||
|
|
|
@ -154,8 +154,10 @@ BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc,
|
||||||
for (const Connection* con : conns) {
|
for (const Connection* con : conns) {
|
||||||
const BlendShapeChannel* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
|
const BlendShapeChannel* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
|
||||||
if (bspc) {
|
if (bspc) {
|
||||||
blendShapeChannels.push_back(bspc);
|
auto pr = blendShapeChannels.insert(bspc);
|
||||||
continue;
|
if (!pr.second) {
|
||||||
|
FBXImporter::LogWarn("there is the same blendShapeChannel id ", bspc->ID());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,8 +181,10 @@ BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const
|
||||||
for (const Connection* con : conns) {
|
for (const Connection* con : conns) {
|
||||||
const ShapeGeometry* const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
|
const ShapeGeometry* const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
|
||||||
if (sg) {
|
if (sg) {
|
||||||
shapeGeometries.push_back(sg);
|
auto pr = shapeGeometries.insert(sg);
|
||||||
continue;
|
if (!pr.second) {
|
||||||
|
FBXImporter::LogWarn("there is the same shapeGeometrie id ", sg->ID());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,7 +243,7 @@ FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr<cons
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
Document::Document(const Parser& parser, const ImportSettings& settings) :
|
Document::Document(Parser& parser, const ImportSettings& settings) :
|
||||||
settings(settings), parser(parser) {
|
settings(settings), parser(parser) {
|
||||||
ASSIMP_LOG_DEBUG("Creating FBX Document");
|
ASSIMP_LOG_DEBUG("Creating FBX Document");
|
||||||
|
|
||||||
|
@ -265,13 +265,17 @@ Document::Document(const Parser& parser, const ImportSettings& settings) :
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
Document::~Document() {
|
Document::~Document()
|
||||||
for(ObjectMap::value_type& v : objects) {
|
{
|
||||||
delete v.second;
|
// The document does not own the memory for the following objects, but we need to call their d'tor
|
||||||
|
// so they can properly free memory like string members:
|
||||||
|
|
||||||
|
for (ObjectMap::value_type &v : objects) {
|
||||||
|
delete_LazyObject(v.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(ConnectionMap::value_type& v : src_connections) {
|
for (ConnectionMap::value_type &v : src_connections) {
|
||||||
delete v.second;
|
delete_Connection(v.second);
|
||||||
}
|
}
|
||||||
// |dest_connections| contain the same Connection objects as the |src_connections|
|
// |dest_connections| contain the same Connection objects as the |src_connections|
|
||||||
}
|
}
|
||||||
|
@ -356,9 +360,11 @@ void Document::ReadObjects() {
|
||||||
DOMError("no Objects dictionary found");
|
DOMError("no Objects dictionary found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StackAllocator &allocator = parser.GetAllocator();
|
||||||
|
|
||||||
// add a dummy entry to represent the Model::RootNode object (id 0),
|
// add a dummy entry to represent the Model::RootNode object (id 0),
|
||||||
// which is only indirectly defined in the input file
|
// which is only indirectly defined in the input file
|
||||||
objects[0] = new LazyObject(0L, *eobjects, *this);
|
objects[0] = new_LazyObject(0L, *eobjects, *this);
|
||||||
|
|
||||||
const Scope& sobjects = *eobjects->Compound();
|
const Scope& sobjects = *eobjects->Compound();
|
||||||
for(const ElementMap::value_type& el : sobjects.Elements()) {
|
for(const ElementMap::value_type& el : sobjects.Elements()) {
|
||||||
|
@ -381,11 +387,13 @@ void Document::ReadObjects() {
|
||||||
DOMError("encountered object with implicitly defined id 0",el.second);
|
DOMError("encountered object with implicitly defined id 0",el.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(objects.find(id) != objects.end()) {
|
const auto foundObject = objects.find(id);
|
||||||
|
if(foundObject != objects.end()) {
|
||||||
DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
|
DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
|
||||||
|
delete foundObject->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
objects[id] = new LazyObject(id, *el.second, *this);
|
objects[id] = new_LazyObject(id, *el.second, *this);
|
||||||
|
|
||||||
// grab all animation stacks upfront since there is no listing of them
|
// grab all animation stacks upfront since there is no listing of them
|
||||||
if(!strcmp(el.first.c_str(),"AnimationStack")) {
|
if(!strcmp(el.first.c_str(),"AnimationStack")) {
|
||||||
|
@ -452,8 +460,10 @@ void Document::ReadPropertyTemplates() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Document::ReadConnections() {
|
void Document::ReadConnections()
|
||||||
const Scope& sc = parser.GetRootScope();
|
{
|
||||||
|
StackAllocator &allocator = parser.GetAllocator();
|
||||||
|
const Scope &sc = parser.GetRootScope();
|
||||||
// read property templates from "Definitions" section
|
// read property templates from "Definitions" section
|
||||||
const Element* const econns = sc["Connections"];
|
const Element* const econns = sc["Connections"];
|
||||||
if(!econns || !econns->Compound()) {
|
if(!econns || !econns->Compound()) {
|
||||||
|
@ -492,7 +502,7 @@ void Document::ReadConnections() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new connection
|
// add new connection
|
||||||
const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this);
|
const Connection* const c = new_Connection(insertionOrder++,src,dest,prop,*this);
|
||||||
src_connections.insert(ConnectionMap::value_type(src,c));
|
src_connections.insert(ConnectionMap::value_type(src,c));
|
||||||
dest_connections.insert(ConnectionMap::value_type(dest,c));
|
dest_connections.insert(ConnectionMap::value_type(dest,c));
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#define INCLUDED_AI_FBX_DOCUMENT_H
|
#define INCLUDED_AI_FBX_DOCUMENT_H
|
||||||
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <unordered_set>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <assimp/mesh.h>
|
#include <assimp/mesh.h>
|
||||||
#include "FBXProperties.h"
|
#include "FBXProperties.h"
|
||||||
|
@ -54,9 +55,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#define _AI_CONCAT(a,b) a ## b
|
#define _AI_CONCAT(a,b) a ## b
|
||||||
#define AI_CONCAT(a,b) _AI_CONCAT(a,b)
|
#define AI_CONCAT(a,b) _AI_CONCAT(a,b)
|
||||||
|
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace FBX {
|
namespace FBX {
|
||||||
|
|
||||||
|
// Use an 'illegal' default FOV value to detect if the FBX camera has set the FOV.
|
||||||
|
static const float kFovUnknown = -1.0f;
|
||||||
|
|
||||||
|
|
||||||
class Parser;
|
class Parser;
|
||||||
class Object;
|
class Object;
|
||||||
struct ImportSettings;
|
struct ImportSettings;
|
||||||
|
@ -80,6 +86,10 @@ class BlendShape;
|
||||||
class Skin;
|
class Skin;
|
||||||
class Cluster;
|
class Cluster;
|
||||||
|
|
||||||
|
#define new_LazyObject new (allocator.Allocate(sizeof(LazyObject))) LazyObject
|
||||||
|
#define new_Connection new (allocator.Allocate(sizeof(Connection))) Connection
|
||||||
|
#define delete_LazyObject(_p) (_p)->~LazyObject()
|
||||||
|
#define delete_Connection(_p) (_p)->~Connection()
|
||||||
|
|
||||||
/** Represents a delay-parsed FBX objects. Many objects in the scene
|
/** Represents a delay-parsed FBX objects. Many objects in the scene
|
||||||
* are not needed by assimp, so it makes no sense to parse them
|
* are not needed by assimp, so it makes no sense to parse them
|
||||||
|
@ -242,7 +252,7 @@ public:
|
||||||
fbx_simple_property(FilmAspectRatio, float, 1.0f)
|
fbx_simple_property(FilmAspectRatio, float, 1.0f)
|
||||||
fbx_simple_property(ApertureMode, int, 0)
|
fbx_simple_property(ApertureMode, int, 0)
|
||||||
|
|
||||||
fbx_simple_property(FieldOfView, float, 1.0f)
|
fbx_simple_property(FieldOfView, float, kFovUnknown)
|
||||||
fbx_simple_property(FocalLength, float, 1.0f)
|
fbx_simple_property(FocalLength, float, 1.0f)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -855,14 +865,14 @@ public:
|
||||||
return fullWeights;
|
return fullWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<const ShapeGeometry*>& GetShapeGeometries() const {
|
const std::unordered_set<const ShapeGeometry*>& GetShapeGeometries() const {
|
||||||
return shapeGeometries;
|
return shapeGeometries;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float percent;
|
float percent;
|
||||||
WeightArray fullWeights;
|
WeightArray fullWeights;
|
||||||
std::vector<const ShapeGeometry*> shapeGeometries;
|
std::unordered_set<const ShapeGeometry*> shapeGeometries;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** DOM class for BlendShape deformers */
|
/** DOM class for BlendShape deformers */
|
||||||
|
@ -872,12 +882,12 @@ public:
|
||||||
|
|
||||||
virtual ~BlendShape();
|
virtual ~BlendShape();
|
||||||
|
|
||||||
const std::vector<const BlendShapeChannel*>& BlendShapeChannels() const {
|
const std::unordered_set<const BlendShapeChannel*>& BlendShapeChannels() const {
|
||||||
return blendShapeChannels;
|
return blendShapeChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<const BlendShapeChannel*> blendShapeChannels;
|
std::unordered_set<const BlendShapeChannel*> blendShapeChannels;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** DOM class for skin deformer clusters (aka sub-deformers) */
|
/** DOM class for skin deformer clusters (aka sub-deformers) */
|
||||||
|
@ -1072,7 +1082,7 @@ private:
|
||||||
/** DOM root for a FBX file */
|
/** DOM root for a FBX file */
|
||||||
class Document {
|
class Document {
|
||||||
public:
|
public:
|
||||||
Document(const Parser& parser, const ImportSettings& settings);
|
Document(Parser& parser, const ImportSettings& settings);
|
||||||
|
|
||||||
~Document();
|
~Document();
|
||||||
|
|
||||||
|
@ -1156,7 +1166,7 @@ private:
|
||||||
const ImportSettings& settings;
|
const ImportSettings& settings;
|
||||||
|
|
||||||
ObjectMap objects;
|
ObjectMap objects;
|
||||||
const Parser& parser;
|
Parser& parser;
|
||||||
|
|
||||||
PropertyTemplateMap templates;
|
PropertyTemplateMap templates;
|
||||||
ConnectionMap src_connections;
|
ConnectionMap src_connections;
|
||||||
|
|
|
@ -152,19 +152,19 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
// broad-phase tokenized pass in which we identify the core
|
// broad-phase tokenized pass in which we identify the core
|
||||||
// syntax elements of FBX (brackets, commas, key:value mappings)
|
// syntax elements of FBX (brackets, commas, key:value mappings)
|
||||||
TokenList tokens;
|
TokenList tokens;
|
||||||
|
Assimp::StackAllocator tempAllocator;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
bool is_binary = false;
|
bool is_binary = false;
|
||||||
if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
|
if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
|
||||||
is_binary = true;
|
is_binary = true;
|
||||||
TokenizeBinary(tokens, begin, contents.size());
|
TokenizeBinary(tokens, begin, contents.size(), tempAllocator);
|
||||||
} else {
|
} else {
|
||||||
Tokenize(tokens, begin);
|
Tokenize(tokens, begin, tempAllocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// use this information to construct a very rudimentary
|
// use this information to construct a very rudimentary
|
||||||
// parse-tree representing the FBX scope structure
|
// parse-tree representing the FBX scope structure
|
||||||
Parser parser(tokens, is_binary);
|
Parser parser(tokens, tempAllocator, is_binary);
|
||||||
|
|
||||||
// take the raw parse-tree and convert it to a FBX DOM
|
// take the raw parse-tree and convert it to a FBX DOM
|
||||||
Document doc(parser, mSettings);
|
Document doc(parser, mSettings);
|
||||||
|
@ -183,9 +183,11 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
// assimp universal format (M)
|
// assimp universal format (M)
|
||||||
SetFileScale(size_relative_to_cm * 0.01f);
|
SetFileScale(size_relative_to_cm * 0.01f);
|
||||||
|
|
||||||
std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
|
// This collection does not own the memory for the tokens, but we need to call their d'tor
|
||||||
|
std::for_each(tokens.begin(), tokens.end(), Util::destructor_fun<Token>());
|
||||||
|
|
||||||
} catch (std::exception &) {
|
} catch (std::exception &) {
|
||||||
std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
|
std::for_each(tokens.begin(), tokens.end(), Util::destructor_fun<Token>());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,20 +138,6 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
Material::~Material() = default;
|
Material::~Material() = default;
|
||||||
|
|
||||||
aiVector2D uvTrans;
|
|
||||||
aiVector2D uvScaling;
|
|
||||||
ai_real uvRotation;
|
|
||||||
|
|
||||||
std::string type;
|
|
||||||
std::string relativeFileName;
|
|
||||||
std::string fileName;
|
|
||||||
std::string alphaSource;
|
|
||||||
std::shared_ptr<const PropertyTable> props;
|
|
||||||
|
|
||||||
unsigned int crop[4]{};
|
|
||||||
|
|
||||||
const Video* media;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
|
Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
|
||||||
Object(id,element,name),
|
Object(id,element,name),
|
||||||
|
|
|
@ -69,13 +69,16 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name,
|
||||||
}
|
}
|
||||||
const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
|
const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
|
||||||
if (bsp) {
|
if (bsp) {
|
||||||
blendShapes.push_back(bsp);
|
auto pr = blendShapes.insert(bsp);
|
||||||
|
if (!pr.second) {
|
||||||
|
FBXImporter::LogWarn("there is the same blendShape id ", bsp->ID());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
|
const std::unordered_set<const BlendShape*>& Geometry::GetBlendShapes() const {
|
||||||
return blendShapes;
|
return blendShapes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,11 +72,12 @@ public:
|
||||||
|
|
||||||
/// @brief Get the BlendShape attached to this geometry or nullptr
|
/// @brief Get the BlendShape attached to this geometry or nullptr
|
||||||
/// @return The blendshape arrays.
|
/// @return The blendshape arrays.
|
||||||
const std::vector<const BlendShape*>& GetBlendShapes() const;
|
const std::unordered_set<const BlendShape*>& GetBlendShapes() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Skin* skin;
|
const Skin* skin;
|
||||||
std::vector<const BlendShape*> blendShapes;
|
std::unordered_set<const BlendShape*> blendShapes;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<int> MatIndexArray;
|
typedef std::vector<int> MatIndexArray;
|
||||||
|
|
|
@ -88,6 +88,7 @@ namespace {
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
AI_WONT_RETURN void ParseError(const std::string& message, TokenPtr token) AI_WONT_RETURN_SUFFIX;
|
||||||
void ParseError(const std::string& message, TokenPtr token)
|
void ParseError(const std::string& message, TokenPtr token)
|
||||||
{
|
{
|
||||||
if(token) {
|
if(token) {
|
||||||
|
@ -115,8 +116,11 @@ namespace Assimp {
|
||||||
namespace FBX {
|
namespace FBX {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
Element::Element(const Token& key_token, Parser& parser) : key_token(key_token) {
|
Element::Element(const Token& key_token, Parser& parser) :
|
||||||
|
key_token(key_token), compound(nullptr)
|
||||||
|
{
|
||||||
TokenPtr n = nullptr;
|
TokenPtr n = nullptr;
|
||||||
|
StackAllocator &allocator = parser.GetAllocator();
|
||||||
do {
|
do {
|
||||||
n = parser.AdvanceToNextToken();
|
n = parser.AdvanceToNextToken();
|
||||||
if(!n) {
|
if(!n) {
|
||||||
|
@ -145,7 +149,7 @@ Element::Element(const Token& key_token, Parser& parser) : key_token(key_token)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n->Type() == TokenType_OPEN_BRACKET) {
|
if (n->Type() == TokenType_OPEN_BRACKET) {
|
||||||
compound.reset(new Scope(parser));
|
compound = new_Scope(parser);
|
||||||
|
|
||||||
// current token should be a TOK_CLOSE_BRACKET
|
// current token should be a TOK_CLOSE_BRACKET
|
||||||
n = parser.CurrentToken();
|
n = parser.CurrentToken();
|
||||||
|
@ -163,6 +167,15 @@ Element::Element(const Token& key_token, Parser& parser) : key_token(key_token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
Element::~Element()
|
||||||
|
{
|
||||||
|
if (compound) {
|
||||||
|
delete_Scope(compound);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to delete tokens, they are owned by the parser
|
||||||
|
}
|
||||||
|
|
||||||
Scope::Scope(Parser& parser,bool topLevel)
|
Scope::Scope(Parser& parser,bool topLevel)
|
||||||
{
|
{
|
||||||
if(!topLevel) {
|
if(!topLevel) {
|
||||||
|
@ -172,6 +185,7 @@ Scope::Scope(Parser& parser,bool topLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StackAllocator &allocator = parser.GetAllocator();
|
||||||
TokenPtr n = parser.AdvanceToNextToken();
|
TokenPtr n = parser.AdvanceToNextToken();
|
||||||
if (n == nullptr) {
|
if (n == nullptr) {
|
||||||
ParseError("unexpected end of file");
|
ParseError("unexpected end of file");
|
||||||
|
@ -188,36 +202,45 @@ Scope::Scope(Parser& parser,bool topLevel)
|
||||||
ParseError("unexpected content: empty string.");
|
ParseError("unexpected content: empty string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.insert(ElementMap::value_type(str,new_Element(*n,parser)));
|
auto *element = new_Element(*n, parser);
|
||||||
|
|
||||||
// Element() should stop at the next Key token (or right after a Close token)
|
// Element() should stop at the next Key token (or right after a Close token)
|
||||||
n = parser.CurrentToken();
|
n = parser.CurrentToken();
|
||||||
if (n == nullptr) {
|
if (n == nullptr) {
|
||||||
if (topLevel) {
|
if (topLevel) {
|
||||||
|
elements.insert(ElementMap::value_type(str, element));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
delete_Element(element);
|
||||||
ParseError("unexpected end of file",parser.LastToken());
|
ParseError("unexpected end of file",parser.LastToken());
|
||||||
|
} else {
|
||||||
|
elements.insert(ElementMap::value_type(str, element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
Scope::~Scope() {
|
Scope::~Scope()
|
||||||
for(ElementMap::value_type& v : elements) {
|
{
|
||||||
delete v.second;
|
// This collection does not own the memory for the elements, but we need to call their d'tor:
|
||||||
|
|
||||||
|
for (ElementMap::value_type &v : elements) {
|
||||||
|
delete_Element(v.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
Parser::Parser (const TokenList& tokens, bool is_binary)
|
Parser::Parser(const TokenList &tokens, StackAllocator &allocator, bool is_binary) :
|
||||||
: tokens(tokens)
|
tokens(tokens), allocator(allocator), last(), current(), cursor(tokens.begin()), is_binary(is_binary)
|
||||||
, last()
|
|
||||||
, current()
|
|
||||||
, cursor(tokens.begin())
|
|
||||||
, is_binary(is_binary)
|
|
||||||
{
|
{
|
||||||
ASSIMP_LOG_DEBUG("Parsing FBX tokens");
|
ASSIMP_LOG_DEBUG("Parsing FBX tokens");
|
||||||
root.reset(new Scope(*this,true));
|
root = new_Scope(*this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
Parser::~Parser()
|
||||||
|
{
|
||||||
|
delete_Scope(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/LogAux.h>
|
#include <assimp/LogAux.h>
|
||||||
#include <assimp/fast_atof.h>
|
#include <assimp/fast_atof.h>
|
||||||
|
|
||||||
|
#include "Common/StackAllocator.h"
|
||||||
#include "FBXCompileConfig.h"
|
#include "FBXCompileConfig.h"
|
||||||
#include "FBXTokenizer.h"
|
#include "FBXTokenizer.h"
|
||||||
|
|
||||||
|
@ -63,14 +64,14 @@ class Parser;
|
||||||
class Element;
|
class Element;
|
||||||
|
|
||||||
// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03
|
// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03
|
||||||
typedef std::vector< Scope* > ScopeList;
|
using ScopeList = std::vector<Scope*>;
|
||||||
typedef std::fbx_unordered_multimap< std::string, Element* > ElementMap;
|
using ElementMap = std::fbx_unordered_multimap< std::string, Element*>;
|
||||||
|
using ElementCollection = std::pair<ElementMap::const_iterator,ElementMap::const_iterator>;
|
||||||
typedef std::pair<ElementMap::const_iterator,ElementMap::const_iterator> ElementCollection;
|
|
||||||
|
|
||||||
# define new_Scope new Scope
|
|
||||||
# define new_Element new Element
|
|
||||||
|
|
||||||
|
#define new_Scope new (allocator.Allocate(sizeof(Scope))) Scope
|
||||||
|
#define new_Element new (allocator.Allocate(sizeof(Element))) Element
|
||||||
|
#define delete_Scope(_p) (_p)->~Scope()
|
||||||
|
#define delete_Element(_p) (_p)->~Element()
|
||||||
|
|
||||||
/** FBX data entity that consists of a key:value tuple.
|
/** FBX data entity that consists of a key:value tuple.
|
||||||
*
|
*
|
||||||
|
@ -82,15 +83,16 @@ typedef std::pair<ElementMap::const_iterator,ElementMap::const_iterator> Element
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*
|
*
|
||||||
* As can be seen in this sample, elements can contain nested #Scope
|
* As can be seen in this sample, elements can contain nested #Scope
|
||||||
* as their trailing member. **/
|
* as their trailing member.
|
||||||
|
**/
|
||||||
class Element
|
class Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Element(const Token& key_token, Parser& parser);
|
Element(const Token& key_token, Parser& parser);
|
||||||
~Element() = default;
|
~Element();
|
||||||
|
|
||||||
const Scope* Compound() const {
|
const Scope* Compound() const {
|
||||||
return compound.get();
|
return compound;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token& KeyToken() const {
|
const Token& KeyToken() const {
|
||||||
|
@ -104,7 +106,7 @@ public:
|
||||||
private:
|
private:
|
||||||
const Token& key_token;
|
const Token& key_token;
|
||||||
TokenList tokens;
|
TokenList tokens;
|
||||||
std::unique_ptr<Scope> compound;
|
Scope* compound;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** FBX data entity that consists of a 'scope', a collection
|
/** FBX data entity that consists of a 'scope', a collection
|
||||||
|
@ -159,8 +161,8 @@ class Parser
|
||||||
public:
|
public:
|
||||||
/** Parse given a token list. Does not take ownership of the tokens -
|
/** Parse given a token list. Does not take ownership of the tokens -
|
||||||
* the objects must persist during the entire parser lifetime */
|
* the objects must persist during the entire parser lifetime */
|
||||||
Parser (const TokenList& tokens,bool is_binary);
|
Parser(const TokenList &tokens, StackAllocator &allocator, bool is_binary);
|
||||||
~Parser() = default;
|
~Parser();
|
||||||
|
|
||||||
const Scope& GetRootScope() const {
|
const Scope& GetRootScope() const {
|
||||||
return *root;
|
return *root;
|
||||||
|
@ -170,6 +172,10 @@ public:
|
||||||
return is_binary;
|
return is_binary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StackAllocator &GetAllocator() {
|
||||||
|
return allocator;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Scope;
|
friend class Scope;
|
||||||
friend class Element;
|
friend class Element;
|
||||||
|
@ -180,10 +186,10 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const TokenList& tokens;
|
const TokenList& tokens;
|
||||||
|
StackAllocator &allocator;
|
||||||
TokenPtr last, current;
|
TokenPtr last, current;
|
||||||
TokenList::const_iterator cursor;
|
TokenList::const_iterator cursor;
|
||||||
std::unique_ptr<Scope> root;
|
Scope *root;
|
||||||
|
|
||||||
const bool is_binary;
|
const bool is_binary;
|
||||||
};
|
};
|
||||||
|
|
|
@ -94,7 +94,8 @@ AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line,
|
||||||
|
|
||||||
// process a potential data token up to 'cur', adding it to 'output_tokens'.
|
// process a potential data token up to 'cur', adding it to 'output_tokens'.
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*& end,
|
void ProcessDataToken(TokenList &output_tokens, StackAllocator &token_allocator,
|
||||||
|
const char*& start, const char*& end,
|
||||||
unsigned int line,
|
unsigned int line,
|
||||||
unsigned int column,
|
unsigned int column,
|
||||||
TokenType type = TokenType_DATA,
|
TokenType type = TokenType_DATA,
|
||||||
|
@ -131,8 +132,7 @@ void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Tokenize(TokenList& output_tokens, const char* input)
|
void Tokenize(TokenList &output_tokens, const char *input, StackAllocator &token_allocator) {
|
||||||
{
|
|
||||||
ai_assert(input);
|
ai_assert(input);
|
||||||
ASSIMP_LOG_DEBUG("Tokenizing ASCII FBX file");
|
ASSIMP_LOG_DEBUG("Tokenizing ASCII FBX file");
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ void Tokenize(TokenList& output_tokens, const char* input)
|
||||||
in_double_quotes = false;
|
in_double_quotes = false;
|
||||||
token_end = cur;
|
token_end = cur;
|
||||||
|
|
||||||
ProcessDataToken(output_tokens,token_begin,token_end,line,column);
|
ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
|
||||||
pending_data_token = false;
|
pending_data_token = false;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -181,30 +181,30 @@ void Tokenize(TokenList& output_tokens, const char* input)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case ';':
|
case ';':
|
||||||
ProcessDataToken(output_tokens,token_begin,token_end,line,column);
|
ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
|
||||||
comment = true;
|
comment = true;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case '{':
|
case '{':
|
||||||
ProcessDataToken(output_tokens,token_begin,token_end, line, column);
|
ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
|
||||||
output_tokens.push_back(new_Token(cur,cur+1,TokenType_OPEN_BRACKET,line,column));
|
output_tokens.push_back(new_Token(cur,cur+1,TokenType_OPEN_BRACKET,line,column));
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case '}':
|
case '}':
|
||||||
ProcessDataToken(output_tokens,token_begin,token_end,line,column);
|
ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
|
||||||
output_tokens.push_back(new_Token(cur,cur+1,TokenType_CLOSE_BRACKET,line,column));
|
output_tokens.push_back(new_Token(cur,cur+1,TokenType_CLOSE_BRACKET,line,column));
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case ',':
|
case ',':
|
||||||
if (pending_data_token) {
|
if (pending_data_token) {
|
||||||
ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_DATA,true);
|
ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, TokenType_DATA, true);
|
||||||
}
|
}
|
||||||
output_tokens.push_back(new_Token(cur,cur+1,TokenType_COMMA,line,column));
|
output_tokens.push_back(new_Token(cur,cur+1,TokenType_COMMA,line,column));
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case ':':
|
case ':':
|
||||||
if (pending_data_token) {
|
if (pending_data_token) {
|
||||||
ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_KEY,true);
|
ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, TokenType_KEY, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
TokenizeError("unexpected colon", line, column);
|
TokenizeError("unexpected colon", line, column);
|
||||||
|
@ -226,7 +226,7 @@ void Tokenize(TokenList& output_tokens, const char* input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessDataToken(output_tokens,token_begin,token_end,line,column,type);
|
ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
pending_data_token = false;
|
pending_data_token = false;
|
||||||
|
|
|
@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#define INCLUDED_AI_FBX_TOKENIZER_H
|
#define INCLUDED_AI_FBX_TOKENIZER_H
|
||||||
|
|
||||||
#include "FBXCompileConfig.h"
|
#include "FBXCompileConfig.h"
|
||||||
|
#include "Common/StackAllocator.h"
|
||||||
#include <assimp/ai_assert.h>
|
#include <assimp/ai_assert.h>
|
||||||
#include <assimp/defs.h>
|
#include <assimp/defs.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -157,7 +158,8 @@ private:
|
||||||
typedef const Token* TokenPtr;
|
typedef const Token* TokenPtr;
|
||||||
typedef std::vector< TokenPtr > TokenList;
|
typedef std::vector< TokenPtr > TokenList;
|
||||||
|
|
||||||
#define new_Token new Token
|
#define new_Token new (token_allocator.Allocate(sizeof(Token))) Token
|
||||||
|
#define delete_Token(_p) (_p)->~Token()
|
||||||
|
|
||||||
|
|
||||||
/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens.
|
/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens.
|
||||||
|
@ -167,7 +169,7 @@ typedef std::vector< TokenPtr > TokenList;
|
||||||
* @param output_tokens Receives a list of all tokens in the input data.
|
* @param output_tokens Receives a list of all tokens in the input data.
|
||||||
* @param input_buffer Textual input buffer to be processed, 0-terminated.
|
* @param input_buffer Textual input buffer to be processed, 0-terminated.
|
||||||
* @throw DeadlyImportError if something goes wrong */
|
* @throw DeadlyImportError if something goes wrong */
|
||||||
void Tokenize(TokenList& output_tokens, const char* input);
|
void Tokenize(TokenList &output_tokens, const char *input, StackAllocator &tokenAllocator);
|
||||||
|
|
||||||
|
|
||||||
/** Tokenizer function for binary FBX files.
|
/** Tokenizer function for binary FBX files.
|
||||||
|
@ -178,7 +180,7 @@ void Tokenize(TokenList& output_tokens, const char* input);
|
||||||
* @param input_buffer Binary input buffer to be processed.
|
* @param input_buffer Binary input buffer to be processed.
|
||||||
* @param length Length of input buffer, in bytes. There is no 0-terminal.
|
* @param length Length of input buffer, in bytes. There is no 0-terminal.
|
||||||
* @throw DeadlyImportError if something goes wrong */
|
* @throw DeadlyImportError if something goes wrong */
|
||||||
void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length);
|
void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, StackAllocator &tokenAllocator);
|
||||||
|
|
||||||
|
|
||||||
} // ! FBX
|
} // ! FBX
|
||||||
|
|
|
@ -66,6 +66,17 @@ struct delete_fun
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** helper for std::for_each to call the destructor on all items in a container without freeing their heap*/
|
||||||
|
template <typename T>
|
||||||
|
struct destructor_fun {
|
||||||
|
void operator()(const volatile T* del) {
|
||||||
|
if (del) {
|
||||||
|
del->~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/** Get a string representation for a #TokenType. */
|
/** Get a string representation for a #TokenType. */
|
||||||
const char* TokenTypeString(TokenType t);
|
const char* TokenTypeString(TokenType t);
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,9 @@ void HMPImporter::InternReadFile(const std::string &pFile,
|
||||||
throw DeadlyImportError("HMP File is too small.");
|
throw DeadlyImportError("HMP File is too small.");
|
||||||
|
|
||||||
// Allocate storage and copy the contents of the file to a memory buffer
|
// Allocate storage and copy the contents of the file to a memory buffer
|
||||||
mBuffer = new uint8_t[fileSize];
|
auto deleter=[this](uint8_t* ptr){ delete[] ptr; mBuffer = nullptr; };
|
||||||
|
std::unique_ptr<uint8_t[], decltype(deleter)> buffer(new uint8_t[fileSize], deleter);
|
||||||
|
mBuffer = buffer.get();
|
||||||
file->Read((void *)mBuffer, 1, fileSize);
|
file->Read((void *)mBuffer, 1, fileSize);
|
||||||
iFileSize = (unsigned int)fileSize;
|
iFileSize = (unsigned int)fileSize;
|
||||||
|
|
||||||
|
@ -143,9 +145,6 @@ void HMPImporter::InternReadFile(const std::string &pFile,
|
||||||
// Print the magic word to the logger
|
// Print the magic word to the logger
|
||||||
std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic));
|
std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic));
|
||||||
|
|
||||||
delete[] mBuffer;
|
|
||||||
mBuffer = nullptr;
|
|
||||||
|
|
||||||
// We're definitely unable to load this file
|
// We're definitely unable to load this file
|
||||||
throw DeadlyImportError("Unknown HMP subformat ", pFile,
|
throw DeadlyImportError("Unknown HMP subformat ", pFile,
|
||||||
". Magic word (", szBuffer, ") is not known");
|
". Magic word (", szBuffer, ") is not known");
|
||||||
|
@ -153,9 +152,6 @@ void HMPImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
// Set the AI_SCENE_FLAGS_TERRAIN bit
|
// Set the AI_SCENE_FLAGS_TERRAIN bit
|
||||||
pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
|
pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
|
||||||
|
|
||||||
delete[] mBuffer;
|
|
||||||
mBuffer = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -445,11 +441,11 @@ void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char *szC
|
||||||
szCursor += sizeof(uint32_t);
|
szCursor += sizeof(uint32_t);
|
||||||
|
|
||||||
// allocate an output material
|
// allocate an output material
|
||||||
aiMaterial *pcMat = new aiMaterial();
|
std::unique_ptr<aiMaterial> pcMat(new aiMaterial());
|
||||||
|
|
||||||
// read the skin, this works exactly as for MDL7
|
// read the skin, this works exactly as for MDL7
|
||||||
ParseSkinLump_3DGS_MDL7(szCursor, &szCursor,
|
ParseSkinLump_3DGS_MDL7(szCursor, &szCursor,
|
||||||
pcMat, iType, iWidth, iHeight);
|
pcMat.get(), iType, iWidth, iHeight);
|
||||||
|
|
||||||
// now we need to skip any other skins ...
|
// now we need to skip any other skins ...
|
||||||
for (unsigned int i = 1; i < iNumSkins; ++i) {
|
for (unsigned int i = 1; i < iNumSkins; ++i) {
|
||||||
|
@ -468,7 +464,7 @@ void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char *szC
|
||||||
// setup the material ...
|
// setup the material ...
|
||||||
pScene->mNumMaterials = 1;
|
pScene->mNumMaterials = 1;
|
||||||
pScene->mMaterials = new aiMaterial *[1];
|
pScene->mMaterials = new aiMaterial *[1];
|
||||||
pScene->mMaterials[0] = pcMat;
|
pScene->mMaterials[0] = pcMat.release();
|
||||||
|
|
||||||
*szCursorOut = szCursor;
|
*szCursorOut = szCursor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ protected:
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Import a HMP4 file
|
/** Import a HMP4 file
|
||||||
*/
|
*/
|
||||||
void InternReadFile_HMP4();
|
AI_WONT_RETURN void InternReadFile_HMP4() AI_WONT_RETURN_SUFFIX;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Import a HMP5 file
|
/** Import a HMP5 file
|
||||||
|
|
|
@ -38,9 +38,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file IFCBoolean.cpp
|
/// @file IFCBoolean.cpp
|
||||||
* @brief Implements a subset of Ifc boolean operations
|
/// @brief Implements a subset of Ifc boolean operations
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
||||||
|
@ -48,7 +47,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "Common/PolyTools.h"
|
#include "Common/PolyTools.h"
|
||||||
#include "PostProcessing/ProcessHelper.h"
|
#include "PostProcessing/ProcessHelper.h"
|
||||||
|
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -67,8 +65,9 @@ bool IntersectSegmentPlane(const IfcVector3 &p, const IfcVector3 &n, const IfcVe
|
||||||
|
|
||||||
// if segment ends on plane, do not report a hit. We stay on that side until a following segment starting at this
|
// if segment ends on plane, do not report a hit. We stay on that side until a following segment starting at this
|
||||||
// point leaves the plane through the other side
|
// point leaves the plane through the other side
|
||||||
if (std::abs(dotOne + dotTwo) < ai_epsilon)
|
if (std::abs(dotOne + dotTwo) < ai_epsilon) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// if segment starts on the plane, report a hit only if the end lies on the *other* side
|
// if segment starts on the plane, report a hit only if the end lies on the *other* side
|
||||||
if (std::abs(dotTwo) < ai_epsilon) {
|
if (std::abs(dotTwo) < ai_epsilon) {
|
||||||
|
@ -82,13 +81,15 @@ bool IntersectSegmentPlane(const IfcVector3 &p, const IfcVector3 &n, const IfcVe
|
||||||
|
|
||||||
// ignore if segment is parallel to plane and far away from it on either side
|
// ignore if segment is parallel to plane and far away from it on either side
|
||||||
// Warning: if there's a few thousand of such segments which slowly accumulate beyond the epsilon, no hit would be registered
|
// Warning: if there's a few thousand of such segments which slowly accumulate beyond the epsilon, no hit would be registered
|
||||||
if (std::abs(dotOne) < ai_epsilon)
|
if (std::abs(dotOne) < ai_epsilon) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// t must be in [0..1] if the intersection point is within the given segment
|
// t must be in [0..1] if the intersection point is within the given segment
|
||||||
const IfcFloat t = dotTwo / dotOne;
|
const IfcFloat t = dotTwo / dotOne;
|
||||||
if (t > 1.0 || t < 0.0)
|
if (t > 1.0 || t < 0.0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
out = e0 + t * seg;
|
out = e0 + t * seg;
|
||||||
return true;
|
return true;
|
||||||
|
@ -110,11 +111,13 @@ void FilterPolygon(std::vector<IfcVector3> &resultpoly) {
|
||||||
FuzzyVectorCompare fz(epsilon);
|
FuzzyVectorCompare fz(epsilon);
|
||||||
std::vector<IfcVector3>::iterator e = std::unique(resultpoly.begin(), resultpoly.end(), fz);
|
std::vector<IfcVector3>::iterator e = std::unique(resultpoly.begin(), resultpoly.end(), fz);
|
||||||
|
|
||||||
if (e != resultpoly.end())
|
if (e != resultpoly.end()) {
|
||||||
resultpoly.erase(e, resultpoly.end());
|
resultpoly.erase(e, resultpoly.end());
|
||||||
|
}
|
||||||
|
|
||||||
if (!resultpoly.empty() && fz(resultpoly.front(), resultpoly.back()))
|
if (!resultpoly.empty() && fz(resultpoly.front(), resultpoly.back())) {
|
||||||
resultpoly.pop_back();
|
resultpoly.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -291,8 +294,9 @@ bool IntersectsBoundaryProfile(const IfcVector3 &e0, const IfcVector3 &e1, const
|
||||||
}
|
}
|
||||||
|
|
||||||
// Line segment ends at boundary -> ignore any hit, it will be handled by possibly following segments
|
// Line segment ends at boundary -> ignore any hit, it will be handled by possibly following segments
|
||||||
if (endsAtSegment && !halfOpen)
|
if (endsAtSegment && !halfOpen) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Line segment starts at boundary -> generate a hit only if following that line would change the INSIDE/OUTSIDE
|
// Line segment starts at boundary -> generate a hit only if following that line would change the INSIDE/OUTSIDE
|
||||||
// state. This should catch the case where a connected set of segments has a point directly on the boundary,
|
// state. This should catch the case where a connected set of segments has a point directly on the boundary,
|
||||||
|
@ -301,16 +305,18 @@ bool IntersectsBoundaryProfile(const IfcVector3 &e0, const IfcVector3 &e1, const
|
||||||
if (startsAtSegment) {
|
if (startsAtSegment) {
|
||||||
IfcVector3 inside_dir = IfcVector3(b.y, -b.x, 0.0) * windingOrder;
|
IfcVector3 inside_dir = IfcVector3(b.y, -b.x, 0.0) * windingOrder;
|
||||||
bool isGoingInside = (inside_dir * e) > 0.0;
|
bool isGoingInside = (inside_dir * e) > 0.0;
|
||||||
if (isGoingInside == isStartAssumedInside)
|
if (isGoingInside == isStartAssumedInside) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// only insert the point into the list if it is sufficiently far away from the previous intersection point.
|
// only insert the point into the list if it is sufficiently far away from the previous intersection point.
|
||||||
// This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
|
// This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
|
||||||
if (!intersect_results.empty() && intersect_results.back().first == i - 1) {
|
if (!intersect_results.empty() && intersect_results.back().first == i - 1) {
|
||||||
const IfcVector3 diff = intersect_results.back().second - e0;
|
const IfcVector3 diff = intersect_results.back().second - e0;
|
||||||
if (IfcVector2(diff.x, diff.y).SquareLength() < 1e-10)
|
if (IfcVector2(diff.x, diff.y).SquareLength() < 1e-10) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
intersect_results.emplace_back(i, e0);
|
intersect_results.emplace_back(i, e0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -322,9 +328,10 @@ bool IntersectsBoundaryProfile(const IfcVector3 &e0, const IfcVector3 &e1, const
|
||||||
// This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
|
// This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
|
||||||
if (!intersect_results.empty() && intersect_results.back().first == i - 1) {
|
if (!intersect_results.empty() && intersect_results.back().first == i - 1) {
|
||||||
const IfcVector3 diff = intersect_results.back().second - p;
|
const IfcVector3 diff = intersect_results.back().second - p;
|
||||||
if (IfcVector2(diff.x, diff.y).SquareLength() < 1e-10)
|
if (IfcVector2(diff.x, diff.y).SquareLength() < 1e-10) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
intersect_results.emplace_back(i, p);
|
intersect_results.emplace_back(i, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,7 +669,8 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPoly
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid *as, TempMesh &result,
|
void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid *as,
|
||||||
|
TempMesh &result,
|
||||||
const TempMesh &first_operand,
|
const TempMesh &first_operand,
|
||||||
ConversionData &conv) {
|
ConversionData &conv) {
|
||||||
ai_assert(as != nullptr);
|
ai_assert(as != nullptr);
|
||||||
|
@ -763,4 +771,4 @@ void ProcessBoolean(const Schema_2x3::IfcBooleanResult &boolean, TempMesh &resul
|
||||||
} // namespace IFC
|
} // namespace IFC
|
||||||
} // namespace Assimp
|
} // namespace Assimp
|
||||||
|
|
||||||
#endif
|
#endif // ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
|
@ -39,15 +39,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file IFCProfile.cpp
|
/// @file IFCProfile.cpp
|
||||||
* @brief Read profile and curves entities from IFC files
|
/// @brief Read profile and curves entities from IFC files
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
#include "IFCUtil.h"
|
#include "IFCUtil.h"
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace IFC {
|
namespace IFC {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------
|
||||||
|
@ -56,8 +56,7 @@ namespace {
|
||||||
class Conic : public Curve {
|
class Conic : public Curve {
|
||||||
public:
|
public:
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
Conic(const Schema_2x3::IfcConic& entity, ConversionData& conv)
|
Conic(const Schema_2x3::IfcConic& entity, ConversionData& conv) : Curve(entity,conv) {
|
||||||
: Curve(entity,conv) {
|
|
||||||
IfcMatrix4 trafo;
|
IfcMatrix4 trafo;
|
||||||
ConvertAxisPlacement(trafo,*entity.Position,conv);
|
ConvertAxisPlacement(trafo,*entity.Position,conv);
|
||||||
|
|
||||||
|
@ -69,12 +68,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
bool IsClosed() const {
|
bool IsClosed() const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
|
||||||
ai_assert( InRange( a ) );
|
ai_assert( InRange( a ) );
|
||||||
ai_assert( InRange( b ) );
|
ai_assert( InRange( b ) );
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
ParamRange GetParametricRange() const {
|
ParamRange GetParametricRange() const override {
|
||||||
return std::make_pair(static_cast<IfcFloat>( 0. ), static_cast<IfcFloat>( AI_MATH_TWO_PI / conv.angle_scale ));
|
return std::make_pair(static_cast<IfcFloat>( 0. ), static_cast<IfcFloat>( AI_MATH_TWO_PI / conv.angle_scale ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,14 +101,13 @@ protected:
|
||||||
class Circle : public Conic {
|
class Circle : public Conic {
|
||||||
public:
|
public:
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
Circle(const Schema_2x3::IfcCircle& entity, ConversionData& conv)
|
Circle(const Schema_2x3::IfcCircle& entity, ConversionData& conv) : Conic(entity,conv) , entity(entity) {}
|
||||||
: Conic(entity,conv)
|
|
||||||
, entity(entity)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
IfcVector3 Eval(IfcFloat u) const {
|
~Circle() override = default;
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
IfcVector3 Eval(IfcFloat u) const override {
|
||||||
u = -conv.angle_scale * u;
|
u = -conv.angle_scale * u;
|
||||||
return location + static_cast<IfcFloat>(entity.Radius)*(static_cast<IfcFloat>(std::cos(u))*p[0] +
|
return location + static_cast<IfcFloat>(entity.Radius)*(static_cast<IfcFloat>(std::cos(u))*p[0] +
|
||||||
static_cast<IfcFloat>(std::sin(u))*p[1]);
|
static_cast<IfcFloat>(std::sin(u))*p[1]);
|
||||||
|
@ -132,7 +130,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
IfcVector3 Eval(IfcFloat u) const {
|
IfcVector3 Eval(IfcFloat u) const override {
|
||||||
u = -conv.angle_scale * u;
|
u = -conv.angle_scale * u;
|
||||||
return location + static_cast<IfcFloat>(entity.SemiAxis1)*static_cast<IfcFloat>(std::cos(u))*p[0] +
|
return location + static_cast<IfcFloat>(entity.SemiAxis1)*static_cast<IfcFloat>(std::cos(u))*p[0] +
|
||||||
static_cast<IfcFloat>(entity.SemiAxis2)*static_cast<IfcFloat>(std::sin(u))*p[1];
|
static_cast<IfcFloat>(entity.SemiAxis2)*static_cast<IfcFloat>(std::sin(u))*p[1];
|
||||||
|
@ -155,17 +153,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
bool IsClosed() const {
|
bool IsClosed() const override {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
IfcVector3 Eval(IfcFloat u) const {
|
IfcVector3 Eval(IfcFloat u) const override {
|
||||||
return p + u*v;
|
return p + u*v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
|
||||||
ai_assert( InRange( a ) );
|
ai_assert( InRange( a ) );
|
||||||
ai_assert( InRange( b ) );
|
ai_assert( InRange( b ) );
|
||||||
// two points are always sufficient for a line segment
|
// two points are always sufficient for a line segment
|
||||||
|
@ -174,7 +172,7 @@ public:
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
|
void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const override {
|
||||||
ai_assert( InRange( a ) );
|
ai_assert( InRange( a ) );
|
||||||
ai_assert( InRange( b ) );
|
ai_assert( InRange( b ) );
|
||||||
|
|
||||||
|
@ -188,7 +186,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
ParamRange GetParametricRange() const {
|
ParamRange GetParametricRange() const override {
|
||||||
const IfcFloat inf = std::numeric_limits<IfcFloat>::infinity();
|
const IfcFloat inf = std::numeric_limits<IfcFloat>::infinity();
|
||||||
|
|
||||||
return std::make_pair(-inf,+inf);
|
return std::make_pair(-inf,+inf);
|
||||||
|
@ -234,7 +232,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
IfcVector3 Eval(IfcFloat u) const {
|
IfcVector3 Eval(IfcFloat u) const override {
|
||||||
if (curves.empty()) {
|
if (curves.empty()) {
|
||||||
return IfcVector3();
|
return IfcVector3();
|
||||||
}
|
}
|
||||||
|
@ -254,7 +252,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
|
||||||
ai_assert( InRange( a ) );
|
ai_assert( InRange( a ) );
|
||||||
ai_assert( InRange( b ) );
|
ai_assert( InRange( b ) );
|
||||||
size_t cnt = 0;
|
size_t cnt = 0;
|
||||||
|
@ -275,7 +273,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
|
void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const override {
|
||||||
ai_assert( InRange( a ) );
|
ai_assert( InRange( a ) );
|
||||||
ai_assert( InRange( b ) );
|
ai_assert( InRange( b ) );
|
||||||
|
|
||||||
|
@ -293,7 +291,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
ParamRange GetParametricRange() const {
|
ParamRange GetParametricRange() const override {
|
||||||
return std::make_pair(static_cast<IfcFloat>( 0. ),total);
|
return std::make_pair(static_cast<IfcFloat>( 0. ),total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,27 +371,27 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
IfcVector3 Eval(IfcFloat p) const {
|
IfcVector3 Eval(IfcFloat p) const override {
|
||||||
ai_assert(InRange(p));
|
ai_assert(InRange(p));
|
||||||
return base->Eval( TrimParam(p) );
|
return base->Eval( TrimParam(p) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
|
||||||
ai_assert( InRange( a ) );
|
ai_assert( InRange( a ) );
|
||||||
ai_assert( InRange( b ) );
|
ai_assert( InRange( b ) );
|
||||||
return base->EstimateSampleCount(TrimParam(a),TrimParam(b));
|
return base->EstimateSampleCount(TrimParam(a),TrimParam(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
void SampleDiscrete(TempMesh& out,IfcFloat a,IfcFloat b) const {
|
void SampleDiscrete(TempMesh& out,IfcFloat a,IfcFloat b) const override {
|
||||||
ai_assert(InRange(a));
|
ai_assert(InRange(a));
|
||||||
ai_assert(InRange(b));
|
ai_assert(InRange(b));
|
||||||
return base->SampleDiscrete(out,TrimParam(a),TrimParam(b));
|
return base->SampleDiscrete(out,TrimParam(a),TrimParam(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
ParamRange GetParametricRange() const {
|
ParamRange GetParametricRange() const override {
|
||||||
return std::make_pair(static_cast<IfcFloat>( 0. ),maxval);
|
return std::make_pair(static_cast<IfcFloat>( 0. ),maxval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,7 +429,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
IfcVector3 Eval(IfcFloat p) const {
|
IfcVector3 Eval(IfcFloat p) const override {
|
||||||
ai_assert(InRange(p));
|
ai_assert(InRange(p));
|
||||||
|
|
||||||
const size_t b = static_cast<size_t>(std::floor(p));
|
const size_t b = static_cast<size_t>(std::floor(p));
|
||||||
|
@ -444,14 +442,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
|
||||||
ai_assert(InRange(a));
|
ai_assert(InRange(a));
|
||||||
ai_assert(InRange(b));
|
ai_assert(InRange(b));
|
||||||
return static_cast<size_t>( std::ceil(b) - std::floor(a) );
|
return static_cast<size_t>( std::ceil(b) - std::floor(a) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
ParamRange GetParametricRange() const {
|
ParamRange GetParametricRange() const override {
|
||||||
return std::make_pair(static_cast<IfcFloat>( 0. ),static_cast<IfcFloat>(points.size()-1));
|
return std::make_pair(static_cast<IfcFloat>( 0. ),static_cast<IfcFloat>(points.size()-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,7 +514,7 @@ size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
||||||
ai_assert( InRange( a ) );
|
ai_assert( InRange( a ) );
|
||||||
ai_assert( InRange( b ) );
|
ai_assert( InRange( b ) );
|
||||||
|
|
||||||
// arbitrary default value, deriving classes should supply better suited values
|
// arbitrary default value, deriving classes should supply better-suited values
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,24 +38,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file IFCGeometry.cpp
|
/// @file IFCGeometry.cpp
|
||||||
* @brief Geometry conversion and synthesis for IFC
|
/// @brief Geometry conversion and synthesis for IFC
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
#include "IFCUtil.h"
|
#include "IFCUtil.h"
|
||||||
#include "Common/PolyTools.h"
|
#include "Common/PolyTools.h"
|
||||||
#include "PostProcessing/ProcessHelper.h"
|
#include "PostProcessing/ProcessHelper.h"
|
||||||
|
#include "contrib/poly2tri/poly2tri/poly2tri.h"
|
||||||
#ifdef ASSIMP_USE_HUNTER
|
#include "contrib/clipper/clipper.hpp"
|
||||||
# include <poly2tri/poly2tri.h>
|
|
||||||
# include <polyclipping/clipper.hpp>
|
|
||||||
#else
|
|
||||||
# include "../contrib/poly2tri/poly2tri/poly2tri.h"
|
|
||||||
# include "../contrib/clipper/clipper.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -65,8 +56,7 @@ namespace Assimp {
|
||||||
namespace IFC {
|
namespace IFC {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ProcessPolyloop(const Schema_2x3::IfcPolyLoop& loop, TempMesh& meshout, ConversionData& /*conv*/)
|
bool ProcessPolyloop(const Schema_2x3::IfcPolyLoop& loop, TempMesh& meshout, ConversionData& /*conv*/) {
|
||||||
{
|
|
||||||
size_t cnt = 0;
|
size_t cnt = 0;
|
||||||
for(const Schema_2x3::IfcCartesianPoint& c : loop.Polygon) {
|
for(const Schema_2x3::IfcCartesianPoint& c : loop.Polygon) {
|
||||||
IfcVector3 tmp;
|
IfcVector3 tmp;
|
||||||
|
@ -91,8 +81,7 @@ bool ProcessPolyloop(const Schema_2x3::IfcPolyLoop& loop, TempMesh& meshout, Con
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t master_bounds = (size_t)-1)
|
void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t master_bounds = (size_t)-1) {
|
||||||
{
|
|
||||||
// handle all trivial cases
|
// handle all trivial cases
|
||||||
if(inmesh.mVertcnt.empty()) {
|
if(inmesh.mVertcnt.empty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -127,8 +116,7 @@ void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t m
|
||||||
if (master_bounds != (size_t)-1) {
|
if (master_bounds != (size_t)-1) {
|
||||||
ai_assert(master_bounds < inmesh.mVertcnt.size());
|
ai_assert(master_bounds < inmesh.mVertcnt.size());
|
||||||
outer_polygon_it = begin + master_bounds;
|
outer_polygon_it = begin + master_bounds;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
for(iit = begin; iit != end; ++iit) {
|
for(iit = begin; iit != end; ++iit) {
|
||||||
// find the polygon with the largest area and take it as the outer bound.
|
// find the polygon with the largest area and take it as the outer bound.
|
||||||
IfcVector3& n = normals[std::distance(begin,iit)];
|
IfcVector3& n = normals[std::distance(begin,iit)];
|
||||||
|
@ -139,6 +127,7 @@ void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outer_polygon_it == end) {
|
if (outer_polygon_it == end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -205,40 +194,20 @@ void ProcessConnectedFaceSet(const Schema_2x3::IfcConnectedFaceSet& fset, TempMe
|
||||||
|
|
||||||
if(const Schema_2x3::IfcPolyLoop* const polyloop = bound.Bound->ToPtr<Schema_2x3::IfcPolyLoop>()) {
|
if(const Schema_2x3::IfcPolyLoop* const polyloop = bound.Bound->ToPtr<Schema_2x3::IfcPolyLoop>()) {
|
||||||
if(ProcessPolyloop(*polyloop, meshout,conv)) {
|
if(ProcessPolyloop(*polyloop, meshout,conv)) {
|
||||||
|
|
||||||
// The outer boundary is better determined by checking which
|
// The outer boundary is better determined by checking which
|
||||||
// polygon covers the largest area.
|
// polygon covers the largest area.
|
||||||
|
|
||||||
//if(bound.ToPtr<IfcFaceOuterBound>()) {
|
|
||||||
// ob = cnt;
|
|
||||||
//}
|
|
||||||
//++cnt;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is ", bound.Bound->GetClassName());
|
IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is ", bound.Bound->GetClassName());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// And this, even though it is sometimes TRUE and sometimes FALSE,
|
|
||||||
// does not really improve results.
|
|
||||||
|
|
||||||
/*if(!IsTrue(bound.Orientation)) {
|
|
||||||
size_t c = 0;
|
|
||||||
for(unsigned int& c : meshout.vertcnt) {
|
|
||||||
std::reverse(result.verts.begin() + cnt,result.verts.begin() + cnt + c);
|
|
||||||
cnt += c;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
ProcessPolygonBoundaries(result, meshout);
|
ProcessPolygonBoundaries(result, meshout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessRevolvedAreaSolid(const Schema_2x3::IfcRevolvedAreaSolid& solid, TempMesh& result, ConversionData& conv)
|
void ProcessRevolvedAreaSolid(const Schema_2x3::IfcRevolvedAreaSolid& solid, TempMesh& result, ConversionData& conv) {
|
||||||
{
|
|
||||||
TempMesh meshout;
|
TempMesh meshout;
|
||||||
|
|
||||||
// first read the profile description
|
// first read the profile description
|
||||||
|
@ -265,7 +234,8 @@ void ProcessRevolvedAreaSolid(const Schema_2x3::IfcRevolvedAreaSolid& solid, Tem
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned int cnt_segments = std::max(2u,static_cast<unsigned int>(conv.settings.cylindricalTessellation * std::fabs(max_angle)/AI_MATH_HALF_PI_F));
|
const unsigned int cnt_segments =
|
||||||
|
std::max(2u,static_cast<unsigned int>(conv.settings.cylindricalTessellation * std::fabs(max_angle)/AI_MATH_HALF_PI_F));
|
||||||
const IfcFloat delta = max_angle/cnt_segments;
|
const IfcFloat delta = max_angle/cnt_segments;
|
||||||
|
|
||||||
has_area = has_area && std::fabs(max_angle) < AI_MATH_TWO_PI_F*0.99;
|
has_area = has_area && std::fabs(max_angle) < AI_MATH_TWO_PI_F*0.99;
|
||||||
|
@ -324,8 +294,9 @@ void ProcessRevolvedAreaSolid(const Schema_2x3::IfcRevolvedAreaSolid& solid, Tem
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessSweptDiskSolid(const Schema_2x3::IfcSweptDiskSolid &solid, TempMesh& result, ConversionData& conv)
|
void ProcessSweptDiskSolid(const Schema_2x3::IfcSweptDiskSolid &solid,
|
||||||
{
|
TempMesh& result,
|
||||||
|
ConversionData& conv) {
|
||||||
const Curve* const curve = Curve::Convert(*solid.Directrix, conv);
|
const Curve* const curve = Curve::Convert(*solid.Directrix, conv);
|
||||||
if(!curve) {
|
if(!curve) {
|
||||||
IFCImporter::LogError("failed to convert Directrix curve (IfcSweptDiskSolid)");
|
IFCImporter::LogError("failed to convert Directrix curve (IfcSweptDiskSolid)");
|
||||||
|
@ -460,8 +431,7 @@ void ProcessSweptDiskSolid(const Schema_2x3::IfcSweptDiskSolid &solid, TempMesh&
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut)
|
IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut) {
|
||||||
{
|
|
||||||
const std::vector<IfcVector3>& out = curmesh.mVerts;
|
const std::vector<IfcVector3>& out = curmesh.mVerts;
|
||||||
IfcMatrix3 m;
|
IfcMatrix3 m;
|
||||||
|
|
||||||
|
@ -504,10 +474,6 @@ IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVect
|
||||||
IfcVector3 r = (out[idx]-any_point);
|
IfcVector3 r = (out[idx]-any_point);
|
||||||
r.Normalize();
|
r.Normalize();
|
||||||
|
|
||||||
//if(d) {
|
|
||||||
// *d = -any_point * nor;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Reconstruct orthonormal basis
|
// Reconstruct orthonormal basis
|
||||||
// XXX use Gram Schmidt for increased robustness
|
// XXX use Gram Schmidt for increased robustness
|
||||||
IfcVector3 u = r ^ nor;
|
IfcVector3 u = r ^ nor;
|
||||||
|
@ -531,8 +497,7 @@ IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVect
|
||||||
const auto closeDistance = ai_epsilon;
|
const auto closeDistance = ai_epsilon;
|
||||||
|
|
||||||
bool areClose(Schema_2x3::IfcCartesianPoint pt1,Schema_2x3::IfcCartesianPoint pt2) {
|
bool areClose(Schema_2x3::IfcCartesianPoint pt1,Schema_2x3::IfcCartesianPoint pt2) {
|
||||||
if(pt1.Coordinates.size() != pt2.Coordinates.size())
|
if(pt1.Coordinates.size() != pt2.Coordinates.size()) {
|
||||||
{
|
|
||||||
IFCImporter::LogWarn("unable to compare differently-dimensioned points");
|
IFCImporter::LogWarn("unable to compare differently-dimensioned points");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -540,11 +505,11 @@ bool areClose(Schema_2x3::IfcCartesianPoint pt1,Schema_2x3::IfcCartesianPoint pt
|
||||||
auto coord2 = pt2.Coordinates.begin();
|
auto coord2 = pt2.Coordinates.begin();
|
||||||
// we're just testing each dimension separately rather than doing euclidean distance, as we're
|
// we're just testing each dimension separately rather than doing euclidean distance, as we're
|
||||||
// looking for very close coordinates
|
// looking for very close coordinates
|
||||||
for(; coord1 != pt1.Coordinates.end(); coord1++,coord2++)
|
for(; coord1 != pt1.Coordinates.end(); coord1++,coord2++) {
|
||||||
{
|
if(std::fabs(*coord1 - *coord2) > closeDistance) {
|
||||||
if(std::fabs(*coord1 - *coord2) > closeDistance)
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,6 +518,7 @@ bool areClose(IfcVector3 pt1,IfcVector3 pt2) {
|
||||||
std::fabs(pt1.y - pt2.y) < closeDistance &&
|
std::fabs(pt1.y - pt2.y) < closeDistance &&
|
||||||
std::fabs(pt1.z - pt2.z) < closeDistance);
|
std::fabs(pt1.z - pt2.z) < closeDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extrudes the given polygon along the direction, converts it into an opening or applies all openings as necessary.
|
// Extrudes the given polygon along the direction, converts it into an opening or applies all openings as necessary.
|
||||||
void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const TempMesh& curve,
|
void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const TempMesh& curve,
|
||||||
const IfcVector3& extrusionDir, TempMesh& result, ConversionData &conv, bool collect_openings)
|
const IfcVector3& extrusionDir, TempMesh& result, ConversionData &conv, bool collect_openings)
|
||||||
|
@ -590,8 +556,9 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
|
||||||
|
|
||||||
// reverse profile polygon if it's winded in the wrong direction in relation to the extrusion direction
|
// reverse profile polygon if it's winded in the wrong direction in relation to the extrusion direction
|
||||||
IfcVector3 profileNormal = TempMesh::ComputePolygonNormal(in.data(), in.size());
|
IfcVector3 profileNormal = TempMesh::ComputePolygonNormal(in.data(), in.size());
|
||||||
if( profileNormal * dir < 0.0 )
|
if( profileNormal * dir < 0.0 ) {
|
||||||
std::reverse(in.begin(), in.end());
|
std::reverse(in.begin(), in.end());
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<IfcVector3> nors;
|
std::vector<IfcVector3> nors;
|
||||||
const bool openings = !!conv.apply_openings && conv.apply_openings->size();
|
const bool openings = !!conv.apply_openings && conv.apply_openings->size();
|
||||||
|
@ -678,8 +645,7 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
|
||||||
if(n > 0) {
|
if(n > 0) {
|
||||||
for(size_t i = 0; i < in.size(); ++i)
|
for(size_t i = 0; i < in.size(); ++i)
|
||||||
out.push_back(in[i] + dir);
|
out.push_back(in[i] + dir);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
for(size_t i = in.size(); i--; )
|
for(size_t i = in.size(); i--; )
|
||||||
out.push_back(in[i]);
|
out.push_back(in[i]);
|
||||||
}
|
}
|
||||||
|
@ -721,9 +687,10 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid, TempMesh& result,
|
void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid,
|
||||||
ConversionData& conv, bool collect_openings)
|
TempMesh& result,
|
||||||
{
|
ConversionData& conv,
|
||||||
|
bool collect_openings) {
|
||||||
TempMesh meshout;
|
TempMesh meshout;
|
||||||
|
|
||||||
// First read the profile description.
|
// First read the profile description.
|
||||||
|
@ -761,24 +728,23 @@ void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid, Tem
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept, TempMesh& meshout,
|
void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept,
|
||||||
ConversionData& conv)
|
TempMesh& meshout,
|
||||||
{
|
ConversionData& conv) {
|
||||||
if(const Schema_2x3::IfcExtrudedAreaSolid* const solid = swept.ToPtr<Schema_2x3::IfcExtrudedAreaSolid>()) {
|
if(const Schema_2x3::IfcExtrudedAreaSolid* const solid = swept.ToPtr<Schema_2x3::IfcExtrudedAreaSolid>()) {
|
||||||
ProcessExtrudedAreaSolid(*solid,meshout,conv, !!conv.collect_openings);
|
ProcessExtrudedAreaSolid(*solid,meshout,conv, !!conv.collect_openings);
|
||||||
}
|
} else if(const Schema_2x3::IfcRevolvedAreaSolid* const rev = swept.ToPtr<Schema_2x3::IfcRevolvedAreaSolid>()) {
|
||||||
else if(const Schema_2x3::IfcRevolvedAreaSolid* const rev = swept.ToPtr<Schema_2x3::IfcRevolvedAreaSolid>()) {
|
|
||||||
ProcessRevolvedAreaSolid(*rev,meshout,conv);
|
ProcessRevolvedAreaSolid(*rev,meshout,conv);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
IFCImporter::LogWarn("skipping unknown IfcSweptAreaSolid entity, type is ", swept.GetClassName());
|
IFCImporter::LogWarn("skipping unknown IfcSweptAreaSolid entity, type is ", swept.GetClassName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned int matid, std::set<unsigned int>& mesh_indices,
|
bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo,
|
||||||
ConversionData& conv)
|
unsigned int matid,
|
||||||
{
|
std::set<unsigned int>& mesh_indices,
|
||||||
|
ConversionData& conv) {
|
||||||
bool fix_orientation = false;
|
bool fix_orientation = false;
|
||||||
std::shared_ptr< TempMesh > meshtmp = std::make_shared<TempMesh>();
|
std::shared_ptr< TempMesh > meshtmp = std::make_shared<TempMesh>();
|
||||||
if(const Schema_2x3::IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<Schema_2x3::IfcShellBasedSurfaceModel>()) {
|
if(const Schema_2x3::IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<Schema_2x3::IfcShellBasedSurfaceModel>()) {
|
||||||
|
@ -788,41 +754,32 @@ bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned
|
||||||
const Schema_2x3::IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To<Schema_2x3::IfcConnectedFaceSet>();
|
const Schema_2x3::IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To<Schema_2x3::IfcConnectedFaceSet>();
|
||||||
|
|
||||||
ProcessConnectedFaceSet(fs, *meshtmp, conv);
|
ProcessConnectedFaceSet(fs, *meshtmp, conv);
|
||||||
}
|
} catch(std::bad_cast&) {
|
||||||
catch(std::bad_cast&) {
|
|
||||||
IFCImporter::LogWarn("unexpected type error, IfcShell ought to inherit from IfcConnectedFaceSet");
|
IFCImporter::LogWarn("unexpected type error, IfcShell ought to inherit from IfcConnectedFaceSet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fix_orientation = true;
|
fix_orientation = true;
|
||||||
}
|
} else if(const Schema_2x3::IfcConnectedFaceSet* fset = geo.ToPtr<Schema_2x3::IfcConnectedFaceSet>()) {
|
||||||
else if(const Schema_2x3::IfcConnectedFaceSet* fset = geo.ToPtr<Schema_2x3::IfcConnectedFaceSet>()) {
|
|
||||||
ProcessConnectedFaceSet(*fset, *meshtmp, conv);
|
ProcessConnectedFaceSet(*fset, *meshtmp, conv);
|
||||||
fix_orientation = true;
|
fix_orientation = true;
|
||||||
}
|
} else if(const Schema_2x3::IfcSweptAreaSolid* swept = geo.ToPtr<Schema_2x3::IfcSweptAreaSolid>()) {
|
||||||
else if(const Schema_2x3::IfcSweptAreaSolid* swept = geo.ToPtr<Schema_2x3::IfcSweptAreaSolid>()) {
|
|
||||||
ProcessSweptAreaSolid(*swept, *meshtmp, conv);
|
ProcessSweptAreaSolid(*swept, *meshtmp, conv);
|
||||||
}
|
} else if(const Schema_2x3::IfcSweptDiskSolid* disk = geo.ToPtr<Schema_2x3::IfcSweptDiskSolid>()) {
|
||||||
else if(const Schema_2x3::IfcSweptDiskSolid* disk = geo.ToPtr<Schema_2x3::IfcSweptDiskSolid>()) {
|
|
||||||
ProcessSweptDiskSolid(*disk, *meshtmp, conv);
|
ProcessSweptDiskSolid(*disk, *meshtmp, conv);
|
||||||
}
|
} else if(const Schema_2x3::IfcManifoldSolidBrep* brep = geo.ToPtr<Schema_2x3::IfcManifoldSolidBrep>()) {
|
||||||
else if(const Schema_2x3::IfcManifoldSolidBrep* brep = geo.ToPtr<Schema_2x3::IfcManifoldSolidBrep>()) {
|
|
||||||
ProcessConnectedFaceSet(brep->Outer, *meshtmp, conv);
|
ProcessConnectedFaceSet(brep->Outer, *meshtmp, conv);
|
||||||
fix_orientation = true;
|
fix_orientation = true;
|
||||||
}
|
} else if(const Schema_2x3::IfcFaceBasedSurfaceModel* surf = geo.ToPtr<Schema_2x3::IfcFaceBasedSurfaceModel>()) {
|
||||||
else if(const Schema_2x3::IfcFaceBasedSurfaceModel* surf = geo.ToPtr<Schema_2x3::IfcFaceBasedSurfaceModel>()) {
|
|
||||||
for(const Schema_2x3::IfcConnectedFaceSet& fc : surf->FbsmFaces) {
|
for(const Schema_2x3::IfcConnectedFaceSet& fc : surf->FbsmFaces) {
|
||||||
ProcessConnectedFaceSet(fc, *meshtmp, conv);
|
ProcessConnectedFaceSet(fc, *meshtmp, conv);
|
||||||
}
|
}
|
||||||
fix_orientation = true;
|
fix_orientation = true;
|
||||||
}
|
} else if(const Schema_2x3::IfcBooleanResult* boolean = geo.ToPtr<Schema_2x3::IfcBooleanResult>()) {
|
||||||
else if(const Schema_2x3::IfcBooleanResult* boolean = geo.ToPtr<Schema_2x3::IfcBooleanResult>()) {
|
|
||||||
ProcessBoolean(*boolean, *meshtmp, conv);
|
ProcessBoolean(*boolean, *meshtmp, conv);
|
||||||
}
|
} else if(geo.ToPtr<Schema_2x3::IfcBoundingBox>()) {
|
||||||
else if(geo.ToPtr<Schema_2x3::IfcBoundingBox>()) {
|
|
||||||
// silently skip over bounding boxes
|
// silently skip over bounding boxes
|
||||||
return false;
|
return false;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
std::stringstream toLog;
|
std::stringstream toLog;
|
||||||
toLog << "skipping unknown IfcGeometricRepresentationItem entity, type is " << geo.GetClassName() << " id is " << geo.GetID();
|
toLog << "skipping unknown IfcGeometricRepresentationItem entity, type is " << geo.GetClassName() << " id is " << geo.GetID();
|
||||||
IFCImporter::LogWarn(toLog.str().c_str());
|
IFCImporter::LogWarn(toLog.str().c_str());
|
||||||
|
@ -868,9 +825,7 @@ bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void AssignAddedMeshes(std::set<unsigned int>& mesh_indices,aiNode* nd,
|
void AssignAddedMeshes(std::set<unsigned int>& mesh_indices,aiNode* nd, ConversionData& /*conv*/) {
|
||||||
ConversionData& /*conv*/)
|
|
||||||
{
|
|
||||||
if (!mesh_indices.empty()) {
|
if (!mesh_indices.empty()) {
|
||||||
std::set<unsigned int>::const_iterator it = mesh_indices.cbegin();
|
std::set<unsigned int>::const_iterator it = mesh_indices.cbegin();
|
||||||
std::set<unsigned int>::const_iterator end = mesh_indices.cend();
|
std::set<unsigned int>::const_iterator end = mesh_indices.cend();
|
||||||
|
@ -886,9 +841,9 @@ void AssignAddedMeshes(std::set<unsigned int>& mesh_indices,aiNode* nd,
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool TryQueryMeshCache(const Schema_2x3::IfcRepresentationItem& item,
|
bool TryQueryMeshCache(const Schema_2x3::IfcRepresentationItem& item,
|
||||||
std::set<unsigned int>& mesh_indices, unsigned int mat_index,
|
std::set<unsigned int>& mesh_indices,
|
||||||
ConversionData& conv)
|
unsigned int mat_index,
|
||||||
{
|
ConversionData& conv) {
|
||||||
ConversionData::MeshCacheIndex idx(&item, mat_index);
|
ConversionData::MeshCacheIndex idx(&item, mat_index);
|
||||||
ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(idx);
|
ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(idx);
|
||||||
if (it != conv.cached_meshes.end()) {
|
if (it != conv.cached_meshes.end()) {
|
||||||
|
@ -900,18 +855,18 @@ bool TryQueryMeshCache(const Schema_2x3::IfcRepresentationItem& item,
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void PopulateMeshCache(const Schema_2x3::IfcRepresentationItem& item,
|
void PopulateMeshCache(const Schema_2x3::IfcRepresentationItem& item,
|
||||||
const std::set<unsigned int>& mesh_indices, unsigned int mat_index,
|
const std::set<unsigned int>& mesh_indices,
|
||||||
ConversionData& conv)
|
unsigned int mat_index,
|
||||||
{
|
ConversionData& conv) {
|
||||||
ConversionData::MeshCacheIndex idx(&item, mat_index);
|
ConversionData::MeshCacheIndex idx(&item, mat_index);
|
||||||
conv.cached_meshes[idx] = mesh_indices;
|
conv.cached_meshes[idx] = mesh_indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, unsigned int matid,
|
bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item,
|
||||||
|
unsigned int matid,
|
||||||
std::set<unsigned int>& mesh_indices,
|
std::set<unsigned int>& mesh_indices,
|
||||||
ConversionData& conv)
|
ConversionData& conv) {
|
||||||
{
|
|
||||||
// determine material
|
// determine material
|
||||||
unsigned int localmatid = ProcessMaterials(item.GetID(), matid, conv, true);
|
unsigned int localmatid = ProcessMaterials(item.GetID(), matid, conv, true);
|
||||||
|
|
||||||
|
@ -920,8 +875,9 @@ bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, un
|
||||||
if(mesh_indices.size()) {
|
if(mesh_indices.size()) {
|
||||||
PopulateMeshCache(item,mesh_indices,localmatid,conv);
|
PopulateMeshCache(item,mesh_indices,localmatid,conv);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -930,4 +886,4 @@ bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, un
|
||||||
} // ! IFC
|
} // ! IFC
|
||||||
} // ! Assimp
|
} // ! Assimp
|
||||||
|
|
||||||
#endif
|
#endif // ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -40,9 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file IFCLoad.cpp
|
/// @file IFCLoad.cpp
|
||||||
* @brief Implementation of the Industry Foundation Classes loader.
|
/// @brief Implementation of the Industry Foundation Classes loader.
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
||||||
|
@ -92,7 +90,6 @@ using namespace Assimp::IFC;
|
||||||
IfcUnitAssignment
|
IfcUnitAssignment
|
||||||
IfcClosedShell
|
IfcClosedShell
|
||||||
IfcDoor
|
IfcDoor
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -119,14 +116,6 @@ static const aiImporterDesc desc = {
|
||||||
"ifc ifczip step stp"
|
"ifc ifczip step stp"
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
// Constructor to be privately used by Importer
|
|
||||||
IFCImporter::IFCImporter() = default;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
// Destructor, private as well
|
|
||||||
IFCImporter::~IFCImporter() = default;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// Returns whether the class can handle the format of the given file.
|
||||||
bool IFCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
|
bool IFCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
|
||||||
|
@ -256,7 +245,12 @@ void IFCImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
|
|
||||||
// tell the reader for which types we need to simulate STEPs reverse indices
|
// tell the reader for which types we need to simulate STEPs reverse indices
|
||||||
static const char *const inverse_indices_to_track[] = {
|
static const char *const inverse_indices_to_track[] = {
|
||||||
"ifcrelcontainedinspatialstructure", "ifcrelaggregates", "ifcrelvoidselement", "ifcreldefinesbyproperties", "ifcpropertyset", "ifcstyleditem"
|
"ifcrelcontainedinspatialstructure",
|
||||||
|
"ifcrelaggregates",
|
||||||
|
"ifcrelvoidselement",
|
||||||
|
"ifcreldefinesbyproperties",
|
||||||
|
"ifcpropertyset",
|
||||||
|
"ifcstyleditem"
|
||||||
};
|
};
|
||||||
|
|
||||||
// feed the IFC schema into the reader and pre-parse all lines
|
// feed the IFC schema into the reader and pre-parse all lines
|
||||||
|
@ -928,4 +922,4 @@ void MakeTreeRelative(ConversionData &conv) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#endif
|
#endif // ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
|
@ -87,8 +87,8 @@ public:
|
||||||
int cylindricalTessellation;
|
int cylindricalTessellation;
|
||||||
};
|
};
|
||||||
|
|
||||||
IFCImporter();
|
IFCImporter() = default;
|
||||||
~IFCImporter() override;
|
~IFCImporter() override = default;
|
||||||
|
|
||||||
// --------------------
|
// --------------------
|
||||||
bool CanRead(const std::string &pFile,
|
bool CanRead(const std::string &pFile,
|
||||||
|
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -40,9 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file IFCMaterial.cpp
|
/// @file IFCMaterial.cpp
|
||||||
* @brief Implementation of conversion routines to convert IFC materials to aiMaterial
|
/// @brief Implementation of conversion routines to convert IFC materials to aiMaterial
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
||||||
|
@ -174,7 +172,6 @@ unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionDat
|
||||||
|
|
||||||
aiString name;
|
aiString name;
|
||||||
name.Set("<IFCDefault>");
|
name.Set("<IFCDefault>");
|
||||||
// ConvertColorToString( color, name);
|
|
||||||
|
|
||||||
// look if there's already a default material with this base color
|
// look if there's already a default material with this base color
|
||||||
for( size_t a = 0; a < conv.materials.size(); ++a ) {
|
for( size_t a = 0; a < conv.materials.size(); ++a ) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -40,9 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file IFCProfile.cpp
|
/// @file IFCProfile.cpp
|
||||||
* @brief Read profile and curves entities from IFC files
|
/// @brief Read profile and curves entities from IFC files
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
||||||
|
@ -52,8 +50,9 @@ namespace Assimp {
|
||||||
namespace IFC {
|
namespace IFC {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessPolyLine(const Schema_2x3::IfcPolyline& def, TempMesh& meshout, ConversionData& /*conv*/)
|
void ProcessPolyLine(const Schema_2x3::IfcPolyline& def,
|
||||||
{
|
TempMesh& meshout,
|
||||||
|
ConversionData& /*conv*/) {
|
||||||
// this won't produce a valid mesh, it just spits out a list of vertices
|
// this won't produce a valid mesh, it just spits out a list of vertices
|
||||||
IfcVector3 t;
|
IfcVector3 t;
|
||||||
for(const Schema_2x3::IfcCartesianPoint& cp : def.Points) {
|
for(const Schema_2x3::IfcCartesianPoint& cp : def.Points) {
|
||||||
|
@ -64,8 +63,9 @@ void ProcessPolyLine(const Schema_2x3::IfcPolyline& def, TempMesh& meshout, Conv
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ProcessCurve(const Schema_2x3::IfcCurve& curve, TempMesh& meshout, ConversionData& conv)
|
bool ProcessCurve(const Schema_2x3::IfcCurve& curve,
|
||||||
{
|
TempMesh& meshout,
|
||||||
|
ConversionData& conv) {
|
||||||
std::unique_ptr<const Curve> cv(Curve::Convert(curve,conv));
|
std::unique_ptr<const Curve> cv(Curve::Convert(curve,conv));
|
||||||
if (!cv) {
|
if (!cv) {
|
||||||
IFCImporter::LogWarn("skipping unknown IfcCurve entity, type is ", curve.GetClassName());
|
IFCImporter::LogWarn("skipping unknown IfcCurve entity, type is ", curve.GetClassName());
|
||||||
|
@ -90,20 +90,23 @@ bool ProcessCurve(const Schema_2x3::IfcCurve& curve, TempMesh& meshout, Convers
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessClosedProfile(const Schema_2x3::IfcArbitraryClosedProfileDef& def, TempMesh& meshout, ConversionData& conv)
|
void ProcessClosedProfile(const Schema_2x3::IfcArbitraryClosedProfileDef& def,
|
||||||
{
|
TempMesh& meshout,
|
||||||
|
ConversionData& conv) {
|
||||||
ProcessCurve(def.OuterCurve,meshout,conv);
|
ProcessCurve(def.OuterCurve,meshout,conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessOpenProfile(const Schema_2x3::IfcArbitraryOpenProfileDef& def, TempMesh& meshout, ConversionData& conv)
|
void ProcessOpenProfile(const Schema_2x3::IfcArbitraryOpenProfileDef& def,
|
||||||
{
|
TempMesh& meshout,
|
||||||
|
ConversionData& conv) {
|
||||||
ProcessCurve(def.Curve,meshout,conv);
|
ProcessCurve(def.Curve,meshout,conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessParametrizedProfile(const Schema_2x3::IfcParameterizedProfileDef& def, TempMesh& meshout, ConversionData& conv)
|
void ProcessParametrizedProfile(const Schema_2x3::IfcParameterizedProfileDef& def,
|
||||||
{
|
TempMesh& meshout,
|
||||||
|
ConversionData& conv) {
|
||||||
if(const Schema_2x3::IfcRectangleProfileDef* const cprofile = def.ToPtr<Schema_2x3::IfcRectangleProfileDef>()) {
|
if(const Schema_2x3::IfcRectangleProfileDef* const cprofile = def.ToPtr<Schema_2x3::IfcRectangleProfileDef>()) {
|
||||||
const IfcFloat x = cprofile->XDim*0.5f, y = cprofile->YDim*0.5f;
|
const IfcFloat x = cprofile->XDim*0.5f, y = cprofile->YDim*0.5f;
|
||||||
|
|
||||||
|
@ -113,8 +116,7 @@ void ProcessParametrizedProfile(const Schema_2x3::IfcParameterizedProfileDef& de
|
||||||
meshout.mVerts.emplace_back(-x,-y, 0.f );
|
meshout.mVerts.emplace_back(-x,-y, 0.f );
|
||||||
meshout.mVerts.emplace_back( x,-y, 0.f );
|
meshout.mVerts.emplace_back( x,-y, 0.f );
|
||||||
meshout.mVertcnt.push_back(4);
|
meshout.mVertcnt.push_back(4);
|
||||||
}
|
} else if( const Schema_2x3::IfcCircleProfileDef* const circle = def.ToPtr<Schema_2x3::IfcCircleProfileDef>()) {
|
||||||
else if( const Schema_2x3::IfcCircleProfileDef* const circle = def.ToPtr<Schema_2x3::IfcCircleProfileDef>()) {
|
|
||||||
if(def.ToPtr<Schema_2x3::IfcCircleHollowProfileDef>()) {
|
if(def.ToPtr<Schema_2x3::IfcCircleHollowProfileDef>()) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
@ -129,8 +131,7 @@ void ProcessParametrizedProfile(const Schema_2x3::IfcParameterizedProfileDef& de
|
||||||
}
|
}
|
||||||
|
|
||||||
meshout.mVertcnt.push_back(static_cast<unsigned int>(segments));
|
meshout.mVertcnt.push_back(static_cast<unsigned int>(segments));
|
||||||
}
|
} else if( const Schema_2x3::IfcIShapeProfileDef* const ishape = def.ToPtr<Schema_2x3::IfcIShapeProfileDef>()) {
|
||||||
else if( const Schema_2x3::IfcIShapeProfileDef* const ishape = def.ToPtr<Schema_2x3::IfcIShapeProfileDef>()) {
|
|
||||||
// construct simplified IBeam shape
|
// construct simplified IBeam shape
|
||||||
const IfcFloat offset = (ishape->OverallWidth - ishape->WebThickness) / 2;
|
const IfcFloat offset = (ishape->OverallWidth - ishape->WebThickness) / 2;
|
||||||
const IfcFloat inner_height = ishape->OverallDepth - ishape->FlangeThickness * 2;
|
const IfcFloat inner_height = ishape->OverallDepth - ishape->FlangeThickness * 2;
|
||||||
|
@ -150,8 +151,7 @@ void ProcessParametrizedProfile(const Schema_2x3::IfcParameterizedProfileDef& de
|
||||||
meshout.mVerts.emplace_back(ishape->OverallWidth,0,0);
|
meshout.mVerts.emplace_back(ishape->OverallWidth,0,0);
|
||||||
|
|
||||||
meshout.mVertcnt.push_back(12);
|
meshout.mVertcnt.push_back(12);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
IFCImporter::LogWarn("skipping unknown IfcParameterizedProfileDef entity, type is ", def.GetClassName());
|
IFCImporter::LogWarn("skipping unknown IfcParameterizedProfileDef entity, type is ", def.GetClassName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -162,18 +162,14 @@ void ProcessParametrizedProfile(const Schema_2x3::IfcParameterizedProfileDef& de
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv)
|
bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv) {
|
||||||
{
|
|
||||||
if(const Schema_2x3::IfcArbitraryClosedProfileDef* const cprofile = prof.ToPtr<Schema_2x3::IfcArbitraryClosedProfileDef>()) {
|
if(const Schema_2x3::IfcArbitraryClosedProfileDef* const cprofile = prof.ToPtr<Schema_2x3::IfcArbitraryClosedProfileDef>()) {
|
||||||
ProcessClosedProfile(*cprofile,meshout,conv);
|
ProcessClosedProfile(*cprofile,meshout,conv);
|
||||||
}
|
} else if(const Schema_2x3::IfcArbitraryOpenProfileDef* const copen = prof.ToPtr<Schema_2x3::IfcArbitraryOpenProfileDef>()) {
|
||||||
else if(const Schema_2x3::IfcArbitraryOpenProfileDef* const copen = prof.ToPtr<Schema_2x3::IfcArbitraryOpenProfileDef>()) {
|
|
||||||
ProcessOpenProfile(*copen,meshout,conv);
|
ProcessOpenProfile(*copen,meshout,conv);
|
||||||
}
|
} else if(const Schema_2x3::IfcParameterizedProfileDef* const cparam = prof.ToPtr<Schema_2x3::IfcParameterizedProfileDef>()) {
|
||||||
else if(const Schema_2x3::IfcParameterizedProfileDef* const cparam = prof.ToPtr<Schema_2x3::IfcParameterizedProfileDef>()) {
|
|
||||||
ProcessParametrizedProfile(*cparam,meshout,conv);
|
ProcessParametrizedProfile(*cparam,meshout,conv);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
IFCImporter::LogWarn("skipping unknown IfcProfileDef entity, type is ", prof.GetClassName());
|
IFCImporter::LogWarn("skipping unknown IfcProfileDef entity, type is ", prof.GetClassName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -40,14 +39,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file IFCUtil.cpp
|
/// @file IFCUtil.cpp
|
||||||
* @brief Implementation of conversion routines for some common Ifc helper entities.
|
/// @brief Implementation of conversion routines for some common Ifc helper entities.
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
||||||
#include "AssetLib/IFC/IFCUtil.h"
|
#include "AssetLib/IFC/IFCUtil.h"
|
||||||
#include "Common/PolyTools.h"
|
#include "Common/PolyTools.h"
|
||||||
|
#include "Geometry/GeometryUtils.h"
|
||||||
#include "PostProcessing/ProcessHelper.h"
|
#include "PostProcessing/ProcessHelper.h"
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
@ -65,8 +64,7 @@ void TempOpening::Transform(const IfcMatrix4& mat) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
aiMesh* TempMesh::ToMesh()
|
aiMesh* TempMesh::ToMesh() {
|
||||||
{
|
|
||||||
ai_assert(mVerts.size() == std::accumulate(mVertcnt.begin(),mVertcnt.end(),size_t(0)));
|
ai_assert(mVerts.size() == std::accumulate(mVertcnt.begin(),mVertcnt.end(),size_t(0)));
|
||||||
|
|
||||||
if (mVerts.empty()) {
|
if (mVerts.empty()) {
|
||||||
|
@ -104,36 +102,31 @@ aiMesh* TempMesh::ToMesh()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void TempMesh::Clear()
|
void TempMesh::Clear() {
|
||||||
{
|
|
||||||
mVerts.clear();
|
mVerts.clear();
|
||||||
mVertcnt.clear();
|
mVertcnt.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void TempMesh::Transform(const IfcMatrix4& mat)
|
void TempMesh::Transform(const IfcMatrix4& mat) {
|
||||||
{
|
|
||||||
for(IfcVector3& v : mVerts) {
|
for(IfcVector3& v : mVerts) {
|
||||||
v *= mat;
|
v *= mat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
IfcVector3 TempMesh::Center() const
|
IfcVector3 TempMesh::Center() const {
|
||||||
{
|
return mVerts.empty() ? IfcVector3(0.0f, 0.0f, 0.0f) : (std::accumulate(mVerts.begin(),mVerts.end(),IfcVector3()) / static_cast<IfcFloat>(mVerts.size()));
|
||||||
return (mVerts.size() == 0) ? IfcVector3(0.0f, 0.0f, 0.0f) : (std::accumulate(mVerts.begin(),mVerts.end(),IfcVector3()) / static_cast<IfcFloat>(mVerts.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void TempMesh::Append(const TempMesh& other)
|
void TempMesh::Append(const TempMesh& other) {
|
||||||
{
|
|
||||||
mVerts.insert(mVerts.end(),other.mVerts.begin(),other.mVerts.end());
|
mVerts.insert(mVerts.end(),other.mVerts.begin(),other.mVerts.end());
|
||||||
mVertcnt.insert(mVertcnt.end(),other.mVertcnt.begin(),other.mVertcnt.end());
|
mVertcnt.insert(mVertcnt.end(),other.mVertcnt.begin(),other.mVertcnt.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void TempMesh::RemoveDegenerates()
|
void TempMesh::RemoveDegenerates() {
|
||||||
{
|
|
||||||
// The strategy is simple: walk the mesh and compute normals using
|
// The strategy is simple: walk the mesh and compute normals using
|
||||||
// Newell's algorithm. The length of the normals gives the area
|
// Newell's algorithm. The length of the normals gives the area
|
||||||
// of the polygons, which is close to zero for lines.
|
// of the polygons, which is close to zero for lines.
|
||||||
|
@ -166,11 +159,9 @@ void TempMesh::RemoveDegenerates()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
IfcVector3 TempMesh::ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize)
|
IfcVector3 TempMesh::ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize) {
|
||||||
{
|
|
||||||
std::vector<IfcFloat> temp((cnt+2)*3);
|
std::vector<IfcFloat> temp((cnt+2)*3);
|
||||||
for( size_t vofs = 0, i = 0; vofs < cnt; ++vofs )
|
for( size_t vofs = 0, i = 0; vofs < cnt; ++vofs ) {
|
||||||
{
|
|
||||||
const IfcVector3& v = vtcs[vofs];
|
const IfcVector3& v = vtcs[vofs];
|
||||||
temp[i++] = v.x;
|
temp[i++] = v.x;
|
||||||
temp[i++] = v.y;
|
temp[i++] = v.y;
|
||||||
|
@ -185,8 +176,7 @@ IfcVector3 TempMesh::ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bo
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
|
void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
|
||||||
bool normalize,
|
bool normalize,
|
||||||
size_t ofs) const
|
size_t ofs) const {
|
||||||
{
|
|
||||||
size_t max_vcount = 0;
|
size_t max_vcount = 0;
|
||||||
std::vector<unsigned int>::const_iterator begin = mVertcnt.begin()+ofs, end = mVertcnt.end(), iit;
|
std::vector<unsigned int>::const_iterator begin = mVertcnt.begin()+ofs, end = mVertcnt.end(), iit;
|
||||||
for(iit = begin; iit != end; ++iit) {
|
for(iit = begin; iit != end; ++iit) {
|
||||||
|
@ -235,7 +225,7 @@ IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const {
|
||||||
struct CompareVector {
|
struct CompareVector {
|
||||||
bool operator () (const IfcVector3& a, const IfcVector3& b) const {
|
bool operator () (const IfcVector3& a, const IfcVector3& b) const {
|
||||||
IfcVector3 d = a - b;
|
IfcVector3 d = a - b;
|
||||||
IfcFloat eps = ai_epsilon;
|
constexpr IfcFloat eps = ai_epsilon;
|
||||||
return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps);
|
return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -249,29 +239,27 @@ struct FindVector {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void TempMesh::FixupFaceOrientation()
|
void TempMesh::FixupFaceOrientation() {
|
||||||
{
|
|
||||||
const IfcVector3 vavg = Center();
|
const IfcVector3 vavg = Center();
|
||||||
|
|
||||||
// create a list of start indices for all faces to allow random access to faces
|
// create a list of start indices for all faces to allow random access to faces
|
||||||
std::vector<size_t> faceStartIndices(mVertcnt.size());
|
std::vector<size_t> faceStartIndices(mVertcnt.size());
|
||||||
for( size_t i = 0, a = 0; a < mVertcnt.size(); i += mVertcnt[a], ++a )
|
for( size_t i = 0, a = 0; a < mVertcnt.size(); i += mVertcnt[a], ++a ) {
|
||||||
faceStartIndices[a] = i;
|
faceStartIndices[a] = i;
|
||||||
|
}
|
||||||
|
|
||||||
// list all faces on a vertex
|
// list all faces on a vertex
|
||||||
std::map<IfcVector3, std::vector<size_t>, CompareVector> facesByVertex;
|
std::map<IfcVector3, std::vector<size_t>, CompareVector> facesByVertex;
|
||||||
for( size_t a = 0; a < mVertcnt.size(); ++a )
|
for( size_t a = 0; a < mVertcnt.size(); ++a ) {
|
||||||
{
|
for( size_t b = 0; b < mVertcnt[a]; ++b ) {
|
||||||
for( size_t b = 0; b < mVertcnt[a]; ++b )
|
|
||||||
facesByVertex[mVerts[faceStartIndices[a] + b]].push_back(a);
|
facesByVertex[mVerts[faceStartIndices[a] + b]].push_back(a);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// determine neighbourhood for all polys
|
// determine neighbourhood for all polys
|
||||||
std::vector<size_t> neighbour(mVerts.size(), SIZE_MAX);
|
std::vector<size_t> neighbour(mVerts.size(), SIZE_MAX);
|
||||||
std::vector<size_t> tempIntersect(10);
|
std::vector<size_t> tempIntersect(10);
|
||||||
for( size_t a = 0; a < mVertcnt.size(); ++a )
|
for( size_t a = 0; a < mVertcnt.size(); ++a ) {
|
||||||
{
|
for( size_t b = 0; b < mVertcnt[a]; ++b ) {
|
||||||
for( size_t b = 0; b < mVertcnt[a]; ++b )
|
|
||||||
{
|
|
||||||
size_t ib = faceStartIndices[a] + b, nib = faceStartIndices[a] + (b + 1) % mVertcnt[a];
|
size_t ib = faceStartIndices[a] + b, nib = faceStartIndices[a] + (b + 1) % mVertcnt[a];
|
||||||
const std::vector<size_t>& facesOnB = facesByVertex[mVerts[ib]];
|
const std::vector<size_t>& facesOnB = facesByVertex[mVerts[ib]];
|
||||||
const std::vector<size_t>& facesOnNB = facesByVertex[mVerts[nib]];
|
const std::vector<size_t>& facesOnNB = facesByVertex[mVerts[nib]];
|
||||||
|
@ -280,10 +268,12 @@ void TempMesh::FixupFaceOrientation()
|
||||||
std::vector<size_t>::iterator sectend = std::set_intersection(
|
std::vector<size_t>::iterator sectend = std::set_intersection(
|
||||||
facesOnB.begin(), facesOnB.end(), facesOnNB.begin(), facesOnNB.end(), sectstart);
|
facesOnB.begin(), facesOnB.end(), facesOnNB.begin(), facesOnNB.end(), sectstart);
|
||||||
|
|
||||||
if( std::distance(sectstart, sectend) != 2 )
|
if( std::distance(sectstart, sectend) != 2 ) {
|
||||||
continue;
|
continue;
|
||||||
if( *sectstart == a )
|
}
|
||||||
|
if( *sectstart == a ) {
|
||||||
++sectstart;
|
++sectstart;
|
||||||
|
}
|
||||||
neighbour[ib] = *sectstart;
|
neighbour[ib] = *sectstart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,15 +282,14 @@ void TempMesh::FixupFaceOrientation()
|
||||||
// facing outwards. So we reverse this face to point outwards in relation to the center. Then we adapt neighbouring
|
// facing outwards. So we reverse this face to point outwards in relation to the center. Then we adapt neighbouring
|
||||||
// faces to have the same winding until all faces have been tested.
|
// faces to have the same winding until all faces have been tested.
|
||||||
std::vector<bool> faceDone(mVertcnt.size(), false);
|
std::vector<bool> faceDone(mVertcnt.size(), false);
|
||||||
while( std::count(faceDone.begin(), faceDone.end(), false) != 0 )
|
while( std::count(faceDone.begin(), faceDone.end(), false) != 0 ) {
|
||||||
{
|
|
||||||
// find the farthest of the remaining faces
|
// find the farthest of the remaining faces
|
||||||
size_t farthestIndex = SIZE_MAX;
|
size_t farthestIndex = SIZE_MAX;
|
||||||
IfcFloat farthestDistance = -1.0;
|
IfcFloat farthestDistance = -1.0;
|
||||||
for( size_t a = 0; a < mVertcnt.size(); ++a )
|
for( size_t a = 0; a < mVertcnt.size(); ++a ) {
|
||||||
{
|
if( faceDone[a] ) {
|
||||||
if( faceDone[a] )
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
IfcVector3 faceCenter = std::accumulate(mVerts.begin() + faceStartIndices[a],
|
IfcVector3 faceCenter = std::accumulate(mVerts.begin() + faceStartIndices[a],
|
||||||
mVerts.begin() + faceStartIndices[a] + mVertcnt[a], IfcVector3(0.0)) / IfcFloat(mVertcnt[a]);
|
mVerts.begin() + faceStartIndices[a] + mVertcnt[a], IfcVector3(0.0)) / IfcFloat(mVertcnt[a]);
|
||||||
IfcFloat dst = (faceCenter - vavg).SquareLength();
|
IfcFloat dst = (faceCenter - vavg).SquareLength();
|
||||||
|
@ -314,8 +303,7 @@ void TempMesh::FixupFaceOrientation()
|
||||||
/ IfcFloat(mVertcnt[farthestIndex]);
|
/ IfcFloat(mVertcnt[farthestIndex]);
|
||||||
// We accept a bit of negative orientation without reversing. In case of doubt, prefer the orientation given in
|
// We accept a bit of negative orientation without reversing. In case of doubt, prefer the orientation given in
|
||||||
// the file.
|
// the file.
|
||||||
if( (farthestNormal * (farthestCenter - vavg).Normalize()) < -0.4 )
|
if( (farthestNormal * (farthestCenter - vavg).Normalize()) < -0.4 ) {
|
||||||
{
|
|
||||||
size_t fsi = faceStartIndices[farthestIndex], fvc = mVertcnt[farthestIndex];
|
size_t fsi = faceStartIndices[farthestIndex], fvc = mVertcnt[farthestIndex];
|
||||||
std::reverse(mVerts.begin() + fsi, mVerts.begin() + fsi + fvc);
|
std::reverse(mVerts.begin() + fsi, mVerts.begin() + fsi + fvc);
|
||||||
std::reverse(neighbour.begin() + fsi, neighbour.begin() + fsi + fvc);
|
std::reverse(neighbour.begin() + fsi, neighbour.begin() + fsi + fvc);
|
||||||
|
@ -332,19 +320,18 @@ void TempMesh::FixupFaceOrientation()
|
||||||
todo.push_back(farthestIndex);
|
todo.push_back(farthestIndex);
|
||||||
|
|
||||||
// go over its neighbour faces recursively and adapt their winding order to match the farthest face
|
// go over its neighbour faces recursively and adapt their winding order to match the farthest face
|
||||||
while( !todo.empty() )
|
while( !todo.empty() ) {
|
||||||
{
|
|
||||||
size_t tdf = todo.back();
|
size_t tdf = todo.back();
|
||||||
size_t vsi = faceStartIndices[tdf], vc = mVertcnt[tdf];
|
size_t vsi = faceStartIndices[tdf], vc = mVertcnt[tdf];
|
||||||
todo.pop_back();
|
todo.pop_back();
|
||||||
|
|
||||||
// check its neighbours
|
// check its neighbours
|
||||||
for( size_t a = 0; a < vc; ++a )
|
for( size_t a = 0; a < vc; ++a ) {
|
||||||
{
|
|
||||||
// ignore neighbours if we already checked them
|
// ignore neighbours if we already checked them
|
||||||
size_t nbi = neighbour[vsi + a];
|
size_t nbi = neighbour[vsi + a];
|
||||||
if( nbi == SIZE_MAX || faceDone[nbi] )
|
if( nbi == SIZE_MAX || faceDone[nbi] ) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const IfcVector3& vp = mVerts[vsi + a];
|
const IfcVector3& vp = mVerts[vsi + a];
|
||||||
size_t nbvsi = faceStartIndices[nbi], nbvc = mVertcnt[nbi];
|
size_t nbvsi = faceStartIndices[nbi], nbvc = mVertcnt[nbi];
|
||||||
|
@ -387,31 +374,7 @@ void TempMesh::RemoveAdjacentDuplicates() {
|
||||||
IfcVector3 vmin,vmax;
|
IfcVector3 vmin,vmax;
|
||||||
ArrayBounds(&*base, cnt ,vmin,vmax);
|
ArrayBounds(&*base, cnt ,vmin,vmax);
|
||||||
|
|
||||||
|
|
||||||
const IfcFloat epsilon = (vmax-vmin).SquareLength() / static_cast<IfcFloat>(1e9);
|
const IfcFloat epsilon = (vmax-vmin).SquareLength() / static_cast<IfcFloat>(1e9);
|
||||||
//const IfcFloat dotepsilon = 1e-9;
|
|
||||||
|
|
||||||
//// look for vertices that lie directly on the line between their predecessor and their
|
|
||||||
//// successor and replace them with either of them.
|
|
||||||
|
|
||||||
//for(size_t i = 0; i < cnt; ++i) {
|
|
||||||
// IfcVector3& v1 = *(base+i), &v0 = *(base+(i?i-1:cnt-1)), &v2 = *(base+(i+1)%cnt);
|
|
||||||
// const IfcVector3& d0 = (v1-v0), &d1 = (v2-v1);
|
|
||||||
// const IfcFloat l0 = d0.SquareLength(), l1 = d1.SquareLength();
|
|
||||||
// if (!l0 || !l1) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const IfcFloat d = (d0/std::sqrt(l0))*(d1/std::sqrt(l1));
|
|
||||||
|
|
||||||
// if ( d >= 1.f-dotepsilon ) {
|
|
||||||
// v1 = v0;
|
|
||||||
// }
|
|
||||||
// else if ( d < -1.f+dotepsilon ) {
|
|
||||||
// v2 = v1;
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// drop any identical, adjacent vertices. this pass will collect the dropouts
|
// drop any identical, adjacent vertices. this pass will collect the dropouts
|
||||||
// of the previous pass as a side-effect.
|
// of the previous pass as a side-effect.
|
||||||
|
@ -439,78 +402,58 @@ void TempMesh::RemoveAdjacentDuplicates() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void TempMesh::Swap(TempMesh& other)
|
void TempMesh::Swap(TempMesh& other) {
|
||||||
{
|
|
||||||
mVertcnt.swap(other.mVertcnt);
|
mVertcnt.swap(other.mVertcnt);
|
||||||
mVerts.swap(other.mVerts);
|
mVerts.swap(other.mVerts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool IsTrue(const ::Assimp::STEP::EXPRESS::BOOLEAN& in)
|
bool IsTrue(const ::Assimp::STEP::EXPRESS::BOOLEAN& in) {
|
||||||
{
|
|
||||||
return (std::string)in == "TRUE" || (std::string)in == "T";
|
return (std::string)in == "TRUE" || (std::string)in == "T";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
IfcFloat ConvertSIPrefix(const std::string& prefix)
|
IfcFloat ConvertSIPrefix(const std::string& prefix) {
|
||||||
{
|
|
||||||
if (prefix == "EXA") {
|
if (prefix == "EXA") {
|
||||||
return 1e18f;
|
return 1e18f;
|
||||||
}
|
} else if (prefix == "PETA") {
|
||||||
else if (prefix == "PETA") {
|
|
||||||
return 1e15f;
|
return 1e15f;
|
||||||
}
|
} else if (prefix == "TERA") {
|
||||||
else if (prefix == "TERA") {
|
|
||||||
return 1e12f;
|
return 1e12f;
|
||||||
}
|
} else if (prefix == "GIGA") {
|
||||||
else if (prefix == "GIGA") {
|
|
||||||
return 1e9f;
|
return 1e9f;
|
||||||
}
|
} else if (prefix == "MEGA") {
|
||||||
else if (prefix == "MEGA") {
|
|
||||||
return 1e6f;
|
return 1e6f;
|
||||||
}
|
} else if (prefix == "KILO") {
|
||||||
else if (prefix == "KILO") {
|
|
||||||
return 1e3f;
|
return 1e3f;
|
||||||
}
|
} else if (prefix == "HECTO") {
|
||||||
else if (prefix == "HECTO") {
|
|
||||||
return 1e2f;
|
return 1e2f;
|
||||||
}
|
} else if (prefix == "DECA") {
|
||||||
else if (prefix == "DECA") {
|
|
||||||
return 1e-0f;
|
return 1e-0f;
|
||||||
}
|
} else if (prefix == "DECI") {
|
||||||
else if (prefix == "DECI") {
|
|
||||||
return 1e-1f;
|
return 1e-1f;
|
||||||
}
|
} else if (prefix == "CENTI") {
|
||||||
else if (prefix == "CENTI") {
|
|
||||||
return 1e-2f;
|
return 1e-2f;
|
||||||
}
|
} else if (prefix == "MILLI") {
|
||||||
else if (prefix == "MILLI") {
|
|
||||||
return 1e-3f;
|
return 1e-3f;
|
||||||
}
|
} else if (prefix == "MICRO") {
|
||||||
else if (prefix == "MICRO") {
|
|
||||||
return 1e-6f;
|
return 1e-6f;
|
||||||
}
|
} else if (prefix == "NANO") {
|
||||||
else if (prefix == "NANO") {
|
|
||||||
return 1e-9f;
|
return 1e-9f;
|
||||||
}
|
} else if (prefix == "PICO") {
|
||||||
else if (prefix == "PICO") {
|
|
||||||
return 1e-12f;
|
return 1e-12f;
|
||||||
}
|
} else if (prefix == "FEMTO") {
|
||||||
else if (prefix == "FEMTO") {
|
|
||||||
return 1e-15f;
|
return 1e-15f;
|
||||||
}
|
} else if (prefix == "ATTO") {
|
||||||
else if (prefix == "ATTO") {
|
|
||||||
return 1e-18f;
|
return 1e-18f;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
IFCImporter::LogError("Unrecognized SI prefix: ", prefix);
|
IFCImporter::LogError("Unrecognized SI prefix: ", prefix);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in)
|
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in) {
|
||||||
{
|
|
||||||
out.r = static_cast<float>( in.Red );
|
out.r = static_cast<float>( in.Red );
|
||||||
out.g = static_cast<float>( in.Green );
|
out.g = static_cast<float>( in.Green );
|
||||||
out.b = static_cast<float>( in.Blue );
|
out.b = static_cast<float>( in.Blue );
|
||||||
|
@ -518,8 +461,10 @@ void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base)
|
void ConvertColor(aiColor4D& out,
|
||||||
{
|
const Schema_2x3::IfcColourOrFactor& in,
|
||||||
|
ConversionData& conv,
|
||||||
|
const aiColor4D* base) {
|
||||||
if (const ::Assimp::STEP::EXPRESS::REAL* const r = in.ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
|
if (const ::Assimp::STEP::EXPRESS::REAL* const r = in.ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
|
||||||
out.r = out.g = out.b = static_cast<float>(*r);
|
out.r = out.g = out.b = static_cast<float>(*r);
|
||||||
if(base) {
|
if(base) {
|
||||||
|
@ -527,20 +472,18 @@ void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,Conver
|
||||||
out.g *= static_cast<float>( base->g );
|
out.g *= static_cast<float>( base->g );
|
||||||
out.b *= static_cast<float>( base->b );
|
out.b *= static_cast<float>( base->b );
|
||||||
out.a = static_cast<float>( base->a );
|
out.a = static_cast<float>( base->a );
|
||||||
|
} else {
|
||||||
|
out.a = 1.0;
|
||||||
}
|
}
|
||||||
else out.a = 1.0;
|
} else if (const Schema_2x3::IfcColourRgb* const rgb = in.ResolveSelectPtr<Schema_2x3::IfcColourRgb>(conv.db)) {
|
||||||
}
|
|
||||||
else if (const Schema_2x3::IfcColourRgb* const rgb = in.ResolveSelectPtr<Schema_2x3::IfcColourRgb>(conv.db)) {
|
|
||||||
ConvertColor(out,*rgb);
|
ConvertColor(out,*rgb);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
IFCImporter::LogWarn("skipping unknown IfcColourOrFactor entity");
|
IFCImporter::LogWarn("skipping unknown IfcColourOrFactor entity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in)
|
void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in) {
|
||||||
{
|
|
||||||
out = IfcVector3();
|
out = IfcVector3();
|
||||||
for(size_t i = 0; i < in.Coordinates.size(); ++i) {
|
for(size_t i = 0; i < in.Coordinates.size(); ++i) {
|
||||||
out[static_cast<unsigned int>(i)] = in.Coordinates[i];
|
out[static_cast<unsigned int>(i)] = in.Coordinates[i];
|
||||||
|
@ -548,15 +491,13 @@ void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint&
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in)
|
void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in) {
|
||||||
{
|
|
||||||
ConvertDirection(out,in.Orientation);
|
ConvertDirection(out,in.Orientation);
|
||||||
out *= in.Magnitude;
|
out *= in.Magnitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in)
|
void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in) {
|
||||||
{
|
|
||||||
out = IfcVector3();
|
out = IfcVector3();
|
||||||
for(size_t i = 0; i < in.DirectionRatios.size(); ++i) {
|
for(size_t i = 0; i < in.DirectionRatios.size(); ++i) {
|
||||||
out[static_cast<unsigned int>(i)] = in.DirectionRatios[i];
|
out[static_cast<unsigned int>(i)] = in.DirectionRatios[i];
|
||||||
|
@ -570,8 +511,7 @@ void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z)
|
void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z) {
|
||||||
{
|
|
||||||
out.a1 = x.x;
|
out.a1 = x.x;
|
||||||
out.b1 = x.y;
|
out.b1 = x.y;
|
||||||
out.c1 = x.z;
|
out.c1 = x.z;
|
||||||
|
@ -586,8 +526,7 @@ void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in)
|
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in) {
|
||||||
{
|
|
||||||
IfcVector3 loc;
|
IfcVector3 loc;
|
||||||
ConvertCartesianPoint(loc,in.Location);
|
ConvertCartesianPoint(loc,in.Location);
|
||||||
|
|
||||||
|
@ -611,8 +550,7 @@ void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in)
|
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in) {
|
||||||
{
|
|
||||||
IfcVector3 loc;
|
IfcVector3 loc;
|
||||||
ConvertCartesianPoint(loc,in.Location);
|
ConvertCartesianPoint(loc,in.Location);
|
||||||
|
|
||||||
|
@ -628,34 +566,28 @@ void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const Schema_2x3::IfcAxis1Placement& in)
|
void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const Schema_2x3::IfcAxis1Placement& in) {
|
||||||
{
|
|
||||||
ConvertCartesianPoint(pos,in.Location);
|
ConvertCartesianPoint(pos,in.Location);
|
||||||
if (in.Axis) {
|
if (in.Axis) {
|
||||||
ConvertDirection(axis,in.Axis.Get());
|
ConvertDirection(axis,in.Axis.Get());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
axis = IfcVector3(0.f,0.f,1.f);
|
axis = IfcVector3(0.f,0.f,1.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv)
|
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv) {
|
||||||
{
|
|
||||||
if(const Schema_2x3::IfcAxis2Placement3D* pl3 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement3D>(conv.db)) {
|
if(const Schema_2x3::IfcAxis2Placement3D* pl3 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement3D>(conv.db)) {
|
||||||
ConvertAxisPlacement(out,*pl3);
|
ConvertAxisPlacement(out,*pl3);
|
||||||
}
|
} else if(const Schema_2x3::IfcAxis2Placement2D* pl2 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement2D>(conv.db)) {
|
||||||
else if(const Schema_2x3::IfcAxis2Placement2D* pl2 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement2D>(conv.db)) {
|
|
||||||
ConvertAxisPlacement(out,*pl2);
|
ConvertAxisPlacement(out,*pl2);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
IFCImporter::LogWarn("skipping unknown IfcAxis2Placement entity");
|
IFCImporter::LogWarn("skipping unknown IfcAxis2Placement entity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op)
|
void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op) {
|
||||||
{
|
|
||||||
IfcVector3 loc;
|
IfcVector3 loc;
|
||||||
ConvertCartesianPoint(loc,op.LocalOrigin);
|
ConvertCartesianPoint(loc,op.LocalOrigin);
|
||||||
|
|
||||||
|
@ -676,14 +608,12 @@ void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTra
|
||||||
IfcMatrix4::Translation(loc,locm);
|
IfcMatrix4::Translation(loc,locm);
|
||||||
AssignMatrixAxes(out,x,y,z);
|
AssignMatrixAxes(out,x,y,z);
|
||||||
|
|
||||||
|
|
||||||
IfcVector3 vscale;
|
IfcVector3 vscale;
|
||||||
if (const Schema_2x3::IfcCartesianTransformationOperator3DnonUniform* nuni = op.ToPtr<Schema_2x3::IfcCartesianTransformationOperator3DnonUniform>()) {
|
if (const Schema_2x3::IfcCartesianTransformationOperator3DnonUniform* nuni = op.ToPtr<Schema_2x3::IfcCartesianTransformationOperator3DnonUniform>()) {
|
||||||
vscale.x = nuni->Scale?op.Scale.Get():1.f;
|
vscale.x = nuni->Scale?op.Scale.Get():1.f;
|
||||||
vscale.y = nuni->Scale2?nuni->Scale2.Get():1.f;
|
vscale.y = nuni->Scale2?nuni->Scale2.Get():1.f;
|
||||||
vscale.z = nuni->Scale3?nuni->Scale3.Get():1.f;
|
vscale.z = nuni->Scale3?nuni->Scale3.Get():1.f;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const IfcFloat sc = op.Scale?op.Scale.Get():1.f;
|
const IfcFloat sc = op.Scale?op.Scale.Get():1.f;
|
||||||
vscale = IfcVector3(sc,sc,sc);
|
vscale = IfcVector3(sc,sc,sc);
|
||||||
}
|
}
|
||||||
|
@ -694,8 +624,7 @@ void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTra
|
||||||
out = locm * out * s;
|
out = locm * out * s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // ! IFC
|
} // ! IFC
|
||||||
} // ! Assimp
|
} // ! Assimp
|
||||||
|
|
||||||
#endif
|
#endif // ASSIMP_BUILD_NO_IFC_IMPORTER
|
||||||
|
|
|
@ -43,6 +43,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
* @brief Implementation of the Irr importer class
|
* @brief Implementation of the Irr importer class
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "assimp/Exceptional.h"
|
||||||
|
#include "assimp/StringComparison.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
|
||||||
|
|
||||||
#include "AssetLib/Irr/IRRLoader.h"
|
#include "AssetLib/Irr/IRRLoader.h"
|
||||||
|
@ -62,8 +64,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/DefaultLogger.hpp>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
#include <assimp/IOSystem.hpp>
|
#include <assimp/IOSystem.hpp>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
static const aiImporterDesc desc = {
|
static const aiImporterDesc desc = {
|
||||||
|
@ -239,7 +239,7 @@ void IRRImporter::CopyMaterial(std::vector<aiMaterial *> &materials,
|
||||||
// Do we have a default material? If not we need to create one
|
// Do we have a default material? If not we need to create one
|
||||||
if (UINT_MAX == defMatIdx) {
|
if (UINT_MAX == defMatIdx) {
|
||||||
defMatIdx = (unsigned int)materials.size();
|
defMatIdx = (unsigned int)materials.size();
|
||||||
//TODO: add this materials to someone?
|
// TODO: add this materials to someone?
|
||||||
/*aiMaterial* mat = new aiMaterial();
|
/*aiMaterial* mat = new aiMaterial();
|
||||||
|
|
||||||
aiString s;
|
aiString s;
|
||||||
|
@ -380,7 +380,6 @@ void IRRImporter::ComputeAnimations(Node *root, aiNode *real, std::vector<aiNode
|
||||||
if (360 == lcm)
|
if (360 == lcm)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
// find out how many time units we'll need for the finest
|
// find out how many time units we'll need for the finest
|
||||||
// track (in seconds) - this defines the number of output
|
// track (in seconds) - this defines the number of output
|
||||||
// keys (fps * seconds)
|
// keys (fps * seconds)
|
||||||
|
@ -609,7 +608,7 @@ void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
|
||||||
std::vector<aiMaterial *> &materials,
|
std::vector<aiMaterial *> &materials,
|
||||||
unsigned int &defMatIdx) {
|
unsigned int &defMatIdx) {
|
||||||
unsigned int oldMeshSize = (unsigned int)meshes.size();
|
unsigned int oldMeshSize = (unsigned int)meshes.size();
|
||||||
//unsigned int meshTrafoAssign = 0;
|
// unsigned int meshTrafoAssign = 0;
|
||||||
|
|
||||||
// Now determine the type of the node
|
// Now determine the type of the node
|
||||||
switch (root->type) {
|
switch (root->type) {
|
||||||
|
@ -797,7 +796,7 @@ void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
|
||||||
// Now compute the final local transformation matrix of the
|
// Now compute the final local transformation matrix of the
|
||||||
// node from the given translation, rotation and scaling values.
|
// node from the given translation, rotation and scaling values.
|
||||||
// (the rotation is given in Euler angles, XYZ order)
|
// (the rotation is given in Euler angles, XYZ order)
|
||||||
//std::swap((float&)root->rotation.z,(float&)root->rotation.y);
|
// std::swap((float&)root->rotation.z,(float&)root->rotation.y);
|
||||||
rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation));
|
rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation));
|
||||||
|
|
||||||
// apply scaling
|
// apply scaling
|
||||||
|
@ -836,175 +835,164 @@ void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
void IRRImporter::ParseNodeAttributes(pugi::xml_node &attributesNode, IRRImporter::Node *nd, BatchLoader &batch) {
|
||||||
// Imports the given file into the given scene structure.
|
ai_assert(!ASSIMP_stricmp(attributesNode.name(), "attributes")); // Node must be <attributes>
|
||||||
void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
ai_assert(nd != nullptr); // dude
|
||||||
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
|
|
||||||
|
|
||||||
// Check whether we can read from the file
|
// Big switch statement that tests for various tags inside <attributes>
|
||||||
if (file == nullptr) {
|
// and applies them to nd
|
||||||
throw DeadlyImportError("Failed to open IRR file ", pFile);
|
// I don't believe nodes have boolean attributes
|
||||||
|
for (pugi::xml_node &attribute : attributesNode.children()) {
|
||||||
|
if (attribute.type() != pugi::node_element) continue;
|
||||||
|
if (!ASSIMP_stricmp(attribute.name(), "vector3d")) { // <vector3d />
|
||||||
|
VectorProperty prop;
|
||||||
|
ReadVectorProperty(prop, attribute);
|
||||||
|
if (prop.name == "Position") {
|
||||||
|
nd->position = prop.value;
|
||||||
|
} else if (prop.name == "Rotation") {
|
||||||
|
nd->rotation = prop.value;
|
||||||
|
} else if (prop.name == "Scale") {
|
||||||
|
nd->scaling = prop.value;
|
||||||
|
} else if (Node::CAMERA == nd->type) {
|
||||||
|
aiCamera *cam = cameras.back();
|
||||||
|
if (prop.name == "Target") {
|
||||||
|
cam->mLookAt = prop.value;
|
||||||
|
} else if (prop.name == "UpVector") {
|
||||||
|
cam->mUp = prop.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the irrXML parser
|
|
||||||
XmlParser st;
|
|
||||||
if (!st.parse( file.get() )) {
|
|
||||||
throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
|
|
||||||
}
|
}
|
||||||
pugi::xml_node rootElement = st.getRootNode();
|
} else if (!ASSIMP_stricmp(attribute.name(), "float")) { // <float />
|
||||||
|
FloatProperty prop;
|
||||||
// The root node of the scene
|
ReadFloatProperty(prop, attribute);
|
||||||
Node *root = new Node(Node::DUMMY);
|
if (prop.name == "FramesPerSecond" && Node::ANIMMESH == nd->type) {
|
||||||
root->parent = nullptr;
|
nd->framesPerSecond = prop.value;
|
||||||
root->name = "<IRRSceneRoot>";
|
} else if (Node::CAMERA == nd->type) {
|
||||||
|
/* This is the vertical, not the horizontal FOV.
|
||||||
// Current node parent
|
* We need to compute the right FOV from the
|
||||||
Node *curParent = root;
|
* screen aspect which we don't know yet.
|
||||||
|
|
||||||
// Scene-graph node we're currently working on
|
|
||||||
Node *curNode = nullptr;
|
|
||||||
|
|
||||||
// List of output cameras
|
|
||||||
std::vector<aiCamera *> cameras;
|
|
||||||
|
|
||||||
// List of output lights
|
|
||||||
std::vector<aiLight *> lights;
|
|
||||||
|
|
||||||
// Batch loader used to load external models
|
|
||||||
BatchLoader batch(pIOHandler);
|
|
||||||
//batch.SetBasePath(pFile);
|
|
||||||
|
|
||||||
cameras.reserve(5);
|
|
||||||
lights.reserve(5);
|
|
||||||
|
|
||||||
bool inMaterials = false, inAnimator = false;
|
|
||||||
unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
|
|
||||||
|
|
||||||
// Parse the XML file
|
|
||||||
|
|
||||||
//while (reader->read()) {
|
|
||||||
for (pugi::xml_node child : rootElement.children())
|
|
||||||
switch (child.type()) {
|
|
||||||
case pugi::node_element:
|
|
||||||
if (!ASSIMP_stricmp(child.name(), "node")) {
|
|
||||||
// ***********************************************************************
|
|
||||||
/* What we're going to do with the node depends
|
|
||||||
* on its type:
|
|
||||||
*
|
|
||||||
* "mesh" - Load a mesh from an external file
|
|
||||||
* "cube" - Generate a cube
|
|
||||||
* "skybox" - Generate a skybox
|
|
||||||
* "light" - A light source
|
|
||||||
* "sphere" - Generate a sphere mesh
|
|
||||||
* "animatedMesh" - Load an animated mesh from an external file
|
|
||||||
* and join its animation channels with ours.
|
|
||||||
* "empty" - A dummy node
|
|
||||||
* "camera" - A camera
|
|
||||||
* "terrain" - a terrain node (data comes from a heightmap)
|
|
||||||
* "billboard", ""
|
|
||||||
*
|
|
||||||
* Each of these nodes can be animated and all can have multiple
|
|
||||||
* materials assigned (except lights, cameras and dummies, of course).
|
|
||||||
*/
|
*/
|
||||||
// ***********************************************************************
|
if (prop.name == "Fovy") {
|
||||||
//const char *sz = reader->getAttributeValueSafe("type");
|
cameras.back()->mHorizontalFOV = prop.value;
|
||||||
pugi::xml_attribute attrib = child.attribute("type");
|
} else if (prop.name == "Aspect") {
|
||||||
Node *nd;
|
cameras.back()->mAspect = prop.value;
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "mesh") || !ASSIMP_stricmp(attrib.name(), "octTree")) {
|
} else if (prop.name == "ZNear") {
|
||||||
// OctTree's and meshes are treated equally
|
cameras.back()->mClipPlaneNear = prop.value;
|
||||||
nd = new Node(Node::MESH);
|
} else if (prop.name == "ZFar") {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "cube")) {
|
cameras.back()->mClipPlaneFar = prop.value;
|
||||||
nd = new Node(Node::CUBE);
|
}
|
||||||
++guessedMeshCnt;
|
} else if (Node::LIGHT == nd->type) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "skybox")) {
|
/* Additional light information
|
||||||
nd = new Node(Node::SKYBOX);
|
*/
|
||||||
guessedMeshCnt += 6;
|
if (prop.name == "Attenuation") {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "camera")) {
|
lights.back()->mAttenuationLinear = prop.value;
|
||||||
nd = new Node(Node::CAMERA);
|
} else if (prop.name == "OuterCone") {
|
||||||
|
lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
|
||||||
|
} else if (prop.name == "InnerCone") {
|
||||||
|
lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// radius of the sphere to be generated -
|
||||||
|
// or alternatively, size of the cube
|
||||||
|
else if ((Node::SPHERE == nd->type && prop.name == "Radius") ||
|
||||||
|
(Node::CUBE == nd->type && prop.name == "Size")) {
|
||||||
|
nd->sphereRadius = prop.value;
|
||||||
|
}
|
||||||
|
} else if (!ASSIMP_stricmp(attribute.name(), "int")) { // <int />
|
||||||
|
// Only sphere nodes make use of integer attributes
|
||||||
|
if (Node::SPHERE == nd->type) {
|
||||||
|
IntProperty prop;
|
||||||
|
ReadIntProperty(prop, attribute);
|
||||||
|
if (prop.name == "PolyCountX") {
|
||||||
|
nd->spherePolyCountX = prop.value;
|
||||||
|
} else if (prop.name == "PolyCountY") {
|
||||||
|
nd->spherePolyCountY = prop.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!ASSIMP_stricmp(attribute.name(), "string") || !ASSIMP_stricmp(attribute.name(), "enum")) { // <string /> or < enum />
|
||||||
|
StringProperty prop;
|
||||||
|
ReadStringProperty(prop, attribute);
|
||||||
|
if (prop.value.length() == 0) continue; // skip empty strings
|
||||||
|
if (prop.name == "Name") {
|
||||||
|
nd->name = prop.value;
|
||||||
|
|
||||||
// Setup a temporary name for the camera
|
/* If we're either a camera or a light source
|
||||||
aiCamera *cam = new aiCamera();
|
* we need to update the name in the aiLight/
|
||||||
cam->mName.Set(nd->name);
|
* aiCamera structure, too.
|
||||||
cameras.push_back(cam);
|
*/
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "light")) {
|
if (Node::CAMERA == nd->type) {
|
||||||
nd = new Node(Node::LIGHT);
|
cameras.back()->mName.Set(prop.value);
|
||||||
|
} else if (Node::LIGHT == nd->type) {
|
||||||
|
lights.back()->mName.Set(prop.value);
|
||||||
|
}
|
||||||
|
} else if (Node::LIGHT == nd->type && "LightType" == prop.name) {
|
||||||
|
if (prop.value == "Spot")
|
||||||
|
lights.back()->mType = aiLightSource_SPOT;
|
||||||
|
else if (prop.value == "Point")
|
||||||
|
lights.back()->mType = aiLightSource_POINT;
|
||||||
|
else if (prop.value == "Directional")
|
||||||
|
lights.back()->mType = aiLightSource_DIRECTIONAL;
|
||||||
|
else {
|
||||||
|
// We won't pass the validation with aiLightSourceType_UNDEFINED,
|
||||||
|
// so we remove the light and replace it with a silly dummy node
|
||||||
|
delete lights.back();
|
||||||
|
lights.pop_back();
|
||||||
|
nd->type = Node::DUMMY;
|
||||||
|
|
||||||
// Setup a temporary name for the light
|
ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value);
|
||||||
aiLight *cam = new aiLight();
|
}
|
||||||
cam->mName.Set(nd->name);
|
} else if ((prop.name == "Mesh" && Node::MESH == nd->type) ||
|
||||||
lights.push_back(cam);
|
Node::ANIMMESH == nd->type) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "sphere")) {
|
/* This is the file name of the mesh - either
|
||||||
nd = new Node(Node::SPHERE);
|
* animated or not. We need to make sure we setup
|
||||||
++guessedMeshCnt;
|
* the correct post-processing settings here.
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "animatedMesh")) {
|
*/
|
||||||
nd = new Node(Node::ANIMMESH);
|
unsigned int pp = 0;
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "empty")) {
|
BatchLoader::PropertyMap map;
|
||||||
nd = new Node(Node::DUMMY);
|
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "terrain")) {
|
/* If the mesh is a static one remove all animations from the impor data
|
||||||
nd = new Node(Node::TERRAIN);
|
*/
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "billBoard")) {
|
if (Node::ANIMMESH != nd->type) {
|
||||||
// We don't support billboards, so ignore them
|
pp |= aiProcess_RemoveComponent;
|
||||||
ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp");
|
SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
|
||||||
nd = new Node(Node::DUMMY);
|
aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: maybe implement the protection against recursive
|
||||||
|
* loading calls directly in BatchLoader? The current
|
||||||
|
* implementation is not absolutely safe. A LWS and an IRR
|
||||||
|
* file referencing each other *could* cause the system to
|
||||||
|
* recurse forever.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const std::string extension = GetExtension(prop.value);
|
||||||
|
if ("irr" == extension) {
|
||||||
|
ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
|
||||||
} else {
|
} else {
|
||||||
ASSIMP_LOG_WARN("IRR: Found unknown node: ", attrib.name());
|
nd->id = batch.AddLoadRequest(prop.value, pp, &map);
|
||||||
|
nd->meshPath = prop.value;
|
||||||
/* We skip the contents of nodes we don't know.
|
|
||||||
* We parse the transformation and all animators
|
|
||||||
* and skip the rest.
|
|
||||||
*/
|
|
||||||
nd = new Node(Node::DUMMY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attach the newly created node to the scene-graph
|
|
||||||
*/
|
|
||||||
curNode = nd;
|
|
||||||
nd->parent = curParent;
|
|
||||||
curParent->children.push_back(nd);
|
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "materials")) {
|
|
||||||
inMaterials = true;
|
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "animators")) {
|
|
||||||
inAnimator = true;
|
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "attributes")) {
|
|
||||||
// We should have a valid node here
|
|
||||||
// FIX: no ... the scene root node is also contained in an attributes block
|
|
||||||
if (!curNode) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRRImporter::ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd) {
|
||||||
Animator *curAnim = nullptr;
|
Animator *curAnim = nullptr;
|
||||||
|
// Make empty animator
|
||||||
// Materials can occur for nearly any type of node
|
nd->animators.emplace_back();
|
||||||
if (inMaterials && curNode->type != Node::DUMMY) {
|
curAnim = &nd->animators.back(); // Push it back
|
||||||
// This is a material description - parse it!
|
pugi::xml_node attributes = animatorNode.child("attributes");
|
||||||
curNode->materials.emplace_back();
|
if (!attributes) {
|
||||||
std::pair<aiMaterial *, unsigned int> &p = curNode->materials.back();
|
ASSIMP_LOG_WARN("Animator node does not contain attributes. ");
|
||||||
|
return;
|
||||||
p.first = ParseMaterial(p.second);
|
|
||||||
++guessedMatCnt;
|
|
||||||
continue;
|
|
||||||
} else if (inAnimator) {
|
|
||||||
// This is an animation path - add a new animator
|
|
||||||
// to the list.
|
|
||||||
curNode->animators.emplace_back();
|
|
||||||
curAnim = &curNode->animators.back();
|
|
||||||
|
|
||||||
++guessedAnimCnt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse all elements in the attributes block
|
for (pugi::xml_node attrib : attributes.children()) {
|
||||||
* and process them.
|
// XML may contain useless noes like CDATA
|
||||||
*/
|
|
||||||
// while (reader->read()) {
|
|
||||||
for (pugi::xml_node attrib : child.children()) {
|
|
||||||
if (attrib.type() == pugi::node_element) {
|
|
||||||
//if (reader->getNodeType() == EXN_ELEMENT) {
|
|
||||||
//if (!ASSIMP_stricmp(reader->getNodeName(), "vector3d")) {
|
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "vector3d")) {
|
if (!ASSIMP_stricmp(attrib.name(), "vector3d")) {
|
||||||
VectorProperty prop;
|
VectorProperty prop;
|
||||||
ReadVectorProperty(prop);
|
ReadVectorProperty(prop, attrib);
|
||||||
|
|
||||||
if (inAnimator) {
|
|
||||||
if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
|
if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
|
||||||
// We store the rotation euler angles in 'direction'
|
// We store the rotation euler angles in 'direction'
|
||||||
curAnim->direction = prop.value;
|
curAnim->direction = prop.value;
|
||||||
|
@ -1041,36 +1029,20 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
curAnim->direction = prop.value;
|
curAnim->direction = prop.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (prop.name == "Position") {
|
|
||||||
curNode->position = prop.value;
|
|
||||||
} else if (prop.name == "Rotation") {
|
|
||||||
curNode->rotation = prop.value;
|
|
||||||
} else if (prop.name == "Scale") {
|
|
||||||
curNode->scaling = prop.value;
|
|
||||||
} else if (Node::CAMERA == curNode->type) {
|
|
||||||
aiCamera *cam = cameras.back();
|
|
||||||
if (prop.name == "Target") {
|
|
||||||
cam->mLookAt = prop.value;
|
|
||||||
} else if (prop.name == "UpVector") {
|
|
||||||
cam->mUp = prop.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
|
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "bool")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "bool")) {
|
||||||
BoolProperty prop;
|
BoolProperty prop;
|
||||||
ReadBoolProperty(prop);
|
ReadBoolProperty(prop, attrib);
|
||||||
|
|
||||||
if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
|
if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
|
||||||
curAnim->loop = prop.value;
|
curAnim->loop = prop.value;
|
||||||
}
|
}
|
||||||
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
|
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "float")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "float")) {
|
||||||
FloatProperty prop;
|
FloatProperty prop;
|
||||||
ReadFloatProperty(prop);
|
ReadFloatProperty(prop, attrib);
|
||||||
|
|
||||||
if (inAnimator) {
|
|
||||||
// The speed property exists for several animators
|
// The speed property exists for several animators
|
||||||
if (prop.name == "Speed") {
|
if (prop.name == "Speed") {
|
||||||
curAnim->speed = prop.value;
|
curAnim->speed = prop.value;
|
||||||
|
@ -1079,126 +1051,20 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
} else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
|
} else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
|
||||||
curAnim->tightness = prop.value;
|
curAnim->tightness = prop.value;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
|
|
||||||
curNode->framesPerSecond = prop.value;
|
|
||||||
} else if (Node::CAMERA == curNode->type) {
|
|
||||||
/* This is the vertical, not the horizontal FOV.
|
|
||||||
* We need to compute the right FOV from the
|
|
||||||
* screen aspect which we don't know yet.
|
|
||||||
*/
|
|
||||||
if (prop.name == "Fovy") {
|
|
||||||
cameras.back()->mHorizontalFOV = prop.value;
|
|
||||||
} else if (prop.name == "Aspect") {
|
|
||||||
cameras.back()->mAspect = prop.value;
|
|
||||||
} else if (prop.name == "ZNear") {
|
|
||||||
cameras.back()->mClipPlaneNear = prop.value;
|
|
||||||
} else if (prop.name == "ZFar") {
|
|
||||||
cameras.back()->mClipPlaneFar = prop.value;
|
|
||||||
}
|
|
||||||
} else if (Node::LIGHT == curNode->type) {
|
|
||||||
/* Additional light information
|
|
||||||
*/
|
|
||||||
if (prop.name == "Attenuation") {
|
|
||||||
lights.back()->mAttenuationLinear = prop.value;
|
|
||||||
} else if (prop.name == "OuterCone") {
|
|
||||||
lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
|
|
||||||
} else if (prop.name == "InnerCone") {
|
|
||||||
lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// radius of the sphere to be generated -
|
|
||||||
// or alternatively, size of the cube
|
|
||||||
else if ((Node::SPHERE == curNode->type && prop.name == "Radius") || (Node::CUBE == curNode->type && prop.name == "Size")) {
|
|
||||||
|
|
||||||
curNode->sphereRadius = prop.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
|
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "int")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "int")) {
|
||||||
IntProperty prop;
|
IntProperty prop;
|
||||||
ReadIntProperty(prop);
|
ReadIntProperty(prop, attrib);
|
||||||
|
|
||||||
if (inAnimator) {
|
|
||||||
if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
|
if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
|
||||||
curAnim->timeForWay = prop.value;
|
curAnim->timeForWay = prop.value;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// sphere polygon numbers in each direction
|
|
||||||
if (Node::SPHERE == curNode->type) {
|
|
||||||
|
|
||||||
if (prop.name == "PolyCountX") {
|
|
||||||
curNode->spherePolyCountX = prop.value;
|
|
||||||
} else if (prop.name == "PolyCountY") {
|
|
||||||
curNode->spherePolyCountY = prop.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
|
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) {
|
||||||
StringProperty prop;
|
StringProperty prop;
|
||||||
ReadStringProperty(prop);
|
ReadStringProperty(prop, attrib);
|
||||||
if (prop.value.length()) {
|
|
||||||
if (prop.name == "Name") {
|
|
||||||
curNode->name = prop.value;
|
|
||||||
|
|
||||||
/* If we're either a camera or a light source
|
if (prop.name == "Type") {
|
||||||
* we need to update the name in the aiLight/
|
|
||||||
* aiCamera structure, too.
|
|
||||||
*/
|
|
||||||
if (Node::CAMERA == curNode->type) {
|
|
||||||
cameras.back()->mName.Set(prop.value);
|
|
||||||
} else if (Node::LIGHT == curNode->type) {
|
|
||||||
lights.back()->mName.Set(prop.value);
|
|
||||||
}
|
|
||||||
} else if (Node::LIGHT == curNode->type && "LightType" == prop.name) {
|
|
||||||
if (prop.value == "Spot")
|
|
||||||
lights.back()->mType = aiLightSource_SPOT;
|
|
||||||
else if (prop.value == "Point")
|
|
||||||
lights.back()->mType = aiLightSource_POINT;
|
|
||||||
else if (prop.value == "Directional")
|
|
||||||
lights.back()->mType = aiLightSource_DIRECTIONAL;
|
|
||||||
else {
|
|
||||||
// We won't pass the validation with aiLightSourceType_UNDEFINED,
|
|
||||||
// so we remove the light and replace it with a silly dummy node
|
|
||||||
delete lights.back();
|
|
||||||
lights.pop_back();
|
|
||||||
curNode->type = Node::DUMMY;
|
|
||||||
|
|
||||||
ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value);
|
|
||||||
}
|
|
||||||
} else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
|
|
||||||
Node::ANIMMESH == curNode->type) {
|
|
||||||
/* This is the file name of the mesh - either
|
|
||||||
* animated or not. We need to make sure we setup
|
|
||||||
* the correct post-processing settings here.
|
|
||||||
*/
|
|
||||||
unsigned int pp = 0;
|
|
||||||
BatchLoader::PropertyMap map;
|
|
||||||
|
|
||||||
/* If the mesh is a static one remove all animations from the impor data
|
|
||||||
*/
|
|
||||||
if (Node::ANIMMESH != curNode->type) {
|
|
||||||
pp |= aiProcess_RemoveComponent;
|
|
||||||
SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
|
|
||||||
aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: maybe implement the protection against recursive
|
|
||||||
* loading calls directly in BatchLoader? The current
|
|
||||||
* implementation is not absolutely safe. A LWS and an IRR
|
|
||||||
* file referencing each other *could* cause the system to
|
|
||||||
* recurse forever.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const std::string extension = GetExtension(prop.value);
|
|
||||||
if ("irr" == extension) {
|
|
||||||
ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
|
|
||||||
} else {
|
|
||||||
curNode->id = batch.AddLoadRequest(prop.value, pp, &map);
|
|
||||||
curNode->meshPath = prop.value;
|
|
||||||
}
|
|
||||||
} else if (inAnimator && prop.name == "Type") {
|
|
||||||
// type of the animator
|
// type of the animator
|
||||||
if (prop.value == "rotation") {
|
if (prop.value == "rotation") {
|
||||||
curAnim->type = Animator::ROTATION;
|
curAnim->type = Animator::ROTATION;
|
||||||
|
@ -1216,42 +1082,168 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//} else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(), "attributes")) {
|
}
|
||||||
} else if (attrib.type() == pugi::node_null && !ASSIMP_stricmp(attrib.name(), "attributes")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/*case EXN_ELEMENT_END:
|
IRRImporter::Node *IRRImporter::ParseNode(pugi::xml_node &node, BatchLoader &batch) {
|
||||||
|
// Parse <node> tags.
|
||||||
|
// <node> tags have various types
|
||||||
|
// <node> tags can contain <attribute>, <material>
|
||||||
|
// they can also contain other <node> tags, (and can reference other files as well?)
|
||||||
|
// ***********************************************************************
|
||||||
|
/* What we're going to do with the node depends
|
||||||
|
* on its type:
|
||||||
|
*
|
||||||
|
* "mesh" - Load a mesh from an external file
|
||||||
|
* "cube" - Generate a cube
|
||||||
|
* "skybox" - Generate a skybox
|
||||||
|
* "light" - A light source
|
||||||
|
* "sphere" - Generate a sphere mesh
|
||||||
|
* "animatedMesh" - Load an animated mesh from an external file
|
||||||
|
* and join its animation channels with ours.
|
||||||
|
* "empty" - A dummy node
|
||||||
|
* "camera" - A camera
|
||||||
|
* "terrain" - a terrain node (data comes from a heightmap)
|
||||||
|
* "billboard", ""
|
||||||
|
*
|
||||||
|
* Each of these nodes can be animated and all can have multiple
|
||||||
|
* materials assigned (except lights, cameras and dummies, of course).
|
||||||
|
* Said materials and animators are all collected at the bottom
|
||||||
|
*/
|
||||||
|
// ***********************************************************************
|
||||||
|
Node *nd;
|
||||||
|
pugi::xml_attribute nodeTypeAttrib = node.attribute("type");
|
||||||
|
if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "mesh") || !ASSIMP_stricmp(nodeTypeAttrib.value(), "octTree")) {
|
||||||
|
// OctTree's and meshes are treated equally
|
||||||
|
nd = new Node(Node::MESH);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "cube")) {
|
||||||
|
nd = new Node(Node::CUBE);
|
||||||
|
guessedMeshCnt += 1; // Cube is only one mesh
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "skybox")) {
|
||||||
|
nd = new Node(Node::SKYBOX);
|
||||||
|
guessedMeshCnt += 6; // Skybox is a box, with 6 meshes?
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "camera")) {
|
||||||
|
nd = new Node(Node::CAMERA);
|
||||||
|
// Setup a temporary name for the camera
|
||||||
|
aiCamera *cam = new aiCamera();
|
||||||
|
cam->mName.Set(nd->name);
|
||||||
|
cameras.push_back(cam);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "light")) {
|
||||||
|
nd = new Node(Node::LIGHT);
|
||||||
|
// Setup a temporary name for the light
|
||||||
|
aiLight *cam = new aiLight();
|
||||||
|
cam->mName.Set(nd->name);
|
||||||
|
lights.push_back(cam);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "sphere")) {
|
||||||
|
nd = new Node(Node::SPHERE);
|
||||||
|
guessedMeshCnt += 1;
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "animatedMesh")) {
|
||||||
|
nd = new Node(Node::ANIMMESH);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "empty")) {
|
||||||
|
nd = new Node(Node::DUMMY);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "terrain")) {
|
||||||
|
nd = new Node(Node::TERRAIN);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "billBoard")) {
|
||||||
|
// We don't support billboards, so ignore them
|
||||||
|
ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp");
|
||||||
|
nd = new Node(Node::DUMMY);
|
||||||
|
} else {
|
||||||
|
ASSIMP_LOG_WARN("IRR: Found unknown node: ", nodeTypeAttrib.value());
|
||||||
|
|
||||||
// If we reached the end of a node, we need to continue processing its parent
|
/* We skip the contents of nodes we don't know.
|
||||||
if (!ASSIMP_stricmp(reader->getNodeName(), "node")) {
|
* We parse the transformation and all animators
|
||||||
if (!curNode) {
|
* and skip the rest.
|
||||||
// currently is no node set. We need to go
|
*/
|
||||||
// back in the node hierarchy
|
nd = new Node(Node::DUMMY);
|
||||||
if (!curParent) {
|
|
||||||
curParent = root;
|
|
||||||
ASSIMP_LOG_ERROR("IRR: Too many closing <node> elements");
|
|
||||||
} else
|
|
||||||
curParent = curParent->parent;
|
|
||||||
} else
|
|
||||||
curNode = nullptr;
|
|
||||||
}
|
}
|
||||||
// clear all flags
|
|
||||||
else if (!ASSIMP_stricmp(reader->getNodeName(), "materials")) {
|
|
||||||
inMaterials = false;
|
|
||||||
} else if (!ASSIMP_stricmp(reader->getNodeName(), "animators")) {
|
|
||||||
inAnimator = false;
|
|
||||||
}
|
|
||||||
break;*/
|
|
||||||
|
|
||||||
default:
|
// TODO: consolidate all into one loop
|
||||||
// GCC complains that not all enumeration values are handled
|
for (pugi::xml_node subNode : node.children()) {
|
||||||
break;
|
// Collect node attributes first
|
||||||
|
if (!ASSIMP_stricmp(subNode.name(), "attributes")) {
|
||||||
|
ParseNodeAttributes(subNode, nd, batch); // Parse attributes into this node
|
||||||
|
} else if (!ASSIMP_stricmp(subNode.name(), "animators")) {
|
||||||
|
// Then parse any animators
|
||||||
|
// All animators should contain an <attributes> tag
|
||||||
|
|
||||||
|
// This is an animation path - add a new animator
|
||||||
|
// to the list.
|
||||||
|
ParseAnimators(subNode, nd); // Function modifies nd's animator vector
|
||||||
|
guessedAnimCnt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then parse any materials
|
||||||
|
// Materials are available to almost all node types
|
||||||
|
if (nd->type != Node::DUMMY) {
|
||||||
|
if (!ASSIMP_stricmp(subNode.name(), "materials")) {
|
||||||
|
// Parse material description directly
|
||||||
|
// Each material should contain an <attributes> node
|
||||||
|
// with everything specified
|
||||||
|
nd->materials.emplace_back();
|
||||||
|
std::pair<aiMaterial *, unsigned int> &p = nd->materials.back();
|
||||||
|
p.first = ParseMaterial(subNode, p.second);
|
||||||
|
guessedMatCnt += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then parse any child nodes
|
||||||
|
// Attach the newly created node to the scene-graph
|
||||||
|
for (pugi::xml_node child : node.children()) {
|
||||||
|
if (!ASSIMP_stricmp(child.name(), "node")) { // Is a child node
|
||||||
|
Node *childNd = ParseNode(child, batch); // Repeat this function for all children
|
||||||
|
nd->children.push_back(childNd);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return fully specified node
|
||||||
|
return nd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Imports the given file into the given scene structure.
|
||||||
|
void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
||||||
|
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
|
||||||
|
// Check whether we can read from the file
|
||||||
|
if (file == nullptr) {
|
||||||
|
throw DeadlyImportError("Failed to open IRR file ", pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the irrXML parser
|
||||||
|
XmlParser st;
|
||||||
|
if (!st.parse(file.get())) {
|
||||||
|
throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
|
||||||
|
}
|
||||||
|
pugi::xml_node documentRoot = st.getRootNode();
|
||||||
|
|
||||||
|
// The root node of the scene
|
||||||
|
Node *root = new Node(Node::DUMMY);
|
||||||
|
root->parent = nullptr;
|
||||||
|
root->name = "<IRRSceneRoot>";
|
||||||
|
|
||||||
|
// Batch loader used to load external models
|
||||||
|
BatchLoader batch(pIOHandler);
|
||||||
|
// batch.SetBasePath(pFile);
|
||||||
|
|
||||||
|
cameras.reserve(1); // Probably only one camera in entire scene
|
||||||
|
lights.reserve(5);
|
||||||
|
|
||||||
|
this->guessedAnimCnt = 0;
|
||||||
|
this->guessedMeshCnt = 0;
|
||||||
|
this->guessedMatCnt = 0;
|
||||||
|
|
||||||
|
// Parse the XML
|
||||||
|
// Find the scene root from document root.
|
||||||
|
const pugi::xml_node &sceneRoot = documentRoot.child("irr_scene");
|
||||||
|
if (!sceneRoot) throw new DeadlyImportError("IRR: <irr_scene> not found in file");
|
||||||
|
for (pugi::xml_node &child : sceneRoot.children()) {
|
||||||
|
// XML elements are either nodes, animators, attributes, or materials
|
||||||
|
if (!ASSIMP_stricmp(child.name(), "node")) {
|
||||||
|
// Recursive collect subtree children
|
||||||
|
Node *nd = ParseNode(child, batch);
|
||||||
|
// Attach to root
|
||||||
|
root->children.push_back(nd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
// Now iterate through all cameras and compute their final (horizontal) FOV
|
// Now iterate through all cameras and compute their final (horizontal) FOV
|
||||||
for (aiCamera *cam : cameras) {
|
for (aiCamera *cam : cameras) {
|
||||||
|
@ -1337,8 +1329,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
// Now merge all sub scenes and attach them to the correct
|
// Now merge all sub scenes and attach them to the correct
|
||||||
// attachment points in the scenegraph.
|
// attachment points in the scenegraph.
|
||||||
SceneCombiner::MergeScenes(&pScene, tempScene, attach,
|
SceneCombiner::MergeScenes(&pScene, tempScene, attach,
|
||||||
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
|
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
|
||||||
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
|
|
||||||
0));
|
0));
|
||||||
|
|
||||||
// If we have no meshes | no materials now set the INCOMPLETE
|
// If we have no meshes | no materials now set the INCOMPLETE
|
||||||
|
|
|
@ -71,13 +71,13 @@ public:
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details.
|
* See BaseImporter::CanRead() for details.
|
||||||
*/
|
*/
|
||||||
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
|
bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
|
||||||
bool checkSig) const override;
|
bool checkSig) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const aiImporterDesc* GetInfo () const override;
|
const aiImporterDesc *GetInfo() const override;
|
||||||
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
|
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
|
||||||
void SetupProperties(const Importer* pImp) override;
|
void SetupProperties(const Importer *pImp) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Data structure for a scene-graph node animator
|
/** Data structure for a scene-graph node animator
|
||||||
|
@ -94,18 +94,10 @@ private:
|
||||||
|
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
explicit Animator(AT t = UNKNOWN)
|
explicit Animator(AT t = UNKNOWN) :
|
||||||
: type (t)
|
type(t), speed(ai_real(0.001)), direction(ai_real(0.0), ai_real(1.0), ai_real(0.0)), circleRadius(ai_real(1.0)), tightness(ai_real(0.5)), loop(true), timeForWay(100) {
|
||||||
, speed ( ai_real( 0.001 ) )
|
|
||||||
, direction ( ai_real( 0.0 ), ai_real( 1.0 ), ai_real( 0.0 ) )
|
|
||||||
, circleRadius ( ai_real( 1.0) )
|
|
||||||
, tightness ( ai_real( 0.5 ) )
|
|
||||||
, loop (true)
|
|
||||||
, timeForWay (100)
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// common parameters
|
// common parameters
|
||||||
ai_real speed;
|
ai_real speed;
|
||||||
aiVector3D direction;
|
aiVector3D direction;
|
||||||
|
@ -128,11 +120,9 @@ private:
|
||||||
|
|
||||||
/** Data structure for a scene-graph node in an IRR file
|
/** Data structure for a scene-graph node in an IRR file
|
||||||
*/
|
*/
|
||||||
struct Node
|
struct Node {
|
||||||
{
|
|
||||||
// Type of the node
|
// Type of the node
|
||||||
enum ET
|
enum ET {
|
||||||
{
|
|
||||||
LIGHT,
|
LIGHT,
|
||||||
CUBE,
|
CUBE,
|
||||||
MESH,
|
MESH,
|
||||||
|
@ -144,21 +134,20 @@ private:
|
||||||
ANIMMESH
|
ANIMMESH
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
explicit Node(ET t)
|
explicit Node(ET t) :
|
||||||
: type (t)
|
type(t), scaling(1.0, 1.0, 1.0) // assume uniform scaling by default
|
||||||
, scaling (1.0,1.0,1.0) // assume uniform scaling by default
|
,
|
||||||
, parent()
|
parent(),
|
||||||
, framesPerSecond (0.0)
|
framesPerSecond(0.0),
|
||||||
, id()
|
id(),
|
||||||
, sphereRadius (1.0)
|
sphereRadius(1.0),
|
||||||
, spherePolyCountX (100)
|
spherePolyCountX(100),
|
||||||
, spherePolyCountY (100)
|
spherePolyCountY(100) {
|
||||||
{
|
|
||||||
|
|
||||||
// Generate a default name for the node
|
// Generate a default name for the node
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
static int cnt;
|
static int cnt;
|
||||||
ai_snprintf(buffer, 128, "IrrNode_%i",cnt++);
|
ai_snprintf(buffer, 128, "IrrNode_%i", cnt++);
|
||||||
name = std::string(buffer);
|
name = std::string(buffer);
|
||||||
|
|
||||||
// reserve space for up to 5 materials
|
// reserve space for up to 5 materials
|
||||||
|
@ -175,10 +164,10 @@ private:
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
// List of all child nodes
|
// List of all child nodes
|
||||||
std::vector<Node*> children;
|
std::vector<Node *> children;
|
||||||
|
|
||||||
// Parent node
|
// Parent node
|
||||||
Node* parent;
|
Node *parent;
|
||||||
|
|
||||||
// Animated meshes: frames per second
|
// Animated meshes: frames per second
|
||||||
// 0.f if not specified
|
// 0.f if not specified
|
||||||
|
@ -190,13 +179,13 @@ private:
|
||||||
|
|
||||||
// Meshes: List of materials to be assigned
|
// Meshes: List of materials to be assigned
|
||||||
// along with their corresponding material flags
|
// along with their corresponding material flags
|
||||||
std::vector< std::pair<aiMaterial*, unsigned int> > materials;
|
std::vector<std::pair<aiMaterial *, unsigned int>> materials;
|
||||||
|
|
||||||
// Spheres: radius of the sphere to be generates
|
// Spheres: radius of the sphere to be generates
|
||||||
ai_real sphereRadius;
|
ai_real sphereRadius;
|
||||||
|
|
||||||
// Spheres: Number of polygons in the x,y direction
|
// Spheres: Number of polygons in the x,y direction
|
||||||
unsigned int spherePolyCountX,spherePolyCountY;
|
unsigned int spherePolyCountX, spherePolyCountY;
|
||||||
|
|
||||||
// List of all animators assigned to the node
|
// List of all animators assigned to the node
|
||||||
std::list<Animator> animators;
|
std::list<Animator> animators;
|
||||||
|
@ -204,8 +193,7 @@ private:
|
||||||
|
|
||||||
/** Data structure for a vertex in an IRR skybox
|
/** Data structure for a vertex in an IRR skybox
|
||||||
*/
|
*/
|
||||||
struct SkyboxVertex
|
struct SkyboxVertex {
|
||||||
{
|
|
||||||
SkyboxVertex() = default;
|
SkyboxVertex() = default;
|
||||||
|
|
||||||
//! Construction from single vertex components
|
//! Construction from single vertex components
|
||||||
|
@ -213,31 +201,46 @@ private:
|
||||||
ai_real nx, ai_real ny, ai_real nz,
|
ai_real nx, ai_real ny, ai_real nz,
|
||||||
ai_real uvx, ai_real uvy)
|
ai_real uvx, ai_real uvy)
|
||||||
|
|
||||||
: position (px,py,pz)
|
:
|
||||||
, normal (nx,ny,nz)
|
position(px, py, pz), normal(nx, ny, nz), uv(uvx, uvy, 0.0) {}
|
||||||
, uv (uvx,uvy,0.0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
aiVector3D position, normal, uv;
|
aiVector3D position, normal, uv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Parse <node> tag from XML file and extract child node
|
||||||
|
// @param node XML node
|
||||||
|
// @param guessedMeshesContained number of extra guessed meshes
|
||||||
|
IRRImporter::Node *ParseNode(pugi::xml_node &node, BatchLoader& batch);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Parse <attributes> tags within <node> tags and apply to scene node
|
||||||
|
// @param attributeNode XML child node
|
||||||
|
// @param nd Attributed scene node
|
||||||
|
void ParseNodeAttributes(pugi::xml_node &attributeNode, IRRImporter::Node *nd, BatchLoader& batch);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Parse an <animator> node and attach an animator to a node
|
||||||
|
// @param animatorNode XML animator node
|
||||||
|
// @param nd Animated scene node
|
||||||
|
void ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/// Fill the scene-graph recursively
|
/// Fill the scene-graph recursively
|
||||||
void GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
|
void GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
|
||||||
BatchLoader& batch,
|
BatchLoader &batch,
|
||||||
std::vector<aiMesh*>& meshes,
|
std::vector<aiMesh *> &meshes,
|
||||||
std::vector<aiNodeAnim*>& anims,
|
std::vector<aiNodeAnim *> &anims,
|
||||||
std::vector<AttachmentInfo>& attach,
|
std::vector<AttachmentInfo> &attach,
|
||||||
std::vector<aiMaterial*>& materials,
|
std::vector<aiMaterial *> &materials,
|
||||||
unsigned int& defaultMatIdx);
|
unsigned int &defaultMatIdx);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/// Generate a mesh that consists of just a single quad
|
/// Generate a mesh that consists of just a single quad
|
||||||
aiMesh* BuildSingleQuadMesh(const SkyboxVertex& v1,
|
aiMesh *BuildSingleQuadMesh(const SkyboxVertex &v1,
|
||||||
const SkyboxVertex& v2,
|
const SkyboxVertex &v2,
|
||||||
const SkyboxVertex& v3,
|
const SkyboxVertex &v3,
|
||||||
const SkyboxVertex& v4);
|
const SkyboxVertex &v4);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/// Build a sky-box
|
/// Build a sky-box
|
||||||
|
@ -245,8 +248,8 @@ private:
|
||||||
/// @param meshes Receives 6 output meshes
|
/// @param meshes Receives 6 output meshes
|
||||||
/// @param materials The last 6 materials are assigned to the newly
|
/// @param materials The last 6 materials are assigned to the newly
|
||||||
/// created meshes. The names of the materials are adjusted.
|
/// created meshes. The names of the materials are adjusted.
|
||||||
void BuildSkybox(std::vector<aiMesh*>& meshes,
|
void BuildSkybox(std::vector<aiMesh *> &meshes,
|
||||||
std::vector<aiMaterial*> materials);
|
std::vector<aiMaterial *> materials);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Copy a material for a mesh to the output material list
|
/** Copy a material for a mesh to the output material list
|
||||||
|
@ -256,10 +259,10 @@ private:
|
||||||
* @param defMatIdx Default material index - UINT_MAX if not present
|
* @param defMatIdx Default material index - UINT_MAX if not present
|
||||||
* @param mesh Mesh to work on
|
* @param mesh Mesh to work on
|
||||||
*/
|
*/
|
||||||
void CopyMaterial(std::vector<aiMaterial*>& materials,
|
void CopyMaterial(std::vector<aiMaterial *> &materials,
|
||||||
std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
|
std::vector<std::pair<aiMaterial *, unsigned int>> &inmaterials,
|
||||||
unsigned int& defMatIdx,
|
unsigned int &defMatIdx,
|
||||||
aiMesh* mesh);
|
aiMesh *mesh);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Compute animations for a specific node
|
/** Compute animations for a specific node
|
||||||
|
@ -267,8 +270,8 @@ private:
|
||||||
* @param root Node to be processed
|
* @param root Node to be processed
|
||||||
* @param anims The list of output animations
|
* @param anims The list of output animations
|
||||||
*/
|
*/
|
||||||
void ComputeAnimations(Node* root, aiNode* real,
|
void ComputeAnimations(Node *root, aiNode *real,
|
||||||
std::vector<aiNodeAnim*>& anims);
|
std::vector<aiNodeAnim *> &anims);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Configuration option: desired output FPS
|
/// Configuration option: desired output FPS
|
||||||
|
@ -276,6 +279,12 @@ private:
|
||||||
|
|
||||||
/// Configuration option: speed flag was set?
|
/// Configuration option: speed flag was set?
|
||||||
bool configSpeedFlag;
|
bool configSpeedFlag;
|
||||||
|
|
||||||
|
std::vector<aiCamera*> cameras;
|
||||||
|
std::vector<aiLight*> lights;
|
||||||
|
unsigned int guessedMeshCnt;
|
||||||
|
unsigned int guessedMatCnt;
|
||||||
|
unsigned int guessedAnimCnt;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -121,7 +121,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
// Construct the irrXML parser
|
// Construct the irrXML parser
|
||||||
XmlParser parser;
|
XmlParser parser;
|
||||||
if (!parser.parse( file.get() )) {
|
if (!parser.parse(file.get())) {
|
||||||
throw DeadlyImportError("XML parse error while loading IRRMESH file ", pFile);
|
throw DeadlyImportError("XML parse error while loading IRRMESH file ", pFile);
|
||||||
}
|
}
|
||||||
XmlNode root = parser.getRootNode();
|
XmlNode root = parser.getRootNode();
|
||||||
|
@ -133,6 +133,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
meshes.reserve(5);
|
meshes.reserve(5);
|
||||||
|
|
||||||
// temporary data - current mesh buffer
|
// temporary data - current mesh buffer
|
||||||
|
// TODO move all these to inside loop
|
||||||
aiMaterial *curMat = nullptr;
|
aiMaterial *curMat = nullptr;
|
||||||
aiMesh *curMesh = nullptr;
|
aiMesh *curMesh = nullptr;
|
||||||
unsigned int curMatFlags = 0;
|
unsigned int curMatFlags = 0;
|
||||||
|
@ -142,23 +143,28 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
std::vector<aiVector3D> curUVs, curUV2s;
|
std::vector<aiVector3D> curUVs, curUV2s;
|
||||||
|
|
||||||
// some temporary variables
|
// some temporary variables
|
||||||
int textMeaning = 0;
|
// textMeaning is a 15 year old variable, that could've been an enum
|
||||||
int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
|
// int textMeaning = 0; // 0=none? 1=vertices 2=indices
|
||||||
|
// int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
|
||||||
bool useColors = false;
|
bool useColors = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** irrmesh files have a top level <mesh> owning multiple <buffer> nodes.
|
||||||
|
** Each <buffer> contains <material>, <vertices>, and <indices>
|
||||||
|
** <material> tags here directly owns the material data specs
|
||||||
|
** <vertices> are a vertex per line, contains position, UV1 coords, maybe UV2, normal, tangent, bitangent
|
||||||
|
** <boundingbox> is ignored, I think assimp recalculates those?
|
||||||
|
*/
|
||||||
|
|
||||||
// Parse the XML file
|
// Parse the XML file
|
||||||
for (pugi::xml_node child : root.children()) {
|
pugi::xml_node const &meshNode = root.child("mesh");
|
||||||
if (child.type() == pugi::node_element) {
|
for (pugi::xml_node bufferNode : meshNode.children()) {
|
||||||
if (!ASSIMP_stricmp(child.name(), "buffer") && (curMat || curMesh)) {
|
if (ASSIMP_stricmp(bufferNode.name(), "buffer")) {
|
||||||
// end of previous buffer. A material and a mesh should be there
|
// Might be a useless warning
|
||||||
if (!curMat || !curMesh) {
|
ASSIMP_LOG_WARN("IRRMESH: Ignoring non buffer node <", bufferNode.name(), "> in mesh declaration");
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
|
continue;
|
||||||
releaseMaterial(&curMat);
|
|
||||||
releaseMesh(&curMesh);
|
|
||||||
} else {
|
|
||||||
materials.push_back(curMat);
|
|
||||||
meshes.push_back(curMesh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curMat = nullptr;
|
curMat = nullptr;
|
||||||
curMesh = nullptr;
|
curMesh = nullptr;
|
||||||
|
|
||||||
|
@ -169,42 +175,47 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
curUVs.clear();
|
curUVs.clear();
|
||||||
curTangents.clear();
|
curTangents.clear();
|
||||||
curBitangents.clear();
|
curBitangents.clear();
|
||||||
}
|
|
||||||
|
|
||||||
if (!ASSIMP_stricmp(child.name(), "material")) {
|
// TODO ensure all three nodes are present and populated
|
||||||
if (curMat) {
|
// before allocating everything
|
||||||
|
|
||||||
|
// Get first material node
|
||||||
|
pugi::xml_node materialNode = bufferNode.child("material");
|
||||||
|
if (materialNode) {
|
||||||
|
curMat = ParseMaterial(materialNode, curMatFlags);
|
||||||
|
// Warn if there's more materials
|
||||||
|
if (materialNode.next_sibling("material")) {
|
||||||
ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
|
ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
|
||||||
releaseMaterial(&curMat);
|
|
||||||
}
|
}
|
||||||
curMat = ParseMaterial(curMatFlags);
|
} else {
|
||||||
}
|
ASSIMP_LOG_ERROR("IRRMESH: Buffer must contain one material");
|
||||||
/* no else here! */ if (!ASSIMP_stricmp(child.name(), "vertices")) {
|
|
||||||
pugi::xml_attribute attr = child.attribute("vertexCount");
|
|
||||||
int num = attr.as_int();
|
|
||||||
//int num = reader->getAttributeValueAsInt("vertexCount");
|
|
||||||
|
|
||||||
if (!num) {
|
|
||||||
// This is possible ... remove the mesh from the list and skip further reading
|
|
||||||
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
|
|
||||||
|
|
||||||
releaseMaterial(&curMat);
|
|
||||||
releaseMesh(&curMesh);
|
|
||||||
textMeaning = 0;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
curVertices.reserve(num);
|
// Get first vertices node
|
||||||
curNormals.reserve(num);
|
pugi::xml_node verticesNode = bufferNode.child("vertices");
|
||||||
curColors.reserve(num);
|
if (verticesNode) {
|
||||||
curUVs.reserve(num);
|
pugi::xml_attribute vertexCountAttrib = verticesNode.attribute("vertexCount");
|
||||||
|
int vertexCount = vertexCountAttrib.as_int();
|
||||||
|
if (vertexCount == 0) {
|
||||||
|
// This is possible ... remove the mesh from the list and skip further reading
|
||||||
|
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
|
||||||
|
releaseMaterial(&curMat);
|
||||||
|
// releaseMesh(&curMesh);
|
||||||
|
continue; // Bail out early
|
||||||
|
};
|
||||||
|
|
||||||
|
curVertices.reserve(vertexCount);
|
||||||
|
curNormals.reserve(vertexCount);
|
||||||
|
curColors.reserve(vertexCount);
|
||||||
|
curUVs.reserve(vertexCount);
|
||||||
|
|
||||||
|
VertexFormat vertexFormat;
|
||||||
// Determine the file format
|
// Determine the file format
|
||||||
//const char *t = reader->getAttributeValueSafe("type");
|
pugi::xml_attribute typeAttrib = verticesNode.attribute("type");
|
||||||
pugi::xml_attribute t = child.attribute("type");
|
if (!ASSIMP_stricmp("2tcoords", typeAttrib.value())) {
|
||||||
if (!ASSIMP_stricmp("2tcoords", t.name())) {
|
curUV2s.reserve(vertexCount);
|
||||||
curUV2s.reserve(num);
|
vertexFormat = VertexFormat::t2coord;
|
||||||
vertexFormat = 1;
|
|
||||||
|
|
||||||
if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
|
if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
|
||||||
// *********************************************************
|
// *********************************************************
|
||||||
// We have a second texture! So use this UV channel
|
// We have a second texture! So use this UV channel
|
||||||
|
@ -223,29 +234,38 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1));
|
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!ASSIMP_stricmp("tangents", t.name())) {
|
} else if (!ASSIMP_stricmp("tangents", typeAttrib.value())) {
|
||||||
curTangents.reserve(num);
|
curTangents.reserve(vertexCount);
|
||||||
curBitangents.reserve(num);
|
curBitangents.reserve(vertexCount);
|
||||||
vertexFormat = 2;
|
vertexFormat = VertexFormat::tangent;
|
||||||
} else if (ASSIMP_stricmp("standard", t.name())) {
|
} else if (!ASSIMP_stricmp("standard", typeAttrib.value())) {
|
||||||
|
vertexFormat = VertexFormat::standard;
|
||||||
|
} else {
|
||||||
|
// Unsupported format, discard whole buffer/mesh
|
||||||
|
// Assuming we have a correct material, then release it
|
||||||
|
// We don't have a correct mesh for sure here
|
||||||
releaseMaterial(&curMat);
|
releaseMaterial(&curMat);
|
||||||
ASSIMP_LOG_WARN("IRRMESH: Unknown vertex format");
|
ASSIMP_LOG_ERROR("IRRMESH: Unknown vertex format");
|
||||||
} else
|
continue; // Skip rest of buffer
|
||||||
vertexFormat = 0;
|
};
|
||||||
textMeaning = 1;
|
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "indices")) {
|
// We know what format buffer is, collect numbers
|
||||||
if (curVertices.empty() && curMat) {
|
ParseBufferVertices(verticesNode.text().get(), vertexFormat,
|
||||||
releaseMaterial(&curMat);
|
curVertices, curNormals,
|
||||||
throw DeadlyImportError("IRRMESH: indices must come after vertices");
|
curTangents, curBitangents,
|
||||||
|
curUVs, curUV2s, curColors, useColors);
|
||||||
}
|
}
|
||||||
|
|
||||||
textMeaning = 2;
|
// Get indices
|
||||||
|
// At this point we have some vertices and a valid material
|
||||||
|
// Collect indices and create aiMesh at the same time
|
||||||
|
pugi::xml_node indicesNode = bufferNode.child("indices");
|
||||||
|
if (indicesNode) {
|
||||||
// start a new mesh
|
// start a new mesh
|
||||||
curMesh = new aiMesh();
|
curMesh = new aiMesh();
|
||||||
|
|
||||||
// allocate storage for all faces
|
// allocate storage for all faces
|
||||||
pugi::xml_attribute attr = child.attribute("indexCount");
|
pugi::xml_attribute attr = indicesNode.attribute("indexCount");
|
||||||
curMesh->mNumVertices = attr.as_int();
|
curMesh->mNumVertices = attr.as_int();
|
||||||
if (!curMesh->mNumVertices) {
|
if (!curMesh->mNumVertices) {
|
||||||
// This is possible ... remove the mesh from the list and skip further reading
|
// This is possible ... remove the mesh from the list and skip further reading
|
||||||
|
@ -256,9 +276,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
// material - away
|
// material - away
|
||||||
releaseMaterial(&curMat);
|
releaseMaterial(&curMat);
|
||||||
|
continue; // Go to next buffer
|
||||||
textMeaning = 0;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curMesh->mNumVertices % 3) {
|
if (curMesh->mNumVertices % 3) {
|
||||||
|
@ -293,107 +311,6 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
if (curUV2s.size() == curVertices.size()) {
|
if (curUV2s.size() == curVertices.size()) {
|
||||||
curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
|
curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
//break;
|
|
||||||
|
|
||||||
//case EXN_TEXT: {
|
|
||||||
const char *sz = child.child_value();
|
|
||||||
if (textMeaning == 1) {
|
|
||||||
textMeaning = 0;
|
|
||||||
|
|
||||||
// read vertices
|
|
||||||
do {
|
|
||||||
SkipSpacesAndLineEnd(&sz);
|
|
||||||
aiVector3D temp;
|
|
||||||
aiColor4D c;
|
|
||||||
|
|
||||||
// Read the vertex position
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
curVertices.push_back(temp);
|
|
||||||
|
|
||||||
// Read the vertex normals
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
curNormals.push_back(temp);
|
|
||||||
|
|
||||||
// read the vertex colors
|
|
||||||
uint32_t clr = strtoul16(sz, &sz);
|
|
||||||
ColorFromARGBPacked(clr, c);
|
|
||||||
|
|
||||||
if (!curColors.empty() && c != *(curColors.end() - 1))
|
|
||||||
useColors = true;
|
|
||||||
|
|
||||||
curColors.push_back(c);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
// read the first UV coordinate set
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
temp.z = 0.f;
|
|
||||||
temp.y = 1.f - temp.y; // DX to OGL
|
|
||||||
curUVs.push_back(temp);
|
|
||||||
|
|
||||||
// read the (optional) second UV coordinate set
|
|
||||||
if (vertexFormat == 1) {
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
temp.y = 1.f - temp.y; // DX to OGL
|
|
||||||
curUV2s.push_back(temp);
|
|
||||||
}
|
|
||||||
// read optional tangent and bitangent vectors
|
|
||||||
else if (vertexFormat == 2) {
|
|
||||||
// tangents
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
temp.y *= -1.0f;
|
|
||||||
curTangents.push_back(temp);
|
|
||||||
|
|
||||||
// bitangents
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
temp.y *= -1.0f;
|
|
||||||
curBitangents.push_back(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IMPORTANT: We assume that each vertex is specified in one
|
|
||||||
line. So we can skip the rest of the line - unknown vertex
|
|
||||||
elements are ignored.
|
|
||||||
*/
|
|
||||||
|
|
||||||
while (SkipLine(&sz));
|
|
||||||
} else if (textMeaning == 2) {
|
|
||||||
textMeaning = 0;
|
|
||||||
|
|
||||||
// read indices
|
// read indices
|
||||||
aiFace *curFace = curMesh->mFaces;
|
aiFace *curFace = curMesh->mFaces;
|
||||||
|
@ -409,24 +326,33 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
unsigned int curIdx = 0;
|
unsigned int curIdx = 0;
|
||||||
unsigned int total = 0;
|
unsigned int total = 0;
|
||||||
|
|
||||||
|
// NOTE this might explode for UTF-16 and wchars
|
||||||
|
const char *sz = indicesNode.text().get();
|
||||||
|
// For each index loop over aiMesh faces
|
||||||
while (SkipSpacesAndLineEnd(&sz)) {
|
while (SkipSpacesAndLineEnd(&sz)) {
|
||||||
if (curFace >= faceEnd) {
|
if (curFace >= faceEnd) {
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
|
ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// if new face
|
||||||
if (!curIdx) {
|
if (!curIdx) {
|
||||||
curFace->mNumIndices = 3;
|
curFace->mNumIndices = 3;
|
||||||
curFace->mIndices = new unsigned int[3];
|
curFace->mIndices = new unsigned int[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read index base 10
|
||||||
|
// function advances the pointer
|
||||||
unsigned int idx = strtoul10(sz, &sz);
|
unsigned int idx = strtoul10(sz, &sz);
|
||||||
if (idx >= curVertices.size()) {
|
if (idx >= curVertices.size()) {
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
|
ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
|
||||||
idx = 0;
|
idx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make up our own indices?
|
||||||
curFace->mIndices[curIdx] = total++;
|
curFace->mIndices[curIdx] = total++;
|
||||||
|
|
||||||
|
// Copy over data to aiMesh
|
||||||
*pcV++ = curVertices[idx];
|
*pcV++ = curVertices[idx];
|
||||||
if (pcN) *pcN++ = curNormals[idx];
|
if (pcN) *pcN++ = curNormals[idx];
|
||||||
if (pcT) *pcT++ = curTangents[idx];
|
if (pcT) *pcT++ = curTangents[idx];
|
||||||
|
@ -435,14 +361,16 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
if (pcT0) *pcT0++ = curUVs[idx];
|
if (pcT0) *pcT0++ = curUVs[idx];
|
||||||
if (pcT1) *pcT1++ = curUV2s[idx];
|
if (pcT1) *pcT1++ = curUV2s[idx];
|
||||||
|
|
||||||
|
// start new face
|
||||||
if (++curIdx == 3) {
|
if (++curIdx == 3) {
|
||||||
++curFace;
|
++curFace;
|
||||||
curIdx = 0;
|
curIdx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// We should be at the end of mFaces
|
||||||
if (curFace != faceEnd)
|
if (curFace != faceEnd)
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
|
ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
|
||||||
|
}
|
||||||
|
|
||||||
// Finish processing the mesh - do some small material workarounds
|
// Finish processing the mesh - do some small material workarounds
|
||||||
if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
|
if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
|
||||||
|
@ -451,12 +379,9 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
aiMaterial *mat = (aiMaterial *)curMat;
|
aiMaterial *mat = (aiMaterial *)curMat;
|
||||||
mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
|
mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
|
||||||
}
|
}
|
||||||
}
|
// textMeaning = 2;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of the last buffer. A material and a mesh should be there
|
// end of previous buffer. A material and a mesh should be there
|
||||||
if (curMat || curMesh) {
|
|
||||||
if (!curMat || !curMesh) {
|
if (!curMat || !curMesh) {
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
|
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
|
||||||
releaseMaterial(&curMat);
|
releaseMaterial(&curMat);
|
||||||
|
@ -467,7 +392,8 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (materials.empty()) {
|
// If one is empty then so is the other
|
||||||
|
if (materials.empty() || meshes.empty()) {
|
||||||
throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
|
throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +418,105 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
|
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
|
||||||
pScene->mRootNode->mMeshes[i] = i;
|
pScene->mRootNode->mMeshes[i] = i;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
|
||||||
|
std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
|
||||||
|
std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
|
||||||
|
std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,
|
||||||
|
std::vector<aiColor4D> &colors, bool &useColors) {
|
||||||
|
// read vertices
|
||||||
|
do {
|
||||||
|
SkipSpacesAndLineEnd(&sz);
|
||||||
|
aiVector3D temp;
|
||||||
|
aiColor4D c;
|
||||||
|
|
||||||
|
// Read the vertex position
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
vertices.push_back(temp);
|
||||||
|
|
||||||
|
// Read the vertex normals
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
normals.push_back(temp);
|
||||||
|
|
||||||
|
// read the vertex colors
|
||||||
|
uint32_t clr = strtoul16(sz, &sz);
|
||||||
|
ColorFromARGBPacked(clr, c);
|
||||||
|
|
||||||
|
// If we're pushing more than one distinct color
|
||||||
|
if (!colors.empty() && c != *(colors.end() - 1))
|
||||||
|
useColors = true;
|
||||||
|
|
||||||
|
colors.push_back(c);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
// read the first UV coordinate set
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
temp.z = 0.f;
|
||||||
|
temp.y = 1.f - temp.y; // DX to OGL
|
||||||
|
UVs.push_back(temp);
|
||||||
|
|
||||||
|
// NOTE these correspond to specific S3DVertex* structs in irr sourcecode
|
||||||
|
// So by definition, all buffers have either UV2 or tangents or neither
|
||||||
|
// read the (optional) second UV coordinate set
|
||||||
|
if (vertexFormat == VertexFormat::t2coord) {
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
temp.y = 1.f - temp.y; // DX to OGL
|
||||||
|
UV2s.push_back(temp);
|
||||||
}
|
}
|
||||||
|
// read optional tangent and bitangent vectors
|
||||||
|
else if (vertexFormat == VertexFormat::tangent) {
|
||||||
|
// tangents
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
temp.y *= -1.0f;
|
||||||
|
tangents.push_back(temp);
|
||||||
|
|
||||||
|
// bitangents
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
temp.y *= -1.0f;
|
||||||
|
bitangents.push_back(temp);
|
||||||
|
}
|
||||||
|
} while (SkipLine(&sz));
|
||||||
|
/* IMPORTANT: We assume that each vertex is specified in one
|
||||||
|
line. So we can skip the rest of the line - unknown vertex
|
||||||
|
elements are ignored.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER
|
#endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER
|
||||||
|
|
|
@ -85,6 +85,19 @@ protected:
|
||||||
*/
|
*/
|
||||||
void InternReadFile(const std::string &pFile, aiScene *pScene,
|
void InternReadFile(const std::string &pFile, aiScene *pScene,
|
||||||
IOSystem *pIOHandler) override;
|
IOSystem *pIOHandler) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class VertexFormat {
|
||||||
|
standard = 0, // "standard" - also noted as 'normal' format elsewhere
|
||||||
|
t2coord = 1, // "2tcoord" - standard + 2 UV maps
|
||||||
|
tangent = 2, // "tangents" - standard + tangents and bitangents
|
||||||
|
};
|
||||||
|
|
||||||
|
void ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
|
||||||
|
std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
|
||||||
|
std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
|
||||||
|
std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,
|
||||||
|
std::vector<aiColor4D> &colors, bool &useColors);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -43,19 +43,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
* @brief Shared utilities for the IRR and IRRMESH loaders
|
* @brief Shared utilities for the IRR and IRRMESH loaders
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//This section should be excluded only if both the Irrlicht AND the Irrlicht Mesh importers were omitted.
|
// This section should be excluded only if both the Irrlicht AND the Irrlicht Mesh importers were omitted.
|
||||||
#if !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
|
#if !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
|
||||||
|
|
||||||
#include "IRRShared.h"
|
#include "IRRShared.h"
|
||||||
#include <assimp/ParsingUtils.h>
|
#include <assimp/ParsingUtils.h>
|
||||||
#include <assimp/fast_atof.h>
|
#include <assimp/fast_atof.h>
|
||||||
#include <assimp/DefaultLogger.hpp>
|
|
||||||
#include <assimp/material.h>
|
#include <assimp/material.h>
|
||||||
|
#include <assimp/DefaultLogger.hpp>
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
// Transformation matrix to convert from Assimp to IRR space
|
// Transformation matrix to convert from Assimp to IRR space
|
||||||
const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
|
const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4(
|
||||||
1.0f, 0.0f, 0.0f, 0.0f,
|
1.0f, 0.0f, 0.0f, 0.0f,
|
||||||
0.0f, 0.0f, 1.0f, 0.0f,
|
0.0f, 0.0f, 1.0f, 0.0f,
|
||||||
0.0f, 1.0f, 0.0f, 0.0f,
|
0.0f, 1.0f, 0.0f, 0.0f,
|
||||||
|
@ -63,34 +63,34 @@ const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a property in hexadecimal format (i.e. ffffffff)
|
// read a property in hexadecimal format (i.e. ffffffff)
|
||||||
void IrrlichtBase::ReadHexProperty(HexProperty &out ) {
|
void IrrlichtBase::ReadHexProperty(HexProperty &out, pugi::xml_node& hexnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : hexnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string( attrib.value() );
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(),"value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
// parse the hexadecimal value
|
// parse the hexadecimal value
|
||||||
out.value = strtoul16(attrib.name());
|
out.value = strtoul16(attrib.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a decimal property
|
// read a decimal property
|
||||||
void IrrlichtBase::ReadIntProperty(IntProperty & out) {
|
void IrrlichtBase::ReadIntProperty(IntProperty &out, pugi::xml_node& intnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : intnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.value(),"value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
// parse the int value
|
// parse the int value
|
||||||
out.value = strtol10(attrib.name());
|
out.value = strtol10(attrib.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a string property
|
// read a string property
|
||||||
void IrrlichtBase::ReadStringProperty( StringProperty& out) {
|
void IrrlichtBase::ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : stringnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
|
@ -102,9 +102,9 @@ void IrrlichtBase::ReadStringProperty( StringProperty& out) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a boolean property
|
// read a boolean property
|
||||||
void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
|
void IrrlichtBase::ReadBoolProperty(BoolProperty &out, pugi::xml_node& boolnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : boolnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")){
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
// true or false, case insensitive
|
// true or false, case insensitive
|
||||||
|
@ -115,8 +115,8 @@ void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a float property
|
// read a float property
|
||||||
void IrrlichtBase::ReadFloatProperty(FloatProperty &out) {
|
void IrrlichtBase::ReadFloatProperty(FloatProperty &out, pugi::xml_node &floatnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : floatnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
|
@ -128,8 +128,8 @@ void IrrlichtBase::ReadFloatProperty(FloatProperty &out) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a vector property
|
// read a vector property
|
||||||
void IrrlichtBase::ReadVectorProperty( VectorProperty &out ) {
|
void IrrlichtBase::ReadVectorProperty(VectorProperty &out, pugi::xml_node& vectornode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : vectornode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
|
@ -137,28 +137,28 @@ void IrrlichtBase::ReadVectorProperty( VectorProperty &out ) {
|
||||||
const char *ptr = attrib.value();
|
const char *ptr = attrib.value();
|
||||||
|
|
||||||
SkipSpaces(&ptr);
|
SkipSpaces(&ptr);
|
||||||
ptr = fast_atoreal_move<float>( ptr,(float&)out.value.x );
|
ptr = fast_atoreal_move<float>(ptr, (float &)out.value.x);
|
||||||
SkipSpaces(&ptr);
|
SkipSpaces(&ptr);
|
||||||
if (',' != *ptr) {
|
if (',' != *ptr) {
|
||||||
ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
|
ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
|
||||||
} else {
|
} else {
|
||||||
SkipSpaces(ptr + 1, &ptr);
|
SkipSpaces(ptr + 1, &ptr);
|
||||||
}
|
}
|
||||||
ptr = fast_atoreal_move<float>( ptr,(float&)out.value.y );
|
ptr = fast_atoreal_move<float>(ptr, (float &)out.value.y);
|
||||||
SkipSpaces(&ptr);
|
SkipSpaces(&ptr);
|
||||||
if (',' != *ptr) {
|
if (',' != *ptr) {
|
||||||
ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
|
ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
|
||||||
} else {
|
} else {
|
||||||
SkipSpaces(ptr + 1, &ptr);
|
SkipSpaces(ptr + 1, &ptr);
|
||||||
}
|
}
|
||||||
ptr = fast_atoreal_move<float>( ptr,(float&)out.value.z );
|
ptr = fast_atoreal_move<float>(ptr, (float &)out.value.z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Convert a string to a proper aiMappingMode
|
// Convert a string to a proper aiMappingMode
|
||||||
int ConvertMappingMode(const std::string& mode) {
|
int ConvertMappingMode(const std::string &mode) {
|
||||||
if (mode == "texture_clamp_repeat") {
|
if (mode == "texture_clamp_repeat") {
|
||||||
return aiTextureMapMode_Wrap;
|
return aiTextureMapMode_Wrap;
|
||||||
} else if (mode == "texture_clamp_mirror") {
|
} else if (mode == "texture_clamp_mirror") {
|
||||||
|
@ -170,8 +170,8 @@ int ConvertMappingMode(const std::string& mode) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Parse a material from the XML file
|
// Parse a material from the XML file
|
||||||
aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
aiMaterial *IrrlichtBase::ParseMaterial(pugi::xml_node& materialNode, unsigned int &matFlags) {
|
||||||
aiMaterial* mat = new aiMaterial();
|
aiMaterial *mat = new aiMaterial();
|
||||||
aiColor4D clr;
|
aiColor4D clr;
|
||||||
aiString s;
|
aiString s;
|
||||||
|
|
||||||
|
@ -179,10 +179,10 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
int cnt = 0; // number of used texture channels
|
int cnt = 0; // number of used texture channels
|
||||||
unsigned int nd = 0;
|
unsigned int nd = 0;
|
||||||
|
|
||||||
for (pugi::xml_node child : mNode->children()) {
|
for (pugi::xml_node child : materialNode.children()) {
|
||||||
if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties
|
if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties
|
||||||
HexProperty prop;
|
HexProperty prop;
|
||||||
ReadHexProperty(prop);
|
ReadHexProperty(prop, child);
|
||||||
if (prop.name == "Diffuse") {
|
if (prop.name == "Diffuse") {
|
||||||
ColorFromARGBPacked(prop.value, clr);
|
ColorFromARGBPacked(prop.value, clr);
|
||||||
mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
|
mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
|
@ -206,13 +206,13 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
#endif
|
#endif
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties
|
} else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties
|
||||||
FloatProperty prop;
|
FloatProperty prop;
|
||||||
ReadFloatProperty(prop);
|
ReadFloatProperty(prop, child);
|
||||||
if (prop.name == "Shininess") {
|
if (prop.name == "Shininess") {
|
||||||
mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS);
|
mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS);
|
||||||
}
|
}
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties
|
} else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties
|
||||||
BoolProperty prop;
|
BoolProperty prop;
|
||||||
ReadBoolProperty(prop);
|
ReadBoolProperty(prop, child);
|
||||||
if (prop.name == "Wireframe") {
|
if (prop.name == "Wireframe") {
|
||||||
int val = (prop.value ? true : false);
|
int val = (prop.value ? true : false);
|
||||||
mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME);
|
mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME);
|
||||||
|
@ -226,7 +226,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "texture") ||
|
} else if (!ASSIMP_stricmp(child.name(), "texture") ||
|
||||||
!ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties
|
!ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties
|
||||||
StringProperty prop;
|
StringProperty prop;
|
||||||
ReadStringProperty(prop);
|
ReadStringProperty(prop, child);
|
||||||
if (prop.value.length()) {
|
if (prop.value.length()) {
|
||||||
// material type (shader)
|
// material type (shader)
|
||||||
if (prop.name == "Type") {
|
if (prop.name == "Type") {
|
||||||
|
@ -337,7 +337,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//break;
|
// break;
|
||||||
/*case EXN_ELEMENT_END:
|
/*case EXN_ELEMENT_END:
|
||||||
|
|
||||||
// Assume there are no further nested nodes in <material> elements
|
// Assume there are no further nested nodes in <material> elements
|
||||||
|
@ -379,7 +379,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
|
//ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,7 @@ extern const aiMatrix4x4 AI_TO_IRR_MATRIX;
|
||||||
*/
|
*/
|
||||||
class IrrlichtBase {
|
class IrrlichtBase {
|
||||||
protected:
|
protected:
|
||||||
IrrlichtBase() :
|
IrrlichtBase() {
|
||||||
mNode(nullptr) {
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,25 +81,25 @@ protected:
|
||||||
|
|
||||||
/// XML reader instance
|
/// XML reader instance
|
||||||
XmlParser mParser;
|
XmlParser mParser;
|
||||||
pugi::xml_node *mNode;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Parse a material description from the XML
|
/** Parse a material description from the XML
|
||||||
* @return The created material
|
* @return The created material
|
||||||
* @param matFlags Receives AI_IRRMESH_MAT_XX flags
|
* @param matFlags Receives AI_IRRMESH_MAT_XX flags
|
||||||
*/
|
*/
|
||||||
aiMaterial *ParseMaterial(unsigned int &matFlags);
|
aiMaterial *ParseMaterial(pugi::xml_node &materialNode, unsigned int &matFlags);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Read a property of the specified type from the current XML element.
|
/** Read a property of the specified type from the current XML element.
|
||||||
* @param out Receives output data
|
* @param out Receives output data
|
||||||
|
* @param node XML attribute element containing data
|
||||||
*/
|
*/
|
||||||
void ReadHexProperty(HexProperty &out);
|
void ReadHexProperty(HexProperty &out, pugi::xml_node& hexnode);
|
||||||
void ReadStringProperty(StringProperty &out);
|
void ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode);
|
||||||
void ReadBoolProperty(BoolProperty &out);
|
void ReadBoolProperty(BoolProperty &out, pugi::xml_node& boolnode);
|
||||||
void ReadFloatProperty(FloatProperty &out);
|
void ReadFloatProperty(FloatProperty &out, pugi::xml_node& floatnode);
|
||||||
void ReadVectorProperty(VectorProperty &out);
|
void ReadVectorProperty(VectorProperty &out, pugi::xml_node& vectornode);
|
||||||
void ReadIntProperty(IntProperty &out);
|
void ReadIntProperty(IntProperty &out, pugi::xml_node& intnode);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -65,7 +65,6 @@ void LWOImporter::LoadLWOBFile()
|
||||||
if (mFileBuffer + head.length > end)
|
if (mFileBuffer + head.length > end)
|
||||||
{
|
{
|
||||||
throw DeadlyImportError("LWOB: Invalid chunk length");
|
throw DeadlyImportError("LWOB: Invalid chunk length");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
uint8_t* const next = mFileBuffer+head.length;
|
uint8_t* const next = mFileBuffer+head.length;
|
||||||
switch (head.type)
|
switch (head.type)
|
||||||
|
|
|
@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -51,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "AssetLib/LWO/LWOLoader.h"
|
#include "AssetLib/LWO/LWOLoader.h"
|
||||||
#include "PostProcessing/ConvertToLHProcess.h"
|
#include "PostProcessing/ConvertToLHProcess.h"
|
||||||
#include "PostProcessing/ProcessHelper.h"
|
#include "PostProcessing/ProcessHelper.h"
|
||||||
|
#include "Geometry/GeometryUtils.h"
|
||||||
|
|
||||||
#include <assimp/ByteSwapper.h>
|
#include <assimp/ByteSwapper.h>
|
||||||
#include <assimp/SGSpatialSort.h>
|
#include <assimp/SGSpatialSort.h>
|
||||||
|
@ -178,7 +177,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
|
||||||
mLayers->push_back(Layer());
|
mLayers->push_back(Layer());
|
||||||
mCurLayer = &mLayers->back();
|
mCurLayer = &mLayers->back();
|
||||||
mCurLayer->mName = "<LWODefault>";
|
mCurLayer->mName = "<LWODefault>";
|
||||||
mCurLayer->mIndex = (uint16_t) -1;
|
mCurLayer->mIndex = 1;
|
||||||
|
|
||||||
// old lightwave file format (prior to v6)
|
// old lightwave file format (prior to v6)
|
||||||
mIsLWO2 = false;
|
mIsLWO2 = false;
|
||||||
|
@ -398,14 +397,6 @@ void LWOImporter::InternReadFile(const std::string &pFile,
|
||||||
pvVC[w]++;
|
pvVC[w]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
// process vertex weights. We can't properly reconstruct the whole skeleton for now,
|
|
||||||
// but we can create dummy bones for all weight channels which we have.
|
|
||||||
for (unsigned int w = 0; w < layer.mWeightChannels.size();++w)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
face.mIndices[q] = vert;
|
face.mIndices[q] = vert;
|
||||||
}
|
}
|
||||||
pf->mIndices = face.mIndices;
|
pf->mIndices = face.mIndices;
|
||||||
|
@ -429,7 +420,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
|
||||||
// Generate nodes to render the mesh. Store the source layer in the mParent member of the nodes
|
// Generate nodes to render the mesh. Store the source layer in the mParent member of the nodes
|
||||||
unsigned int num = static_cast<unsigned int>(apcMeshes.size() - meshStart);
|
unsigned int num = static_cast<unsigned int>(apcMeshes.size() - meshStart);
|
||||||
if (layer.mName != "<LWODefault>" || num > 0) {
|
if (layer.mName != "<LWODefault>" || num > 0) {
|
||||||
aiNode *pcNode = new aiNode();
|
std::unique_ptr<aiNode> pcNode(new aiNode());
|
||||||
pcNode->mName.Set(layer.mName);
|
pcNode->mName.Set(layer.mName);
|
||||||
pcNode->mParent = (aiNode *)&layer;
|
pcNode->mParent = (aiNode *)&layer;
|
||||||
pcNode->mNumMeshes = num;
|
pcNode->mNumMeshes = num;
|
||||||
|
@ -439,7 +430,8 @@ void LWOImporter::InternReadFile(const std::string &pFile,
|
||||||
for (unsigned int p = 0; p < pcNode->mNumMeshes; ++p)
|
for (unsigned int p = 0; p < pcNode->mNumMeshes; ++p)
|
||||||
pcNode->mMeshes[p] = p + meshStart;
|
pcNode->mMeshes[p] = p + meshStart;
|
||||||
}
|
}
|
||||||
apcNodes[layer.mIndex] = pcNode;
|
ASSIMP_LOG_DEBUG("insert apcNode for layer ", layer.mIndex, " \"", layer.mName, "\"");
|
||||||
|
apcNodes[layer.mIndex] = pcNode.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +527,6 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
|
||||||
continue;
|
continue;
|
||||||
vNormals += v;
|
vNormals += v;
|
||||||
}
|
}
|
||||||
mesh->mNormals[idx] = vNormals.Normalize();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -556,7 +547,6 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
|
||||||
const aiVector3D &v = faceNormals[*a];
|
const aiVector3D &v = faceNormals[*a];
|
||||||
vNormals += v;
|
vNormals += v;
|
||||||
}
|
}
|
||||||
vNormals.Normalize();
|
|
||||||
for (std::vector<unsigned int>::const_iterator a = poResult.begin(); a != poResult.end(); ++a) {
|
for (std::vector<unsigned int>::const_iterator a = poResult.begin(); a != poResult.end(); ++a) {
|
||||||
mesh->mNormals[*a] = vNormals;
|
mesh->mNormals[*a] = vNormals;
|
||||||
vertexDone[*a] = true;
|
vertexDone[*a] = true;
|
||||||
|
@ -564,6 +554,7 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GeometryUtils::normalizeVectorArray(mesh->mNormals, mesh->mNormals, mesh->mNumVertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -572,7 +563,13 @@ void LWOImporter::GenerateNodeGraph(std::map<uint16_t, aiNode *> &apcNodes) {
|
||||||
aiNode *root = mScene->mRootNode = new aiNode();
|
aiNode *root = mScene->mRootNode = new aiNode();
|
||||||
root->mName.Set("<LWORoot>");
|
root->mName.Set("<LWORoot>");
|
||||||
|
|
||||||
|
ASSIMP_LOG_DEBUG("apcNodes initial size: ", apcNodes.size());
|
||||||
|
if (!apcNodes.empty()) {
|
||||||
|
ASSIMP_LOG_DEBUG("first apcNode is: ", apcNodes.begin()->first, " \"", apcNodes.begin()->second->mName.C_Str(), "\"");
|
||||||
|
}
|
||||||
|
|
||||||
//Set parent of all children, inserting pivots
|
//Set parent of all children, inserting pivots
|
||||||
|
{
|
||||||
std::map<uint16_t, aiNode *> mapPivot;
|
std::map<uint16_t, aiNode *> mapPivot;
|
||||||
for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) {
|
for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) {
|
||||||
|
|
||||||
|
@ -581,9 +578,9 @@ void LWOImporter::GenerateNodeGraph(std::map<uint16_t, aiNode *> &apcNodes) {
|
||||||
uint16_t parentIndex = nodeLayer->mParent;
|
uint16_t parentIndex = nodeLayer->mParent;
|
||||||
|
|
||||||
//Create pivot node, store it into the pivot map, and set the parent as the pivot
|
//Create pivot node, store it into the pivot map, and set the parent as the pivot
|
||||||
aiNode *pivotNode = new aiNode();
|
std::unique_ptr<aiNode> pivotNode(new aiNode());
|
||||||
pivotNode->mName.Set("Pivot-" + std::string(itapcNodes->second->mName.data));
|
pivotNode->mName.Set("Pivot-" + std::string(itapcNodes->second->mName.data));
|
||||||
itapcNodes->second->mParent = pivotNode;
|
itapcNodes->second->mParent = pivotNode.get();
|
||||||
|
|
||||||
//Look for the parent node to attach the pivot to
|
//Look for the parent node to attach the pivot to
|
||||||
if (apcNodes.find(parentIndex) != apcNodes.end()) {
|
if (apcNodes.find(parentIndex) != apcNodes.end()) {
|
||||||
|
@ -600,12 +597,30 @@ void LWOImporter::GenerateNodeGraph(std::map<uint16_t, aiNode *> &apcNodes) {
|
||||||
pivotNode->mTransformation.a4 = nodeLayer->mPivot.x;
|
pivotNode->mTransformation.a4 = nodeLayer->mPivot.x;
|
||||||
pivotNode->mTransformation.b4 = nodeLayer->mPivot.y;
|
pivotNode->mTransformation.b4 = nodeLayer->mPivot.y;
|
||||||
pivotNode->mTransformation.c4 = nodeLayer->mPivot.z;
|
pivotNode->mTransformation.c4 = nodeLayer->mPivot.z;
|
||||||
mapPivot[-(itapcNodes->first + 2)] = pivotNode;
|
uint16_t pivotNodeId = static_cast<uint16_t>(-(itapcNodes->first + 2));
|
||||||
|
ASSIMP_LOG_DEBUG("insert pivot node: ", pivotNodeId);
|
||||||
|
auto oldNodeIt = mapPivot.find(pivotNodeId);
|
||||||
|
if (oldNodeIt != mapPivot.end()) {
|
||||||
|
ASSIMP_LOG_ERROR("attempted to insert pivot node which already exists in pivot map ", pivotNodeId, " \"", pivotNode->mName.C_Str(), "\"");
|
||||||
|
} else {
|
||||||
|
mapPivot.emplace(pivotNodeId, pivotNode.release());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASSIMP_LOG_DEBUG("pivot nodes: ", mapPivot.size());
|
||||||
//Merge pivot map into node map
|
//Merge pivot map into node map
|
||||||
for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) {
|
for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end();) {
|
||||||
apcNodes[itMapPivot->first] = itMapPivot->second;
|
uint16_t pivotNodeId = itMapPivot->first;
|
||||||
|
auto oldApcNodeIt = apcNodes.find(pivotNodeId);
|
||||||
|
if (oldApcNodeIt != apcNodes.end()) {
|
||||||
|
ASSIMP_LOG_ERROR("attempted to insert pivot node which already exists in apc nodes ", pivotNodeId, " \"", itMapPivot->second->mName.C_Str(), "\"");
|
||||||
|
} else {
|
||||||
|
apcNodes.emplace(pivotNodeId, itMapPivot->second);
|
||||||
|
}
|
||||||
|
itMapPivot->second = nullptr;
|
||||||
|
itMapPivot = mapPivot.erase(itMapPivot);
|
||||||
|
}
|
||||||
|
ASSIMP_LOG_DEBUG("total nodes: ", apcNodes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set children of all parents
|
//Set children of all parents
|
||||||
|
@ -627,8 +642,15 @@ void LWOImporter::GenerateNodeGraph(std::map<uint16_t, aiNode *> &apcNodes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mScene->mRootNode->mNumChildren)
|
if (!mScene->mRootNode->mNumChildren) {
|
||||||
|
ASSIMP_LOG_DEBUG("All apcNodes:");
|
||||||
|
for (auto nodeIt = apcNodes.begin(); nodeIt != apcNodes.end(); ) {
|
||||||
|
ASSIMP_LOG_DEBUG("Node ", nodeIt->first, " \"", nodeIt->second->mName.C_Str(), "\"");
|
||||||
|
nodeIt->second = nullptr;
|
||||||
|
nodeIt = apcNodes.erase(nodeIt);
|
||||||
|
}
|
||||||
throw DeadlyImportError("LWO: Unable to build a valid node graph");
|
throw DeadlyImportError("LWO: Unable to build a valid node graph");
|
||||||
|
}
|
||||||
|
|
||||||
// Remove a single root node with no meshes assigned to it ...
|
// Remove a single root node with no meshes assigned to it ...
|
||||||
if (1 == mScene->mRootNode->mNumChildren) {
|
if (1 == mScene->mRootNode->mNumChildren) {
|
||||||
|
@ -1462,7 +1484,6 @@ void LWOImporter::LoadLWO2File() {
|
||||||
|
|
||||||
if (mFileBuffer + head.length > end) {
|
if (mFileBuffer + head.length > end) {
|
||||||
throw DeadlyImportError("LWO2: Chunk length points behind the file");
|
throw DeadlyImportError("LWO2: Chunk length points behind the file");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
uint8_t *const next = mFileBuffer + head.length;
|
uint8_t *const next = mFileBuffer + head.length;
|
||||||
mFileBuffer += bufOffset;
|
mFileBuffer += bufOffset;
|
||||||
|
|
|
@ -345,7 +345,7 @@ void LWOImporter::ConvertMaterial(const LWO::Surface &surf, aiMaterial *pcMat) {
|
||||||
|
|
||||||
// (the diffuse value is just a scaling factor)
|
// (the diffuse value is just a scaling factor)
|
||||||
// If a diffuse texture is set, we set this value to 1.0
|
// If a diffuse texture is set, we set this value to 1.0
|
||||||
clr = (b && false ? aiColor3D(1.0, 1.0, 1.0) : surf.mColor);
|
clr = (b ? aiColor3D(1.0, 1.0, 1.0) : surf.mColor);
|
||||||
clr.r *= surf.mDiffuseValue;
|
clr.r *= surf.mDiffuseValue;
|
||||||
clr.g *= surf.mDiffuseValue;
|
clr.g *= surf.mDiffuseValue;
|
||||||
clr.b *= surf.mDiffuseValue;
|
clr.b *= surf.mDiffuseValue;
|
||||||
|
|
|
@ -632,8 +632,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
nodes.push_back(d);
|
nodes.push_back(d);
|
||||||
}
|
}
|
||||||
ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'Channel\'");
|
ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'Channel\'");
|
||||||
}
|
} else {
|
||||||
|
|
||||||
// important: index of channel
|
// important: index of channel
|
||||||
nodes.back().channels.emplace_back();
|
nodes.back().channels.emplace_back();
|
||||||
LWO::Envelope &env = nodes.back().channels.back();
|
LWO::Envelope &env = nodes.back().channels.back();
|
||||||
|
@ -643,7 +642,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
// currently we can just interpret the standard channels 0...9
|
// currently we can just interpret the standard channels 0...9
|
||||||
// (hack) assume that index-i yields the binary channel type from LWO
|
// (hack) assume that index-i yields the binary channel type from LWO
|
||||||
env.type = (LWO::EnvelopeType)(env.index + 1);
|
env.type = (LWO::EnvelopeType)(env.index + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 'Envelope': a single animation channel
|
// 'Envelope': a single animation channel
|
||||||
else if ((*it).tokens[0] == "Envelope") {
|
else if ((*it).tokens[0] == "Envelope") {
|
||||||
|
|
|
@ -138,18 +138,31 @@ bool MD5Parser::ParseSection(Section &out) {
|
||||||
char *sz = buffer;
|
char *sz = buffer;
|
||||||
while (!IsSpaceOrNewLine(*buffer)) {
|
while (!IsSpaceOrNewLine(*buffer)) {
|
||||||
++buffer;
|
++buffer;
|
||||||
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
out.mName = std::string(sz, (uintptr_t)(buffer - sz));
|
out.mName = std::string(sz, (uintptr_t)(buffer - sz));
|
||||||
SkipSpaces();
|
while (IsSpace(*buffer)) {
|
||||||
|
++buffer;
|
||||||
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool running = true;
|
bool running = true;
|
||||||
while (running) {
|
while (running) {
|
||||||
if ('{' == *buffer) {
|
if ('{' == *buffer) {
|
||||||
// it is a normal section so read all lines
|
// it is a normal section so read all lines
|
||||||
++buffer;
|
++buffer;
|
||||||
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
bool run = true;
|
bool run = true;
|
||||||
while (run) {
|
while (run) {
|
||||||
if (!SkipSpacesAndLineEnd()) {
|
while (IsSpaceOrNewLine(*buffer)) {
|
||||||
|
++buffer;
|
||||||
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ('\0' == *buffer) {
|
||||||
return false; // seems this was the last section
|
return false; // seems this was the last section
|
||||||
}
|
}
|
||||||
if ('}' == *buffer) {
|
if ('}' == *buffer) {
|
||||||
|
@ -164,25 +177,39 @@ bool MD5Parser::ParseSection(Section &out) {
|
||||||
elem.szStart = buffer;
|
elem.szStart = buffer;
|
||||||
|
|
||||||
// terminate the line with zero
|
// terminate the line with zero
|
||||||
while (!IsLineEnd(*buffer))
|
while (!IsLineEnd(*buffer)) {
|
||||||
++buffer;
|
++buffer;
|
||||||
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (*buffer) {
|
if (*buffer) {
|
||||||
++lineNumber;
|
++lineNumber;
|
||||||
*buffer++ = '\0';
|
*buffer++ = '\0';
|
||||||
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else if (!IsSpaceOrNewLine(*buffer)) {
|
} else if (!IsSpaceOrNewLine(*buffer)) {
|
||||||
// it is an element at global scope. Parse its value and go on
|
// it is an element at global scope. Parse its value and go on
|
||||||
sz = buffer;
|
sz = buffer;
|
||||||
while (!IsSpaceOrNewLine(*buffer++))
|
while (!IsSpaceOrNewLine(*buffer++)) {
|
||||||
;
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
out.mGlobalValue = std::string(sz, (uintptr_t)(buffer - sz));
|
out.mGlobalValue = std::string(sz, (uintptr_t)(buffer - sz));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return SkipSpacesAndLineEnd();
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
|
while (IsSpaceOrNewLine(*buffer)) {
|
||||||
|
++buffer;
|
||||||
|
if (buffer == bufferEnd)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return '\0' != *buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -229,14 +256,19 @@ bool MD5Parser::ParseSection(Section &out) {
|
||||||
|
|
||||||
// parse a string, enclosed in quotation marks
|
// parse a string, enclosed in quotation marks
|
||||||
#define AI_MD5_PARSE_STRING_IN_QUOTATION(out) \
|
#define AI_MD5_PARSE_STRING_IN_QUOTATION(out) \
|
||||||
while ('\"' != *sz) \
|
out.length = 0; \
|
||||||
|
while ('\"' != *sz && '\0' != *sz) \
|
||||||
++sz; \
|
++sz; \
|
||||||
|
if ('\0' != *sz) { \
|
||||||
const char *szStart = ++sz; \
|
const char *szStart = ++sz; \
|
||||||
while ('\"' != *sz) \
|
while ('\"' != *sz && '\0' != *sz) \
|
||||||
++sz; \
|
++sz; \
|
||||||
|
if ('\0' != *sz) { \
|
||||||
const char *szEnd = (sz++); \
|
const char *szEnd = (sz++); \
|
||||||
out.length = (ai_uint32)(szEnd - szStart); \
|
out.length = (ai_uint32)(szEnd - szStart); \
|
||||||
::memcpy(out.data, szStart, out.length); \
|
::memcpy(out.data, szStart, out.length); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
out.data[out.length] = '\0';
|
out.data[out.length] = '\0';
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// .MD5MESH parsing function
|
// .MD5MESH parsing function
|
||||||
|
|
|
@ -365,9 +365,7 @@ public:
|
||||||
static void ReportWarning (const char* warn, unsigned int line);
|
static void ReportWarning (const char* warn, unsigned int line);
|
||||||
|
|
||||||
|
|
||||||
void ReportError (const char* error) {
|
AI_WONT_RETURN void ReportError (const char* error) AI_WONT_RETURN_SUFFIX;
|
||||||
return ReportError(error, lineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReportWarning (const char* warn) {
|
void ReportWarning (const char* warn) {
|
||||||
return ReportWarning(warn, lineNumber);
|
return ReportWarning(warn, lineNumber);
|
||||||
|
@ -404,6 +402,9 @@ private:
|
||||||
unsigned int lineNumber;
|
unsigned int lineNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void MD5Parser::ReportError(const char* error) {
|
||||||
|
ReportError(error, lineNumber);
|
||||||
|
}
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
inline bool MD5Parser::SkipLine(const char* in, const char** out) {
|
inline bool MD5Parser::SkipLine(const char* in, const char** out) {
|
||||||
++lineNumber;
|
++lineNumber;
|
||||||
|
|
|
@ -470,14 +470,16 @@ void HL1MDLLoader::read_bones() {
|
||||||
|
|
||||||
temp_bones_.resize(header_->numbones);
|
temp_bones_.resize(header_->numbones);
|
||||||
|
|
||||||
|
// Create the main 'bones' node that will contain all MDL root bones.
|
||||||
aiNode *bones_node = new aiNode(AI_MDL_HL1_NODE_BONES);
|
aiNode *bones_node = new aiNode(AI_MDL_HL1_NODE_BONES);
|
||||||
rootnode_children_.push_back(bones_node);
|
rootnode_children_.push_back(bones_node);
|
||||||
bones_node->mNumChildren = static_cast<unsigned int>(header_->numbones);
|
|
||||||
bones_node->mChildren = new aiNode *[bones_node->mNumChildren];
|
// Store roots bones IDs temporarily.
|
||||||
|
std::vector<int> roots;
|
||||||
|
|
||||||
// Create bone matrices in local space.
|
// Create bone matrices in local space.
|
||||||
for (int i = 0; i < header_->numbones; ++i) {
|
for (int i = 0; i < header_->numbones; ++i) {
|
||||||
aiNode *bone_node = temp_bones_[i].node = bones_node->mChildren[i] = new aiNode(unique_bones_names[i]);
|
aiNode *bone_node = temp_bones_[i].node = new aiNode(unique_bones_names[i]);
|
||||||
|
|
||||||
aiVector3D angles(pbone[i].value[3], pbone[i].value[4], pbone[i].value[5]);
|
aiVector3D angles(pbone[i].value[3], pbone[i].value[4], pbone[i].value[5]);
|
||||||
temp_bones_[i].absolute_transform = bone_node->mTransformation =
|
temp_bones_[i].absolute_transform = bone_node->mTransformation =
|
||||||
|
@ -485,9 +487,11 @@ void HL1MDLLoader::read_bones() {
|
||||||
aiVector3D(pbone[i].value[0], pbone[i].value[1], pbone[i].value[2]));
|
aiVector3D(pbone[i].value[0], pbone[i].value[1], pbone[i].value[2]));
|
||||||
|
|
||||||
if (pbone[i].parent == -1) {
|
if (pbone[i].parent == -1) {
|
||||||
bone_node->mParent = scene_->mRootNode;
|
bone_node->mParent = bones_node;
|
||||||
|
roots.push_back(i); // This bone has no parent. Add it to the roots list.
|
||||||
} else {
|
} else {
|
||||||
bone_node->mParent = bones_node->mChildren[pbone[i].parent];
|
bone_node->mParent = temp_bones_[pbone[i].parent].node;
|
||||||
|
temp_bones_[pbone[i].parent].children.push_back(i); // Add this bone to the parent bone's children list.
|
||||||
|
|
||||||
temp_bones_[i].absolute_transform =
|
temp_bones_[i].absolute_transform =
|
||||||
temp_bones_[pbone[i].parent].absolute_transform * bone_node->mTransformation;
|
temp_bones_[pbone[i].parent].absolute_transform * bone_node->mTransformation;
|
||||||
|
@ -496,6 +500,36 @@ void HL1MDLLoader::read_bones() {
|
||||||
temp_bones_[i].offset_matrix = temp_bones_[i].absolute_transform;
|
temp_bones_[i].offset_matrix = temp_bones_[i].absolute_transform;
|
||||||
temp_bones_[i].offset_matrix.Inverse();
|
temp_bones_[i].offset_matrix.Inverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allocate memory for each MDL root bone.
|
||||||
|
bones_node->mNumChildren = static_cast<unsigned int>(roots.size());
|
||||||
|
bones_node->mChildren = new aiNode *[bones_node->mNumChildren];
|
||||||
|
|
||||||
|
// Build all bones children hierarchy starting from each MDL root bone.
|
||||||
|
for (size_t i = 0; i < roots.size(); ++i)
|
||||||
|
{
|
||||||
|
const TempBone &root_bone = temp_bones_[roots[i]];
|
||||||
|
bones_node->mChildren[i] = root_bone.node;
|
||||||
|
build_bone_children_hierarchy(root_bone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HL1MDLLoader::build_bone_children_hierarchy(const TempBone &bone)
|
||||||
|
{
|
||||||
|
if (bone.children.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
aiNode* bone_node = bone.node;
|
||||||
|
bone_node->mNumChildren = static_cast<unsigned int>(bone.children.size());
|
||||||
|
bone_node->mChildren = new aiNode *[bone_node->mNumChildren];
|
||||||
|
|
||||||
|
// Build each child bone's hierarchy recursively.
|
||||||
|
for (size_t i = 0; i < bone.children.size(); ++i)
|
||||||
|
{
|
||||||
|
const TempBone &child_bone = temp_bones_[bone.children[i]];
|
||||||
|
bone_node->mChildren[i] = child_bone.node;
|
||||||
|
build_bone_children_hierarchy(child_bone);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -143,6 +143,14 @@ private:
|
||||||
*/
|
*/
|
||||||
static bool get_num_blend_controllers(const int num_blend_animations, int &num_blend_controllers);
|
static bool get_num_blend_controllers(const int num_blend_animations, int &num_blend_controllers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Build a bone's node children hierarchy.
|
||||||
|
*
|
||||||
|
* \param[in] bone The bone for which we must build all children hierarchy.
|
||||||
|
*/
|
||||||
|
struct TempBone;
|
||||||
|
void build_bone_children_hierarchy(const TempBone& bone);
|
||||||
|
|
||||||
/** Output scene to be filled */
|
/** Output scene to be filled */
|
||||||
aiScene *scene_;
|
aiScene *scene_;
|
||||||
|
|
||||||
|
@ -198,11 +206,13 @@ private:
|
||||||
TempBone() :
|
TempBone() :
|
||||||
node(nullptr),
|
node(nullptr),
|
||||||
absolute_transform(),
|
absolute_transform(),
|
||||||
offset_matrix() {}
|
offset_matrix(),
|
||||||
|
children() {}
|
||||||
|
|
||||||
aiNode *node;
|
aiNode *node;
|
||||||
aiMatrix4x4 absolute_transform;
|
aiMatrix4x4 absolute_transform;
|
||||||
aiMatrix4x4 offset_matrix;
|
aiMatrix4x4 offset_matrix;
|
||||||
|
std::vector<int> children; // Bone children
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<TempBone> temp_bones_;
|
std::vector<TempBone> temp_bones_;
|
||||||
|
|
|
@ -271,10 +271,16 @@ void MDLImporter::InternReadFile(const std::string &pFile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Check whether we're still inside the valid file range
|
||||||
|
bool MDLImporter::IsPosValid(const void *szPos) const {
|
||||||
|
return szPos && (const unsigned char *)szPos <= this->mBuffer + this->iFileSize && szPos >= this->mBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Check whether we're still inside the valid file range
|
// Check whether we're still inside the valid file range
|
||||||
void MDLImporter::SizeCheck(const void *szPos) {
|
void MDLImporter::SizeCheck(const void *szPos) {
|
||||||
if (!szPos || (const unsigned char *)szPos > this->mBuffer + this->iFileSize) {
|
if (!IsPosValid(szPos)) {
|
||||||
throw DeadlyImportError("Invalid MDL file. The file is too small "
|
throw DeadlyImportError("Invalid MDL file. The file is too small "
|
||||||
"or contains invalid data.");
|
"or contains invalid data.");
|
||||||
}
|
}
|
||||||
|
@ -284,7 +290,7 @@ void MDLImporter::SizeCheck(const void *szPos) {
|
||||||
// Just for debugging purposes
|
// Just for debugging purposes
|
||||||
void MDLImporter::SizeCheck(const void *szPos, const char *szFile, unsigned int iLine) {
|
void MDLImporter::SizeCheck(const void *szPos, const char *szFile, unsigned int iLine) {
|
||||||
ai_assert(nullptr != szFile);
|
ai_assert(nullptr != szFile);
|
||||||
if (!szPos || (const unsigned char *)szPos > mBuffer + iFileSize) {
|
if (!IsPosValid(szPos)) {
|
||||||
// remove a directory if there is one
|
// remove a directory if there is one
|
||||||
const char *szFilePtr = ::strrchr(szFile, '\\');
|
const char *szFilePtr = ::strrchr(szFile, '\\');
|
||||||
if (!szFilePtr) {
|
if (!szFilePtr) {
|
||||||
|
@ -975,7 +981,7 @@ void MDLImporter::CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7 **apcOutBones)
|
||||||
}
|
}
|
||||||
|
|
||||||
// store the name of the bone
|
// store the name of the bone
|
||||||
pcOutBone->mName.length = (size_t)iMaxLen;
|
pcOutBone->mName.length = static_cast<ai_uint32>(iMaxLen);
|
||||||
::memcpy(pcOutBone->mName.data, pcBone->name, pcOutBone->mName.length);
|
::memcpy(pcOutBone->mName.data, pcBone->name, pcOutBone->mName.length);
|
||||||
pcOutBone->mName.data[pcOutBone->mName.length] = '\0';
|
pcOutBone->mName.data[pcOutBone->mName.length] = '\0';
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ protected:
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Import a CS:S/HL2 MDL file (not fully implemented)
|
/** Import a CS:S/HL2 MDL file (not fully implemented)
|
||||||
*/
|
*/
|
||||||
void InternReadFile_HL2( );
|
AI_WONT_RETURN void InternReadFile_HL2( ) AI_WONT_RETURN_SUFFIX;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Check whether a given position is inside the valid range
|
/** Check whether a given position is inside the valid range
|
||||||
|
@ -150,6 +150,7 @@ protected:
|
||||||
*/
|
*/
|
||||||
void SizeCheck(const void* szPos);
|
void SizeCheck(const void* szPos);
|
||||||
void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine);
|
void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine);
|
||||||
|
bool IsPosValid(const void* szPos) const;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Validate the header data structure of a game studio MDL7 file
|
/** Validate the header data structure of a game studio MDL7 file
|
||||||
|
|
|
@ -481,6 +481,8 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
|
||||||
pcNew->achFormatHint[2] = 's';
|
pcNew->achFormatHint[2] = 's';
|
||||||
pcNew->achFormatHint[3] = '\0';
|
pcNew->achFormatHint[3] = '\0';
|
||||||
|
|
||||||
|
SizeCheck(szCurrent + pcNew->mWidth);
|
||||||
|
|
||||||
pcNew->pcData = (aiTexel *)new unsigned char[pcNew->mWidth];
|
pcNew->pcData = (aiTexel *)new unsigned char[pcNew->mWidth];
|
||||||
memcpy(pcNew->pcData, szCurrent, pcNew->mWidth);
|
memcpy(pcNew->pcData, szCurrent, pcNew->mWidth);
|
||||||
szCurrent += iWidth;
|
szCurrent += iWidth;
|
||||||
|
@ -493,12 +495,12 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
|
||||||
|
|
||||||
aiString szFile;
|
aiString szFile;
|
||||||
const size_t iLen = strlen((const char *)szCurrent);
|
const size_t iLen = strlen((const char *)szCurrent);
|
||||||
size_t iLen2 = iLen + 1;
|
size_t iLen2 = iLen > (MAXLEN - 1) ? (MAXLEN - 1) : iLen;
|
||||||
iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
|
|
||||||
memcpy(szFile.data, (const char *)szCurrent, iLen2);
|
memcpy(szFile.data, (const char *)szCurrent, iLen2);
|
||||||
|
szFile.data[iLen2] = '\0';
|
||||||
szFile.length = static_cast<ai_uint32>(iLen2);
|
szFile.length = static_cast<ai_uint32>(iLen2);
|
||||||
|
|
||||||
szCurrent += iLen2;
|
szCurrent += iLen2 + 1;
|
||||||
|
|
||||||
// place this as diffuse texture
|
// place this as diffuse texture
|
||||||
pcMatOut->AddProperty(&szFile, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
pcMatOut->AddProperty(&szFile, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||||
|
@ -703,7 +705,14 @@ void MDLImporter::SkipSkinLump_3DGS_MDL7(
|
||||||
tex.pcData = bad_texel;
|
tex.pcData = bad_texel;
|
||||||
tex.mHeight = iHeight;
|
tex.mHeight = iHeight;
|
||||||
tex.mWidth = iWidth;
|
tex.mWidth = iWidth;
|
||||||
|
|
||||||
|
try {
|
||||||
ParseTextureColorData(szCurrent, iMasked, &iSkip, &tex);
|
ParseTextureColorData(szCurrent, iMasked, &iSkip, &tex);
|
||||||
|
} catch (...) {
|
||||||
|
// FIX: Important, otherwise the destructor will crash
|
||||||
|
tex.pcData = nullptr;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
// FIX: Important, otherwise the destructor will crash
|
// FIX: Important, otherwise the destructor will crash
|
||||||
tex.pcData = nullptr;
|
tex.pcData = nullptr;
|
||||||
|
|
|
@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <assimp/types.h>
|
||||||
#include "MMDCpp14.h"
|
#include "MMDCpp14.h"
|
||||||
|
|
||||||
namespace pmx
|
namespace pmx
|
||||||
|
@ -730,7 +731,7 @@ namespace pmx
|
||||||
std::unique_ptr<PmxAncherRigidBody []> anchers;
|
std::unique_ptr<PmxAncherRigidBody []> anchers;
|
||||||
int pin_vertex_count;
|
int pin_vertex_count;
|
||||||
std::unique_ptr<int []> pin_vertices;
|
std::unique_ptr<int []> pin_vertices;
|
||||||
void Read(std::istream *stream, PmxSetting *setting);
|
AI_WONT_RETURN void Read(std::istream *stream, PmxSetting *setting) AI_WONT_RETURN_SUFFIX;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PmxModel
|
class PmxModel
|
||||||
|
|
|
@ -486,7 +486,7 @@ void MS3DImporter::InternReadFile( const std::string& pFile,
|
||||||
|
|
||||||
for (unsigned int j = 0,n = 0; j < m->mNumFaces; ++j) {
|
for (unsigned int j = 0,n = 0; j < m->mNumFaces; ++j) {
|
||||||
aiFace& f = m->mFaces[j];
|
aiFace& f = m->mFaces[j];
|
||||||
if (g.triangles[j]>triangles.size()) {
|
if (g.triangles[j] >= triangles.size()) {
|
||||||
throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
|
throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,7 +494,7 @@ void MS3DImporter::InternReadFile( const std::string& pFile,
|
||||||
f.mIndices = new unsigned int[f.mNumIndices=3];
|
f.mIndices = new unsigned int[f.mNumIndices=3];
|
||||||
|
|
||||||
for (unsigned int k = 0; k < 3; ++k,++n) {
|
for (unsigned int k = 0; k < 3; ++k,++n) {
|
||||||
if (t.indices[k]>vertices.size()) {
|
if (t.indices[k] >= vertices.size()) {
|
||||||
throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
|
throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/importerdesc.h>
|
#include <assimp/importerdesc.h>
|
||||||
#include <assimp/StreamReader.h>
|
#include <assimp/StreamReader.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
|
@ -160,6 +161,9 @@ void NDOImporter::InternReadFile( const std::string& pFile,
|
||||||
|
|
||||||
temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
|
temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
|
||||||
head = (const char*)reader.GetPtr();
|
head = (const char*)reader.GetPtr();
|
||||||
|
if (std::numeric_limits<unsigned int>::max() - 76 < temp) {
|
||||||
|
throw DeadlyImportError("Invalid name length");
|
||||||
|
}
|
||||||
reader.IncPtr(temp + 76); /* skip unknown stuff */
|
reader.IncPtr(temp + 76); /* skip unknown stuff */
|
||||||
|
|
||||||
obj.name = std::string(head, temp);
|
obj.name = std::string(head, temp);
|
||||||
|
|
|
@ -284,7 +284,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
|
||||||
for (unsigned int i = 0; i < numFaces; ) {
|
for (unsigned int i = 0; i < numFaces; ) {
|
||||||
if(!GetNextLine(buffer,line)) {
|
if(!GetNextLine(buffer,line)) {
|
||||||
ASSIMP_LOG_ERROR("OFF: The number of faces in the header is incorrect");
|
ASSIMP_LOG_ERROR("OFF: The number of faces in the header is incorrect");
|
||||||
break;
|
throw DeadlyImportError("OFF: The number of faces in the header is incorrect");
|
||||||
}
|
}
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
sz = line; SkipSpaces(&sz);
|
sz = line; SkipSpaces(&sz);
|
||||||
|
|
|
@ -239,8 +239,6 @@ struct Mesh {
|
||||||
unsigned int m_uiMaterialIndex;
|
unsigned int m_uiMaterialIndex;
|
||||||
/// True, if normals are stored.
|
/// True, if normals are stored.
|
||||||
bool m_hasNormals;
|
bool m_hasNormals;
|
||||||
/// True, if vertex colors are stored.
|
|
||||||
bool m_hasVertexColors;
|
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
explicit Mesh(const std::string &name) :
|
explicit Mesh(const std::string &name) :
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
Open Asset Import Library (assimp)
|
Open Asset Import Library (assimp)
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
Copyright (c) 2006-2020, assimp team
|
Copyright (c) 2006-2023, assimp team
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
@ -84,7 +84,6 @@ ObjFileImporter::ObjFileImporter() :
|
||||||
// Destructor.
|
// Destructor.
|
||||||
ObjFileImporter::~ObjFileImporter() {
|
ObjFileImporter::~ObjFileImporter() {
|
||||||
delete m_pRootObject;
|
delete m_pRootObject;
|
||||||
m_pRootObject = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -270,7 +269,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model *pModel, const ObjFile
|
||||||
for (size_t i = 0; i < pObject->m_Meshes.size(); ++i) {
|
for (size_t i = 0; i < pObject->m_Meshes.size(); ++i) {
|
||||||
unsigned int meshId = pObject->m_Meshes[i];
|
unsigned int meshId = pObject->m_Meshes[i];
|
||||||
aiMesh *pMesh = createTopology(pModel, pObject, meshId);
|
aiMesh *pMesh = createTopology(pModel, pObject, meshId);
|
||||||
if (pMesh) {
|
if (pMesh != nullptr) {
|
||||||
if (pMesh->mNumFaces > 0) {
|
if (pMesh->mNumFaces > 0) {
|
||||||
MeshArray.push_back(pMesh);
|
MeshArray.push_back(pMesh);
|
||||||
} else {
|
} else {
|
||||||
|
@ -331,7 +330,6 @@ aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjF
|
||||||
|
|
||||||
for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) {
|
for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) {
|
||||||
const ObjFile::Face *inp = pObjMesh->m_Faces[index];
|
const ObjFile::Face *inp = pObjMesh->m_Faces[index];
|
||||||
//ai_assert(nullptr != inp);
|
|
||||||
|
|
||||||
if (inp->mPrimitiveType == aiPrimitiveType_LINE) {
|
if (inp->mPrimitiveType == aiPrimitiveType_LINE) {
|
||||||
pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size() - 1);
|
pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size() - 1);
|
||||||
|
@ -500,6 +498,10 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel,
|
||||||
|
|
||||||
if (vertexIndex) {
|
if (vertexIndex) {
|
||||||
if (!last) {
|
if (!last) {
|
||||||
|
if (pMesh->mNumVertices <= newIndex + 1) {
|
||||||
|
throw DeadlyImportError("OBJ: bad vertex index");
|
||||||
|
}
|
||||||
|
|
||||||
pMesh->mVertices[newIndex + 1] = pMesh->mVertices[newIndex];
|
pMesh->mVertices[newIndex + 1] = pMesh->mVertices[newIndex];
|
||||||
if (!sourceFace->m_normals.empty() && !pModel->mNormals.empty()) {
|
if (!sourceFace->m_normals.empty() && !pModel->mNormals.empty()) {
|
||||||
pMesh->mNormals[newIndex + 1] = pMesh->mNormals[newIndex];
|
pMesh->mNormals[newIndex + 1] = pMesh->mNormals[newIndex];
|
||||||
|
|
|
@ -252,8 +252,8 @@ void ObjFileMtlImporter::load() {
|
||||||
case 'a': // Anisotropy
|
case 'a': // Anisotropy
|
||||||
{
|
{
|
||||||
++m_DataIt;
|
++m_DataIt;
|
||||||
getFloatValue(m_pModel->mCurrentMaterial->anisotropy);
|
|
||||||
if (m_pModel->mCurrentMaterial != nullptr)
|
if (m_pModel->mCurrentMaterial != nullptr)
|
||||||
|
getFloatValue(m_pModel->mCurrentMaterial->anisotropy);
|
||||||
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
|
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -371,6 +371,7 @@ void ObjFileMtlImporter::getTexture() {
|
||||||
if (m_pModel->mCurrentMaterial == nullptr) {
|
if (m_pModel->mCurrentMaterial == nullptr) {
|
||||||
m_pModel->mCurrentMaterial = new ObjFile::Material();
|
m_pModel->mCurrentMaterial = new ObjFile::Material();
|
||||||
m_pModel->mCurrentMaterial->MaterialName.Set("Empty_Material");
|
m_pModel->mCurrentMaterial->MaterialName.Set("Empty_Material");
|
||||||
|
m_pModel->mMaterialMap["Empty_Material"] = m_pModel->mCurrentMaterial;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *pPtr(&(*m_DataIt));
|
const char *pPtr(&(*m_DataIt));
|
||||||
|
|
|
@ -156,9 +156,17 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
|
||||||
// read in vertex definition (homogeneous coords)
|
// read in vertex definition (homogeneous coords)
|
||||||
getHomogeneousVector3(m_pModel->mVertices);
|
getHomogeneousVector3(m_pModel->mVertices);
|
||||||
} else if (numComponents == 6) {
|
} else if (numComponents == 6) {
|
||||||
|
// fill previous omitted vertex-colors by default
|
||||||
|
if (m_pModel->mVertexColors.size() < m_pModel->mVertices.size()) {
|
||||||
|
m_pModel->mVertexColors.resize(m_pModel->mVertices.size(), aiVector3D(0, 0, 0));
|
||||||
|
}
|
||||||
// read vertex and vertex-color
|
// read vertex and vertex-color
|
||||||
getTwoVectors3(m_pModel->mVertices, m_pModel->mVertexColors);
|
getTwoVectors3(m_pModel->mVertices, m_pModel->mVertexColors);
|
||||||
}
|
}
|
||||||
|
// append omitted vertex-colors as default for the end if any vertex-color exists
|
||||||
|
if (!m_pModel->mVertexColors.empty() && m_pModel->mVertexColors.size() < m_pModel->mVertices.size()) {
|
||||||
|
m_pModel->mVertexColors.resize(m_pModel->mVertices.size(), aiVector3D(0, 0, 0));
|
||||||
|
}
|
||||||
} else if (*m_DataIt == 't') {
|
} else if (*m_DataIt == 't') {
|
||||||
// read in texture coordinate ( 2D or 3D )
|
// read in texture coordinate ( 2D or 3D )
|
||||||
++m_DataIt;
|
++m_DataIt;
|
||||||
|
@ -236,7 +244,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
|
||||||
getNameNoSpace(m_DataIt, m_DataItEnd, name);
|
getNameNoSpace(m_DataIt, m_DataItEnd, name);
|
||||||
insideCstype = name == "cstype";
|
insideCstype = name == "cstype";
|
||||||
goto pf_skip_line;
|
goto pf_skip_line;
|
||||||
} break;
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
pf_skip_line:
|
pf_skip_line:
|
||||||
|
@ -456,8 +464,19 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
|
||||||
iPos = 0;
|
iPos = 0;
|
||||||
} else {
|
} else {
|
||||||
//OBJ USES 1 Base ARRAYS!!!!
|
//OBJ USES 1 Base ARRAYS!!!!
|
||||||
const char *token = &(*m_DataIt);
|
int iVal;
|
||||||
const int iVal = ::atoi(token);
|
auto end = m_DataIt;
|
||||||
|
// find either the buffer end or the '\0'
|
||||||
|
while (end < m_DataItEnd && *end != '\0')
|
||||||
|
++end;
|
||||||
|
// avoid temporary string allocation if there is a zero
|
||||||
|
if (end != m_DataItEnd) {
|
||||||
|
iVal = ::atoi(&(*m_DataIt));
|
||||||
|
} else {
|
||||||
|
// otherwise make a zero terminated copy, which is safe to pass to atoi
|
||||||
|
std::string number(&(*m_DataIt), m_DataItEnd - m_DataIt);
|
||||||
|
iVal = ::atoi(number.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// increment iStep position based off of the sign and # of digits
|
// increment iStep position based off of the sign and # of digits
|
||||||
int tmp = iVal;
|
int tmp = iVal;
|
||||||
|
|
|
@ -57,7 +57,7 @@ namespace Assimp {
|
||||||
namespace Ogre {
|
namespace Ogre {
|
||||||
|
|
||||||
//AI_WONT_RETURN void ThrowAttibuteError(const XmlParser *reader, const std::string &name, const std::string &error = "") AI_WONT_RETURN_SUFFIX;
|
//AI_WONT_RETURN void ThrowAttibuteError(const XmlParser *reader, const std::string &name, const std::string &error = "") AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN void ThrowAttibuteError(const std::string &nodeName, const std::string &name, const std::string &error) AI_WONT_RETURN_SUFFIX;
|
||||||
AI_WONT_RETURN void ThrowAttibuteError(const std::string &nodeName, const std::string &name, const std::string &error) {
|
AI_WONT_RETURN void ThrowAttibuteError(const std::string &nodeName, const std::string &name, const std::string &error) {
|
||||||
if (!error.empty()) {
|
if (!error.empty()) {
|
||||||
throw DeadlyImportError(error, " in node '", nodeName, "' and attribute '", name, "'");
|
throw DeadlyImportError(error, " in node '", nodeName, "' and attribute '", name, "'");
|
||||||
|
@ -128,7 +128,6 @@ bool OgreXmlSerializer::ReadAttribute<bool>(XmlNode &xmlNode, const char *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowAttibuteError(xmlNode.name(), name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'");
|
ThrowAttibuteError(xmlNode.name(), name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mesh XML constants
|
// Mesh XML constants
|
||||||
|
@ -490,7 +489,7 @@ bool OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *me
|
||||||
OgreXmlSerializer serializer(xmlParser.get());
|
OgreXmlSerializer serializer(xmlParser.get());
|
||||||
XmlNode root = xmlParser->getRootNode();
|
XmlNode root = xmlParser->getRootNode();
|
||||||
if (std::string(root.name()) != nnSkeleton) {
|
if (std::string(root.name()) != nnSkeleton) {
|
||||||
printf("\nSkeleton is not a valid root: %s\n", root.name());
|
ASSIMP_LOG_VERBOSE_DEBUG("nSkeleton is not a valid root: ", root.name(), ".");
|
||||||
for (auto &a : root.children()) {
|
for (auto &a : root.children()) {
|
||||||
if (std::string(a.name()) == nnSkeleton) {
|
if (std::string(a.name()) == nnSkeleton) {
|
||||||
root = a;
|
root = a;
|
||||||
|
|
|
@ -460,14 +460,12 @@ void OpenGEXImporter::handleMetricNode(DDLNode *node, aiScene * /*pScene*/) {
|
||||||
void OpenGEXImporter::handleNameNode(DDLNode *node, aiScene * /*pScene*/) {
|
void OpenGEXImporter::handleNameNode(DDLNode *node, aiScene * /*pScene*/) {
|
||||||
if (nullptr == m_currentNode) {
|
if (nullptr == m_currentNode) {
|
||||||
throw DeadlyImportError("No current node for name.");
|
throw DeadlyImportError("No current node for name.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value *val(node->getValue());
|
Value *val(node->getValue());
|
||||||
if (nullptr != val) {
|
if (nullptr != val) {
|
||||||
if (Value::ValueType::ddl_string != val->m_type) {
|
if (Value::ValueType::ddl_string != val->m_type) {
|
||||||
throw DeadlyImportError("OpenGEX: invalid data type for value in node name.");
|
throw DeadlyImportError("OpenGEX: invalid data type for value in node name.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string name(val->getString());
|
const std::string name(val->getString());
|
||||||
|
@ -508,7 +506,6 @@ static void getRefNames(DDLNode *node, std::vector<std::string> &names) {
|
||||||
void OpenGEXImporter::handleObjectRefNode(DDLNode *node, aiScene * /*pScene*/) {
|
void OpenGEXImporter::handleObjectRefNode(DDLNode *node, aiScene * /*pScene*/) {
|
||||||
if (nullptr == m_currentNode) {
|
if (nullptr == m_currentNode) {
|
||||||
throw DeadlyImportError("No parent node for name.");
|
throw DeadlyImportError("No parent node for name.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> objRefNames;
|
std::vector<std::string> objRefNames;
|
||||||
|
@ -532,7 +529,6 @@ void OpenGEXImporter::handleObjectRefNode(DDLNode *node, aiScene * /*pScene*/) {
|
||||||
void OpenGEXImporter::handleMaterialRefNode(ODDLParser::DDLNode *node, aiScene * /*pScene*/) {
|
void OpenGEXImporter::handleMaterialRefNode(ODDLParser::DDLNode *node, aiScene * /*pScene*/) {
|
||||||
if (nullptr == m_currentNode) {
|
if (nullptr == m_currentNode) {
|
||||||
throw DeadlyImportError("No parent node for name.");
|
throw DeadlyImportError("No parent node for name.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> matRefNames;
|
std::vector<std::string> matRefNames;
|
||||||
|
@ -672,14 +668,12 @@ static void setMatrix(aiNode *node, DataArrayList *transformData) {
|
||||||
void OpenGEXImporter::handleTransformNode(ODDLParser::DDLNode *node, aiScene * /*pScene*/) {
|
void OpenGEXImporter::handleTransformNode(ODDLParser::DDLNode *node, aiScene * /*pScene*/) {
|
||||||
if (nullptr == m_currentNode) {
|
if (nullptr == m_currentNode) {
|
||||||
throw DeadlyImportError("No parent node for name.");
|
throw DeadlyImportError("No parent node for name.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataArrayList *transformData(node->getDataArrayList());
|
DataArrayList *transformData(node->getDataArrayList());
|
||||||
if (nullptr != transformData) {
|
if (nullptr != transformData) {
|
||||||
if (transformData->m_numItems != 16) {
|
if (transformData->m_numItems != 16) {
|
||||||
throw DeadlyImportError("Invalid number of data for transform matrix.");
|
throw DeadlyImportError("Invalid number of data for transform matrix.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
setMatrix(m_currentNode, transformData);
|
setMatrix(m_currentNode, transformData);
|
||||||
}
|
}
|
||||||
|
@ -835,7 +829,6 @@ static void copyColor4DArray(size_t numItems, DataArrayList *vaList, aiColor4D *
|
||||||
void OpenGEXImporter::handleVertexArrayNode(ODDLParser::DDLNode *node, aiScene * /*pScene*/) {
|
void OpenGEXImporter::handleVertexArrayNode(ODDLParser::DDLNode *node, aiScene * /*pScene*/) {
|
||||||
if (nullptr == node) {
|
if (nullptr == node) {
|
||||||
throw DeadlyImportError("No parent node for name.");
|
throw DeadlyImportError("No parent node for name.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Property *prop = node->getProperties();
|
Property *prop = node->getProperties();
|
||||||
|
@ -876,12 +869,10 @@ void OpenGEXImporter::handleVertexArrayNode(ODDLParser::DDLNode *node, aiScene *
|
||||||
void OpenGEXImporter::handleIndexArrayNode(ODDLParser::DDLNode *node, aiScene * /*pScene*/) {
|
void OpenGEXImporter::handleIndexArrayNode(ODDLParser::DDLNode *node, aiScene * /*pScene*/) {
|
||||||
if (nullptr == node) {
|
if (nullptr == node) {
|
||||||
throw DeadlyImportError("No parent node for name.");
|
throw DeadlyImportError("No parent node for name.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nullptr == m_currentMesh) {
|
if (nullptr == m_currentMesh) {
|
||||||
throw DeadlyImportError("No current mesh for index data found.");
|
throw DeadlyImportError("No current mesh for index data found.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataArrayList *vaList = node->getDataArrayList();
|
DataArrayList *vaList = node->getDataArrayList();
|
||||||
|
|
|
@ -382,11 +382,10 @@ void Q3DImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
goto outer;
|
goto outer;
|
||||||
} break;
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw DeadlyImportError("Quick3D: Unknown chunk");
|
throw DeadlyImportError("Quick3D: Unknown chunk");
|
||||||
break;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
outer:
|
outer:
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace Assimp {
|
||||||
class RAWImporter : public BaseImporter {
|
class RAWImporter : public BaseImporter {
|
||||||
public:
|
public:
|
||||||
RAWImporter();
|
RAWImporter();
|
||||||
~RAWImporter();
|
~RAWImporter() override;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
|
|
|
@ -85,7 +85,7 @@ static const aiImporterDesc desc = {
|
||||||
struct SIBChunk {
|
struct SIBChunk {
|
||||||
uint32_t Tag;
|
uint32_t Tag;
|
||||||
uint32_t Size;
|
uint32_t Size;
|
||||||
} PACK_STRUCT;
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
POS,
|
POS,
|
||||||
|
|
|
@ -590,7 +590,7 @@ void SMDImporter::CreateOutputMaterials() {
|
||||||
pScene->mMaterials[iMat] = pcMat;
|
pScene->mMaterials[iMat] = pcMat;
|
||||||
|
|
||||||
aiString szName;
|
aiString szName;
|
||||||
szName.length = (size_t)ai_snprintf(szName.data,MAXLEN,"Texture_%u",iMat);
|
szName.length = static_cast<ai_uint32>(ai_snprintf(szName.data,MAXLEN,"Texture_%u",iMat));
|
||||||
pcMat->AddProperty(&szName,AI_MATKEY_NAME);
|
pcMat->AddProperty(&szName,AI_MATKEY_NAME);
|
||||||
|
|
||||||
if (aszTextures[iMat].length())
|
if (aszTextures[iMat].length())
|
||||||
|
@ -837,7 +837,10 @@ void SMDImporter::ParseNodeInfo(const char* szCurrent, const char** szCurrentOut
|
||||||
unsigned int iBone = 0;
|
unsigned int iBone = 0;
|
||||||
SkipSpacesAndLineEnd(szCurrent,&szCurrent);
|
SkipSpacesAndLineEnd(szCurrent,&szCurrent);
|
||||||
if ( !ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent)) {
|
if ( !ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent)) {
|
||||||
LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index");
|
throw DeadlyImportError("Unexpected EOF/EOL while parsing bone index");
|
||||||
|
}
|
||||||
|
if (iBone == UINT_MAX) {
|
||||||
|
LogErrorNoThrow("Invalid bone number while parsing bone index");
|
||||||
SMDI_PARSE_RETURN;
|
SMDI_PARSE_RETURN;
|
||||||
}
|
}
|
||||||
// add our bone to the list
|
// add our bone to the list
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace Assimp {
|
||||||
class UnrealImporter : public BaseImporter {
|
class UnrealImporter : public BaseImporter {
|
||||||
public:
|
public:
|
||||||
UnrealImporter();
|
UnrealImporter();
|
||||||
~UnrealImporter();
|
~UnrealImporter() override;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** @brief Returns whether we can handle the format of the given file
|
/** @brief Returns whether we can handle the format of the given file
|
||||||
|
|
|
@ -578,7 +578,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector<XFile::Materi
|
||||||
aiString name;
|
aiString name;
|
||||||
pScene->mMaterials[b]->Get( AI_MATKEY_NAME, name);
|
pScene->mMaterials[b]->Get( AI_MATKEY_NAME, name);
|
||||||
if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) {
|
if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) {
|
||||||
oldMat.sceneIndex = a;
|
oldMat.sceneIndex = b;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -839,7 +839,6 @@ void XFileParser::ParseDataObjectAnimationKey(AnimBone *pAnimBone) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ThrowException("Unknown key type ", keyType, " in animation.");
|
ThrowException("Unknown key type ", keyType, " in animation.");
|
||||||
break;
|
|
||||||
} // end switch
|
} // end switch
|
||||||
|
|
||||||
// key separator
|
// key separator
|
||||||
|
|
|
@ -58,8 +58,6 @@ class X3DExporter {
|
||||||
Value(value) {
|
Value(value) {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
SAttribute(SAttribute &&rhs) AI_NO_EXCEPT = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/***********************************************/
|
/***********************************************/
|
||||||
|
|
|
@ -55,6 +55,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
AI_WONT_RETURN inline void Throw_ArgOutOfRange(const std::string &argument) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_CloseNotFound(const std::string &node) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_ConvertFail_Str2ArrF(const std::string &nodeName, const std::string &pAttrValue) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_ConvertFail_Str2ArrD(const std::string &nodeName, const std::string &pAttrValue) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_ConvertFail_Str2ArrB(const std::string &nodeName, const std::string &pAttrValue) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_ConvertFail_Str2ArrI(const std::string &nodeName, const std::string &pAttrValue) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_DEF_And_USE(const std::string &nodeName) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_IncorrectAttr(const std::string &nodeName, const std::string &pAttrName) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &pAttrName) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_TagCountIncorrect(const std::string &pNode) AI_WONT_RETURN_SUFFIX;
|
||||||
|
AI_WONT_RETURN inline void Throw_USE_NotFound(const std::string &nodeName, const std::string &pAttrValue) AI_WONT_RETURN_SUFFIX;
|
||||||
|
|
||||||
inline void Throw_ArgOutOfRange(const std::string &argument) {
|
inline void Throw_ArgOutOfRange(const std::string &argument) {
|
||||||
throw DeadlyImportError("Argument value is out of range for: \"" + argument + "\".");
|
throw DeadlyImportError("Argument value is out of range for: \"" + argument + "\".");
|
||||||
|
|
|
@ -12,7 +12,6 @@ bool X3DXmlHelper::getColor3DAttribute(XmlNode &node, const char *attributeName,
|
||||||
tokenize<std::string>(val, values, " ");
|
tokenize<std::string>(val, values, " ");
|
||||||
if (values.size() != 3) {
|
if (values.size() != 3) {
|
||||||
Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
|
Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
auto it = values.begin();
|
auto it = values.begin();
|
||||||
color.r = stof(*it++);
|
color.r = stof(*it++);
|
||||||
|
@ -30,7 +29,6 @@ bool X3DXmlHelper::getVector2DAttribute(XmlNode &node, const char *attributeName
|
||||||
tokenize<std::string>(val, values, " ");
|
tokenize<std::string>(val, values, " ");
|
||||||
if (values.size() != 2) {
|
if (values.size() != 2) {
|
||||||
Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
|
Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
auto it = values.begin();
|
auto it = values.begin();
|
||||||
color.x = stof(*it++);
|
color.x = stof(*it++);
|
||||||
|
@ -47,7 +45,6 @@ bool X3DXmlHelper::getVector3DAttribute(XmlNode &node, const char *attributeName
|
||||||
tokenize<std::string>(val, values, " ");
|
tokenize<std::string>(val, values, " ");
|
||||||
if (values.size() != 3) {
|
if (values.size() != 3) {
|
||||||
Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
|
Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
auto it = values.begin();
|
auto it = values.begin();
|
||||||
color.x = stof(*it++);
|
color.x = stof(*it++);
|
||||||
|
|
|
@ -513,21 +513,22 @@ struct Camera : public Object {
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
|
struct Perspective {
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
float aspectRatio; //!<The floating - point aspect ratio of the field of view. (0 = undefined = use the canvas one)
|
float aspectRatio; //!<The floating - point aspect ratio of the field of view. (0 = undefined = use the canvas one)
|
||||||
float yfov; //!<The floating - point vertical field of view in radians. (required)
|
float yfov; //!<The floating - point vertical field of view in radians. (required)
|
||||||
float zfar; //!<The floating - point distance to the far clipping plane. (required)
|
float zfar; //!<The floating - point distance to the far clipping plane. (required)
|
||||||
float znear; //!< The floating - point distance to the near clipping plane. (required)
|
float znear; //!< The floating - point distance to the near clipping plane. (required)
|
||||||
} perspective;
|
};
|
||||||
|
|
||||||
struct {
|
struct Ortographic {
|
||||||
float xmag; //! The floating-point horizontal magnification of the view. (required)
|
float xmag; //! The floating-point horizontal magnification of the view. (required)
|
||||||
float ymag; //! The floating-point vertical magnification of the view. (required)
|
float ymag; //! The floating-point vertical magnification of the view. (required)
|
||||||
float zfar; //! The floating-point distance to the far clipping plane. (required)
|
float zfar; //! The floating-point distance to the far clipping plane. (required)
|
||||||
float znear; //! The floating-point distance to the near clipping plane. (required)
|
float znear; //! The floating-point distance to the near clipping plane. (required)
|
||||||
} ortographic;
|
};
|
||||||
|
union {
|
||||||
|
struct Perspective perspective;
|
||||||
|
struct Ortographic ortographic;
|
||||||
};
|
};
|
||||||
|
|
||||||
Camera() = default;
|
Camera() = default;
|
||||||
|
|
|
@ -93,7 +93,10 @@ const aiImporterDesc *glTFImporter::GetInfo() const {
|
||||||
bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const {
|
bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const {
|
||||||
glTF::Asset asset(pIOHandler);
|
glTF::Asset asset(pIOHandler);
|
||||||
try {
|
try {
|
||||||
asset.Load(pFile, GetExtension(pFile) == "glb");
|
asset.Load(pFile,
|
||||||
|
CheckMagicToken(
|
||||||
|
pIOHandler, pFile, AI_GLB_MAGIC_NUMBER, 1, 0,
|
||||||
|
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
|
||||||
return asset.asset;
|
return asset.asset;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -697,7 +700,10 @@ void glTFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOS
|
||||||
|
|
||||||
// read the asset file
|
// read the asset file
|
||||||
glTF::Asset asset(pIOHandler);
|
glTF::Asset asset(pIOHandler);
|
||||||
asset.Load(pFile, GetExtension(pFile) == "glb");
|
asset.Load(pFile,
|
||||||
|
CheckMagicToken(
|
||||||
|
pIOHandler, pFile, AI_GLB_MAGIC_NUMBER, 1, 0,
|
||||||
|
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
|
||||||
|
|
||||||
//
|
//
|
||||||
// Copy the data out
|
// Copy the data out
|
||||||
|
|
|
@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* glTF Extensions Support:
|
* glTF Extensions Support:
|
||||||
* KHR_materials_pbrSpecularGlossiness full
|
* KHR_materials_pbrSpecularGlossiness full
|
||||||
|
* KHR_materials_specular full
|
||||||
* KHR_materials_unlit full
|
* KHR_materials_unlit full
|
||||||
* KHR_lights_punctual full
|
* KHR_lights_punctual full
|
||||||
* KHR_materials_sheen full
|
* KHR_materials_sheen full
|
||||||
|
@ -370,6 +371,15 @@ struct CustomExtension {
|
||||||
CustomExtension& operator=(const CustomExtension&) = default;
|
CustomExtension& operator=(const CustomExtension&) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Represents metadata in an glTF2 object
|
||||||
|
struct Extras {
|
||||||
|
std::vector<CustomExtension> mValues;
|
||||||
|
|
||||||
|
inline bool HasExtras() const {
|
||||||
|
return !mValues.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//! Base class for all glTF top-level objects
|
//! Base class for all glTF top-level objects
|
||||||
struct Object {
|
struct Object {
|
||||||
int index; //!< The index of this object within its property container
|
int index; //!< The index of this object within its property container
|
||||||
|
@ -378,7 +388,7 @@ struct Object {
|
||||||
std::string name; //!< The user-defined name of this object
|
std::string name; //!< The user-defined name of this object
|
||||||
|
|
||||||
CustomExtension customExtensions;
|
CustomExtension customExtensions;
|
||||||
CustomExtension extras;
|
Extras extras;
|
||||||
|
|
||||||
//! Objects marked as special are not exported (used to emulate the binary body buffer)
|
//! Objects marked as special are not exported (used to emulate the binary body buffer)
|
||||||
virtual bool IsSpecial() const { return false; }
|
virtual bool IsSpecial() const { return false; }
|
||||||
|
@ -483,7 +493,7 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Buffer();
|
Buffer();
|
||||||
~Buffer();
|
~Buffer() override;
|
||||||
|
|
||||||
void Read(Value &obj, Asset &r);
|
void Read(Value &obj, Asset &r);
|
||||||
|
|
||||||
|
@ -565,7 +575,7 @@ struct Accessor : public Object {
|
||||||
inline size_t GetMaxByteSize();
|
inline size_t GetMaxByteSize();
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void ExtractData(T *&outData);
|
size_t ExtractData(T *&outData, const std::vector<unsigned int> *remappingIndices = nullptr);
|
||||||
|
|
||||||
void WriteData(size_t count, const void *src_buffer, size_t src_stride);
|
void WriteData(size_t count, const void *src_buffer, size_t src_stride);
|
||||||
void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride);
|
void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride);
|
||||||
|
@ -710,6 +720,7 @@ const vec4 defaultBaseColor = { 1, 1, 1, 1 };
|
||||||
const vec3 defaultEmissiveFactor = { 0, 0, 0 };
|
const vec3 defaultEmissiveFactor = { 0, 0, 0 };
|
||||||
const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 };
|
const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 };
|
||||||
const vec3 defaultSpecularFactor = { 1, 1, 1 };
|
const vec3 defaultSpecularFactor = { 1, 1, 1 };
|
||||||
|
const vec3 defaultSpecularColorFactor = { 0, 0, 0 };
|
||||||
const vec3 defaultSheenFactor = { 0, 0, 0 };
|
const vec3 defaultSheenFactor = { 0, 0, 0 };
|
||||||
const vec3 defaultAttenuationColor = { 1, 1, 1 };
|
const vec3 defaultAttenuationColor = { 1, 1, 1 };
|
||||||
|
|
||||||
|
@ -753,6 +764,16 @@ struct PbrSpecularGlossiness {
|
||||||
void SetDefaults();
|
void SetDefaults();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MaterialSpecular {
|
||||||
|
float specularFactor;
|
||||||
|
vec3 specularColorFactor;
|
||||||
|
TextureInfo specularTexture;
|
||||||
|
TextureInfo specularColorTexture;
|
||||||
|
|
||||||
|
MaterialSpecular() { SetDefaults(); }
|
||||||
|
void SetDefaults();
|
||||||
|
};
|
||||||
|
|
||||||
struct MaterialSheen {
|
struct MaterialSheen {
|
||||||
vec3 sheenColorFactor;
|
vec3 sheenColorFactor;
|
||||||
float sheenRoughnessFactor;
|
float sheenRoughnessFactor;
|
||||||
|
@ -817,6 +838,9 @@ struct Material : public Object {
|
||||||
//extension: KHR_materials_pbrSpecularGlossiness
|
//extension: KHR_materials_pbrSpecularGlossiness
|
||||||
Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness;
|
Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness;
|
||||||
|
|
||||||
|
//extension: KHR_materials_specular
|
||||||
|
Nullable<MaterialSpecular> materialSpecular;
|
||||||
|
|
||||||
//extension: KHR_materials_sheen
|
//extension: KHR_materials_sheen
|
||||||
Nullable<MaterialSheen> materialSheen;
|
Nullable<MaterialSheen> materialSheen;
|
||||||
|
|
||||||
|
@ -1099,6 +1123,7 @@ public:
|
||||||
//! Keeps info about the enabled extensions
|
//! Keeps info about the enabled extensions
|
||||||
struct Extensions {
|
struct Extensions {
|
||||||
bool KHR_materials_pbrSpecularGlossiness;
|
bool KHR_materials_pbrSpecularGlossiness;
|
||||||
|
bool KHR_materials_specular;
|
||||||
bool KHR_materials_unlit;
|
bool KHR_materials_unlit;
|
||||||
bool KHR_lights_punctual;
|
bool KHR_lights_punctual;
|
||||||
bool KHR_texture_transform;
|
bool KHR_texture_transform;
|
||||||
|
@ -1114,6 +1139,7 @@ public:
|
||||||
|
|
||||||
Extensions() :
|
Extensions() :
|
||||||
KHR_materials_pbrSpecularGlossiness(false),
|
KHR_materials_pbrSpecularGlossiness(false),
|
||||||
|
KHR_materials_specular(false),
|
||||||
KHR_materials_unlit(false),
|
KHR_materials_unlit(false),
|
||||||
KHR_lights_punctual(false),
|
KHR_lights_punctual(false),
|
||||||
KHR_texture_transform(false),
|
KHR_texture_transform(false),
|
||||||
|
|
|
@ -45,6 +45,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/StringUtils.h>
|
#include <assimp/StringUtils.h>
|
||||||
#include <assimp/DefaultLogger.hpp>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
#include <assimp/Base64.hpp>
|
#include <assimp/Base64.hpp>
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
#include <rapidjson/schema.h>
|
||||||
|
#include <rapidjson/stringbuffer.h>
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#ifdef ASSIMP_ENABLE_DRACO
|
#ifdef ASSIMP_ENABLE_DRACO
|
||||||
|
@ -139,6 +142,18 @@ inline CustomExtension ReadExtensions(const char *name, Value &obj) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Extras ReadExtras(Value &obj) {
|
||||||
|
Extras ret;
|
||||||
|
|
||||||
|
ret.mValues.reserve(obj.MemberCount());
|
||||||
|
for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
|
||||||
|
auto &val = it->value;
|
||||||
|
ret.mValues.emplace_back(ReadExtensions(it->name.GetString(), val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
inline void CopyData(size_t count, const uint8_t *src, size_t src_stride,
|
inline void CopyData(size_t count, const uint8_t *src, size_t src_stride,
|
||||||
uint8_t *dst, size_t dst_stride) {
|
uint8_t *dst, size_t dst_stride) {
|
||||||
if (src_stride == dst_stride) {
|
if (src_stride == dst_stride) {
|
||||||
|
@ -248,7 +263,7 @@ inline void Object::ReadExtensions(Value &val) {
|
||||||
|
|
||||||
inline void Object::ReadExtras(Value &val) {
|
inline void Object::ReadExtras(Value &val) {
|
||||||
if (Value *curExtras = FindObject(val, "extras")) {
|
if (Value *curExtras = FindObject(val, "extras")) {
|
||||||
this->extras = glTF2::ReadExtensions("extras", *curExtras);
|
this->extras = glTF2::ReadExtras(*curExtras);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -962,14 +977,15 @@ inline size_t Accessor::GetMaxByteSize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void Accessor::ExtractData(T *&outData) {
|
size_t Accessor::ExtractData(T *&outData, const std::vector<unsigned int> *remappingIndices) {
|
||||||
uint8_t *data = GetPointer();
|
uint8_t *data = GetPointer();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name));
|
throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const size_t usedCount = (remappingIndices != nullptr) ? remappingIndices->size() : count;
|
||||||
const size_t elemSize = GetElementSize();
|
const size_t elemSize = GetElementSize();
|
||||||
const size_t totalSize = elemSize * count;
|
const size_t totalSize = elemSize * usedCount;
|
||||||
|
|
||||||
const size_t stride = GetStride();
|
const size_t stride = GetStride();
|
||||||
|
|
||||||
|
@ -980,18 +996,31 @@ void Accessor::ExtractData(T *&outData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t maxSize = GetMaxByteSize();
|
const size_t maxSize = GetMaxByteSize();
|
||||||
if (count * stride > maxSize) {
|
|
||||||
throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
outData = new T[count];
|
outData = new T[usedCount];
|
||||||
|
|
||||||
|
if (remappingIndices != nullptr) {
|
||||||
|
const unsigned int maxIndex = static_cast<unsigned int>(maxSize / stride - 1);
|
||||||
|
for (size_t i = 0; i < usedCount; ++i) {
|
||||||
|
size_t srcIdx = (*remappingIndices)[i];
|
||||||
|
if (srcIdx > maxIndex) {
|
||||||
|
throw DeadlyImportError("GLTF: index*stride ", (srcIdx * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
|
||||||
|
}
|
||||||
|
memcpy(outData + i, data + srcIdx * stride, elemSize);
|
||||||
|
}
|
||||||
|
} else { // non-indexed cases
|
||||||
|
if (usedCount * stride > maxSize) {
|
||||||
|
throw DeadlyImportError("GLTF: count*stride ", (usedCount * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
|
||||||
|
}
|
||||||
if (stride == elemSize && targetElemSize == elemSize) {
|
if (stride == elemSize && targetElemSize == elemSize) {
|
||||||
memcpy(outData, data, totalSize);
|
memcpy(outData, data, totalSize);
|
||||||
} else {
|
} else {
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < usedCount; ++i) {
|
||||||
memcpy(outData + i, data + i * stride, elemSize);
|
memcpy(outData + i, data + i * stride, elemSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return usedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) {
|
inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) {
|
||||||
|
@ -1250,6 +1279,19 @@ inline void Material::Read(Value &material, Asset &r) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (r.extensionsUsed.KHR_materials_specular) {
|
||||||
|
if (Value *curMatSpecular = FindObject(*extensions, "KHR_materials_specular")) {
|
||||||
|
MaterialSpecular specular;
|
||||||
|
|
||||||
|
ReadMember(*curMatSpecular, "specularFactor", specular.specularFactor);
|
||||||
|
ReadTextureProperty(r, *curMatSpecular, "specularTexture", specular.specularTexture);
|
||||||
|
ReadMember(*curMatSpecular, "specularColorFactor", specular.specularColorFactor);
|
||||||
|
ReadTextureProperty(r, *curMatSpecular, "specularColorTexture", specular.specularColorTexture);
|
||||||
|
|
||||||
|
this->materialSpecular = Nullable<MaterialSpecular>(specular);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Extension KHR_texture_transform is handled in ReadTextureProperty
|
// Extension KHR_texture_transform is handled in ReadTextureProperty
|
||||||
|
|
||||||
if (r.extensionsUsed.KHR_materials_sheen) {
|
if (r.extensionsUsed.KHR_materials_sheen) {
|
||||||
|
@ -1347,6 +1389,12 @@ inline void PbrSpecularGlossiness::SetDefaults() {
|
||||||
glossinessFactor = 1.0f;
|
glossinessFactor = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void MaterialSpecular::SetDefaults() {
|
||||||
|
//KHR_materials_specular properties
|
||||||
|
SetVector(specularColorFactor, defaultSpecularColorFactor);
|
||||||
|
specularFactor = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
inline void MaterialSheen::SetDefaults() {
|
inline void MaterialSheen::SetDefaults() {
|
||||||
//KHR_materials_sheen properties
|
//KHR_materials_sheen properties
|
||||||
SetVector(sheenColorFactor, defaultSheenFactor);
|
SetVector(sheenColorFactor, defaultSheenFactor);
|
||||||
|
@ -2033,6 +2081,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
|
CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
|
||||||
|
CHECK_EXT(KHR_materials_specular);
|
||||||
CHECK_EXT(KHR_materials_unlit);
|
CHECK_EXT(KHR_materials_unlit);
|
||||||
CHECK_EXT(KHR_lights_punctual);
|
CHECK_EXT(KHR_lights_punctual);
|
||||||
CHECK_EXT(KHR_texture_transform);
|
CHECK_EXT(KHR_texture_transform);
|
||||||
|
|
|
@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* glTF Extensions Support:
|
* glTF Extensions Support:
|
||||||
* KHR_materials_pbrSpecularGlossiness: full
|
* KHR_materials_pbrSpecularGlossiness: full
|
||||||
|
* KHR_materials_specular: full
|
||||||
* KHR_materials_unlit: full
|
* KHR_materials_unlit: full
|
||||||
* KHR_materials_sheen: full
|
* KHR_materials_sheen: full
|
||||||
* KHR_materials_clearcoat: full
|
* KHR_materials_clearcoat: full
|
||||||
|
|
|
@ -418,6 +418,26 @@ namespace glTF2 {
|
||||||
exts.AddMember("KHR_materials_unlit", unlit, w.mAl);
|
exts.AddMember("KHR_materials_unlit", unlit, w.mAl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m.materialSpecular.isPresent) {
|
||||||
|
Value materialSpecular(rapidjson::Type::kObjectType);
|
||||||
|
materialSpecular.SetObject();
|
||||||
|
|
||||||
|
MaterialSpecular &specular = m.materialSpecular.value;
|
||||||
|
|
||||||
|
if (specular.specularFactor != 0.0f) {
|
||||||
|
WriteFloat(materialSpecular, specular.specularFactor, "specularFactor", w.mAl);
|
||||||
|
WriteTex(materialSpecular, specular.specularTexture, "specularTexture", w.mAl);
|
||||||
|
}
|
||||||
|
if (specular.specularColorFactor[0] != defaultSpecularColorFactor[0] && specular.specularColorFactor[1] != defaultSpecularColorFactor[1] && specular.specularColorFactor[2] != defaultSpecularColorFactor[2]) {
|
||||||
|
WriteVec(materialSpecular, specular.specularColorFactor, "specularColorFactor", w.mAl);
|
||||||
|
WriteTex(materialSpecular, specular.specularColorTexture, "specularColorTexture", w.mAl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!materialSpecular.ObjectEmpty()) {
|
||||||
|
exts.AddMember("KHR_materials_specular", materialSpecular, w.mAl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m.materialSheen.isPresent) {
|
if (m.materialSheen.isPresent) {
|
||||||
Value materialSheen(rapidjson::Type::kObjectType);
|
Value materialSheen(rapidjson::Type::kObjectType);
|
||||||
|
|
||||||
|
@ -634,6 +654,44 @@ namespace glTF2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void WriteExtrasValue(Value &parent, const CustomExtension &value, AssetWriter &w) {
|
||||||
|
Value valueNode;
|
||||||
|
|
||||||
|
if (value.mStringValue.isPresent) {
|
||||||
|
MakeValue(valueNode, value.mStringValue.value.c_str(), w.mAl);
|
||||||
|
} else if (value.mDoubleValue.isPresent) {
|
||||||
|
MakeValue(valueNode, value.mDoubleValue.value, w.mAl);
|
||||||
|
} else if (value.mUint64Value.isPresent) {
|
||||||
|
MakeValue(valueNode, value.mUint64Value.value, w.mAl);
|
||||||
|
} else if (value.mInt64Value.isPresent) {
|
||||||
|
MakeValue(valueNode, value.mInt64Value.value, w.mAl);
|
||||||
|
} else if (value.mBoolValue.isPresent) {
|
||||||
|
MakeValue(valueNode, value.mBoolValue.value, w.mAl);
|
||||||
|
} else if (value.mValues.isPresent) {
|
||||||
|
valueNode.SetObject();
|
||||||
|
for (auto const &subvalue : value.mValues.value) {
|
||||||
|
WriteExtrasValue(valueNode, subvalue, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.AddMember(StringRef(value.name), valueNode, w.mAl);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void WriteExtras(Value &obj, const Extras &extras, AssetWriter &w) {
|
||||||
|
if (!extras.HasExtras()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value extrasNode;
|
||||||
|
extrasNode.SetObject();
|
||||||
|
|
||||||
|
for (auto const &value : extras.mValues) {
|
||||||
|
WriteExtrasValue(extrasNode, value, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.AddMember("extras", extrasNode, w.mAl);
|
||||||
|
}
|
||||||
|
|
||||||
inline void Write(Value& obj, Node& n, AssetWriter& w)
|
inline void Write(Value& obj, Node& n, AssetWriter& w)
|
||||||
{
|
{
|
||||||
if (n.matrix.isPresent) {
|
if (n.matrix.isPresent) {
|
||||||
|
@ -669,6 +727,8 @@ namespace glTF2 {
|
||||||
if(n.skeletons.size()) {
|
if(n.skeletons.size()) {
|
||||||
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
|
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WriteExtras(obj, n.extras, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/)
|
inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/)
|
||||||
|
@ -742,7 +802,6 @@ namespace glTF2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline AssetWriter::AssetWriter(Asset& a)
|
inline AssetWriter::AssetWriter(Asset& a)
|
||||||
: mDoc()
|
: mDoc()
|
||||||
, mAsset(a)
|
, mAsset(a)
|
||||||
|
@ -836,7 +895,7 @@ namespace glTF2 {
|
||||||
throw DeadlyExportError("Failed to write scene data!");
|
throw DeadlyExportError("Failed to write scene data!");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t jsonChunkLength = (docBuffer.GetSize() + 3) & ~3; // Round up to next multiple of 4
|
uint32_t jsonChunkLength = static_cast<uint32_t>((docBuffer.GetSize() + 3) & ~3); // Round up to next multiple of 4
|
||||||
auto paddingLength = jsonChunkLength - docBuffer.GetSize();
|
auto paddingLength = jsonChunkLength - docBuffer.GetSize();
|
||||||
|
|
||||||
GLB_Chunk jsonChunk;
|
GLB_Chunk jsonChunk;
|
||||||
|
@ -862,7 +921,7 @@ namespace glTF2 {
|
||||||
int GLB_Chunk_count = 1;
|
int GLB_Chunk_count = 1;
|
||||||
uint32_t binaryChunkLength = 0;
|
uint32_t binaryChunkLength = 0;
|
||||||
if (bodyBuffer->byteLength > 0) {
|
if (bodyBuffer->byteLength > 0) {
|
||||||
binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4
|
binaryChunkLength = static_cast<uint32_t>((bodyBuffer->byteLength + 3) & ~3); // Round up to next multiple of 4
|
||||||
|
|
||||||
auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength;
|
auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength;
|
||||||
++GLB_Chunk_count;
|
++GLB_Chunk_count;
|
||||||
|
@ -929,6 +988,10 @@ namespace glTF2 {
|
||||||
exts.PushBack(StringRef("KHR_materials_unlit"), mAl);
|
exts.PushBack(StringRef("KHR_materials_unlit"), mAl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->mAsset.extensionsUsed.KHR_materials_specular) {
|
||||||
|
exts.PushBack(StringRef("KHR_materials_specular"), mAl);
|
||||||
|
}
|
||||||
|
|
||||||
if (this->mAsset.extensionsUsed.KHR_materials_sheen) {
|
if (this->mAsset.extensionsUsed.KHR_materials_sheen) {
|
||||||
exts.PushBack(StringRef("KHR_materials_sheen"), mAl);
|
exts.PushBack(StringRef("KHR_materials_sheen"), mAl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,7 +263,7 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
|
||||||
for (short idx = 0; bufferData_ptr < bufferData_end; idx += 1, bufferData_ptr += numCompsIn) {
|
for (short idx = 0; bufferData_ptr < bufferData_end; idx += 1, bufferData_ptr += numCompsIn) {
|
||||||
bool bNonZero = false;
|
bool bNonZero = false;
|
||||||
|
|
||||||
//for the data, check any component Non Zero
|
// for the data, check any component Non Zero
|
||||||
for (unsigned int j = 0; j < numCompsOut; j++) {
|
for (unsigned int j = 0; j < numCompsOut; j++) {
|
||||||
double valueData = bufferData_ptr[j];
|
double valueData = bufferData_ptr[j];
|
||||||
double valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
|
double valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
|
||||||
|
@ -273,11 +273,11 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//all zeros, continue
|
// all zeros, continue
|
||||||
if (!bNonZero)
|
if (!bNonZero)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
//non zero, store the data
|
// non zero, store the data
|
||||||
for (unsigned int j = 0; j < numCompsOut; j++) {
|
for (unsigned int j = 0; j < numCompsOut; j++) {
|
||||||
T valueData = bufferData_ptr[j];
|
T valueData = bufferData_ptr[j];
|
||||||
T valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
|
T valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
|
||||||
|
@ -286,14 +286,14 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
|
||||||
vNZIdx.push_back(idx);
|
vNZIdx.push_back(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
//avoid all-0, put 1 item
|
// avoid all-0, put 1 item
|
||||||
if (vNZDiff.size() == 0) {
|
if (vNZDiff.size() == 0) {
|
||||||
for (unsigned int j = 0; j < numCompsOut; j++)
|
for (unsigned int j = 0; j < numCompsOut; j++)
|
||||||
vNZDiff.push_back(0);
|
vNZDiff.push_back(0);
|
||||||
vNZIdx.push_back(0);
|
vNZIdx.push_back(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//process data
|
// process data
|
||||||
outputNZDiff = new T[vNZDiff.size()];
|
outputNZDiff = new T[vNZDiff.size()];
|
||||||
memcpy(outputNZDiff, vNZDiff.data(), vNZDiff.size() * sizeof(T));
|
memcpy(outputNZDiff, vNZDiff.data(), vNZDiff.size() * sizeof(T));
|
||||||
|
|
||||||
|
@ -361,7 +361,7 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
|
||||||
acc->sparse.reset(new Accessor::Sparse);
|
acc->sparse.reset(new Accessor::Sparse);
|
||||||
acc->sparse->count = nzCount;
|
acc->sparse->count = nzCount;
|
||||||
|
|
||||||
//indices
|
// indices
|
||||||
unsigned int bytesPerIdx = sizeof(unsigned short);
|
unsigned int bytesPerIdx = sizeof(unsigned short);
|
||||||
size_t indices_offset = buffer->byteLength;
|
size_t indices_offset = buffer->byteLength;
|
||||||
size_t indices_padding = indices_offset % bytesPerIdx;
|
size_t indices_padding = indices_offset % bytesPerIdx;
|
||||||
|
@ -379,7 +379,7 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
|
||||||
acc->sparse->indicesByteOffset = 0;
|
acc->sparse->indicesByteOffset = 0;
|
||||||
acc->WriteSparseIndices(nzCount, nzIdx, 1 * bytesPerIdx);
|
acc->WriteSparseIndices(nzCount, nzIdx, 1 * bytesPerIdx);
|
||||||
|
|
||||||
//values
|
// values
|
||||||
size_t values_offset = buffer->byteLength;
|
size_t values_offset = buffer->byteLength;
|
||||||
size_t values_padding = values_offset % bytesPerComp;
|
size_t values_padding = values_offset % bytesPerComp;
|
||||||
values_offset += values_padding;
|
values_offset += values_padding;
|
||||||
|
@ -395,9 +395,9 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
|
||||||
acc->sparse->valuesByteOffset = 0;
|
acc->sparse->valuesByteOffset = 0;
|
||||||
acc->WriteSparseValues(nzCount, nzDiff, numCompsIn * bytesPerComp);
|
acc->WriteSparseValues(nzCount, nzDiff, numCompsIn * bytesPerComp);
|
||||||
|
|
||||||
//clear
|
// clear
|
||||||
delete[](char *) nzDiff;
|
delete[] (char *)nzDiff;
|
||||||
delete[](char *) nzIdx;
|
delete[] (char *)nzIdx;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
@ -443,6 +443,61 @@ inline Ref<Accessor> ExportData(Asset &a, std::string &meshName, Ref<Buffer> &bu
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void ExportNodeExtras(const aiMetadataEntry &metadataEntry, aiString name, CustomExtension &value) {
|
||||||
|
|
||||||
|
value.name = name.C_Str();
|
||||||
|
switch (metadataEntry.mType) {
|
||||||
|
case AI_BOOL:
|
||||||
|
value.mBoolValue.value = *static_cast<bool *>(metadataEntry.mData);
|
||||||
|
value.mBoolValue.isPresent = true;
|
||||||
|
break;
|
||||||
|
case AI_INT32:
|
||||||
|
value.mInt64Value.value = *static_cast<int32_t *>(metadataEntry.mData);
|
||||||
|
value.mInt64Value.isPresent = true;
|
||||||
|
break;
|
||||||
|
case AI_UINT64:
|
||||||
|
value.mUint64Value.value = *static_cast<uint64_t *>(metadataEntry.mData);
|
||||||
|
value.mUint64Value.isPresent = true;
|
||||||
|
break;
|
||||||
|
case AI_FLOAT:
|
||||||
|
value.mDoubleValue.value = *static_cast<float *>(metadataEntry.mData);
|
||||||
|
value.mDoubleValue.isPresent = true;
|
||||||
|
break;
|
||||||
|
case AI_DOUBLE:
|
||||||
|
value.mDoubleValue.value = *static_cast<double *>(metadataEntry.mData);
|
||||||
|
value.mDoubleValue.isPresent = true;
|
||||||
|
break;
|
||||||
|
case AI_AISTRING:
|
||||||
|
value.mStringValue.value = static_cast<aiString *>(metadataEntry.mData)->C_Str();
|
||||||
|
value.mStringValue.isPresent = true;
|
||||||
|
break;
|
||||||
|
case AI_AIMETADATA: {
|
||||||
|
const aiMetadata *subMetadata = static_cast<aiMetadata *>(metadataEntry.mData);
|
||||||
|
value.mValues.value.resize(subMetadata->mNumProperties);
|
||||||
|
value.mValues.isPresent = true;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < subMetadata->mNumProperties; ++i) {
|
||||||
|
ExportNodeExtras(subMetadata->mValues[i], subMetadata->mKeys[i], value.mValues.value.at(i));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// AI_AIVECTOR3D not handled
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ExportNodeExtras(const aiMetadata *metadata, Extras &extras) {
|
||||||
|
if (metadata == nullptr || metadata->mNumProperties == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
extras.mValues.resize(metadata->mNumProperties);
|
||||||
|
for (unsigned int i = 0; i < metadata->mNumProperties; ++i) {
|
||||||
|
ExportNodeExtras(metadata->mValues[i], metadata->mKeys[i], extras.mValues.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void SetSamplerWrap(SamplerWrap &wrap, aiTextureMapMode map) {
|
inline void SetSamplerWrap(SamplerWrap &wrap, aiTextureMapMode map) {
|
||||||
switch (map) {
|
switch (map) {
|
||||||
case aiTextureMapMode_Clamp:
|
case aiTextureMapMode_Clamp:
|
||||||
|
@ -544,7 +599,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
|
||||||
if (curTex != nullptr) { // embedded
|
if (curTex != nullptr) { // embedded
|
||||||
texture->source->name = curTex->mFilename.C_Str();
|
texture->source->name = curTex->mFilename.C_Str();
|
||||||
|
|
||||||
//basisu: embedded ktx2, bu
|
// basisu: embedded ktx2, bu
|
||||||
if (curTex->achFormatHint[0]) {
|
if (curTex->achFormatHint[0]) {
|
||||||
std::string mimeType = "image/";
|
std::string mimeType = "image/";
|
||||||
if (memcmp(curTex->achFormatHint, "jpg", 3) == 0)
|
if (memcmp(curTex->achFormatHint, "jpg", 3) == 0)
|
||||||
|
@ -564,7 +619,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
|
||||||
}
|
}
|
||||||
|
|
||||||
// The asset has its own buffer, see Image::SetData
|
// The asset has its own buffer, see Image::SetData
|
||||||
//basisu: "image/ktx2", "image/basis" as is
|
// basisu: "image/ktx2", "image/basis" as is
|
||||||
texture->source->SetData(reinterpret_cast<uint8_t *>(curTex->pcData), curTex->mWidth, *mAsset);
|
texture->source->SetData(reinterpret_cast<uint8_t *>(curTex->pcData), curTex->mWidth, *mAsset);
|
||||||
} else {
|
} else {
|
||||||
texture->source->uri = path;
|
texture->source->uri = path;
|
||||||
|
@ -574,7 +629,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//basisu
|
// basisu
|
||||||
if (useBasisUniversal) {
|
if (useBasisUniversal) {
|
||||||
mAsset->extensionsUsed.KHR_texture_basisu = true;
|
mAsset->extensionsUsed.KHR_texture_basisu = true;
|
||||||
mAsset->extensionsRequired.KHR_texture_basisu = true;
|
mAsset->extensionsRequired.KHR_texture_basisu = true;
|
||||||
|
@ -597,7 +652,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, NormalTextureInfo &prop, ai
|
||||||
GetMatTex(mat, texture, prop.texCoord, tt, slot);
|
GetMatTex(mat, texture, prop.texCoord, tt, slot);
|
||||||
|
|
||||||
if (texture) {
|
if (texture) {
|
||||||
//GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
|
// GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
|
||||||
GetMatTexProp(mat, prop.scale, "scale", tt, slot);
|
GetMatTexProp(mat, prop.scale, "scale", tt, slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -608,7 +663,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, OcclusionTextureInfo &prop,
|
||||||
GetMatTex(mat, texture, prop.texCoord, tt, slot);
|
GetMatTex(mat, texture, prop.texCoord, tt, slot);
|
||||||
|
|
||||||
if (texture) {
|
if (texture) {
|
||||||
//GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
|
// GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
|
||||||
GetMatTexProp(mat, prop.strength, "strength", tt, slot);
|
GetMatTexProp(mat, prop.strength, "strength", tt, slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -640,11 +695,10 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial &mat, vec3 &prop, const cha
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This extension has been deprecated, only export with the specific flag enabled, defaults to false. Uses KHR_material_specular default.
|
||||||
bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) {
|
bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
// If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension
|
// If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension
|
||||||
// NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular
|
|
||||||
|
|
||||||
if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) {
|
if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) {
|
||||||
result = true;
|
result = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -674,6 +728,25 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool glTF2Exporter::GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular) {
|
||||||
|
// Specular requires either/or, default factors of zero disables specular, so do not export
|
||||||
|
if (GetMatColor(mat, specular.specularColorFactor, AI_MATKEY_COLOR_SPECULAR) != AI_SUCCESS && mat.Get(AI_MATKEY_SPECULAR_FACTOR, specular.specularFactor) != AI_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// The spec states that the default is 1.0 and [1.0, 1.0, 1.0]. We if both are 0, which should disable specular. Otherwise, if one is 0, set to 1.0
|
||||||
|
const bool colorFactorIsZero = specular.specularColorFactor[0] == defaultSpecularColorFactor[0] && specular.specularColorFactor[1] == defaultSpecularColorFactor[1] && specular.specularColorFactor[2] == defaultSpecularColorFactor[2];
|
||||||
|
if (specular.specularFactor == 0.0f && colorFactorIsZero) {
|
||||||
|
return false;
|
||||||
|
} else if (specular.specularFactor == 0.0f) {
|
||||||
|
specular.specularFactor = 1.0f;
|
||||||
|
} else if (colorFactorIsZero) {
|
||||||
|
specular.specularColorFactor[0] = specular.specularColorFactor[1] = specular.specularColorFactor[2] = 1.0f;
|
||||||
|
}
|
||||||
|
GetMatTex(mat, specular.specularColorTexture, aiTextureType_SPECULAR);
|
||||||
|
GetMatTex(mat, specular.specularTexture, aiTextureType_SPECULAR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) {
|
bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) {
|
||||||
// Return true if got any valid Sheen properties or textures
|
// Return true if got any valid Sheen properties or textures
|
||||||
if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) {
|
if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) {
|
||||||
|
@ -759,20 +832,30 @@ void glTF2Exporter::ExportMaterials() {
|
||||||
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR);
|
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR);
|
||||||
|
|
||||||
if (!m->pbrMetallicRoughness.baseColorTexture.texture) {
|
if (!m->pbrMetallicRoughness.baseColorTexture.texture) {
|
||||||
//if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture
|
// if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture
|
||||||
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE);
|
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_DIFFUSE_ROUGHNESS);
|
||||||
|
|
||||||
|
if (!m->pbrMetallicRoughness.metallicRoughnessTexture.texture) {
|
||||||
|
// if there wasn't a aiTextureType_DIFFUSE_ROUGHNESS defined in the source, fallback to aiTextureType_METALNESS
|
||||||
|
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_METALNESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m->pbrMetallicRoughness.metallicRoughnessTexture.texture) {
|
||||||
|
// if there still wasn't a aiTextureType_METALNESS defined in the source, fallback to AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE
|
||||||
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
|
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
|
||||||
|
}
|
||||||
|
|
||||||
if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) {
|
if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) {
|
||||||
// if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material.
|
// if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material.
|
||||||
//a fallback to any diffuse color should be used instead
|
// a fallback to any diffuse color should be used instead
|
||||||
GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
|
GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) {
|
if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) {
|
||||||
//if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
|
// if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
|
||||||
m->pbrMetallicRoughness.metallicFactor = 0;
|
m->pbrMetallicRoughness.metallicFactor = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,10 +868,10 @@ void glTF2Exporter::ExportMaterials() {
|
||||||
if (mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) {
|
if (mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) {
|
||||||
// convert specular color to luminance
|
// convert specular color to luminance
|
||||||
float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f;
|
float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f;
|
||||||
//normalize shininess (assuming max is 1000) with an inverse exponentional curve
|
// normalize shininess (assuming max is 1000) with an inverse exponentional curve
|
||||||
float normalizedShininess = std::sqrt(shininess / 1000);
|
float normalizedShininess = std::sqrt(shininess / 1000);
|
||||||
|
|
||||||
//clamp the shininess value between 0 and 1
|
// clamp the shininess value between 0 and 1
|
||||||
normalizedShininess = std::min(std::max(normalizedShininess, 0.0f), 1.0f);
|
normalizedShininess = std::min(std::max(normalizedShininess, 0.0f), 1.0f);
|
||||||
// low specular intensity values should produce a rough material even if shininess is high.
|
// low specular intensity values should produce a rough material even if shininess is high.
|
||||||
normalizedShininess = normalizedShininess * specularIntensity;
|
normalizedShininess = normalizedShininess * specularIntensity;
|
||||||
|
@ -818,9 +901,9 @@ void glTF2Exporter::ExportMaterials() {
|
||||||
m->alphaMode = alphaMode.C_Str();
|
m->alphaMode = alphaMode.C_Str();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
// This extension has been deprecated, only export with the specific flag enabled, defaults to false. Uses KHR_material_specular default.
|
||||||
|
if (mProperties->GetPropertyBool(AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS)) {
|
||||||
// KHR_materials_pbrSpecularGlossiness extension
|
// KHR_materials_pbrSpecularGlossiness extension
|
||||||
// NOTE: This extension is being considered for deprecation (Dec 2020)
|
|
||||||
PbrSpecularGlossiness pbrSG;
|
PbrSpecularGlossiness pbrSG;
|
||||||
if (GetMatSpecGloss(mat, pbrSG)) {
|
if (GetMatSpecGloss(mat, pbrSG)) {
|
||||||
mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
|
mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
|
||||||
|
@ -837,7 +920,12 @@ void glTF2Exporter::ExportMaterials() {
|
||||||
} else {
|
} else {
|
||||||
// These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness
|
// These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness
|
||||||
if (!m->pbrSpecularGlossiness.isPresent) {
|
if (!m->pbrSpecularGlossiness.isPresent) {
|
||||||
// Sheen
|
MaterialSpecular specular;
|
||||||
|
if (GetMatSpecular(mat, specular)) {
|
||||||
|
mAsset->extensionsUsed.KHR_materials_specular = true;
|
||||||
|
m->materialSpecular = Nullable<MaterialSpecular>(specular);
|
||||||
|
}
|
||||||
|
|
||||||
MaterialSheen sheen;
|
MaterialSheen sheen;
|
||||||
if (GetMatSheen(mat, sheen)) {
|
if (GetMatSheen(mat, sheen)) {
|
||||||
mAsset->extensionsUsed.KHR_materials_sheen = true;
|
mAsset->extensionsUsed.KHR_materials_sheen = true;
|
||||||
|
@ -981,7 +1069,7 @@ void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buf
|
||||||
if (boneIndexFitted != -1) {
|
if (boneIndexFitted != -1) {
|
||||||
vertexJointData[vertexId][boneIndexFitted] = static_cast<float>(jointNamesIndex);
|
vertexJointData[vertexId][boneIndexFitted] = static_cast<float>(jointNamesIndex);
|
||||||
}
|
}
|
||||||
}else {
|
} else {
|
||||||
vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex);
|
vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex);
|
||||||
vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight;
|
vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight;
|
||||||
|
|
||||||
|
@ -1284,24 +1372,24 @@ void glTF2Exporter::MergeMeshes() {
|
||||||
|
|
||||||
unsigned int nMeshes = static_cast<unsigned int>(node->meshes.size());
|
unsigned int nMeshes = static_cast<unsigned int>(node->meshes.size());
|
||||||
|
|
||||||
//skip if it's 1 or less meshes per node
|
// skip if it's 1 or less meshes per node
|
||||||
if (nMeshes > 1) {
|
if (nMeshes > 1) {
|
||||||
Ref<Mesh> firstMesh = node->meshes.at(0);
|
Ref<Mesh> firstMesh = node->meshes.at(0);
|
||||||
|
|
||||||
//loop backwards to allow easy removal of a mesh from a node once it's merged
|
// loop backwards to allow easy removal of a mesh from a node once it's merged
|
||||||
for (unsigned int m = nMeshes - 1; m >= 1; --m) {
|
for (unsigned int m = nMeshes - 1; m >= 1; --m) {
|
||||||
Ref<Mesh> mesh = node->meshes.at(m);
|
Ref<Mesh> mesh = node->meshes.at(m);
|
||||||
|
|
||||||
//append this mesh's primitives to the first mesh's primitives
|
// append this mesh's primitives to the first mesh's primitives
|
||||||
firstMesh->primitives.insert(
|
firstMesh->primitives.insert(
|
||||||
firstMesh->primitives.end(),
|
firstMesh->primitives.end(),
|
||||||
mesh->primitives.begin(),
|
mesh->primitives.begin(),
|
||||||
mesh->primitives.end());
|
mesh->primitives.end());
|
||||||
|
|
||||||
//remove the mesh from the list of meshes
|
// remove the mesh from the list of meshes
|
||||||
unsigned int removedIndex = mAsset->meshes.Remove(mesh->id.c_str());
|
unsigned int removedIndex = mAsset->meshes.Remove(mesh->id.c_str());
|
||||||
|
|
||||||
//find the presence of the removed mesh in other nodes
|
// find the presence of the removed mesh in other nodes
|
||||||
for (unsigned int nn = 0; nn < mAsset->nodes.Size(); ++nn) {
|
for (unsigned int nn = 0; nn < mAsset->nodes.Size(); ++nn) {
|
||||||
Ref<Node> curNode = mAsset->nodes.Get(nn);
|
Ref<Node> curNode = mAsset->nodes.Get(nn);
|
||||||
|
|
||||||
|
@ -1320,7 +1408,7 @@ void glTF2Exporter::MergeMeshes() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//since we were looping backwards, reverse the order of merged primitives to their original order
|
// since we were looping backwards, reverse the order of merged primitives to their original order
|
||||||
std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end());
|
std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1363,6 +1451,8 @@ unsigned int glTF2Exporter::ExportNode(const aiNode *n, Ref<Node> &parent) {
|
||||||
node->parent = parent;
|
node->parent = parent;
|
||||||
node->name = name;
|
node->name = name;
|
||||||
|
|
||||||
|
ExportNodeExtras(n->mMetaData, node->extras);
|
||||||
|
|
||||||
if (!n->mTransformation.IsIdentity()) {
|
if (!n->mTransformation.IsIdentity()) {
|
||||||
if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) {
|
if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) {
|
||||||
aiQuaternion quaternion;
|
aiQuaternion quaternion;
|
||||||
|
@ -1445,9 +1535,9 @@ inline void ExtractTranslationSampler(Asset &asset, std::string &animId, Ref<Buf
|
||||||
const aiVectorKey &key = nodeChannel->mPositionKeys[i];
|
const aiVectorKey &key = nodeChannel->mPositionKeys[i];
|
||||||
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
|
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
|
||||||
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
|
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
|
||||||
values[(i * 3) + 0] = (ai_real) key.mValue.x;
|
values[(i * 3) + 0] = (ai_real)key.mValue.x;
|
||||||
values[(i * 3) + 1] = (ai_real) key.mValue.y;
|
values[(i * 3) + 1] = (ai_real)key.mValue.y;
|
||||||
values[(i * 3) + 2] = (ai_real) key.mValue.z;
|
values[(i * 3) + 2] = (ai_real)key.mValue.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
|
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
|
||||||
|
@ -1464,9 +1554,9 @@ inline void ExtractScaleSampler(Asset &asset, std::string &animId, Ref<Buffer> &
|
||||||
const aiVectorKey &key = nodeChannel->mScalingKeys[i];
|
const aiVectorKey &key = nodeChannel->mScalingKeys[i];
|
||||||
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
|
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
|
||||||
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
|
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
|
||||||
values[(i * 3) + 0] = (ai_real) key.mValue.x;
|
values[(i * 3) + 0] = (ai_real)key.mValue.x;
|
||||||
values[(i * 3) + 1] = (ai_real) key.mValue.y;
|
values[(i * 3) + 1] = (ai_real)key.mValue.y;
|
||||||
values[(i * 3) + 2] = (ai_real) key.mValue.z;
|
values[(i * 3) + 2] = (ai_real)key.mValue.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
|
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
|
||||||
|
@ -1483,10 +1573,10 @@ inline void ExtractRotationSampler(Asset &asset, std::string &animId, Ref<Buffer
|
||||||
const aiQuatKey &key = nodeChannel->mRotationKeys[i];
|
const aiQuatKey &key = nodeChannel->mRotationKeys[i];
|
||||||
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
|
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
|
||||||
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
|
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
|
||||||
values[(i * 4) + 0] = (ai_real) key.mValue.x;
|
values[(i * 4) + 0] = (ai_real)key.mValue.x;
|
||||||
values[(i * 4) + 1] = (ai_real) key.mValue.y;
|
values[(i * 4) + 1] = (ai_real)key.mValue.y;
|
||||||
values[(i * 4) + 2] = (ai_real) key.mValue.z;
|
values[(i * 4) + 2] = (ai_real)key.mValue.z;
|
||||||
values[(i * 4) + 3] = (ai_real) key.mValue.w;
|
values[(i * 4) + 3] = (ai_real)key.mValue.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
|
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
|
||||||
|
|
|
@ -76,6 +76,7 @@ struct OcclusionTextureInfo;
|
||||||
struct Node;
|
struct Node;
|
||||||
struct Texture;
|
struct Texture;
|
||||||
struct PbrSpecularGlossiness;
|
struct PbrSpecularGlossiness;
|
||||||
|
struct MaterialSpecular;
|
||||||
struct MaterialSheen;
|
struct MaterialSheen;
|
||||||
struct MaterialClearcoat;
|
struct MaterialClearcoat;
|
||||||
struct MaterialTransmission;
|
struct MaterialTransmission;
|
||||||
|
@ -117,6 +118,7 @@ protected:
|
||||||
aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec4 &prop, const char *propName, int type, int idx) const;
|
aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec4 &prop, const char *propName, int type, int idx) const;
|
||||||
aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec3 &prop, const char *propName, int type, int idx) const;
|
aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec3 &prop, const char *propName, int type, int idx) const;
|
||||||
bool GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG);
|
bool GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG);
|
||||||
|
bool GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular);
|
||||||
bool GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen);
|
bool GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen);
|
||||||
bool GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat);
|
bool GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat);
|
||||||
bool GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission);
|
bool GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission);
|
||||||
|
|
|
@ -100,8 +100,6 @@ glTF2Importer::glTF2Importer() :
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
glTF2Importer::~glTF2Importer() = default;
|
|
||||||
|
|
||||||
const aiImporterDesc *glTF2Importer::GetInfo() const {
|
const aiImporterDesc *glTF2Importer::GetInfo() const {
|
||||||
return &desc;
|
return &desc;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +112,11 @@ bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, b
|
||||||
|
|
||||||
if (pIOHandler) {
|
if (pIOHandler) {
|
||||||
glTF2::Asset asset(pIOHandler);
|
glTF2::Asset asset(pIOHandler);
|
||||||
return asset.CanRead(filename, extension == "glb");
|
return asset.CanRead(
|
||||||
|
filename,
|
||||||
|
CheckMagicToken(
|
||||||
|
pIOHandler, filename, AI_GLB_MAGIC_NUMBER, 1, 0,
|
||||||
|
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -232,7 +234,8 @@ inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
|
||||||
SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
|
SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
|
||||||
|
|
||||||
if (prop.texture && prop.texture->source) {
|
if (prop.texture && prop.texture->source) {
|
||||||
mat->AddProperty(&prop.strength, 1, AI_MATKEY_GLTF_TEXTURE_STRENGTH(texType, texSlot));
|
std::string textureStrengthKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + "strength";
|
||||||
|
mat->AddProperty(&prop.strength, 1, textureStrengthKey.c_str(), texType, texSlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,8 +281,19 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
|
||||||
aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
|
aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
|
||||||
aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
|
aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
|
||||||
|
|
||||||
|
// KHR_materials_specular
|
||||||
|
if (mat.materialSpecular.isPresent) {
|
||||||
|
MaterialSpecular &specular = mat.materialSpecular.value;
|
||||||
|
// Default values of zero disables Specular
|
||||||
|
if (std::memcmp(specular.specularColorFactor, defaultSpecularColorFactor, sizeof(glTFCommon::vec3)) != 0 || specular.specularFactor != 0.0f) {
|
||||||
|
SetMaterialColorProperty(r, specular.specularColorFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
|
||||||
|
aimat->AddProperty(&specular.specularFactor, 1, AI_MATKEY_SPECULAR_FACTOR);
|
||||||
|
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularTexture, aimat, aiTextureType_SPECULAR);
|
||||||
|
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularColorTexture, aimat, aiTextureType_SPECULAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
// pbrSpecularGlossiness
|
// pbrSpecularGlossiness
|
||||||
if (mat.pbrSpecularGlossiness.isPresent) {
|
else if (mat.pbrSpecularGlossiness.isPresent) {
|
||||||
PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
|
PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
|
||||||
|
|
||||||
SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
|
SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
|
@ -432,10 +446,10 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
|
||||||
#endif // ASSIMP_BUILD_DEBUG
|
#endif // ASSIMP_BUILD_DEBUG
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
aiColor4D *GetVertexColorsForType(Ref<Accessor> input) {
|
aiColor4D *GetVertexColorsForType(Ref<Accessor> input, std::vector<unsigned int> *vertexRemappingTable) {
|
||||||
constexpr float max = std::numeric_limits<T>::max();
|
constexpr float max = std::numeric_limits<T>::max();
|
||||||
aiColor4t<T> *colors;
|
aiColor4t<T> *colors;
|
||||||
input->ExtractData(colors);
|
input->ExtractData(colors, vertexRemappingTable);
|
||||||
auto output = new aiColor4D[input->count];
|
auto output = new aiColor4D[input->count];
|
||||||
for (size_t i = 0; i < input->count; i++) {
|
for (size_t i = 0; i < input->count; i++) {
|
||||||
output[i] = aiColor4D(
|
output[i] = aiColor4D(
|
||||||
|
@ -450,18 +464,73 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes");
|
ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes");
|
||||||
std::vector<std::unique_ptr<aiMesh>> meshes;
|
std::vector<std::unique_ptr<aiMesh>> meshes;
|
||||||
|
|
||||||
unsigned int k = 0;
|
|
||||||
meshOffsets.clear();
|
meshOffsets.clear();
|
||||||
|
meshOffsets.reserve(r.meshes.Size() + 1);
|
||||||
|
mVertexRemappingTables.clear();
|
||||||
|
|
||||||
|
// Count the number of aiMeshes
|
||||||
|
unsigned int num_aiMeshes = 0;
|
||||||
|
for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
|
||||||
|
meshOffsets.push_back(num_aiMeshes);
|
||||||
|
num_aiMeshes += unsigned(r.meshes[m].primitives.size());
|
||||||
|
}
|
||||||
|
meshOffsets.push_back(num_aiMeshes); // add a last element so we can always do meshOffsets[n+1] - meshOffsets[n]
|
||||||
|
|
||||||
|
std::vector<unsigned int> reverseMappingIndices;
|
||||||
|
std::vector<unsigned int> indexBuffer;
|
||||||
|
meshes.reserve(num_aiMeshes);
|
||||||
|
mVertexRemappingTables.resize(num_aiMeshes);
|
||||||
|
|
||||||
for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
|
for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
|
||||||
Mesh &mesh = r.meshes[m];
|
Mesh &mesh = r.meshes[m];
|
||||||
|
|
||||||
meshOffsets.push_back(k);
|
|
||||||
k += unsigned(mesh.primitives.size());
|
|
||||||
|
|
||||||
for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
|
for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
|
||||||
Mesh::Primitive &prim = mesh.primitives[p];
|
Mesh::Primitive &prim = mesh.primitives[p];
|
||||||
|
|
||||||
|
Mesh::Primitive::Attributes &attr = prim.attributes;
|
||||||
|
|
||||||
|
// Find out the maximum number of vertices:
|
||||||
|
size_t numAllVertices = 0;
|
||||||
|
if (!attr.position.empty() && attr.position[0]) {
|
||||||
|
numAllVertices = attr.position[0]->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract used vertices:
|
||||||
|
bool useIndexBuffer = prim.indices;
|
||||||
|
std::vector<unsigned int> *vertexRemappingTable = nullptr;
|
||||||
|
|
||||||
|
if (useIndexBuffer) {
|
||||||
|
size_t count = prim.indices->count;
|
||||||
|
indexBuffer.resize(count);
|
||||||
|
reverseMappingIndices.clear();
|
||||||
|
vertexRemappingTable = &mVertexRemappingTables[meshes.size()];
|
||||||
|
vertexRemappingTable->reserve(count / 3); // this is a very rough heuristic to reduce re-allocations
|
||||||
|
Accessor::Indexer data = prim.indices->GetIndexer();
|
||||||
|
if (!data.IsValid()) {
|
||||||
|
throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the vertex remapping table and the modified index buffer (used later instead of the original one)
|
||||||
|
// In case no index buffer is used, the original vertex arrays are being used so no remapping is required in the first place.
|
||||||
|
const unsigned int unusedIndex = ~0u;
|
||||||
|
for (unsigned int i = 0; i < count; ++i) {
|
||||||
|
unsigned int index = data.GetUInt(i);
|
||||||
|
if (index >= numAllVertices) {
|
||||||
|
// Out-of-range indices will be filtered out when adding the faces and then lead to a warning. At this stage, we just keep them.
|
||||||
|
indexBuffer[i] = index;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (index >= reverseMappingIndices.size()) {
|
||||||
|
reverseMappingIndices.resize(index + 1, unusedIndex);
|
||||||
|
}
|
||||||
|
if (reverseMappingIndices[index] == unusedIndex) {
|
||||||
|
reverseMappingIndices[index] = static_cast<unsigned int>(vertexRemappingTable->size());
|
||||||
|
vertexRemappingTable->push_back(index);
|
||||||
|
}
|
||||||
|
indexBuffer[i] = reverseMappingIndices[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
aiMesh *aim = new aiMesh();
|
aiMesh *aim = new aiMesh();
|
||||||
meshes.push_back(std::unique_ptr<aiMesh>(aim));
|
meshes.push_back(std::unique_ptr<aiMesh>(aim));
|
||||||
|
|
||||||
|
@ -491,28 +560,25 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh::Primitive::Attributes &attr = prim.attributes;
|
|
||||||
|
|
||||||
if (!attr.position.empty() && attr.position[0]) {
|
if (!attr.position.empty() && attr.position[0]) {
|
||||||
aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->count);
|
aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->ExtractData(aim->mVertices, vertexRemappingTable));
|
||||||
attr.position[0]->ExtractData(aim->mVertices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!attr.normal.empty() && attr.normal[0]) {
|
if (!attr.normal.empty() && attr.normal[0]) {
|
||||||
if (attr.normal[0]->count != aim->mNumVertices) {
|
if (attr.normal[0]->count != numAllVertices) {
|
||||||
DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored.");
|
DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored.");
|
||||||
} else {
|
} else {
|
||||||
attr.normal[0]->ExtractData(aim->mNormals);
|
attr.normal[0]->ExtractData(aim->mNormals, vertexRemappingTable);
|
||||||
|
|
||||||
// only extract tangents if normals are present
|
// only extract tangents if normals are present
|
||||||
if (!attr.tangent.empty() && attr.tangent[0]) {
|
if (!attr.tangent.empty() && attr.tangent[0]) {
|
||||||
if (attr.tangent[0]->count != aim->mNumVertices) {
|
if (attr.tangent[0]->count != numAllVertices) {
|
||||||
DefaultLogger::get()->warn("Tangent count in mesh \"", mesh.name, "\" does not match the vertex count, tangents ignored.");
|
DefaultLogger::get()->warn("Tangent count in mesh \"", mesh.name, "\" does not match the vertex count, tangents ignored.");
|
||||||
} else {
|
} else {
|
||||||
// generate bitangents from normals and tangents according to spec
|
// generate bitangents from normals and tangents according to spec
|
||||||
Tangent *tangents = nullptr;
|
Tangent *tangents = nullptr;
|
||||||
|
|
||||||
attr.tangent[0]->ExtractData(tangents);
|
attr.tangent[0]->ExtractData(tangents, vertexRemappingTable);
|
||||||
|
|
||||||
aim->mTangents = new aiVector3D[aim->mNumVertices];
|
aim->mTangents = new aiVector3D[aim->mNumVertices];
|
||||||
aim->mBitangents = new aiVector3D[aim->mNumVertices];
|
aim->mBitangents = new aiVector3D[aim->mNumVertices];
|
||||||
|
@ -529,7 +595,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) {
|
for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) {
|
||||||
if (attr.color[c]->count != aim->mNumVertices) {
|
if (attr.color[c]->count != numAllVertices) {
|
||||||
DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name,
|
DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name,
|
||||||
"\" does not match the vertex count");
|
"\" does not match the vertex count");
|
||||||
continue;
|
continue;
|
||||||
|
@ -537,12 +603,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
|
|
||||||
auto componentType = attr.color[c]->componentType;
|
auto componentType = attr.color[c]->componentType;
|
||||||
if (componentType == glTF2::ComponentType_FLOAT) {
|
if (componentType == glTF2::ComponentType_FLOAT) {
|
||||||
attr.color[c]->ExtractData(aim->mColors[c]);
|
attr.color[c]->ExtractData(aim->mColors[c], vertexRemappingTable);
|
||||||
} else {
|
} else {
|
||||||
if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
|
if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
|
||||||
aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]);
|
aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c], vertexRemappingTable);
|
||||||
} else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) {
|
} else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) {
|
||||||
aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c]);
|
aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c], vertexRemappingTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -552,13 +618,13 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attr.texcoord[tc]->count != aim->mNumVertices) {
|
if (attr.texcoord[tc]->count != numAllVertices) {
|
||||||
DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name,
|
DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name,
|
||||||
"\" does not match the vertex count");
|
"\" does not match the vertex count");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
|
attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc], vertexRemappingTable);
|
||||||
aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
|
aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
|
||||||
|
|
||||||
aiVector3D *values = aim->mTextureCoords[tc];
|
aiVector3D *values = aim->mTextureCoords[tc];
|
||||||
|
@ -583,11 +649,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
Mesh::Primitive::Target &target = targets[i];
|
Mesh::Primitive::Target &target = targets[i];
|
||||||
|
|
||||||
if (needPositions) {
|
if (needPositions) {
|
||||||
if (target.position[0]->count != aim->mNumVertices) {
|
if (target.position[0]->count != numAllVertices) {
|
||||||
ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
|
ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
|
||||||
} else {
|
} else {
|
||||||
aiVector3D *positionDiff = nullptr;
|
aiVector3D *positionDiff = nullptr;
|
||||||
target.position[0]->ExtractData(positionDiff);
|
target.position[0]->ExtractData(positionDiff, vertexRemappingTable);
|
||||||
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
|
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
|
||||||
aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
|
aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
|
||||||
}
|
}
|
||||||
|
@ -595,11 +661,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (needNormals) {
|
if (needNormals) {
|
||||||
if (target.normal[0]->count != aim->mNumVertices) {
|
if (target.normal[0]->count != numAllVertices) {
|
||||||
ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
|
ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
|
||||||
} else {
|
} else {
|
||||||
aiVector3D *normalDiff = nullptr;
|
aiVector3D *normalDiff = nullptr;
|
||||||
target.normal[0]->ExtractData(normalDiff);
|
target.normal[0]->ExtractData(normalDiff, vertexRemappingTable);
|
||||||
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
|
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
|
||||||
aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
|
aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
|
||||||
}
|
}
|
||||||
|
@ -610,14 +676,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
if (!aiAnimMesh.HasNormals()) {
|
if (!aiAnimMesh.HasNormals()) {
|
||||||
// prevent nullptr access to aiAnimMesh.mNormals below when no normals are available
|
// prevent nullptr access to aiAnimMesh.mNormals below when no normals are available
|
||||||
ASSIMP_LOG_WARN("Bitangents of target ", i, " in mesh \"", mesh.name, "\" can't be computed, because mesh has no normals.");
|
ASSIMP_LOG_WARN("Bitangents of target ", i, " in mesh \"", mesh.name, "\" can't be computed, because mesh has no normals.");
|
||||||
} else if (target.tangent[0]->count != aim->mNumVertices) {
|
} else if (target.tangent[0]->count != numAllVertices) {
|
||||||
ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
|
ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
|
||||||
} else {
|
} else {
|
||||||
Tangent *tangent = nullptr;
|
Tangent *tangent = nullptr;
|
||||||
attr.tangent[0]->ExtractData(tangent);
|
attr.tangent[0]->ExtractData(tangent, vertexRemappingTable);
|
||||||
|
|
||||||
aiVector3D *tangentDiff = nullptr;
|
aiVector3D *tangentDiff = nullptr;
|
||||||
target.tangent[0]->ExtractData(tangentDiff);
|
target.tangent[0]->ExtractData(tangentDiff, vertexRemappingTable);
|
||||||
|
|
||||||
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
|
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
|
||||||
tangent[vertexId].xyz += tangentDiff[vertexId];
|
tangent[vertexId].xyz += tangentDiff[vertexId];
|
||||||
|
@ -641,20 +707,15 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
aiFace *facePtr = nullptr;
|
aiFace *facePtr = nullptr;
|
||||||
size_t nFaces = 0;
|
size_t nFaces = 0;
|
||||||
|
|
||||||
if (prim.indices) {
|
if (useIndexBuffer) {
|
||||||
size_t count = prim.indices->count;
|
size_t count = indexBuffer.size();
|
||||||
|
|
||||||
Accessor::Indexer data = prim.indices->GetIndexer();
|
|
||||||
if (!data.IsValid()) {
|
|
||||||
throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (prim.mode) {
|
switch (prim.mode) {
|
||||||
case PrimitiveMode_POINTS: {
|
case PrimitiveMode_POINTS: {
|
||||||
nFaces = count;
|
nFaces = count;
|
||||||
facePtr = faces = new aiFace[nFaces];
|
facePtr = faces = new aiFace[nFaces];
|
||||||
for (unsigned int i = 0; i < count; ++i) {
|
for (unsigned int i = 0; i < count; ++i) {
|
||||||
SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
|
SetFaceAndAdvance1(facePtr, aim->mNumVertices, indexBuffer[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -667,7 +728,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
}
|
}
|
||||||
facePtr = faces = new aiFace[nFaces];
|
facePtr = faces = new aiFace[nFaces];
|
||||||
for (unsigned int i = 0; i < count; i += 2) {
|
for (unsigned int i = 0; i < count; i += 2) {
|
||||||
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1));
|
SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -676,12 +737,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
case PrimitiveMode_LINE_STRIP: {
|
case PrimitiveMode_LINE_STRIP: {
|
||||||
nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
|
nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
|
||||||
facePtr = faces = new aiFace[nFaces];
|
facePtr = faces = new aiFace[nFaces];
|
||||||
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1));
|
SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[1]);
|
||||||
for (unsigned int i = 2; i < count; ++i) {
|
for (unsigned int i = 2; i < count; ++i) {
|
||||||
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i));
|
SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[i - 1], indexBuffer[i]);
|
||||||
}
|
}
|
||||||
if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
|
if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
|
||||||
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]);
|
SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[static_cast<int>(count) - 1], faces[0].mIndices[0]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -694,7 +755,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
}
|
}
|
||||||
facePtr = faces = new aiFace[nFaces];
|
facePtr = faces = new aiFace[nFaces];
|
||||||
for (unsigned int i = 0; i < count; i += 3) {
|
for (unsigned int i = 0; i < count; i += 3) {
|
||||||
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
|
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -705,10 +766,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
// The ordering is to ensure that the triangles are all drawn with the same orientation
|
// The ordering is to ensure that the triangles are all drawn with the same orientation
|
||||||
if ((i + 1) % 2 == 0) {
|
if ((i + 1) % 2 == 0) {
|
||||||
// For even n, vertices n + 1, n, and n + 2 define triangle n
|
// For even n, vertices n + 1, n, and n + 2 define triangle n
|
||||||
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
|
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i + 1], indexBuffer[i], indexBuffer[i + 2]);
|
||||||
} else {
|
} else {
|
||||||
// For odd n, vertices n, n+1, and n+2 define triangle n
|
// For odd n, vertices n, n+1, and n+2 define triangle n
|
||||||
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
|
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -716,9 +777,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
case PrimitiveMode_TRIANGLE_FAN:
|
case PrimitiveMode_TRIANGLE_FAN:
|
||||||
nFaces = count - 2;
|
nFaces = count - 2;
|
||||||
facePtr = faces = new aiFace[nFaces];
|
facePtr = faces = new aiFace[nFaces];
|
||||||
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
|
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[1], indexBuffer[2]);
|
||||||
for (unsigned int i = 1; i < nFaces; ++i) {
|
for (unsigned int i = 1; i < nFaces; ++i) {
|
||||||
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2));
|
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[i + 1], indexBuffer[i + 2]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -823,8 +884,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
meshOffsets.push_back(k);
|
|
||||||
|
|
||||||
CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
|
CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -957,7 +1016,8 @@ static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map) {
|
static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map, std::vector<unsigned int>* vertexRemappingTablePtr) {
|
||||||
|
|
||||||
Mesh::Primitive::Attributes &attr = primitive.attributes;
|
Mesh::Primitive::Attributes &attr = primitive.attributes;
|
||||||
if (attr.weight.empty() || attr.joint.empty()) {
|
if (attr.weight.empty() || attr.joint.empty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -966,14 +1026,14 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t num_vertices = attr.weight[0]->count;
|
size_t num_vertices = 0;
|
||||||
|
|
||||||
struct Weights {
|
struct Weights {
|
||||||
float values[4];
|
float values[4];
|
||||||
};
|
};
|
||||||
Weights **weights = new Weights*[attr.weight.size()];
|
Weights **weights = new Weights*[attr.weight.size()];
|
||||||
for (size_t w = 0; w < attr.weight.size(); ++w) {
|
for (size_t w = 0; w < attr.weight.size(); ++w) {
|
||||||
attr.weight[w]->ExtractData(weights[w]);
|
num_vertices = attr.weight[w]->ExtractData(weights[w], vertexRemappingTablePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Indices8 {
|
struct Indices8 {
|
||||||
|
@ -987,12 +1047,12 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
|
||||||
if (attr.joint[0]->GetElementSize() == 4) {
|
if (attr.joint[0]->GetElementSize() == 4) {
|
||||||
indices8 = new Indices8*[attr.joint.size()];
|
indices8 = new Indices8*[attr.joint.size()];
|
||||||
for (size_t j = 0; j < attr.joint.size(); ++j) {
|
for (size_t j = 0; j < attr.joint.size(); ++j) {
|
||||||
attr.joint[j]->ExtractData(indices8[j]);
|
attr.joint[j]->ExtractData(indices8[j], vertexRemappingTablePtr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
indices16 = new Indices16 *[attr.joint.size()];
|
indices16 = new Indices16 *[attr.joint.size()];
|
||||||
for (size_t j = 0; j < attr.joint.size(); ++j) {
|
for (size_t j = 0; j < attr.joint.size(); ++j) {
|
||||||
attr.joint[j]->ExtractData(indices16[j]);
|
attr.joint[j]->ExtractData(indices16[j], vertexRemappingTablePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
@ -1051,15 +1111,13 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) {
|
void ParseExtras(aiMetadata* metadata, const Extras& extras) {
|
||||||
if (extension.mValues.isPresent) {
|
for (auto const &value : extras.mValues) {
|
||||||
for (auto const &subExtension : extension.mValues.value) {
|
ParseExtensions(metadata, value);
|
||||||
ParseExtensions(metadata, subExtension);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
|
aiNode *glTF2Importer::ImportNode(glTF2::Asset &r, glTF2::Ref<glTF2::Node> &ptr) {
|
||||||
Node &node = *ptr;
|
Node &node = *ptr;
|
||||||
|
|
||||||
aiNode *ainode = new aiNode(GetNodeName(node));
|
aiNode *ainode = new aiNode(GetNodeName(node));
|
||||||
|
@ -1071,18 +1129,18 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
|
||||||
std::fill(ainode->mChildren, ainode->mChildren + ainode->mNumChildren, nullptr);
|
std::fill(ainode->mChildren, ainode->mChildren + ainode->mNumChildren, nullptr);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
|
for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
|
||||||
aiNode *child = ImportNode(pScene, r, meshOffsets, node.children[i]);
|
aiNode *child = ImportNode(r, node.children[i]);
|
||||||
child->mParent = ainode;
|
child->mParent = ainode;
|
||||||
ainode->mChildren[i] = child;
|
ainode->mChildren[i] = child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.customExtensions || node.extras) {
|
if (node.customExtensions || node.extras.HasExtras()) {
|
||||||
ainode->mMetaData = new aiMetadata;
|
ainode->mMetaData = new aiMetadata;
|
||||||
if (node.customExtensions) {
|
if (node.customExtensions) {
|
||||||
ParseExtensions(ainode->mMetaData, node.customExtensions);
|
ParseExtensions(ainode->mMetaData, node.customExtensions);
|
||||||
}
|
}
|
||||||
if (node.extras) {
|
if (node.extras.HasExtras()) {
|
||||||
ParseExtras(ainode->mMetaData, node.extras);
|
ParseExtras(ainode->mMetaData, node.extras);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1104,11 +1162,13 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
|
||||||
|
|
||||||
if (node.skin) {
|
if (node.skin) {
|
||||||
for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
|
for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
|
||||||
aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo];
|
unsigned int aiMeshIdx = meshOffsets[mesh_idx] + primitiveNo;
|
||||||
|
aiMesh *mesh = mScene->mMeshes[aiMeshIdx];
|
||||||
unsigned int numBones = static_cast<unsigned int>(node.skin->jointNames.size());
|
unsigned int numBones = static_cast<unsigned int>(node.skin->jointNames.size());
|
||||||
|
std::vector<unsigned int> *vertexRemappingTablePtr = mVertexRemappingTables[aiMeshIdx].empty() ? nullptr : &mVertexRemappingTables[aiMeshIdx];
|
||||||
|
|
||||||
std::vector<std::vector<aiVertexWeight>> weighting(numBones);
|
std::vector<std::vector<aiVertexWeight>> weighting(numBones);
|
||||||
BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
|
BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting, vertexRemappingTablePtr);
|
||||||
|
|
||||||
mesh->mNumBones = static_cast<unsigned int>(numBones);
|
mesh->mNumBones = static_cast<unsigned int>(numBones);
|
||||||
mesh->mBones = new aiBone *[mesh->mNumBones];
|
mesh->mBones = new aiBone *[mesh->mNumBones];
|
||||||
|
@ -1125,7 +1185,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
|
||||||
// mapping which makes things doubly-slow.
|
// mapping which makes things doubly-slow.
|
||||||
|
|
||||||
mat4 *pbindMatrices = nullptr;
|
mat4 *pbindMatrices = nullptr;
|
||||||
node.skin->inverseBindMatrices->ExtractData(pbindMatrices);
|
node.skin->inverseBindMatrices->ExtractData(pbindMatrices, nullptr);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < numBones; ++i) {
|
for (uint32_t i = 0; i < numBones; ++i) {
|
||||||
const std::vector<aiVertexWeight> &weights = weighting[i];
|
const std::vector<aiVertexWeight> &weights = weighting[i];
|
||||||
|
@ -1171,16 +1231,11 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.camera) {
|
if (node.camera) {
|
||||||
pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
|
mScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
|
||||||
if (node.translation.isPresent) {
|
|
||||||
aiVector3D trans;
|
|
||||||
CopyValue(node.translation.value, trans);
|
|
||||||
pScene->mCameras[node.camera.GetIndex()]->mPosition = trans;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.light) {
|
if (node.light) {
|
||||||
pScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
|
mScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
|
||||||
|
|
||||||
// range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
|
// range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
|
||||||
// it is added to meta data of parent node, because there is no other place to put it
|
// it is added to meta data of parent node, because there is no other place to put it
|
||||||
|
@ -1212,7 +1267,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) {
|
||||||
// The root nodes
|
// The root nodes
|
||||||
unsigned int numRootNodes = unsigned(rootNodes.size());
|
unsigned int numRootNodes = unsigned(rootNodes.size());
|
||||||
if (numRootNodes == 1) { // a single root node: use it
|
if (numRootNodes == 1) { // a single root node: use it
|
||||||
mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
|
mScene->mRootNode = ImportNode(r, rootNodes[0]);
|
||||||
} else if (numRootNodes > 1) { // more than one root node: create a fake root
|
} else if (numRootNodes > 1) { // more than one root node: create a fake root
|
||||||
aiNode *root = mScene->mRootNode = new aiNode("ROOT");
|
aiNode *root = mScene->mRootNode = new aiNode("ROOT");
|
||||||
|
|
||||||
|
@ -1220,7 +1275,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) {
|
||||||
std::fill(root->mChildren, root->mChildren + numRootNodes, nullptr);
|
std::fill(root->mChildren, root->mChildren + numRootNodes, nullptr);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < numRootNodes; ++i) {
|
for (unsigned int i = 0; i < numRootNodes; ++i) {
|
||||||
aiNode *node = ImportNode(mScene, r, meshOffsets, rootNodes[i]);
|
aiNode *node = ImportNode(r, rootNodes[i]);
|
||||||
node->mParent = root;
|
node->mParent = root;
|
||||||
root->mChildren[root->mNumChildren++] = node;
|
root->mChildren[root->mNumChildren++] = node;
|
||||||
}
|
}
|
||||||
|
@ -1621,13 +1676,17 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
|
||||||
|
|
||||||
// clean all member arrays
|
// clean all member arrays
|
||||||
meshOffsets.clear();
|
meshOffsets.clear();
|
||||||
|
mVertexRemappingTables.clear();
|
||||||
mEmbeddedTexIdxs.clear();
|
mEmbeddedTexIdxs.clear();
|
||||||
|
|
||||||
this->mScene = pScene;
|
this->mScene = pScene;
|
||||||
|
|
||||||
// read the asset file
|
// read the asset file
|
||||||
glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(mSchemaDocumentProvider));
|
glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(mSchemaDocumentProvider));
|
||||||
asset.Load(pFile, GetExtension(pFile) == "glb");
|
asset.Load(pFile,
|
||||||
|
CheckMagicToken(
|
||||||
|
pIOHandler, pFile, AI_GLB_MAGIC_NUMBER, 1, 0,
|
||||||
|
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
|
||||||
if (asset.scene) {
|
if (asset.scene) {
|
||||||
pScene->mName = asset.scene->name;
|
pScene->mName = asset.scene->name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#define AI_GLTF2IMPORTER_H_INC
|
#define AI_GLTF2IMPORTER_H_INC
|
||||||
|
|
||||||
#include <assimp/BaseImporter.h>
|
#include <assimp/BaseImporter.h>
|
||||||
|
#include <AssetLib/glTF2/glTF2Asset.h>
|
||||||
|
|
||||||
struct aiNode;
|
struct aiNode;
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ namespace Assimp {
|
||||||
class glTF2Importer : public BaseImporter {
|
class glTF2Importer : public BaseImporter {
|
||||||
public:
|
public:
|
||||||
glTF2Importer();
|
glTF2Importer();
|
||||||
~glTF2Importer() override;
|
~glTF2Importer() override = default;
|
||||||
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
|
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -76,10 +77,12 @@ private:
|
||||||
void ImportNodes(glTF2::Asset &a);
|
void ImportNodes(glTF2::Asset &a);
|
||||||
void ImportAnimations(glTF2::Asset &a);
|
void ImportAnimations(glTF2::Asset &a);
|
||||||
void ImportCommonMetadata(glTF2::Asset &a);
|
void ImportCommonMetadata(glTF2::Asset &a);
|
||||||
|
aiNode *ImportNode(glTF2::Asset &r, glTF2::Ref<glTF2::Node> &ptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<unsigned int> meshOffsets;
|
std::vector<unsigned int> meshOffsets;
|
||||||
std::vector<int> mEmbeddedTexIdxs;
|
std::vector<int> mEmbeddedTexIdxs;
|
||||||
|
std::vector<std::vector<unsigned int>> mVertexRemappingTables; // for each converted aiMesh in the scene, it stores a list of vertices that are actually used
|
||||||
aiScene *mScene;
|
aiScene *mScene;
|
||||||
|
|
||||||
/// An instance of rapidjson::IRemoteSchemaDocumentProvider
|
/// An instance of rapidjson::IRemoteSchemaDocumentProvider
|
||||||
|
|
|
@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue