diff --git a/.coveralls.yml b/.coveralls.yml
new file mode 100644
index 000000000..f69c0ba34
--- /dev/null
+++ b/.coveralls.yml
@@ -0,0 +1,2 @@
+service_name: travis-pro
+repo_token: GZXuNlublKFy7HAewHAZLk5ZwgipTFAOA
diff --git a/.gitignore b/.gitignore
index d0d5e9631..664708a90 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,13 @@ cmake_uninstall.cmake
assimp-config.cmake
assimp-config-version.cmake
+# MakeFile
+Makefile
+code/Makefile
+test/Makefile
+test/headercheck/Makefile
+tools/assimp_cmd/Makefile
+
# Tests
test/results
diff --git a/.travis.sh b/.travis.sh
index fefd5292d..c2852855f 100755
--- a/.travis.sh
+++ b/.travis.sh
@@ -1,13 +1,13 @@
function generate()
{
- cmake -G "Unix Makefiles" -DASSIMP_ENABLE_BOOST_WORKAROUND=YES -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD
+ cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS
}
if [ $ANDROID ]; then
ant -v -Dmy.dir=${TRAVIS_BUILD_DIR} -f ${TRAVIS_BUILD_DIR}/port/jassimp/build.xml ndk-jni
else
generate \
- && make \
+ && make -j4 \
&& sudo make install \
&& sudo ldconfig \
&& (cd test/unit; ../../bin/unit) \
diff --git a/.travis.yml b/.travis.yml
index 05f963291..ee385186a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,38 @@
before_install:
- sudo apt-get update -qq
+<<<<<<< HEAD
- sudo apt-get install cmake
env:
global:
- secure: "lZ7pHQvl5dpZWzBQAaIMf0wqrvtcZ4wiZKeIZjf83TEsflW8+z0uTpIuN30ZV6Glth/Sq1OhLnTP5+N57fZU/1ebA5twHdvP4bS5CIUUg71/CXQZNl36xeaqvxsG/xRrdpKOsPdjAOsQ9KPTQulsX43XDLS7CasMiLvYOpqKcPc="
+=======
+ - sudo apt-get install cmake python3
+ - if [ $LINUX ]; then sudo apt-get install -qq freeglut3-dev libxmu-dev libxi-dev ; fi
+ - echo -e "#ifndef A_R_H_INC\n#define A_R_H_INC\n#define GitVersion ${TRAVIS_JOB_ID}\n#define GitBranch \"${TRAVIS_BRANCH}\"\n#endif // A_R_H_INC" > revision.h
+ # install latest LCOV (1.9 was failing)
+ - cd ${TRAVIS_BUILD_DIR}
+ - wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz
+ - tar xf lcov_1.11.orig.tar.gz
+ - sudo make -C lcov-1.11/ install
+ - gem install coveralls-lcov
+ - lcov --version
+ - g++ --version
+
+branches:
+ only:
+ - master
+
+env:
+ global:
+ - PV=r8e PLATF=linux-x86_64 NDK_HOME=${TRAVIS_BUILD_DIR}/android-ndk-${PV} PATH=${PATH}:${NDK_HOME}
+ matrix:
+ - LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON
+ - LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF
+ - LINUX=1 SHARED_BUILD=ON ENABLE_COVERALLS=OFF
+ - LINUX=1 SHARED_BUILD=OFF ENABLE_COVERALLS=OFF
+ - ANDROID=1
+>>>>>>> master
language: cpp
@@ -14,8 +42,22 @@ compiler:
before_script:
cmake . -DASSIMP_ENABLE_BOOST_WORKAROUND=YES
+before_script:
+ - cd ${TRAVIS_BUILD_DIR}
+ # init coverage to 0 (optional)
+ - lcov --directory . --zerocounters
+
script:
- make
+ - export COVERALLS_SERVICE_NAME=travis-ci
+ - export COVERALLS_REPO_TOKEN=abc12345
+ - . ./.travis.sh
+
+after_success:
+ - cd ${TRAVIS_BUILD_DIR}
+ - lcov --directory . --capture --output-file coverage.info
+ - lcov --remove coverage.info '/usr/*' 'contrib/*' 'test/*' --output-file coverage.info
+ - lcov --list coverage.info
+ - coveralls-lcov --source-encoding=ISO-8859-1 --repo-token=${COVERALLS_TOKEN} coverage.info
addons:
coverity_scan:
diff --git a/CHANGES b/CHANGES
index 0ff79cbe1..0c734162b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,22 @@
CHANGELOG
----------------------------------------------------------------------
+3.2.1 (2016-10-01)
+
+FEATURES:
+ - Updated glTF exporter to meet 1.0 specification.
+
+FIXES/HOUSEKEEPING:
+ - Fixed glTF Validator errors for exported glTF format.
+
+ISSUES:
+ - Hard coded sampler setting for
+ - magFilter
+ - minFilter
+ - void* in ExportData for accessor max and min.
+
+
+
3.2.0 (2015-11-03)
FEATURES:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 77de1ee14..cd6838dc6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,20 +40,20 @@ cmake_minimum_required( VERSION 2.8 )
PROJECT( Assimp )
# All supported options ###############################################
-OPTION( BUILD_SHARED_LIBS
- "Build package with shared libraries."
+OPTION( BUILD_SHARED_LIBS
+ "Build package with shared libraries."
ON
)
OPTION( ASSIMP_DOUBLE_PRECISION
"Set to ON to enable double precision processing"
OFF
)
-OPTION( ASSIMP_OPT_BUILD_PACKAGES
- "Set to ON to generate CPack configuration files and packaging targets"
+OPTION( ASSIMP_OPT_BUILD_PACKAGES
+ "Set to ON to generate CPack configuration files and packaging targets"
OFF
)
-OPTION( ASSIMP_ANDROID_JNIIOSYSTEM
- "Android JNI IOSystem support is active"
+OPTION( ASSIMP_ANDROID_JNIIOSYSTEM
+ "Android JNI IOSystem support is active"
OFF
)
OPTION( ASSIMP_NO_EXPORT
@@ -76,7 +76,13 @@ OPTION ( ASSIMP_BUILD_TESTS
"If the test suite for Assimp is built in addition to the library."
ON
)
+OPTION ( ASSIMP_COVERALLS
+ "Eańable this to measure test coverage."
+ OFF
+)
+
IF(MSVC)
+ set (CMAKE_PREFIX_PATH "D:\\libs\\devil")
OPTION( ASSIMP_INSTALL_PDB
"Install MSVC debug files."
ON
@@ -149,11 +155,6 @@ SET(LIBASSIMP-DEV_COMPONENT "libassimp${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_M
SET(CPACK_COMPONENTS_ALL assimp-bin ${LIBASSIMP_COMPONENT} ${LIBASSIMP-DEV_COMPONENT} assimp-dev)
SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names")
-# Workaround to be able to deal with compiler bug "Too many sections" with mingw.
-IF( CMAKE_COMPILER_IS_MINGW )
- ADD_DEFINITIONS(-DASSIMP_BUILD_NO_IFC_IMPORTER )
-ENDIF()
-
# Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux
IF( UNIX )
IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux
@@ -177,6 +178,12 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW )
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall -Wno-long-long -pedantic -std=c++11" )
ENDIF()
+if (ASSIMP_COVERALLS)
+ include(Coveralls)
+ 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")
+endif()
+
INCLUDE (FindPkgConfig)
INCLUDE_DIRECTORIES( include )
@@ -320,7 +327,6 @@ ELSE (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
-
ADD_SUBDIRECTORY( code/ )
IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
IF ( WIN32 )
@@ -336,10 +342,10 @@ IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
# Why here? Maybe user do not want Qt viewer and have no Qt.
# Why assimp_qt_viewer/CMakeLists.txt still contain similar check?
# Because viewer can be build independently of Assimp.
- FIND_PACKAGE(Qt5 QUIET)
+ FIND_PACKAGE(Qt5Widgets QUIET)
FIND_PACKAGE(DevIL QUIET)
FIND_PACKAGE(OpenGL QUIET)
- IF ( Qt5_FOUND AND IL_FOUND AND OPENGL_FOUND)
+ IF ( Qt5Widgets_FOUND AND IL_FOUND AND OPENGL_FOUND)
ADD_SUBDIRECTORY( tools/assimp_qt_viewer/ )
ELSE()
SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "")
@@ -356,7 +362,7 @@ IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
ENDIF (NOT OPENGL_FOUND)
MESSAGE (WARNING "Build of assimp_qt_viewer is disabled. Unsatisfied dendencies: ${ASSIMP_QT_VIEWER_DEPENDENCIES}")
- ENDIF ( Qt5_FOUND AND IL_FOUND AND OPENGL_FOUND)
+ ENDIF ( Qt5Widgets_FOUND AND IL_FOUND AND OPENGL_FOUND)
ENDIF ( ASSIMP_BUILD_ASSIMP_TOOLS )
IF ( ASSIMP_BUILD_SAMPLES)
diff --git a/Readme.md b/Readme.md
index d91f290fa..977bf4601 100644
--- a/Readme.md
+++ b/Readme.md
@@ -15,6 +15,9 @@ Coverity
src="https://scan.coverity.com/projects/5607/badge.svg"/>
+Code coverage:[](https://coveralls.io/github/assimp/assimp?branch=master)
+
+Gitter chat: [](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
__[open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.__
#### Supported file formats ####
diff --git a/appveyor.yml b/appveyor.yml
index d732c72e7..12dede7d6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -27,10 +27,12 @@ install:
# Make compiler command line tools available
- call c:\projects\assimp\scripts\appveyor\compiler_setup.bat
-build_script:
- - cd c:\projects\assimp
- - cmake CMakeLists.txt -G "Visual Studio %Configuration%"
- - msbuild /m /p:Configuration=Release /p:Platform="Win32" Assimp.sln
+build_script:
+- cd c:\projects\assimp
+- if "%platform%" equ "x64" (cmake CMakeLists.txt -G "Visual Studio %Configuration% Win64")
+- if "%platform%" equ "x86" (cmake CMakeLists.txt -G "Visual Studio %Configuration%")
+- if "%platform%" equ "x64" (msbuild /m /p:Configuration=Release /p:Platform="x64" Assimp.sln)
+- if "%platform%" equ "x86" (msbuild /m /p:Configuration=Release /p:Platform="Win32" Assimp.sln)
after_build:
- 7z a assimp.7z c:\projects\assimp\bin\release\* c:\projects\assimp\lib\release\*
@@ -38,3 +40,13 @@ after_build:
artifacts:
- path: assimp.7z
name: assimp_lib
+
+addons:
+ coverity_scan:
+ project:
+ name: "assimp/assimp"
+ description: ""
+ notification_email: kim.kulling@googlemail.com
+ build_command_prepend: cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD
+ build_command: make< make install
+ branch_pattern: coverity_scan
diff --git a/cmake-modules/Coveralls.cmake b/cmake-modules/Coveralls.cmake
new file mode 100644
index 000000000..5343f1583
--- /dev/null
+++ b/cmake-modules/Coveralls.cmake
@@ -0,0 +1,126 @@
+#
+# The MIT License (MIT)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Copyright (C) 2014 Joakim Söderberg
+#
+
+set(_CMAKE_SCRIPT_PATH ${CMAKE_CURRENT_LIST_DIR}) # must be outside coveralls_setup() to get correct path
+
+#
+# Param _COVERAGE_SRCS A list of source files that coverage should be collected for.
+# Param _COVERALLS_UPLOAD Upload the result to coveralls?
+#
+
+function(coveralls_setup _COVERAGE_SRCS _COVERALLS_UPLOAD)
+
+ if (ARGC GREATER 2)
+ set(_CMAKE_SCRIPT_PATH ${ARGN})
+ message(STATUS "Coveralls: Using alternate CMake script dir: ${_CMAKE_SCRIPT_PATH}")
+ endif()
+
+ if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake")
+ message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake")
+ endif()
+
+ if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake")
+ message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake")
+ endif()
+
+ # When passing a CMake list to an external process, the list
+ # will be converted from the format "1;2;3" to "1 2 3".
+ # This means the script we're calling won't see it as a list
+ # of sources, but rather just one long path. We remedy this
+ # by replacing ";" with "*" and then reversing that in the script
+ # that we're calling.
+ # http://cmake.3232098.n2.nabble.com/Passing-a-CMake-list-quot-as-is-quot-to-a-custom-target-td6505681.html
+ set(COVERAGE_SRCS_TMP ${_COVERAGE_SRCS})
+ set(COVERAGE_SRCS "")
+ foreach (COVERAGE_SRC ${COVERAGE_SRCS_TMP})
+ set(COVERAGE_SRCS "${COVERAGE_SRCS}*${COVERAGE_SRC}")
+ endforeach()
+
+ #message("Coverage sources: ${COVERAGE_SRCS}")
+ set(COVERALLS_FILE ${PROJECT_BINARY_DIR}/coveralls.json)
+
+ add_custom_target(coveralls_generate
+
+ # Zero the coverage counters.
+ COMMAND ${CMAKE_COMMAND} -DPROJECT_BINARY_DIR="${PROJECT_BINARY_DIR}" -P "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake"
+
+ # Run regress tests.
+ COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
+
+ # Generate Gcov and translate it into coveralls JSON.
+ # We do this by executing an external CMake script.
+ # (We don't want this to run at CMake generation time, but after compilation and everything has run).
+ COMMAND ${CMAKE_COMMAND}
+ -DCOVERAGE_SRCS="${COVERAGE_SRCS}" # TODO: This is passed like: "a b c", not "a;b;c"
+ -DCOVERALLS_OUTPUT_FILE="${COVERALLS_FILE}"
+ -DCOV_PATH="${PROJECT_BINARY_DIR}"
+ -DPROJECT_ROOT="${PROJECT_SOURCE_DIR}"
+ -P "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake"
+
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ COMMENT "Generating coveralls output..."
+ )
+
+ if (_COVERALLS_UPLOAD)
+ message("COVERALLS UPLOAD: ON")
+
+ find_program(CURL_EXECUTABLE curl)
+
+ if (NOT CURL_EXECUTABLE)
+ message(FATAL_ERROR "Coveralls: curl not found! Aborting")
+ endif()
+
+ add_custom_target(coveralls_upload
+ # Upload the JSON to coveralls.
+ COMMAND ${CURL_EXECUTABLE}
+ -S -F json_file=@${COVERALLS_FILE}
+ https://coveralls.io/api/v1/jobs
+
+ DEPENDS coveralls_generate
+
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ COMMENT "Uploading coveralls output...")
+
+ add_custom_target(coveralls DEPENDS coveralls_upload)
+ else()
+ message("COVERALLS UPLOAD: OFF")
+ add_custom_target(coveralls DEPENDS coveralls_generate)
+ endif()
+
+endfunction()
+
+macro(coveralls_turn_on_coverage)
+ if(NOT (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
+ AND (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
+ message(FATAL_ERROR "Coveralls: Compiler ${CMAKE_C_COMPILER_ID} is not GNU gcc! Aborting... You can set this on the command line using CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake ..")
+ endif()
+
+ if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+ message(FATAL_ERROR "Coveralls: Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug")
+ endif()
+
+ 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")
+endmacro()
+
diff --git a/cmake-modules/CoverallsClear.cmake b/cmake-modules/CoverallsClear.cmake
new file mode 100644
index 000000000..a9ad48763
--- /dev/null
+++ b/cmake-modules/CoverallsClear.cmake
@@ -0,0 +1,31 @@
+#
+# The MIT License (MIT)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Copyright (C) 2014 Joakim Söderberg
+#
+
+# do not follow symlinks in file(GLOB_RECURSE ...)
+cmake_policy(SET CMP0009 NEW)
+
+file(GLOB_RECURSE GCDA_FILES "${PROJECT_BINARY_DIR}/*.gcda")
+if(NOT GCDA_FILES STREQUAL "")
+ file(REMOVE ${GCDA_FILES})
+endif()
\ No newline at end of file
diff --git a/cmake-modules/CoverallsGenerateGcov.cmake b/cmake-modules/CoverallsGenerateGcov.cmake
new file mode 100644
index 000000000..ba506e3d4
--- /dev/null
+++ b/cmake-modules/CoverallsGenerateGcov.cmake
@@ -0,0 +1,482 @@
+#
+# The MIT License (MIT)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Copyright (C) 2014 Joakim Söderberg
+#
+# This is intended to be run by a custom target in a CMake project like this.
+# 0. Compile program with coverage support.
+# 1. Clear coverage data. (Recursively delete *.gcda in build dir)
+# 2. Run the unit tests.
+# 3. Run this script specifying which source files the coverage should be performed on.
+#
+# This script will then use gcov to generate .gcov files in the directory specified
+# via the COV_PATH var. This should probably be the same as your cmake build dir.
+#
+# It then parses the .gcov files to convert them into the Coveralls JSON format:
+# https://coveralls.io/docs/api
+#
+# Example for running as standalone CMake script from the command line:
+# (Note it is important the -P is at the end...)
+# $ cmake -DCOV_PATH=$(pwd)
+# -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c"
+# -P ../cmake/CoverallsGcovUpload.cmake
+#
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+
+
+#
+# Make sure we have the needed arguments.
+#
+if (NOT COVERALLS_OUTPUT_FILE)
+ message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE")
+endif()
+
+if (NOT COV_PATH)
+ message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH")
+endif()
+
+if (NOT COVERAGE_SRCS)
+ message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS")
+endif()
+
+if (NOT PROJECT_ROOT)
+ message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.")
+endif()
+
+# Since it's not possible to pass a CMake list properly in the
+# "1;2;3" format to an external process, we have replaced the
+# ";" with "*", so reverse that here so we get it back into the
+# CMake list format.
+string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS})
+
+if (NOT DEFINED ENV{GCOV})
+ find_program(GCOV_EXECUTABLE gcov)
+else()
+ find_program(GCOV_EXECUTABLE $ENV{GCOV})
+endif()
+
+# convert all paths in COVERAGE_SRCS to absolute paths
+set(COVERAGE_SRCS_TMP "")
+foreach (COVERAGE_SRC ${COVERAGE_SRCS})
+ if (NOT "${COVERAGE_SRC}" MATCHES "^/")
+ set(COVERAGE_SRC ${PROJECT_ROOT}/${COVERAGE_SRC})
+ endif()
+ list(APPEND COVERAGE_SRCS_TMP ${COVERAGE_SRC})
+endforeach()
+set(COVERAGE_SRCS ${COVERAGE_SRCS_TMP})
+unset(COVERAGE_SRCS_TMP)
+
+if (NOT GCOV_EXECUTABLE)
+ message(FATAL_ERROR "gcov not found! Aborting...")
+endif()
+
+find_package(Git)
+
+set(JSON_REPO_TEMPLATE
+ "{
+ \"head\": {
+ \"id\": \"\@GIT_COMMIT_HASH\@\",
+ \"author_name\": \"\@GIT_AUTHOR_NAME\@\",
+ \"author_email\": \"\@GIT_AUTHOR_EMAIL\@\",
+ \"committer_name\": \"\@GIT_COMMITTER_NAME\@\",
+ \"committer_email\": \"\@GIT_COMMITTER_EMAIL\@\",
+ \"message\": \"\@GIT_COMMIT_MESSAGE\@\"
+ },
+ \"branch\": \"@GIT_BRANCH@\",
+ \"remotes\": []
+ }"
+)
+
+# TODO: Fill in git remote data
+if (GIT_FOUND)
+ # Branch.
+ execute_process(
+ COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_VARIABLE GIT_BRANCH
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ macro (git_log_format FORMAT_CHARS VAR_NAME)
+ execute_process(
+ COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_VARIABLE ${VAR_NAME}
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ endmacro()
+
+ git_log_format(an GIT_AUTHOR_NAME)
+ git_log_format(ae GIT_AUTHOR_EMAIL)
+ git_log_format(cn GIT_COMMITTER_NAME)
+ git_log_format(ce GIT_COMMITTER_EMAIL)
+ git_log_format(B GIT_COMMIT_MESSAGE)
+ git_log_format(H GIT_COMMIT_HASH)
+
+ if(GIT_COMMIT_MESSAGE)
+ string(REPLACE "\n" "\\n" GIT_COMMIT_MESSAGE ${GIT_COMMIT_MESSAGE})
+ endif()
+
+ message("Git exe: ${GIT_EXECUTABLE}")
+ message("Git branch: ${GIT_BRANCH}")
+ message("Git author: ${GIT_AUTHOR_NAME}")
+ message("Git e-mail: ${GIT_AUTHOR_EMAIL}")
+ message("Git commiter name: ${GIT_COMMITTER_NAME}")
+ message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}")
+ message("Git commit hash: ${GIT_COMMIT_HASH}")
+ message("Git commit message: ${GIT_COMMIT_MESSAGE}")
+
+ string(CONFIGURE ${JSON_REPO_TEMPLATE} JSON_REPO_DATA)
+else()
+ set(JSON_REPO_DATA "{}")
+endif()
+
+############################# Macros #########################################
+
+#
+# This macro converts from the full path format gcov outputs:
+#
+# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+#
+# to the original source file path the .gcov is for:
+#
+# /path/to/project/root/subdir/the_file.c
+#
+macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME)
+
+ # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+ # ->
+ # #path#to#project#root#subdir#the_file.c.gcov
+ get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME)
+
+ # #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c
+ string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT})
+ string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP})
+ set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}")
+endmacro()
+
+##############################################################################
+
+# Get the coverage data.
+file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda")
+message("GCDA files:")
+
+# Get a list of all the object directories needed by gcov
+# (The directories the .gcda files and .o files are found in)
+# and run gcov on those.
+foreach(GCDA ${GCDA_FILES})
+ message("Process: ${GCDA}")
+ message("------------------------------------------------------------------------------")
+ get_filename_component(GCDA_DIR ${GCDA} PATH)
+
+ #
+ # The -p below refers to "Preserve path components",
+ # This means that the generated gcov filename of a source file will
+ # keep the original files entire filepath, but / is replaced with #.
+ # Example:
+ #
+ # /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda
+ # ------------------------------------------------------------------------------
+ # File '/path/to/project/root/subdir/the_file.c'
+ # Lines executed:68.34% of 199
+ # /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov'
+ #
+ # If -p is not specified then the file is named only "the_file.c.gcov"
+ #
+ execute_process(
+ COMMAND ${GCOV_EXECUTABLE} -p -o ${GCDA_DIR} ${GCDA}
+ WORKING_DIRECTORY ${COV_PATH}
+ )
+endforeach()
+
+# TODO: Make these be absolute path
+file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov)
+
+# Get only the filenames to use for filtering.
+#set(COVERAGE_SRCS_NAMES "")
+#foreach (COVSRC ${COVERAGE_SRCS})
+# get_filename_component(COVSRC_NAME ${COVSRC} NAME)
+# message("${COVSRC} -> ${COVSRC_NAME}")
+# list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}")
+#endforeach()
+
+#
+# Filter out all but the gcov files we want.
+#
+# We do this by comparing the list of COVERAGE_SRCS filepaths that the
+# user wants the coverage data for with the paths of the generated .gcov files,
+# so that we only keep the relevant gcov files.
+#
+# Example:
+# COVERAGE_SRCS =
+# /path/to/project/root/subdir/the_file.c
+#
+# ALL_GCOV_FILES =
+# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+# /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov
+#
+# Result should be:
+# GCOV_FILES =
+# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+#
+set(GCOV_FILES "")
+#message("Look in coverage sources: ${COVERAGE_SRCS}")
+message("\nFilter out unwanted GCOV files:")
+message("===============================")
+
+set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS})
+
+foreach (GCOV_FILE ${ALL_GCOV_FILES})
+
+ #
+ # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+ # ->
+ # /path/to/project/root/subdir/the_file.c
+ get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
+ file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
+
+ # Is this in the list of source files?
+ # TODO: We want to match against relative path filenames from the source file root...
+ list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND)
+
+ if (NOT WAS_FOUND EQUAL -1)
+ message("YES: ${GCOV_FILE}")
+ list(APPEND GCOV_FILES ${GCOV_FILE})
+
+ # We remove it from the list, so we don't bother searching for it again.
+ # Also files left in COVERAGE_SRCS_REMAINING after this loop ends should
+ # have coverage data generated from them (no lines are covered).
+ list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH})
+ else()
+ message("NO: ${GCOV_FILE}")
+ endif()
+endforeach()
+
+# TODO: Enable setting these
+set(JSON_SERVICE_NAME "travis-ci")
+set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID})
+set(JSON_REPO_TOKEN $ENV{COVERALLS_REPO_TOKEN})
+
+set(JSON_TEMPLATE
+"{
+ \"repo_token\": \"\@JSON_REPO_TOKEN\@\",
+ \"service_name\": \"\@JSON_SERVICE_NAME\@\",
+ \"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\",
+ \"source_files\": \@JSON_GCOV_FILES\@,
+ \"git\": \@JSON_REPO_DATA\@
+}"
+)
+
+set(SRC_FILE_TEMPLATE
+"{
+ \"name\": \"\@GCOV_SRC_REL_PATH\@\",
+ \"source_digest\": \"\@GCOV_CONTENTS_MD5\@\",
+ \"coverage\": \@GCOV_FILE_COVERAGE\@
+ }"
+)
+
+message("\nGenerate JSON for files:")
+message("=========================")
+
+set(JSON_GCOV_FILES "[")
+
+# Read the GCOV files line by line and get the coverage data.
+foreach (GCOV_FILE ${GCOV_FILES})
+
+ get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
+ file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
+
+ # The new coveralls API doesn't need the entire source (Yay!)
+ # However, still keeping that part for now. Will cleanup in the future.
+ file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
+ message("MD5: ${GCOV_SRC_PATH} = ${GCOV_CONTENTS_MD5}")
+
+ # Loads the gcov file as a list of lines.
+ # (We first open the file and replace all occurences of [] with _
+ # because CMake will fail to parse a line containing unmatched brackets...
+ # also the \ to escaped \n in macros screws up things.)
+ # https://public.kitware.com/Bug/view.php?id=15369
+ file(READ ${GCOV_FILE} GCOV_CONTENTS)
+ string(REPLACE "[" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+ string(REPLACE "]" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+ string(REPLACE "\\" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+
+ # Remove file contents to avoid encoding issues (cmake 2.8 has no ENCODING option)
+ string(REGEX REPLACE "([^:]*):([^:]*):([^\n]*)\n" "\\1:\\2: \n" GCOV_CONTENTS "${GCOV_CONTENTS}")
+ file(WRITE ${GCOV_FILE}_tmp "${GCOV_CONTENTS}")
+
+ file(STRINGS ${GCOV_FILE}_tmp GCOV_LINES)
+ list(LENGTH GCOV_LINES LINE_COUNT)
+
+ # Instead of trying to parse the source from the
+ # gcov file, simply read the file contents from the source file.
+ # (Parsing it from the gcov is hard because C-code uses ; in many places
+ # which also happens to be the same as the CMake list delimeter).
+ file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE)
+
+ string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ # According to http://json.org/ these should be escaped as well.
+ # Don't know how to do that in CMake however...
+ #string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ #string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ #string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+
+ # We want a json array of coverage data as a single string
+ # start building them from the contents of the .gcov
+ set(GCOV_FILE_COVERAGE "[")
+
+ set(GCOV_LINE_COUNT 1) # Line number for the .gcov.
+ set(DO_SKIP 0)
+ foreach (GCOV_LINE ${GCOV_LINES})
+ #message("${GCOV_LINE}")
+ # Example of what we're parsing:
+ # Hitcount |Line | Source
+ # " 8: 26: if (!allowed || (strlen(allowed) == 0))"
+ string(REGEX REPLACE
+ "^([^:]*):([^:]*):(.*)$"
+ "\\1;\\2;\\3"
+ RES
+ "${GCOV_LINE}")
+
+ # Check if we should exclude lines using the Lcov syntax.
+ string(REGEX MATCH "LCOV_EXCL_START" START_SKIP "${GCOV_LINE}")
+ string(REGEX MATCH "LCOV_EXCL_END" END_SKIP "${GCOV_LINE}")
+ string(REGEX MATCH "LCOV_EXCL_LINE" LINE_SKIP "${GCOV_LINE}")
+
+ set(RESET_SKIP 0)
+ if (LINE_SKIP AND NOT DO_SKIP)
+ set(DO_SKIP 1)
+ set(RESET_SKIP 1)
+ endif()
+
+ if (START_SKIP)
+ set(DO_SKIP 1)
+ message("${GCOV_LINE_COUNT}: Start skip")
+ endif()
+
+ if (END_SKIP)
+ set(DO_SKIP 0)
+ endif()
+
+ list(LENGTH RES RES_COUNT)
+
+ if (RES_COUNT GREATER 2)
+ list(GET RES 0 HITCOUNT)
+ list(GET RES 1 LINE)
+ list(GET RES 2 SOURCE)
+
+ string(STRIP ${HITCOUNT} HITCOUNT)
+ string(STRIP ${LINE} LINE)
+
+ # Lines with 0 line numbers are metadata and can be ignored.
+ if (NOT ${LINE} EQUAL 0)
+
+ if (DO_SKIP)
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
+ else()
+ # Translate the hitcount into valid JSON values.
+ if (${HITCOUNT} STREQUAL "#####" OR ${HITCOUNT} STREQUAL "=====")
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
+ elseif (${HITCOUNT} STREQUAL "-")
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
+ else()
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ")
+ endif()
+ endif()
+ endif()
+ else()
+ message(WARNING "Failed to properly parse line (RES_COUNT = ${RES_COUNT}) ${GCOV_FILE}:${GCOV_LINE_COUNT}\n-->${GCOV_LINE}")
+ endif()
+
+ if (RESET_SKIP)
+ set(DO_SKIP 0)
+ endif()
+ math(EXPR GCOV_LINE_COUNT "${GCOV_LINE_COUNT}+1")
+ endforeach()
+
+ message("${GCOV_LINE_COUNT} of ${LINE_COUNT} lines read!")
+
+ # Advanced way of removing the trailing comma in the JSON array.
+ # "[1, 2, 3, " -> "[1, 2, 3"
+ string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
+
+ # Append the trailing ] to complete the JSON array.
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
+
+ # Generate the final JSON for this file.
+ message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...")
+ string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
+
+ set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
+endforeach()
+
+# Loop through all files we couldn't find any coverage for
+# as well, and generate JSON for those as well with 0% coverage.
+foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING})
+
+ # Set variables for json replacement
+ set(GCOV_SRC_PATH ${NOT_COVERED_SRC})
+ file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
+ file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
+
+ # Loads the source file as a list of lines.
+ file(STRINGS ${NOT_COVERED_SRC} SRC_LINES)
+
+ set(GCOV_FILE_COVERAGE "[")
+ set(GCOV_FILE_SOURCE "")
+
+ foreach (SOURCE ${SRC_LINES})
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
+
+ string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}")
+ string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}")
+ string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}")
+ string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}")
+ set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n")
+ endforeach()
+
+ # Remove trailing comma, and complete JSON array with ]
+ string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
+
+ # Generate the final JSON for this file.
+ message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...")
+ string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
+ set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
+endforeach()
+
+# Get rid of trailing comma.
+string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES})
+set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]")
+
+# Generate the final complete JSON!
+message("Generate final JSON...")
+string(CONFIGURE ${JSON_TEMPLATE} JSON)
+
+file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}")
+message("###########################################################################")
+message("Generated coveralls JSON containing coverage data:")
+message("${COVERALLS_OUTPUT_FILE}")
+message("###########################################################################")
diff --git a/cmake-modules/FindRT.cmake b/cmake-modules/FindRT.cmake
new file mode 100644
index 000000000..17d5df81d
--- /dev/null
+++ b/cmake-modules/FindRT.cmake
@@ -0,0 +1,20 @@
+# Try to find real time libraries
+# Once done, this will define
+#
+# RT_FOUND - system has rt library
+# RT_LIBRARIES - rt libraries directory
+#
+# Source: https://gitlab.cern.ch/dss/eos/commit/44070e575faaa46bd998708ef03eedb381506ff0
+#
+
+if(RT_LIBRARIES)
+ set(RT_FIND_QUIETLY TRUE)
+endif(RT_LIBRARIES)
+
+find_library(RT_LIBRARY rt)
+set(RT_LIBRARIES ${RT_LIBRARY})
+# handle the QUIETLY and REQUIRED arguments and set
+# RT_FOUND to TRUE if all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(rt DEFAULT_MSG RT_LIBRARY)
+mark_as_advanced(RT_LIBRARY)
diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp
index 9bef33525..d34901854 100644
--- a/code/3DSExporter.cpp
+++ b/code/3DSExporter.cpp
@@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "3DSExporter.h"
#include "3DSLoader.h"
+#include "3DSHelper.h"
#include "SceneCombiner.h"
#include "SplitLargeMeshes.h"
#include "StringComparison.h"
@@ -54,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
namespace Assimp {
+using namespace D3DS;
namespace {
diff --git a/code/3DSHelper.h b/code/3DSHelper.h
index b1de4ee77..7a2ad3f8a 100644
--- a/code/3DSHelper.h
+++ b/code/3DSHelper.h
@@ -369,14 +369,13 @@ struct Material
{
//! Default constructor. Builds a default name for the material
Material()
- :
- mDiffuse (0.6,0.6,0.6), // FIX ... we won't want object to be black
- mSpecularExponent (0.0),
- mShininessStrength (1.0),
- mShading(Discreet3DS::Gouraud),
- mTransparency (1.0),
- mBumpHeight (1.0),
- mTwoSided (false)
+ : mDiffuse ( ai_real( 0.6 ), ai_real( 0.6 ), ai_real( 0.6 ) ) // FIX ... we won't want object to be black
+ , mSpecularExponent ( ai_real( 0.0 ) )
+ , mShininessStrength ( ai_real( 1.0 ) )
+ , mShading(Discreet3DS::Gouraud)
+ , mTransparency ( ai_real( 1.0 ) )
+ , mBumpHeight ( ai_real( 1.0 ) )
+ , mTwoSided (false)
{
static int iCnt = 0;
diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp
index bae3cb1e6..0a1fe776b 100644
--- a/code/3DSLoader.cpp
+++ b/code/3DSLoader.cpp
@@ -1166,14 +1166,15 @@ void Discreet3DSImporter::ParseMaterialChunk()
case Discreet3DS::CHUNK_MAT_TRANSPARENCY:
{
- // This is the material's transparency
- ai_real* pcf = &mScene->mMaterials.back().mTransparency;
- *pcf = ParsePercentageChunk();
+ // This is the material's transparency
+ ai_real* pcf = &mScene->mMaterials.back().mTransparency;
+ *pcf = ParsePercentageChunk();
- // NOTE: transparency, not opacity
- if (is_qnan(*pcf))
- *pcf = 1.0;
- else *pcf = 1.0 - *pcf * (ai_real)0xFFFF / 100.0;
+ // NOTE: transparency, not opacity
+ if (is_qnan(*pcf))
+ *pcf = ai_real( 1.0 );
+ else
+ *pcf = ai_real( 1.0 ) - *pcf * (ai_real)0xFFFF / ai_real( 100.0 );
}
break;
@@ -1199,21 +1200,23 @@ void Discreet3DSImporter::ParseMaterialChunk()
case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT:
{ // This is the shininess strength of the material
- ai_real* pcf = &mScene->mMaterials.back().mShininessStrength;
- *pcf = ParsePercentageChunk();
- if (is_qnan(*pcf))
- *pcf = 0.0;
- else *pcf *= (ai_real)0xffff / 100.0;
+ ai_real* pcf = &mScene->mMaterials.back().mShininessStrength;
+ *pcf = ParsePercentageChunk();
+ if (is_qnan(*pcf))
+ *pcf = ai_real( 0.0 );
+ else
+ *pcf *= (ai_real)0xffff / ai_real( 100.0 );
}
break;
case Discreet3DS::CHUNK_MAT_SELF_ILPCT:
{ // This is the self illumination strength of the material
- ai_real f = ParsePercentageChunk();
- if (is_qnan(f))
- f = 0.0;
- else f *= (ai_real)0xFFFF / 100.0;
- mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f);
+ ai_real f = ParsePercentageChunk();
+ if (is_qnan(f))
+ f = ai_real( 0.0 );
+ else
+ f *= (ai_real)0xFFFF / ai_real( 100.0 );
+ mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f);
}
break;
@@ -1272,7 +1275,7 @@ void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
case Discreet3DS::CHUNK_PERCENTD:
// Manually parse the blend factor
- pcOut->mTextureBlend = stream->GetF8();
+ pcOut->mTextureBlend = ai_real( stream->GetF8() );
break;
case Discreet3DS::CHUNK_PERCENTF:
@@ -1282,7 +1285,7 @@ void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
case Discreet3DS::CHUNK_PERCENTW:
// Manually parse the blend factor
- pcOut->mTextureBlend = (ai_real)((uint16_t)stream->GetI2()) / 100.0;
+ pcOut->mTextureBlend = (ai_real)((uint16_t)stream->GetI2()) / ai_real( 100.0 );
break;
case Discreet3DS::CHUNK_MAT_MAP_USCALE:
@@ -1355,8 +1358,7 @@ ai_real Discreet3DSImporter::ParsePercentageChunk()
// ------------------------------------------------------------------------------------------------
// Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color
-void Discreet3DSImporter::ParseColorChunk(aiColor3D* out,
- bool acceptPercent)
+void Discreet3DSImporter::ParseColorChunk( aiColor3D* out, bool acceptPercent )
{
ai_assert(out != NULL);
@@ -1389,13 +1391,16 @@ void Discreet3DSImporter::ParseColorChunk(aiColor3D* out,
case Discreet3DS::CHUNK_LINRGBB:
bGamma = true;
case Discreet3DS::CHUNK_RGBB:
- if (sizeof(char) * 3 > diff) {
- *out = clrError;
- return;
+ {
+ if ( sizeof( char ) * 3 > diff ) {
+ *out = clrError;
+ return;
+ }
+ const ai_real invVal = ai_real( 1.0 ) / ai_real( 255.0 );
+ out->r = ( ai_real ) ( uint8_t ) stream->GetI1() * invVal;
+ out->g = ( ai_real ) ( uint8_t ) stream->GetI1() * invVal;
+ out->b = ( ai_real ) ( uint8_t ) stream->GetI1() * invVal;
}
- out->r = (ai_real)(uint8_t)stream->GetI1() / 255.0;
- out->g = (ai_real)(uint8_t)stream->GetI1() / 255.0;
- out->b = (ai_real)(uint8_t)stream->GetI1() / 255.0;
break;
// Percentage chunks are accepted, too.
@@ -1409,7 +1414,7 @@ void Discreet3DSImporter::ParseColorChunk(aiColor3D* out,
case Discreet3DS::CHUNK_PERCENTW:
if (acceptPercent && 1 <= diff) {
- out->g = out->b = out->r = (ai_real)(uint8_t)stream->GetI1() / 255.0;
+ out->g = out->b = out->r = (ai_real)(uint8_t)stream->GetI1() / ai_real( 255.0 );
break;
}
*out = clrError;
diff --git a/code/AMFImporter.cpp b/code/AMFImporter.cpp
new file mode 100644
index 000000000..d68e01904
--- /dev/null
+++ b/code/AMFImporter.cpp
@@ -0,0 +1,702 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2016, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/// \file AMFImporter.cpp
+/// \brief AMF-format files importer for Assimp: main algorithm implementation.
+/// \date 2016
+/// \author smal.root@gmail.com
+
+#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
+
+// Header files, Assimp.
+#include "AMFImporter.hpp"
+#include "AMFImporter_Macro.hpp"
+
+#include "fast_atof.h"
+#include "DefaultIOSystem.h"
+
+// Header files, stdlib.
+#include
+#include
+
+namespace Assimp
+{
+
+/// \var aiImporterDesc AMFImporter::Description
+/// Conastant which hold importer description
+const aiImporterDesc AMFImporter::Description = {
+ "Additive manufacturing file format(AMF) Importer",
+ "smalcom",
+ "",
+ "See documentation in source code. Chapter: Limitations.",
+ aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+ 0,
+ 0,
+ 0,
+ 0,
+ "amf"
+};
+
+void AMFImporter::Clear()
+{
+ mNodeElement_Cur = nullptr;
+ mUnit.clear();
+ mMaterial_Converted.clear();
+ mTexture_Converted.clear();
+ // Delete all elements
+ if(mNodeElement_List.size())
+ {
+ for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
+
+ mNodeElement_List.clear();
+ }
+}
+
+AMFImporter::~AMFImporter()
+{
+ if(mReader != nullptr) delete mReader;
+ // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
+ Clear();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: find set ************************************************************/
+/*********************************************************************************************************************************************/
+
+bool AMFImporter::Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const
+{
+ for(CAMFImporter_NodeElement* ne: mNodeElement_List)
+ {
+ if((ne->ID == pID) && (ne->Type == pType))
+ {
+ if(pNodeElement != nullptr) *pNodeElement = ne;
+
+ return true;
+ }
+ }// for(CAMFImporter_NodeElement* ne: mNodeElement_List)
+
+ return false;
+}
+
+bool AMFImporter::Find_ConvertedNode(const std::string& pID, std::list& pNodeList, aiNode** pNode) const
+{
+aiString node_name(pID.c_str());
+
+ for(aiNode* node: pNodeList)
+ {
+ if(node->mName == node_name)
+ {
+ if(pNode != nullptr) *pNode = node;
+
+ return true;
+ }
+ }// for(aiNode* node: pNodeList)
+
+ return false;
+}
+
+bool AMFImporter::Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const
+{
+ for(const SPP_Material& mat: mMaterial_Converted)
+ {
+ if(mat.ID == pID)
+ {
+ if(pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
+
+ return true;
+ }
+ }// for(const SPP_Material& mat: mMaterial_Converted)
+
+ return false;
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: throw set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::Throw_CloseNotFound(const std::string& pNode)
+{
+ throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
+}
+
+void AMFImporter::Throw_IncorrectAttr(const std::string& pAttrName)
+{
+ throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
+}
+
+void AMFImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
+{
+ throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
+}
+
+void AMFImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
+{
+ throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
+}
+
+void AMFImporter::Throw_ID_NotFound(const std::string& pID) const
+{
+ throw DeadlyImportError("Not found node with name \"" + pID + "\".");
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************* Functions: XML set ************************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::XML_CheckNode_MustHaveChildren()
+{
+ if(mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
+}
+
+void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
+{
+const size_t Uns_Skip_Len = 3;
+const char* Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
+
+static bool skipped_before[Uns_Skip_Len] = { false, false, false };
+
+std::string nn(mReader->getNodeName());
+bool found = false;
+bool close_found = false;
+size_t sk_idx;
+
+ for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
+ {
+ if(nn != Uns_Skip[sk_idx]) continue;
+
+ found = true;
+ if(mReader->isEmptyElement())
+ {
+ close_found = true;
+
+ goto casu_cres;
+ }
+
+ while(mReader->read())
+ {
+ if((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName()))
+ {
+ close_found = true;
+
+ goto casu_cres;
+ }
+ }
+ }// for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
+
+casu_cres:
+
+ if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
+ if(!close_found) Throw_CloseNotFound(nn);
+
+ if(!skipped_before[sk_idx])
+ {
+ skipped_before[sk_idx] = true;
+ LogWarning("Skipping node \"" + nn + "\" in " + pParentNodeName + ".");
+ }
+}
+
+bool AMFImporter::XML_SearchNode(const std::string& pNodeName)
+{
+ while(mReader->read())
+ {
+ if((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
+ }
+
+ return false;
+}
+
+bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
+{
+std::string val(mReader->getAttributeValue(pAttrIdx));
+
+ if((val == "false") || (val == "0"))
+ return false;
+ else if((val == "true") || (val == "1"))
+ return true;
+ else
+ throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
+}
+
+float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
+{
+std::string val;
+float tvalf;
+
+ ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
+ fast_atoreal_move(val.c_str(), tvalf, false);
+
+ return tvalf;
+}
+
+uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)
+{
+ return strtoul10(mReader->getAttributeValue(pAttrIdx));
+}
+
+float AMFImporter::XML_ReadNode_GetVal_AsFloat()
+{
+std::string val;
+float tvalf;
+
+ if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
+ if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
+
+ ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
+ fast_atoreal_move(val.c_str(), tvalf, false);
+
+ return tvalf;
+}
+
+uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32()
+{
+ if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
+ if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
+
+ return strtoul10(mReader->getNodeData());
+}
+
+void AMFImporter::XML_ReadNode_GetVal_AsString(std::string& pValue)
+{
+ if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
+ if(mReader->getNodeType() != irr::io::EXN_TEXT)
+ throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
+
+ pValue = mReader->getNodeData();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: parse set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode)
+{
+ mNodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
+ mNodeElement_Cur = pNode;// switch current element to new one.
+}
+
+void AMFImporter::ParseHelper_Node_Exit()
+{
+ // check if we can walk up.
+ if(mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
+}
+
+void AMFImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
+{
+size_t instr_len;
+
+ pOutString.clear();
+ instr_len = strlen(pInStr);
+ if(!instr_len) return;
+
+ pOutString.reserve(instr_len * 3 / 2);
+ // check and correct floats in format ".x". Must be "x.y".
+ if(pInStr[0] == '.') pOutString.push_back('0');
+
+ pOutString.push_back(pInStr[0]);
+ for(size_t ci = 1; ci < instr_len; ci++)
+ {
+ if((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t')))
+ {
+ pOutString.push_back('0');
+ pOutString.push_back('.');
+ }
+ else
+ {
+ pOutString.push_back(pInStr[ci]);
+ }
+ }
+}
+
+static bool ParseHelper_Decode_Base64_IsBase64(const char pChar)
+{
+ return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
+}
+
+void AMFImporter::ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector& pOutputData) const
+{
+// With help from
+// RenИ Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
+const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+uint8_t tidx = 0;
+uint8_t arr4[4], arr3[3];
+
+ // check input data
+ if(pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
+ // prepare output place
+ pOutputData.clear();
+ pOutputData.reserve(pInputBase64.size() / 4 * 3);
+
+ for(size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--)
+ {
+ if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
+ {
+ arr4[tidx++] = pInputBase64[in_idx++];
+ if(tidx == 4)
+ {
+ for(tidx = 0; tidx < 4; tidx++) arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
+
+ arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
+ arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
+ arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
+ for(tidx = 0; tidx < 3; tidx++) pOutputData.push_back(arr3[tidx]);
+
+ tidx = 0;
+ }// if(tidx == 4)
+ }// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
+ else
+ {
+ in_idx++;
+ }// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
+ }
+
+ if(tidx)
+ {
+ for(uint8_t i = tidx; i < 4; i++) arr4[i] = 0;
+ for(uint8_t i = 0; i < 4; i++) arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
+
+ arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
+ arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
+ arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
+ for(uint8_t i = 0; i < (tidx - 1); i++) pOutputData.push_back(arr3[i]);
+ }
+}
+
+void AMFImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
+{
+irr::io::IrrXMLReader* OldReader = mReader;// store current XMLreader.
+std::unique_ptr file(pIOHandler->Open(pFile, "rb"));
+
+ // Check whether we can read from the file
+ if(file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
+
+ // generate a XML reader for it
+ std::unique_ptr mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
+ mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
+ if(!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
+ //
+ // start reading
+ // search for root tag
+ if(XML_SearchNode("amf"))
+ ParseNode_Root();
+ else
+ throw DeadlyImportError("Root node \"amf\" not found.");
+
+ delete mReader;
+ // restore old XMLreader
+ mReader = OldReader;
+}
+
+//
+//
+// Root XML element.
+// Multi elements - No.
+void AMFImporter::ParseNode_Root()
+{
+std::string unit, version;
+CAMFImporter_NodeElement* ne;
+
+ // Read attributes for node .
+ MACRO_ATTRREAD_LOOPBEG;
+ MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
+ MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
+ MACRO_ATTRREAD_LOOPEND_WSKIP;
+
+ // Check attributes
+ if(!mUnit.empty())
+ {
+ if((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
+ }
+
+ // create root node element.
+ ne = new CAMFImporter_NodeElement_Root(nullptr);
+ mNodeElement_Cur = ne;// set first "current" element
+ // and assign attribute's values
+ ((CAMFImporter_NodeElement_Root*)ne)->Unit = unit;
+ ((CAMFImporter_NodeElement_Root*)ne)->Version = version;
+
+ // Check for child nodes
+ if(!mReader->isEmptyElement())
+ {
+ MACRO_NODECHECK_LOOPBEGIN("amf");
+ if(XML_CheckNode_NameEqual("object")) { ParseNode_Object(); continue; }
+ if(XML_CheckNode_NameEqual("material")) { ParseNode_Material(); continue; }
+ if(XML_CheckNode_NameEqual("texture")) { ParseNode_Texture(); continue; }
+ if(XML_CheckNode_NameEqual("constellation")) { ParseNode_Constellation(); continue; }
+ if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
+ MACRO_NODECHECK_LOOPEND("amf");
+ mNodeElement_Cur = ne;// force restore "current" element
+ }// if(!mReader->isEmptyElement())
+
+ mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
+}
+
+//
+//
+// A collection of objects or constellations with specific relative locations.
+// Multi elements - Yes.
+// Parent element - .
+void AMFImporter::ParseNode_Constellation()
+{
+std::string id;
+CAMFImporter_NodeElement* ne;
+
+ // Read attributes for node .
+ MACRO_ATTRREAD_LOOPBEG;
+ MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
+ MACRO_ATTRREAD_LOOPEND;
+
+ // create and if needed - define new grouping object.
+ ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
+
+ CAMFImporter_NodeElement_Constellation& als = *((CAMFImporter_NodeElement_Constellation*)ne);// alias for convenience
+
+ if(!id.empty()) als.ID = id;
+ // Check for child nodes
+ if(!mReader->isEmptyElement())
+ {
+ ParseHelper_Node_Enter(ne);
+ MACRO_NODECHECK_LOOPBEGIN("constellation");
+ if(XML_CheckNode_NameEqual("instance")) { ParseNode_Instance(); continue; }
+ if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
+ MACRO_NODECHECK_LOOPEND("constellation");
+ ParseHelper_Node_Exit();
+ }// if(!mReader->isEmptyElement())
+ else
+ {
+ mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+ }// if(!mReader->isEmptyElement()) else
+
+ mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+//
+//
+// A collection of objects or constellations with specific relative locations.
+// Multi elements - Yes.
+// Parent element - .
+void AMFImporter::ParseNode_Instance()
+{
+std::string objectid;
+CAMFImporter_NodeElement* ne;
+
+ // Read attributes for node .
+ MACRO_ATTRREAD_LOOPBEG;
+ MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
+ MACRO_ATTRREAD_LOOPEND;
+
+ // used object id must be defined, check that.
+ if(objectid.empty()) throw DeadlyImportError("\"objectid\" in must be defined.");
+ // create and define new grouping object.
+ ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
+
+ CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);// alias for convenience
+
+ als.ObjectID = objectid;
+ // Check for child nodes
+ if(!mReader->isEmptyElement())
+ {
+ bool read_flag[6] = { false, false, false, false, false, false };
+
+ als.Delta.Set(0, 0, 0);
+ als.Rotation.Set(0, 0, 0);
+ ParseHelper_Node_Enter(ne);
+ MACRO_NODECHECK_LOOPBEGIN("instance");
+ MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
+ MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
+ MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
+ MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
+ MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
+ MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
+ MACRO_NODECHECK_LOOPEND("instance");
+ ParseHelper_Node_Exit();
+ // also convert degrees to radians.
+ als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
+ als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
+ als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
+ }// if(!mReader->isEmptyElement())
+ else
+ {
+ mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+ }// if(!mReader->isEmptyElement()) else
+
+ mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+//
+// An object definition.
+// Multi elements - Yes.
+// Parent element - .
+void AMFImporter::ParseNode_Object()
+{
+std::string id;
+CAMFImporter_NodeElement* ne;
+
+ // Read attributes for node