diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 0aff9456..00000000 --- a/.cirrus.yml +++ /dev/null @@ -1,30 +0,0 @@ -build_and_store_wheels: &BUILD_AND_STORE_WHEELS - install_cibuildwheel_script: - - python -m pip install cibuildwheel==2.16.2 - run_cibuildwheel_script: - - cibuildwheel - wheels_artifacts: - path: "wheelhouse/*" - - # Upload only for tagged commit - only_if: $CIRRUS_TAG != '' - publish_script: - - python -m pip install twine - - python -m twine upload --repository-url https://upload.pypi.org/legacy/ --username __token__ wheelhouse/*.whl - - -linux_aarch64_task: - name: Build Linux aarch64 wheels. - compute_engine_instance: - image_project: cirrus-images - image: family/docker-builder-arm64 - architecture: arm64 - platform: linux - cpu: 4 - memory: 4G - environment: - TWINE_PASSWORD: ENCRYPTED[ade2037764e68fea251152f7585f3f77cdd748af06dc0f06942c45a8a8770fff19032c985f8dc193229c8adb2c0fecb9] - - install_pre_requirements_script: - - apt install -y python3-venv python-is-python3 - <<: *BUILD_AND_STORE_WHEELS diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 74210b03..00000000 --- a/.clang-format +++ /dev/null @@ -1,7 +0,0 @@ ---- -BasedOnStyle: Google -IndentWidth: 2 -TabWidth: 2 -UseTab: Never -BreakBeforeBraces: Attach -Standard: Cpp03 diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 971557fd..00000000 --- a/.drone.yml +++ /dev/null @@ -1,10 +0,0 @@ -image: syoyo/ubu-dev -script: - - curl -L -o premake4 https://github.com/syoyo/orebuildenv/blob/master/build/linux/bin/premake4?raw=true - - chmod +x ./premake4 - - ./premake4 gmake - - make -notify: - email: - recipients: - - syoyo@lighttransport.com diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..bdb0cabc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 0fd988d9..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,15 +0,0 @@ -# These are supported funding model platforms - -github: syoyo # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -polar: # Replace with a single Polar username -buy_me_a_coffee: # Replace with a single Buy Me a Coffee username -thanks_dev: # Replace with a single thanks.dev username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3ba13e0c..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/issue-report.md b/.github/ISSUE_TEMPLATE/issue-report.md deleted file mode 100644 index 1361a329..00000000 --- a/.github/ISSUE_TEMPLATE/issue-report.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Issue report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the issue** -A clear and concise description of what the issue is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Compile TinyObjLoader with '...' -2. Load .obj file '...' -3. See error - -Please attach minimal and reproducible files(source codes, .obj/.mtl files, etc) - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Environment** - - TinyObjLoader version - - OS: [e.g. Linux] - - Compiler [e.g. gcc 7.3] - - Other environment [e.g. Python version if you use python binding] diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml deleted file mode 100644 index b37104a2..00000000 --- a/.github/workflows/wheels.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: Build and upload to PyPI - -# Build on every branch push, tag push, and pull request change: -on: [push, pull_request] - -jobs: - - build_wheels: - name: Build wheels on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true # Optional, use if you use setuptools_scm - - - name: Build wheels - uses: pypa/cibuildwheel@v2.16.5 - # to supply options, put them in 'env', like: - # env: - # CIBW_SOME_OPTION: value - # Disable building PyPy wheels on all platforms - env: - CIBW_ARCHS_MACOS: "x86_64 universal2 arm64" - CIBW_ARCHS_WINDOWS: "AMD64 x86" - # disable aarm64 build since its too slow to build(docker + qemu) - CIBW_ARCHS_LINUX: "x86_64 i686" - # it looks cibuildwheel fails to add version string to wheel file for python 3.6, so skip it - CIBW_SKIP: pp* - - - uses: actions/upload-artifact@v4 - with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} - path: ./wheelhouse/*.whl - - # It looks cibuildwheels did not clean build folder(CMake), and it results to Windows arm64 build failure(trying to reuse x86 build of .obj) - # So supply separated build job for Windows ARM64 build - # TODO: clean build folder using CIBW_BEFORE_ALL? - build_win_arm64_wheels: - name: Build ARM64 wheels on Windows. - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true # Optional, use if you use setuptools_scm - - - name: Build wheels - uses: pypa/cibuildwheel@v2.16.5 - # to supply options, put them in 'env', like: - # env: - # CIBW_SOME_OPTION: value - # Disable building PyPy wheels on all platforms - env: - CIBW_ARCHS_WINDOWS: "ARM64" - CIBW_SKIP: pp* - - - uses: actions/upload-artifact@v4 - with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} - path: ./wheelhouse/*.whl - - make_sdist: - name: Make SDist - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Optional, use if you use setuptools_scm - fetch-tags: true # Optional, use if you use setuptools_scm - - - name: Build SDist - run: pipx run build --sdist - - - uses: actions/upload-artifact@v4 - with: - name: cibw-sdist - path: dist/*.tar.gz - - upload_all: - needs: [build_wheels, build_wheels, make_sdist] - runs-on: ubuntu-latest - environment: release - permissions: - # IMPORTANT: this permission is mandatory for trusted publishing - id-token: write - # upload to PyPI on every tag starting with 'v' - # NOTE: Without github.event_name & githug.ref check, `upload_all` task is still triggered on 'main' branch push. - # (then get 'Branch "main" is not allowed to deploy to release due to environment protection rules.' error) - # So still do event_name and github.ref check. - # TODO: Make it work only using Github `environment` feature. - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - # alternatively, to publish when a GitHub Release is created, use the following rule: - # if: github.event_name == 'push' && github.event.action == 'published' - steps: - - uses: actions/download-artifact@v4 - with: - pattern: cibw-* - path: dist - merge-multiple: true - - - uses: pypa/gh-action-pypi-publish@release/v1 - with: - # Use Trusted Publisher feature: - # https://docs.pypi.org/trusted-publishers/ - # so no use of PYPI_API_TOKEN - #password: ${{ secrets.PYPI_API_TOKEN }} - # - # Avoid race condition when using multiple CIs - skip-existing: true - verbose: true diff --git a/.gitignore b/.gitignore index f9b4d691..94e381f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,50 @@ -#Common folder for building objects -build/ -/python/dist/ -/python/*.egg-info -/python/.eggs -/python/tiny_obj_loader.h -/tests/tester -/tests/tester.dSYM +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Visual Studio Directory +Visual_Studio/ + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 12b67f28..00000000 --- a/.travis.yml +++ /dev/null @@ -1,56 +0,0 @@ -language: cpp -sudo: required -matrix: - include: - - addons: &1 - apt: - sources: - - george-edison55-precise-backports - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.7 - packages: - - cmake - - cmake-data - - ninja-build - - g++-4.9 - - clang-3.7 - compiler: clang - env: DEPLOY_BUILD=1 COMPILER_VERSION=3.7 BUILD_TYPE=Debug - - addons: *1 - compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Release - - addons: &2 - apt: - sources: - - george-edison55-precise-backports - - ubuntu-toolchain-r-test - packages: - - cmake - - cmake-data - - ninja-build - - g++-4.9 - compiler: gcc - env: COMPILER_VERSION=4.9 BUILD_TYPE=Debug - - addons: *2 - compiler: gcc - env: COMPILER_VERSION=4.9 BUILD_TYPE=Release - - addons: *1 - compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug -before_install: -- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi -- if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get update python; fi -- if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get install python-dev libffi-dev libssl-dev; fi -- if [ -n "$REPORT_COVERAGE" ]; then sudo pip install --upgrade pip; fi -- if [ -n "$REPORT_COVERAGE" ]; then CXX=g++ pip install --user requests[security]; fi -- if [ -n "$REPORT_COVERAGE" ]; then CXX=g++ pip install --user cpp-coveralls; fi -script: -- cd tests -- make check -- if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r .. -e examples -e tools -e - jni -e python -e images -E ".*CompilerId.*" -E ".*feature_tests.*" ; fi -- cd .. -- rm -rf dist -- mkdir dist -- cp tiny_obj_loader.h dist/ - diff --git a/BUILD.bazel b/BUILD.bazel deleted file mode 100644 index dda60c76..00000000 --- a/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -cc_library( - name = "tinyobjloader", - hdrs = ["tiny_obj_loader.h"], - copts = select({ - "@platforms//os:windows": [], - "//conditions:default": ["-Wno-maybe-uninitialized"], - }), - visibility = ["//visibility:public"], -) diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 9aea91fa..00000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,221 +0,0 @@ -#Tiny Object Loader Cmake configuration file. -#This configures the Cmake system with multiple properties, depending -#on the platform and configuration it is set to build in. -cmake_minimum_required(VERSION 3.16) -project(tinyobjloader CXX) -set(TINYOBJLOADER_SOVERSION 2) -set(TINYOBJLOADER_VERSION 2.0.0-rc.13) -set(PY_TARGET "pytinyobjloader") - -#optional double precision support -option(TINYOBJLOADER_USE_DOUBLE "Build library with double precision instead of single (float)" OFF) -option(TINYOBJLOADER_WITH_PYTHON "Build Python module(for developer). Use pyproject.toml/setup.py to build Python module for end-users" OFF) -option(TINYOBJLOADER_PREFER_LOCAL_PYTHON_INSTALLATION - "Prefer locally-installed Python interpreter than system or conda/brew installed Python. Please specify your Python interpreter with `Python3_EXECUTABLE` cmake option if you enable this option." - OFF) - -list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/sanitizers) -find_package(Sanitizers) # Address sanitizer (-DSANITIZE_ADDRESS=ON) - - -if(TINYOBJLOADER_USE_DOUBLE) - set(LIBRARY_NAME ${PROJECT_NAME}_double) -else() - set(LIBRARY_NAME ${PROJECT_NAME}) -endif() - -#Folder Shortcuts -set(TINYOBJLOADEREXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/examples) - -set(tinyobjloader-Source - ${CMAKE_CURRENT_SOURCE_DIR}/tiny_obj_loader.h - ${CMAKE_CURRENT_SOURCE_DIR}/tiny_obj_loader.cc - ) - -set(tinyobjloader-Example-Source - ${CMAKE_CURRENT_SOURCE_DIR}/loader_example.cc - ) - -set(tinyobjloader-examples-objsticher - ${TINYOBJLOADEREXAMPLES_DIR}/obj_sticher/obj_writer.h - ${TINYOBJLOADEREXAMPLES_DIR}/obj_sticher/obj_writer.cc - ${TINYOBJLOADEREXAMPLES_DIR}/obj_sticher/obj_sticher.cc - ) - -#Install destinations -include(GNUInstallDirs) - -set(TINYOBJLOADER_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake) -set(TINYOBJLOADER_DOC_DIR ${CMAKE_INSTALL_DOCDIR}) -set(TINYOBJLOADER_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}) -set(TINYOBJLOADER_LIBRARY_DIR ${CMAKE_INSTALL_LIBDIR}) -set(TINYOBJLOADER_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -set(TINYOBJLOADER_RUNTIME_DIR ${CMAKE_INSTALL_BINDIR}) - -option(TINYOBJLOADER_BUILD_TEST_LOADER "Build Example Loader Application" OFF) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -# Build standalone .so for Python binding(for developer) -if (TINYOBJLOADER_WITH_PYTHON) - - if(TINYOBJLOADER_PREFER_LOCAL_PYTHON_INSTALLATION) - #message(STATUS "Local Python") - set(Python3_FIND_FRAMEWORK NEVER) # Do not search framework python - set(Python3_FIND_STRATEGY LOCATION) - set(Python3_FIND_REGISTRY NEVER) # Windows only - else() - set(Python3_FIND_FRAMEWORK LAST - )# Prefer Brew/Conda to Apple framework python - endif() - - find_package( - Python3 - COMPONENTS Interpreter Development - REQUIRED) - - find_package(pybind11 CONFIG REQUIRED) - -endif() - - - -add_library(${LIBRARY_NAME} ${tinyobjloader-Source}) -add_sanitizers(${LIBRARY_NAME}) - -if(BUILD_SHARED_LIBS) - set_target_properties(${LIBRARY_NAME} PROPERTIES - SOVERSION ${TINYOBJLOADER_SOVERSION} - WINDOWS_EXPORT_ALL_SYMBOLS ON - ) -endif() - -if(TINYOBJLOADER_USE_DOUBLE) - target_compile_definitions(${LIBRARY_NAME} PUBLIC TINYOBJLOADER_USE_DOUBLE) -endif() - -set_target_properties(${LIBRARY_NAME} PROPERTIES VERSION ${TINYOBJLOADER_VERSION}) - -target_include_directories(${LIBRARY_NAME} INTERFACE - $ - $ - ) - -export(TARGETS ${LIBRARY_NAME} FILE ${PROJECT_NAME}-targets.cmake) - -if(TINYOBJLOADER_BUILD_TEST_LOADER) - add_executable(test_loader ${tinyobjloader-Example-Source}) - target_link_libraries(test_loader ${LIBRARY_NAME}) -endif() - -option(TINYOBJLOADER_BUILD_OBJ_STICHER "Build OBJ Sticher Application" OFF) -if(TINYOBJLOADER_BUILD_OBJ_STICHER) - add_executable(obj_sticher ${tinyobjloader-examples-objsticher}) - target_link_libraries(obj_sticher ${LIBRARY_NAME}) - - install(TARGETS - obj_sticher - DESTINATION - ${TINYOBJLOADER_RUNTIME_DIR} - ) -endif() - -if (TINYOBJLOADER_WITH_PYTHON) - # pybind11 method: - pybind11_add_module(${PY_TARGET} ${CMAKE_SOURCE_DIR}/python/bindings.cc ${CMAKE_SOURCE_DIR}/python/tiny_obj_loader.cc) - - add_sanitizers(${PY_TARGET}) - set_target_properties(${PY_TARGET} PROPERTIES OUTPUT_NAME "tinyobjloader") - - # copy .so to jdepp/ - add_custom_command( - TARGET ${PY_TARGET} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy "$" - "${CMAKE_SOURCE_DIR}/python/$" - COMMENT "copying tinyobjloader native python module file to python/" - VERBATIM) - -endif() - -#Write CMake package config files -include(CMakePackageConfigHelpers) - -configure_package_config_file( - ${PROJECT_NAME}-config.cmake.in - ${PROJECT_NAME}-config.cmake - INSTALL_DESTINATION - ${TINYOBJLOADER_CMAKE_DIR} - PATH_VARS - TINYOBJLOADER_INCLUDE_DIR - TINYOBJLOADER_LIBRARY_DIR - NO_CHECK_REQUIRED_COMPONENTS_MACRO - ) - -write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake - VERSION - ${TINYOBJLOADER_VERSION} - COMPATIBILITY - SameMajorVersion - ) - -#pkg-config file -configure_file(${PROJECT_NAME}.pc.in ${LIBRARY_NAME}.pc @ONLY) - -if(DEFINED ENV{LIB_FUZZING_ENGINE}) - add_executable(fuzz_ParseFromString fuzzer/fuzz_ParseFromString.cc) - target_link_libraries(fuzz_ParseFromString ${LIBRARY_NAME} $ENV{LIB_FUZZING_ENGINE}) -endif() - -#Installation -install(TARGETS - ${LIBRARY_NAME} - EXPORT ${PROJECT_NAME}-targets - DESTINATION - ${TINYOBJLOADER_LIBRARY_DIR} - PUBLIC_HEADER DESTINATION - ${TINYOBJLOADER_INCLUDE_DIR} - RUNTIME DESTINATION - ${TINYOBJLOADER_RUNTIME_DIR} - ) -install(EXPORT - ${PROJECT_NAME}-targets - NAMESPACE - tinyobjloader:: - DESTINATION - ${TINYOBJLOADER_CMAKE_DIR} - ) -install(FILES - tiny_obj_loader.h - DESTINATION - ${TINYOBJLOADER_INCLUDE_DIR} - ) -install(FILES - LICENSE - DESTINATION - ${TINYOBJLOADER_DOC_DIR} - ) -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" - DESTINATION - ${TINYOBJLOADER_CMAKE_DIR} - ) -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}.pc" - DESTINATION - ${TINYOBJLOADER_PKGCONFIG_DIR} - ) - -if(NOT TARGET uninstall) - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake IMMEDIATE @ONLY) - - add_custom_target( - uninstall COMMAND ${CMAKE_COMMAND} -P - ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) -endif() diff --git a/LICENSE b/LICENSE deleted file mode 100644 index e9fbe447..00000000 --- a/LICENSE +++ /dev/null @@ -1,42 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2012-2019 Syoyo Fujita and many contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------- - -mapbox/earcut.hpp - -ISC License - -Copyright (c) 2015, Mapbox - -Permission to use, copy, modify, and/or distribute this software for any purpose -with or without fee is hereby granted, provided that the above copyright notice -and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -THIS SOFTWARE. - diff --git a/experimental/lfpAlloc/LICENSE b/LICENSE.txt similarity index 87% rename from experimental/lfpAlloc/LICENSE rename to LICENSE.txt index b9e2c10a..7b47e98e 100644 --- a/experimental/lfpAlloc/LICENSE +++ b/LICENSE.txt @@ -1,6 +1,6 @@ -The MIT License (MIT) +MIT License -Copyright (c) 2014 Adam Schwalm +Copyright (c) 2016 Robert Smith Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ 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 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. \ No newline at end of file +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index d2632da5..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,8 +0,0 @@ -include pyproject.toml -include setup.py -include README.md -include LICENSE -include python/sample.py -include python/bindings.cc -include python/tiny_obj_loader.cc -include tiny_obj_loader.h diff --git a/MODULE.bazel b/MODULE.bazel deleted file mode 100644 index f8859286..00000000 --- a/MODULE.bazel +++ /dev/null @@ -1,9 +0,0 @@ -module( - name = "tinyobjloader", - compatibility_level = 1, -) - -bazel_dep( - name = "platforms", - version = "0.0.8", -) diff --git a/README.md b/README.md index 8bfcdc2b..5734f966 100644 --- a/README.md +++ b/README.md @@ -1,443 +1,107 @@ -# tinyobjloader +# OBJ LOADER -[![PyPI version](https://badge.fury.io/py/tinyobjloader.svg)](https://badge.fury.io/py/tinyobjloader) +The quick to use single header OBJ loader -[![AZ Build Status](https://dev.azure.com/tinyobjloader/tinyobjloader/_apis/build/status/tinyobjloader.tinyobjloader?branchName=master)](https://dev.azure.com/tinyobjloader/tinyobjloader/_build/latest?definitionId=1&branchName=master) +## Quick Description -[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/m6wfkvket7gth8wn/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyobjloader-6e4qf/branch/master) +OBJ Loader is a simple, header only, .obj model file loader that will take in a path to a file, load it into the Loader class object, then allow you to get the data from each mesh loaded. This will load each mesh within the model with the corresponding data such as vertices, indices, and material. Plus a large array of vertices, indices and materials which you can do whatever you want with. -[![Coverage Status](https://coveralls.io/repos/github/syoyo/tinyobjloader/badge.svg?branch=master)](https://coveralls.io/github/syoyo/tinyobjloader?branch=master) +## Prerequisites -[![AUR version](https://img.shields.io/aur/version/tinyobjloader?logo=arch-linux)](https://aur.archlinux.org/packages/tinyobjloader) +OBJ Loader was made with compatibility in mind. So you only need the header file to get going as it uses only STD and self made data structures. -Tiny but powerful single file wavefront obj loader written in C++03. No dependency except for C++ STL. It can parse over 10M polygons with moderate memory and time. +## Installation -`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) +All you need to to is copy over the OBJ_Loader.h header file, include it in your solution, and you are good to go. It is located within the source folder. -If you are looking for C99 version, please see https://github.com/syoyo/tinyobjloader-c . +## Examples -Version notice --------------- +Examples are found within the examples folder. In order to compile these you will need to link OBJ_Loader.h to the compiler in since it is not included within the example folders. -We recommend to use `master`(`main`) branch. Its v2.0 release candidate. Most features are now nearly robust and stable(Remaining task for release v2.0 is polishing C++ and Python API, and fix built-in triangulation code). +## Quick Use Guide -We have released new version v1.0.0 on 20 Aug, 2016. -Old version is available as `v0.9.x` branch https://github.com/syoyo/tinyobjloader/tree/v0.9.x +1. Include OBJ_Loader.h: '#include "OBJ_Loader.h"' +2. Create a Loader Object: 'objl::Loader loader' +3. Load a File Into the Loader Object: 'loader.LoadFile("path_to_object_and_name.obj")' +4. Get What you want out of the Mesh Objects: 'cout << loader.LoadedMeshes[0].MeshName << endl' -## What's new +## Classes & Structures -* 29 Jul, 2021 : Added Mapbox's earcut for robust triangulation. Also fixes triangulation bug(still there is some issue in built-in triangulation algorithm: https://github.com/tinyobjloader/tinyobjloader/issues/319). -* 19 Feb, 2020 : The repository has been moved to https://github.com/tinyobjloader/tinyobjloader ! -* 18 May, 2019 : Python binding!(See `python` folder. Also see https://pypi.org/project/tinyobjloader/) -* 14 Apr, 2019 : Bump version v2.0.0 rc0. New C++ API and python bindings!(1.x API still exists for backward compatibility) -* 20 Aug, 2016 : Bump version v1.0.0. New data structure and API! +These are all of the included classes and only the relevant members and methods. There are others to find if you want but these are all you will need to know to operate. -## Requirements +### Vector2 -* C++03 compiler +1. float X, Y : Position Variables -### Old version +### Vector3 -Previous old version is available in `v0.9.x` branch. +1. float X, Y, Z : Position Variables -## Example +### Vertex -![Rungholt](images/rungholt.jpg) - -tinyobjloader can successfully load 6M triangles Rungholt scene. -http://casual-effects.com/data/index.html - -![](images/sanmugel.png) - -* [examples/viewer/](examples/viewer) OpenGL .obj viewer -* [examples/callback_api/](examples/callback_api/) Callback API example -* [examples/voxelize/](examples/voxelize/) Voxelizer example - -## Use case - -TinyObjLoader is successfully used in ... - -### New version(v1.0.x) - -* Double precision support through `TINYOBJLOADER_USE_DOUBLE` thanks to noma -* Loading models in Vulkan Tutorial https://vulkan-tutorial.com/Loading_models -* .obj viewer with Metal https://github.com/middlefeng/NuoModelViewer/tree/master -* Vulkan Cookbook https://github.com/PacktPublishing/Vulkan-Cookbook -* cudabox: CUDA Solid Voxelizer Engine https://github.com/gaspardzoss/cudavox -* Drake: A planning, control, and analysis toolbox for nonlinear dynamical systems https://github.com/RobotLocomotion/drake -* VFPR - a Vulkan Forward Plus Renderer : https://github.com/WindyDarian/Vulkan-Forward-Plus-Renderer -* glslViewer: https://github.com/patriciogonzalezvivo/glslViewer -* Lighthouse2: https://github.com/jbikker/lighthouse2 -* rayrender(an open source R package for raytracing scenes in created in R): https://github.com/tylermorganwall/rayrender -* liblava - A modern C++ and easy-to-use framework for the Vulkan API. [MIT]: https://github.com/liblava/liblava -* rtxON - Simple Vulkan raytracing tutorials https://github.com/iOrange/rtxON -* metal-ray-tracer - Writing ray-tracer using Metal Performance Shaders https://github.com/sergeyreznik/metal-ray-tracer https://sergeyreznik.github.io/metal-ray-tracer/index.html -* Supernova Engine - 2D and 3D projects with Lua or C++ in data oriented design: https://github.com/supernovaengine/supernova -* AGE (Arc Game Engine) - An open-source engine for building 2D & 3D real-time rendering and interactive contents: https://github.com/MohitSethi99/ArcGameEngine -* [Wicked Engine](https://github.com/turanszkij/WickedEngine) - 3D engine with modern graphics -* [Lumina Game Engine](https://github.com/MrDrElliot/LuminaEngine) - A modern, high-performance game engine built with Vulkan -* Your project here! (Plese send PR) - -### Old version(v0.9.x) - -* bullet3 https://github.com/erwincoumans/bullet3 -* pbrt-v2 https://github.com/mmp/pbrt-v2 -* OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 -* mallie https://lighttransport.github.io/mallie -* IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/ -* Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf -* Awesome Bump http://awesomebump.besaba.com/about/ -* sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront -* pbrt-v3 https://github.com/mmp/pbrt-v3 -* cocos2d-x https://github.com/cocos2d/cocos2d-x/ -* Android Vulkan demo https://github.com/SaschaWillems/Vulkan -* voxelizer https://github.com/karimnaaji/voxelizer -* Probulator https://github.com/kayru/Probulator -* OptiX Prime baking https://github.com/nvpro-samples/optix_prime_baking -* FireRays SDK https://github.com/GPUOpen-LibrariesAndSDKs/FireRays_SDK -* parg, tiny C library of various graphics utilities and GL demos https://github.com/prideout/parg -* Opengl unit of ChronoEngine https://github.com/projectchrono/chrono-opengl -* Point Based Global Illumination on modern GPU https://pbgi.wordpress.com/code-source/ -* Fast OBJ file importing and parsing in CUDA http://researchonline.jcu.edu.au/42515/1/2015.CVM.OBJCUDA.pdf -* Sorted Shading for Uni-Directional Pathtracing by Joshua Bainbridge https://nccastaff.bournemouth.ac.uk/jmacey/MastersProjects/MSc15/02Josh/joshua_bainbridge_thesis.pdf -* GeeXLab http://www.geeks3d.com/hacklab/20160531/geexlab-0-12-0-0-released-for-windows/ - - -## Features - -* Group(parse multiple group name) -* Vertex - * Vertex color(as an extension: https://blender.stackexchange.com/questions/31997/how-can-i-get-vertex-painted-obj-files-to-import-into-blender) -* Texcoord -* Normal -* Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification) -* Callback API for custom loading. -* Double precision support(for HPC application). -* Smoothing group -* Python binding : See `python` folder. - * Precompiled binary(manylinux1-x86_64 only) is hosted at pypi https://pypi.org/project/tinyobjloader/) - -### Primitives - -* [x] face(`f`) -* [x] lines(`l`) -* [ ] points(`p`) -* [ ] curve -* [ ] 2D curve -* [ ] surface. -* [ ] Free form curve/surfaces +1. Vector3 Position : Position vector +2. Vector3 Normal : Normal vector +3. Vector2 TextureCoordinate : Texture Coordinate vector ### Material -* PBR material extension for .MTL. Please see [pbr-mtl.md](pbr-mtl.md) for details. -* Texture options -* Unknown material attributes are returned as key-value(value is string) map. - -## TODO - -* [ ] Fix obj_sticker example. -* [ ] More unit test codes. - -## License - -TinyObjLoader is licensed under MIT license. - -### Third party licenses. - -* pybind11 : BSD-style license. -* mapbox earcut.hpp: ISC License. - -## Usage - -### Installation - -One option is to simply copy the header file into your project and to make sure that `TINYOBJLOADER_IMPLEMENTATION` is defined exactly once. - -### Building tinyobjloader - Using vcpkg(not recommended though) - -Although it is not a recommended way, you can download and install tinyobjloader using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: - - git clone https://github.com/Microsoft/vcpkg.git - cd vcpkg - ./bootstrap-vcpkg.sh - ./vcpkg integrate install - ./vcpkg install tinyobjloader - -The tinyobjloader port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. - -### Data format - -`attrib_t` contains single and linear array of vertex data(position, normal and texcoord). - -``` -attrib_t::vertices => 3 floats per vertex - - v[0] v[1] v[2] v[3] v[n-1] - +-----------+-----------+-----------+-----------+ +-----------+ - | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | - +-----------+-----------+-----------+-----------+ +-----------+ - -attrib_t::normals => 3 floats per vertex - - n[0] n[1] n[2] n[3] n[n-1] - +-----------+-----------+-----------+-----------+ +-----------+ - | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | - +-----------+-----------+-----------+-----------+ +-----------+ - -attrib_t::texcoords => 2 floats per vertex - - t[0] t[1] t[2] t[3] t[n-1] - +-----------+-----------+-----------+-----------+ +-----------+ - | u | v | u | v | u | v | u | v | .... | u | v | - +-----------+-----------+-----------+-----------+ +-----------+ - -attrib_t::colors => 3 floats per vertex(vertex color. optional) +1. std::string name : Name of loaded material +2. Vector3 Ka : Ambient color +3. Vector3 Kd : Diffuse color +4. Vector3 Ks : Specular color +5. float Ns : Specular exponent +6. float Ni : Optical density +7. float d : Dissolve variable +8. int illum : Illumination variable +9. std::string map_Ka : Ambient texture map name +10. std::string map_Kd : Diffuse texture map name +11. std::string map_Ks : Specular texture map name +12. std::string map_d : Alpha texture map name +13. std::string map_bump : Bump map name - c[0] c[1] c[2] c[3] c[n-1] - +-----------+-----------+-----------+-----------+ +-----------+ - | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | - +-----------+-----------+-----------+-----------+ +-----------+ +### Mesh -``` +1. std::string MeshName : The Mesh Name given in the .obj +2. std::vector Vertices : Vertex List +3. std::vector Indices : Index List +4. Material MeshMaterial : Material assigned to this mesh -Each `shape_t::mesh_t` does not contain vertex data but contains array index to `attrib_t`. -See `loader_example.cc` for more details. +### Loader +1. bool LoadFile(std::string Path) : Load a file from a path. Return true if found and loaded. Return false if not +2. std::vector LoadedMeshes : Loaded Mesh Objects +3. std::vector LoadedVertices : Loaded Vertex Objects +4. std::vector LoadedIndices : Loaded Index Positions +5. std::vector LoadedMaterials : Loaded Material Objects -``` +## Credits -mesh_t::indices => array of vertex indices. +Robert Smith - +----+----+----+----+----+----+----+----+----+----+ +--------+ - | i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7 | i8 | i9 | ... | i(n-1) | - +----+----+----+----+----+----+----+----+----+----+ +--------+ - -Each index has an array index to attrib_t::vertices, attrib_t::normals and attrib_t::texcoords. - -mesh_t::num_face_vertices => array of the number of vertices per face(e.g. 3 = triangle, 4 = quad , 5 or more = N-gons). - - - +---+---+---+ +---+ - | 3 | 4 | 3 | ...... | 3 | - +---+---+---+ +---+ - | | | | - | | | +-----------------------------------------+ - | | | | - | | +------------------------------+ | - | | | | - | +------------------+ | | - | | | | - |/ |/ |/ |/ - - mesh_t::indices - - | face[0] | face[1] | face[2] | | face[n-1] | - +----+----+----+----+----+----+----+----+----+----+ +--------+--------+--------+ - | i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7 | i8 | i9 | ... | i(n-3) | i(n-2) | i(n-1) | - +----+----+----+----+----+----+----+----+----+----+ +--------+--------+--------+ - -``` - -Note that when `triangulate` flag is true in `tinyobj::LoadObj()` argument, `num_face_vertices` are all filled with 3(triangle). - -### float data type - -TinyObjLoader now use `real_t` for floating point data type. -Default is `float(32bit)`. -You can enable `double(64bit)` precision by using `TINYOBJLOADER_USE_DOUBLE` define. - -### Robust triangulation - -When you enable `triangulation`(default is enabled), -TinyObjLoader triangulate polygons(faces with 4 or more vertices). - -Built-in triangulation code may not work well in some polygon shape. - -You can define `TINYOBJLOADER_USE_MAPBOX_EARCUT` for robust triangulation using `mapbox/earcut.hpp`. -This requires C++11 compiler though. And you need to copy `mapbox/earcut.hpp` to your project. -If you have your own `mapbox/earcut.hpp` file incuded in your project, you can define `TINYOBJLOADER_DONOT_INCLUDE_MAPBOX_EARCUT` so that `mapbox/earcut.hpp` is not included inside of `tiny_obj_loader.h`. - -#### Example code (Deprecated API) - -```c++ -#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc -// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust triangulation. Requires C++11 -//#define TINYOBJLOADER_USE_MAPBOX_EARCUT -#include "tiny_obj_loader.h" - -std::string inputfile = "cornell_box.obj"; -tinyobj::attrib_t attrib; -std::vector shapes; -std::vector materials; - -std::string warn; -std::string err; - -bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, inputfile.c_str()); - -if (!warn.empty()) { - std::cout << warn << std::endl; -} - -if (!err.empty()) { - std::cerr << err << std::endl; -} - -if (!ret) { - exit(1); -} - -// Loop over shapes -for (size_t s = 0; s < shapes.size(); s++) { - // Loop over faces(polygon) - size_t index_offset = 0; - for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { - size_t fv = size_t(shapes[s].mesh.num_face_vertices[f]); - - // Loop over vertices in the face. - for (size_t v = 0; v < fv; v++) { - // access to vertex - tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v]; - - tinyobj::real_t vx = attrib.vertices[3*size_t(idx.vertex_index)+0]; - tinyobj::real_t vy = attrib.vertices[3*size_t(idx.vertex_index)+1]; - tinyobj::real_t vz = attrib.vertices[3*size_t(idx.vertex_index)+2]; - - // Check if `normal_index` is zero or positive. negative = no normal data - if (idx.normal_index >= 0) { - tinyobj::real_t nx = attrib.normals[3*size_t(idx.normal_index)+0]; - tinyobj::real_t ny = attrib.normals[3*size_t(idx.normal_index)+1]; - tinyobj::real_t nz = attrib.normals[3*size_t(idx.normal_index)+2]; - } - - // Check if `texcoord_index` is zero or positive. negative = no texcoord data - if (idx.texcoord_index >= 0) { - tinyobj::real_t tx = attrib.texcoords[2*size_t(idx.texcoord_index)+0]; - tinyobj::real_t ty = attrib.texcoords[2*size_t(idx.texcoord_index)+1]; - } - // Optional: vertex colors - // tinyobj::real_t red = attrib.colors[3*size_t(idx.vertex_index)+0]; - // tinyobj::real_t green = attrib.colors[3*size_t(idx.vertex_index)+1]; - // tinyobj::real_t blue = attrib.colors[3*size_t(idx.vertex_index)+2]; - } - index_offset += fv; - - // per-face material - shapes[s].mesh.material_ids[f]; - } -} - -``` - -#### Example code (New Object Oriented API) - -```c++ -#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc -// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust triangulation. Requires C++11 -//#define TINYOBJLOADER_USE_MAPBOX_EARCUT -#include "tiny_obj_loader.h" - - -std::string inputfile = "cornell_box.obj"; -tinyobj::ObjReaderConfig reader_config; -reader_config.mtl_search_path = "./"; // Path to material files - -tinyobj::ObjReader reader; - -if (!reader.ParseFromFile(inputfile, reader_config)) { - if (!reader.Error().empty()) { - std::cerr << "TinyObjReader: " << reader.Error(); - } - exit(1); -} - -if (!reader.Warning().empty()) { - std::cout << "TinyObjReader: " << reader.Warning(); -} - -auto& attrib = reader.GetAttrib(); -auto& shapes = reader.GetShapes(); -auto& materials = reader.GetMaterials(); - -// Loop over shapes -for (size_t s = 0; s < shapes.size(); s++) { - // Loop over faces(polygon) - size_t index_offset = 0; - for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { - size_t fv = size_t(shapes[s].mesh.num_face_vertices[f]); - - // Loop over vertices in the face. - for (size_t v = 0; v < fv; v++) { - // access to vertex - tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v]; - tinyobj::real_t vx = attrib.vertices[3*size_t(idx.vertex_index)+0]; - tinyobj::real_t vy = attrib.vertices[3*size_t(idx.vertex_index)+1]; - tinyobj::real_t vz = attrib.vertices[3*size_t(idx.vertex_index)+2]; - - // Check if `normal_index` is zero or positive. negative = no normal data - if (idx.normal_index >= 0) { - tinyobj::real_t nx = attrib.normals[3*size_t(idx.normal_index)+0]; - tinyobj::real_t ny = attrib.normals[3*size_t(idx.normal_index)+1]; - tinyobj::real_t nz = attrib.normals[3*size_t(idx.normal_index)+2]; - } - - // Check if `texcoord_index` is zero or positive. negative = no texcoord data - if (idx.texcoord_index >= 0) { - tinyobj::real_t tx = attrib.texcoords[2*size_t(idx.texcoord_index)+0]; - tinyobj::real_t ty = attrib.texcoords[2*size_t(idx.texcoord_index)+1]; - } - - // Optional: vertex colors - // tinyobj::real_t red = attrib.colors[3*size_t(idx.vertex_index)+0]; - // tinyobj::real_t green = attrib.colors[3*size_t(idx.vertex_index)+1]; - // tinyobj::real_t blue = attrib.colors[3*size_t(idx.vertex_index)+2]; - } - index_offset += fv; - - // per-face material - shapes[s].mesh.material_ids[f]; - } -} - -``` - - - -## Optimized loader - -Optimized multi-threaded .obj loader is available at `experimental/` directory. -If you want absolute performance to load .obj data, this optimized loader will fit your purpose. -Note that the optimized loader uses C++11 thread and it does less error checks but may work most .obj data. +## License -Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core m5 1.2GHz). +OBJ Loader uses the MIT license. The MIT license allows free use and modification of the software as long as they provide credit back to me and don't hold me liable for anything that may go wrong. If you want to read the license in full look into the file entitled: LICENSE -* Rungholt scene(6M triangles) - * old version(v0.9.x): 15500 msecs. - * baseline(v1.0.x): 6800 msecs(2.3x faster than old version) - * optimised: 1500 msecs(10x faster than old version, 4.5x faster than baseline) +## Version Information -## Python binding +### Current Version -``` -$ python -m pip install tinyobjloader -``` +Version 2.0 -See [python/sample.py](python/sample.py) for example use of Python binding of tinyobjloader. +### Upcoming Features -### CI + PyPI upload +1. Bug Fixing +2. Adding more variables -cibuildwheels + twine upload for each git tagging event is handled in Github Actions and Cirrus CI(arm builds). +### Previous Versions -#### How to bump version(For developer) +#### Version 1.0 (September-13-2016) -* Apply `black` to python files(`python/sample.py`) -* Bump version in CMakeLists.txt -* Commit and push `release`. Confirm C.I. build is OK. -* Create tag starting with `v`(e.g. `v2.1.0`) -* `git push --tags` - * version settings is automatically handled in python binding through setuptools_scm. - * cibuildwheels + pypi upload(through twine) will be automatically triggered in Github Actions + Cirrus CI. +1. Base Implementation Done +2. README Created +3. LICENSE Created +4. Mesh Index and Vertex Load Implemented -## Tests +#### Version 2.0 (September-14-2016) -Unit tests are provided in `tests` directory. See `tests/README.md` for details. +1. Now Reads Material Data \ No newline at end of file diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h new file mode 100644 index 00000000..7abd6563 --- /dev/null +++ b/Source/OBJ_Loader.h @@ -0,0 +1,1167 @@ +// OBJ_Loader.h - A Single Header OBJ Model Loader + +#pragma once + +// Iostream - STD I/O Library +#include + +// Vector - STD Vector/Array Library +#include + +// String - STD String Library +#include + +// fStream - STD File I/O Library +#include + +// Math.h - STD math Library +#include + +// Print progress to console while loading (large models) +#define OBJL_CONSOLE_OUTPUT + +// Namespace: OBJL +// +// Description: The namespace that holds eveyrthing that +// is needed and used for the OBJ Model Loader +namespace objl +{ + // Structure: Vector2 + // + // Description: A 2D Vector that Holds Positional Data + struct Vector2 + { + // Default Constructor + Vector2() + { + X = 0.0f; + Y = 0.0f; + } + // Variable Set Constructor + Vector2(float X_, float Y_) + { + X = X_; + Y = Y_; + } + // Bool Equals Operator Overload + bool operator==(const Vector2& other) const + { + return (this->X == other.X && this->Y == other.Y); + } + // Bool Not Equals Operator Overload + bool operator!=(const Vector2& other) const + { + return !(this->X == other.X && this->Y == other.Y); + } + // Addition Operator Overload + Vector2 operator+(const Vector2& right) const + { + return Vector2(this->X + right.X, this->Y + right.Y); + } + // Subtraction Operator Overload + Vector2 operator-(const Vector2& right) const + { + return Vector2(this->X - right.X, this->Y - right.Y); + } + // Float Multiplication Operator Overload + Vector2 operator*(const float& other) const + { + return Vector2(this->X *other, this->Y * other); + } + + // Positional Variables + float X; + float Y; + }; + + // Structure: Vector3 + // + // Description: A 3D Vector that Holds Positional Data + struct Vector3 + { + // Default Constructor + Vector3() + { + X = 0.0f; + Y = 0.0f; + Z = 0.0f; + } + // Variable Set Constructor + Vector3(float X_, float Y_, float Z_) + { + X = X_; + Y = Y_; + Z = Z_; + } + // Bool Equals Operator Overload + bool operator==(const Vector3& other) const + { + return (this->X == other.X && this->Y == other.Y && this->Z == other.Z); + } + // Bool Not Equals Operator Overload + bool operator!=(const Vector3& other) const + { + return !(this->X == other.X && this->Y == other.Y && this->Z == other.Z); + } + // Addition Operator Overload + Vector3 operator+(const Vector3& right) const + { + return Vector3(this->X + right.X, this->Y + right.Y, this->Z + right.Z); + } + // Subtraction Operator Overload + Vector3 operator-(const Vector3& right) const + { + return Vector3(this->X - right.X, this->Y - right.Y, this->Z - right.Z); + } + // Float Multiplication Operator Overload + Vector3 operator*(const float& other) const + { + return Vector3(this->X * other, this->Y * other, this->Z * other); + } + // Float Division Operator Overload + Vector3 operator/(const float& other) const + { + return Vector3(this->X / other, this->Y / other, this->Z / other); + } + + // Positional Variables + float X; + float Y; + float Z; + }; + + // Structure: Vertex + // + // Description: Model Vertex object that holds + // a Position, Normal, and Texture Coordinate + struct Vertex + { + // Position Vector + Vector3 Position; + + // Normal Vector + Vector3 Normal; + + // Texture Coordinate Vector + Vector2 TextureCoordinate; + }; + + struct Material + { + Material() + { + name; + Ns = 0.0f; + Ni = 0.0f; + d = 0.0f; + illum = 0; + } + + // Material Name + std::string name; + // Ambient Color + Vector3 Ka; + // Diffuse Color + Vector3 Kd; + // Specular Color + Vector3 Ks; + // Specular Exponent + float Ns; + // Optical Density + float Ni; + // Dissolve + float d; + // Illumination + int illum; + // Ambient Texture Map + std::string map_Ka; + // Diffuse Texture Map + std::string map_Kd; + // Specular Texture Map + std::string map_Ks; + // Specular Hightlight Map + std::string map_Ns; + // Alpha Texture Map + std::string map_d; + // Bump Map + std::string map_bump; + }; + + // Structure: Mesh + // + // Description: A Simple Mesh Object that holds + // a name, a vertex list, and an index list + struct Mesh + { + // Default Constructor + Mesh() + { + + } + // Variable Set Constructor + Mesh(std::vector& _Vertices, std::vector& _Indices) + { + Vertices = _Vertices; + Indices = _Indices; + } + // Mesh Name + std::string MeshName; + // Vertex List + std::vector Vertices; + // Index List + std::vector Indices; + + // Material + Material MeshMaterial; + }; + + // Namespace: Math + // + // Description: The namespace that holds all of the math + // functions need for OBJL + namespace math + { + // Vector3 Cross Product + inline Vector3 CrossV3(const Vector3 a, const Vector3 b) + { + return Vector3(a.Y * b.Z - a.Z * b.Y, + a.Z * b.X - a.X * b.Z, + a.X * b.Y - a.Y * b.X); + } + + // Vector3 Magnitude Calculation + inline float MagnitudeV3(const Vector3 in) + { + return (sqrtf(powf(in.X, 2) + powf(in.Y, 2) + powf(in.Z, 2))); + } + + // Vector3 DotProduct + inline float DotV3(const Vector3 a, const Vector3 b) + { + return (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z); + } + + // Angle between 2 Vector3 Objects + inline float AngleBetweenV3(const Vector3 a, const Vector3 b) + { + float angle = DotV3(a, b); + angle /= (MagnitudeV3(a) * MagnitudeV3(b)); + return angle = acosf(angle); + } + + // Projection Calculation of a onto b + inline Vector3 ProjV3(const Vector3 a, const Vector3 b) + { + Vector3 bn = b / MagnitudeV3(b); + return bn * DotV3(a, bn); + } + } + + // Namespace: Algorithm + // + // Description: The namespace that holds all of the + // Algorithms needed for OBJL + namespace algorithm + { + // Vector3 Multiplication Opertor Overload + inline Vector3 operator*(const float& left, const Vector3& right) + { + return Vector3(right.X * left, right.Y * left, right.Z * left); + } + + // A test to see if P1 is on the same side as P2 of a line segment ab + inline bool SameSide(Vector3 p1, Vector3 p2, Vector3 a, Vector3 b) + { + Vector3 cp1 = math::CrossV3(b - a, p1 - a); + Vector3 cp2 = math::CrossV3(b - a, p2 - a); + + if (math::DotV3(cp1, cp2) >= 0) + return true; + else + return false; + } + + // Generate a cross produect normal for a triangle + inline Vector3 GenTriNormal(Vector3 t1, Vector3 t2, Vector3 t3) + { + Vector3 u = t2 - t1; + Vector3 v = t3 - t1; + + Vector3 normal = math::CrossV3(u,v); + + return normal; + } + + // Check to see if a Vector3 Point is within a 3 Vector3 Triangle + inline bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3) + { + // Test to see if it is within an infinite prism that the triangle outlines. + bool within_tri_prisim = SameSide(point, tri1, tri2, tri3) && SameSide(point, tri2, tri1, tri3) + && SameSide(point, tri3, tri1, tri2); + + // If it isn't it will never be on the triangle + if (!within_tri_prisim) + return false; + + // Calulate Triangle's Normal + Vector3 n = GenTriNormal(tri1, tri2, tri3); + + // Project the point onto this normal + Vector3 proj = math::ProjV3(point, n); + + // If the distance from the triangle to the point is 0 + // it lies on the triangle + if (math::MagnitudeV3(proj) == 0) + return true; + else + return false; + } + + // Split a String into a string array at a given token + inline void split(const std::string &in, + std::vector &out, + std::string token) + { + out.clear(); + + std::string temp; + + for (int i = 0; i < int(in.size()); i++) + { + std::string test = in.substr(i, token.size()); + + if (test == token) + { + if (!temp.empty()) + { + out.push_back(temp); + temp.clear(); + i += (int)token.size() - 1; + } + else + { + out.push_back(""); + } + } + else if (i + token.size() >= in.size()) + { + temp += in.substr(i, token.size()); + out.push_back(temp); + break; + } + else + { + temp += in[i]; + } + } + } + + // Get tail of string after first token and possibly following spaces + inline std::string tail(const std::string &in) + { + size_t token_start = in.find_first_not_of(" \t"); + size_t space_start = in.find_first_of(" \t", token_start); + size_t tail_start = in.find_first_not_of(" \t", space_start); + size_t tail_end = in.find_last_not_of(" \t"); + if (tail_start != std::string::npos && tail_end != std::string::npos) + { + return in.substr(tail_start, tail_end - tail_start + 1); + } + else if (tail_start != std::string::npos) + { + return in.substr(tail_start); + } + return ""; + } + + // Get first token of string + inline std::string firstToken(const std::string &in) + { + if (!in.empty()) + { + size_t token_start = in.find_first_not_of(" \t"); + size_t token_end = in.find_first_of(" \t", token_start); + if (token_start != std::string::npos && token_end != std::string::npos) + { + return in.substr(token_start, token_end - token_start); + } + else if (token_start != std::string::npos) + { + return in.substr(token_start); + } + } + return ""; + } + + // Get element at given index position + template + inline const T & getElement(const std::vector &elements, std::string &index) + { + int idx = std::stoi(index); + if (idx < 0) + idx = int(elements.size()) + idx; + else + idx--; + return elements[idx]; + } + } + + // Class: Loader + // + // Description: The OBJ Model Loader + class Loader + { + public: + // Default Constructor + Loader() + { + + } + ~Loader() + { + LoadedMeshes.clear(); + } + + // Load a file into the loader + // + // If file is loaded return true + // + // If the file is unable to be found + // or unable to be loaded return false + bool LoadFile(std::string Path) + { + // If the file is not an .obj file return false + if (Path.substr(Path.size() - 4, 4) != ".obj") + return false; + + + std::ifstream file(Path); + + if (!file.is_open()) + return false; + + LoadedMeshes.clear(); + LoadedVertices.clear(); + LoadedIndices.clear(); + + std::vector Positions; + std::vector TCoords; + std::vector Normals; + + std::vector Vertices; + std::vector Indices; + + std::vector MeshMatNames; + + bool listening = false; + std::string meshname; + + Mesh tempMesh; + + #ifdef OBJL_CONSOLE_OUTPUT + const unsigned int outputEveryNth = 1000; + unsigned int outputIndicator = outputEveryNth; + #endif + + std::string curline; + while (std::getline(file, curline)) + { + #ifdef OBJL_CONSOLE_OUTPUT + if ((outputIndicator = ((outputIndicator + 1) % outputEveryNth)) == 1) + { + if (!meshname.empty()) + { + std::cout + << "\r- " << meshname + << "\t| vertices > " << Positions.size() + << "\t| texcoords > " << TCoords.size() + << "\t| normals > " << Normals.size() + << "\t| triangles > " << (Vertices.size() / 3) + << (!MeshMatNames.empty() ? "\t| material: " + MeshMatNames.back() : ""); + } + } + #endif + + // Generate a Mesh Object or Prepare for an object to be created + if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g" || curline[0] == 'g') + { + if (!listening) + { + listening = true; + + if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g") + { + meshname = algorithm::tail(curline); + } + else + { + meshname = "unnamed"; + } + } + else + { + // Generate the mesh to put into the array + + if (!Indices.empty() && !Vertices.empty()) + { + // Create Mesh + tempMesh = Mesh(Vertices, Indices); + tempMesh.MeshName = meshname; + + // Insert Mesh + LoadedMeshes.push_back(tempMesh); + + // Cleanup + Vertices.clear(); + Indices.clear(); + meshname.clear(); + + meshname = algorithm::tail(curline); + } + else + { + if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g") + { + meshname = algorithm::tail(curline); + } + else + { + meshname = "unnamed"; + } + } + } + #ifdef OBJL_CONSOLE_OUTPUT + std::cout << std::endl; + outputIndicator = 0; + #endif + } + // Generate a Vertex Position + if (algorithm::firstToken(curline) == "v") + { + std::vector spos; + Vector3 vpos; + algorithm::split(algorithm::tail(curline), spos, " "); + + vpos.X = std::stof(spos[0]); + vpos.Y = std::stof(spos[1]); + vpos.Z = std::stof(spos[2]); + + Positions.push_back(vpos); + } + // Generate a Vertex Texture Coordinate + if (algorithm::firstToken(curline) == "vt") + { + std::vector stex; + Vector2 vtex; + algorithm::split(algorithm::tail(curline), stex, " "); + + vtex.X = std::stof(stex[0]); + vtex.Y = std::stof(stex[1]); + + TCoords.push_back(vtex); + } + // Generate a Vertex Normal; + if (algorithm::firstToken(curline) == "vn") + { + std::vector snor; + Vector3 vnor; + algorithm::split(algorithm::tail(curline), snor, " "); + + vnor.X = std::stof(snor[0]); + vnor.Y = std::stof(snor[1]); + vnor.Z = std::stof(snor[2]); + + Normals.push_back(vnor); + } + // Generate a Face (vertices & indices) + if (algorithm::firstToken(curline) == "f") + { + // Generate the vertices + std::vector vVerts; + GenVerticesFromRawOBJ(vVerts, Positions, TCoords, Normals, curline); + + // Add Vertices + for (int i = 0; i < int(vVerts.size()); i++) + { + Vertices.push_back(vVerts[i]); + + LoadedVertices.push_back(vVerts[i]); + } + + std::vector iIndices; + + VertexTriangluation(iIndices, vVerts); + + // Add Indices + for (int i = 0; i < int(iIndices.size()); i++) + { + unsigned int indnum = (unsigned int)((Vertices.size()) - vVerts.size()) + iIndices[i]; + Indices.push_back(indnum); + + indnum = (unsigned int)((LoadedVertices.size()) - vVerts.size()) + iIndices[i]; + LoadedIndices.push_back(indnum); + + } + } + // Get Mesh Material Name + if (algorithm::firstToken(curline) == "usemtl") + { + MeshMatNames.push_back(algorithm::tail(curline)); + + // Create new Mesh, if Material changes within a group + if (!Indices.empty() && !Vertices.empty()) + { + // Create Mesh + tempMesh = Mesh(Vertices, Indices); + tempMesh.MeshName = meshname; + int i = 2; + while(1) { + tempMesh.MeshName = meshname + "_" + std::to_string(i); + + for (auto &m : LoadedMeshes) + if (m.MeshName == tempMesh.MeshName) + continue; + break; + } + + // Insert Mesh + LoadedMeshes.push_back(tempMesh); + + // Cleanup + Vertices.clear(); + Indices.clear(); + } + + #ifdef OBJL_CONSOLE_OUTPUT + outputIndicator = 0; + #endif + } + // Load Materials + if (algorithm::firstToken(curline) == "mtllib") + { + // Generate LoadedMaterial + + // Generate a path to the material file + std::vector temp; + algorithm::split(Path, temp, "/"); + + std::string pathtomat = ""; + + if (temp.size() != 1) + { + for (int i = 0; i < temp.size() - 1; i++) + { + pathtomat += temp[i] + "/"; + } + } + + + pathtomat += algorithm::tail(curline); + + #ifdef OBJL_CONSOLE_OUTPUT + std::cout << std::endl << "- find materials in: " << pathtomat << std::endl; + #endif + + // Load Materials + LoadMaterials(pathtomat); + } + } + + #ifdef OBJL_CONSOLE_OUTPUT + std::cout << std::endl; + #endif + + // Deal with last mesh + + if (!Indices.empty() && !Vertices.empty()) + { + // Create Mesh + tempMesh = Mesh(Vertices, Indices); + tempMesh.MeshName = meshname; + + // Insert Mesh + LoadedMeshes.push_back(tempMesh); + } + + file.close(); + + // Set Materials for each Mesh + for (int i = 0; i < MeshMatNames.size(); i++) + { + std::string matname = MeshMatNames[i]; + + // Find corresponding material name in loaded materials + // when found copy material variables into mesh material + for (int j = 0; j < LoadedMaterials.size(); j++) + { + if (LoadedMaterials[j].name == matname) + { + LoadedMeshes[i].MeshMaterial = LoadedMaterials[j]; + break; + } + } + } + + if (LoadedMeshes.empty() && LoadedVertices.empty() && LoadedIndices.empty()) + { + return false; + } + else + { + return true; + } + } + + // Loaded Mesh Objects + std::vector LoadedMeshes; + // Loaded Vertex Objects + std::vector LoadedVertices; + // Loaded Index Positions + std::vector LoadedIndices; + // Loaded Material Objects + std::vector LoadedMaterials; + + private: + // Generate vertices from a list of positions, + // tcoords, normals and a face line + void GenVerticesFromRawOBJ(std::vector& oVerts, + const std::vector& iPositions, + const std::vector& iTCoords, + const std::vector& iNormals, + std::string icurline) + { + std::vector sface, svert; + Vertex vVert; + algorithm::split(algorithm::tail(icurline), sface, " "); + + bool noNormal = false; + + // For every given vertex do this + for (int i = 0; i < int(sface.size()); i++) + { + // See What type the vertex is. + int vtype; + + algorithm::split(sface[i], svert, "/"); + + // Check for just position - v1 + if (svert.size() == 1) + { + // Only position + vtype = 1; + } + + // Check for position & texture - v1/vt1 + if (svert.size() == 2) + { + // Position & Texture + vtype = 2; + } + + // Check for Position, Texture and Normal - v1/vt1/vn1 + // or if Position and Normal - v1//vn1 + if (svert.size() == 3) + { + if (svert[1] != "") + { + // Position, Texture, and Normal + vtype = 4; + } + else + { + // Position & Normal + vtype = 3; + } + } + + // Calculate and store the vertex + switch (vtype) + { + case 1: // P + { + vVert.Position = algorithm::getElement(iPositions, svert[0]); + vVert.TextureCoordinate = Vector2(0, 0); + noNormal = true; + oVerts.push_back(vVert); + break; + } + case 2: // P/T + { + vVert.Position = algorithm::getElement(iPositions, svert[0]); + vVert.TextureCoordinate = algorithm::getElement(iTCoords, svert[1]); + noNormal = true; + oVerts.push_back(vVert); + break; + } + case 3: // P//N + { + vVert.Position = algorithm::getElement(iPositions, svert[0]); + vVert.TextureCoordinate = Vector2(0, 0); + vVert.Normal = algorithm::getElement(iNormals, svert[2]); + oVerts.push_back(vVert); + break; + } + case 4: // P/T/N + { + vVert.Position = algorithm::getElement(iPositions, svert[0]); + vVert.TextureCoordinate = algorithm::getElement(iTCoords, svert[1]); + vVert.Normal = algorithm::getElement(iNormals, svert[2]); + oVerts.push_back(vVert); + break; + } + default: + { + break; + } + } + } + + // take care of missing normals + // these may not be truly acurate but it is the + // best they get for not compiling a mesh with normals + if (noNormal) + { + Vector3 A = oVerts[0].Position - oVerts[1].Position; + Vector3 B = oVerts[2].Position - oVerts[1].Position; + + Vector3 normal = math::CrossV3(A, B); + + for (int i = 0; i < int(oVerts.size()); i++) + { + oVerts[i].Normal = normal; + } + } + } + + // Triangulate a list of vertices into a face by printing + // inducies corresponding with triangles within it + void VertexTriangluation(std::vector& oIndices, + const std::vector& iVerts) + { + // If there are 2 or less verts, + // no triangle can be created, + // so exit + if (iVerts.size() < 3) + { + return; + } + // If it is a triangle no need to calculate it + if (iVerts.size() == 3) + { + oIndices.push_back(0); + oIndices.push_back(1); + oIndices.push_back(2); + return; + } + + // Create a list of vertices + std::vector tVerts = iVerts; + + while (true) + { + // For every vertex + for (int i = 0; i < int(tVerts.size()); i++) + { + // pPrev = the previous vertex in the list + Vertex pPrev; + if (i == 0) + { + pPrev = tVerts[tVerts.size() - 1]; + } + else + { + pPrev = tVerts[i - 1]; + } + + // pCur = the current vertex; + Vertex pCur = tVerts[i]; + + // pNext = the next vertex in the list + Vertex pNext; + if (i == tVerts.size() - 1) + { + pNext = tVerts[0]; + } + else + { + pNext = tVerts[i + 1]; + } + + // Check to see if there are only 3 verts left + // if so this is the last triangle + if (tVerts.size() == 3) + { + // Create a triangle from pCur, pPrev, pNext + for (int j = 0; j < int(tVerts.size()); j++) + { + if (iVerts[j].Position == pCur.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pPrev.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pNext.Position) + oIndices.push_back(j); + } + + tVerts.clear(); + break; + } + if (tVerts.size() == 4) + { + // Create a triangle from pCur, pPrev, pNext + for (int j = 0; j < int(iVerts.size()); j++) + { + if (iVerts[j].Position == pCur.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pPrev.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pNext.Position) + oIndices.push_back(j); + } + + Vector3 tempVec; + for (int j = 0; j < int(tVerts.size()); j++) + { + if (tVerts[j].Position != pCur.Position + && tVerts[j].Position != pPrev.Position + && tVerts[j].Position != pNext.Position) + { + tempVec = tVerts[j].Position; + break; + } + } + + // Create a triangle from pCur, pPrev, pNext + for (int j = 0; j < int(iVerts.size()); j++) + { + if (iVerts[j].Position == pPrev.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pNext.Position) + oIndices.push_back(j); + if (iVerts[j].Position == tempVec) + oIndices.push_back(j); + } + + tVerts.clear(); + break; + } + + // If Vertex is not an interior vertex + float angle = math::AngleBetweenV3(pPrev.Position - pCur.Position, pNext.Position - pCur.Position) * (180 / 3.14159265359); + if (angle <= 0 && angle >= 180) + continue; + + // If any vertices are within this triangle + bool inTri = false; + for (int j = 0; j < int(iVerts.size()); j++) + { + if (algorithm::inTriangle(iVerts[j].Position, pPrev.Position, pCur.Position, pNext.Position) + && iVerts[j].Position != pPrev.Position + && iVerts[j].Position != pCur.Position + && iVerts[j].Position != pNext.Position) + { + inTri = true; + break; + } + } + if (inTri) + continue; + + // Create a triangle from pCur, pPrev, pNext + for (int j = 0; j < int(iVerts.size()); j++) + { + if (iVerts[j].Position == pCur.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pPrev.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pNext.Position) + oIndices.push_back(j); + } + + // Delete pCur from the list + for (int j = 0; j < int(tVerts.size()); j++) + { + if (tVerts[j].Position == pCur.Position) + { + tVerts.erase(tVerts.begin() + j); + break; + } + } + + // reset i to the start + // -1 since loop will add 1 to it + i = -1; + } + + // if no triangles were created + if (oIndices.size() == 0) + break; + + // if no more vertices + if (tVerts.size() == 0) + break; + } + } + + // Load Materials from .mtl file + bool LoadMaterials(std::string path) + { + // If the file is not a material file return false + if (path.substr(path.size() - 4, path.size()) != ".mtl") + return false; + + std::ifstream file(path); + + // If the file is not found return false + if (!file.is_open()) + return false; + + Material tempMaterial; + + bool listening = false; + + // Go through each line looking for material variables + std::string curline; + while (std::getline(file, curline)) + { + // new material and material name + if (algorithm::firstToken(curline) == "newmtl") + { + if (!listening) + { + listening = true; + + if (curline.size() > 7) + { + tempMaterial.name = algorithm::tail(curline); + } + else + { + tempMaterial.name = "none"; + } + } + else + { + // Generate the material + + // Push Back loaded Material + LoadedMaterials.push_back(tempMaterial); + + // Clear Loaded Material + tempMaterial = Material(); + + if (curline.size() > 7) + { + tempMaterial.name = algorithm::tail(curline); + } + else + { + tempMaterial.name = "none"; + } + } + } + // Ambient Color + if (algorithm::firstToken(curline) == "Ka") + { + std::vector temp; + algorithm::split(algorithm::tail(curline), temp, " "); + + if (temp.size() != 3) + continue; + + tempMaterial.Ka.X = std::stof(temp[0]); + tempMaterial.Ka.Y = std::stof(temp[1]); + tempMaterial.Ka.Z = std::stof(temp[2]); + } + // Diffuse Color + if (algorithm::firstToken(curline) == "Kd") + { + std::vector temp; + algorithm::split(algorithm::tail(curline), temp, " "); + + if (temp.size() != 3) + continue; + + tempMaterial.Kd.X = std::stof(temp[0]); + tempMaterial.Kd.Y = std::stof(temp[1]); + tempMaterial.Kd.Z = std::stof(temp[2]); + } + // Specular Color + if (algorithm::firstToken(curline) == "Ks") + { + std::vector temp; + algorithm::split(algorithm::tail(curline), temp, " "); + + if (temp.size() != 3) + continue; + + tempMaterial.Ks.X = std::stof(temp[0]); + tempMaterial.Ks.Y = std::stof(temp[1]); + tempMaterial.Ks.Z = std::stof(temp[2]); + } + // Specular Exponent + if (algorithm::firstToken(curline) == "Ns") + { + tempMaterial.Ns = std::stof(algorithm::tail(curline)); + } + // Optical Density + if (algorithm::firstToken(curline) == "Ni") + { + tempMaterial.Ni = std::stof(algorithm::tail(curline)); + } + // Dissolve + if (algorithm::firstToken(curline) == "d") + { + tempMaterial.d = std::stof(algorithm::tail(curline)); + } + // Illumination + if (algorithm::firstToken(curline) == "illum") + { + tempMaterial.illum = std::stoi(algorithm::tail(curline)); + } + // Ambient Texture Map + if (algorithm::firstToken(curline) == "map_Ka") + { + tempMaterial.map_Ka = algorithm::tail(curline); + } + // Diffuse Texture Map + if (algorithm::firstToken(curline) == "map_Kd") + { + tempMaterial.map_Kd = algorithm::tail(curline); + } + // Specular Texture Map + if (algorithm::firstToken(curline) == "map_Ks") + { + tempMaterial.map_Ks = algorithm::tail(curline); + } + // Specular Hightlight Map + if (algorithm::firstToken(curline) == "map_Ns") + { + tempMaterial.map_Ns = algorithm::tail(curline); + } + // Alpha Texture Map + if (algorithm::firstToken(curline) == "map_d") + { + tempMaterial.map_d = algorithm::tail(curline); + } + // Bump Map + if (algorithm::firstToken(curline) == "map_Bump" || algorithm::firstToken(curline) == "map_bump" || algorithm::firstToken(curline) == "bump") + { + tempMaterial.map_bump = algorithm::tail(curline); + } + } + + // Deal with last material + + // Push Back loaded Material + LoadedMaterials.push_back(tempMaterial); + + // Test to see if anything was loaded + // If not return false + if (LoadedMaterials.empty()) + return false; + // If so return true + else + return true; + } + }; +} diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 93f691ce..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,22 +0,0 @@ -version: 1.0.{build} - -platform: x64 - -install: - ####################################################################################### - # All external dependencies are installed in C:\projects\deps - ####################################################################################### - - mkdir C:\projects\deps - - ####################################################################################### - # Install Ninja - ####################################################################################### - - set NINJA_URL="https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-win.zip" - - appveyor DownloadFile %NINJA_URL% -FileName ninja.zip - - 7z x ninja.zip -oC:\projects\deps\ninja > nul - - set PATH=C:\projects\deps\ninja;%PATH% - - ninja --version - -build_script: - - cd tests - - vcbuild.bat diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 2580f172..00000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,170 +0,0 @@ -# -# Python wheels build is now done in Github Actions + Cirrus CI(for arm build) -# so python build is disabled in Azure pipelines. -# - -variables: - # https://cibuildwheel.readthedocs.io/en/stable/cpp_standards/ - # cibuildwheel now supports python 3.6+(as of 2022 Oct) - #CIBW_SKIP: "pp*" - CIBW_BEFORE_BUILD: "pip install pybind11" - CIBW_ARCHS_LINUXBEFORE_BUILD: "pip install pybind11" - # disable aarch64 build for a while since it(pulling docker aarch64 image) exceeds Azure's 60 min limit - # NOTE: aarch64 linux support in Azure pipeline is not yet officially supported(as of 2022 Oct) https://github.com/microsoft/azure-pipelines-agent/issues/3935 - #CIBW_ARCHS_LINUX: auto aarch64 - CIBW_ARCHS_MACOS: x86_64 universal2 arm64 - #CIBW_BEFORE_BUILD_MACOS: "pip install -U pip setuptools" - #CIBW_BEFORE_BUILD_LINUX: "pip install -U pip setuptools" - #CIBW_TEST_COMMAND: TODO "python -c \"import tinyobjloader; tinyobjloader.test()\"" - CIBW_BUILD_VERBOSITY: "2" - #CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 - #CIBW_MANYLINUX_I686_IMAGE: manylinux2014 - -jobs: - - job: unit_linux - pool: { vmImage: "ubuntu-latest" } - steps: - - script: | - cd tests - make && ./tester - displayName: Run unit tests - - - job: python_format - pool: { vmImage: "ubuntu-latest" } - steps: - - task: UsePythonVersion@0 - - script: | - # 19.10b0 triggers 'cannot import name '_unicodefun' from 'click'' error. - # https://stackoverflow.com/questions/71673404/importerror-cannot-import-name-unicodefun-from-click - #pip install black==19.10b0 - #pip install black==22.3.0 - pip install black==22.10.0 - - black --check python/ - displayName: Check Python code format - - # Disabled: python build - ## - ## Ubuntu16.04 seems now deprecated(as of 2021/12/01), - ## so use `ubuntu-latest` - #- job: linux - # pool: {vmImage: "ubuntu-latest"} - # steps: - # - task: UsePythonVersion@0 - # - bash: | - # python3 -m pip install --upgrade pip - # pip3 install cibuildwheel twine - - # # Use pipx to build source dist - # pip3 install pipx - - # # Source dist - # pipx run build --sdist - # ls -la dist/* - - # # build binary wheels - # cibuildwheel --platform linux --output-dir wheelhouse . - - # - task: CopyFiles@2 - # inputs: - # contents: 'wheelhouse/**' - # targetFolder: $(Build.ArtifactStagingDirectory) - - # - task: CopyFiles@2 - # inputs: - # contents: 'dist/**' - # targetFolder: $(Build.ArtifactStagingDirectory) - - # - task: PublishBuildArtifacts@1 - # inputs: - # path: $(Build.ArtifactStagingDirectory) - # artifactName: tinyobjDeployLinux - - #- job: macos - # pool: {vmImage: 'macOS-latest'} - # variables: - # # Support C++11: https://github.com/joerick/cibuildwheel/pull/156 - # MACOSX_DEPLOYMENT_TARGET: 10.9 - # steps: - # - task: UsePythonVersion@0 - # - bash: | - # python3 -m pip install --upgrade pip - # pip3 install cibuildwheel - # cibuildwheel --platform macos --output-dir wheelhouse . - # - task: CopyFiles@2 - # inputs: - # contents: 'wheelhouse/*.whl' - # targetFolder: $(Build.ArtifactStagingDirectory) - # - task: PublishBuildArtifacts@1 - # inputs: - # path: $(Build.ArtifactStagingDirectory) - # artifactName: tinyobjDeployMacOS - - #- job: windows - # pool: {vmImage: 'windows-latest'} - # steps: - # - task: UsePythonVersion@0 - # - bash: | - # python -m pip install --upgrade pip - # pip install cibuildwheel - # cibuildwheel --platform windows --output-dir wheelhouse . - # - task: CopyFiles@2 - # inputs: - # contents: 'wheelhouse/*.whl' - # targetFolder: $(Build.ArtifactStagingDirectory) - # - task: PublishBuildArtifacts@1 - # inputs: - # path: $(Build.ArtifactStagingDirectory) - # artifactName: tinyobjDeployWindows - - #- job: deployPyPI - # # Based on vispy: https://github.com/vispy/vispy/blob/master/azure-pipelines.yml - # pool: {vmImage: 'ubuntu-latest'} - # condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v')) - # dependsOn: - # - linux - # - macos - # - windows - # steps: - # - task: UsePythonVersion@0 - - # # TODO(syoyo): Use buildType: specific to download multiple artifacts at once? - # - task: DownloadBuildArtifacts@0 - # inputs: - # artifactName: 'tinyobjDeployLinux' - # downloadPath: $(Pipeline.Workspace) - - # - task: DownloadBuildArtifacts@0 - # inputs: - # artifactName: 'tinyobjDeployMacOS' - # downloadPath: $(Pipeline.Workspace) - - # - task: DownloadBuildArtifacts@0 - # inputs: - # artifactName: 'tinyobjDeployWindows' - # downloadPath: $(Pipeline.Workspace) - - # # Publish to PyPI through twine - # - bash: | - # cd $(Pipeline.Workspace) - # find . - # python -m pip install --upgrade pip - # pip install twine - # echo tinyobjDeployLinux/dist/* - # echo tinyobjDeployLinux/wheelhouse/* tinyobjDeployMacOS/wheelhouse/* tinyobjDeployWindows/wheelhouse/* - # twine upload -u "__token__" --skip-existing tinyobjDeployLinux/dist/* tinyobjDeployLinux/wheelhouse/* tinyobjDeployMacOS/wheelhouse/* tinyobjDeployWindows/wheelhouse/* - # env: - # TWINE_PASSWORD: $(pypiToken2) - -trigger: - branches: - include: - - '*' - tags: - include: - - 'v*' - -pr: - branches: - include: - - "*" diff --git a/bootstrap-cmake-linux-with-pyhthon.sh b/bootstrap-cmake-linux-with-pyhthon.sh deleted file mode 100755 index 96cf4bf6..00000000 --- a/bootstrap-cmake-linux-with-pyhthon.sh +++ /dev/null @@ -1,20 +0,0 @@ -curdir=`pwd` - -builddir=${curdir}/build_python_module - -rm -rf ${builddir} -mkdir ${builddir} - -# set path to pybind11 -# If you install pybind11 through pip, its usually installed to /pybind11. -pybind11_path=`python -c "import site; print (site.getsitepackages()[0])"` -echo ${pybind11_path} - -CC=clang CXX=clang++ \ - pybind11_DIR=${pybind11_path}/pybind11 \ - cmake \ - -B${builddir} \ - -DCMAKE_VERBOSE_MAKEFILE=1 \ - -DTINYOBJLOADER_WITH_PYTHON=1 - -cd ${curdir} diff --git a/build.ninja b/build.ninja deleted file mode 100644 index 77801bb5..00000000 --- a/build.ninja +++ /dev/null @@ -1,53 +0,0 @@ -ninja_required_version = 1.4 - -gnubuilddir = build -gnudefines = -gnuincludes = -I. -gnucflags = -O2 -g -gnucxxflags = -O2 -g -pedantic -Wall -Wextra -Wcast-align -Wcast-qual $ - -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self $ - -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast $ - -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion $ - -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror $ - -Wno-unused -fsanitize=address -gnuldflags = -fsanitize=address - -pool link_pool - depth = 1 - -rule gnucxx - command = $gnucxx -MMD -MF $out.d $gnudefines $gnuincludes $gnucxxflags $ - -c $in -o $out - description = CXX $out - depfile = $out.d - deps = gcc -rule gnucc - command = $gnucc -MMD -MF $out.d $gnudefines $gnuincludes $gnucflags -c $ - $in -o $out - description = CC $out - depfile = $out.d - deps = gcc -rule gnulink - command = $gnuld -o $out $in $libs $gnuldflags - description = LINK $out - pool = link_pool -rule gnuar - command = $gnuar rsc $out $in - description = AR $out - pool = link_pool -rule gnustamp - command = touch $out - description = STAMP $out - -gnucxx = g++ -gnucc = gcc -gnuld = $gnucxx -gnuar = ar - -build loader_example.o: gnucxx loader_example.cc - - -build loader_example: gnulink loader_example.o -build all: phony loader_example - -default all diff --git a/cmake/ClangClCMakeCompileRules.cmake b/cmake/ClangClCMakeCompileRules.cmake deleted file mode 100644 index a3bcf1c2..00000000 --- a/cmake/ClangClCMakeCompileRules.cmake +++ /dev/null @@ -1,9 +0,0 @@ -# macOS paths usually start with /Users/*. Unfortunately, clang-cl interprets -# paths starting with /U as macro undefines, so we need to put a -- before the -# input file path to force it to be treated as a path. CMake's compilation rules -# should be tweaked accordingly, but until that's done, and to support older -# CMake versions, overriding compilation rules works well enough. This file will -# be included by cmake after the default compilation rules have already been set -# up, so we can just modify them instead of duplicating them entirely. -string(REPLACE "-c " "-c -- " CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}") -string(REPLACE "-c " "-c -- " CMAKE_CXX_COMPILE_OBJECT "${CMAKE_CXX_COMPILE_OBJECT}") diff --git a/cmake/aarch64-linux-gnu.toolchain b/cmake/aarch64-linux-gnu.toolchain deleted file mode 100644 index cdcdaf25..00000000 --- a/cmake/aarch64-linux-gnu.toolchain +++ /dev/null @@ -1,14 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR aarch64) -set(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu) - -set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu/) - -# Sync with GitHub Actions config -set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) -set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/cmake/clang-cl-msvc-windows.cmake b/cmake/clang-cl-msvc-windows.cmake deleted file mode 100644 index e2eac142..00000000 --- a/cmake/clang-cl-msvc-windows.cmake +++ /dev/null @@ -1,327 +0,0 @@ -# From llvm/cmake/platforms/WinMsvc.cmake -# Modified to use clang-cl on native Windows. - -# Cross toolchain configuration for using clang-cl on non-Windows hosts to -# target MSVC. -# -# Usage: -# cmake -G Ninja -# -DCMAKE_TOOLCHAIN_FILE=/path/to/this/file -# -DHOST_ARCH=[aarch64|arm64|armv7|arm|i686|x86|x86_64|x64] -# -DLLVM_NATIVE_TOOLCHAIN=/path/to/llvm/installation -# -DMSVC_BASE=/path/to/MSVC/system/libraries/and/includes -# -DWINSDK_BASE=/path/to/windows-sdk -# -DWINSDK_VER=windows sdk version folder name -# -# HOST_ARCH: -# The architecture to build for. -# -# LLVM_NATIVE_TOOLCHAIN: -# *Absolute path* to a folder containing the toolchain which will be used to -# build. At a minimum, this folder should have a bin directory with a -# copy of clang-cl, clang, clang++, and lld-link, as well as a lib directory -# containing clang's system resource directory. -# -# MSVC_BASE: -# *Absolute path* to the folder containing MSVC headers and system libraries. -# The layout of the folder matches that which is intalled by MSVC 2017 on -# Windows, and should look like this: -# -# ${MSVC_BASE} -# include -# vector -# stdint.h -# etc... -# lib -# x64 -# libcmt.lib -# msvcrt.lib -# etc... -# x86 -# libcmt.lib -# msvcrt.lib -# etc... -# -# For versions of MSVC < 2017, or where you have a hermetic toolchain in a -# custom format, you must use symlinks or restructure it to look like the above. -# -# WINSDK_BASE: -# Together with WINSDK_VER, determines the location of Windows SDK headers -# and libraries. -# -# WINSDK_VER: -# Together with WINSDK_BASE, determines the locations of Windows SDK headers -# and libraries. -# -# WINSDK_BASE and WINSDK_VER work together to define a folder layout that matches -# that of the Windows SDK installation on a standard Windows machine. It should -# match the layout described below. -# -# Note that if you install Windows SDK to a windows machine and simply copy the -# files, it will already be in the correct layout. -# -# ${WINSDK_BASE} -# Include -# ${WINSDK_VER} -# shared -# ucrt -# um -# windows.h -# etc... -# Lib -# ${WINSDK_VER} -# ucrt -# x64 -# x86 -# ucrt.lib -# etc... -# um -# x64 -# x86 -# kernel32.lib -# etc -# -# IMPORTANT: In order for this to work, you will need a valid copy of the Windows -# SDK and C++ STL headers and libraries on your host. Additionally, since the -# Windows libraries and headers are not case-correct, this toolchain file sets -# up a VFS overlay for the SDK headers and case-correcting symlinks for the -# libraries when running on a case-sensitive filesystem. - - -# When configuring CMake with a toolchain file against a top-level CMakeLists.txt, -# it will actually run CMake many times, once for each small test program used to -# determine what features a compiler supports. Unfortunately, none of these -# invocations share a CMakeCache.txt with the top-level invocation, meaning they -# won't see the value of any arguments the user passed via -D. Since these are -# necessary to properly configure MSVC in both the top-level configuration as well as -# all feature-test invocations, we set environment variables with the values so that -# these environments get inherited by child invocations. We can switch to -# CMAKE_TRY_COMPILE_PLATFORM_VARIABLES once our minimum supported CMake version -# is 3.6 or greater. -function(init_user_prop prop) - if(${prop}) - set(ENV{_${prop}} "${${prop}}") - else() - set(${prop} "$ENV{_${prop}}" PARENT_SCOPE) - endif() -endfunction() - -function(generate_winsdk_vfs_overlay winsdk_include_dir output_path) - set(include_dirs) - file(GLOB_RECURSE entries LIST_DIRECTORIES true "${winsdk_include_dir}/*") - foreach(entry ${entries}) - if(IS_DIRECTORY "${entry}") - list(APPEND include_dirs "${entry}") - endif() - endforeach() - - file(WRITE "${output_path}" "version: 0\n") - file(APPEND "${output_path}" "case-sensitive: false\n") - file(APPEND "${output_path}" "roots:\n") - - foreach(dir ${include_dirs}) - file(GLOB headers RELATIVE "${dir}" "${dir}/*.h") - if(NOT headers) - continue() - endif() - - file(APPEND "${output_path}" " - name: \"${dir}\"\n") - file(APPEND "${output_path}" " type: directory\n") - file(APPEND "${output_path}" " contents:\n") - - foreach(header ${headers}) - file(APPEND "${output_path}" " - name: \"${header}\"\n") - file(APPEND "${output_path}" " type: file\n") - file(APPEND "${output_path}" " external-contents: \"${dir}/${header}\"\n") - endforeach() - endforeach() -endfunction() - -function(generate_winsdk_lib_symlinks winsdk_um_lib_dir output_dir) - execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${output_dir}") - file(GLOB libraries RELATIVE "${winsdk_um_lib_dir}" "${winsdk_um_lib_dir}/*") - foreach(library ${libraries}) - string(TOLOWER "${library}" all_lowercase_symlink_name) - if(NOT library STREQUAL all_lowercase_symlink_name) - execute_process(COMMAND "${CMAKE_COMMAND}" - -E create_symlink - "${winsdk_um_lib_dir}/${library}" - "${output_dir}/${all_lowercase_symlink_name}") - endif() - - get_filename_component(name_we "${library}" NAME_WE) - get_filename_component(ext "${library}" EXT) - string(TOLOWER "${ext}" lowercase_ext) - set(lowercase_ext_symlink_name "${name_we}${lowercase_ext}") - if(NOT library STREQUAL lowercase_ext_symlink_name AND - NOT all_lowercase_symlink_name STREQUAL lowercase_ext_symlink_name) - execute_process(COMMAND "${CMAKE_COMMAND}" - -E create_symlink - "${winsdk_um_lib_dir}/${library}" - "${output_dir}/${lowercase_ext_symlink_name}") - endif() - endforeach() -endfunction() - -set(CMAKE_SYSTEM_NAME Windows) -set(CMAKE_SYSTEM_VERSION 10.0) -set(CMAKE_SYSTEM_PROCESSOR AMD64) - -init_user_prop(HOST_ARCH) -init_user_prop(LLVM_NATIVE_TOOLCHAIN) -init_user_prop(MSVC_BASE) -init_user_prop(WINSDK_BASE) -init_user_prop(WINSDK_VER) - -if(NOT HOST_ARCH) - set(HOST_ARCH x86_64) -endif() -if(HOST_ARCH STREQUAL "aarch64" OR HOST_ARCH STREQUAL "arm64") - set(TRIPLE_ARCH "aarch64") - set(WINSDK_ARCH "arm64") -elseif(HOST_ARCH STREQUAL "armv7" OR HOST_ARCH STREQUAL "arm") - set(TRIPLE_ARCH "armv7") - set(WINSDK_ARCH "arm") -elseif(HOST_ARCH STREQUAL "i686" OR HOST_ARCH STREQUAL "x86") - set(TRIPLE_ARCH "i686") - set(WINSDK_ARCH "x86") -elseif(HOST_ARCH STREQUAL "x86_64" OR HOST_ARCH STREQUAL "x64") - set(TRIPLE_ARCH "x86_64") - set(WINSDK_ARCH "x64") -else() - message(SEND_ERROR "Unknown host architecture ${HOST_ARCH}. Must be aarch64 (or arm64), armv7 (or arm), i686 (or x86), or x86_64 (or x64).") -endif() - -set(MSVC_INCLUDE "${MSVC_BASE}/include") -set(ATLMFC_INCLUDE "${MSVC_BASE}/atlmfc/include") -set(MSVC_LIB "${MSVC_BASE}/lib") -set(ATLMFC_LIB "${MSVC_BASE}/atlmfc/lib") -set(WINSDK_INCLUDE "${WINSDK_BASE}/Include/${WINSDK_VER}") -set(WINSDK_LIB "${WINSDK_BASE}/Lib/${WINSDK_VER}") - -# Do some sanity checking to make sure we can find a native toolchain and -# that the Windows SDK / MSVC STL directories look kosher. -if(NOT EXISTS "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl.exe" OR - NOT EXISTS "${LLVM_NATIVE_TOOLCHAIN}/bin/lld-link.exe") - message(SEND_ERROR - "LLVM_NATIVE_TOOLCHAIN folder '${LLVM_NATIVE_TOOLCHAIN}' does not " - "point to a valid directory containing bin/clang-cl.exe and bin/lld-link.exe " - "binaries") -endif() - -if(NOT EXISTS "${MSVC_BASE}" OR - NOT EXISTS "${MSVC_INCLUDE}" OR - NOT EXISTS "${MSVC_LIB}") - message(SEND_ERROR - "CMake variable MSVC_BASE must point to a folder containing MSVC " - "system headers and libraries") -endif() - -if(NOT EXISTS "${WINSDK_BASE}" OR - NOT EXISTS "${WINSDK_INCLUDE}" OR - NOT EXISTS "${WINSDK_LIB}") - message(SEND_ERROR - "CMake variable WINSDK_BASE and WINSDK_VER must resolve to a valid " - "Windows SDK installation") -endif() - -if(NOT EXISTS "${WINSDK_INCLUDE}/um/Windows.h") - message(SEND_ERROR "Cannot find Windows.h") -endif() -if(NOT EXISTS "${WINSDK_INCLUDE}/um/WINDOWS.H") - set(case_sensitive_filesystem TRUE) -endif() - -set(CMAKE_C_COMPILER "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl.exe" CACHE FILEPATH "") -set(CMAKE_CXX_COMPILER "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl.exe" CACHE FILEPATH "") -set(CMAKE_LINKER "${LLVM_NATIVE_TOOLCHAIN}/bin/lld-link.exe" CACHE FILEPATH "") - -# Even though we're cross-compiling, we need some native tools (e.g. llvm-tblgen), and those -# native tools have to be built before we can start doing the cross-build. LLVM supports -# a CROSS_TOOLCHAIN_FLAGS_NATIVE argument which consists of a list of flags to pass to CMake -# when configuring the NATIVE portion of the cross-build. By default we construct this so -# that it points to the tools in the same location as the native clang-cl that we're using. -list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_ASM_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang") -list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_C_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang") -list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_CXX_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang++") - -set(CROSS_TOOLCHAIN_FLAGS_NATIVE "${_CTF_NATIVE_DEFAULT}" CACHE STRING "") - -set(COMPILE_FLAGS - -D_CRT_SECURE_NO_WARNINGS - --target=${TRIPLE_ARCH}-windows-msvc - -fms-compatibility-version=19.11 - -imsvc "\"${ATLMFC_INCLUDE}\"" - -imsvc "\"${MSVC_INCLUDE}\"" - -imsvc "\"${WINSDK_INCLUDE}/ucrt\"" - -imsvc "\"${WINSDK_INCLUDE}/shared\"" - -imsvc "\"${WINSDK_INCLUDE}/um\"" - -imsvc "\"${WINSDK_INCLUDE}/winrt\"") - -if(case_sensitive_filesystem) - # Ensure all sub-configures use the top-level VFS overlay instead of generating their own. - init_user_prop(winsdk_vfs_overlay_path) - if(NOT winsdk_vfs_overlay_path) - set(winsdk_vfs_overlay_path "${CMAKE_BINARY_DIR}/winsdk_vfs_overlay.yaml") - generate_winsdk_vfs_overlay("${WINSDK_BASE}/Include/${WINSDK_VER}" "${winsdk_vfs_overlay_path}") - init_user_prop(winsdk_vfs_overlay_path) - endif() - list(APPEND COMPILE_FLAGS - -Xclang -ivfsoverlay -Xclang "${winsdk_vfs_overlay_path}") -endif() - -string(REPLACE ";" " " COMPILE_FLAGS "${COMPILE_FLAGS}") - -# We need to preserve any flags that were passed in by the user. However, we -# can't append to CMAKE_C_FLAGS and friends directly, because toolchain files -# will be re-invoked on each reconfigure and therefore need to be idempotent. -# The assignments to the _INITIAL cache variables don't use FORCE, so they'll -# only be populated on the initial configure, and their values won't change -# afterward. -set(_CMAKE_C_FLAGS_INITIAL "${CMAKE_C_FLAGS}" CACHE STRING "") -set(CMAKE_C_FLAGS "${_CMAKE_C_FLAGS_INITIAL} ${COMPILE_FLAGS}" CACHE STRING "" FORCE) - -set(_CMAKE_CXX_FLAGS_INITIAL "${CMAKE_CXX_FLAGS}" CACHE STRING "") -set(CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS_INITIAL} ${COMPILE_FLAGS}" CACHE STRING "" FORCE) - -set(LINK_FLAGS - # Prevent CMake from attempting to invoke mt.exe. It only recognizes the slashed form and not the dashed form. - /manifest:no - - -libpath:"${ATLMFC_LIB}/${WINSDK_ARCH}" - -libpath:"${MSVC_LIB}/${WINSDK_ARCH}" - -libpath:"${WINSDK_LIB}/ucrt/${WINSDK_ARCH}" - -libpath:"${WINSDK_LIB}/um/${WINSDK_ARCH}") - -if(case_sensitive_filesystem) - # Ensure all sub-configures use the top-level symlinks dir instead of generating their own. - init_user_prop(winsdk_lib_symlinks_dir) - if(NOT winsdk_lib_symlinks_dir) - set(winsdk_lib_symlinks_dir "${CMAKE_BINARY_DIR}/winsdk_lib_symlinks") - generate_winsdk_lib_symlinks("${WINSDK_BASE}/Lib/${WINSDK_VER}/um/${WINSDK_ARCH}" "${winsdk_lib_symlinks_dir}") - init_user_prop(winsdk_lib_symlinks_dir) - endif() - list(APPEND LINK_FLAGS - -libpath:"${winsdk_lib_symlinks_dir}") -endif() - -string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}") - -# See explanation for compiler flags above for the _INITIAL variables. -set(_CMAKE_EXE_LINKER_FLAGS_INITIAL "${CMAKE_EXE_LINKER_FLAGS}" CACHE STRING "") -set(CMAKE_EXE_LINKER_FLAGS "${_CMAKE_EXE_LINKER_FLAGS_INITIAL} ${LINK_FLAGS}" CACHE STRING "" FORCE) - -set(_CMAKE_MODULE_LINKER_FLAGS_INITIAL "${CMAKE_MODULE_LINKER_FLAGS}" CACHE STRING "") -set(CMAKE_MODULE_LINKER_FLAGS "${_CMAKE_MODULE_LINKER_FLAGS_INITIAL} ${LINK_FLAGS}" CACHE STRING "" FORCE) - -set(_CMAKE_SHARED_LINKER_FLAGS_INITIAL "${CMAKE_SHARED_LINKER_FLAGS}" CACHE STRING "") -set(CMAKE_SHARED_LINKER_FLAGS "${_CMAKE_SHARED_LINKER_FLAGS_INITIAL} ${LINK_FLAGS}" CACHE STRING "" FORCE) - -# CMake populates these with a bunch of unnecessary libraries, which requires -# extra case-correcting symlinks and what not. Instead, let projects explicitly -# control which libraries they require. -set(CMAKE_C_STANDARD_LIBRARIES "" CACHE STRING "" FORCE) -set(CMAKE_CXX_STANDARD_LIBRARIES "" CACHE STRING "" FORCE) - -# Allow clang-cl to work with macOS paths. -set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_LIST_DIR}/ClangClCMakeCompileRules.cmake") diff --git a/cmake/clang-cl-msvc-wsl.cmake b/cmake/clang-cl-msvc-wsl.cmake deleted file mode 100644 index ffe21314..00000000 --- a/cmake/clang-cl-msvc-wsl.cmake +++ /dev/null @@ -1,327 +0,0 @@ -# From llvm/cmake/platforms/WinMsvc.cmake -# Modified to use clang-cl on native Windows. - -# Cross toolchain configuration for using clang-cl on non-Windows hosts to -# target MSVC. -# -# Usage: -# cmake -G Ninja -# -DCMAKE_TOOLCHAIN_FILE=/path/to/this/file -# -DHOST_ARCH=[aarch64|arm64|armv7|arm|i686|x86|x86_64|x64] -# -DLLVM_NATIVE_TOOLCHAIN=/path/to/llvm/installation -# -DMSVC_BASE=/path/to/MSVC/system/libraries/and/includes -# -DWINSDK_BASE=/path/to/windows-sdk -# -DWINSDK_VER=windows sdk version folder name -# -# HOST_ARCH: -# The architecture to build for. -# -# LLVM_NATIVE_TOOLCHAIN: -# *Absolute path* to a folder containing the toolchain which will be used to -# build. At a minimum, this folder should have a bin directory with a -# copy of clang-cl, clang, clang++, and lld-link, as well as a lib directory -# containing clang's system resource directory. -# -# MSVC_BASE: -# *Absolute path* to the folder containing MSVC headers and system libraries. -# The layout of the folder matches that which is intalled by MSVC 2017 on -# Windows, and should look like this: -# -# ${MSVC_BASE} -# include -# vector -# stdint.h -# etc... -# lib -# x64 -# libcmt.lib -# msvcrt.lib -# etc... -# x86 -# libcmt.lib -# msvcrt.lib -# etc... -# -# For versions of MSVC < 2017, or where you have a hermetic toolchain in a -# custom format, you must use symlinks or restructure it to look like the above. -# -# WINSDK_BASE: -# Together with WINSDK_VER, determines the location of Windows SDK headers -# and libraries. -# -# WINSDK_VER: -# Together with WINSDK_BASE, determines the locations of Windows SDK headers -# and libraries. -# -# WINSDK_BASE and WINSDK_VER work together to define a folder layout that matches -# that of the Windows SDK installation on a standard Windows machine. It should -# match the layout described below. -# -# Note that if you install Windows SDK to a windows machine and simply copy the -# files, it will already be in the correct layout. -# -# ${WINSDK_BASE} -# Include -# ${WINSDK_VER} -# shared -# ucrt -# um -# windows.h -# etc... -# Lib -# ${WINSDK_VER} -# ucrt -# x64 -# x86 -# ucrt.lib -# etc... -# um -# x64 -# x86 -# kernel32.lib -# etc -# -# IMPORTANT: In order for this to work, you will need a valid copy of the Windows -# SDK and C++ STL headers and libraries on your host. Additionally, since the -# Windows libraries and headers are not case-correct, this toolchain file sets -# up a VFS overlay for the SDK headers and case-correcting symlinks for the -# libraries when running on a case-sensitive filesystem. - - -# When configuring CMake with a toolchain file against a top-level CMakeLists.txt, -# it will actually run CMake many times, once for each small test program used to -# determine what features a compiler supports. Unfortunately, none of these -# invocations share a CMakeCache.txt with the top-level invocation, meaning they -# won't see the value of any arguments the user passed via -D. Since these are -# necessary to properly configure MSVC in both the top-level configuration as well as -# all feature-test invocations, we set environment variables with the values so that -# these environments get inherited by child invocations. We can switch to -# CMAKE_TRY_COMPILE_PLATFORM_VARIABLES once our minimum supported CMake version -# is 3.6 or greater. -function(init_user_prop prop) - if(${prop}) - set(ENV{_${prop}} "${${prop}}") - else() - set(${prop} "$ENV{_${prop}}" PARENT_SCOPE) - endif() -endfunction() - -function(generate_winsdk_vfs_overlay winsdk_include_dir output_path) - set(include_dirs) - file(GLOB_RECURSE entries LIST_DIRECTORIES true "${winsdk_include_dir}/*") - foreach(entry ${entries}) - if(IS_DIRECTORY "${entry}") - list(APPEND include_dirs "${entry}") - endif() - endforeach() - - file(WRITE "${output_path}" "version: 0\n") - file(APPEND "${output_path}" "case-sensitive: false\n") - file(APPEND "${output_path}" "roots:\n") - - foreach(dir ${include_dirs}) - file(GLOB headers RELATIVE "${dir}" "${dir}/*.h") - if(NOT headers) - continue() - endif() - - file(APPEND "${output_path}" " - name: \"${dir}\"\n") - file(APPEND "${output_path}" " type: directory\n") - file(APPEND "${output_path}" " contents:\n") - - foreach(header ${headers}) - file(APPEND "${output_path}" " - name: \"${header}\"\n") - file(APPEND "${output_path}" " type: file\n") - file(APPEND "${output_path}" " external-contents: \"${dir}/${header}\"\n") - endforeach() - endforeach() -endfunction() - -function(generate_winsdk_lib_symlinks winsdk_um_lib_dir output_dir) - execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${output_dir}") - file(GLOB libraries RELATIVE "${winsdk_um_lib_dir}" "${winsdk_um_lib_dir}/*") - foreach(library ${libraries}) - string(TOLOWER "${library}" all_lowercase_symlink_name) - if(NOT library STREQUAL all_lowercase_symlink_name) - execute_process(COMMAND "${CMAKE_COMMAND}" - -E create_symlink - "${winsdk_um_lib_dir}/${library}" - "${output_dir}/${all_lowercase_symlink_name}") - endif() - - get_filename_component(name_we "${library}" NAME_WE) - get_filename_component(ext "${library}" EXT) - string(TOLOWER "${ext}" lowercase_ext) - set(lowercase_ext_symlink_name "${name_we}${lowercase_ext}") - if(NOT library STREQUAL lowercase_ext_symlink_name AND - NOT all_lowercase_symlink_name STREQUAL lowercase_ext_symlink_name) - execute_process(COMMAND "${CMAKE_COMMAND}" - -E create_symlink - "${winsdk_um_lib_dir}/${library}" - "${output_dir}/${lowercase_ext_symlink_name}") - endif() - endforeach() -endfunction() - -set(CMAKE_SYSTEM_NAME Windows) -set(CMAKE_SYSTEM_VERSION 10.0) -set(CMAKE_SYSTEM_PROCESSOR AMD64) - -init_user_prop(HOST_ARCH) -init_user_prop(LLVM_NATIVE_TOOLCHAIN) -init_user_prop(MSVC_BASE) -init_user_prop(WINSDK_BASE) -init_user_prop(WINSDK_VER) - -if(NOT HOST_ARCH) - set(HOST_ARCH x86_64) -endif() -if(HOST_ARCH STREQUAL "aarch64" OR HOST_ARCH STREQUAL "arm64") - set(TRIPLE_ARCH "aarch64") - set(WINSDK_ARCH "arm64") -elseif(HOST_ARCH STREQUAL "armv7" OR HOST_ARCH STREQUAL "arm") - set(TRIPLE_ARCH "armv7") - set(WINSDK_ARCH "arm") -elseif(HOST_ARCH STREQUAL "i686" OR HOST_ARCH STREQUAL "x86") - set(TRIPLE_ARCH "i686") - set(WINSDK_ARCH "x86") -elseif(HOST_ARCH STREQUAL "x86_64" OR HOST_ARCH STREQUAL "x64") - set(TRIPLE_ARCH "x86_64") - set(WINSDK_ARCH "x64") -else() - message(SEND_ERROR "Unknown host architecture ${HOST_ARCH}. Must be aarch64 (or arm64), armv7 (or arm), i686 (or x86), or x86_64 (or x64).") -endif() - -set(MSVC_INCLUDE "${MSVC_BASE}/include") -set(ATLMFC_INCLUDE "${MSVC_BASE}/atlmfc/include") -set(MSVC_LIB "${MSVC_BASE}/lib") -set(ATLMFC_LIB "${MSVC_BASE}/atlmfc/lib") -set(WINSDK_INCLUDE "${WINSDK_BASE}/Include/${WINSDK_VER}") -set(WINSDK_LIB "${WINSDK_BASE}/Lib/${WINSDK_VER}") - -# Do some sanity checking to make sure we can find a native toolchain and -# that the Windows SDK / MSVC STL directories look kosher. -if(NOT EXISTS "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl" OR - NOT EXISTS "${LLVM_NATIVE_TOOLCHAIN}/bin/lld-link") - message(SEND_ERROR - "LLVM_NATIVE_TOOLCHAIN folder '${LLVM_NATIVE_TOOLCHAIN}' does not " - "point to a valid directory containing bin/clang-cl and bin/lld-link " - "binaries") -endif() - -if(NOT EXISTS "${MSVC_BASE}" OR - NOT EXISTS "${MSVC_INCLUDE}" OR - NOT EXISTS "${MSVC_LIB}") - message(SEND_ERROR - "CMake variable MSVC_BASE must point to a folder containing MSVC " - "system headers and libraries") -endif() - -if(NOT EXISTS "${WINSDK_BASE}" OR - NOT EXISTS "${WINSDK_INCLUDE}" OR - NOT EXISTS "${WINSDK_LIB}") - message(SEND_ERROR - "CMake variable WINSDK_BASE and WINSDK_VER must resolve to a valid " - "Windows SDK installation") -endif() - -if(NOT EXISTS "${WINSDK_INCLUDE}/um/Windows.h") - message(SEND_ERROR "Cannot find Windows.h") -endif() -if(NOT EXISTS "${WINSDK_INCLUDE}/um/WINDOWS.H") - set(case_sensitive_filesystem TRUE) -endif() - -set(CMAKE_C_COMPILER "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl" CACHE FILEPATH "") -set(CMAKE_CXX_COMPILER "${LLVM_NATIVE_TOOLCHAIN}/bin/clang-cl" CACHE FILEPATH "") -set(CMAKE_LINKER "${LLVM_NATIVE_TOOLCHAIN}/bin/lld-link" CACHE FILEPATH "") - -# Even though we're cross-compiling, we need some native tools (e.g. llvm-tblgen), and those -# native tools have to be built before we can start doing the cross-build. LLVM supports -# a CROSS_TOOLCHAIN_FLAGS_NATIVE argument which consists of a list of flags to pass to CMake -# when configuring the NATIVE portion of the cross-build. By default we construct this so -# that it points to the tools in the same location as the native clang-cl that we're using. -list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_ASM_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang") -list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_C_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang") -list(APPEND _CTF_NATIVE_DEFAULT "-DCMAKE_CXX_COMPILER=${LLVM_NATIVE_TOOLCHAIN}/bin/clang++") - -set(CROSS_TOOLCHAIN_FLAGS_NATIVE "${_CTF_NATIVE_DEFAULT}" CACHE STRING "") - -set(COMPILE_FLAGS - -D_CRT_SECURE_NO_WARNINGS - --target=${TRIPLE_ARCH}-windows-msvc - -fms-compatibility-version=19.11 - -imsvc "\"${ATLMFC_INCLUDE}\"" - -imsvc "\"${MSVC_INCLUDE}\"" - -imsvc "\"${WINSDK_INCLUDE}/ucrt\"" - -imsvc "\"${WINSDK_INCLUDE}/shared\"" - -imsvc "\"${WINSDK_INCLUDE}/um\"" - -imsvc "\"${WINSDK_INCLUDE}/winrt\"") - -if(case_sensitive_filesystem) - # Ensure all sub-configures use the top-level VFS overlay instead of generating their own. - init_user_prop(winsdk_vfs_overlay_path) - if(NOT winsdk_vfs_overlay_path) - set(winsdk_vfs_overlay_path "${CMAKE_BINARY_DIR}/winsdk_vfs_overlay.yaml") - generate_winsdk_vfs_overlay("${WINSDK_BASE}/Include/${WINSDK_VER}" "${winsdk_vfs_overlay_path}") - init_user_prop(winsdk_vfs_overlay_path) - endif() - list(APPEND COMPILE_FLAGS - -Xclang -ivfsoverlay -Xclang "${winsdk_vfs_overlay_path}") -endif() - -string(REPLACE ";" " " COMPILE_FLAGS "${COMPILE_FLAGS}") - -# We need to preserve any flags that were passed in by the user. However, we -# can't append to CMAKE_C_FLAGS and friends directly, because toolchain files -# will be re-invoked on each reconfigure and therefore need to be idempotent. -# The assignments to the _INITIAL cache variables don't use FORCE, so they'll -# only be populated on the initial configure, and their values won't change -# afterward. -set(_CMAKE_C_FLAGS_INITIAL "${CMAKE_C_FLAGS}" CACHE STRING "") -set(CMAKE_C_FLAGS "${_CMAKE_C_FLAGS_INITIAL} ${COMPILE_FLAGS}" CACHE STRING "" FORCE) - -set(_CMAKE_CXX_FLAGS_INITIAL "${CMAKE_CXX_FLAGS}" CACHE STRING "") -set(CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS_INITIAL} ${COMPILE_FLAGS}" CACHE STRING "" FORCE) - -set(LINK_FLAGS - # Prevent CMake from attempting to invoke mt.exe. It only recognizes the slashed form and not the dashed form. - /manifest:no - - -libpath:"${ATLMFC_LIB}/${WINSDK_ARCH}" - -libpath:"${MSVC_LIB}/${WINSDK_ARCH}" - -libpath:"${WINSDK_LIB}/ucrt/${WINSDK_ARCH}" - -libpath:"${WINSDK_LIB}/um/${WINSDK_ARCH}") - -if(case_sensitive_filesystem) - # Ensure all sub-configures use the top-level symlinks dir instead of generating their own. - init_user_prop(winsdk_lib_symlinks_dir) - if(NOT winsdk_lib_symlinks_dir) - set(winsdk_lib_symlinks_dir "${CMAKE_BINARY_DIR}/winsdk_lib_symlinks") - generate_winsdk_lib_symlinks("${WINSDK_BASE}/Lib/${WINSDK_VER}/um/${WINSDK_ARCH}" "${winsdk_lib_symlinks_dir}") - init_user_prop(winsdk_lib_symlinks_dir) - endif() - list(APPEND LINK_FLAGS - -libpath:"${winsdk_lib_symlinks_dir}") -endif() - -string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}") - -# See explanation for compiler flags above for the _INITIAL variables. -set(_CMAKE_EXE_LINKER_FLAGS_INITIAL "${CMAKE_EXE_LINKER_FLAGS}" CACHE STRING "") -set(CMAKE_EXE_LINKER_FLAGS "${_CMAKE_EXE_LINKER_FLAGS_INITIAL} ${LINK_FLAGS}" CACHE STRING "" FORCE) - -set(_CMAKE_MODULE_LINKER_FLAGS_INITIAL "${CMAKE_MODULE_LINKER_FLAGS}" CACHE STRING "") -set(CMAKE_MODULE_LINKER_FLAGS "${_CMAKE_MODULE_LINKER_FLAGS_INITIAL} ${LINK_FLAGS}" CACHE STRING "" FORCE) - -set(_CMAKE_SHARED_LINKER_FLAGS_INITIAL "${CMAKE_SHARED_LINKER_FLAGS}" CACHE STRING "") -set(CMAKE_SHARED_LINKER_FLAGS "${_CMAKE_SHARED_LINKER_FLAGS_INITIAL} ${LINK_FLAGS}" CACHE STRING "" FORCE) - -# CMake populates these with a bunch of unnecessary libraries, which requires -# extra case-correcting symlinks and what not. Instead, let projects explicitly -# control which libraries they require. -set(CMAKE_C_STANDARD_LIBRARIES "" CACHE STRING "" FORCE) -set(CMAKE_CXX_STANDARD_LIBRARIES "" CACHE STRING "" FORCE) - -# Allow clang-cl to work with macOS paths. -set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_LIST_DIR}/ClangClCMakeCompileRules.cmake") diff --git a/cmake/linux_i386.toolchain.cmake b/cmake/linux_i386.toolchain.cmake deleted file mode 100644 index 9c4a5094..00000000 --- a/cmake/linux_i386.toolchain.cmake +++ /dev/null @@ -1,17 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR "i386") -set(CMAKE_C_COMPILER_TARGET i386-linux-gnu) - -# Assume debian/ubuntu -#set(CMAKE_FIND_ROOT_PATH /usr/lib/i386-linux-gnu/) - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - -# https://stackoverflow.com/questions/41557927/using-usr-lib-i386-linux-gnu-instead-of-usr-lib-x86-64-linux-gnu-to-find-libra -set(FIND_LIBRARY_USE_LIB64_PATHS OFF) diff --git a/cmake/llvm-mingw-cross.cmake b/cmake/llvm-mingw-cross.cmake deleted file mode 100644 index f7e1759c..00000000 --- a/cmake/llvm-mingw-cross.cmake +++ /dev/null @@ -1,24 +0,0 @@ -SET(CMAKE_SYSTEM_NAME Windows) - -IF (DEFINED ENV{LLVM_MINGW_DIR}) - SET(LLVM_MINGW_ROOT "$ENV{LLVM_MINGW_DIR}") -ELSE () - SET(LLVM_MINGW_ROOT "/mnt/data/local/llvm-mingw-20200325-ubuntu-18.04") -ENDIF() - - -SET(CMAKE_C_COMPILER ${LLVM_MINGW_ROOT}/bin/x86_64-w64-mingw32-clang) -SET(CMAKE_CXX_COMPILER ${LLVM_MINGW_ROOT}/bin/x86_64-w64-mingw32-clang++) -SET(CMAKE_RC_COMPILER ${LLVM_MINGW_ROOT}/bin/x86_64-w64-mingw32-windres) - -#SET(CMAKE_C_LINK_EXECUTABLE x86_64-w64-mingw32-gcc) -#SET(CMAKE_CXX_LINK_EXECUTABLE x86_64-w64-mingw32-g++) - -SET(CMAKE_FIND_ROOT_PATH ${LLVM_MINGW_ROOT}/x86_64-w64-mingw32) - -# We may need some advanced thread APIs to compile, so enable 0x601(Win7) if required. -# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WIN32_WINNT=0x601") - -SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/llvm-mingw-win64.cmake b/cmake/llvm-mingw-win64.cmake deleted file mode 100644 index 439b5240..00000000 --- a/cmake/llvm-mingw-win64.cmake +++ /dev/null @@ -1,20 +0,0 @@ -SET(CMAKE_SYSTEM_NAME Windows) - -IF (DEFINED ENV{LLVM_MINGW_DIR}) - SET(LLVM_MINGW_ROOT "$ENV{LLVM_MINGW_DIR}") -ELSE () - SET(LLVM_MINGW_ROOT "C:/ProgramData/llvm-mingw") -ENDIF() - -SET(CMAKE_C_COMPILER ${LLVM_MINGW_ROOT}/bin/x86_64-w64-mingw32-clang.exe) -SET(CMAKE_CXX_COMPILER ${LLVM_MINGW_ROOT}/bin/x86_64-w64-mingw32-clang++.exe) -SET(CMAKE_RC_COMPILER ${LLVM_MINGW_ROOT}/bin/x86_64-w64-mingw32-windres.exe) - -SET(CMAKE_FIND_ROOT_PATH ${LLVM_MINGW_ROOT}/x86_64-w64-mingw32) - -# We may need some advanced thread APIs to compile tinyusz. use 0x601(Win7) if required -# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WIN32_WINNT=0x601") - -SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/mingw64-cross.cmake b/cmake/mingw64-cross.cmake deleted file mode 100644 index 479cdd4d..00000000 --- a/cmake/mingw64-cross.cmake +++ /dev/null @@ -1,20 +0,0 @@ -SET(CMAKE_SYSTEM_NAME Windows) - -IF (DEFINED ENV{MINGW_GCC_DIR}) - SET(MINGW_GCC_ROOT "$ENV{MINGW_GCC_DIR}") -ELSE () - # Assume mingw cross compiler is installed in your system - SET(MINGW_GCC_ROOT "/usr") -ENDIF() - -# win32 may fail to compile with C++11 threads. - -SET(CMAKE_C_COMPILER ${MINGW_GCC_ROOT}/bin/x86_64-w64-mingw32-gcc-posix) -SET(CMAKE_CXX_COMPILER ${MINGW_GCC_ROOT}/bin/x86_64-w64-mingw32-g++-posix) -SET(CMAKE_RC_COMPILER ${MINGW_GCC_ROOT}/bin/x86_64-w64-mingw32-windres) - -SET(CMAKE_FIND_ROOT_PATH ${MINGW_GCC_ROOT}/x86_64-w64-mingw32) - -SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/sanitizers/FindASan.cmake b/cmake/sanitizers/FindASan.cmake deleted file mode 100644 index 98ea7cb3..00000000 --- a/cmake/sanitizers/FindASan.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# 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. - -option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) - -set(FLAG_CANDIDATES - # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. - "-g -fsanitize=address -fno-omit-frame-pointer" - "-g -fsanitize=address" - - # Older deprecated flag for ASan - "-g -faddress-sanitizer" -) - - -if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) - message(FATAL_ERROR "AddressSanitizer is not compatible with " - "ThreadSanitizer or MemorySanitizer.") -endif () - - -include(sanitize-helpers) - -if (SANITIZE_ADDRESS) - sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" - "ASan") - - find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) - mark_as_advanced(ASan_WRAPPER) -endif () - -function (add_sanitize_address TARGET) - if (NOT SANITIZE_ADDRESS) - return() - endif () - - sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") -endfunction () diff --git a/cmake/sanitizers/FindMSan.cmake b/cmake/sanitizers/FindMSan.cmake deleted file mode 100644 index 22d0050e..00000000 --- a/cmake/sanitizers/FindMSan.cmake +++ /dev/null @@ -1,57 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# 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. - -option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) - -set(FLAG_CANDIDATES - "-g -fsanitize=memory" -) - - -include(sanitize-helpers) - -if (SANITIZE_MEMORY) - if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - message(WARNING "MemorySanitizer disabled for target ${TARGET} because " - "MemorySanitizer is supported for Linux systems only.") - set(SANITIZE_MEMORY Off CACHE BOOL - "Enable MemorySanitizer for sanitized targets." FORCE) - elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) - message(WARNING "MemorySanitizer disabled for target ${TARGET} because " - "MemorySanitizer is supported for 64bit systems only.") - set(SANITIZE_MEMORY Off CACHE BOOL - "Enable MemorySanitizer for sanitized targets." FORCE) - else () - sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" - "MSan") - endif () -endif () - -function (add_sanitize_memory TARGET) - if (NOT SANITIZE_MEMORY) - return() - endif () - - sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") -endfunction () diff --git a/cmake/sanitizers/FindSanitizers.cmake b/cmake/sanitizers/FindSanitizers.cmake deleted file mode 100755 index 101bab84..00000000 --- a/cmake/sanitizers/FindSanitizers.cmake +++ /dev/null @@ -1,94 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# 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. - -# If any of the used compiler is a GNU compiler, add a second option to static -# link against the sanitizers. -option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) - - - - -set(FIND_QUIETLY_FLAG "") -if (DEFINED Sanitizers_FIND_QUIETLY) - set(FIND_QUIETLY_FLAG "QUIET") -endif () - -find_package(ASan ${FIND_QUIETLY_FLAG}) -find_package(TSan ${FIND_QUIETLY_FLAG}) -find_package(MSan ${FIND_QUIETLY_FLAG}) -find_package(UBSan ${FIND_QUIETLY_FLAG}) - - - - -function(sanitizer_add_blacklist_file FILE) - if(NOT IS_ABSOLUTE ${FILE}) - set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") - endif() - get_filename_component(FILE "${FILE}" REALPATH) - - sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" - "SanitizerBlacklist" "SanBlist") -endfunction() - -function(add_sanitizers ...) - # If no sanitizer is enabled, return immediately. - if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR - SANITIZE_UNDEFINED)) - return() - endif () - - foreach (TARGET ${ARGV}) - # Check if this target will be compiled by exactly one compiler. Other- - # wise sanitizers can't be used and a warning should be printed once. - get_target_property(TARGET_TYPE ${TARGET} TYPE) - if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") - message(WARNING "Can't use any sanitizers for target ${TARGET}, " - "because it is an interface library and cannot be " - "compiled directly.") - return() - endif () - sanitizer_target_compilers(${TARGET} TARGET_COMPILER) - list(LENGTH TARGET_COMPILER NUM_COMPILERS) - if (NUM_COMPILERS GREATER 1) - message(WARNING "Can't use any sanitizers for target ${TARGET}, " - "because it will be compiled by incompatible compilers. " - "Target will be compiled without sanitizers.") - return() - - # If the target is compiled by no or no known compiler, give a warning. - elseif (NUM_COMPILERS EQUAL 0) - message(WARNING "Sanitizers for target ${TARGET} may not be" - " usable, because it uses no or an unknown compiler. " - "This is a false warning for targets using only " - "object lib(s) as input.") - endif () - - # Add sanitizers for target. - add_sanitize_address(${TARGET}) - add_sanitize_thread(${TARGET}) - add_sanitize_memory(${TARGET}) - add_sanitize_undefined(${TARGET}) - endforeach () -endfunction(add_sanitizers) diff --git a/cmake/sanitizers/FindTSan.cmake b/cmake/sanitizers/FindTSan.cmake deleted file mode 100644 index 3cba3c03..00000000 --- a/cmake/sanitizers/FindTSan.cmake +++ /dev/null @@ -1,65 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# 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. - -option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) - -set(FLAG_CANDIDATES - "-g -fsanitize=thread" -) - - -# ThreadSanitizer is not compatible with MemorySanitizer. -if (SANITIZE_THREAD AND SANITIZE_MEMORY) - message(FATAL_ERROR "ThreadSanitizer is not compatible with " - "MemorySanitizer.") -endif () - - -include(sanitize-helpers) - -if (SANITIZE_THREAD) - if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND - NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " - "ThreadSanitizer is supported for Linux systems and macOS only.") - set(SANITIZE_THREAD Off CACHE BOOL - "Enable ThreadSanitizer for sanitized targets." FORCE) - elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) - message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " - "ThreadSanitizer is supported for 64bit systems only.") - set(SANITIZE_THREAD Off CACHE BOOL - "Enable ThreadSanitizer for sanitized targets." FORCE) - else () - sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" - "TSan") - endif () -endif () - -function (add_sanitize_thread TARGET) - if (NOT SANITIZE_THREAD) - return() - endif () - - sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") -endfunction () diff --git a/cmake/sanitizers/FindUBSan.cmake b/cmake/sanitizers/FindUBSan.cmake deleted file mode 100644 index ae103f71..00000000 --- a/cmake/sanitizers/FindUBSan.cmake +++ /dev/null @@ -1,46 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# 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. - -option(SANITIZE_UNDEFINED - "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) - -set(FLAG_CANDIDATES - "-g -fsanitize=undefined" -) - - -include(sanitize-helpers) - -if (SANITIZE_UNDEFINED) - sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" - "UndefinedBehaviorSanitizer" "UBSan") -endif () - -function (add_sanitize_undefined TARGET) - if (NOT SANITIZE_UNDEFINED) - return() - endif () - - sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") -endfunction () diff --git a/cmake/sanitizers/asan-wrapper b/cmake/sanitizers/asan-wrapper deleted file mode 100755 index 5d541033..00000000 --- a/cmake/sanitizers/asan-wrapper +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh - -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# 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. - -# This script is a wrapper for AddressSanitizer. In some special cases you need -# to preload AddressSanitizer to avoid error messages - e.g. if you're -# preloading another library to your application. At the moment this script will -# only do something, if we're running on a Linux platform. OSX might not be -# affected. - - -# Exit immediately, if platform is not Linux. -if [ "$(uname)" != "Linux" ] -then - exec $@ -fi - - -# Get the used libasan of the application ($1). If a libasan was found, it will -# be prepended to LD_PRELOAD. -libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) -if [ -n "$libasan" ] -then - if [ -n "$LD_PRELOAD" ] - then - export LD_PRELOAD="$libasan:$LD_PRELOAD" - else - export LD_PRELOAD="$libasan" - fi -fi - -# Execute the application. -exec $@ diff --git a/cmake/sanitizers/sanitize-helpers.cmake b/cmake/sanitizers/sanitize-helpers.cmake deleted file mode 100755 index 3649b074..00000000 --- a/cmake/sanitizers/sanitize-helpers.cmake +++ /dev/null @@ -1,177 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# 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. - -# Helper function to get the language of a source file. -function (sanitizer_lang_of_source FILE RETURN_VAR) - get_filename_component(LONGEST_EXT "${FILE}" EXT) - # If extension is empty return. This can happen for extensionless headers - if("${LONGEST_EXT}" STREQUAL "") - set(${RETURN_VAR} "" PARENT_SCOPE) - return() - endif() - # Get shortest extension as some files can have dot in their names - string(REGEX REPLACE "^.*(\\.[^.]+)$" "\\1" FILE_EXT ${LONGEST_EXT}) - string(TOLOWER "${FILE_EXT}" FILE_EXT) - string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) - - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - foreach (LANG ${ENABLED_LANGUAGES}) - list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) - if (NOT ${TEMP} EQUAL -1) - set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) - return() - endif () - endforeach() - - set(${RETURN_VAR} "" PARENT_SCOPE) -endfunction () - - -# Helper function to get compilers used by a target. -function (sanitizer_target_compilers TARGET RETURN_VAR) - # Check if all sources for target use the same compiler. If a target uses - # e.g. C and Fortran mixed and uses different compilers (e.g. clang and - # gfortran) this can trigger huge problems, because different compilers may - # use different implementations for sanitizers. - set(BUFFER "") - get_target_property(TSOURCES ${TARGET} SOURCES) - foreach (FILE ${TSOURCES}) - # If expression was found, FILE is a generator-expression for an object - # library. Object libraries will be ignored. - string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) - if ("${_file}" STREQUAL "") - sanitizer_lang_of_source(${FILE} LANG) - if (LANG) - list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) - endif () - endif () - endforeach () - - list(REMOVE_DUPLICATES BUFFER) - set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) -endfunction () - - -# Helper function to check compiler flags for language compiler. -function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" ${VARIABLE}) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) - - elseif (${LANG} STREQUAL "Fortran") - # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible - # with older Cmake versions, we will check if this module is present - # before we use it. Otherwise we will define Fortran coverage support as - # not available. - include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) - if (INCLUDED) - check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) - elseif (NOT CMAKE_REQUIRED_QUIET) - message(STATUS "Performing Test ${VARIABLE}") - message(STATUS "Performing Test ${VARIABLE}" - " - Failed (Check not supported)") - endif () - endif() -endfunction () - - -# Helper function to test compiler flags. -function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) - set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) - - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - foreach (LANG ${ENABLED_LANGUAGES}) - # Sanitizer flags are not dependend on language, but the used compiler. - # So instead of searching flags foreach language, search flags foreach - # compiler used. - set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) - foreach (FLAG ${FLAG_CANDIDATES}) - if(NOT CMAKE_REQUIRED_QUIET) - message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") - endif() - - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - unset(${PREFIX}_FLAG_DETECTED CACHE) - sanitizer_check_compiler_flag("${FLAG}" ${LANG} - ${PREFIX}_FLAG_DETECTED) - - if (${PREFIX}_FLAG_DETECTED) - # If compiler is a GNU compiler, search for static flag, if - # SANITIZE_LINK_STATIC is enabled. - if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) - string(TOLOWER ${PREFIX} PREFIX_lower) - sanitizer_check_compiler_flag( - "-static-lib${PREFIX_lower}" ${LANG} - ${PREFIX}_STATIC_FLAG_DETECTED) - - if (${PREFIX}_STATIC_FLAG_DETECTED) - set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") - endif () - endif () - - set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING - "${NAME} flags for ${COMPILER} compiler.") - mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) - break() - endif () - endforeach () - - if (NOT ${PREFIX}_FLAG_DETECTED) - set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING - "${NAME} flags for ${COMPILER} compiler.") - mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) - - message(WARNING "${NAME} is not available for ${COMPILER} " - "compiler. Targets using this compiler will be " - "compiled without ${NAME}.") - endif () - endif () - endforeach () -endfunction () - - -# Helper to assign sanitizer flags for TARGET. -function (sanitizer_add_flags TARGET NAME PREFIX) - # Get list of compilers used by target and check, if sanitizer is available - # for this target. Other compiler checks like check for conflicting - # compilers will be done in add_sanitizers function. - sanitizer_target_compilers(${TARGET} TARGET_COMPILER) - list(LENGTH TARGET_COMPILER NUM_COMPILERS) - if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") - return() - endif() - - # Set compile- and link-flags for target. - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") -endfunction () diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in deleted file mode 100644 index 2037e365..00000000 --- a/cmake_uninstall.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ -if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") - message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") -endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") - -file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) -string(REGEX REPLACE "\n" ";" files "${files}") -foreach(file ${files}) - message(STATUS "Uninstalling $ENV{DESTDIR}${file}") - if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") - exec_program( - "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" - OUTPUT_VARIABLE rm_out - RETURN_VALUE rm_retval - ) - if(NOT "${rm_retval}" STREQUAL 0) - message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") - endif(NOT "${rm_retval}" STREQUAL 0) - else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") - message(STATUS "File $ENV{DESTDIR}${file} does not exist.") - endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") -endforeach(file) diff --git a/deps/cpplint.py b/deps/cpplint.py deleted file mode 100755 index ccc25d4c..00000000 --- a/deps/cpplint.py +++ /dev/null @@ -1,6323 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use 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 Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# 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. - -"""Does google-lint on c++ files. - -The goal of this script is to identify places in the code that *may* -be in non-compliance with google style. It does not attempt to fix -up these problems -- the point is to educate. It does also not -attempt to find all problems, or to ensure that everything it does -find is legitimately a problem. - -In particular, we can get very confused by /* and // inside strings! -We do a small hack, which is to ignore //'s with "'s after them on the -same line, but it is far from perfect (in either direction). -""" - -import codecs -import copy -import getopt -import math # for log -import os -import re -import sre_compile -import string -import sys -import unicodedata - - -_USAGE = """ -Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] - [--counting=total|toplevel|detailed] [--root=subdir] - [--linelength=digits] - [file] ... - - The style guidelines this tries to follow are those in - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml - - Every problem is given a confidence score from 1-5, with 5 meaning we are - certain of the problem, and 1 meaning it could be a legitimate construct. - This will miss some errors, and is not a substitute for a code review. - - To suppress false-positive errors of a certain category, add a - 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) - suppresses errors of all categories on that line. - - The files passed in will be linted; at least one file must be provided. - Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the - extensions with the --extensions flag. - - Flags: - - output=vs7 - By default, the output is formatted to ease emacs parsing. Visual Studio - compatible output (vs7) may also be used. Other formats are unsupported. - - verbose=# - Specify a number 0-5 to restrict errors to certain verbosity levels. - - filter=-x,+y,... - Specify a comma-separated list of category-filters to apply: only - error messages whose category names pass the filters will be printed. - (Category names are printed with the message and look like - "[whitespace/indent]".) Filters are evaluated left to right. - "-FOO" and "FOO" means "do not print categories that start with FOO". - "+FOO" means "do print categories that start with FOO". - - Examples: --filter=-whitespace,+whitespace/braces - --filter=whitespace,runtime/printf,+runtime/printf_format - --filter=-,+build/include_what_you_use - - To see a list of all the categories used in cpplint, pass no arg: - --filter= - - counting=total|toplevel|detailed - The total number of errors found is always printed. If - 'toplevel' is provided, then the count of errors in each of - the top-level categories like 'build' and 'whitespace' will - also be printed. If 'detailed' is provided, then a count - is provided for each category like 'build/class'. - - root=subdir - The root directory used for deriving header guard CPP variable. - By default, the header guard CPP variable is calculated as the relative - path to the directory that contains .git, .hg, or .svn. When this flag - is specified, the relative path is calculated from the specified - directory. If the specified directory does not exist, this flag is - ignored. - - Examples: - Assuming that src/.git exists, the header guard CPP variables for - src/chrome/browser/ui/browser.h are: - - No flag => CHROME_BROWSER_UI_BROWSER_H_ - --root=chrome => BROWSER_UI_BROWSER_H_ - --root=chrome/browser => UI_BROWSER_H_ - - linelength=digits - This is the allowed line length for the project. The default value is - 80 characters. - - Examples: - --linelength=120 - - extensions=extension,extension,... - The allowed file extensions that cpplint will check - - Examples: - --extensions=hpp,cpp - - cpplint.py supports per-directory configurations specified in CPPLINT.cfg - files. CPPLINT.cfg file can contain a number of key=value pairs. - Currently the following options are supported: - - set noparent - filter=+filter1,-filter2,... - exclude_files=regex - linelength=80 - - "set noparent" option prevents cpplint from traversing directory tree - upwards looking for more .cfg files in parent directories. This option - is usually placed in the top-level project directory. - - The "filter" option is similar in function to --filter flag. It specifies - message filters in addition to the |_DEFAULT_FILTERS| and those specified - through --filter command-line flag. - - "exclude_files" allows to specify a regular expression to be matched against - a file name. If the expression matches, the file is skipped and not run - through liner. - - "linelength" allows to specify the allowed line length for the project. - - CPPLINT.cfg has an effect on files in the same directory and all - sub-directories, unless overridden by a nested configuration file. - - Example file: - filter=-build/include_order,+build/include_alpha - exclude_files=.*\.cc - - The above example disables build/include_order warning and enables - build/include_alpha as well as excludes all .cc from being - processed by linter, in the current directory (where the .cfg - file is located) and all sub-directories. -""" - -# We categorize each error message we print. Here are the categories. -# We want an explicit list so we can list them all in cpplint --filter=. -# If you add a new error message with a new category, add it to the list -# here! cpplint_unittest.py should tell you if you forget to do this. -_ERROR_CATEGORIES = [ - 'build/class', - 'build/c++11', - 'build/deprecated', - 'build/endif_comment', - 'build/explicit_make_pair', - 'build/forward_decl', - 'build/header_guard', - 'build/include', - 'build/include_alpha', - 'build/include_order', - 'build/include_what_you_use', - 'build/namespaces', - 'build/printf_format', - 'build/storage_class', - 'legal/copyright', - 'readability/alt_tokens', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/function', - 'readability/inheritance', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/namespace', - 'readability/nolint', - 'readability/nul', - 'readability/strings', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/indentation_namespace', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/string', - 'runtime/threadsafe_fn', - 'runtime/vlog', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/empty_conditional_body', - 'whitespace/empty_loop_body', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/forcolon', - 'whitespace/indent', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo', - ] - -# These error categories are no longer enforced by cpplint, but for backwards- -# compatibility they may still appear in NOLINT comments. -_LEGACY_ERROR_CATEGORIES = [ - 'readability/streams', - ] - -# The default state of the category filter. This is overridden by the --filter= -# flag. By default all errors are on, so only add here categories that should be -# off by default (i.e., categories that must be enabled by the --filter= flags). -# All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] - -# We used to check for high-bit characters, but after much discussion we -# decided those were OK, as long as they were in UTF-8 and didn't represent -# hard-coded international strings, which belong in a separate i18n file. - -# C++ headers -_CPP_HEADERS = frozenset([ - # Legacy - 'algobase.h', - 'algo.h', - 'alloc.h', - 'builtinbuf.h', - 'bvector.h', - 'complex.h', - 'defalloc.h', - 'deque.h', - 'editbuf.h', - 'fstream.h', - 'function.h', - 'hash_map', - 'hash_map.h', - 'hash_set', - 'hash_set.h', - 'hashtable.h', - 'heap.h', - 'indstream.h', - 'iomanip.h', - 'iostream.h', - 'istream.h', - 'iterator.h', - 'list.h', - 'map.h', - 'multimap.h', - 'multiset.h', - 'ostream.h', - 'pair.h', - 'parsestream.h', - 'pfstream.h', - 'procbuf.h', - 'pthread_alloc', - 'pthread_alloc.h', - 'rope', - 'rope.h', - 'ropeimpl.h', - 'set.h', - 'slist', - 'slist.h', - 'stack.h', - 'stdiostream.h', - 'stl_alloc.h', - 'stl_relops.h', - 'streambuf.h', - 'stream.h', - 'strfile.h', - 'strstream.h', - 'tempbuf.h', - 'tree.h', - 'type_traits.h', - 'vector.h', - # 17.6.1.2 C++ library headers - 'algorithm', - 'array', - 'atomic', - 'bitset', - 'chrono', - 'codecvt', - 'complex', - 'condition_variable', - 'deque', - 'exception', - 'forward_list', - 'fstream', - 'functional', - 'future', - 'initializer_list', - 'iomanip', - 'ios', - 'iosfwd', - 'iostream', - 'istream', - 'iterator', - 'limits', - 'list', - 'locale', - 'map', - 'memory', - 'mutex', - 'new', - 'numeric', - 'ostream', - 'queue', - 'random', - 'ratio', - 'regex', - 'set', - 'sstream', - 'stack', - 'stdexcept', - 'streambuf', - 'string', - 'strstream', - 'system_error', - 'thread', - 'tuple', - 'typeindex', - 'typeinfo', - 'type_traits', - 'unordered_map', - 'unordered_set', - 'utility', - 'valarray', - 'vector', - # 17.6.1.2 C++ headers for C library facilities - 'cassert', - 'ccomplex', - 'cctype', - 'cerrno', - 'cfenv', - 'cfloat', - 'cinttypes', - 'ciso646', - 'climits', - 'clocale', - 'cmath', - 'csetjmp', - 'csignal', - 'cstdalign', - 'cstdarg', - 'cstdbool', - 'cstddef', - 'cstdint', - 'cstdio', - 'cstdlib', - 'cstring', - 'ctgmath', - 'ctime', - 'cuchar', - 'cwchar', - 'cwctype', - ]) - - -# These headers are excluded from [build/include] and [build/include_order] -# checks: -# - Anything not following google file name conventions (containing an -# uppercase character, such as Python.h or nsStringAPI.h, for example). -# - Lua headers. -_THIRD_PARTY_HEADERS_PATTERN = re.compile( - r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') - - -# Assertion macros. These are defined in base/logging.h and -# testing/base/gunit.h. Note that the _M versions need to come first -# for substring matching to work. -_CHECK_MACROS = [ - 'DCHECK', 'CHECK', - 'EXPECT_TRUE_M', 'EXPECT_TRUE', - 'ASSERT_TRUE_M', 'ASSERT_TRUE', - 'EXPECT_FALSE_M', 'EXPECT_FALSE', - 'ASSERT_FALSE_M', 'ASSERT_FALSE', - ] - -# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE -_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) - -for op, replacement in [('==', 'EQ'), ('!=', 'NE'), - ('>=', 'GE'), ('>', 'GT'), - ('<=', 'LE'), ('<', 'LT')]: - _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement - _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement - -for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), - ('>=', 'LT'), ('>', 'LE'), - ('<=', 'GT'), ('<', 'GE')]: - _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement - _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement - -# Alternative tokens and their replacements. For full list, see section 2.5 -# Alternative tokens [lex.digraph] in the C++ standard. -# -# Digraphs (such as '%:') are not included here since it's a mess to -# match those on a word boundary. -_ALT_TOKEN_REPLACEMENT = { - 'and': '&&', - 'bitor': '|', - 'or': '||', - 'xor': '^', - 'compl': '~', - 'bitand': '&', - 'and_eq': '&=', - 'or_eq': '|=', - 'xor_eq': '^=', - 'not': '!', - 'not_eq': '!=' - } - -# Compile regular expression that matches all the above keywords. The "[ =()]" -# bit is meant to avoid matching these keywords outside of boolean expressions. -# -# False positives include C-style multi-line comments and multi-line strings -# but those have always been troublesome for cpplint. -_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') - - -# These constants define types of headers for use with -# _IncludeState.CheckNextIncludeOrder(). -_C_SYS_HEADER = 1 -_CPP_SYS_HEADER = 2 -_LIKELY_MY_HEADER = 3 -_POSSIBLE_MY_HEADER = 4 -_OTHER_HEADER = 5 - -# These constants define the current inline assembly state -_NO_ASM = 0 # Outside of inline assembly block -_INSIDE_ASM = 1 # Inside inline assembly block -_END_ASM = 2 # Last line of inline assembly block -_BLOCK_ASM = 3 # The whole block is an inline assembly block - -# Match start of assembly blocks -_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' - r'(?:\s+(volatile|__volatile__))?' - r'\s*[{(]') - - -_regexp_compile_cache = {} - -# {str, set(int)}: a map from error categories to sets of linenumbers -# on which those errors are expected and should be suppressed. -_error_suppressions = {} - -# The root directory used for deriving header guard CPP variable. -# This is set by --root flag. -_root = None - -# The allowed line length of files. -# This is set by --linelength flag. -_line_length = 80 - -# The allowed extensions for file names -# This is set by --extensions flag. -_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh']) - -def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of error-suppressions. - - Parses any NOLINT comments on the current line, updating the global - error_suppressions store. Reports an error if the NOLINT comment - was malformed. - - Args: - filename: str, the name of the input file. - raw_line: str, the line of input text, with comments. - linenum: int, the number of the current line. - error: function, an error handler. - """ - matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) - if matched: - if matched.group(1): - suppressed_line = linenum + 1 - else: - suppressed_line = linenum - category = matched.group(2) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(suppressed_line) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] - if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(suppressed_line) - elif category not in _LEGACY_ERROR_CATEGORIES: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - - -def ResetNolintSuppressions(): - """Resets the set of NOLINT suppressions to empty.""" - _error_suppressions.clear() - - -def IsErrorSuppressedByNolint(category, linenum): - """Returns true if the specified error category is suppressed on this line. - - Consults the global error_suppressions map populated by - ParseNolintSuppressions/ResetNolintSuppressions. - - Args: - category: str, the category of the error. - linenum: int, the current line number. - Returns: - bool, True iff the error should be suppressed due to a NOLINT comment. - """ - return (linenum in _error_suppressions.get(category, set()) or - linenum in _error_suppressions.get(None, set())) - - -def Match(pattern, s): - """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both Match and Search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def ReplaceAll(pattern, rep, s): - """Replaces instances of pattern in a string with a replacement. - - The compiled regex is kept in a cache shared by Match and Search. - - Args: - pattern: regex pattern - rep: replacement text - s: search string - - Returns: - string with replacements made (or original string if no replacements) - """ - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].sub(rep, s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) - - -class _IncludeState(object): - """Tracks line numbers for includes, and the order in which includes appear. - - include_list contains list of lists of (header, line number) pairs. - It's a lists of lists rather than just one flat list to make it - easier to update across preprocessor boundaries. - - Call CheckNextIncludeOrder() once for each header in the file, passing - in the type constants defined above. Calls in an illegal order will - raise an _IncludeError with an appropriate error message. - - """ - # self._section will move monotonically through this set. If it ever - # needs to move backwards, CheckNextIncludeOrder will raise an error. - _INITIAL_SECTION = 0 - _MY_H_SECTION = 1 - _C_SECTION = 2 - _CPP_SECTION = 3 - _OTHER_H_SECTION = 4 - - _TYPE_NAMES = { - _C_SYS_HEADER: 'C system header', - _CPP_SYS_HEADER: 'C++ system header', - _LIKELY_MY_HEADER: 'header this file implements', - _POSSIBLE_MY_HEADER: 'header this file may implement', - _OTHER_HEADER: 'other header', - } - _SECTION_NAMES = { - _INITIAL_SECTION: "... nothing. (This can't be an error.)", - _MY_H_SECTION: 'a header this file implements', - _C_SECTION: 'C system header', - _CPP_SECTION: 'C++ system header', - _OTHER_H_SECTION: 'other header', - } - - def __init__(self): - self.include_list = [[]] - self.ResetSection('') - - def FindHeader(self, header): - """Check if a header has already been included. - - Args: - header: header to check. - Returns: - Line number of previous occurrence, or -1 if the header has not - been seen before. - """ - for section_list in self.include_list: - for f in section_list: - if f[0] == header: - return f[1] - return -1 - - def ResetSection(self, directive): - """Reset section checking for preprocessor directive. - - Args: - directive: preprocessor directive (e.g. "if", "else"). - """ - # The name of the current section. - self._section = self._INITIAL_SECTION - # The path of last found header. - self._last_header = '' - - # Update list of includes. Note that we never pop from the - # include list. - if directive in ('if', 'ifdef', 'ifndef'): - self.include_list.append([]) - elif directive in ('else', 'elif'): - self.include_list[-1] = [] - - def SetLastHeader(self, header_path): - self._last_header = header_path - - def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparison. - - - replaces "-" with "_" so they both cmp the same. - - removes '-inl' since we don't require them to be after the main header. - - lowercase everything, just in case. - - Args: - header_path: Path to be canonicalized. - - Returns: - Canonicalized path. - """ - return header_path.replace('-inl.h', '.h').replace('-', '_').lower() - - def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): - """Check if a header is in alphabetical order with the previous header. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - header_path: Canonicalized header to be checked. - - Returns: - Returns true if the header is in alphabetical order. - """ - # If previous section is different from current section, _last_header will - # be reset to empty string, so it's always less than current header. - # - # If previous line was a blank line, assume that the headers are - # intentionally sorted the way they are. - if (self._last_header > header_path and - Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): - return False - return True - - def CheckNextIncludeOrder(self, header_type): - """Returns a non-empty error message if the next header is out of order. - - This function also updates the internal state to be ready to check - the next include. - - Args: - header_type: One of the _XXX_HEADER constants defined above. - - Returns: - The empty string if the header is in the right order, or an - error message describing what's wrong. - - """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) - - last_section = self._section - - if header_type == _C_SYS_HEADER: - if self._section <= self._C_SECTION: - self._section = self._C_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _CPP_SYS_HEADER: - if self._section <= self._CPP_SECTION: - self._section = self._CPP_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _LIKELY_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - self._section = self._OTHER_H_SECTION - elif header_type == _POSSIBLE_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - # This will always be the fallback because we're not sure - # enough that the header is associated with this file. - self._section = self._OTHER_H_SECTION - else: - assert header_type == _OTHER_HEADER - self._section = self._OTHER_H_SECTION - - if last_section != self._section: - self._last_header = '' - - return '' - - -class _CppLintState(object): - """Maintains module-wide state..""" - - def __init__(self): - self.verbose_level = 1 # global setting. - self.error_count = 0 # global count of reported errors - # filters to apply when emitting error messages - self.filters = _DEFAULT_FILTERS[:] - # backup of filter list. Used to restore the state after each file. - self._filters_backup = self.filters[:] - self.counting = 'total' # In what way are we counting errors? - self.errors_by_category = {} # string to int dict storing error counts - - # output format: - # "emacs" - format that emacs can parse (default) - # "vs7" - format that Microsoft Visual Studio 7 can parse - self.output_format = 'emacs' - - def SetOutputFormat(self, output_format): - """Sets the output format for errors.""" - self.output_format = output_format - - def SetVerboseLevel(self, level): - """Sets the module's verbosity, and returns the previous setting.""" - last_verbose_level = self.verbose_level - self.verbose_level = level - return last_verbose_level - - def SetCountingStyle(self, counting_style): - """Sets the module's counting options.""" - self.counting = counting_style - - def SetFilters(self, filters): - """Sets the error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "+whitespace/indent"). - Each filter should start with + or -; else we die. - - Raises: - ValueError: The comma-separated filters did not all start with '+' or '-'. - E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" - """ - # Default filters always have less priority than the flag ones. - self.filters = _DEFAULT_FILTERS[:] - self.AddFilters(filters) - - def AddFilters(self, filters): - """ Adds more filters to the existing list of error-message filters. """ - for filt in filters.split(','): - clean_filt = filt.strip() - if clean_filt: - self.filters.append(clean_filt) - for filt in self.filters: - if not (filt.startswith('+') or filt.startswith('-')): - raise ValueError('Every filter in --filters must start with + or -' - ' (%s does not)' % filt) - - def BackupFilters(self): - """ Saves the current filter list to backup storage.""" - self._filters_backup = self.filters[:] - - def RestoreFilters(self): - """ Restores filters previously backed up.""" - self.filters = self._filters_backup[:] - - def ResetErrorCounts(self): - """Sets the module's error statistic back to zero.""" - self.error_count = 0 - self.errors_by_category = {} - - def IncrementErrorCount(self, category): - """Bumps the module's error statistic.""" - self.error_count += 1 - if self.counting in ('toplevel', 'detailed'): - if self.counting != 'detailed': - category = category.split('/')[0] - if category not in self.errors_by_category: - self.errors_by_category[category] = 0 - self.errors_by_category[category] += 1 - - def PrintErrorCounts(self): - """Print a summary of errors by category, and the total.""" - for category, count in self.errors_by_category.iteritems(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % - (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) - -_cpplint_state = _CppLintState() - - -def _OutputFormat(): - """Gets the module's output format.""" - return _cpplint_state.output_format - - -def _SetOutputFormat(output_format): - """Sets the module's output format.""" - _cpplint_state.SetOutputFormat(output_format) - - -def _VerboseLevel(): - """Returns the module's verbosity setting.""" - return _cpplint_state.verbose_level - - -def _SetVerboseLevel(level): - """Sets the module's verbosity, and returns the previous setting.""" - return _cpplint_state.SetVerboseLevel(level) - - -def _SetCountingStyle(level): - """Sets the module's counting options.""" - _cpplint_state.SetCountingStyle(level) - - -def _Filters(): - """Returns the module's list of output filters, as a list.""" - return _cpplint_state.filters - - -def _SetFilters(filters): - """Sets the module's error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.SetFilters(filters) - -def _AddFilters(filters): - """Adds more filter overrides. - - Unlike _SetFilters, this function does not reset the current list of filters - available. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.AddFilters(filters) - -def _BackupFilters(): - """ Saves the current filter list to backup storage.""" - _cpplint_state.BackupFilters() - -def _RestoreFilters(): - """ Restores filters previously backed up.""" - _cpplint_state.RestoreFilters() - -class _FunctionState(object): - """Tracks current function name and the number of lines in its body.""" - - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. - _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. - - def __init__(self): - self.in_a_function = False - self.lines_in_function = 0 - self.current_function = '' - - def Begin(self, function_name): - """Start analyzing function body. - - Args: - function_name: The name of the function being tracked. - """ - self.in_a_function = True - self.lines_in_function = 0 - self.current_function = function_name - - def Count(self): - """Count line in current function body.""" - if self.in_a_function: - self.lines_in_function += 1 - - def Check(self, error, filename, linenum): - """Report if too many lines in function body. - - Args: - error: The function to call with any errors found. - filename: The name of the current file. - linenum: The number of the line to check. - """ - if Match(r'T(EST|est)', self.current_function): - base_trigger = self._TEST_TRIGGER - else: - base_trigger = self._NORMAL_TRIGGER - trigger = base_trigger * 2**_VerboseLevel() - - if self.lines_in_function > trigger: - error_level = int(math.log(self.lines_in_function / base_trigger, 2)) - # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... - if error_level > 5: - error_level = 5 - error(filename, linenum, 'readability/fn_size', error_level, - 'Small and focused functions are preferred:' - ' %s has %d non-comment lines' - ' (error triggered by exceeding %d lines).' % ( - self.current_function, self.lines_in_function, trigger)) - - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False - - -class _IncludeError(Exception): - """Indicates a problem with the include order in a file.""" - pass - - -class FileInfo(object): - """Provides utility functions for filenames. - - FileInfo provides easy access to the components of a file's path - relative to the project root. - """ - - def __init__(self, filename): - self._filename = filename - - def FullName(self): - """Make Windows paths like Unix.""" - return os.path.abspath(self._filename).replace('\\', '/') - - def RepositoryName(self): - """FullName after removing the local path to the repository. - - If we have a real absolute path name here we can try to do something smart: - detecting the root of the checkout and truncating /path/to/checkout from - the name so that we get header guards that don't include things like - "C:\Documents and Settings\..." or "/home/username/..." in them and thus - people on different computers who have checked the source out to different - locations won't see bogus errors. - """ - fullname = self.FullName() - - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) - - if os.path.exists(os.path.join(project_dir, ".svn")): - # If there's a .svn file in the current directory, we recursively look - # up the directory tree for the top of the SVN checkout - root_dir = project_dir - one_up_dir = os.path.dirname(root_dir) - while os.path.exists(os.path.join(one_up_dir, ".svn")): - root_dir = os.path.dirname(root_dir) - one_up_dir = os.path.dirname(one_up_dir) - - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = os.path.dirname(fullname) - while (root_dir != os.path.dirname(root_dir) and - not os.path.exists(os.path.join(root_dir, ".git")) and - not os.path.exists(os.path.join(root_dir, ".hg")) and - not os.path.exists(os.path.join(root_dir, ".svn"))): - root_dir = os.path.dirname(root_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Don't know what to do; header guard warnings may be wrong... - return fullname - - def Split(self): - """Splits the file into the directory, basename, and extension. - - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') - - Returns: - A tuple of (directory, basename, extension). - """ - - googlename = self.RepositoryName() - project, rest = os.path.split(googlename) - return (project,) + os.path.splitext(rest) - - def BaseName(self): - """File base name - text after the final slash, before the final period.""" - return self.Split()[1] - - def Extension(self): - """File extension - text following the final period.""" - return self.Split()[2] - - def NoExtension(self): - """File has no source file extension.""" - return '/'.join(self.Split()[0:2]) - - def IsSource(self): - """File has a source file extension.""" - return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') - - -def _ShouldPrintError(category, confidence, linenum): - """If confidence >= verbose, category passes filter and is not suppressed.""" - - # There are three ways we might decide not to print an error message: - # a "NOLINT(category)" comment appears in the source, - # the verbosity level isn't high enough, or the filters filter it out. - if IsErrorSuppressedByNolint(category, linenum): - return False - - if confidence < _cpplint_state.verbose_level: - return False - - is_filtered = False - for one_filter in _Filters(): - if one_filter.startswith('-'): - if category.startswith(one_filter[1:]): - is_filtered = True - elif one_filter.startswith('+'): - if category.startswith(one_filter[1:]): - is_filtered = False - else: - assert False # should have been checked for in SetFilter. - if is_filtered: - return False - - return True - - -def Error(filename, linenum, category, confidence, message): - """Logs the fact we've found a lint error. - - We log where the error was found, and also our confidence in the error, - that is, how certain we are this is a legitimate style regression, and - not a misidentification or a use that's sometimes justified. - - False positives can be suppressed by the use of - "cpplint(category)" comments on the offending line. These are - parsed into _error_suppressions. - - Args: - filename: The name of the file containing the error. - linenum: The number of the line containing the error. - category: A string used to describe the "category" this bug - falls under: "whitespace", say, or "runtime". Categories - may have a hierarchy separated by slashes: "whitespace/indent". - confidence: A number from 1-5 representing a confidence score for - the error, with 5 meaning that we are certain of the problem, - and 1 meaning that it could be a legitimate construct. - message: The error message. - """ - if _ShouldPrintError(category, confidence, linenum): - _cpplint_state.IncrementErrorCount(category) - if _cpplint_state.output_format == 'vs7': - sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - else: - sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - - -# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. -_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( - r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Match a single C style comment on the same line. -_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' -# Matches multi-line C style comments. -# This RE is a little bit more complicated than one might expect, because we -# have to take care of space removals tools so we can handle comments inside -# statements better. -# The current rule is: We only clear spaces from both sides when we're at the -# end of the line. Otherwise, we try to remove spaces from the right side, -# if this doesn't work we try on left side but only if there's a non-character -# on the right. -_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( - r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + - _RE_PATTERN_C_COMMENTS + r'\s+|' + - r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + - _RE_PATTERN_C_COMMENTS + r')') - - -def IsCppString(line): - """Does line terminate so, that the next symbol is in string constant. - - This function does not consider single-line nor multi-line comments. - - Args: - line: is a partial line of code starting from the 0..n. - - Returns: - True, if next character appended to 'line' is inside a - string constant. - """ - - line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" - return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 - - -def CleanseRawStrings(raw_lines): - """Removes C++11 raw strings from lines. - - Before: - static const char kData[] = R"( - multi-line string - )"; - - After: - static const char kData[] = "" - (replaced by blank line) - ""; - - Args: - raw_lines: list of raw lines. - - Returns: - list of lines with C++11 raw strings replaced by empty strings. - """ - - delimiter = None - lines_without_raw_strings = [] - for line in raw_lines: - if delimiter: - # Inside a raw string, look for the end - end = line.find(delimiter) - if end >= 0: - # Found the end of the string, match leading space for this - # line and resume copying the original lines, and also insert - # a "" on the last line. - leading_space = Match(r'^(\s*)\S', line) - line = leading_space.group(1) + '""' + line[end + len(delimiter):] - delimiter = None - else: - # Haven't found the end yet, append a blank line. - line = '""' - - # Look for beginning of a raw string, and replace them with - # empty strings. This is done in a loop to handle multiple raw - # strings on the same line. - while delimiter is None: - # Look for beginning of a raw string. - # See 2.14.15 [lex.string] for syntax. - matched = Match(r'^(.*)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) - if matched: - delimiter = ')' + matched.group(2) + '"' - - end = matched.group(3).find(delimiter) - if end >= 0: - # Raw string ended on same line - line = (matched.group(1) + '""' + - matched.group(3)[end + len(delimiter):]) - delimiter = None - else: - # Start of a multi-line raw string - line = matched.group(1) + '""' - else: - break - - lines_without_raw_strings.append(line) - - # TODO(unknown): if delimiter is not None here, we might want to - # emit a warning for unterminated string. - return lines_without_raw_strings - - -def FindNextMultiLineCommentStart(lines, lineix): - """Find the beginning marker for a multiline comment.""" - while lineix < len(lines): - if lines[lineix].strip().startswith('/*'): - # Only return this marker if the comment goes beyond this line - if lines[lineix].strip().find('*/', 2) < 0: - return lineix - lineix += 1 - return len(lines) - - -def FindNextMultiLineCommentEnd(lines, lineix): - """We are inside a comment, find the end marker.""" - while lineix < len(lines): - if lines[lineix].strip().endswith('*/'): - return lineix - lineix += 1 - return len(lines) - - -def RemoveMultiLineCommentsFromRange(lines, begin, end): - """Clears a range of lines for multi-line comments.""" - # Having // dummy comments makes the lines non-empty, so we will not get - # unnecessary blank line warnings later in the code. - for i in range(begin, end): - lines[i] = '/**/' - - -def RemoveMultiLineComments(filename, lines, error): - """Removes multiline (c-style) comments from lines.""" - lineix = 0 - while lineix < len(lines): - lineix_begin = FindNextMultiLineCommentStart(lines, lineix) - if lineix_begin >= len(lines): - return - lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) - if lineix_end >= len(lines): - error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, - 'Could not find end of multi-line comment') - return - RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) - lineix = lineix_end + 1 - - -def CleanseComments(line): - """Removes //-comments and single-line C-style /* */ comments. - - Args: - line: A line of C++ source. - - Returns: - The line with single-line comments removed. - """ - commentpos = line.find('//') - if commentpos != -1 and not IsCppString(line[:commentpos]): - line = line[:commentpos].rstrip() - # get rid of /* ... */ - return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) - - -class CleansedLines(object): - """Holds 4 copies of all lines with different preprocessing applied to them. - - 1) elided member contains lines without strings and comments. - 2) lines member contains lines without comments. - 3) raw_lines member contains all the lines without processing. - 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw - strings removed. - All these members are of , and of the same length. - """ - - def __init__(self, lines): - self.elided = [] - self.lines = [] - self.raw_lines = lines - self.num_lines = len(lines) - self.lines_without_raw_strings = CleanseRawStrings(lines) - for linenum in range(len(self.lines_without_raw_strings)): - self.lines.append(CleanseComments( - self.lines_without_raw_strings[linenum])) - elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) - self.elided.append(CleanseComments(elided)) - - def NumLines(self): - """Returns the number of lines represented.""" - return self.num_lines - - @staticmethod - def _CollapseStrings(elided): - """Collapses strings and chars on a line to simple "" or '' blocks. - - We nix strings first so we're not fooled by text like '"http://"' - - Args: - elided: The line being processed. - - Returns: - The line with collapsed strings. - """ - if _RE_PATTERN_INCLUDE.match(elided): - return elided - - # Remove escaped characters first to make quote/single quote collapsing - # basic. Things that look like escaped characters shouldn't occur - # outside of strings and chars. - elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) - - # Replace quoted strings and digit separators. Both single quotes - # and double quotes are processed in the same loop, otherwise - # nested quotes wouldn't work. - collapsed = '' - while True: - # Find the first quote character - match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) - if not match: - collapsed += elided - break - head, quote, tail = match.groups() - - if quote == '"': - # Collapse double quoted strings - second_quote = tail.find('"') - if second_quote >= 0: - collapsed += head + '""' - elided = tail[second_quote + 1:] - else: - # Unmatched double quote, don't bother processing the rest - # of the line since this is probably a multiline string. - collapsed += elided - break - else: - # Found single quote, check nearby text to eliminate digit separators. - # - # There is no special handling for floating point here, because - # the integer/fractional/exponent parts would all be parsed - # correctly as long as there are digits on both sides of the - # separator. So we are fine as long as we don't see something - # like "0.'3" (gcc 4.9.0 will not allow this literal). - if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): - match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) - collapsed += head + match_literal.group(1).replace("'", '') - elided = match_literal.group(2) - else: - second_quote = tail.find('\'') - if second_quote >= 0: - collapsed += head + "''" - elided = tail[second_quote + 1:] - else: - # Unmatched single quote - collapsed += elided - break - - return collapsed - - -def FindEndOfExpressionInLine(line, startpos, stack): - """Find the position just after the end of current parenthesized expression. - - Args: - line: a CleansedLines line. - startpos: start searching at this position. - stack: nesting stack at startpos. - - Returns: - On finding matching end: (index just after matching end, None) - On finding an unclosed expression: (-1, None) - Otherwise: (-1, new stack at end of this line) - """ - for i in xrange(startpos, len(line)): - char = line[i] - if char in '([{': - # Found start of parenthesized expression, push to expression stack - stack.append(char) - elif char == '<': - # Found potential start of template argument list - if i > 0 and line[i - 1] == '<': - # Left shift operator - if stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - elif i > 0 and Search(r'\boperator\s*$', line[0:i]): - # operator<, don't add to stack - continue - else: - # Tentative start of template argument list - stack.append('<') - elif char in ')]}': - # Found end of parenthesized expression. - # - # If we are currently expecting a matching '>', the pending '<' - # must have been an operator. Remove them from expression stack. - while stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - if ((stack[-1] == '(' and char == ')') or - (stack[-1] == '[' and char == ']') or - (stack[-1] == '{' and char == '}')): - stack.pop() - if not stack: - return (i + 1, None) - else: - # Mismatched parentheses - return (-1, None) - elif char == '>': - # Found potential end of template argument list. - - # Ignore "->" and operator functions - if (i > 0 and - (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): - continue - - # Pop the stack if there is a matching '<'. Otherwise, ignore - # this '>' since it must be an operator. - if stack: - if stack[-1] == '<': - stack.pop() - if not stack: - return (i + 1, None) - elif char == ';': - # Found something that look like end of statements. If we are currently - # expecting a '>', the matching '<' must have been an operator, since - # template argument list should not contain statements. - while stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - - # Did not find end of expression or unbalanced parentheses on this line - return (-1, stack) - - -def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [ or <, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the - linenum/pos that correspond to the closing of the expression. - - TODO(unknown): cpplint spends a fair bit of time matching parentheses. - Ideally we would want to index all opening and closing parentheses once - and have CloseExpression be just a simple lookup, but due to preprocessor - tricks, this is not so easy. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *past* the closing brace, or - (line, len(lines), -1) if we never find a close. Note we ignore - strings and comments when matching; and the line we return is the - 'cleansed' line at linenum. - """ - - line = clean_lines.elided[linenum] - if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): - return (line, clean_lines.NumLines(), -1) - - # Check first line - (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) - if end_pos > -1: - return (line, linenum, end_pos) - - # Continue scanning forward - while stack and linenum < clean_lines.NumLines() - 1: - linenum += 1 - line = clean_lines.elided[linenum] - (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) - if end_pos > -1: - return (line, linenum, end_pos) - - # Did not find end of expression before end of file, give up - return (line, clean_lines.NumLines(), -1) - - -def FindStartOfExpressionInLine(line, endpos, stack): - """Find position at the matching start of current expression. - - This is almost the reverse of FindEndOfExpressionInLine, but note - that the input position and returned position differs by 1. - - Args: - line: a CleansedLines line. - endpos: start searching at this position. - stack: nesting stack at endpos. - - Returns: - On finding matching start: (index at matching start, None) - On finding an unclosed expression: (-1, None) - Otherwise: (-1, new stack at beginning of this line) - """ - i = endpos - while i >= 0: - char = line[i] - if char in ')]}': - # Found end of expression, push to expression stack - stack.append(char) - elif char == '>': - # Found potential end of template argument list. - # - # Ignore it if it's a "->" or ">=" or "operator>" - if (i > 0 and - (line[i - 1] == '-' or - Match(r'\s>=\s', line[i - 1:]) or - Search(r'\boperator\s*$', line[0:i]))): - i -= 1 - else: - stack.append('>') - elif char == '<': - # Found potential start of template argument list - if i > 0 and line[i - 1] == '<': - # Left shift operator - i -= 1 - else: - # If there is a matching '>', we can pop the expression stack. - # Otherwise, ignore this '<' since it must be an operator. - if stack and stack[-1] == '>': - stack.pop() - if not stack: - return (i, None) - elif char in '([{': - # Found start of expression. - # - # If there are any unmatched '>' on the stack, they must be - # operators. Remove those. - while stack and stack[-1] == '>': - stack.pop() - if not stack: - return (-1, None) - if ((char == '(' and stack[-1] == ')') or - (char == '[' and stack[-1] == ']') or - (char == '{' and stack[-1] == '}')): - stack.pop() - if not stack: - return (i, None) - else: - # Mismatched parentheses - return (-1, None) - elif char == ';': - # Found something that look like end of statements. If we are currently - # expecting a '<', the matching '>' must have been an operator, since - # template argument list should not contain statements. - while stack and stack[-1] == '>': - stack.pop() - if not stack: - return (-1, None) - - i -= 1 - - return (-1, stack) - - -def ReverseCloseExpression(clean_lines, linenum, pos): - """If input points to ) or } or ] or >, finds the position that opens it. - - If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the - linenum/pos that correspond to the opening of the expression. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *at* the opening brace, or - (line, 0, -1) if we never find the matching opening brace. Note - we ignore strings and comments when matching; and the line we - return is the 'cleansed' line at linenum. - """ - line = clean_lines.elided[linenum] - if line[pos] not in ')}]>': - return (line, 0, -1) - - # Check last line - (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) - if start_pos > -1: - return (line, linenum, start_pos) - - # Continue scanning backward - while stack and linenum > 0: - linenum -= 1 - line = clean_lines.elided[linenum] - (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) - if start_pos > -1: - return (line, linenum, start_pos) - - # Did not find start of expression before beginning of file, give up - return (line, 0, -1) - - -def CheckForCopyright(filename, lines, error): - """Logs an error if no Copyright message appears at the top of the file.""" - - # We'll say it should occur by line 10. Don't forget there's a - # dummy line at the front. - for line in xrange(1, min(len(lines), 11)): - if re.search(r'Copyright', lines[line], re.I): break - else: # means no copyright line was found - error(filename, 0, 'legal/copyright', 5, - 'No copyright message found. ' - 'You should have a line: "Copyright [year] "') - - -def GetIndentLevel(line): - """Return the number of leading spaces in line. - - Args: - line: A string to check. - - Returns: - An integer count of leading spaces, possibly zero. - """ - indent = Match(r'^( *)\S', line) - if indent: - return len(indent.group(1)) - else: - return 0 - - -def GetHeaderGuardCPPVariable(filename): - """Returns the CPP variable that should be used as a header guard. - - Args: - filename: The name of a C++ header file. - - Returns: - The CPP variable that should be used as a header guard in the - named file. - - """ - - # Restores original filename in case that cpplint is invoked from Emacs's - # flymake. - filename = re.sub(r'_flymake\.h$', '.h', filename) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - # Replace 'c++' with 'cpp'. - filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RepositoryName() - if _root: - file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) - return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' - - -def CheckForHeaderGuard(filename, clean_lines, error): - """Checks that the file contains a header guard. - - Logs an error if no #ifndef header guard is present. For other - headers, checks that the full pathname is used. - - Args: - filename: The name of the C++ header file. - clean_lines: A CleansedLines instance containing the file. - error: The function to call with any errors found. - """ - - # Don't check for header guards if there are error suppression - # comments somewhere in this file. - # - # Because this is silencing a warning for a nonexistent line, we - # only support the very specific NOLINT(build/header_guard) syntax, - # and not the general NOLINT or NOLINT(*) syntax. - raw_lines = clean_lines.lines_without_raw_strings - for i in raw_lines: - if Search(r'//\s*NOLINT\(build/header_guard\)', i): - return - - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = '' - ifndef_linenum = 0 - define = '' - endif = '' - endif_linenum = 0 - for linenum, line in enumerate(raw_lines): - linesplit = line.split() - if len(linesplit) >= 2: - # find the first occurrence of #ifndef and #define, save arg - if not ifndef and linesplit[0] == '#ifndef': - # set ifndef to the header guard presented on the #ifndef line. - ifndef = linesplit[1] - ifndef_linenum = linenum - if not define and linesplit[0] == '#define': - define = linesplit[1] - # find the last occurrence of #endif, save entire line - if line.startswith('#endif'): - endif = line - endif_linenum = linenum - - if not ifndef or not define or ifndef != define: - error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) - return - - # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ - # for backward compatibility. - if ifndef != cppvar: - error_level = 0 - if ifndef != cppvar + '_': - error_level = 5 - - ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, - error) - error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) - - # Check for "//" comments on endif line. - ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, - error) - match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) - if match: - if match.group(1) == '_': - # Issue low severity warning for deprecated double trailing underscore - error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif // %s"' % cppvar) - return - - # Didn't find the corresponding "//" comment. If this file does not - # contain any "//" comments at all, it could be that the compiler - # only wants "/**/" comments, look for those instead. - no_single_line_comments = True - for i in xrange(1, len(raw_lines) - 1): - line = raw_lines[i] - if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): - no_single_line_comments = False - break - - if no_single_line_comments: - match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) - if match: - if match.group(1) == '_': - # Low severity warning for double trailing underscore - error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif /* %s */"' % cppvar) - return - - # Didn't find anything - error(filename, endif_linenum, 'build/header_guard', 5, - '#endif line should be "#endif // %s"' % cppvar) - - -def CheckHeaderFileIncluded(filename, include_state, error): - """Logs an error if a .cc file does not include its header.""" - - # Do not check test files - if filename.endswith('_test.cc') or filename.endswith('_unittest.cc'): - return - - fileinfo = FileInfo(filename) - headerfile = filename[0:len(filename) - 2] + 'h' - if not os.path.exists(headerfile): - return - headername = FileInfo(headerfile).RepositoryName() - first_include = 0 - for section_list in include_state.include_list: - for f in section_list: - if headername in f[0] or f[0] in headername: - return - if not first_include: - first_include = f[1] - - error(filename, first_include, 'build/include', 5, - '%s should include its header file %s' % (fileinfo.RepositoryName(), - headername)) - - -def CheckForBadCharacters(filename, lines, error): - """Logs an error for each line containing bad characters. - - Two kinds of bad characters: - - 1. Unicode replacement characters: These indicate that either the file - contained invalid UTF-8 (likely) or Unicode replacement characters (which - it shouldn't). Note that it's possible for this to throw off line - numbering if the invalid UTF-8 occurred adjacent to a newline. - - 2. NUL bytes. These are problematic for some tools. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - for linenum, line in enumerate(lines): - if u'\ufffd' in line: - error(filename, linenum, 'readability/utf8', 5, - 'Line contains invalid UTF-8 (or Unicode replacement character).') - if '\0' in line: - error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') - - -def CheckForNewlineAtEOF(filename, lines, error): - """Logs an error if there is no newline char at the end of the file. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - # The array lines() was created by adding two newlines to the - # original file (go figure), then splitting on \n. - # To verify that the file ends in \n, we just have to make sure the - # last-but-two element of lines() exists and is empty. - if len(lines) < 3 or lines[-2]: - error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, - 'Could not find a newline character at the end of the file.') - - -def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): - """Logs an error if we see /* ... */ or "..." that extend past one line. - - /* ... */ comments are legit inside macros, for one line. - Otherwise, we prefer // comments, so it's ok to warn about the - other. Likewise, it's ok for strings to extend across multiple - lines, as long as a line continuation character (backslash) - terminates each line. Although not currently prohibited by the C++ - style guide, it's ugly and unnecessary. We don't do well with either - in this lint program, so we warn about both. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remove all \\ (escaped backslashes) from the line. They are OK, and the - # second (escaped) slash may trigger later \" detection erroneously. - line = line.replace('\\\\', '') - - if line.count('/*') > line.count('*/'): - error(filename, linenum, 'readability/multiline_comment', 5, - 'Complex multi-line /*...*/-style comment found. ' - 'Lint may give bogus warnings. ' - 'Consider replacing these with //-style comments, ' - 'with #if 0...#endif, ' - 'or with more clearly structured multi-line comments.') - - if (line.count('"') - line.count('\\"')) % 2: - error(filename, linenum, 'readability/multiline_string', 5, - 'Multi-line string ("...") found. This lint script doesn\'t ' - 'do well with such strings, and may give bogus warnings. ' - 'Use C++11 raw strings or concatenation instead.') - - -# (non-threadsafe name, thread-safe alternative, validation pattern) -# -# The validation pattern is used to eliminate false positives such as: -# _rand(); // false positive due to substring match. -# ->rand(); // some member function rand(). -# ACMRandom rand(seed); // some variable named rand. -# ISAACRandom rand(); // another variable named rand. -# -# Basically we require the return value of these functions to be used -# in some expression context on the same line by matching on some -# operator before the function name. This eliminates constructors and -# member function calls. -_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' -_THREADING_LIST = ( - ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), - ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), - ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), - ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), - ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), - ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), - ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), - ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), - ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), - ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), - ('strtok(', 'strtok_r(', - _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), - ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), - ) - - -def CheckPosixThreading(filename, clean_lines, linenum, error): - """Checks for calls to thread-unsafe functions. - - Much code has been originally written without consideration of - multi-threading. Also, engineers are relying on their old experience; - they have learned posix before threading extensions were added. These - tests guide the engineers to use thread-safe functions (when using - posix directly). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: - # Additional pattern matching check to confirm that this is the - # function we are looking for - if Search(pattern, line): - error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_func + - '...) instead of ' + single_thread_func + - '...) for improved thread safety.') - - -def CheckVlogArguments(filename, clean_lines, linenum, error): - """Checks that VLOG() is only used for defining a logging level. - - For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and - VLOG(FATAL) are not. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): - error(filename, linenum, 'runtime/vlog', 5, - 'VLOG() should be used with numeric verbosity level. ' - 'Use LOG() if you want symbolic severity levels.') - -# Matches invalid increment: *count++, which moves pointer instead of -# incrementing a value. -_RE_PATTERN_INVALID_INCREMENT = re.compile( - r'^\s*\*\w+(\+\+|--);') - - -def CheckInvalidIncrement(filename, clean_lines, linenum, error): - """Checks for invalid increment *count++. - - For example following function: - void increment_counter(int* count) { - *count++; - } - is invalid, because it effectively does count++, moving pointer, and should - be replaced with ++*count, (*count)++ or *count += 1. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if _RE_PATTERN_INVALID_INCREMENT.match(line): - error(filename, linenum, 'runtime/invalid_increment', 5, - 'Changing pointer instead of value (or unused value of operator*).') - - -def IsMacroDefinition(clean_lines, linenum): - if Search(r'^#define', clean_lines[linenum]): - return True - - if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): - return True - - return False - - -def IsForwardClassDeclaration(clean_lines, linenum): - return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) - - -class _BlockInfo(object): - """Stores information about a generic block of code.""" - - def __init__(self, seen_open_brace): - self.seen_open_brace = seen_open_brace - self.open_parentheses = 0 - self.inline_asm = _NO_ASM - self.check_namespace_indentation = False - - def CheckBegin(self, filename, clean_lines, linenum, error): - """Run checks that applies to text up to the opening brace. - - This is mostly for checking the text after the class identifier - and the "{", usually where the base class is specified. For other - blocks, there isn't much to check, so we always pass. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Run checks that applies to text after the closing brace. - - This is mostly used for checking end of namespace comments. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def IsBlockInfo(self): - """Returns true if this block is a _BlockInfo. - - This is convenient for verifying that an object is an instance of - a _BlockInfo, but not an instance of any of the derived classes. - - Returns: - True for this class, False for derived classes. - """ - return self.__class__ == _BlockInfo - - -class _ExternCInfo(_BlockInfo): - """Stores information about an 'extern "C"' block.""" - - def __init__(self): - _BlockInfo.__init__(self, True) - - -class _ClassInfo(_BlockInfo): - """Stores information about a class.""" - - def __init__(self, name, class_or_struct, clean_lines, linenum): - _BlockInfo.__init__(self, False) - self.name = name - self.starting_linenum = linenum - self.is_derived = False - self.check_namespace_indentation = True - if class_or_struct == 'struct': - self.access = 'public' - self.is_struct = True - else: - self.access = 'private' - self.is_struct = False - - # Remember initial indentation level for this class. Using raw_lines here - # instead of elided to account for leading comments. - self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) - - # Try to find the end of the class. This will be confused by things like: - # class A { - # } *x = { ... - # - # But it's still good enough for CheckSectionSpacing. - self.last_line = 0 - depth = 0 - for i in range(linenum, clean_lines.NumLines()): - line = clean_lines.elided[i] - depth += line.count('{') - line.count('}') - if not depth: - self.last_line = i - break - - def CheckBegin(self, filename, clean_lines, linenum, error): - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): - self.is_derived = True - - def CheckEnd(self, filename, clean_lines, linenum, error): - # If there is a DISALLOW macro, it should appear near the end of - # the class. - seen_last_thing_in_class = False - for i in xrange(linenum - 1, self.starting_linenum, -1): - match = Search( - r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + - self.name + r'\)', - clean_lines.elided[i]) - if match: - if seen_last_thing_in_class: - error(filename, i, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') - break - - if not Match(r'^\s*$', clean_lines.elided[i]): - seen_last_thing_in_class = True - - # Check that closing brace is aligned with beginning of the class. - # Only do this if the closing brace is indented by only whitespaces. - # This means we will not check single-line class definitions. - indent = Match(r'^( *)\}', clean_lines.elided[linenum]) - if indent and len(indent.group(1)) != self.class_indent: - if self.is_struct: - parent = 'struct ' + self.name - else: - parent = 'class ' + self.name - error(filename, linenum, 'whitespace/indent', 3, - 'Closing brace should be aligned with beginning of %s' % parent) - - -class _NamespaceInfo(_BlockInfo): - """Stores information about a namespace.""" - - def __init__(self, name, linenum): - _BlockInfo.__init__(self, False) - self.name = name or '' - self.starting_linenum = linenum - self.check_namespace_indentation = True - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Check end of namespace comments.""" - line = clean_lines.raw_lines[linenum] - - # Check how many lines is enclosed in this namespace. Don't issue - # warning for missing namespace comments if there aren't enough - # lines. However, do apply checks if there is already an end of - # namespace comment and it's incorrect. - # - # TODO(unknown): We always want to check end of namespace comments - # if a namespace is large, but sometimes we also want to apply the - # check if a short namespace contained nontrivial things (something - # other than forward declarations). There is currently no logic on - # deciding what these nontrivial things are, so this check is - # triggered by namespace size only, which works most of the time. - if (linenum - self.starting_linenum < 10 - and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): - return - - # Look for matching comment at end of namespace. - # - # Note that we accept C style "/* */" comments for terminating - # namespaces, so that code that terminate namespaces inside - # preprocessor macros can be cpplint clean. - # - # We also accept stuff like "// end of namespace ." with the - # period at the end. - # - # Besides these, we don't accept anything else, otherwise we might - # get false negatives when existing comment is a substring of the - # expected namespace. - if self.name: - # Named namespace - if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + - r'[\*/\.\\\s]*$'), - line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) - else: - # Anonymous namespace - if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): - # If "// namespace anonymous" or "// anonymous namespace (more text)", - # mention "// anonymous namespace" as an acceptable form - if Match(r'}.*\b(namespace anonymous|anonymous namespace)\b', line): - error(filename, linenum, 'readability/namespace', 5, - 'Anonymous namespace should be terminated with "// namespace"' - ' or "// anonymous namespace"') - else: - error(filename, linenum, 'readability/namespace', 5, - 'Anonymous namespace should be terminated with "// namespace"') - - -class _PreprocessorInfo(object): - """Stores checkpoints of nesting stacks when #if/#else is seen.""" - - def __init__(self, stack_before_if): - # The entire nesting stack before #if - self.stack_before_if = stack_before_if - - # The entire nesting stack up to #else - self.stack_before_else = [] - - # Whether we have already seen #else or #elif - self.seen_else = False - - -class NestingState(object): - """Holds states related to parsing braces.""" - - def __init__(self): - # Stack for tracking all braces. An object is pushed whenever we - # see a "{", and popped when we see a "}". Only 3 types of - # objects are possible: - # - _ClassInfo: a class or struct. - # - _NamespaceInfo: a namespace. - # - _BlockInfo: some other type of block. - self.stack = [] - - # Top of the previous stack before each Update(). - # - # Because the nesting_stack is updated at the end of each line, we - # had to do some convoluted checks to find out what is the current - # scope at the beginning of the line. This check is simplified by - # saving the previous top of nesting stack. - # - # We could save the full stack, but we only need the top. Copying - # the full nesting stack would slow down cpplint by ~10%. - self.previous_stack_top = [] - - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. - - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. - """ - return (not self.stack) or self.stack[-1].seen_open_brace - - def InNamespaceBody(self): - """Check if we are currently one level inside a namespace body. - - Returns: - True if top of the stack is a namespace block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _NamespaceInfo) - - def InExternC(self): - """Check if we are currently one level inside an 'extern "C"' block. - - Returns: - True if top of the stack is an extern block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _ExternCInfo) - - def InClassDeclaration(self): - """Check if we are currently one level inside a class or struct declaration. - - Returns: - True if top of the stack is a class/struct, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _ClassInfo) - - def InAsmBlock(self): - """Check if we are currently one level inside an inline ASM block. - - Returns: - True if the top of the stack is a block containing inline ASM. - """ - return self.stack and self.stack[-1].inline_asm != _NO_ASM - - def InTemplateArgumentList(self, clean_lines, linenum, pos): - """Check if current position is inside template argument list. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: position just after the suspected template argument. - Returns: - True if (linenum, pos) is inside template arguments. - """ - while linenum < clean_lines.NumLines(): - # Find the earliest character that might indicate a template argument - line = clean_lines.elided[linenum] - match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) - if not match: - linenum += 1 - pos = 0 - continue - token = match.group(1) - pos += len(match.group(0)) - - # These things do not look like template argument list: - # class Suspect { - # class Suspect x; } - if token in ('{', '}', ';'): return False - - # These things look like template argument list: - # template - # template - # template - # template - if token in ('>', '=', '[', ']', '.'): return True - - # Check if token is an unmatched '<'. - # If not, move on to the next character. - if token != '<': - pos += 1 - if pos >= len(line): - linenum += 1 - pos = 0 - continue - - # We can't be sure if we just find a single '<', and need to - # find the matching '>'. - (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) - if end_pos < 0: - # Not sure if template argument list or syntax error in file - return False - linenum = end_line - pos = end_pos - return False - - def UpdatePreprocessor(self, line): - """Update preprocessor stack. - - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif - - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. - - - Preprocessor condition evaluates to false from #else/#elif up - to #endif. We still perform lint checks on these lines, but - these do not affect nesting stack. - - Args: - line: current line to check. - """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): - # Beginning of #if block, save the nesting stack here. The saved - # stack will allow us to restore the parsing state in the #else case. - self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): - # Beginning of #else block - if self.pp_stack: - if not self.pp_stack[-1].seen_else: - # This is the first #else or #elif block. Remember the - # whole nesting stack up to this point. This is what we - # keep after the #endif. - self.pp_stack[-1].seen_else = True - self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) - - # Restore the stack to how it was before the #if - self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) - else: - # TODO(unknown): unexpected #else, issue warning? - pass - elif Match(r'^\s*#\s*endif\b', line): - # End of #if or #else blocks. - if self.pp_stack: - # If we saw an #else, we will need to restore the nesting - # stack to its former state before the #else, otherwise we - # will just continue from where we left off. - if self.pp_stack[-1].seen_else: - # Here we can just use a shallow copy since we are the last - # reference to it. - self.stack = self.pp_stack[-1].stack_before_else - # Drop the corresponding #if - self.pp_stack.pop() - else: - # TODO(unknown): unexpected #endif, issue warning? - pass - - # TODO(unknown): Update() is too long, but we will refactor later. - def Update(self, filename, clean_lines, linenum, error): - """Update nesting state with current line. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remember top of the previous nesting stack. - # - # The stack is always pushed/popped and not modified in place, so - # we can just do a shallow copy instead of copy.deepcopy. Using - # deepcopy would slow down cpplint by ~28%. - if self.stack: - self.previous_stack_top = self.stack[-1] - else: - self.previous_stack_top = None - - # Update pp_stack - self.UpdatePreprocessor(line) - - # Count parentheses. This is to avoid adding struct arguments to - # the nesting stack. - if self.stack: - inner_block = self.stack[-1] - depth_change = line.count('(') - line.count(')') - inner_block.open_parentheses += depth_change - - # Also check if we are starting or ending an inline assembly block. - if inner_block.inline_asm in (_NO_ASM, _END_ASM): - if (depth_change != 0 and - inner_block.open_parentheses == 1 and - _MATCH_ASM.match(line)): - # Enter assembly block - inner_block.inline_asm = _INSIDE_ASM - else: - # Not entering assembly block. If previous line was _END_ASM, - # we will now shift to _NO_ASM state. - inner_block.inline_asm = _NO_ASM - elif (inner_block.inline_asm == _INSIDE_ASM and - inner_block.open_parentheses == 0): - # Exit assembly block - inner_block.inline_asm = _END_ASM - - # Consume namespace declaration at the beginning of the line. Do - # this in a loop so that we catch same line declarations like this: - # namespace proto2 { namespace bridge { class MessageSet; } } - while True: - # Match start of namespace. The "\b\s*" below catches namespace - # declarations even if it weren't followed by a whitespace, this - # is so that we don't confuse our namespace checker. The - # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) - if not namespace_decl_match: - break - - new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) - self.stack.append(new_namespace) - - line = namespace_decl_match.group(2) - if line.find('{') != -1: - new_namespace.seen_open_brace = True - line = line[line.find('{') + 1:] - - # Look for a class declaration in whatever is left of the line - # after parsing namespaces. The regexp accounts for decorated classes - # such as in: - # class LOCKABLE API Object { - # }; - class_decl_match = Match( - r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' - r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' - r'(.*)$', line) - if (class_decl_match and - (not self.stack or self.stack[-1].open_parentheses == 0)): - # We do not want to accept classes that are actually template arguments: - # template , - # template class Ignore3> - # void Function() {}; - # - # To avoid template argument cases, we scan forward and look for - # an unmatched '>'. If we see one, assume we are inside a - # template argument list. - end_declaration = len(class_decl_match.group(1)) - if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): - self.stack.append(_ClassInfo( - class_decl_match.group(3), class_decl_match.group(2), - clean_lines, linenum)) - line = class_decl_match.group(4) - - # If we have not yet seen the opening brace for the innermost block, - # run checks here. - if not self.SeenOpenBrace(): - self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) - - # Update access control if we are inside a class/struct - if self.stack and isinstance(self.stack[-1], _ClassInfo): - classinfo = self.stack[-1] - access_match = Match( - r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' - r':(?:[^:]|$)', - line) - if access_match: - classinfo.access = access_match.group(2) - - # Check that access keywords are indented +1 space. Skip this - # check if the keywords are not preceded by whitespaces. - indent = access_match.group(1) - if (len(indent) != classinfo.class_indent + 1 and - Match(r'^\s*$', indent)): - if classinfo.is_struct: - parent = 'struct ' + classinfo.name - else: - parent = 'class ' + classinfo.name - slots = '' - if access_match.group(3): - slots = access_match.group(3) - error(filename, linenum, 'whitespace/indent', 3, - '%s%s: should be indented +1 space inside %s' % ( - access_match.group(2), slots, parent)) - - # Consume braces or semicolons from what's left of the line - while True: - # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) - if not matched: - break - - token = matched.group(1) - if token == '{': - # If namespace or class hasn't seen a opening brace yet, mark - # namespace/class head as complete. Push a new block onto the - # stack otherwise. - if not self.SeenOpenBrace(): - self.stack[-1].seen_open_brace = True - elif Match(r'^extern\s*"[^"]*"\s*\{', line): - self.stack.append(_ExternCInfo()) - else: - self.stack.append(_BlockInfo(True)) - if _MATCH_ASM.match(line): - self.stack[-1].inline_asm = _BLOCK_ASM - - elif token == ';' or token == ')': - # If we haven't seen an opening brace yet, but we already saw - # a semicolon, this is probably a forward declaration. Pop - # the stack for these. - # - # Similarly, if we haven't seen an opening brace yet, but we - # already saw a closing parenthesis, then these are probably - # function arguments with extra "class" or "struct" keywords. - # Also pop these stack for these. - if not self.SeenOpenBrace(): - self.stack.pop() - else: # token == '}' - # Perform end of block checks and pop the stack. - if self.stack: - self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) - self.stack.pop() - line = matched.group(2) - - def InnermostClass(self): - """Get class info on the top of the stack. - - Returns: - A _ClassInfo object if we are inside a class, or None otherwise. - """ - for i in range(len(self.stack), 0, -1): - classinfo = self.stack[i - 1] - if isinstance(classinfo, _ClassInfo): - return classinfo - return None - - def CheckCompletedBlocks(self, filename, error): - """Checks that all classes and namespaces have been completely parsed. - - Call this when all lines in a file have been processed. - Args: - filename: The name of the current file. - error: The function to call with any errors found. - """ - # Note: This test can result in false positives if #ifdef constructs - # get in the way of brace matching. See the testBuildClass test in - # cpplint_unittest.py for an example of this. - for obj in self.stack: - if isinstance(obj, _ClassInfo): - error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) - elif isinstance(obj, _NamespaceInfo): - error(filename, obj.starting_linenum, 'build/namespaces', 5, - 'Failed to find complete declaration of namespace %s' % - obj.name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - nesting_state, error): - r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. - - Complain about several constructs which gcc-2 accepts, but which are - not standard C++. Warning about these in lint is one way to ease the - transition to new compilers. - - put storage class first (e.g. "static const" instead of "const static"). - - "%lld" instead of %qd" in printf-type functions. - - "%1$d" is non-standard in printf-type functions. - - "\%" is an undefined character escape sequence. - - text after #endif is not allowed. - - invalid inner-style forward declaration. - - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', - line): - error(filename, linenum, 'build/deprecated', 3, - '>? and ))?' - # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' - error(filename, linenum, 'runtime/member_string_references', 2, - 'const string& members are dangerous. It is much better to use ' - 'alternatives, such as pointers or simple constants.') - - # Everything else in this function operates on class declarations. - # Return early if the top of the nesting stack is not a class, or if - # the class head is not completed yet. - classinfo = nesting_state.InnermostClass() - if not classinfo or not classinfo.seen_open_brace: - return - - # The class may have been declared with namespace or classname qualifiers. - # The constructor and destructor will not have those qualifiers. - base_classname = classinfo.name.split('::')[-1] - - # Look for single-argument constructors that aren't marked explicit. - # Technically a valid construct, but against style. Also look for - # non-single-argument constructors which are also technically valid, but - # strongly suggest something is wrong. - explicit_constructor_match = Match( - r'\s+(?:inline\s+)?(explicit\s+)?(?:inline\s+)?%s\s*' - r'\(((?:[^()]|\([^()]*\))*)\)' - % re.escape(base_classname), - line) - - if explicit_constructor_match: - is_marked_explicit = explicit_constructor_match.group(1) - - if not explicit_constructor_match.group(2): - constructor_args = [] - else: - constructor_args = explicit_constructor_match.group(2).split(',') - - # collapse arguments so that commas in template parameter lists and function - # argument parameter lists don't split arguments in two - i = 0 - while i < len(constructor_args): - constructor_arg = constructor_args[i] - while (constructor_arg.count('<') > constructor_arg.count('>') or - constructor_arg.count('(') > constructor_arg.count(')')): - constructor_arg += ',' + constructor_args[i + 1] - del constructor_args[i + 1] - constructor_args[i] = constructor_arg - i += 1 - - defaulted_args = [arg for arg in constructor_args if '=' in arg] - noarg_constructor = (not constructor_args or # empty arg list - # 'void' arg specifier - (len(constructor_args) == 1 and - constructor_args[0].strip() == 'void')) - onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg - not noarg_constructor) or - # all but at most one arg defaulted - (len(constructor_args) >= 1 and - not noarg_constructor and - len(defaulted_args) >= len(constructor_args) - 1)) - initializer_list_constructor = bool( - onearg_constructor and - Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) - copy_constructor = bool( - onearg_constructor and - Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' - % re.escape(base_classname), constructor_args[0].strip())) - - if (not is_marked_explicit and - onearg_constructor and - not initializer_list_constructor and - not copy_constructor): - if defaulted_args: - error(filename, linenum, 'runtime/explicit', 5, - 'Constructors callable with one argument ' - 'should be marked explicit.') - else: - error(filename, linenum, 'runtime/explicit', 5, - 'Single-parameter constructors should be marked explicit.') - elif is_marked_explicit and not onearg_constructor: - if noarg_constructor: - error(filename, linenum, 'runtime/explicit', 5, - 'Zero-parameter constructors should not be marked explicit.') - else: - error(filename, linenum, 'runtime/explicit', 0, - 'Constructors that require multiple arguments ' - 'should not be marked explicit.') - - -def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): - """Checks for the correctness of various spacing around function calls. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - fncall = match.group(1) # look inside the parens for function calls - break - - # Except in if/for/while/switch, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', - fncall) and - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and - not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and - not Search(r'\bcase\s+\(', fncall)): - # TODO(unknown): Space after an operator function seem to be a common - # error, silence those for now by restricting them to highest verbosity. - if Search(r'\boperator_*\b', line): - error(filename, linenum, 'whitespace/parens', 0, - 'Extra space before ( in function call') - else: - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - -def IsBlankLine(line): - """Returns true if the given line is blank. - - We consider a line to be blank if the line is empty or consists of - only white spaces. - - Args: - line: A line of a string. - - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() - - -def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, - error): - is_namespace_indent_item = ( - len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and - nesting_state.previous_stack_top == nesting_state.stack[-2]) - - if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, - clean_lines.elided, line): - CheckItemIndentationInNamespace(filename, clean_lines.elided, - line, error) - - -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in xrange(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.Check(error, filename, linenum) - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - -_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') - - -def CheckComment(line, filename, linenum, next_line_start, error): - """Checks for common mistakes in comments. - - Args: - line: The line in question. - filename: The name of the current file. - linenum: The number of the line to check. - next_line_start: The first non-whitespace column of the next line. - error: The function to call with any errors found. - """ - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - # Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison - if (line.count('"', 0, commentpos) - - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes - # Allow one space for new scopes, two spaces otherwise: - if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and - ((commentpos >= 1 and - line[commentpos-1] not in string.whitespace) or - (commentpos >= 2 and - line[commentpos-2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') - - # Checks for common mistakes in TODO comments. - comment = line[commentpos:] - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 1: - error(filename, linenum, 'whitespace/todo', 2, - 'Too many spaces before TODO') - - username = match.group(2) - if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') - - middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - - # If the comment contains an alphanumeric character, there - # should be a space somewhere between it and the // unless - # it's a /// or //! Doxygen comment. - if (Match(r'//[^ ]*\w', comment) and - not Match(r'(///|//\!)(\s+|$)', comment)): - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - - -def CheckAccess(filename, clean_lines, linenum, nesting_state, error): - """Checks for improper use of DISALLOW* macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] # get rid of comments and strings - - matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' - r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) - if not matched: - return - if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): - if nesting_state.stack[-1].access != 'private': - error(filename, linenum, 'readability/constructors', 3, - '%s must be in the private: section' % matched.group(1)) - - else: - # Found DISALLOW* macro outside a class declaration, or perhaps it - # was used inside a function when it should have been part of the - # class declaration. We could issue a warning here, but it - # probably resulted in a compiler error already. - pass - - -def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for the correctness of various spacing issues in the code. - - Things we check for: spaces around operators, spaces after - if/for/while/switch, no spaces around parens in function calls, two - spaces between code and comment, don't start a block with a blank - line, don't end a function with a blank line, don't add a blank line - after public/protected/private, don't have too many blank lines in a row. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside C++11 - # raw strings, - raw = clean_lines.lines_without_raw_strings - line = raw[linenum] - - # Before nixing comments, check if the line is blank for no good - # reason. This includes the first line after a block is opened, and - # blank lines at the end of a function (ie, right before a line like '}' - # - # Skip all the blank line checks if we are immediately inside a - # namespace body. In other words, don't issue blank line warnings - # for this block: - # namespace { - # - # } - # - # A warning about missing end of namespace comments will be issued instead. - # - # Also skip blank line checks for 'extern "C"' blocks, which are formatted - # like namespaces. - if (IsBlankLine(line) and - not nesting_state.InNamespaceBody() and - not nesting_state.InExternC()): - elided = clean_lines.elided - prev_line = elided[linenum - 1] - prevbrace = prev_line.rfind('{') - # TODO(unknown): Don't complain if line before blank line, and line after, - # both start with alnums and are indented the same amount. - # This ignores whitespace at the start of a namespace block - # because those are not usually indented. - if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: - # OK, we have a blank line at the start of a code block. Before we - # complain, we check if it is an exception to the rule: The previous - # non-empty line has the parameters of a function header that are indented - # 4 spaces (because they did not fit in a 80 column line when placed on - # the same line as the function name). We also check for the case where - # the previous line is indented 6 spaces, which may happen when the - # initializers of a constructor do not fit into a 80 column line. - exception = False - if Match(r' {6}\w', prev_line): # Initializer list? - # We are looking for the opening column of initializer list, which - # should be indented 4 spaces to cause 6 space indentation afterwards. - search_position = linenum-2 - while (search_position >= 0 - and Match(r' {6}\w', elided[search_position])): - search_position -= 1 - exception = (search_position >= 0 - and elided[search_position][:5] == ' :') - else: - # Search for the function arguments or an initializer list. We use a - # simple heuristic here: If the line is indented 4 spaces; and we have a - # closing paren, without the opening paren, followed by an opening brace - # or colon (for initializer lists) we assume that it is the last line of - # a function header. If we have a colon indented 4 spaces, it is an - # initializer list. - exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', - prev_line) - or Match(r' {4}:', prev_line)) - - if not exception: - error(filename, linenum, 'whitespace/blank_line', 2, - 'Redundant blank line at the start of a code block ' - 'should be deleted.') - # Ignore blank lines at the end of a block in a long if-else - # chain, like this: - # if (condition1) { - # // Something followed by a blank line - # - # } else if (condition2) { - # // Something else - # } - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - if (next_line - and Match(r'\s*}', next_line) - and next_line.find('} else ') == -1): - error(filename, linenum, 'whitespace/blank_line', 3, - 'Redundant blank line at the end of a code block ' - 'should be deleted.') - - matched = Match(r'\s*(public|protected|private):', prev_line) - if matched: - error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) - - # Next, check comments - next_line_start = 0 - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - next_line_start = len(next_line) - len(next_line.lstrip()) - CheckComment(line, filename, linenum, next_line_start, error) - - # get rid of comments and strings - line = clean_lines.elided[linenum] - - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'return []() {};' - if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - - # In range-based for, we wanted spaces before and after the colon, but - # not around "::" tokens that might appear. - if (Search(r'for *\(.*[^:]:[^: ]', line) or - Search(r'for *\(.*[^: ]:[^:]', line)): - error(filename, linenum, 'whitespace/forcolon', 2, - 'Missing space around colon in range-based for loop') - - -def CheckOperatorSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing around operators. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Don't try to do spacing checks for operator methods. Do this by - # replacing the troublesome characters with something else, - # preserving column position for all other characters. - # - # The replacement is done repeatedly to avoid false positives from - # operators that call operators. - while True: - match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) - if match: - line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) - else: - break - - # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". - # Otherwise not. Note we only check for non-spaces on *both* sides; - # sometimes people put non-spaces on one side when aligning ='s among - # many lines (not that this is behavior that I approve of...) - if ((Search(r'[\w.]=', line) or - Search(r'=[\w.]', line)) - and not Search(r'\b(if|while|for) ', line) - # Operators taken from [lex.operators] in C++11 standard. - and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) - and not Search(r'operator=', line)): - error(filename, linenum, 'whitespace/operators', 4, - 'Missing spaces around =') - - # It's ok not to have spaces around binary operators like + - * /, but if - # there's too little whitespace, we get concerned. It's hard to tell, - # though, so we punt on this one for now. TODO. - - # You should always have whitespace around binary operators. - # - # Check <= and >= first to avoid false positives with < and >, then - # check non-include lines for spacing around < and >. - # - # If the operator is followed by a comma, assume it's be used in a - # macro context and don't do any checks. This avoids false - # positives. - # - # Note that && is not included here. Those are checked separately - # in CheckRValueReference - match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - elif not Match(r'#.*include', line): - # Look for < that is not surrounded by spaces. This is only - # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a - # space. This is done to avoid some false positives with shifts. - match = Match(r'^(.*[^\s<])<[^\s=<,]', line) - if match: - (_, _, end_pos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if end_pos <= -1: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') - - # Look for > that is not surrounded by spaces. Similar to the - # above, we only trigger if both sides are missing spaces to avoid - # false positives with shifts. - match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) - if match: - (_, _, start_pos) = ReverseCloseExpression( - clean_lines, linenum, len(match.group(1))) - if start_pos <= -1: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') - - # We allow no-spaces around << when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - # - # We also allow operators following an opening parenthesis, since - # those tend to be macros that deal with operators. - match = Search(r'(operator|[^\s(<])(?:L|UL|ULL|l|ul|ull)?<<([^\s,=<])', line) - if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and - not (match.group(1) == 'operator' and match.group(2) == ';')): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <<') - - # We allow no-spaces around >> for almost anything. This is because - # C++11 allows ">>" to close nested templates, which accounts for - # most cases when ">>" is not followed by a space. - # - # We still warn on ">>" followed by alpha character, because that is - # likely due to ">>" being used for right shifts, e.g.: - # value >> alpha - # - # When ">>" is used to close templates, the alphanumeric letter that - # follows would be part of an identifier, and there should still be - # a space separating the template type and the identifier. - # type> alpha - match = Search(r'>>[a-zA-Z_]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >>') - - # There shouldn't be space around unary operators - match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) - if match: - error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) - - -def CheckParenthesisSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing around parentheses. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # No spaces after an if, while, switch, or for - match = Search(r' (if\(|for\(|while\(|switch\()', line) - if match: - error(filename, linenum, 'whitespace/parens', 5, - 'Missing space before ( in %s' % match.group(1)) - - # For if/for/while/switch, the left and right parens should be - # consistent about how many spaces are inside the parens, and - # there should either be zero or one spaces inside the parens. - # We don't want: "if ( foo)" or "if ( foo )". - # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. - match = Search(r'\b(if|for|while|switch)\s*' - r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', - line) - if match: - if len(match.group(2)) != len(match.group(4)): - if not (match.group(3) == ';' and - len(match.group(2)) == 1 + len(match.group(4)) or - not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): - error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) - if len(match.group(2)) not in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - -def CheckCommaSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing near commas and semicolons. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - raw = clean_lines.lines_without_raw_strings - line = clean_lines.elided[linenum] - - # You should always have a space after a comma (either as fn arg or operator) - # - # This does not apply when the non-space character following the - # comma is another comma, since the only time when that happens is - # for empty macro arguments. - # - # We run this check in two passes: first pass on elided lines to - # verify that lines contain missing whitespaces, second pass on raw - # lines to confirm that those missing whitespaces are not due to - # elided comments. - if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and - Search(r',[^,\s]', raw[linenum])): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # You should always have a space after a semicolon - # except for few corner cases - # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more - # space after ; - if Search(r';[^\s};\\)/]', line): - error(filename, linenum, 'whitespace/semicolon', 3, - 'Missing space after ;') - - -def CheckBracesSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing near commas. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces. And since you should never have braces at the beginning of a line, - # this is an easy test. - match = Match(r'^(.*[^ ({>]){', line) - if match: - # Try a bit harder to check for brace initialization. This - # happens in one of the following forms: - # Constructor() : initializer_list_{} { ... } - # Constructor{}.MemberFunction() - # Type variable{}; - # FunctionCall(type{}, ...); - # LastArgument(..., type{}); - # LOG(INFO) << type{} << " ..."; - # map_of_type[{...}] = ...; - # ternary = expr ? new type{} : nullptr; - # OuterTemplate{}> - # - # We check for the character following the closing brace, and - # silence the warning if it's one of those listed above, i.e. - # "{.;,)<>]:". - # - # To account for nested initializer list, we allow any number of - # closing braces up to "{;,)<". We can't simply silence the - # warning on first sight of closing brace, because that would - # cause false negatives for things that are not initializer lists. - # Silence this: But not this: - # Outer{ if (...) { - # Inner{...} if (...){ // Missing space before { - # }; } - # - # There is a false negative with this approach if people inserted - # spurious semicolons, e.g. "if (cond){};", but we will catch the - # spurious semicolon with a separate check. - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - trailing_text = '' - if endpos > -1: - trailing_text = endline[endpos:] - for offset in xrange(endlinenum + 1, - min(endlinenum + 3, clean_lines.NumLines() - 1)): - trailing_text += clean_lines.elided[offset] - if not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') - - # Make sure '} else {' has spaces. - if Search(r'}else', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before else') - - # You shouldn't have a space before a semicolon at the end of the line. - # There's a special case for "for" since the style guide allows space before - # the semicolon there. - if Search(r':\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Semicolon defining empty statement. Use {} instead.') - elif Search(r'^\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Line contains only semicolon. If this should be an empty statement, ' - 'use {} instead.') - elif (Search(r'\s+;\s*$', line) and - not Search(r'\bfor\b', line)): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Extra space before last semicolon. If this should be an empty ' - 'statement, use {} instead.') - - -def IsDecltype(clean_lines, linenum, column): - """Check if the token ending on (linenum, column) is decltype(). - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: the number of the line to check. - column: end column of the token to check. - Returns: - True if this token is decltype() expression, False otherwise. - """ - (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) - if start_col < 0: - return False - if Search(r'\bdecltype\s*$', text[0:start_col]): - return True - return False - - -def IsTemplateParameterList(clean_lines, linenum, column): - """Check if the token ending on (linenum, column) is the end of template<>. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: the number of the line to check. - column: end column of the token to check. - Returns: - True if this token is end of a template parameter list, False otherwise. - """ - (_, startline, startpos) = ReverseCloseExpression( - clean_lines, linenum, column) - if (startpos > -1 and - Search(r'\btemplate\s*$', clean_lines.elided[startline][0:startpos])): - return True - return False - - -def IsRValueType(typenames, clean_lines, nesting_state, linenum, column): - """Check if the token ending on (linenum, column) is a type. - - Assumes that text to the right of the column is "&&" or a function - name. - - Args: - typenames: set of type names from template-argument-list. - clean_lines: A CleansedLines instance containing the file. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - linenum: the number of the line to check. - column: end column of the token to check. - Returns: - True if this token is a type, False if we are not sure. - """ - prefix = clean_lines.elided[linenum][0:column] - - # Get one word to the left. If we failed to do so, this is most - # likely not a type, since it's unlikely that the type name and "&&" - # would be split across multiple lines. - match = Match(r'^(.*)(\b\w+|[>*)&])\s*$', prefix) - if not match: - return False - - # Check text following the token. If it's "&&>" or "&&," or "&&...", it's - # most likely a rvalue reference used inside a template. - suffix = clean_lines.elided[linenum][column:] - if Match(r'&&\s*(?:[>,]|\.\.\.)', suffix): - return True - - # Check for known types and end of templates: - # int&& variable - # vector&& variable - # - # Because this function is called recursively, we also need to - # recognize pointer and reference types: - # int* Function() - # int& Function() - if (match.group(2) in typenames or - match.group(2) in ['char', 'char16_t', 'char32_t', 'wchar_t', 'bool', - 'short', 'int', 'long', 'signed', 'unsigned', - 'float', 'double', 'void', 'auto', '>', '*', '&']): - return True - - # If we see a close parenthesis, look for decltype on the other side. - # decltype would unambiguously identify a type, anything else is - # probably a parenthesized expression and not a type. - if match.group(2) == ')': - return IsDecltype( - clean_lines, linenum, len(match.group(1)) + len(match.group(2)) - 1) - - # Check for casts and cv-qualifiers. - # match.group(1) remainder - # -------------- --------- - # const_cast< type&& - # const type&& - # type const&& - if Search(r'\b(?:const_cast\s*<|static_cast\s*<|dynamic_cast\s*<|' - r'reinterpret_cast\s*<|\w+\s)\s*$', - match.group(1)): - return True - - # Look for a preceding symbol that might help differentiate the context. - # These are the cases that would be ambiguous: - # match.group(1) remainder - # -------------- --------- - # Call ( expression && - # Declaration ( type&& - # sizeof ( type&& - # if ( expression && - # while ( expression && - # for ( type&& - # for( ; expression && - # statement ; type&& - # block { type&& - # constructor { expression && - start = linenum - line = match.group(1) - match_symbol = None - while start >= 0: - # We want to skip over identifiers and commas to get to a symbol. - # Commas are skipped so that we can find the opening parenthesis - # for function parameter lists. - match_symbol = Match(r'^(.*)([^\w\s,])[\w\s,]*$', line) - if match_symbol: - break - start -= 1 - line = clean_lines.elided[start] - - if not match_symbol: - # Probably the first statement in the file is an rvalue reference - return True - - if match_symbol.group(2) == '}': - # Found closing brace, probably an indicate of this: - # block{} type&& - return True - - if match_symbol.group(2) == ';': - # Found semicolon, probably one of these: - # for(; expression && - # statement; type&& - - # Look for the previous 'for(' in the previous lines. - before_text = match_symbol.group(1) - for i in xrange(start - 1, max(start - 6, 0), -1): - before_text = clean_lines.elided[i] + before_text - if Search(r'for\s*\([^{};]*$', before_text): - # This is the condition inside a for-loop - return False - - # Did not find a for-init-statement before this semicolon, so this - # is probably a new statement and not a condition. - return True - - if match_symbol.group(2) == '{': - # Found opening brace, probably one of these: - # block{ type&& = ... ; } - # constructor{ expression && expression } - - # Look for a closing brace or a semicolon. If we see a semicolon - # first, this is probably a rvalue reference. - line = clean_lines.elided[start][0:len(match_symbol.group(1)) + 1] - end = start - depth = 1 - while True: - for ch in line: - if ch == ';': - return True - elif ch == '{': - depth += 1 - elif ch == '}': - depth -= 1 - if depth == 0: - return False - end += 1 - if end >= clean_lines.NumLines(): - break - line = clean_lines.elided[end] - # Incomplete program? - return False - - if match_symbol.group(2) == '(': - # Opening parenthesis. Need to check what's to the left of the - # parenthesis. Look back one extra line for additional context. - before_text = match_symbol.group(1) - if linenum > 1: - before_text = clean_lines.elided[linenum - 1] + before_text - before_text = match_symbol.group(1) - - # Patterns that are likely to be types: - # [](type&& - # for (type&& - # sizeof(type&& - # operator=(type&& - # - if Search(r'(?:\]|\bfor|\bsizeof|\boperator\s*\S+\s*)\s*$', before_text): - return True - - # Patterns that are likely to be expressions: - # if (expression && - # while (expression && - # : initializer(expression && - # , initializer(expression && - # ( FunctionCall(expression && - # + FunctionCall(expression && - # + (expression && - # - # The last '+' represents operators such as '+' and '-'. - if Search(r'(?:\bif|\bwhile|[-+=%^(]*>)?\s*$', - match_symbol.group(1)) - if match_func: - # Check for constructors, which don't have return types. - if Search(r'\b(?:explicit|inline)$', match_func.group(1)): - return True - implicit_constructor = Match(r'\s*(\w+)\((?:const\s+)?(\w+)', prefix) - if (implicit_constructor and - implicit_constructor.group(1) == implicit_constructor.group(2)): - return True - return IsRValueType(typenames, clean_lines, nesting_state, linenum, - len(match_func.group(1))) - - # Nothing before the function name. If this is inside a block scope, - # this is probably a function call. - return not (nesting_state.previous_stack_top and - nesting_state.previous_stack_top.IsBlockInfo()) - - if match_symbol.group(2) == '>': - # Possibly a closing bracket, check that what's on the other side - # looks like the start of a template. - return IsTemplateParameterList( - clean_lines, start, len(match_symbol.group(1))) - - # Some other symbol, usually something like "a=b&&c". This is most - # likely not a type. - return False - - -def IsDeletedOrDefault(clean_lines, linenum): - """Check if current constructor or operator is deleted or default. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if this is a deleted or default constructor. - """ - open_paren = clean_lines.elided[linenum].find('(') - if open_paren < 0: - return False - (close_line, _, close_paren) = CloseExpression( - clean_lines, linenum, open_paren) - if close_paren < 0: - return False - return Match(r'\s*=\s*(?:delete|default)\b', close_line[close_paren:]) - - -def IsRValueAllowed(clean_lines, linenum, typenames): - """Check if RValue reference is allowed on a particular line. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - typenames: set of type names from template-argument-list. - Returns: - True if line is within the region where RValue references are allowed. - """ - # Allow region marked by PUSH/POP macros - for i in xrange(linenum, 0, -1): - line = clean_lines.elided[i] - if Match(r'GOOGLE_ALLOW_RVALUE_REFERENCES_(?:PUSH|POP)', line): - if not line.endswith('PUSH'): - return False - for j in xrange(linenum, clean_lines.NumLines(), 1): - line = clean_lines.elided[j] - if Match(r'GOOGLE_ALLOW_RVALUE_REFERENCES_(?:PUSH|POP)', line): - return line.endswith('POP') - - # Allow operator= - line = clean_lines.elided[linenum] - if Search(r'\boperator\s*=\s*\(', line): - return IsDeletedOrDefault(clean_lines, linenum) - - # Allow constructors - match = Match(r'\s*(?:[\w<>]+::)*([\w<>]+)\s*::\s*([\w<>]+)\s*\(', line) - if match and match.group(1) == match.group(2): - return IsDeletedOrDefault(clean_lines, linenum) - if Search(r'\b(?:explicit|inline)\s+[\w<>]+\s*\(', line): - return IsDeletedOrDefault(clean_lines, linenum) - - if Match(r'\s*[\w<>]+\s*\(', line): - previous_line = 'ReturnType' - if linenum > 0: - previous_line = clean_lines.elided[linenum - 1] - if Match(r'^\s*$', previous_line) or Search(r'[{}:;]\s*$', previous_line): - return IsDeletedOrDefault(clean_lines, linenum) - - # Reject types not mentioned in template-argument-list - while line: - match = Match(r'^.*?(\w+)\s*&&(.*)$', line) - if not match: - break - if match.group(1) not in typenames: - return False - line = match.group(2) - - # All RValue types that were in template-argument-list should have - # been removed by now. Those were allowed, assuming that they will - # be forwarded. - # - # If there are no remaining RValue types left (i.e. types that were - # not found in template-argument-list), flag those as not allowed. - return line.find('&&') < 0 - - -def GetTemplateArgs(clean_lines, linenum): - """Find list of template arguments associated with this function declaration. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Line number containing the start of the function declaration, - usually one line after the end of the template-argument-list. - Returns: - Set of type names, or empty set if this does not appear to have - any template parameters. - """ - # Find start of function - func_line = linenum - while func_line > 0: - line = clean_lines.elided[func_line] - if Match(r'^\s*$', line): - return set() - if line.find('(') >= 0: - break - func_line -= 1 - if func_line == 0: - return set() - - # Collapse template-argument-list into a single string - argument_list = '' - match = Match(r'^(\s*template\s*)<', clean_lines.elided[func_line]) - if match: - # template-argument-list on the same line as function name - start_col = len(match.group(1)) - _, end_line, end_col = CloseExpression(clean_lines, func_line, start_col) - if end_col > -1 and end_line == func_line: - start_col += 1 # Skip the opening bracket - argument_list = clean_lines.elided[func_line][start_col:end_col] - - elif func_line > 1: - # template-argument-list one line before function name - match = Match(r'^(.*)>\s*$', clean_lines.elided[func_line - 1]) - if match: - end_col = len(match.group(1)) - _, start_line, start_col = ReverseCloseExpression( - clean_lines, func_line - 1, end_col) - if start_col > -1: - start_col += 1 # Skip the opening bracket - while start_line < func_line - 1: - argument_list += clean_lines.elided[start_line][start_col:] - start_col = 0 - start_line += 1 - argument_list += clean_lines.elided[func_line - 1][start_col:end_col] - - if not argument_list: - return set() - - # Extract type names - typenames = set() - while True: - match = Match(r'^[,\s]*(?:typename|class)(?:\.\.\.)?\s+(\w+)(.*)$', - argument_list) - if not match: - break - typenames.add(match.group(1)) - argument_list = match.group(2) - return typenames - - -def CheckRValueReference(filename, clean_lines, linenum, nesting_state, error): - """Check for rvalue references. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - # Find lines missing spaces around &&. - # TODO(unknown): currently we don't check for rvalue references - # with spaces surrounding the && to avoid false positives with - # boolean expressions. - line = clean_lines.elided[linenum] - match = Match(r'^(.*\S)&&', line) - if not match: - match = Match(r'(.*)&&\S', line) - if (not match) or '(&&)' in line or Search(r'\boperator\s*$', match.group(1)): - return - - # Either poorly formed && or an rvalue reference, check the context - # to get a more accurate error message. Mostly we want to determine - # if what's to the left of "&&" is a type or not. - typenames = GetTemplateArgs(clean_lines, linenum) - and_pos = len(match.group(1)) - if IsRValueType(typenames, clean_lines, nesting_state, linenum, and_pos): - if not IsRValueAllowed(clean_lines, linenum, typenames): - error(filename, linenum, 'build/c++11', 3, - 'RValue references are an unapproved C++ feature.') - else: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around &&') - - -def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): - """Checks for additional blank line issues related to sections. - - Currently the only thing checked here is blank line before protected/private. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - class_info: A _ClassInfo objects. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Skip checks if the class is small, where small means 25 lines or less. - # 25 lines seems like a good cutoff since that's the usual height of - # terminals, and any class that can't fit in one screen can't really - # be considered "small". - # - # Also skip checks if we are on the first line. This accounts for - # classes that look like - # class Foo { public: ... }; - # - # If we didn't find the end of the class, last_line would be zero, - # and the check will be skipped by the first condition. - if (class_info.last_line - class_info.starting_linenum <= 24 or - linenum <= class_info.starting_linenum): - return - - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) - if matched: - # Issue warning if the line before public/protected/private was - # not a blank line, but don't do this if the previous line contains - # "class" or "struct". This can happen two ways: - # - We are at the beginning of the class. - # - We are forward-declaring an inner class that is semantically - # private, but needed to be public for implementation reasons. - # Also ignores cases where the previous line ends with a backslash as can be - # common when defining classes in C macros. - prev_line = clean_lines.lines[linenum - 1] - if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): - # Try a bit harder to find the beginning of the class. This is to - # account for multi-line base-specifier lists, e.g.: - # class Derived - # : public Base { - end_class_head = class_info.starting_linenum - for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): - end_class_head = i - break - if end_class_head < linenum - 1: - error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) - - -def GetPreviousNonBlankLine(clean_lines, linenum): - """Return the most recent non-blank line and its line number. - - Args: - clean_lines: A CleansedLines instance containing the file contents. - linenum: The number of the line to check. - - Returns: - A tuple with two elements. The first element is the contents of the last - non-blank line before the current line, or the empty string if this is the - first non-blank line. The second is the line number of that line, or -1 - if this is the first non-blank line. - """ - - prevlinenum = linenum - 1 - while prevlinenum >= 0: - prevline = clean_lines.elided[prevlinenum] - if not IsBlankLine(prevline): # if not a blank line... - return (prevline, prevlinenum) - prevlinenum -= 1 - return ('', -1) - - -def CheckBraces(filename, clean_lines, linenum, error): - """Looks for misplaced braces (e.g. at the end of line). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] # get rid of comments and strings - - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone is using - # braces in a block to explicitly create a new scope, which is commonly used - # to control the lifetime of stack-allocated variables. Braces are also - # used for brace initializers inside function calls. We don't detect this - # perfectly: we just don't complain if the last non-whitespace character on - # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the - # previous line starts a preprocessor block. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[,;:}{(]\s*$', prevline) and - not Match(r'\s*#', prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end of the previous line') - - # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\b\s*(?:if\b|\{|$)', line): - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): - error(filename, linenum, 'whitespace/newline', 4, - 'An else should appear on the same line as the preceding }') - - # If braces come on one side of an else, they should be on both. - # However, we have to worry about "else if" that spans multiple lines! - if Search(r'else if\s*\(', line): # could be multi-line if - brace_on_left = bool(Search(r'}\s*else if\s*\(', line)) - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - brace_on_right = endline[endpos:].find('{') != -1 - if brace_on_left != brace_on_right: # must be brace after if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - - # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): - error(filename, linenum, 'whitespace/newline', 4, - 'Else clause should never be on same line as else (use 2 lines)') - - # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): - error(filename, linenum, 'whitespace/newline', 4, - 'do/while clauses should not be on a single line') - - # Check single-line if/else bodies. The style guide says 'curly braces are not - # required for single-line statements'. We additionally allow multi-line, - # single statements, but we reject anything with more than one semicolon in - # it. This means that the first semicolon after the if should be at the end of - # its line, and the line after that should have an indent level equal to or - # lower than the if. We also check for ambiguous if/else nesting without - # braces. - if_else_match = Search(r'\b(if\s*\(|else\b)', line) - if if_else_match and not Match(r'\s*#', line): - if_indent = GetIndentLevel(line) - endline, endlinenum, endpos = line, linenum, if_else_match.end() - if_match = Search(r'\bif\s*\(', line) - if if_match: - # This could be a multiline if condition, so find the end first. - pos = if_match.end() - 1 - (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) - # Check for an opening brace, either directly after the if or on the next - # line. If found, this isn't a single-statement conditional. - if (not Match(r'\s*{', endline[endpos:]) - and not (Match(r'\s*$', endline[endpos:]) - and endlinenum < (len(clean_lines.elided) - 1) - and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): - while (endlinenum < len(clean_lines.elided) - and ';' not in clean_lines.elided[endlinenum][endpos:]): - endlinenum += 1 - endpos = 0 - if endlinenum < len(clean_lines.elided): - endline = clean_lines.elided[endlinenum] - # We allow a mix of whitespace and closing braces (e.g. for one-liner - # methods) and a single \ after the semicolon (for macros) - endpos = endline.find(';') - if not Match(r';[\s}]*(\\?)$', endline[endpos:]): - # Semicolon isn't the last character, there's something trailing. - # Output a warning if the semicolon is not contained inside - # a lambda expression. - if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', - endline): - error(filename, linenum, 'readability/braces', 4, - 'If/else bodies with multiple statements require braces') - elif endlinenum < len(clean_lines.elided) - 1: - # Make sure the next line is dedented - next_line = clean_lines.elided[endlinenum + 1] - next_indent = GetIndentLevel(next_line) - # With ambiguous nested if statements, this will error out on the - # if that *doesn't* match the else, regardless of whether it's the - # inner one or outer one. - if (if_match and Match(r'\s*else\b', next_line) - and next_indent != if_indent): - error(filename, linenum, 'readability/braces', 4, - 'Else clause should be indented at the same level as if. ' - 'Ambiguous nested if/else chains require braces.') - elif next_indent > if_indent: - error(filename, linenum, 'readability/braces', 4, - 'If/else bodies with multiple statements require braces') - - -def CheckTrailingSemicolon(filename, clean_lines, linenum, error): - """Looks for redundant trailing semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] - - # Block bodies should not be followed by a semicolon. Due to C++11 - # brace initialization, there are more places where semicolons are - # required than not, so we use a whitelist approach to check these - # rather than a blacklist. These are the places where "};" should - # be replaced by just "}": - # 1. Some flavor of block following closing parenthesis: - # for (;;) {}; - # while (...) {}; - # switch (...) {}; - # Function(...) {}; - # if (...) {}; - # if (...) else if (...) {}; - # - # 2. else block: - # if (...) else {}; - # - # 3. const member function: - # Function(...) const {}; - # - # 4. Block following some statement: - # x = 42; - # {}; - # - # 5. Block at the beginning of a function: - # Function(...) { - # {}; - # } - # - # Note that naively checking for the preceding "{" will also match - # braces inside multi-dimensional arrays, but this is fine since - # that expression will not contain semicolons. - # - # 6. Block following another block: - # while (true) {} - # {}; - # - # 7. End of namespaces: - # namespace {}; - # - # These semicolons seems far more common than other kinds of - # redundant semicolons, possibly due to people converting classes - # to namespaces. For now we do not warn for this case. - # - # Try matching case 1 first. - match = Match(r'^(.*\)\s*)\{', line) - if match: - # Matched closing parenthesis (case 1). Check the token before the - # matching opening parenthesis, and don't warn if it looks like a - # macro. This avoids these false positives: - # - macro that defines a base class - # - multi-line macro that defines a base class - # - macro that defines the whole class-head - # - # But we still issue warnings for macros that we know are safe to - # warn, specifically: - # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P - # - TYPED_TEST - # - INTERFACE_DEF - # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: - # - # We implement a whitelist of safe macros instead of a blacklist of - # unsafe macros, even though the latter appears less frequently in - # google code and would have been easier to implement. This is because - # the downside for getting the whitelist wrong means some extra - # semicolons, while the downside for getting the blacklist wrong - # would result in compile errors. - # - # In addition to macros, we also don't want to warn on - # - Compound literals - # - Lambdas - # - alignas specifier with anonymous structs: - closing_brace_pos = match.group(1).rfind(')') - opening_parenthesis = ReverseCloseExpression( - clean_lines, linenum, closing_brace_pos) - if opening_parenthesis[2] > -1: - line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] - macro = Search(r'\b([A-Z_]+)\s*$', line_prefix) - func = Match(r'^(.*\])\s*$', line_prefix) - if ((macro and - macro.group(1) not in ( - 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', - 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', - 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or - (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or - Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or - Search(r'\s+=\s*$', line_prefix)): - match = None - if (match and - opening_parenthesis[1] > 1 and - Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): - # Multi-line lambda-expression - match = None - - else: - # Try matching cases 2-3. - match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) - if not match: - # Try matching cases 4-6. These are always matched on separate lines. - # - # Note that we can't simply concatenate the previous line to the - # current line and do a single match, otherwise we may output - # duplicate warnings for the blank line case: - # if (cond) { - # // blank line - # } - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if prevline and Search(r'[;{}]\s*$', prevline): - match = Match(r'^(\s*)\{', line) - - # Check matching closing brace - if match: - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if endpos > -1 and Match(r'^\s*;', endline[endpos:]): - # Current {} pair is eligible for semicolon check, and we have found - # the redundant semicolon, output warning here. - # - # Note: because we are scanning forward for opening braces, and - # outputting warnings for the matching closing brace, if there are - # nested blocks with trailing semicolons, we will get the error - # messages in reversed order. - error(filename, endlinenum, 'readability/braces', 4, - "You don't need a ; after a }") - - -def CheckEmptyBlockBody(filename, clean_lines, linenum, error): - """Look for empty loop/conditional body with only a single semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Search for loop keywords at the beginning of the line. Because only - # whitespaces are allowed before the keywords, this will also ignore most - # do-while-loops, since those lines should start with closing brace. - # - # We also check "if" blocks here, since an empty conditional block - # is likely an error. - line = clean_lines.elided[linenum] - matched = Match(r'\s*(for|while|if)\s*\(', line) - if matched: - # Find the end of the conditional expression - (end_line, end_linenum, end_pos) = CloseExpression( - clean_lines, linenum, line.find('(')) - - # Output warning if what follows the condition expression is a semicolon. - # No warning for all other cases, including whitespace or newline, since we - # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): - if matched.group(1) == 'if': - error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, - 'Empty conditional bodies should use {}') - else: - error(filename, end_linenum, 'whitespace/empty_loop_body', 5, - 'Empty loop bodies should use {} or continue') - - -def FindCheckMacro(line): - """Find a replaceable CHECK-like macro. - - Args: - line: line to search on. - Returns: - (macro name, start position), or (None, -1) if no replaceable - macro is found. - """ - for macro in _CHECK_MACROS: - i = line.find(macro) - if i >= 0: - # Find opening parenthesis. Do a regular expression match here - # to make sure that we are matching the expected CHECK macro, as - # opposed to some other macro that happens to contain the CHECK - # substring. - matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) - if not matched: - continue - return (macro, len(matched.group(1))) - return (None, -1) - - -def CheckCheck(filename, clean_lines, linenum, error): - """Checks the use of CHECK and EXPECT macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Decide the set of replacement macros that should be suggested - lines = clean_lines.elided - (check_macro, start_pos) = FindCheckMacro(lines[linenum]) - if not check_macro: - return - - # Find end of the boolean expression by matching parentheses - (last_line, end_line, end_pos) = CloseExpression( - clean_lines, linenum, start_pos) - if end_pos < 0: - return - - # If the check macro is followed by something other than a - # semicolon, assume users will log their own custom error messages - # and don't suggest any replacements. - if not Match(r'\s*;', last_line[end_pos:]): - return - - if linenum == end_line: - expression = lines[linenum][start_pos + 1:end_pos - 1] - else: - expression = lines[linenum][start_pos + 1:] - for i in xrange(linenum + 1, end_line): - expression += lines[i] - expression += last_line[0:end_pos - 1] - - # Parse expression so that we can take parentheses into account. - # This avoids false positives for inputs like "CHECK((a < 4) == b)", - # which is not replaceable by CHECK_LE. - lhs = '' - rhs = '' - operator = None - while expression: - matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' - r'==|!=|>=|>|<=|<|\()(.*)$', expression) - if matched: - token = matched.group(1) - if token == '(': - # Parenthesized operand - expression = matched.group(2) - (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) - if end < 0: - return # Unmatched parenthesis - lhs += '(' + expression[0:end] - expression = expression[end:] - elif token in ('&&', '||'): - # Logical and/or operators. This means the expression - # contains more than one term, for example: - # CHECK(42 < a && a < b); - # - # These are not replaceable with CHECK_LE, so bail out early. - return - elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): - # Non-relational operator - lhs += token - expression = matched.group(2) - else: - # Relational operator - operator = token - rhs = matched.group(2) - break - else: - # Unparenthesized operand. Instead of appending to lhs one character - # at a time, we do another regular expression match to consume several - # characters at once if possible. Trivial benchmark shows that this - # is more efficient when the operands are longer than a single - # character, which is generally the case. - matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) - if not matched: - matched = Match(r'^(\s*\S)(.*)$', expression) - if not matched: - break - lhs += matched.group(1) - expression = matched.group(2) - - # Only apply checks if we got all parts of the boolean expression - if not (lhs and operator and rhs): - return - - # Check that rhs do not contain logical operators. We already know - # that lhs is fine since the loop above parses out && and ||. - if rhs.find('&&') > -1 or rhs.find('||') > -1: - return - - # At least one of the operands must be a constant literal. This is - # to avoid suggesting replacements for unprintable things like - # CHECK(variable != iterator) - # - # The following pattern matches decimal, hex integers, strings, and - # characters (in that order). - lhs = lhs.strip() - rhs = rhs.strip() - match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' - if Match(match_constant, lhs) or Match(match_constant, rhs): - # Note: since we know both lhs and rhs, we can provide a more - # descriptive error message like: - # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) - # Instead of: - # Consider using CHECK_EQ instead of CHECK(a == b) - # - # We are still keeping the less descriptive message because if lhs - # or rhs gets long, the error message might become unreadable. - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[check_macro][operator], - check_macro, operator)) - - -def CheckAltTokens(filename, clean_lines, linenum, error): - """Check alternative keywords being used in boolean expressions. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Avoid preprocessor lines - if Match(r'^\s*#', line): - return - - # Last ditch effort to avoid multi-line comments. This will not help - # if the comment started before the current line or ended after the - # current line, but it catches most of the false positives. At least, - # it provides a way to workaround this warning for people who use - # multi-line comments in preprocessor macros. - # - # TODO(unknown): remove this once cpplint has better support for - # multi-line comments. - if line.find('/*') >= 0 or line.find('*/') >= 0: - return - - for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): - error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) - - -def GetLineWidth(line): - """Determines the width of the line in column positions. - - Args: - line: A string, which may be a Unicode string. - - Returns: - The width of the line in column positions, accounting for Unicode - combining characters and wide characters. - """ - if isinstance(line, unicode): - width = 0 - for uc in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(uc) in ('W', 'F'): - width += 2 - elif not unicodedata.combining(uc): - width += 1 - return width - else: - return len(line) - - -def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, - error): - """Checks rules from the 'C++ style rules' section of cppguide.html. - - Most of these rules are hard to test (naming, comment style), but we - do what we can. In particular we check for 2-space indents, line lengths, - tab usage, spaces inside code, etc. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside C++11 - # raw strings, - raw_lines = clean_lines.lines_without_raw_strings - line = raw_lines[linenum] - - if line.find('\t') != -1: - error(filename, linenum, 'whitespace/tab', 1, - 'Tab found; better to use spaces') - - # One or three blank spaces at the beginning of the line is weird; it's - # hard to reconcile that with 2-space indents. - # NOTE: here are the conditions rob pike used for his tests. Mine aren't - # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces - # if(RLENGTH > 20) complain = 0; - # if(match($0, " +(error|private|public|protected):")) complain = 0; - # if(match(prev, "&& *$")) complain = 0; - # if(match(prev, "\\|\\| *$")) complain = 0; - # if(match(prev, "[\",=><] *$")) complain = 0; - # if(match($0, " <<")) complain = 0; - # if(match(prev, " +for \\(")) complain = 0; - # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' - classinfo = nesting_state.InnermostClass() - initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - # There are certain situations we allow one space, notably for - # section labels, and also lines containing multi-line raw strings. - elif ((initial_spaces == 1 or initial_spaces == 3) and - not Match(scope_or_label_pattern, cleansed_line) and - not (clean_lines.raw_lines[linenum] != line and - Match(r'^\s*""', line))): - error(filename, linenum, 'whitespace/indent', 3, - 'Weird number of spaces at line-start. ' - 'Are you using a 2-space indent?') - - # Check if the line is a header guard. - is_header_guard = False - if file_extension == 'h': - cppvar = GetHeaderGuardCPPVariable(filename) - if (line.startswith('#ifndef %s' % cppvar) or - line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar)): - is_header_guard = True - # #include lines and header guards can be long, since there's no clean way to - # split them. - # - # URLs can be long too. It's possible to split these, but it makes them - # harder to cut&paste. - # - # The "$Id:...$" comment may also get very long without it being the - # developers fault. - if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): - line_width = GetLineWidth(line) - extended_length = int((_line_length * 1.25)) - if line_width > extended_length: - error(filename, linenum, 'whitespace/line_length', 4, - 'Lines should very rarely be longer than %i characters' % - extended_length) - elif line_width > _line_length: - error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= %i characters long' % _line_length) - - if (cleansed_line.count(';') > 1 and - # for loops are allowed two ;'s (and may run over two lines). - cleansed_line.find('for') == -1 and - (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or - GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and - # It's ok to have many commands in a switch case that fits in 1 line - not ((cleansed_line.find('case ') != -1 or - cleansed_line.find('default:') != -1) and - cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 0, - 'More than one command on the same line') - - # Some more style checks - CheckBraces(filename, clean_lines, linenum, error) - CheckTrailingSemicolon(filename, clean_lines, linenum, error) - CheckEmptyBlockBody(filename, clean_lines, linenum, error) - CheckAccess(filename, clean_lines, linenum, nesting_state, error) - CheckSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckOperatorSpacing(filename, clean_lines, linenum, error) - CheckParenthesisSpacing(filename, clean_lines, linenum, error) - CheckCommaSpacing(filename, clean_lines, linenum, error) - CheckBracesSpacing(filename, clean_lines, linenum, error) - CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) - CheckRValueReference(filename, clean_lines, linenum, nesting_state, error) - CheckCheck(filename, clean_lines, linenum, error) - CheckAltTokens(filename, clean_lines, linenum, error) - classinfo = nesting_state.InnermostClass() - if classinfo: - CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) - - -_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') -# Matches the first component of a filename delimited by -s and _s. That is: -# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' -_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') - - -def _DropCommonSuffixes(filename): - """Drops common suffixes like _test.cc or -inl.h from filename. - - For example: - >>> _DropCommonSuffixes('foo/foo-inl.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/bar/foo.cc') - 'foo/bar/foo' - >>> _DropCommonSuffixes('foo/foo_internal.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') - 'foo/foo_unusualinternal' - - Args: - filename: The input filename. - - Returns: - The filename with the common suffix removed. - """ - for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', - 'inl.h', 'impl.h', 'internal.h'): - if (filename.endswith(suffix) and len(filename) > len(suffix) and - filename[-len(suffix) - 1] in ('-', '_')): - return filename[:-len(suffix) - 1] - return os.path.splitext(filename)[0] - - -def _IsTestFilename(filename): - """Determines if the given filename has a suffix that identifies it as a test. - - Args: - filename: The input filename. - - Returns: - True if 'filename' looks like a test, False otherwise. - """ - if (filename.endswith('_test.cc') or - filename.endswith('_unittest.cc') or - filename.endswith('_regtest.cc')): - return True - else: - return False - - -def _ClassifyInclude(fileinfo, include, is_system): - """Figures out what kind of header 'include' is. - - Args: - fileinfo: The current file cpplint is running over. A FileInfo instance. - include: The path to a #included file. - is_system: True if the #include used <> rather than "". - - Returns: - One of the _XXX_HEADER constants. - - For example: - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) - _C_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) - _CPP_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) - _LIKELY_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), - ... 'bar/foo_other_ext.h', False) - _POSSIBLE_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) - _OTHER_HEADER - """ - # This is a list of all standard c++ header files, except - # those already checked for above. - is_cpp_h = include in _CPP_HEADERS - - if is_system: - if is_cpp_h: - return _CPP_SYS_HEADER - else: - return _C_SYS_HEADER - - # If the target file and the include we're checking share a - # basename when we drop common extensions, and the include - # lives in . , then it's likely to be owned by the target file. - target_dir, target_base = ( - os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) - include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) - if target_base == include_base and ( - include_dir == target_dir or - include_dir == os.path.normpath(target_dir + '/../public')): - return _LIKELY_MY_HEADER - - # If the target and include share some initial basename - # component, it's possible the target is implementing the - # include, so it's allowed to be first, but we'll never - # complain if it's not there. - target_first_component = _RE_FIRST_COMPONENT.match(target_base) - include_first_component = _RE_FIRST_COMPONENT.match(include_base) - if (target_first_component and include_first_component and - target_first_component.group(0) == - include_first_component.group(0)): - return _POSSIBLE_MY_HEADER - - return _OTHER_HEADER - - - -def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): - """Check rules that are applicable to #include lines. - - Strings on #include lines are NOT removed from elided line, to make - certain tasks easier. However, to prevent false positives, checks - applicable to #include lines in CheckLanguage must be put here. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - fileinfo = FileInfo(filename) - line = clean_lines.lines[linenum] - - # "include" should use the new style "foo/bar.h" instead of just "bar.h" - # Only do this check if the included header follows google naming - # conventions. If not, assume that it's a 3rd party API that - # requires special include conventions. - # - # We also make an exception for Lua headers, which follow google - # naming convention but not the include convention. - match = Match(r'#include\s*"([^/]+\.h)"', line) - if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): - error(filename, linenum, 'build/include', 4, - 'Include the directory when naming .h files') - - # we shouldn't include a file more than once. actually, there are a - # handful of instances where doing so is okay, but in general it's - # not. - match = _RE_PATTERN_INCLUDE.search(line) - if match: - include = match.group(2) - is_system = (match.group(1) == '<') - duplicate_line = include_state.FindHeader(include) - if duplicate_line >= 0: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, duplicate_line)) - elif (include.endswith('.cc') and - os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): - error(filename, linenum, 'build/include', 4, - 'Do not include .cc files from other packages') - elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): - include_state.include_list[-1].append((include, linenum)) - - # We want to ensure that headers appear in the right order: - # 1) for foo.cc, foo.h (preferred location) - # 2) c system files - # 3) cpp system files - # 4) for foo.cc, foo.h (deprecated location) - # 5) other google headers - # - # We classify each include statement as one of those 5 types - # using a number of techniques. The include_state object keeps - # track of the highest type seen, and complains if we see a - # lower type after that. - error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) - if error_message: - error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) - canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) - if not include_state.IsInAlphabeticalOrder( - clean_lines, linenum, canonical_include): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - include_state.SetLastHeader(canonical_include) - - - -def _GetTextInside(text, start_pattern): - r"""Retrieves all the text between matching open and close parentheses. - - Given a string of lines and a regular expression string, retrieve all the text - following the expression and between opening punctuation symbols like - (, [, or {, and the matching close-punctuation symbol. This properly nested - occurrences of the punctuations, so for the text like - printf(a(), b(c())); - a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. - start_pattern must match string having an open punctuation symbol at the end. - - Args: - text: The lines to extract text. Its comments and strings must be elided. - It can be single line and can span multiple lines. - start_pattern: The regexp string indicating where to start extracting - the text. - Returns: - The extracted text. - None if either the opening string or ending punctuation could not be found. - """ - # TODO(unknown): Audit cpplint.py to see what places could be profitably - # rewritten to use _GetTextInside (and use inferior regexp matching today). - - # Give opening punctuations to get the matching close-punctuations. - matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.itervalues()) - - # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) - if not match: # start_pattern not found in text. - return None - start_position = match.end(0) - - assert start_position > 0, ( - 'start_pattern must ends with an opening punctuation.') - assert text[start_position - 1] in matching_punctuation, ( - 'start_pattern must ends with an opening punctuation.') - # Stack of closing punctuations we expect to have in text after position. - punctuation_stack = [matching_punctuation[text[start_position - 1]]] - position = start_position - while punctuation_stack and position < len(text): - if text[position] == punctuation_stack[-1]: - punctuation_stack.pop() - elif text[position] in closing_punctuation: - # A closing punctuation without matching opening punctuations. - return None - elif text[position] in matching_punctuation: - punctuation_stack.append(matching_punctuation[text[position]]) - position += 1 - if punctuation_stack: - # Opening punctuations left without matching close-punctuations. - return None - # punctuations match. - return text[start_position:position - 1] - - -# Patterns for matching call-by-reference parameters. -# -# Supports nested templates up to 2 levels deep using this messy pattern: -# < (?: < (?: < [^<>]* -# > -# | [^<>] )* -# > -# | [^<>] )* -# > -_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* -_RE_PATTERN_TYPE = ( - r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' - r'(?:\w|' - r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' - r'::)+') -# A call-by-reference parameter ends with '& identifier'. -_RE_PATTERN_REF_PARAM = re.compile( - r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' - r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') -# A call-by-const-reference parameter either ends with 'const& identifier' -# or looks like 'const type& identifier' when 'type' is atomic. -_RE_PATTERN_CONST_REF_PARAM = ( - r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + - r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') - - -def CheckLanguage(filename, clean_lines, linenum, file_extension, - include_state, nesting_state, error): - """Checks rules from the 'C++ language rules' section of cppguide.html. - - Some of these rules are hard to test (function overloading, using - uint32 inappropriately), but we do the best we can. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - include_state: An _IncludeState instance in which the headers are inserted. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - # If the line is empty or consists of entirely a comment, no need to - # check it. - line = clean_lines.elided[linenum] - if not line: - return - - match = _RE_PATTERN_INCLUDE.search(line) - if match: - CheckIncludeLine(filename, clean_lines, linenum, include_state, error) - return - - # Reset include state across preprocessor directives. This is meant - # to silence warnings for conditional includes. - match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) - if match: - include_state.ResetSection(match.group(1)) - - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') - - # Perform other checks now that we are sure that this is not an include line - CheckCasts(filename, clean_lines, linenum, error) - CheckGlobalStatic(filename, clean_lines, linenum, error) - CheckPrintf(filename, clean_lines, linenum, error) - - if file_extension == 'h': - # TODO(unknown): check that 1-arg constructors are explicit. - # How to tell it's a constructor? - # (handled in CheckForNonStandardConstructs for now) - # TODO(unknown): check that classes declare or disable copy/assign - # (level 1 error) - pass - - # Check if people are using the verboten C basic types. The only exception - # we regularly allow is "unsigned short port" for port. - if Search(r'\bshort port\b', line): - if not Search(r'\bunsigned short port\b', line): - error(filename, linenum, 'runtime/int', 4, - 'Use "unsigned short" for ports, not "short"') - else: - match = Search(r'\b(short|long(?! +double)|long long)\b', line) - if match: - error(filename, linenum, 'runtime/int', 4, - 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) - - # Check if some verboten operator overloading is going on - # TODO(unknown): catch out-of-line unary operator&: - # class X {}; - # int operator&(const X& x) { return 42; } // unary operator& - # The trick is it's hard to tell apart from binary operator&: - # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): - error(filename, linenum, 'runtime/operator', 4, - 'Unary operator& is dangerous. Do not use it.') - - # Check for suspicious usage of "if" like - # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') - - # Check for potential format string bugs like printf(foo). - # We constrain the pattern not to pick things like DocidForPrintf(foo). - # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) - # TODO(unknown): Catch the following case. Need to change the calling - # convention of the whole function to process multiple line to handle it. - # printf( - # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); - printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') - if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) - if match and match.group(1) != '__VA_ARGS__': - function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) - - # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): - error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) - - if Search(r'\busing namespace\b', line): - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') - - # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) - if (match and match.group(2) != 'return' and match.group(2) != 'delete' and - match.group(3).find(']') == -1): - # Split the size using space and arithmetic operators as delimiters. - # If any of the resulting tokens are not compile time constants then - # report the error. - tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) - is_const = True - skip_next = False - for tok in tokens: - if skip_next: - skip_next = False - continue - - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue - - tok = tok.lstrip('(') - tok = tok.rstrip(')') - if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue - # A catch all for tricky sizeof cases, including 'sizeof expression', - # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' - # requires skipping the next token because we split on ' ' and '*'. - if tok.startswith('sizeof'): - skip_next = True - continue - is_const = False - break - if not is_const: - error(filename, linenum, 'runtime/arrays', 1, - 'Do not use variable-length arrays. Use an appropriately named ' - "('k' followed by CamelCase) compile-time constant for the size.") - - # Check for use of unnamed namespaces in header files. Registration - # macros are typically OK, so we allow use of "namespace {" on lines - # that end with backslashes. - if (file_extension == 'h' - and Search(r'\bnamespace\s*{', line) - and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, - 'Do not use unnamed namespaces in header files. See ' - 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') - - -def CheckGlobalStatic(filename, clean_lines, linenum, error): - """Check for unsafe global or static objects. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Match two lines at a time to support multiline declarations - if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): - line += clean_lines.elided[linenum + 1].strip() - - # Check for people declaring static/global STL strings at the top level. - # This is dangerous because the C++ language does not guarantee that - # globals with constructors are initialized before the first access. - match = Match( - r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', - line) - - # Remove false positives: - # - String pointers (as opposed to values). - # string *pointer - # const string *pointer - # string const *pointer - # string *const pointer - # - # - Functions and template specializations. - # string Function(... - # string Class::Method(... - # - # - Operators. These are matched separately because operator names - # cross non-word boundaries, and trying to match both operators - # and functions at the same time would decrease accuracy of - # matching identifiers. - # string Class::operator*() - if (match and - not Search(r'\bstring\b(\s+const)?\s*\*\s*(const\s+)?\w', line) and - not Search(r'\boperator\W', line) and - not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(3))): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string instead: ' - '"%schar %s[]".' % - (match.group(1), match.group(2))) - - if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') - - -def CheckPrintf(filename, clean_lines, linenum, error): - """Check for printf related issues. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # When snprintf is used, the second argument shouldn't be a literal. - match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) - if match and match.group(2) != '0': - # If 2nd arg is zero, snprintf is used to calculate size. - error(filename, linenum, 'runtime/printf', 3, - 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' - 'to snprintf.' % (match.group(1), match.group(2))) - - # Check if some verboten C functions are being used. - if Search(r'\bsprintf\s*\(', line): - error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\s*\(', line) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) - - -def IsDerivedFunction(clean_lines, linenum): - """Check if current line contains an inherited function. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line contains a function with "override" - virt-specifier. - """ - # Scan back a few lines for start of current function - for i in xrange(linenum, max(-1, linenum - 10), -1): - match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) - if match: - # Look for "override" after the matching closing parenthesis - line, _, closing_paren = CloseExpression( - clean_lines, i, len(match.group(1))) - return (closing_paren >= 0 and - Search(r'\boverride\b', line[closing_paren:])) - return False - - -def IsOutOfLineMethodDefinition(clean_lines, linenum): - """Check if current line contains an out-of-line method definition. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line contains an out-of-line method definition. - """ - # Scan back a few lines for start of current function - for i in xrange(linenum, max(-1, linenum - 10), -1): - if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): - return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None - return False - - -def IsInitializerList(clean_lines, linenum): - """Check if current line is inside constructor initializer list. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line appears to be inside constructor initializer - list, False otherwise. - """ - for i in xrange(linenum, 1, -1): - line = clean_lines.elided[i] - if i == linenum: - remove_function_body = Match(r'^(.*)\{\s*$', line) - if remove_function_body: - line = remove_function_body.group(1) - - if Search(r'\s:\s*\w+[({]', line): - # A lone colon tend to indicate the start of a constructor - # initializer list. It could also be a ternary operator, which - # also tend to appear in constructor initializer lists as - # opposed to parameter lists. - return True - if Search(r'\}\s*,\s*$', line): - # A closing brace followed by a comma is probably the end of a - # brace-initialized member in constructor initializer list. - return True - if Search(r'[{};]\s*$', line): - # Found one of the following: - # - A closing brace or semicolon, probably the end of the previous - # function. - # - An opening brace, probably the start of current class or namespace. - # - # Current line is probably not inside an initializer list since - # we saw one of those things without seeing the starting colon. - return False - - # Got to the beginning of the file without seeing the start of - # constructor initializer list. - return False - - -def CheckForNonConstReference(filename, clean_lines, linenum, - nesting_state, error): - """Check for non-const references. - - Separate from CheckLanguage since it scans backwards from current - line, instead of scanning forward. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - # Do nothing if there is no '&' on current line. - line = clean_lines.elided[linenum] - if '&' not in line: - return - - # If a function is inherited, current function doesn't have much of - # a choice, so any non-const references should not be blamed on - # derived function. - if IsDerivedFunction(clean_lines, linenum): - return - - # Don't warn on out-of-line method definitions, as we would warn on the - # in-line declaration, if it isn't marked with 'override'. - if IsOutOfLineMethodDefinition(clean_lines, linenum): - return - - # Long type names may be broken across multiple lines, usually in one - # of these forms: - # LongType - # ::LongTypeContinued &identifier - # LongType:: - # LongTypeContinued &identifier - # LongType< - # ...>::LongTypeContinued &identifier - # - # If we detected a type split across two lines, join the previous - # line to current line so that we can match const references - # accordingly. - # - # Note that this only scans back one line, since scanning back - # arbitrary number of lines would be expensive. If you have a type - # that spans more than 2 lines, please use a typedef. - if linenum > 1: - previous = None - if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): - # previous_line\n + ::current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', - clean_lines.elided[linenum - 1]) - elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): - # previous_line::\n + current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', - clean_lines.elided[linenum - 1]) - if previous: - line = previous.group(1) + line.lstrip() - else: - # Check for templated parameter that is split across multiple lines - endpos = line.rfind('>') - if endpos > -1: - (_, startline, startpos) = ReverseCloseExpression( - clean_lines, linenum, endpos) - if startpos > -1 and startline < linenum: - # Found the matching < on an earlier line, collect all - # pieces up to current line. - line = '' - for i in xrange(startline, linenum + 1): - line += clean_lines.elided[i].strip() - - # Check for non-const references in function parameters. A single '&' may - # found in the following places: - # inside expression: binary & for bitwise AND - # inside expression: unary & for taking the address of something - # inside declarators: reference parameter - # We will exclude the first two cases by checking that we are not inside a - # function body, including one that was just introduced by a trailing '{'. - # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. - if (nesting_state.previous_stack_top and - not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or - isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): - # Not at toplevel, not within a class, and not within a namespace - return - - # Avoid initializer lists. We only need to scan back from the - # current line for something that starts with ':'. - # - # We don't need to check the current line, since the '&' would - # appear inside the second set of parentheses on the current line as - # opposed to the first set. - if linenum > 0: - for i in xrange(linenum - 1, max(0, linenum - 10), -1): - previous_line = clean_lines.elided[i] - if not Search(r'[),]\s*$', previous_line): - break - if Match(r'^\s*:\s+\S', previous_line): - return - - # Avoid preprocessors - if Search(r'\\\s*$', line): - return - - # Avoid constructor initializer lists - if IsInitializerList(clean_lines, linenum): - return - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". Do not check - # those function parameters. - # - # We also accept & in static_assert, which looks like a function but - # it's actually a declaration expression. - whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' - r'operator\s*[<>][<>]|' - r'static_assert|COMPILE_ASSERT' - r')\s*\(') - if Search(whitelisted_functions, line): - return - elif not Search(r'\S+\([^)]*$', line): - # Don't see a whitelisted function on this line. Actually we - # didn't see any function name on this line, so this is likely a - # multi-line parameter list. Try a bit harder to catch this case. - for i in xrange(2): - if (linenum > i and - Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): - return - - decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body - for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): - if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer: ' + - ReplaceAll(' *<', '<', parameter)) - - -def CheckCasts(filename, clean_lines, linenum, error): - """Various cast related checks. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Check to see if they're using an conversion function cast. - # I just try to capture the most common basic types, though there are more. - # Parameterless conversion functions, such as bool(), are allowed as they are - # probably a member operator declaration or default constructor. - match = Search( - r'(\bnew\s+|\S<\s*(?:const\s+)?)?\b' - r'(int|float|double|bool|char|int32|uint32|int64|uint64)' - r'(\([^)].*)', line) - expecting_function = ExpectingFunctionArgs(clean_lines, linenum) - if match and not expecting_function: - matched_type = match.group(2) - - # matched_new_or_template is used to silence two false positives: - # - New operators - # - Template arguments with function types - # - # For template arguments, we match on types immediately following - # an opening bracket without any spaces. This is a fast way to - # silence the common case where the function type is the first - # template argument. False negative with less-than comparison is - # avoided because those operators are usually followed by a space. - # - # function // bracket + no space = false positive - # value < double(42) // bracket + space = true positive - matched_new_or_template = match.group(1) - - # Avoid arrays by looking for brackets that come after the closing - # parenthesis. - if Match(r'\([^()]+\)\s*\[', match.group(3)): - return - - # Other things to ignore: - # - Function pointers - # - Casts to pointer types - # - Placement new - # - Alias declarations - matched_funcptr = match.group(3) - if (matched_new_or_template is None and - not (matched_funcptr and - (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', - matched_funcptr) or - matched_funcptr.startswith('(*)'))) and - not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and - not Search(r'new\(\S+\)\s*' + matched_type, line)): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - matched_type) - - if not expecting_function: - CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # - # (char *) "foo" should always be a const_cast (reinterpret_cast won't - # compile). - if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', - r'\((char\s?\*+\s?)\)\s*"', error): - pass - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', - r'\((\w+\s?\*+\s?)\)', error) - - # In addition, we look for people taking the address of a cast. This - # is dangerous -- casts can assign to temporaries, so the pointer doesn't - # point where you think. - # - # Some non-identifier character is required before the '&' for the - # expression to be recognized as a cast. These are casts: - # expression = &static_cast(temporary()); - # function(&(int*)(temporary())); - # - # This is not a cast: - # reference_type&(int* function_param); - match = Search( - r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' - r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) - if match: - # Try a better error message when the & is bound to something - # dereferenced by the casted pointer, as opposed to the casted - # pointer itself. - parenthesis_error = False - match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) - if match: - _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) - if x1 >= 0 and clean_lines.elided[y1][x1] == '(': - _, y2, x2 = CloseExpression(clean_lines, y1, x1) - if x2 >= 0: - extended_line = clean_lines.elided[y2][x2:] - if y2 < clean_lines.NumLines() - 1: - extended_line += clean_lines.elided[y2 + 1] - if Match(r'\s*(?:->|\[)', extended_line): - parenthesis_error = True - - if parenthesis_error: - error(filename, linenum, 'readability/casting', 4, - ('Are you taking an address of something dereferenced ' - 'from a cast? Wrapping the dereferenced expression in ' - 'parentheses will make the binding more obvious')) - else: - error(filename, linenum, 'runtime/casting', 4, - ('Are you taking an address of a cast? ' - 'This is dangerous: could be a temp var. ' - 'Take the address before doing the cast, rather than after')) - - -def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): - """Checks for a C-style cast by looking for the pattern. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast, static_cast, or const_cast, depending. - pattern: The regular expression used to find C-style casts. - error: The function to call with any errors found. - - Returns: - True if an error was emitted. - False otherwise. - """ - line = clean_lines.elided[linenum] - match = Search(pattern, line) - if not match: - return False - - # Exclude lines with keywords that tend to look like casts - context = line[0:match.start(1) - 1] - if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): - return False - - # Try expanding current context to see if we one level of - # parentheses inside a macro. - if linenum > 0: - for i in xrange(linenum - 1, max(0, linenum - 5), -1): - context = clean_lines.elided[i] + context - if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): - return False - - # operator++(int) and operator--(int) - if context.endswith(' operator++') or context.endswith(' operator--'): - return False - - # A single unnamed argument for a function tends to look like old - # style cast. If we see those, don't issue warnings for deprecated - # casts, instead issue warnings for unnamed arguments where - # appropriate. - # - # These are things that we want warnings for, since the style guide - # explicitly require all parameters to be named: - # Function(int); - # Function(int) { - # ConstMember(int) const; - # ConstMember(int) const { - # ExceptionMember(int) throw (...); - # ExceptionMember(int) throw (...) { - # PureVirtual(int) = 0; - # [](int) -> bool { - # - # These are functions of some sort, where the compiler would be fine - # if they had named parameters, but people often omit those - # identifiers to reduce clutter: - # (FunctionPointer)(int); - # (FunctionPointer)(int) = value; - # Function((function_pointer_arg)(int)) - # Function((function_pointer_arg)(int), int param) - # ; - # <(FunctionPointerTemplateArgument)(int)>; - remainder = line[match.end(0):] - if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', - remainder): - # Looks like an unnamed parameter. - - # Don't warn on any kind of template arguments. - if Match(r'^\s*>', remainder): - return False - - # Don't warn on assignments to function pointers, but keep warnings for - # unnamed parameters to pure virtual functions. Note that this pattern - # will also pass on assignments of "0" to function pointers, but the - # preferred values for those would be "nullptr" or "NULL". - matched_zero = Match(r'^\s=\s*(\S+)\s*;', remainder) - if matched_zero and matched_zero.group(1) != '0': - return False - - # Don't warn on function pointer declarations. For this we need - # to check what came before the "(type)" string. - if Match(r'.*\)\s*$', line[0:match.start(0)]): - return False - - # Don't warn if the parameter is named with block comments, e.g.: - # Function(int /*unused_param*/); - raw_line = clean_lines.raw_lines[linenum] - if '/*' in raw_line: - return False - - # Passed all filters, issue warning here. - error(filename, linenum, 'readability/function', 3, - 'All parameters should be named in a function') - return True - - # At this point, all that should be left is actual casts. - error(filename, linenum, 'readability/casting', 4, - 'Using C-style cast. Use %s<%s>(...) instead' % - (cast_type, match.group(1))) - - return True - - -def ExpectingFunctionArgs(clean_lines, linenum): - """Checks whether where function type arguments are expected. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - - Returns: - True if the line at 'linenum' is inside something that expects arguments - of function types. - """ - line = clean_lines.elided[linenum] - return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or - (linenum >= 2 and - (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', - clean_lines.elided[linenum - 1]) or - Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', - clean_lines.elided[linenum - 2]) or - Search(r'\bstd::m?function\s*\<\s*$', - clean_lines.elided[linenum - 1])))) - - -_HEADERS_CONTAINING_TEMPLATES = ( - ('', ('deque',)), - ('', ('unary_function', 'binary_function', - 'plus', 'minus', 'multiplies', 'divides', 'modulus', - 'negate', - 'equal_to', 'not_equal_to', 'greater', 'less', - 'greater_equal', 'less_equal', - 'logical_and', 'logical_or', 'logical_not', - 'unary_negate', 'not1', 'binary_negate', 'not2', - 'bind1st', 'bind2nd', - 'pointer_to_unary_function', - 'pointer_to_binary_function', - 'ptr_fun', - 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', - 'mem_fun_ref_t', - 'const_mem_fun_t', 'const_mem_fun1_t', - 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', - 'mem_fun_ref', - )), - ('', ('numeric_limits',)), - ('', ('list',)), - ('', ('map', 'multimap',)), - ('', ('allocator',)), - ('', ('queue', 'priority_queue',)), - ('', ('set', 'multiset',)), - ('', ('stack',)), - ('', ('char_traits', 'basic_string',)), - ('', ('tuple',)), - ('', ('pair',)), - ('', ('vector',)), - - # gcc extensions. - # Note: std::hash is their hash, ::hash is our hash - ('', ('hash_map', 'hash_multimap',)), - ('', ('hash_set', 'hash_multiset',)), - ('', ('slist',)), - ) - -_RE_PATTERN_STRING = re.compile(r'\bstring\b') - -_re_pattern_algorithm_header = [] -for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', - 'transform'): - # Match max(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_algorithm_header.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - '')) - -_re_pattern_templates = [] -for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: - for _template in _templates: - _re_pattern_templates.append( - (re.compile(r'(\<|\b)' + _template + r'\s*\<'), - _template + '<>', - _header)) - - -def FilesBelongToSameModule(filename_cc, filename_h): - """Check if these two filenames belong to the same module. - - The concept of a 'module' here is a as follows: - foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the - same 'module' if they are in the same directory. - some/path/public/xyzzy and some/path/internal/xyzzy are also considered - to belong to the same module here. - - If the filename_cc contains a longer path than the filename_h, for example, - '/absolute/path/to/base/sysinfo.cc', and this file would include - 'base/sysinfo.h', this function also produces the prefix needed to open the - header. This is used by the caller of this function to more robustly open the - header file. We don't have access to the real include paths in this context, - so we need this guesswork here. - - Known bugs: tools/base/bar.cc and base/bar.h belong to the same module - according to this implementation. Because of this, this function gives - some false positives. This should be sufficiently rare in practice. - - Args: - filename_cc: is the path for the .cc file - filename_h: is the path for the header path - - Returns: - Tuple with a bool and a string: - bool: True if filename_cc and filename_h belong to the same module. - string: the additional prefix needed to open the header file. - """ - - if not filename_cc.endswith('.cc'): - return (False, '') - filename_cc = filename_cc[:-len('.cc')] - if filename_cc.endswith('_unittest'): - filename_cc = filename_cc[:-len('_unittest')] - elif filename_cc.endswith('_test'): - filename_cc = filename_cc[:-len('_test')] - filename_cc = filename_cc.replace('/public/', '/') - filename_cc = filename_cc.replace('/internal/', '/') - - if not filename_h.endswith('.h'): - return (False, '') - filename_h = filename_h[:-len('.h')] - if filename_h.endswith('-inl'): - filename_h = filename_h[:-len('-inl')] - filename_h = filename_h.replace('/public/', '/') - filename_h = filename_h.replace('/internal/', '/') - - files_belong_to_same_module = filename_cc.endswith(filename_h) - common_path = '' - if files_belong_to_same_module: - common_path = filename_cc[:-len(filename_h)] - return files_belong_to_same_module, common_path - - -def UpdateIncludeState(filename, include_dict, io=codecs): - """Fill up the include_dict with new includes found from the file. - - Args: - filename: the name of the header to read. - include_dict: a dictionary in which the headers are inserted. - io: The io factory to use to read the file. Provided for testability. - - Returns: - True if a header was successfully added. False otherwise. - """ - headerfile = None - try: - headerfile = io.open(filename, 'r', 'utf8', 'replace') - except IOError: - return False - linenum = 0 - for line in headerfile: - linenum += 1 - clean_line = CleanseComments(line) - match = _RE_PATTERN_INCLUDE.search(clean_line) - if match: - include = match.group(2) - include_dict.setdefault(include, linenum) - return True - - -def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, - io=codecs): - """Reports for missing stl includes. - - This function will output warnings to make sure you are including the headers - necessary for the stl containers and functions that you use. We only give one - reason to include a header. For example, if you use both equal_to<> and - less<> in a .h file, only one (the latter in the file) of these will be - reported as a reason to include the . - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - include_state: An _IncludeState instance. - error: The function to call with any errors found. - io: The IO factory to use to read the header file. Provided for unittest - injection. - """ - required = {} # A map of header name to linenumber and the template entity. - # Example of required: { '': (1219, 'less<>') } - - for linenum in xrange(clean_lines.NumLines()): - line = clean_lines.elided[linenum] - if not line or line[0] == '#': - continue - - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[''] = (linenum, 'string') - - for pattern, template, header in _re_pattern_algorithm_header: - if pattern.search(line): - required[header] = (linenum, template) - - # The following function is just a speed up, no semantics are changed. - if not '<' in line: # Reduces the cpu time usage by skipping lines. - continue - - for pattern, template, header in _re_pattern_templates: - if pattern.search(line): - required[header] = (linenum, template) - - # The policy is that if you #include something in foo.h you don't need to - # include it again in foo.cc. Here, we will look at possible includes. - # Let's flatten the include_state include_list and copy it into a dictionary. - include_dict = dict([item for sublist in include_state.include_list - for item in sublist]) - - # Did we find the header for this file (if any) and successfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_dict is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = include_dict.keys() - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_dict, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: - return - - # All the lines have been processed, report the errors found. - for required_header_unstripped in required: - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_dict: - error(filename, required[required_header_unstripped][0], - 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) - - -_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') - - -def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): - """Check that make_pair's template arguments are deduced. - - G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are - specified explicitly, and such use isn't intended in any case. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) - if match: - error(filename, linenum, 'build/explicit_make_pair', - 4, # 4 = high confidence - 'For C++11-compatibility, omit template arguments from make_pair' - ' OR use pair directly OR if appropriate, construct a pair directly') - - -def CheckDefaultLambdaCaptures(filename, clean_lines, linenum, error): - """Check that default lambda captures are not used. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # A lambda introducer specifies a default capture if it starts with "[=" - # or if it starts with "[&" _not_ followed by an identifier. - match = Match(r'^(.*)\[\s*(?:=|&[^\w])', line) - if match: - # Found a potential error, check what comes after the lambda-introducer. - # If it's not open parenthesis (for lambda-declarator) or open brace - # (for compound-statement), it's not a lambda. - line, _, pos = CloseExpression(clean_lines, linenum, len(match.group(1))) - if pos >= 0 and Match(r'^\s*[{(]', line[pos:]): - error(filename, linenum, 'build/c++11', - 4, # 4 = high confidence - 'Default lambda captures are an unapproved C++ feature.') - - -def CheckRedundantVirtual(filename, clean_lines, linenum, error): - """Check if line contains a redundant "virtual" function-specifier. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Look for "virtual" on current line. - line = clean_lines.elided[linenum] - virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) - if not virtual: return - - # Ignore "virtual" keywords that are near access-specifiers. These - # are only used in class base-specifier and do not apply to member - # functions. - if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or - Match(r'^\s+(public|protected|private)\b', virtual.group(3))): - return - - # Ignore the "virtual" keyword from virtual base classes. Usually - # there is a column on the same line in these cases (virtual base - # classes are rare in google3 because multiple inheritance is rare). - if Match(r'^.*[^:]:[^:].*$', line): return - - # Look for the next opening parenthesis. This is the start of the - # parameter list (possibly on the next line shortly after virtual). - # TODO(unknown): doesn't work if there are virtual functions with - # decltype() or other things that use parentheses, but csearch suggests - # that this is rare. - end_col = -1 - end_line = -1 - start_col = len(virtual.group(2)) - for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): - line = clean_lines.elided[start_line][start_col:] - parameter_list = Match(r'^([^(]*)\(', line) - if parameter_list: - # Match parentheses to find the end of the parameter list - (_, end_line, end_col) = CloseExpression( - clean_lines, start_line, start_col + len(parameter_list.group(1))) - break - start_col = 0 - - if end_col < 0: - return # Couldn't find end of parameter list, give up - - # Look for "override" or "final" after the parameter list - # (possibly on the next few lines). - for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): - line = clean_lines.elided[i][end_col:] - match = Search(r'\b(override|final)\b', line) - if match: - error(filename, linenum, 'readability/inheritance', 4, - ('"virtual" is redundant since function is ' - 'already declared as "%s"' % match.group(1))) - - # Set end_col to check whole lines after we are done with the - # first line. - end_col = 0 - if Search(r'[^\w]\s*$', line): - break - - -def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error): - """Check if line contains a redundant "override" or "final" virt-specifier. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Look for closing parenthesis nearby. We need one to confirm where - # the declarator ends and where the virt-specifier starts to avoid - # false positives. - line = clean_lines.elided[linenum] - declarator_end = line.rfind(')') - if declarator_end >= 0: - fragment = line[declarator_end:] - else: - if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0: - fragment = line - else: - return - - # Check that at most one of "override" or "final" is present, not both - if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment): - error(filename, linenum, 'readability/inheritance', 4, - ('"override" is redundant since function is ' - 'already declared as "final"')) - - - - -# Returns true if we are at a new block, and it is directly -# inside of a namespace. -def IsBlockInNameSpace(nesting_state, is_forward_declaration): - """Checks that the new block is directly in a namespace. - - Args: - nesting_state: The _NestingState object that contains info about our state. - is_forward_declaration: If the class is a forward declared class. - Returns: - Whether or not the new block is directly in a namespace. - """ - if is_forward_declaration: - if len(nesting_state.stack) >= 1 and ( - isinstance(nesting_state.stack[-1], _NamespaceInfo)): - return True - else: - return False - - return (len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.stack[-2], _NamespaceInfo)) - - -def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, - raw_lines_no_comments, linenum): - """This method determines if we should apply our namespace indentation check. - - Args: - nesting_state: The current nesting state. - is_namespace_indent_item: If we just put a new class on the stack, True. - If the top of the stack is not a class, or we did not recently - add the class, False. - raw_lines_no_comments: The lines without the comments. - linenum: The current line number we are processing. - - Returns: - True if we should apply our namespace indentation check. Currently, it - only works for classes and namespaces inside of a namespace. - """ - - is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, - linenum) - - if not (is_namespace_indent_item or is_forward_declaration): - return False - - # If we are in a macro, we do not want to check the namespace indentation. - if IsMacroDefinition(raw_lines_no_comments, linenum): - return False - - return IsBlockInNameSpace(nesting_state, is_forward_declaration) - - -# Call this method if the line is directly inside of a namespace. -# If the line above is blank (excluding comments) or the start of -# an inner namespace, it cannot be indented. -def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, - error): - line = raw_lines_no_comments[linenum] - if Match(r'^\s+', line): - error(filename, linenum, 'runtime/indentation_namespace', 4, - 'Do not indent within a namespace') - - -def ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions=[]): - """Processes a single line in the file. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - clean_lines: An array of strings, each representing a line of the file, - with comments stripped. - line: Number of line being processed. - include_state: An _IncludeState instance in which the headers are inserted. - function_state: A _FunctionState instance which counts function lines, etc. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[line], line, error) - nesting_state.Update(filename, clean_lines, line, error) - CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, - error) - if nesting_state.InAsmBlock(): return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) - CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) - CheckLanguage(filename, clean_lines, line, file_extension, include_state, - nesting_state, error) - CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) - CheckForNonStandardConstructs(filename, clean_lines, line, - nesting_state, error) - CheckVlogArguments(filename, clean_lines, line, error) - CheckPosixThreading(filename, clean_lines, line, error) - CheckInvalidIncrement(filename, clean_lines, line, error) - CheckMakePairUsesDeduction(filename, clean_lines, line, error) - CheckDefaultLambdaCaptures(filename, clean_lines, line, error) - CheckRedundantVirtual(filename, clean_lines, line, error) - CheckRedundantOverrideOrFinal(filename, clean_lines, line, error) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) - -def FlagCxx11Features(filename, clean_lines, linenum, error): - """Flag those c++11 features that we only allow in certain places. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Flag unapproved C++11 headers. - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - if include and include.group(1) in ('cfenv', - 'condition_variable', - 'fenv.h', - 'future', - 'mutex', - 'thread', - 'chrono', - 'ratio', - 'regex', - 'system_error', - ): - error(filename, linenum, 'build/c++11', 5, - ('<%s> is an unapproved C++11 header.') % include.group(1)) - - # The only place where we need to worry about C++11 keywords and library - # features in preprocessor directives is in macro definitions. - if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return - - # These are classes and free functions. The classes are always - # mentioned as std::*, but we only catch the free functions if - # they're not found by ADL. They're alphabetical by header. - for top_name in ( - # type_traits - 'alignment_of', - 'aligned_union', - ): - if Search(r'\bstd::%s\b' % top_name, line): - error(filename, linenum, 'build/c++11', 5, - ('std::%s is an unapproved C++11 class or function. Send c-style ' - 'an example of where it would make your code more readable, and ' - 'they may let you use it.') % top_name) - - -def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=[]): - """Performs lint checks and reports any errors to the given error function. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - lines = (['// marker so line numbers and indices both start at 1'] + lines + - ['// marker so line numbers end in a known way']) - - include_state = _IncludeState() - function_state = _FunctionState() - nesting_state = NestingState() - - ResetNolintSuppressions() - - CheckForCopyright(filename, lines, error) - - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) - - if file_extension == 'h': - CheckForHeaderGuard(filename, clean_lines, error) - - for line in xrange(clean_lines.NumLines()): - ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions) - FlagCxx11Features(filename, clean_lines, line, error) - nesting_state.CheckCompletedBlocks(filename, error) - - CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) - - # Check that the .cc file has included its header if it exists. - if file_extension == 'cc': - CheckHeaderFileIncluded(filename, include_state, error) - - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForBadCharacters(filename, lines, error) - - CheckForNewlineAtEOF(filename, lines, error) - -def ProcessConfigOverrides(filename): - """ Loads the configuration files and processes the config overrides. - - Args: - filename: The name of the file being processed by the linter. - - Returns: - False if the current |filename| should not be processed further. - """ - - abs_filename = os.path.abspath(filename) - cfg_filters = [] - keep_looking = True - while keep_looking: - abs_path, base_name = os.path.split(abs_filename) - if not base_name: - break # Reached the root directory. - - cfg_file = os.path.join(abs_path, "CPPLINT.cfg") - abs_filename = abs_path - if not os.path.isfile(cfg_file): - continue - - try: - with open(cfg_file) as file_handle: - for line in file_handle: - line, _, _ = line.partition('#') # Remove comments. - if not line.strip(): - continue - - name, _, val = line.partition('=') - name = name.strip() - val = val.strip() - if name == 'set noparent': - keep_looking = False - elif name == 'filter': - cfg_filters.append(val) - elif name == 'exclude_files': - # When matching exclude_files pattern, use the base_name of - # the current file name or the directory name we are processing. - # For example, if we are checking for lint errors in /foo/bar/baz.cc - # and we found the .cfg file at /foo/CPPLINT.cfg, then the config - # file's "exclude_files" filter is meant to be checked against "bar" - # and not "baz" nor "bar/baz.cc". - if base_name: - pattern = re.compile(val) - if pattern.match(base_name): - sys.stderr.write('Ignoring "%s": file excluded by "%s". ' - 'File path component "%s" matches ' - 'pattern "%s"\n' % - (filename, cfg_file, base_name, val)) - return False - elif name == 'linelength': - global _line_length - try: - _line_length = int(val) - except ValueError: - sys.stderr.write('Line length must be numeric.') - else: - sys.stderr.write( - 'Invalid configuration option (%s) in file %s\n' % - (name, cfg_file)) - - except IOError: - sys.stderr.write( - "Skipping config file '%s': Can't open for reading\n" % cfg_file) - keep_looking = False - - # Apply all the accumulated filters in reverse order (top-level directory - # config options having the least priority). - for filter in reversed(cfg_filters): - _AddFilters(filter) - - return True - - -def ProcessFile(filename, vlevel, extra_check_functions=[]): - """Does google-lint on a single file. - - Args: - filename: The name of the file to parse. - - vlevel: The level of errors to report. Every error of confidence - >= verbose_level will be reported. 0 is a good default. - - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - - _SetVerboseLevel(vlevel) - _BackupFilters() - - if not ProcessConfigOverrides(filename): - _RestoreFilters() - return - - lf_lines = [] - crlf_lines = [] - try: - # Support the UNIX convention of using "-" for stdin. Note that - # we are not opening the file with universal newline support - # (which codecs doesn't support anyway), so the resulting lines do - # contain trailing '\r' characters if we are reading a file that - # has CRLF endings. - # If after the split a trailing '\r' is present, it is removed - # below. - if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') - else: - lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') - - # Remove trailing '\r'. - # The -1 accounts for the extra trailing blank line we get from split() - for linenum in range(len(lines) - 1): - if lines[linenum].endswith('\r'): - lines[linenum] = lines[linenum].rstrip('\r') - crlf_lines.append(linenum + 1) - else: - lf_lines.append(linenum + 1) - - except IOError: - sys.stderr.write( - "Skipping input '%s': Can't open for reading\n" % filename) - _RestoreFilters() - return - - # Note, if no dot is found, this will give the entire filename as the ext. - file_extension = filename[filename.rfind('.') + 1:] - - # When reading from stdin, the extension is unknown, so no cpplint tests - # should rely on the extension. - if filename != '-' and file_extension not in _valid_extensions: - sys.stderr.write('Ignoring %s; not a valid file name ' - '(%s)\n' % (filename, ', '.join(_valid_extensions))) - else: - ProcessFileData(filename, file_extension, lines, Error, - extra_check_functions) - - # If end-of-line sequences are a mix of LF and CR-LF, issue - # warnings on the lines with CR. - # - # Don't issue any warnings if all lines are uniformly LF or CR-LF, - # since critique can handle these just fine, and the style guide - # doesn't dictate a particular end of line sequence. - # - # We can't depend on os.linesep to determine what the desired - # end-of-line sequence should be, since that will return the - # server-side end-of-line sequence. - if lf_lines and crlf_lines: - # Warn on every line with CR. An alternative approach might be to - # check whether the file is mostly CRLF or just LF, and warn on the - # minority, we bias toward LF here since most tools prefer LF. - for linenum in crlf_lines: - Error(filename, linenum, 'whitespace/newline', 1, - 'Unexpected \\r (^M) found; better to use only \\n') - - sys.stderr.write('Done processing %s\n' % filename) - _RestoreFilters() - - -def PrintUsage(message): - """Prints a brief usage string and exits, optionally with an error message. - - Args: - message: The optional error message. - """ - sys.stderr.write(_USAGE) - if message: - sys.exit('\nFATAL ERROR: ' + message) - else: - sys.exit(1) - - -def PrintCategories(): - """Prints a list of all the error-categories used by error messages. - - These are the categories used to filter messages via --filter. - """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) - sys.exit(0) - - -def ParseArguments(args): - """Parses the command line arguments. - - This may set the output format and verbosity level as side-effects. - - Args: - args: The command line arguments: - - Returns: - The list of filenames to lint. - """ - try: - (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', - 'counting=', - 'filter=', - 'root=', - 'linelength=', - 'extensions=']) - except getopt.GetoptError: - PrintUsage('Invalid arguments.') - - verbosity = _VerboseLevel() - output_format = _OutputFormat() - filters = '' - counting_style = '' - - for (opt, val) in opts: - if opt == '--help': - PrintUsage(None) - elif opt == '--output': - if val not in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') - output_format = val - elif opt == '--verbose': - verbosity = int(val) - elif opt == '--filter': - filters = val - if not filters: - PrintCategories() - elif opt == '--counting': - if val not in ('total', 'toplevel', 'detailed'): - PrintUsage('Valid counting options are total, toplevel, and detailed') - counting_style = val - elif opt == '--root': - global _root - _root = val - elif opt == '--linelength': - global _line_length - try: - _line_length = int(val) - except ValueError: - PrintUsage('Line length must be digits.') - elif opt == '--extensions': - global _valid_extensions - try: - _valid_extensions = set(val.split(',')) - except ValueError: - PrintUsage('Extensions must be comma seperated list.') - - if not filenames: - PrintUsage('No files were specified.') - - _SetOutputFormat(output_format) - _SetVerboseLevel(verbosity) - _SetFilters(filters) - _SetCountingStyle(counting_style) - - return filenames - - -def main(): - filenames = ParseArguments(sys.argv[1:]) - - # Change stderr to write with replacement characters so we don't die - # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') - - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - _cpplint_state.PrintErrorCounts() - - sys.exit(_cpplint_state.error_count > 0) - - -if __name__ == '__main__': - main() diff --git a/examples/1 - LoadAndPrint/box_stack.mtl b/examples/1 - LoadAndPrint/box_stack.mtl new file mode 100644 index 00000000..71357186 --- /dev/null +++ b/examples/1 - LoadAndPrint/box_stack.mtl @@ -0,0 +1,32 @@ +# Blender MTL File: 'box_stack.blend' +# Material Count: 3 + +newmtl mat_bot +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl mat_mid +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl mat_top +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/examples/1 - LoadAndPrint/box_stack.obj b/examples/1 - LoadAndPrint/box_stack.obj new file mode 100644 index 00000000..c455d7f0 --- /dev/null +++ b/examples/1 - LoadAndPrint/box_stack.obj @@ -0,0 +1,86 @@ +# Blender v2.76 (sub 0) OBJ File: 'box_stack.blend' +# www.blender.org +mtllib box_stack.mtl +o mTop_cTop +v 0.234105 2.076805 0.234105 +v 0.234105 2.545015 0.234105 +v -0.234105 2.076805 0.234105 +v -0.234105 2.545015 0.234105 +v 0.234105 2.076805 -0.234105 +v 0.234105 2.545015 -0.234105 +v -0.234105 2.076805 -0.234105 +v -0.234105 2.545015 -0.234105 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vt 1.000000 -0.000000 +vt 0.000000 -0.000000 +vn 0.000000 0.000000 1.000000 +vn -1.000000 0.000000 0.000000 +vn -0.000000 0.000000 -1.000000 +vn 1.000000 0.000000 -0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl mat_top +s off +f 2/1/1 4/2/1 3/3/1 1/4/1 +f 4/1/2 8/2/2 7/3/2 3/4/2 +f 8/2/3 6/1/3 5/4/3 7/3/3 +f 6/2/4 2/1/4 1/4/4 5/3/4 +f 1/4/5 3/1/5 7/2/5 5/3/5 +f 6/3/6 8/2/6 4/1/6 2/4/6 +o mMid_cMid +v 0.000000 1.004430 0.754531 +v 0.000000 2.071499 0.754531 +v -0.754531 1.004430 0.000000 +v -0.754531 2.071499 0.000000 +v 0.754531 1.004430 -0.000000 +v 0.754531 2.071499 -0.000000 +v -0.000000 1.004430 -0.754531 +v -0.000000 2.071499 -0.754531 +vt 0.003117 1.000000 +vt 1.003117 1.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vn -0.707100 0.000000 0.707100 +vn -0.707100 0.000000 -0.707100 +vn 0.707100 0.000000 -0.707100 +vn 0.707100 0.000000 0.707100 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl mat_mid +s off +f 10/5/7 12/6/7 11/7/7 9/8/7 +f 12/9/8 16/10/8 15/7/8 11/8/8 +f 16/10/9 14/9/9 13/8/9 15/7/9 +f 14/10/10 10/9/10 9/8/10 13/7/10 +f 9/8/11 11/9/11 15/10/11 13/7/11 +f 14/7/12 16/10/12 12/9/12 10/8/12 +o mBot_cBot +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vn -1.000000 0.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 0.000000 1.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl mat_bot +s off +f 18/11/13 20/12/13 19/13/13 17/14/13 +f 20/11/14 24/12/14 23/13/14 19/14/14 +f 24/12/15 22/11/15 21/14/15 23/13/15 +f 22/12/16 18/11/16 17/14/16 21/13/16 +f 17/14/17 19/11/17 23/12/17 21/13/17 +f 22/13/18 24/12/18 20/11/18 18/14/18 diff --git a/examples/1 - LoadAndPrint/e1_loadandprint.cpp b/examples/1 - LoadAndPrint/e1_loadandprint.cpp new file mode 100644 index 00000000..ced2187c --- /dev/null +++ b/examples/1 - LoadAndPrint/e1_loadandprint.cpp @@ -0,0 +1,101 @@ +// Example 1: Load and Print +// +// Load the data from the .obj then print it into a file +// called e1Out.txt + +// Iostream - STD I/O Library +#include + +// fStream - STD File I/O Library +#include + +// OBJ_Loader - .obj Loader +#include "OBJ_Loader.h" + +// Main function +int main(int argc, char* argv[]) +{ + // Initialize Loader + objl::Loader Loader; + + // Load .obj File + bool loadout = Loader.LoadFile("box_stack.obj"); + + // Check to see if it loaded + + // If so continue + if (loadout) + { + // Create/Open e1Out.txt + std::ofstream file("e1Out.txt"); + + // Go through each loaded mesh and out its contents + for (int i = 0; i < Loader.LoadedMeshes.size(); i++) + { + // Copy one of the loaded meshes to be our current mesh + objl::Mesh curMesh = Loader.LoadedMeshes[i]; + + // Print Mesh Name + file << "Mesh " << i << ": " << curMesh.MeshName << "\n"; + + // Print Vertices + file << "Vertices:\n"; + + // Go through each vertex and print its number, + // position, normal, and texture coordinate + for (int j = 0; j < curMesh.Vertices.size(); j++) + { + file << "V" << j << ": " << + "P(" << curMesh.Vertices[j].Position.X << ", " << curMesh.Vertices[j].Position.Y << ", " << curMesh.Vertices[j].Position.Z << ") " << + "N(" << curMesh.Vertices[j].Normal.X << ", " << curMesh.Vertices[j].Normal.Y << ", " << curMesh.Vertices[j].Normal.Z << ") " << + "TC(" << curMesh.Vertices[j].TextureCoordinate.X << ", " << curMesh.Vertices[j].TextureCoordinate.Y << ")\n"; + } + + // Print Indices + file << "Indices:\n"; + + // Go through every 3rd index and print the + // triangle that these indices represent + for (int j = 0; j < curMesh.Indices.size(); j += 3) + { + file << "T" << j / 3 << ": " << curMesh.Indices[j] << ", " << curMesh.Indices[j + 1] << ", " << curMesh.Indices[j + 2] << "\n"; + } + + // Print Material + file << "Material: " << curMesh.MeshMaterial.name << "\n"; + file << "Ambient Color: " << curMesh.MeshMaterial.Ka.X << ", " << curMesh.MeshMaterial.Ka.Y << ", " << curMesh.MeshMaterial.Ka.Z << "\n"; + file << "Diffuse Color: " << curMesh.MeshMaterial.Kd.X << ", " << curMesh.MeshMaterial.Kd.Y << ", " << curMesh.MeshMaterial.Kd.Z << "\n"; + file << "Specular Color: " << curMesh.MeshMaterial.Ks.X << ", " << curMesh.MeshMaterial.Ks.Y << ", " << curMesh.MeshMaterial.Ks.Z << "\n"; + file << "Specular Exponent: " << curMesh.MeshMaterial.Ns << "\n"; + file << "Optical Density: " << curMesh.MeshMaterial.Ni << "\n"; + file << "Dissolve: " << curMesh.MeshMaterial.d << "\n"; + file << "Illumination: " << curMesh.MeshMaterial.illum << "\n"; + file << "Ambient Texture Map: " << curMesh.MeshMaterial.map_Ka << "\n"; + file << "Diffuse Texture Map: " << curMesh.MeshMaterial.map_Kd << "\n"; + file << "Specular Texture Map: " << curMesh.MeshMaterial.map_Ks << "\n"; + file << "Alpha Texture Map: " << curMesh.MeshMaterial.map_d << "\n"; + file << "Bump Map: " << curMesh.MeshMaterial.map_bump << "\n"; + + // Leave a space to separate from the next mesh + file << "\n"; + } + + // Close File + file.close(); + } + // If not output an error + else + { + // Create/Open e1Out.txt + std::ofstream file("e1Out.txt"); + + // Output Error + file << "Failed to Load File. May have failed to find it or it was not an .obj file.\n"; + + // Close File + file.close(); + } + + // Exit the program + return 0; +} \ No newline at end of file diff --git a/examples/callback_api/Makefile b/examples/callback_api/Makefile deleted file mode 100644 index 45d60d85..00000000 --- a/examples/callback_api/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - clang++ -I../../ -Wall -g -o example main.cc diff --git a/examples/callback_api/main.cc b/examples/callback_api/main.cc deleted file mode 100644 index 1911501f..00000000 --- a/examples/callback_api/main.cc +++ /dev/null @@ -1,171 +0,0 @@ -// -// An example of how to use callback API. -// This example is minimum and incomplete. Just showing the usage of callback -// API. -// You need to implement your own Mesh data struct constrution based on this -// example in practical. -// -#define TINYOBJLOADER_IMPLEMENTATION -#include "tiny_obj_loader.h" - -#include -#include -#include -#include -#include -#include - -typedef struct { - std::vector vertices; - std::vector normals; - std::vector texcoords; - std::vector v_indices; - std::vector vn_indices; - std::vector vt_indices; - - std::vector materials; - -} MyMesh; - -void vertex_cb(void *user_data, float x, float y, float z, float w) { - MyMesh *mesh = reinterpret_cast(user_data); - printf("v[%ld] = %f, %f, %f (w %f)\n", mesh->vertices.size() / 3, x, y, z, w); - - mesh->vertices.push_back(x); - mesh->vertices.push_back(y); - mesh->vertices.push_back(z); - // Discard w -} - -void normal_cb(void *user_data, float x, float y, float z) { - MyMesh *mesh = reinterpret_cast(user_data); - printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z); - - mesh->normals.push_back(x); - mesh->normals.push_back(y); - mesh->normals.push_back(z); -} - -void texcoord_cb(void *user_data, float x, float y, float z) { - MyMesh *mesh = reinterpret_cast(user_data); - printf("vt[%ld] = %f, %f, %f\n", mesh->texcoords.size() / 3, x, y, z); - - mesh->texcoords.push_back(x); - mesh->texcoords.push_back(y); - mesh->texcoords.push_back(z); -} - -void index_cb(void *user_data, tinyobj::index_t *indices, int num_indices) { - // NOTE: the value of each index is raw value. - // For example, the application must manually adjust the index with offset - // (e.g. v_indices.size()) when the value is negative(whic means relative - // index). - // Also, the first index starts with 1, not 0. - // See fixIndex() function in tiny_obj_loader.h for details. - // Also, 0 is set for the index value which - // does not exist in .obj - MyMesh *mesh = reinterpret_cast(user_data); - - for (int i = 0; i < num_indices; i++) { - tinyobj::index_t idx = indices[i]; - printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), idx.vertex_index, - idx.normal_index, idx.texcoord_index); - - if (idx.vertex_index != 0) { - mesh->v_indices.push_back(idx.vertex_index); - } - if (idx.normal_index != 0) { - mesh->vn_indices.push_back(idx.normal_index); - } - if (idx.texcoord_index != 0) { - mesh->vt_indices.push_back(idx.texcoord_index); - } - } -} - -void usemtl_cb(void *user_data, const char *name, int material_idx) { - MyMesh *mesh = reinterpret_cast(user_data); - if ((material_idx > -1) && (material_idx < mesh->materials.size())) { - printf("usemtl. material id = %d(name = %s)\n", material_idx, - mesh->materials[material_idx].name.c_str()); - } else { - printf("usemtl. name = %s\n", name); - } -} - -void mtllib_cb(void *user_data, const tinyobj::material_t *materials, - int num_materials) { - MyMesh *mesh = reinterpret_cast(user_data); - printf("mtllib. # of materials = %d\n", num_materials); - - for (int i = 0; i < num_materials; i++) { - mesh->materials.push_back(materials[i]); - } -} - -void group_cb(void *user_data, const char **names, int num_names) { - // MyMesh *mesh = reinterpret_cast(user_data); - printf("group : name = \n"); - - for (int i = 0; i < num_names; i++) { - printf(" %s\n", names[i]); - } -} - -void object_cb(void *user_data, const char *name) { - // MyMesh *mesh = reinterpret_cast(user_data); - printf("object : name = %s\n", name); -} - -int main(int argc, char **argv) { - tinyobj::callback_t cb; - cb.vertex_cb = vertex_cb; - cb.normal_cb = normal_cb; - cb.texcoord_cb = texcoord_cb; - cb.index_cb = index_cb; - cb.usemtl_cb = usemtl_cb; - cb.mtllib_cb = mtllib_cb; - cb.group_cb = group_cb; - cb.object_cb = object_cb; - - MyMesh mesh; - std::string warn; - std::string err; - std::string filename = "../../models/cornell_box.obj"; - if (argc > 1) { - filename = std::string(argv[1]); - } - std::ifstream ifs(filename.c_str()); - - if (ifs.fail()) { - std::cerr << "file not found." << std::endl; - return EXIT_FAILURE; - } - - tinyobj::MaterialFileReader mtlReader("../../models/"); - - bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &warn, &err); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << err << std::endl; - } - - if (!ret) { - std::cerr << "Failed to parse .obj" << std::endl; - return EXIT_FAILURE; - } - - printf("# of vertices = %ld\n", mesh.vertices.size() / 3); - printf("# of normals = %ld\n", mesh.normals.size() / 3); - printf("# of texcoords = %ld\n", mesh.texcoords.size() / 2); - printf("# of vertex indices = %ld\n", mesh.v_indices.size()); - printf("# of normal indices = %ld\n", mesh.vn_indices.size()); - printf("# of texcoord indices = %ld\n", mesh.vt_indices.size()); - printf("# of materials = %ld\n", mesh.materials.size()); - - return EXIT_SUCCESS; -} diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc deleted file mode 100644 index 1057d470..00000000 --- a/examples/obj_sticher/obj_sticher.cc +++ /dev/null @@ -1,163 +0,0 @@ -// -// Stiches multiple .obj files into one .obj. -// -#include "obj_writer.h" - -#include "../../tiny_obj_loader.h" - -#include -#include -#include -#include - -typedef std::vector Shape; -typedef std::vector Material; -typedef tinyobj::attrib_t Attribute; - -void -StichObjs( - tinyobj::attrib_t& out_attribute, - std::vector& out_shape, - std::vector& out_material, - const std::vector& attributes, - const std::vector& shapes, - const std::vector& materials) -{ - // The amount of attributes, shape-vectors and material-vecotrs should be the same. - if(attributes.size() != shapes.size() && attributes.size() != materials.size()){ - std::cerr << "Size of attributes, shapes and Materials don't fit!" << attributes.size() << " " << shapes.size() <<" " << materials.size() << std::endl;; - exit(1); - } - int num_shapes = 0; - // 4 values (vertices, normals, texcoords, colors) - std::vector num_attributes(4, 0); - int num_materials = 0; - for(int i = 0; i < shapes.size(); i++){ - num_shapes += shapes[i].size(); - } - for(int i = 0; i < attributes.size(); i++){ - num_attributes[0] += attributes[i].vertices.size(); - num_attributes[1] += attributes[i].normals.size(); - num_attributes[2] += attributes[i].texcoords.size(); - num_attributes[3] += attributes[i].colors.size(); - } - for(int i = 0; i < materials.size(); i++){ - num_materials += materials[i].size(); - } - - // More performant, than push_back - out_attribute.vertices.resize(num_attributes[0]); - out_attribute.normals.resize(num_attributes[1]); - out_attribute.texcoords.resize(num_attributes[2]); - out_attribute.colors.resize(num_attributes[3]); - out_shape.resize(num_shapes); - out_material.resize(num_materials); - - int material_id_offset = 0; - int shape_id_offset = 0; - int vertex_idx_offset = 0; - int normal_idx_offset = 0; - int texcoord_idx_offset = 0; - int color_idx_offset = 0; - - // shapes.size() = attributes.size() = materials.size() - for (size_t i = 0; i < shapes.size(); i++) { - - // Copy shapes - for (size_t k = 0; k < shapes[i].size(); k++) { - std::string new_name = shapes[i][k].name; - // Add suffix - char buf[1024]; - sprintf(buf, "_%04d", (int)i); - new_name += std::string(buf); - - printf("shape[%ld][%ld].name = %s\n", i, k, shapes[i][k].name.c_str()); - - tinyobj::shape_t new_shape = shapes[i][k]; - // Add material offset. - for(size_t f = 0; f < new_shape.mesh.material_ids.size(); f++) { - new_shape.mesh.material_ids[f] += material_id_offset; - } - // Add indices offset. - for(size_t f = 0; f < new_shape.mesh.indices.size(); f++){ - tinyobj::index_t& ref = new_shape.mesh.indices[f]; - if(ref.vertex_index > -1){ - ref.vertex_index += vertex_idx_offset; - } - if(ref.normal_index > -1){ - ref.normal_index += normal_idx_offset; - } - if(ref.texcoord_index > -1){ - ref.texcoord_index += texcoord_idx_offset; - } - } - - new_shape.name = new_name; - printf("shape[%ld][%ld].new_name = %s\n", i, k, new_shape.name.c_str()); - - out_shape[shape_id_offset++] = new_shape; - } - - // Copy materials - for (size_t k = 0; k < materials[i].size(); k++) { - out_material[material_id_offset++] = materials[i][k]; - } - - // Copy attributes (3 floats per vertex, 3 floats per normal, 2 floats per texture-coordinate, 3 floats per color) - // You could also include a check here, if the sizes are dividable by 3 (resp. 2), but it's safe to simply assume, they do. - std::copy(attributes[i].vertices.begin(), attributes[i].vertices.end(), out_attribute.vertices.begin() + vertex_idx_offset * 3); - vertex_idx_offset += attributes[i].vertices.size() / 3; - std::copy(attributes[i].normals.begin(), attributes[i].normals.end(), out_attribute.normals.begin() + normal_idx_offset * 3); - normal_idx_offset += attributes[i].normals.size() / 3; - std::copy(attributes[i].texcoords.begin(), attributes[i].texcoords.end(), out_attribute.texcoords.begin() + texcoord_idx_offset * 2); - texcoord_idx_offset += attributes[i].texcoords.size() / 2; - std::copy(attributes[i].colors.begin(), attributes[i].colors.end(), out_attribute.colors.begin() + color_idx_offset); - color_idx_offset += attributes[i].colors.size(); - } -} - -int main(int argc, char **argv) -{ - if (argc < 3) { - printf("Usage: obj_sticher input0.obj input1.obj ... output.obj\n"); - exit(1); - } - - int num_objfiles = argc - 2; - std::string out_filename = std::string(argv[argc-1]); // last element - - std::vector attributes(num_objfiles); - std::vector shapes(num_objfiles); - std::vector materials(num_objfiles); - - for (int i = 0; i < num_objfiles; i++) { - std::cout << "Loading " << argv[i+1] << " ... " << std::flush; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attributes[i], &shapes[i], &materials[i], &warn, &err, argv[i+1]); - if (!warn.empty()) { - std::cerr << "WARN:" << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << err << std::endl; - } - if (!ret) { - exit(1); - } - - std::cout << "DONE." << std::endl; - } - - Attribute out_attribute; - Shape out_shape; - Material out_material; - StichObjs(out_attribute, out_shape, out_material, attributes, shapes, materials); - - bool coordTransform = true; - bool ret = WriteObj(out_filename, out_attribute, out_shape, out_material, coordTransform); - assert(ret); - - return 0; -} diff --git a/examples/obj_sticher/obj_writer.cc b/examples/obj_sticher/obj_writer.cc deleted file mode 100644 index 31a2c895..00000000 --- a/examples/obj_sticher/obj_writer.cc +++ /dev/null @@ -1,164 +0,0 @@ -// -// Simple wavefront .obj writer -// -#include "obj_writer.h" -#include - -static std::string GetFileBasename(const std::string& FileName) -{ - if(FileName.find_last_of(".") != std::string::npos) - return FileName.substr(0, FileName.find_last_of(".")); - return ""; -} - -bool WriteMat(const std::string& filename, const std::vector& materials) { - FILE* fp = fopen(filename.c_str(), "w"); - if (!fp) { - fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str()); - return false; - } - - for (size_t i = 0; i < materials.size(); i++) { - - tinyobj::material_t mat = materials[i]; - - fprintf(fp, "newmtl %s\n", mat.name.c_str()); - fprintf(fp, "Ka %f %f %f\n", mat.ambient[0], mat.ambient[1], mat.ambient[2]); - fprintf(fp, "Kd %f %f %f\n", mat.diffuse[0], mat.diffuse[1], mat.diffuse[2]); - fprintf(fp, "Ks %f %f %f\n", mat.specular[0], mat.specular[1], mat.specular[2]); - fprintf(fp, "Kt %f %f %f\n", mat.transmittance[0], mat.transmittance[1], mat.transmittance[2]); - fprintf(fp, "Ke %f %f %f\n", mat.emission[0], mat.emission[1], mat.emission[2]); - fprintf(fp, "Ns %f\n", mat.shininess); - fprintf(fp, "Ni %f\n", mat.ior); - fprintf(fp, "illum %d\n", mat.illum); - fprintf(fp, "\n"); - // @todo { texture } - } - - fclose(fp); - - return true; -} - -bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector& shapes, const std::vector& materials, bool coordTransform) { - FILE* fp = fopen(filename.c_str(), "w"); - if (!fp) { - fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str()); - return false; - } - - std::string basename = GetFileBasename(filename); - std::string material_filename = basename + ".mtl"; - - int prev_material_id = -1; - - fprintf(fp, "mtllib %s\n\n", material_filename.c_str()); - - // facevarying vtx - for (size_t k = 0; k < attributes.vertices.size(); k+=3) { - if (coordTransform) { - fprintf(fp, "v %f %f %f\n", - attributes.vertices[k + 0], - attributes.vertices[k + 2], - -attributes.vertices[k + 1]); - } else { - fprintf(fp, "v %f %f %f\n", - attributes.vertices[k + 0], - attributes.vertices[k + 1], - attributes.vertices[k + 2]); - } - } - - fprintf(fp, "\n"); - - // facevarying normal - for (size_t k = 0; k < attributes.normals.size(); k += 3) { - if (coordTransform) { - fprintf(fp, "vn %f %f %f\n", - attributes.normals[k + 0], - attributes.normals[k + 2], - -attributes.normals[k + 1]); - } else { - fprintf(fp, "vn %f %f %f\n", - attributes.normals[k + 0], - attributes.normals[k + 1], - attributes.normals[k + 2]); - } - } - - fprintf(fp, "\n"); - - // facevarying texcoord - for (size_t k = 0; k < attributes.texcoords.size(); k += 2) { - fprintf(fp, "vt %f %f\n", - attributes.texcoords[k + 0], - attributes.texcoords[k + 1]); - } - - for (size_t i = 0; i < shapes.size(); i++) { - fprintf(fp, "\n"); - - if (shapes[i].name.empty()) { - fprintf(fp, "g Unknown\n"); - } else { - fprintf(fp, "g %s\n", shapes[i].name.c_str()); - } - - bool has_vn = false; - bool has_vt = false; - // Assumes normals and textures are set shape-wise. - if(shapes[i].mesh.indices.size() > 0){ - has_vn = shapes[i].mesh.indices[0].normal_index != -1; - has_vt = shapes[i].mesh.indices[0].texcoord_index != -1; - } - - // face - int face_index = 0; - for (size_t k = 0; k < shapes[i].mesh.indices.size(); k += shapes[i].mesh.num_face_vertices[face_index++]) { - // Check Materials - int material_id = shapes[i].mesh.material_ids[face_index]; - if (material_id != prev_material_id) { - std::string material_name = materials[material_id].name; - fprintf(fp, "usemtl %s\n", material_name.c_str()); - prev_material_id = material_id; - } - - unsigned char v_per_f = shapes[i].mesh.num_face_vertices[face_index]; - // Imperformant, but if you want to have variable vertices per face, you need some kind of a dynamic loop. - fprintf(fp, "f"); - for(int l = 0; l < v_per_f; l++){ - const tinyobj::index_t& ref = shapes[i].mesh.indices[k + l]; - if(has_vn && has_vt){ - // v0/t0/vn0 - fprintf(fp, " %d/%d/%d", ref.vertex_index + 1, ref.texcoord_index + 1, ref.normal_index + 1); - continue; - } - if(has_vn && !has_vt){ - // v0//vn0 - fprintf(fp, " %d//%d", ref.vertex_index + 1, ref.normal_index + 1); - continue; - } - if(!has_vn && has_vt){ - // v0/vt0 - fprintf(fp, " %d/%d", ref.vertex_index + 1, ref.texcoord_index + 1); - continue; - } - if(!has_vn && !has_vt){ - // v0 v1 v2 - fprintf(fp, " %d", ref.vertex_index + 1); - continue; - } - } - fprintf(fp, "\n"); - } - } - - fclose(fp); - - // - // Write material file - // - bool ret = WriteMat(material_filename, materials); - - return ret; -} diff --git a/examples/obj_sticher/obj_writer.h b/examples/obj_sticher/obj_writer.h deleted file mode 100644 index 262c6816..00000000 --- a/examples/obj_sticher/obj_writer.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __OBJ_WRITER_H__ -#define __OBJ_WRITER_H__ - -#include "../../tiny_obj_loader.h" - -extern bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector& shapes, const std::vector& materials, bool coordTransform = false); - -#endif // __OBJ_WRITER_H__ diff --git a/examples/obj_sticher/premake4.lua b/examples/obj_sticher/premake4.lua deleted file mode 100644 index 9c2deb6e..00000000 --- a/examples/obj_sticher/premake4.lua +++ /dev/null @@ -1,38 +0,0 @@ -lib_sources = { - "../../tiny_obj_loader.cc" -} - -sources = { - "obj_sticher.cc", - "obj_writer.cc", - } - --- premake4.lua -solution "ObjStickerSolution" - configurations { "Release", "Debug" } - - if (os.is("windows")) then - platforms { "x32", "x64" } - else - platforms { "native", "x32", "x64" } - end - - includedirs { - "../../" - } - - -- A project defines one build target - project "obj_sticher" - kind "ConsoleApp" - language "C++" - files { lib_sources, sources } - - configuration "Debug" - defines { "DEBUG" } -- -DDEBUG - flags { "Symbols" } - targetname "obj_sticher_debug" - - configuration "Release" - -- defines { "NDEBUG" } -- -NDEBUG - flags { "Symbols", "Optimize" } - targetname "obj_sticher" diff --git a/examples/skin_weight/Makefile b/examples/skin_weight/Makefile deleted file mode 100644 index 59e4e3c3..00000000 --- a/examples/skin_weight/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - clang++ -std=c++11 -o skin_weight -I../../ -g main.cc diff --git a/examples/skin_weight/README.md b/examples/skin_weight/README.md deleted file mode 100644 index 800d8472..00000000 --- a/examples/skin_weight/README.md +++ /dev/null @@ -1,7 +0,0 @@ -This example printf skin weight of vertex(`vw`). TinyObjLoader extension. - -## Run example - -``` -$ ./skin_weight ../../models/skin-weight.obj -``` diff --git a/examples/skin_weight/main.cc b/examples/skin_weight/main.cc deleted file mode 100644 index a3770afe..00000000 --- a/examples/skin_weight/main.cc +++ /dev/null @@ -1,103 +0,0 @@ -// -// g++ -g -std=c++11 main.cc -// -#define TINYOBJLOADER_IMPLEMENTATION -#include "tiny_obj_loader.h" - -#include -#include -#include -#include -#include -#include - -#include // C++11 - -#ifdef __clang__ -#pragma clang diagnostic push -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif -#endif - -static void ConstructVertexWeight( - const std::vector &vertices, - const std::vector &skin_weights, - std::vector *vertex_skin_weights) -{ - size_t num_vertices = vertices.size() / 3; - - vertex_skin_weights->resize(num_vertices); - - for (size_t i = 0; i < skin_weights.size(); i++) { - const tinyobj::skin_weight_t &skin = skin_weights[i]; - - assert(skin.vertex_id >= 0); - assert(skin.vertex_id < num_vertices); - - (*vertex_skin_weights)[skin.vertex_id] = skin; - } - - // now you can lookup i'th vertex skin weight by `vertex_skin_weights[i]` - - -} - -static bool TestLoadObj(const char* filename, const char* basepath = nullptr, - bool triangulate = true) { - std::cout << "Loading " << filename << std::endl; - - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename, - basepath, triangulate); - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - if (!ret) { - printf("Failed to load/parse .obj.\n"); - return false; - } - - std::vector vertex_skin_weights; - - ConstructVertexWeight( - attrib.vertices, - attrib.skin_weights, - &vertex_skin_weights); - - for (size_t v = 0; v < vertex_skin_weights.size(); v++) { - std::cout << "vertex[" << v << "] num_weights = " << vertex_skin_weights[v].weightValues.size() << "\n"; - for (size_t w = 0; w < vertex_skin_weights[v].weightValues.size(); w++) { - std::cout << " w[" << w << "] joint = " << vertex_skin_weights[v].weightValues[w].joint_id - << ", weight = " << vertex_skin_weights[v].weightValues[w].weight << "\n"; - } - } - - return true; -} - - -int main(int argc, char** argv) { - if (argc < 2) { - std::cerr << "Need input.obj\n"; - return EXIT_FAILURE; - } - - const char* basepath = nullptr; - if (argc > 2) { - basepath = argv[2]; - } - assert(true == TestLoadObj(argv[1], basepath)); - - return 0; -} diff --git a/examples/viewer/.gitignore b/examples/viewer/.gitignore deleted file mode 100644 index 378eac25..00000000 --- a/examples/viewer/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/examples/viewer/CMakeLists.txt b/examples/viewer/CMakeLists.txt deleted file mode 100644 index df3af268..00000000 --- a/examples/viewer/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# cmake -S . -B build && cmake --build build && build/app - -# cmake -S . -B build -G "Ninja" && cmake --build build && build/app -# cmake -S . -B build -G "CodeBlocks - Ninja" && cmake --build build && build/app -# cmake -S . -B build -G "Visual Studio 17 2022" && cmake --build build && build/app - -cmake_minimum_required(VERSION 3.18) -project( app VERSION 0.1 ) - -file(GLOB SOURCE_FILES "*.c*" ) -add_executable(app ${SOURCE_FILES}) - -find_package( OpenGL REQUIRED ) -find_package( glfw3 REQUIRED ) - -set(ADDITIONAL_LIBRARIES "") -if(WIN32) -set(ADDITIONAL_LIBRARIES winmm) -endif() - -set(GLEW_LIBRARY "") -if(UNIX) -set(GLEW_LIBRARY GLEW) -else() -find_package( glew REQUIRED ) -set(GLEW_LIBRARY GLEW::glew) -endif() - -target_link_libraries(${PROJECT_NAME} OpenGL::GL OpenGL::GLU glfw ${ADDITIONAL_LIBRARIES} ${GLEW_LIBRARY} ) diff --git a/examples/viewer/README.md b/examples/viewer/README.md deleted file mode 100644 index 76207bfa..00000000 --- a/examples/viewer/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Simple .obj viewer with glew + glfw3 + OpenGL - -## Requirements - -* premake5 -* glfw3 -* glew -* xcursor -* xinerama - -## Build on MaCOSX - -Install glfw3 and glew using brew. -Then, - - $ premake5 gmake - $ make - -## Build on Linux - -Set `PKG_CONFIG_PATH` or Edit path to glfw3 and glew in `premake4.lua` - -Then, - - $ premake5 gmake - $ make - -## Build on Windows. - -* Visual Studio 2013 -* Windows 64bit - * 32bit may work. - -Put glfw3 and glew library somewhere and replace include and lib path in `premake4.lua` - -Then, - - > premake5.exe vs2013 - -## TODO - -* [ ] Alpha texturing. -* [ ] Support per-face material. -* [ ] Use shader-based GL rendering. -* [ ] PBR shader support. diff --git a/examples/viewer/premake4.lua b/examples/viewer/premake4.lua deleted file mode 100644 index 4e1e54f0..00000000 --- a/examples/viewer/premake4.lua +++ /dev/null @@ -1,44 +0,0 @@ -solution "objview" - -- location ( "build" ) - configurations { "Release", "Debug" } - platforms {"native", "x64", "x32"} - - project "objview" - - kind "ConsoleApp" - language "C++" - files { "viewer.cc", "trackball.cc" } - includedirs { "./" } - includedirs { "../../" } - - configuration { "linux" } - linkoptions { "`pkg-config --libs glfw3`" } - links { "GL", "GLU", "m", "GLEW", "X11", "Xrandr", "Xinerama", "Xi", "Xxf86vm", "Xcursor", "dl" } - linkoptions { "-pthread" } - - configuration { "windows" } - -- Path to GLFW3 - includedirs { '../../../../local/glfw-3.1.2.bin.WIN64/include' } - libdirs { '../../../../local/glfw-3.1.2.bin.WIN64/lib-vc2013' } - -- Path to GLEW - includedirs { '../../../../local/glew-1.13.0/include' } - libdirs { '../../../../local/glew-1.13.0/lib/Release/x64' } - - links { "glfw3", "glew32", "gdi32", "winmm", "user32", "glu32","opengl32", "kernel32" } - defines { "_CRT_SECURE_NO_WARNINGS" } - - configuration { "macosx" } - includedirs { "/usr/local/include" } - buildoptions { "-Wno-deprecated-declarations" } - libdirs { "/usr/local/lib" } - links { "glfw3", "GLEW" } - linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" } - - configuration "Debug" - defines { "DEBUG" } - flags { "Symbols", "ExtraWarnings"} - - configuration "Release" - defines { "NDEBUG" } - flags { "Optimize", "ExtraWarnings"} - diff --git a/examples/viewer/stb_image.h b/examples/viewer/stb_image.h deleted file mode 100644 index d60371b9..00000000 --- a/examples/viewer/stb_image.h +++ /dev/null @@ -1,7897 +0,0 @@ -/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine Simon Breuss (16-bit PNM) - John-Mark Allen - Carmelo J Fdez-Aguera - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Eugene Golushkov Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko github:mosra - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE - - Jacko Dirks - - To add your name to the credits, pick a random blank space in the middle and fill it. - 80% of merge conflicts on stb PRs are due to people adding their name at the end - of the credits. -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// To query the width, height and component count of an image without having to -// decode the full file, you can use the stbi_info family of functions: -// -// int x,y,n,ok; -// ok = stbi_info(filename, &x, &y, &n); -// // returns ok=1 and sets x, y, n if image is a supported format, -// // 0 otherwise. -// -// Note that stb_image pervasively uses ints in its public API for sizes, -// including sizes of memory buffers. This is now part of the API and thus -// hard to change without causing breakage. As a result, the various image -// loaders all have certain limits on image size; these differ somewhat -// by format but generally boil down to either just under 2GB or just under -// 1GB. When the decoded image would be larger than this, stb_image decoding -// will fail. -// -// Additionally, stb_image will reject image files that have any of their -// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, -// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, -// the only way to have an image with such dimensions load correctly -// is for it to have a rather extreme aspect ratio. Either way, the -// assumption here is that such larger images are likely to be malformed -// or malicious. If you do need to load an image with individual dimensions -// larger than that, and it still fits in the overall size limit, you can -// #define STBI_MAX_DIMENSIONS on your own to be something larger. -// -// =========================================================================== -// -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// We optionally support converting iPhone-formatted PNGs (which store -// premultiplied BGRA) back to RGB, even though they're internally encoded -// differently. To enable this conversion, call -// stbi_convert_iphone_png_to_rgb(1). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// -// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater -// than that size (in either width or height) without further processing. -// This is to let programs in the wild set an upper bound to prevent -// denial-of-service attacks on untrusted data, as one could generate a -// valid image of gigantic dimensions and force stb_image to allocate a -// huge block of memory and spend disproportionate time decoding it. By -// default this is set to (1 << 24), which is 16777216, but that's still -// very big. - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -#include -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef STBIDEF -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// on most compilers (and ALL modern mainstream compilers) this is threadsafe -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// as above, but only applies to images loaded on the thread that calls the function -// this function is only available if your compiler supports thread-local variables; -// calling it will fail to link if your compiler doesn't -STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); -STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - -#ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local - #endif - - #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif - #endif -#endif - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#endif - -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif - -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -#ifdef _MSC_VER -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name -#else -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -#ifndef STBI_MAX_DIMENSIONS -#define STBI_MAX_DIMENSIONS (1 << 24) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__pnm_is16(stbi__context *s); -#endif - -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -#ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} -#endif - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load_global = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - // test the formats with a very explicit header first (at least a FOURCC - // or distinctive magic number first) - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - - // then the formats that can end up attempting to load with just 1 or 2 - // bytes matching expectations; these are prone to false positives, so - // try them later - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) - return 0; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} -#endif - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - z += (stbi__uint32)stbi__get16le(s) << 16; - return z; -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & (sgn - 1)); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * (1 << j->succ_low)); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * (1 << shift)); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios - // and I've never seen a non-corrupted JPEG file actually use them - for (i=0; i < s->img_n; ++i) { - if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); - if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // nothing to do if no components requested; check this now to avoid - // accessing uninitialized coutput[0] later - if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return stbi__errpuc("outofmem", "Out of memory"); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return stbi__err("outofmem", "Out of memory"); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - if (!j) return stbi__err("outofmem", "Out of memory"); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) -#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[STBI__ZNSYMS]; - stbi__uint16 value[STBI__ZNSYMS]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); -} - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ - } - stbi__fill_bits(a); - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - if (!final) return stbi__err("outofmem", "Out of memory"); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load_global = 0; -static int stbi__de_iphone_flag_global = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_global = flag_true_if_should_convert; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global -#define stbi__de_iphone_flag stbi__de_iphone_flag_global -#else -static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; -static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; - -STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; - stbi__unpremultiply_on_load_set = 1; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_local = flag_true_if_should_convert; - stbi__de_iphone_flag_set = 1; -} - -#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ - ? stbi__unpremultiply_on_load_local \ - : stbi__unpremultiply_on_load_global) -#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ - ? stbi__de_iphone_flag_local \ - : stbi__de_iphone_flag_global) -#endif // STBI_THREAD_LOCAL - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; -} stbi__bmp_data; - -static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) -{ - // BI_BITFIELDS specifies masks explicitly, don't override - if (compress == 3) - return 1; - - if (compress == 0) { - if (info->bpp == 16) { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } else if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - // otherwise, use defaults, which is all-0 - info->mr = info->mg = info->mb = info->ma = 0; - } - return 1; - } - return 0; // error -} - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes - if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - stbi__bmp_set_mask_defaults(info, compress); - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - // V4/V5 header - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs - stbi__bmp_set_mask_defaults(info, compress); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } - } - - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO - - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - if (!result) return stbi__errpuc("outofmem", "Out of memory"); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!g) return stbi__err("outofmem", "Out of memory"); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) -{ - STBI_FREE(g->out); - STBI_FREE(g->history); - STBI_FREE(g->background); - - if (out) STBI_FREE(out); - if (delays && *delays) STBI_FREE(*delays); - return stbi__errpuc("outofmem", "Out of memory"); -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - int out_size = 0; - int delays_size = 0; - - STBI_NOTUSED(out_size); - STBI_NOTUSED(delays_size); - - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (!tmp) - return stbi__load_gif_main_outofmem(&g, out, delays); - else { - out = (stbi_uc*) tmp; - out_size = layers * stride; - } - - if (delays) { - int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - if (!new_delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - *delays = new_delays; - delays_size = layers * sizeof(int); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (!out) - return stbi__load_gif_main_outofmem(&g, out, delays); - out_size = layers * stride; - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - if (!*delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - delays_size = layers * sizeof(int); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - if (p == NULL) { - stbi__rewind( s ); - return 0; - } - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - STBI_NOTUSED(stbi__get32be(s)); - STBI_NOTUSED(stbi__get32be(s)); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); - if (ri->bits_per_channel == 0) - return 0; - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - if (maxv > 65535) - return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); - else if (maxv > 255) - return 16; - else - return 8; -} - -static int stbi__pnm_is16(stbi__context *s) -{ - if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) - return 1; - return 0; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_is16(s)) return 1; - #endif - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -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. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/examples/viewer/trackball.cc b/examples/viewer/trackball.cc deleted file mode 100644 index 86ff3b3f..00000000 --- a/examples/viewer/trackball.cc +++ /dev/null @@ -1,292 +0,0 @@ -/* - * (c) Copyright 1993, 1994, Silicon Graphics, Inc. - * ALL RIGHTS RESERVED - * Permission to use, copy, modify, and distribute this software for - * any purpose and without fee is hereby granted, provided that the above - * copyright notice appear in all copies and that both the copyright notice - * and this permission notice appear in supporting documentation, and that - * the name of Silicon Graphics, Inc. not be used in advertising - * or publicity pertaining to distribution of the software without specific, - * written prior permission. - * - * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" - * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON - * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, - * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY - * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, - * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF - * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN - * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE - * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. - * - * US Government Users Restricted Rights - * Use, duplication, or disclosure by the Government is subject to - * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph - * (c)(1)(ii) of the Rights in Technical Data and Computer Software - * clause at DFARS 252.227-7013 and/or in similar or successor - * clauses in the FAR or the DOD or NASA FAR Supplement. - * Unpublished-- rights reserved under the copyright laws of the - * United States. Contractor/manufacturer is Silicon Graphics, - * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. - * - * OpenGL(TM) is a trademark of Silicon Graphics, Inc. - */ -/* - * Trackball code: - * - * Implementation of a virtual trackball. - * Implemented by Gavin Bell, lots of ideas from Thant Tessman and - * the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129. - * - * Vector manip code: - * - * Original code from: - * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli - * - * Much mucking with by: - * Gavin Bell - */ -#include -#include "trackball.h" - -/* - * This size should really be based on the distance from the center of - * rotation to the point on the object underneath the mouse. That - * point would then track the mouse as closely as possible. This is a - * simple example, though, so that is left as an Exercise for the - * Programmer. - */ -#define TRACKBALLSIZE (0.8) - -/* - * Local function prototypes (not defined in trackball.h) - */ -static float tb_project_to_sphere(float, float, float); -static void normalize_quat(float[4]); - -static void vzero(float *v) { - v[0] = 0.0; - v[1] = 0.0; - v[2] = 0.0; -} - -static void vset(float *v, float x, float y, float z) { - v[0] = x; - v[1] = y; - v[2] = z; -} - -static void vsub(const float *src1, const float *src2, float *dst) { - dst[0] = src1[0] - src2[0]; - dst[1] = src1[1] - src2[1]; - dst[2] = src1[2] - src2[2]; -} - -static void vcopy(const float *v1, float *v2) { - register int i; - for (i = 0; i < 3; i++) - v2[i] = v1[i]; -} - -static void vcross(const float *v1, const float *v2, float *cross) { - float temp[3]; - - temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]); - temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]); - temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]); - vcopy(temp, cross); -} - -static float vlength(const float *v) { - return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); -} - -static void vscale(float *v, float div) { - v[0] *= div; - v[1] *= div; - v[2] *= div; -} - -static void vnormal(float *v) { vscale(v, 1.0 / vlength(v)); } - -static float vdot(const float *v1, const float *v2) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; -} - -static void vadd(const float *src1, const float *src2, float *dst) { - dst[0] = src1[0] + src2[0]; - dst[1] = src1[1] + src2[1]; - dst[2] = src1[2] + src2[2]; -} - -/* - * Ok, simulate a track-ball. Project the points onto the virtual - * trackball, then figure out the axis of rotation, which is the cross - * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) - * Note: This is a deformed trackball-- is a trackball in the center, - * but is deformed into a hyperbolic sheet of rotation away from the - * center. This particular function was chosen after trying out - * several variations. - * - * It is assumed that the arguments to this routine are in the range - * (-1.0 ... 1.0) - */ -void trackball(float q[4], float p1x, float p1y, float p2x, float p2y) { - float a[3]; /* Axis of rotation */ - float phi; /* how much to rotate about axis */ - float p1[3], p2[3], d[3]; - float t; - - if (p1x == p2x && p1y == p2y) { - /* Zero rotation */ - vzero(q); - q[3] = 1.0; - return; - } - - /* - * First, figure out z-coordinates for projection of P1 and P2 to - * deformed sphere - */ - vset(p1, p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y)); - vset(p2, p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y)); - - /* - * Now, we want the cross product of P1 and P2 - */ - vcross(p2, p1, a); - - /* - * Figure out how much to rotate around that axis. - */ - vsub(p1, p2, d); - t = vlength(d) / (2.0 * TRACKBALLSIZE); - - /* - * Avoid problems with out-of-control values... - */ - if (t > 1.0) - t = 1.0; - if (t < -1.0) - t = -1.0; - phi = 2.0 * asin(t); - - axis_to_quat(a, phi, q); -} - -/* - * Given an axis and angle, compute quaternion. - */ -void axis_to_quat(float a[3], float phi, float q[4]) { - vnormal(a); - vcopy(a, q); - vscale(q, sin(phi / 2.0)); - q[3] = cos(phi / 2.0); -} - -/* - * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet - * if we are away from the center of the sphere. - */ -static float tb_project_to_sphere(float r, float x, float y) { - float d, t, z; - - d = sqrt(x * x + y * y); - if (d < r * 0.70710678118654752440) { /* Inside sphere */ - z = sqrt(r * r - d * d); - } else { /* On hyperbola */ - t = r / 1.41421356237309504880; - z = t * t / d; - } - return z; -} - -/* - * Given two rotations, e1 and e2, expressed as quaternion rotations, - * figure out the equivalent single rotation and stuff it into dest. - * - * This routine also normalizes the result every RENORMCOUNT times it is - * called, to keep error from creeping in. - * - * NOTE: This routine is written so that q1 or q2 may be the same - * as dest (or each other). - */ - -#define RENORMCOUNT 97 - -void add_quats(float q1[4], float q2[4], float dest[4]) { - static int count = 0; - float t1[4], t2[4], t3[4]; - float tf[4]; - - vcopy(q1, t1); - vscale(t1, q2[3]); - - vcopy(q2, t2); - vscale(t2, q1[3]); - - vcross(q2, q1, t3); - vadd(t1, t2, tf); - vadd(t3, tf, tf); - tf[3] = q1[3] * q2[3] - vdot(q1, q2); - - dest[0] = tf[0]; - dest[1] = tf[1]; - dest[2] = tf[2]; - dest[3] = tf[3]; - - if (++count > RENORMCOUNT) { - count = 0; - normalize_quat(dest); - } -} - -/* - * Quaternions always obey: a^2 + b^2 + c^2 + d^2 = 1.0 - * If they don't add up to 1.0, dividing by their magnitued will - * renormalize them. - * - * Note: See the following for more information on quaternions: - * - * - Shoemake, K., Animating rotation with quaternion curves, Computer - * Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985. - * - Pletinckx, D., Quaternion calculus as a basic tool in computer - * graphics, The Visual Computer 5, 2-13, 1989. - */ -static void normalize_quat(float q[4]) { - int i; - float mag; - - mag = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); - for (i = 0; i < 4; i++) - q[i] /= mag; -} - -/* - * Build a rotation matrix, given a quaternion rotation. - * - */ -void build_rotmatrix(float m[4][4], const float q[4]) { - m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]); - m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]); - m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]); - m[0][3] = 0.0; - - m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]); - m[1][1] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]); - m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]); - m[1][3] = 0.0; - - m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]); - m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]); - m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]); - m[2][3] = 0.0; - - m[3][0] = 0.0; - m[3][1] = 0.0; - m[3][2] = 0.0; - m[3][3] = 1.0; -} diff --git a/examples/viewer/trackball.h b/examples/viewer/trackball.h deleted file mode 100644 index b1f94374..00000000 --- a/examples/viewer/trackball.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * (c) Copyright 1993, 1994, Silicon Graphics, Inc. - * ALL RIGHTS RESERVED - * Permission to use, copy, modify, and distribute this software for - * any purpose and without fee is hereby granted, provided that the above - * copyright notice appear in all copies and that both the copyright notice - * and this permission notice appear in supporting documentation, and that - * the name of Silicon Graphics, Inc. not be used in advertising - * or publicity pertaining to distribution of the software without specific, - * written prior permission. - * - * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" - * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON - * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, - * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY - * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, - * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF - * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN - * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE - * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. - * - * US Government Users Restricted Rights - * Use, duplication, or disclosure by the Government is subject to - * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph - * (c)(1)(ii) of the Rights in Technical Data and Computer Software - * clause at DFARS 252.227-7013 and/or in similar or successor - * clauses in the FAR or the DOD or NASA FAR Supplement. - * Unpublished-- rights reserved under the copyright laws of the - * United States. Contractor/manufacturer is Silicon Graphics, - * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. - * - * OpenGL(TM) is a trademark of Silicon Graphics, Inc. - */ -/* - * trackball.h - * A virtual trackball implementation - * Written by Gavin Bell for Silicon Graphics, November 1988. - */ - -/* - * Pass the x and y coordinates of the last and current positions of - * the mouse, scaled so they are from (-1.0 ... 1.0). - * - * The resulting rotation is returned as a quaternion rotation in the - * first paramater. - */ -void trackball(float q[4], float p1x, float p1y, float p2x, float p2y); - -void negate_quat(float *q, float *qn); - -/* - * Given two quaternions, add them together to get a third quaternion. - * Adding quaternions to get a compound rotation is analagous to adding - * translations to get a compound translation. When incrementally - * adding rotations, the first argument here should be the new - * rotation, the second and third the total rotation (which will be - * over-written with the resulting new total rotation). - */ -void add_quats(float *q1, float *q2, float *dest); - -/* - * A useful function, builds a rotation matrix in Matrix based on - * given quaternion. - */ -void build_rotmatrix(float m[4][4], const float q[4]); - -/* - * This function computes a quaternion based on an axis (defined by - * the given vector) and an angle about which to rotate. The angle is - * expressed in radians. The result is put into the third argument. - */ -void axis_to_quat(float a[3], float phi, float q[4]); diff --git a/examples/viewer/viewer.cc b/examples/viewer/viewer.cc deleted file mode 100644 index 875cf181..00000000 --- a/examples/viewer/viewer.cc +++ /dev/null @@ -1,1085 +0,0 @@ -// -// Simple .obj viewer(vertex only) -// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef __APPLE__ -#include -#else -#include -#endif - -#include - -#define TINYOBJLOADER_IMPLEMENTATION -// TINYOBJLOADER_USE_MAPBOX_EARCUT: Enable better triangulation. Requires C++11 -//#define TINYOBJLOADER_USE_MAPBOX_EARCUT -#include "../../tiny_obj_loader.h" - -#include "trackball.h" - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Weverything" -#endif - -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#ifdef _WIN32 -#ifdef __cplusplus -extern "C" { -#endif -#include - -#ifdef max -#undef max -#endif - -#ifdef min -#undef min -#endif - -#include -#ifdef __cplusplus -} -#endif -#pragma comment(lib, "winmm.lib") -#else -#if defined(__unix__) || defined(__APPLE__) -#include -#else -#include -#endif -#endif - -class timerutil { - public: -#ifdef _WIN32 - typedef DWORD time_t; - - timerutil() { ::timeBeginPeriod(1); } - ~timerutil() { ::timeEndPeriod(1); } - - void start() { t_[0] = ::timeGetTime(); } - void end() { t_[1] = ::timeGetTime(); } - - time_t sec() { return (time_t)((t_[1] - t_[0]) / 1000); } - time_t msec() { return (time_t)((t_[1] - t_[0])); } - time_t usec() { return (time_t)((t_[1] - t_[0]) * 1000); } - time_t current() { return ::timeGetTime(); } - -#else -#if defined(__unix__) || defined(__APPLE__) - typedef unsigned long int time_t; - - void start() { gettimeofday(tv + 0, &tz); } - void end() { gettimeofday(tv + 1, &tz); } - - time_t sec() { return (time_t)(tv[1].tv_sec - tv[0].tv_sec); } - time_t msec() { - return this->sec() * 1000 + - (time_t)((tv[1].tv_usec - tv[0].tv_usec) / 1000); - } - time_t usec() { - return this->sec() * 1000000 + (time_t)(tv[1].tv_usec - tv[0].tv_usec); - } - time_t current() { - struct timeval t; - gettimeofday(&t, NULL); - return (time_t)(t.tv_sec * 1000 + t.tv_usec); - } - -#else // C timer - // using namespace std; - typedef clock_t time_t; - - void start() { t_[0] = clock(); } - void end() { t_[1] = clock(); } - - time_t sec() { return (time_t)((t_[1] - t_[0]) / CLOCKS_PER_SEC); } - time_t msec() { return (time_t)((t_[1] - t_[0]) * 1000 / CLOCKS_PER_SEC); } - time_t usec() { return (time_t)((t_[1] - t_[0]) * 1000000 / CLOCKS_PER_SEC); } - time_t current() { return (time_t)clock(); } - -#endif -#endif - - private: -#ifdef _WIN32 - DWORD t_[2]; -#else -#if defined(__unix__) || defined(__APPLE__) - struct timeval tv[2]; - struct timezone tz; -#else - time_t t_[2]; -#endif -#endif -}; - -typedef struct { - GLuint vb_id; // vertex buffer id - int numTriangles; - size_t material_id; -} DrawObject; - -std::vector gDrawObjects; - -int width = 768; -int height = 768; - -double prevMouseX, prevMouseY; -bool mouseLeftPressed; -bool mouseMiddlePressed; -bool mouseRightPressed; -float curr_quat[4]; -float prev_quat[4]; -float eye[3], lookat[3], up[3]; -bool g_show_wire = true; -bool g_cull_face = false; - -GLFWwindow* window; - -static std::string GetBaseDir(const std::string& filepath) { - if (filepath.find_last_of("/\\") != std::string::npos) - return filepath.substr(0, filepath.find_last_of("/\\")); - return ""; -} - -static bool FileExists(const std::string& abs_filename) { - bool ret; - FILE* fp = fopen(abs_filename.c_str(), "rb"); - if (fp) { - ret = true; - fclose(fp); - } else { - ret = false; - } - - return ret; -} - -static void CheckErrors(std::string desc) { - GLenum e = glGetError(); - if (e != GL_NO_ERROR) { - fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc.c_str(), e, e); - exit(20); - } -} - -static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) { - float v10[3]; - v10[0] = v1[0] - v0[0]; - v10[1] = v1[1] - v0[1]; - v10[2] = v1[2] - v0[2]; - - float v20[3]; - v20[0] = v2[0] - v0[0]; - v20[1] = v2[1] - v0[1]; - v20[2] = v2[2] - v0[2]; - - N[0] = v10[1] * v20[2] - v10[2] * v20[1]; - N[1] = v10[2] * v20[0] - v10[0] * v20[2]; - N[2] = v10[0] * v20[1] - v10[1] * v20[0]; - - float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2]; - if (len2 > 0.0f) { - float len = sqrtf(len2); - - N[0] /= len; - N[1] /= len; - N[2] /= len; - } -} - -namespace // Local utility functions -{ -struct vec3 { - float v[3]; - vec3() { - v[0] = 0.0f; - v[1] = 0.0f; - v[2] = 0.0f; - } -}; - -void normalizeVector(vec3 &v) { - float len2 = v.v[0] * v.v[0] + v.v[1] * v.v[1] + v.v[2] * v.v[2]; - if (len2 > 0.0f) { - float len = sqrtf(len2); - - v.v[0] /= len; - v.v[1] /= len; - v.v[2] /= len; - } -} - -/* - There are 2 approaches here to automatically generating vertex normals. The - old approach (computeSmoothingNormals) doesn't handle multiple smoothing - groups properly, as it effectively merges all smoothing groups present in the - OBJ file into a single group. However, it can be useful when the OBJ file - contains vertex normals which you want to use, but is missing some, as it - will attempt to fill in the missing normals without generating new shapes. - - The new approach (computeSmoothingShapes, computeAllSmoothingNormals) handles - multiple smoothing groups but is a bit more complicated, as handling this - correctly requires potentially generating new vertices (and hence shapes). - In general, the new approach is most useful if your OBJ file is missing - vertex normals entirely, and instead relies on smoothing groups to correctly - generate them as a pre-process. That said, it can be used to reliably - generate vertex normals in the general case. If you want to always generate - normals in this way, simply force set regen_all_normals to true below. By - default, it's only true when there are no vertex normals present. One other - thing to keep in mind is that the statistics printed apply to the model - *prior* to shape regeneration, so you'd need to print them again if you want - to see the new statistics. - - TODO(syoyo): import computeSmoothingShapes and computeAllSmoothingNormals to - tinyobjloader as utility functions. -*/ - -// Check if `mesh_t` contains smoothing group id. -bool hasSmoothingGroup(const tinyobj::shape_t& shape) -{ - for (size_t i = 0; i < shape.mesh.smoothing_group_ids.size(); i++) { - if (shape.mesh.smoothing_group_ids[i] > 0) { - return true; - } - } - return false; -} - -void computeSmoothingNormals(const tinyobj::attrib_t& attrib, const tinyobj::shape_t& shape, - std::map& smoothVertexNormals) { - smoothVertexNormals.clear(); - std::map::iterator iter; - - for (size_t f = 0; f < shape.mesh.indices.size() / 3; f++) { - // Get the three indexes of the face (all faces are triangular) - tinyobj::index_t idx0 = shape.mesh.indices[3 * f + 0]; - tinyobj::index_t idx1 = shape.mesh.indices[3 * f + 1]; - tinyobj::index_t idx2 = shape.mesh.indices[3 * f + 2]; - - // Get the three vertex indexes and coordinates - int vi[3]; // indexes - float v[3][3]; // coordinates - - for (int k = 0; k < 3; k++) { - vi[0] = idx0.vertex_index; - vi[1] = idx1.vertex_index; - vi[2] = idx2.vertex_index; - assert(vi[0] >= 0); - assert(vi[1] >= 0); - assert(vi[2] >= 0); - - v[0][k] = attrib.vertices[3 * vi[0] + k]; - v[1][k] = attrib.vertices[3 * vi[1] + k]; - v[2][k] = attrib.vertices[3 * vi[2] + k]; - } - - // Compute the normal of the face - float normal[3]; - CalcNormal(normal, v[0], v[1], v[2]); - - // Add the normal to the three vertexes - for (size_t i = 0; i < 3; ++i) { - iter = smoothVertexNormals.find(vi[i]); - if (iter != smoothVertexNormals.end()) { - // add - iter->second.v[0] += normal[0]; - iter->second.v[1] += normal[1]; - iter->second.v[2] += normal[2]; - } else { - smoothVertexNormals[vi[i]].v[0] = normal[0]; - smoothVertexNormals[vi[i]].v[1] = normal[1]; - smoothVertexNormals[vi[i]].v[2] = normal[2]; - } - } - - } // f - - // Normalize the normals, that is, make them unit vectors - for (iter = smoothVertexNormals.begin(); iter != smoothVertexNormals.end(); - iter++) { - normalizeVector(iter->second); - } - -} // computeSmoothingNormals - -static void computeAllSmoothingNormals(tinyobj::attrib_t& attrib, - std::vector& shapes) { - vec3 p[3]; - for (size_t s = 0, slen = shapes.size(); s < slen; ++s) { - const tinyobj::shape_t& shape(shapes[s]); - size_t facecount = shape.mesh.num_face_vertices.size(); - assert(shape.mesh.smoothing_group_ids.size()); - - for (size_t f = 0, flen = facecount; f < flen; ++f) { - for (unsigned int v = 0; v < 3; ++v) { - tinyobj::index_t idx = shape.mesh.indices[3*f + v]; - assert(idx.vertex_index != -1); - p[v].v[0] = attrib.vertices[3*idx.vertex_index ]; - p[v].v[1] = attrib.vertices[3*idx.vertex_index+1]; - p[v].v[2] = attrib.vertices[3*idx.vertex_index+2]; - } - - // cross(p[1] - p[0], p[2] - p[0]) - float nx = (p[1].v[1] - p[0].v[1]) * (p[2].v[2] - p[0].v[2]) - - (p[1].v[2] - p[0].v[2]) * (p[2].v[1] - p[0].v[1]); - float ny = (p[1].v[2] - p[0].v[2]) * (p[2].v[0] - p[0].v[0]) - - (p[1].v[0] - p[0].v[0]) * (p[2].v[2] - p[0].v[2]); - float nz = (p[1].v[0] - p[0].v[0]) * (p[2].v[1] - p[0].v[1]) - - (p[1].v[1] - p[0].v[1]) * (p[2].v[0] - p[0].v[0]); - - // Don't normalize here. - for (unsigned int v = 0; v < 3; ++v) { - tinyobj::index_t idx = shape.mesh.indices[3*f + v]; - attrib.normals[3*idx.normal_index ] += nx; - attrib.normals[3*idx.normal_index+1] += ny; - attrib.normals[3*idx.normal_index+2] += nz; - } - } - } - - assert(attrib.normals.size() % 3 == 0); - for (size_t i = 0, nlen = attrib.normals.size() / 3; i < nlen; ++i) { - tinyobj::real_t& nx = attrib.normals[3*i ]; - tinyobj::real_t& ny = attrib.normals[3*i+1]; - tinyobj::real_t& nz = attrib.normals[3*i+2]; - tinyobj::real_t len = sqrtf(nx*nx + ny*ny + nz*nz); - tinyobj::real_t scale = len == 0 ? 0 : 1 / len; - nx *= scale; - ny *= scale; - nz *= scale; - } -} - -static void computeSmoothingShape(tinyobj::attrib_t& inattrib, tinyobj::shape_t& inshape, - std::vector>& sortedids, - unsigned int idbegin, unsigned int idend, - std::vector& outshapes, - tinyobj::attrib_t& outattrib) { - unsigned int sgroupid = sortedids[idbegin].first; - bool hasmaterials = inshape.mesh.material_ids.size(); - // Make a new shape from the set of faces in the range [idbegin, idend). - outshapes.emplace_back(); - tinyobj::shape_t& outshape = outshapes.back(); - outshape.name = inshape.name; - // Skip lines and points. - - std::unordered_map remap; - for (unsigned int id = idbegin; id < idend; ++id) { - unsigned int face = sortedids[id].second; - - outshape.mesh.num_face_vertices.push_back(3); // always triangles - if (hasmaterials) - outshape.mesh.material_ids.push_back(inshape.mesh.material_ids[face]); - outshape.mesh.smoothing_group_ids.push_back(sgroupid); - // Skip tags. - - for (unsigned int v = 0; v < 3; ++v) { - tinyobj::index_t inidx = inshape.mesh.indices[3*face + v], outidx; - assert(inidx.vertex_index != -1); - auto iter = remap.find(inidx.vertex_index); - // Smooth group 0 disables smoothing so no shared vertices in that case. - if (sgroupid && iter != remap.end()) { - outidx.vertex_index = (*iter).second; - outidx.normal_index = outidx.vertex_index; - outidx.texcoord_index = (inidx.texcoord_index == -1) ? -1 : outidx.vertex_index; - } - else { - assert(outattrib.vertices.size() % 3 == 0); - unsigned int offset = static_cast(outattrib.vertices.size() / 3); - outidx.vertex_index = outidx.normal_index = offset; - outidx.texcoord_index = (inidx.texcoord_index == -1) ? -1 : offset; - outattrib.vertices.push_back(inattrib.vertices[3*inidx.vertex_index ]); - outattrib.vertices.push_back(inattrib.vertices[3*inidx.vertex_index+1]); - outattrib.vertices.push_back(inattrib.vertices[3*inidx.vertex_index+2]); - outattrib.normals.push_back(0.0f); - outattrib.normals.push_back(0.0f); - outattrib.normals.push_back(0.0f); - if (inidx.texcoord_index != -1) { - outattrib.texcoords.push_back(inattrib.texcoords[2*inidx.texcoord_index ]); - outattrib.texcoords.push_back(inattrib.texcoords[2*inidx.texcoord_index+1]); - } - remap[inidx.vertex_index] = offset; - } - outshape.mesh.indices.push_back(outidx); - } - } -} - -static void computeSmoothingShapes(tinyobj::attrib_t &inattrib, - std::vector& inshapes, - std::vector& outshapes, - tinyobj::attrib_t& outattrib) { - for (size_t s = 0, slen = inshapes.size() ; s < slen; ++s) { - tinyobj::shape_t& inshape = inshapes[s]; - - unsigned int numfaces = static_cast(inshape.mesh.smoothing_group_ids.size()); - assert(numfaces); - std::vector> sortedids(numfaces); - for (unsigned int i = 0; i < numfaces; ++i) - sortedids[i] = std::make_pair(inshape.mesh.smoothing_group_ids[i], i); - sort(sortedids.begin(), sortedids.end()); - - unsigned int activeid = sortedids[0].first; - unsigned int id = activeid, idbegin = 0, idend = 0; - // Faces are now bundled by smoothing group id, create shapes from these. - while (idbegin < numfaces) { - while (activeid == id && ++idend < numfaces) - id = sortedids[idend].first; - computeSmoothingShape(inattrib, inshape, sortedids, idbegin, idend, - outshapes, outattrib); - activeid = id; - idbegin = idend; - } - } -} - -} // namespace - -static bool LoadObjAndConvert(float bmin[3], float bmax[3], - std::vector* drawObjects, - std::vector& materials, - std::map& textures, - const char* filename) { - tinyobj::attrib_t inattrib; - std::vector inshapes; - - timerutil tm; - - tm.start(); - - std::string base_dir = GetBaseDir(filename); - if (base_dir.empty()) { - base_dir = "."; - } -#ifdef _WIN32 - base_dir += "\\"; -#else - base_dir += "/"; -#endif - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&inattrib, &inshapes, &materials, &warn, &err, filename, - base_dir.c_str()); - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - if (!err.empty()) { - std::cerr << err << std::endl; - } - - tm.end(); - - if (!ret) { - std::cerr << "Failed to load " << filename << std::endl; - return false; - } - - printf("Parsing time: %d [ms]\n", (int)tm.msec()); - - printf("# of vertices = %d\n", (int)(inattrib.vertices.size()) / 3); - printf("# of normals = %d\n", (int)(inattrib.normals.size()) / 3); - printf("# of texcoords = %d\n", (int)(inattrib.texcoords.size()) / 2); - printf("# of materials = %d\n", (int)materials.size()); - printf("# of shapes = %d\n", (int)inshapes.size()); - - // Append `default` material - materials.push_back(tinyobj::material_t()); - - for (size_t i = 0; i < materials.size(); i++) { - printf("material[%d].diffuse_texname = %s\n", int(i), - materials[i].diffuse_texname.c_str()); - } - - // Load diffuse textures - { - for (size_t m = 0; m < materials.size(); m++) { - tinyobj::material_t* mp = &materials[m]; - - if (mp->diffuse_texname.length() > 0) { - // Only load the texture if it is not already loaded - if (textures.find(mp->diffuse_texname) == textures.end()) { - GLuint texture_id; - int w, h; - int comp; - - std::string texture_filename = mp->diffuse_texname; - if (!FileExists(texture_filename)) { - // Append base dir. - texture_filename = base_dir + mp->diffuse_texname; - if (!FileExists(texture_filename)) { - std::cerr << "Unable to find file: " << mp->diffuse_texname - << std::endl; - exit(1); - } - } - - unsigned char* image = - stbi_load(texture_filename.c_str(), &w, &h, &comp, STBI_default); - if (!image) { - std::cerr << "Unable to load texture: " << texture_filename - << std::endl; - exit(1); - } - std::cout << "Loaded texture: " << texture_filename << ", w = " << w - << ", h = " << h << ", comp = " << comp << std::endl; - - glGenTextures(1, &texture_id); - glBindTexture(GL_TEXTURE_2D, texture_id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (comp == 3) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, - GL_UNSIGNED_BYTE, image); - } else if (comp == 4) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, - GL_UNSIGNED_BYTE, image); - } else { - assert(0); // TODO - } - glBindTexture(GL_TEXTURE_2D, 0); - stbi_image_free(image); - textures.insert(std::make_pair(mp->diffuse_texname, texture_id)); - } - } - } - } - - bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); - bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); - - bool regen_all_normals = inattrib.normals.size() == 0; - tinyobj::attrib_t outattrib; - std::vector outshapes; - if (regen_all_normals) { - computeSmoothingShapes(inattrib, inshapes, outshapes, outattrib); - computeAllSmoothingNormals(outattrib, outshapes); - } - - std::vector& shapes = regen_all_normals ? outshapes : inshapes; - tinyobj::attrib_t& attrib = regen_all_normals ? outattrib : inattrib; - - { - for (size_t s = 0; s < shapes.size(); s++) { - DrawObject o; - std::vector buffer; // pos(3float), normal(3float), color(3float) - - // Check for smoothing group and compute smoothing normals - std::map smoothVertexNormals; - if (!regen_all_normals && (hasSmoothingGroup(shapes[s]) > 0)) { - std::cout << "Compute smoothingNormal for shape [" << s << "]" << std::endl; - computeSmoothingNormals(attrib, shapes[s], smoothVertexNormals); - } - - for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) { - tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0]; - tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1]; - tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2]; - - int current_material_id = shapes[s].mesh.material_ids[f]; - - if ((current_material_id < 0) || - (current_material_id >= static_cast(materials.size()))) { - // Invaid material ID. Use default material. - current_material_id = - materials.size() - - 1; // Default material is added to the last item in `materials`. - } - // if (current_material_id >= materials.size()) { - // std::cerr << "Invalid material index: " << current_material_id << - // std::endl; - //} - // - float diffuse[3]; - for (size_t i = 0; i < 3; i++) { - diffuse[i] = materials[current_material_id].diffuse[i]; - } - float tc[3][2]; - if (attrib.texcoords.size() > 0) { - if ((idx0.texcoord_index < 0) || (idx1.texcoord_index < 0) || - (idx2.texcoord_index < 0)) { - // face does not contain valid uv index. - tc[0][0] = 0.0f; - tc[0][1] = 0.0f; - tc[1][0] = 0.0f; - tc[1][1] = 0.0f; - tc[2][0] = 0.0f; - tc[2][1] = 0.0f; - } else { - assert(attrib.texcoords.size() > - size_t(2 * idx0.texcoord_index + 1)); - assert(attrib.texcoords.size() > - size_t(2 * idx1.texcoord_index + 1)); - assert(attrib.texcoords.size() > - size_t(2 * idx2.texcoord_index + 1)); - - // Flip Y coord. - tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index]; - tc[0][1] = 1.0f - attrib.texcoords[2 * idx0.texcoord_index + 1]; - tc[1][0] = attrib.texcoords[2 * idx1.texcoord_index]; - tc[1][1] = 1.0f - attrib.texcoords[2 * idx1.texcoord_index + 1]; - tc[2][0] = attrib.texcoords[2 * idx2.texcoord_index]; - tc[2][1] = 1.0f - attrib.texcoords[2 * idx2.texcoord_index + 1]; - } - } else { - tc[0][0] = 0.0f; - tc[0][1] = 0.0f; - tc[1][0] = 0.0f; - tc[1][1] = 0.0f; - tc[2][0] = 0.0f; - tc[2][1] = 0.0f; - } - - float v[3][3]; - for (int k = 0; k < 3; k++) { - int f0 = idx0.vertex_index; - int f1 = idx1.vertex_index; - int f2 = idx2.vertex_index; - assert(f0 >= 0); - assert(f1 >= 0); - assert(f2 >= 0); - - v[0][k] = attrib.vertices[3 * f0 + k]; - v[1][k] = attrib.vertices[3 * f1 + k]; - v[2][k] = attrib.vertices[3 * f2 + k]; - bmin[k] = std::min(v[0][k], bmin[k]); - bmin[k] = std::min(v[1][k], bmin[k]); - bmin[k] = std::min(v[2][k], bmin[k]); - bmax[k] = std::max(v[0][k], bmax[k]); - bmax[k] = std::max(v[1][k], bmax[k]); - bmax[k] = std::max(v[2][k], bmax[k]); - } - - float n[3][3]; - { - bool invalid_normal_index = false; - if (attrib.normals.size() > 0) { - int nf0 = idx0.normal_index; - int nf1 = idx1.normal_index; - int nf2 = idx2.normal_index; - - if ((nf0 < 0) || (nf1 < 0) || (nf2 < 0)) { - // normal index is missing from this face. - invalid_normal_index = true; - } else { - for (int k = 0; k < 3; k++) { - assert(size_t(3 * nf0 + k) < attrib.normals.size()); - assert(size_t(3 * nf1 + k) < attrib.normals.size()); - assert(size_t(3 * nf2 + k) < attrib.normals.size()); - n[0][k] = attrib.normals[3 * nf0 + k]; - n[1][k] = attrib.normals[3 * nf1 + k]; - n[2][k] = attrib.normals[3 * nf2 + k]; - } - } - } else { - invalid_normal_index = true; - } - - if (invalid_normal_index && !smoothVertexNormals.empty()) { - // Use smoothing normals - int f0 = idx0.vertex_index; - int f1 = idx1.vertex_index; - int f2 = idx2.vertex_index; - - if (f0 >= 0 && f1 >= 0 && f2 >= 0) { - n[0][0] = smoothVertexNormals[f0].v[0]; - n[0][1] = smoothVertexNormals[f0].v[1]; - n[0][2] = smoothVertexNormals[f0].v[2]; - - n[1][0] = smoothVertexNormals[f1].v[0]; - n[1][1] = smoothVertexNormals[f1].v[1]; - n[1][2] = smoothVertexNormals[f1].v[2]; - - n[2][0] = smoothVertexNormals[f2].v[0]; - n[2][1] = smoothVertexNormals[f2].v[1]; - n[2][2] = smoothVertexNormals[f2].v[2]; - - invalid_normal_index = false; - } - } - - if (invalid_normal_index) { - // compute geometric normal - CalcNormal(n[0], v[0], v[1], v[2]); - n[1][0] = n[0][0]; - n[1][1] = n[0][1]; - n[1][2] = n[0][2]; - n[2][0] = n[0][0]; - n[2][1] = n[0][1]; - n[2][2] = n[0][2]; - } - } - - for (int k = 0; k < 3; k++) { - buffer.push_back(v[k][0]); - buffer.push_back(v[k][1]); - buffer.push_back(v[k][2]); - buffer.push_back(n[k][0]); - buffer.push_back(n[k][1]); - buffer.push_back(n[k][2]); - // Combine normal and diffuse to get color. - float normal_factor = 0.2; - float diffuse_factor = 1 - normal_factor; - float c[3] = {n[k][0] * normal_factor + diffuse[0] * diffuse_factor, - n[k][1] * normal_factor + diffuse[1] * diffuse_factor, - n[k][2] * normal_factor + diffuse[2] * diffuse_factor}; - float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]; - if (len2 > 0.0f) { - float len = sqrtf(len2); - - c[0] /= len; - c[1] /= len; - c[2] /= len; - } - buffer.push_back(c[0] * 0.5 + 0.5); - buffer.push_back(c[1] * 0.5 + 0.5); - buffer.push_back(c[2] * 0.5 + 0.5); - - buffer.push_back(tc[k][0]); - buffer.push_back(tc[k][1]); - } - } - - o.vb_id = 0; - o.numTriangles = 0; - - // OpenGL viewer does not support texturing with per-face material. - if (shapes[s].mesh.material_ids.size() > 0 && - shapes[s].mesh.material_ids.size() > s) { - o.material_id = shapes[s].mesh.material_ids[0]; // use the material ID - // of the first face. - } else { - o.material_id = materials.size() - 1; // = ID for default material. - } - printf("shape[%d] material_id %d\n", int(s), int(o.material_id)); - - if (buffer.size() > 0) { - glGenBuffers(1, &o.vb_id); - glBindBuffer(GL_ARRAY_BUFFER, o.vb_id); - glBufferData(GL_ARRAY_BUFFER, buffer.size() * sizeof(float), - &buffer.at(0), GL_STATIC_DRAW); - o.numTriangles = buffer.size() / (3 + 3 + 3 + 2) / - 3; // 3:vtx, 3:normal, 3:col, 2:texcoord - - printf("shape[%d] # of triangles = %d\n", static_cast(s), - o.numTriangles); - } - - drawObjects->push_back(o); - } - } - - printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]); - printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]); - - return true; -} - -static void reshapeFunc(GLFWwindow* window, int w, int h) { - int fb_w, fb_h; - // Get actual framebuffer size. - glfwGetFramebufferSize(window, &fb_w, &fb_h); - - glViewport(0, 0, fb_w, fb_h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(45.0, (float)w / (float)h, 0.01f, 100.0f); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - width = w; - height = h; -} - -static void keyboardFunc(GLFWwindow* window, int key, int scancode, int action, - int mods) { - (void)window; - (void)scancode; - (void)mods; - if (action == GLFW_PRESS || action == GLFW_REPEAT) { - // Move camera - float mv_x = 0, mv_y = 0, mv_z = 0; - if (key == GLFW_KEY_K) - mv_x += 1; - else if (key == GLFW_KEY_J) - mv_x += -1; - else if (key == GLFW_KEY_L) - mv_y += 1; - else if (key == GLFW_KEY_H) - mv_y += -1; - else if (key == GLFW_KEY_P) - mv_z += 1; - else if (key == GLFW_KEY_N) - mv_z += -1; - // camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05); - // Close window - if (key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) { - glfwSetWindowShouldClose(window, GL_TRUE); - } - - if (key == GLFW_KEY_W) { - // toggle wireframe - g_show_wire = !g_show_wire; - } - - if (key == GLFW_KEY_C) { - // cull option - g_cull_face = !g_cull_face; - } - - // init_frame = true; - } -} - -static void clickFunc(GLFWwindow* window, int button, int action, int mods) { - (void)window; - (void)mods; - if (button == GLFW_MOUSE_BUTTON_LEFT) { - if (action == GLFW_PRESS) { - mouseLeftPressed = true; - trackball(prev_quat, 0.0, 0.0, 0.0, 0.0); - } else if (action == GLFW_RELEASE) { - mouseLeftPressed = false; - } - } - if (button == GLFW_MOUSE_BUTTON_RIGHT) { - if (action == GLFW_PRESS) { - mouseRightPressed = true; - } else if (action == GLFW_RELEASE) { - mouseRightPressed = false; - } - } - if (button == GLFW_MOUSE_BUTTON_MIDDLE) { - if (action == GLFW_PRESS) { - mouseMiddlePressed = true; - } else if (action == GLFW_RELEASE) { - mouseMiddlePressed = false; - } - } -} - -static void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) { - (void)window; - float rotScale = 1.0f; - float transScale = 2.0f; - - if (mouseLeftPressed) { - trackball(prev_quat, rotScale * (2.0f * prevMouseX - width) / (float)width, - rotScale * (height - 2.0f * prevMouseY) / (float)height, - rotScale * (2.0f * mouse_x - width) / (float)width, - rotScale * (height - 2.0f * mouse_y) / (float)height); - - add_quats(prev_quat, curr_quat, curr_quat); - } else if (mouseMiddlePressed) { - eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width; - lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width; - eye[1] += transScale * (mouse_y - prevMouseY) / (float)height; - lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height; - } else if (mouseRightPressed) { - eye[2] += transScale * (mouse_y - prevMouseY) / (float)height; - lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height; - } - - // Update mouse point - prevMouseX = mouse_x; - prevMouseY = mouse_y; -} - -static void Draw(const std::vector& drawObjects, - std::vector& materials, - std::map& textures) { - glPolygonMode(GL_FRONT, GL_FILL); - if (g_cull_face) { - glPolygonMode(GL_BACK, GL_LINE); - } else { - glPolygonMode(GL_BACK, GL_FILL); - } - - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(1.0, 1.0); - GLsizei stride = (3 + 3 + 3 + 2) * sizeof(float); - for (size_t i = 0; i < drawObjects.size(); i++) { - DrawObject o = drawObjects[i]; - if (o.vb_id < 1) { - continue; - } - - glBindBuffer(GL_ARRAY_BUFFER, o.vb_id); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - glBindTexture(GL_TEXTURE_2D, 0); - if ((o.material_id < materials.size())) { - std::string diffuse_texname = materials[o.material_id].diffuse_texname; - if (textures.find(diffuse_texname) != textures.end()) { - glBindTexture(GL_TEXTURE_2D, textures[diffuse_texname]); - } - } - glVertexPointer(3, GL_FLOAT, stride, (const void*)0); - glNormalPointer(GL_FLOAT, stride, (const void*)(sizeof(float) * 3)); - glColorPointer(3, GL_FLOAT, stride, (const void*)(sizeof(float) * 6)); - glTexCoordPointer(2, GL_FLOAT, stride, (const void*)(sizeof(float) * 9)); - - glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); - CheckErrors("drawarrays"); - glBindTexture(GL_TEXTURE_2D, 0); - } - - // draw wireframe - if (g_show_wire) { - glDisable(GL_POLYGON_OFFSET_FILL); - glPolygonMode(GL_FRONT, GL_LINE); - glPolygonMode(GL_BACK, GL_LINE); - - glColor3f(0.0f, 0.0f, 0.4f); - for (size_t i = 0; i < drawObjects.size(); i++) { - DrawObject o = drawObjects[i]; - if (o.vb_id < 1) { - continue; - } - - glBindBuffer(GL_ARRAY_BUFFER, o.vb_id); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(3, GL_FLOAT, stride, (const void*)0); - glNormalPointer(GL_FLOAT, stride, (const void*)(sizeof(float) * 3)); - glColorPointer(3, GL_FLOAT, stride, (const void*)(sizeof(float) * 6)); - glTexCoordPointer(2, GL_FLOAT, stride, (const void*)(sizeof(float) * 9)); - - glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); - CheckErrors("drawarrays"); - } - } -} - -static void Init() { - trackball(curr_quat, 0, 0, 0, 0); - - eye[0] = 0.0f; - eye[1] = 0.0f; - eye[2] = 3.0f; - - lookat[0] = 0.0f; - lookat[1] = 0.0f; - lookat[2] = 0.0f; - - up[0] = 0.0f; - up[1] = 1.0f; - up[2] = 0.0f; -} - -int main(int argc, char** argv) { - if (argc < 2) { - std::cout << "Needs input.obj\n" << std::endl; - return 0; - } - - Init(); - - if (!glfwInit()) { - std::cerr << "Failed to initialize GLFW." << std::endl; - return -1; - } - - window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL); - if (window == NULL) { - std::cerr << "Failed to open GLFW window. " << std::endl; - glfwTerminate(); - return 1; - } - - std::cout << "W : Toggle wireframe\n"; - std::cout << "C : Toggle face culling\n"; - //std::cout << "K, J, H, L, P, N : Move camera\n"; - std::cout << "Q, Esc : quit\n"; - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - // Callback - glfwSetWindowSizeCallback(window, reshapeFunc); - glfwSetKeyCallback(window, keyboardFunc); - glfwSetMouseButtonCallback(window, clickFunc); - glfwSetCursorPosCallback(window, motionFunc); - - glewExperimental = true; - if (glewInit() != GLEW_OK) { - std::cerr << "Failed to initialize GLEW." << std::endl; - return -1; - } - - reshapeFunc(window, width, height); - - float bmin[3], bmax[3]; - std::vector materials; - std::map textures; - if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, materials, textures, - argv[1])) { - return -1; - } - - float maxExtent = 0.5f * (bmax[0] - bmin[0]); - if (maxExtent < 0.5f * (bmax[1] - bmin[1])) { - maxExtent = 0.5f * (bmax[1] - bmin[1]); - } - if (maxExtent < 0.5f * (bmax[2] - bmin[2])) { - maxExtent = 0.5f * (bmax[2] - bmin[2]); - } - - while (glfwWindowShouldClose(window) == GL_FALSE) { - glfwPollEvents(); - glClearColor(0.1f, 0.2f, 0.3f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glEnable(GL_DEPTH_TEST); - glEnable(GL_TEXTURE_2D); - - // camera & rotate - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - GLfloat mat[4][4]; - gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], - up[1], up[2]); - build_rotmatrix(mat, curr_quat); - glMultMatrixf(&mat[0][0]); - - // Fit to -1, 1 - glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent); - - // Centerize object. - glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]), - -0.5 * (bmax[2] + bmin[2])); - - Draw(gDrawObjects, materials, textures); - - glfwSwapBuffers(window); - } - - glfwTerminate(); -} diff --git a/examples/voxelize/Makefile b/examples/voxelize/Makefile deleted file mode 100644 index 98189d93..00000000 --- a/examples/voxelize/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - g++ -o voxelizer main.cc diff --git a/examples/voxelize/README.md b/examples/voxelize/README.md deleted file mode 100644 index 8a113b6d..00000000 --- a/examples/voxelize/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Voxelize .obj and export it as also in .obj - -## Third party library - -This example uses https://github.com/karimnaaji/voxelizer, which is licensed under MIT Liense. diff --git a/examples/voxelize/main.cc b/examples/voxelize/main.cc deleted file mode 100644 index 6f119395..00000000 --- a/examples/voxelize/main.cc +++ /dev/null @@ -1,75 +0,0 @@ -#define VOXELIZER_IMPLEMENTATION -#include "voxelizer.h" - -#define TINYOBJLOADER_IMPLEMENTATION -#include "../../tiny_obj_loader.h" - -bool Voxelize(const char* filename, float voxelsizex, float voxelsizey, float voxelsizez, float precision) -{ - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename); - - if (!err.empty()) { - printf("err: %s\n", err.c_str()); - } - - if (!ret) { - printf("failed to load : %s\n", filename); - return false; - } - - if (shapes.size() == 0) { - printf("err: # of shapes are zero.\n"); - return false; - } - - // Only use first shape. - { - vx_mesh_t* mesh; - vx_mesh_t* result; - - mesh = vx_mesh_alloc(attrib.vertices.size(), shapes[0].mesh.indices.size()); - - for (size_t f = 0; f < shapes[0].mesh.indices.size(); f++) { - mesh->indices[f] = shapes[0].mesh.indices[f].vertex_index; - } - - for (size_t v = 0; v < attrib.vertices.size() / 3; v++) { - mesh->vertices[v].x = attrib.vertices[3*v+0]; - mesh->vertices[v].y = attrib.vertices[3*v+1]; - mesh->vertices[v].z = attrib.vertices[3*v+2]; - } - - result = vx_voxelize(mesh, voxelsizex, voxelsizey, voxelsizez, precision); - - printf("Number of vertices: %ld\n", result->nvertices); - printf("Number of indices: %ld\n", result->nindices); - } - return true; -} - - -int -main( - int argc, - char** argv) -{ - if (argc < 4) { - printf("Usage: voxelize input.obj voxelsizex voxelsizey voxelsizez precision\n"); - exit(-1); - } - - const char* filename = argv[1]; - float voxelsizex = atof(argv[2]); - float voxelsizey = atof(argv[3]); - float voxelsizez = atof(argv[4]); - float prec = atof(argv[5]); - bool ret = Voxelize(filename, voxelsizex, voxelsizey, voxelsizez, prec); - - return ret ? EXIT_SUCCESS : EXIT_FAILURE; -} - diff --git a/examples/voxelize/voxelizer.h b/examples/voxelize/voxelizer.h deleted file mode 100644 index 49586954..00000000 --- a/examples/voxelize/voxelizer.h +++ /dev/null @@ -1,764 +0,0 @@ -// -// LICENCE: -// The MIT License (MIT) -// -// Copyright (c) 2016 Karim Naaji, karim.naaji@gmail.com -// -// 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 -// -// REFERENCES: -// http://matthias-mueller-fischer.ch/publications/tetraederCollision.pdf -// http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/tribox2.txt -// -// HOWTO: -// #define VOXELIZER_IMPLEMENTATION -// #define VOXELIZER_DEBUG // Only if assertions need to be checked -// #include "voxelizer.h" -// -// HISTORY: -// - version 0.9.0: Initial -// -// TODO: -// - Triangle face merging -// - Add colors from input mesh -// - Potential issue with voxel bigger than triangle -// - -#ifndef VOXELIZER_H -#define VOXELIZER_H - -// ------------------------------------------------------------------------------------------------ -// VOXELIZER PUBLIC API -// - -#ifndef VOXELIZER_HELPERS -#include // malloc, calloc, free -#endif - -typedef struct vx_vertex { - union { - float v[3]; - struct { - float x; - float y; - float z; - }; - }; -} vx_vertex_t; - -typedef struct vx_mesh { - vx_vertex_t* vertices; // Contiguous mesh vertices - vx_vertex_t* normals; // Contiguous mesh normals - unsigned int* indices; // Mesh indices - unsigned int* normalindices; // Mesh normal indices - size_t nindices; // The number of normal indices - size_t nvertices; // The number of vertices - size_t nnormals; // The number of normals -} vx_mesh_t; - -vx_mesh_t* vx_voxelize(vx_mesh_t* _mesh, // The input mesh - float voxelsizex, // Voxel size on X-axis - float voxelsizey, // Voxel size on Y-axis - float voxelsizez, // Voxel size on Z-axis - float precision); // A precision factor that reduces "holes" artifact - // usually a precision = voxelsize / 10. works ok. - -void vx_mesh_free(vx_mesh_t* _mesh); -vx_mesh_t* vx_mesh_alloc(int nindices, int nvertices); - -// Voxelizer Helpers, define your own if needed -#ifndef VOXELIZER_HELPERS -#define VOXELIZER_HELPERS 1 -#define VX_MIN(a, b) (a > b ? b : a) -#define VX_MAX(a, b) (a > b ? a : b) -#define VX_FINDMINMAX(x0, x1, x2, min, max) \ - min = max = x0; \ - if (x1 < min) min = x1; \ - if (x1 > max) max = x1; \ - if (x2 < min) min = x2; \ - if (x2 > max) max = x2; -#define VX_CLAMP(v, lo, hi) VX_MAX(lo, VX_MIN(hi, v)) -#define VX_MALLOC(T, N) ((T*) malloc(N * sizeof(T))) -#define VX_FREE(T) free(T) -#define VX_CALLOC(T, N) ((T*) calloc(N * sizeof(T), 1)) -#define VX_SWAP(T, A, B) { T tmp = B; B = A; A = tmp; } -#ifdef VOXELIZER_DEBUG -#define VX_ASSERT(STMT) if (!(STMT)) { *(int *)0 = 0; } -#else -#define VX_ASSERT(STMT) -#endif // VOXELIZER_DEBUG -#endif // VOXELIZER_HELPERS - -// -// END VOXELIZER PUBLIC API -// ------------------------------------------------------------------------------------------------ - -#endif // VOXELIZER_H - -#ifdef VOXELIZER_IMPLEMENTATION - -#include // ceil, fabs & al. -#include // hughh -#include // memcpy - -#define VOXELIZER_EPSILON (0.0000001) -#define VOXELIZER_NORMAL_INDICES_SIZE (6) -#define VOXELIZER_INDICES_SIZE (36) -#define VOXELIZER_HASH_TABLE_SIZE (4096) - -unsigned int vx_voxel_indices[VOXELIZER_INDICES_SIZE] = { - 0, 1, 2, - 0, 2, 3, - 3, 2, 6, - 3, 6, 7, - 0, 7, 4, - 0, 3, 7, - 4, 7, 5, - 7, 6, 5, - 0, 4, 5, - 0, 5, 1, - 1, 5, 6, - 1, 6, 2, -}; - -float vx_normals[18] = { - 0.0, -1.0, 0.0, - 0.0, 1.0, 0.0, - 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, - -1.0, 0.0, 0.0, - 0.0, 0.0, -1.0, -}; - -unsigned int vx_normal_indices[VOXELIZER_NORMAL_INDICES_SIZE] = { - 3, 2, 1, 5, 4, 0, -}; - -typedef struct vx_aabb { - vx_vertex_t min; - vx_vertex_t max; -} vx_aabb_t; - -typedef struct vx_edge { - vx_vertex_t p1; - vx_vertex_t p2; -} vx_edge_t; - -typedef struct vx_triangle { - vx_vertex_t p1; - vx_vertex_t p2; - vx_vertex_t p3; -} vx_triangle_t; - -typedef struct vx_hash_table_node { - struct vx_hash_table_node* next; - struct vx_hash_table_node* prev; - void* data; -} vx_hash_table_node_t; - -typedef struct vx_hash_table { - vx_hash_table_node_t** elements; - size_t size; -} vx_hash_table_t; - -vx_hash_table_t* vx__hash_table_alloc(size_t size) -{ - vx_hash_table_t* table = VX_MALLOC(vx_hash_table_t, 1); - if (!table) { return NULL; } - table->size = size; - table->elements = VX_MALLOC(vx_hash_table_node_t*, size); - if (!table->elements) { return NULL; } - for (size_t i = 0; i < table->size; ++i) { - table->elements[i] = NULL; - } - return table; -} - -void vx__hash_table_free(vx_hash_table_t* table, bool freedata) -{ - for (size_t i = 0; i < table->size; ++i) { - vx_hash_table_node_t* node = table->elements[i]; - - if (node) { - if (node->next) { - while (node->next) { - node = node->next; - if (freedata) { - VX_FREE(node->prev->data); - } - VX_FREE(node->prev); - } - VX_FREE(node); - } else { - VX_FREE(node->data); - VX_FREE(node); - } - } - } - - VX_FREE(table->elements); - VX_FREE(table); -} - -bool vx__hash_table_insert(vx_hash_table_t* table, - size_t hash, - void* data, - bool (*compfunc)(void* d1, void* d2)) -{ - if (!table->elements[hash]) { - table->elements[hash] = VX_MALLOC(vx_hash_table_node_t, 1); - table->elements[hash]->prev = NULL; - table->elements[hash]->next = NULL; - table->elements[hash]->data = data; - } else { - vx_hash_table_node_t* node = table->elements[hash]; - - if (compfunc && compfunc(node->data, data)) { - return false; - } - - while (node->next) { - node = node->next; - if (compfunc && compfunc(node->data, data)) { - return false; - } - } - - vx_hash_table_node_t* nnode = VX_MALLOC(vx_hash_table_node_t, 1); - - nnode->prev = node; - nnode->next = NULL; - nnode->data = data; - node->next = nnode; - } - return true; -} - -void vx_mesh_free(vx_mesh_t* m) -{ - VX_FREE(m->vertices); - m->vertices = NULL; - m->nvertices = 0; - VX_FREE(m->indices); - m->indices = NULL; - m->nindices = 0; - if (m->normals) { VX_FREE(m->normals); } - VX_FREE(m); -} - -vx_mesh_t* vx_mesh_alloc(int nvertices, int nindices) -{ - vx_mesh_t* m = VX_MALLOC(vx_mesh_t, 1); - if (!m) { return NULL; } - m->indices = VX_CALLOC(unsigned int, nindices); - if (!m->indices) { return NULL; } - m->vertices = VX_CALLOC(vx_vertex_t, nvertices); - if (!m->vertices) { return NULL; } - m->normals = NULL; - m->nindices = nindices; - m->nvertices = nvertices; - return m; -} - -float vx__map_to_voxel(float position, float voxelSize, bool min) -{ - float vox = (position + (position < 0.f ? -1.f : 1.f) * voxelSize * 0.5f) / voxelSize; - return (min ? floor(vox) : ceil(vox)) * voxelSize; -} - -vx_vertex_t vx__vertex_cross(vx_vertex_t* v1, vx_vertex_t* v2) -{ - vx_vertex_t cross; - cross.x = v1->y * v2->z - v1->z * v2->y; - cross.y = v1->z * v2->x - v1->x * v2->z; - cross.z = v1->x * v2->y - v1->y * v2->x; - return cross; -} - -bool vx__vertex_equals(vx_vertex_t* v1, vx_vertex_t* v2) { - return fabs(v1->x - v2->x) < VOXELIZER_EPSILON && - fabs(v1->y - v2->y) < VOXELIZER_EPSILON && - fabs(v1->z - v2->z) < VOXELIZER_EPSILON; -} - -bool vx__vertex_comp_func(void* a, void* b) -{ - return vx__vertex_equals((vx_vertex_t*) a, (vx_vertex_t*) b); -} - -void vx__vertex_sub(vx_vertex_t* a, vx_vertex_t* b) -{ - a->x -= b->x; - a->y -= b->y; - a->z -= b->z; -} - -void vx__vertex_add(vx_vertex_t* a, vx_vertex_t* b) -{ - a->x += b->x; - a->y += b->y; - a->z += b->z; -} - -void vx__vertex_multiply(vx_vertex_t* a, float v) -{ - a->x *= v; - a->y *= v; - a->z *= v; -} - -float vx__vertex_dot(vx_vertex_t* v1, vx_vertex_t* v2) -{ - return v1->x * v2->x + v1->y * v2->y + v1->z * v2->z; -} - -int vx__plane_box_overlap(vx_vertex_t* normal, - float d, - vx_vertex_t* halfboxsize) -{ - vx_vertex_t vmin, vmax; - - for (int dim = 0; dim <= 2; dim++) { - if (normal->v[dim] > 0.0f) { - vmin.v[dim] = -halfboxsize->v[dim]; - vmax.v[dim] = halfboxsize->v[dim]; - } else { - vmin.v[dim] = halfboxsize->v[dim]; - vmax.v[dim] = -halfboxsize->v[dim]; - } - } - - if (vx__vertex_dot(normal, &vmin) + d > 0.0f) { - return false; - } - - if (vx__vertex_dot(normal, &vmax) + d >= 0.0f) { - return true; - } - - return false; -} - -#define AXISTEST_X01(a, b, fa, fb) \ - p1 = a * v1.y - b * v1.z; \ - p3 = a * v3.y - b * v3.z; \ - if (p1 < p3) { \ - min = p1; max = p3; \ - } else { \ - min = p3; max = p1; \ - } \ - rad = fa * halfboxsize.y + fb * halfboxsize.z; \ - if (min > rad || max < -rad) { \ - return false; \ - } \ - -#define AXISTEST_X2(a, b, fa, fb) \ - p1 = a * v1.y - b * v1.z; \ - p2 = a * v2.y - b * v2.z; \ - if (p1 < p2) { \ - min = p1; max = p2; \ - } else { \ - min = p2; max = p1; \ - } \ - rad = fa * halfboxsize.y + fb * halfboxsize.z; \ - if (min > rad || max < -rad) { \ - return false; \ - } \ - -#define AXISTEST_Y02(a, b, fa, fb) \ - p1 = -a * v1.x + b * v1.z; \ - p3 = -a * v3.x + b * v3.z; \ - if (p1 < p3) { \ - min = p1; max = p3; \ - } else { \ - min = p3; max = p1; \ - } \ - rad = fa * halfboxsize.x + fb * halfboxsize.z; \ - if (min > rad || max < -rad) { \ - return false; \ - } \ - -#define AXISTEST_Y1(a, b, fa, fb) \ - p1 = -a * v1.x + b * v1.z; \ - p2 = -a * v2.x + b * v2.z; \ - if (p1 < p2) { \ - min = p1; max = p2; \ - } else { \ - min = p2; max = p1; \ - } \ - rad = fa * halfboxsize.x + fb * halfboxsize.z; \ - if (min > rad || max < -rad) { \ - return false; \ - } - -#define AXISTEST_Z12(a, b, fa, fb) \ - p2 = a * v2.x - b * v2.y; \ - p3 = a * v3.x - b * v3.y; \ - if (p3 < p2) { \ - min = p3; max = p2; \ - } else { \ - min = p2; max = p3; \ - } \ - rad = fa * halfboxsize.x + fb * halfboxsize.y; \ - if (min > rad || max < -rad) { \ - return false; \ - } - -#define AXISTEST_Z0(a, b, fa, fb) \ - p1 = a * v1.x - b * v1.y; \ - p2 = a * v2.x - b * v2.y; \ - if (p1 < p2) { \ - min = p1; max = p2; \ - } else { \ - min = p2; max = p1; \ - } \ - rad = fa * halfboxsize.x + fb * halfboxsize.y; \ - if (min > rad || max < -rad) { \ - return false; \ - } - -int vx__triangle_box_overlap(vx_vertex_t boxcenter, - vx_vertex_t halfboxsize, - vx_triangle_t triangle) -{ - vx_vertex_t v1, v2, v3, normal, e1, e2, e3; - float min, max, d, p1, p2, p3, rad, fex, fey, fez; - - v1 = triangle.p1; - v2 = triangle.p2; - v3 = triangle.p3; - - vx__vertex_sub(&v1, &boxcenter); - vx__vertex_sub(&v2, &boxcenter); - vx__vertex_sub(&v3, &boxcenter); - - e1 = v2; - e2 = v3; - e3 = v1; - - vx__vertex_sub(&e1, &v1); - vx__vertex_sub(&e2, &v2); - vx__vertex_sub(&e3, &v3); - - fex = fabs(e1.x); - fey = fabs(e1.y); - fez = fabs(e1.z); - - AXISTEST_X01(e1.z, e1.y, fez, fey); - AXISTEST_Y02(e1.z, e1.x, fez, fex); - AXISTEST_Z12(e1.y, e1.x, fey, fex); - - fex = fabs(e2.x); - fey = fabs(e2.y); - fez = fabs(e2.z); - - AXISTEST_X01(e2.z, e2.y, fez, fey); - AXISTEST_Y02(e2.z, e2.x, fez, fex); - AXISTEST_Z0(e2.y, e2.x, fey, fex); - - fex = fabs(e3.x); - fey = fabs(e3.y); - fez = fabs(e3.z); - - AXISTEST_X2(e3.z, e3.y, fez, fey); - AXISTEST_Y1(e3.z, e3.x, fez, fex); - AXISTEST_Z12(e3.y, e3.x, fey, fex); - - VX_FINDMINMAX(v1.x, v2.x, v3.x, min, max); - if (min > halfboxsize.x || max < -halfboxsize.x) { - return false; - } - - VX_FINDMINMAX(v1.y, v2.y, v3.y, min, max); - if (min > halfboxsize.y || max < -halfboxsize.y) { - return false; - } - - VX_FINDMINMAX(v1.z, v2.z, v3.z, min, max); - if (min > halfboxsize.z || max < -halfboxsize.z) { - return false; - } - - normal = vx__vertex_cross(&e1, &e2); - d = -vx__vertex_dot(&normal, &v1); - - if (!vx__plane_box_overlap(&normal, d, &halfboxsize)) { - return false; - } - - return true; -} - -#undef AXISTEST_X2 -#undef AXISTEST_X01 -#undef AXISTEST_Y1 -#undef AXISTEST_Y02 -#undef AXISTEST_Z0 -#undef AXISTEST_Z12 - -float vx__triangle_area(vx_triangle_t* triangle) { - vx_vertex_t ab = triangle->p2; - vx_vertex_t ac = triangle->p3; - - vx__vertex_sub(&ab, &triangle->p1); - vx__vertex_sub(&ac, &triangle->p1); - - float a0 = ab.y * ac.z - ab.z * ac.y; - float a1 = ab.z * ac.x - ab.x * ac.z; - float a2 = ab.x * ac.y - ab.y * ac.x; - - return sqrtf(powf(a0, 2.f) + powf(a1, 2.f) + powf(a2, 2.f)) * 0.5f; -} - -void vx__aabb_init(vx_aabb_t* aabb) -{ - aabb->max.x = aabb->max.y = aabb->max.z = -INFINITY; - aabb->min.x = aabb->min.y = aabb->min.z = INFINITY; -} - -vx_aabb_t vx__triangle_aabb(vx_triangle_t* triangle) -{ - vx_aabb_t aabb; - - vx__aabb_init(&aabb); - - aabb.max.x = VX_MAX(aabb.max.x, triangle->p1.x); aabb.max.x = VX_MAX(aabb.max.x, - triangle->p2.x); aabb.max.x = VX_MAX(aabb.max.x, triangle->p3.x); - aabb.max.y = VX_MAX(aabb.max.y, triangle->p1.y); aabb.max.y = VX_MAX(aabb.max.y, - triangle->p2.y); aabb.max.y = VX_MAX(aabb.max.y, triangle->p3.y); - aabb.max.z = VX_MAX(aabb.max.z, triangle->p1.z); aabb.max.z = VX_MAX(aabb.max.z, - triangle->p2.z); aabb.max.z = VX_MAX(aabb.max.z, triangle->p3.z); - - aabb.min.x = VX_MIN(aabb.min.x, triangle->p1.x); aabb.min.x = VX_MIN(aabb.min.x, - triangle->p2.x); aabb.min.x = VX_MIN(aabb.min.x, triangle->p3.x); - aabb.min.y = VX_MIN(aabb.min.y, triangle->p1.y); aabb.min.y = VX_MIN(aabb.min.y, - triangle->p2.y); aabb.min.y = VX_MIN(aabb.min.y, triangle->p3.y); - aabb.min.z = VX_MIN(aabb.min.z, triangle->p1.z); aabb.min.z = VX_MIN(aabb.min.z, - triangle->p2.z); aabb.min.z = VX_MIN(aabb.min.z, triangle->p3.z); - - return aabb; -} - -vx_vertex_t vx__aabb_center(vx_aabb_t* a) -{ - vx_vertex_t boxcenter = a->min; - vx__vertex_add(&boxcenter, &a->max); - vx__vertex_multiply(&boxcenter, 0.5f); - - return boxcenter; -} - -vx_vertex_t vx__aabb_half_size(vx_aabb_t* a) -{ - vx_vertex_t size; - - size.x = fabs(a->max.x - a->min.x) * 0.5f; - size.y = fabs(a->max.y - a->min.y) * 0.5f; - size.z = fabs(a->max.z - a->min.z) * 0.5f; - - return size; -} - -vx_aabb_t vx__aabb_merge(vx_aabb_t* a, vx_aabb_t* b) -{ - vx_aabb_t merge; - - merge.min.x = VX_MIN(a->min.x, b->min.x); - merge.min.y = VX_MIN(a->min.y, b->min.y); - merge.min.z = VX_MIN(a->min.z, b->min.z); - - merge.max.x = VX_MAX(a->max.x, b->max.x); - merge.max.y = VX_MAX(a->max.y, b->max.y); - merge.max.z = VX_MAX(a->max.z, b->max.z); - - return merge; -} - -size_t vx__vertex_hash(vx_vertex_t pos, size_t n) -{ - size_t a = (size_t)(pos.x * 73856093); - size_t b = (size_t)(pos.y * 19349663); - size_t c = (size_t)(pos.z * 83492791); - - return (a ^ b ^ c) % n; -} - -void vx__add_voxel(vx_mesh_t* mesh, - vx_vertex_t* pos, - float* vertices) -{ - for (size_t i = 0; i < 8; ++i) { - size_t index = i+mesh->nvertices; - - mesh->vertices[index].x = vertices[i*3+0] + pos->x; - mesh->vertices[index].y = vertices[i*3+1] + pos->y; - mesh->vertices[index].z = vertices[i*3+2] + pos->z; - } - - int j = -1; - for (size_t i = 0; i < VOXELIZER_INDICES_SIZE; ++i) { - if (i % 6 == 0) { - j++; - } - mesh->normalindices[i+mesh->nindices] = vx_normal_indices[j]; - } - - for (size_t i = 0; i < VOXELIZER_INDICES_SIZE; ++i) { - mesh->indices[i+mesh->nindices] = vx_voxel_indices[i] + mesh->nvertices; - } - - mesh->nindices += VOXELIZER_INDICES_SIZE; - mesh->nvertices += 8; -} - -vx_mesh_t* vx_voxelize(vx_mesh_t* m, - float voxelsizex, - float voxelsizey, - float voxelsizez, - float precision) -{ - vx_mesh_t* outmesh = NULL; - vx_hash_table_t* table = NULL; - size_t voxels = 0; - - float halfsizex = voxelsizex * 0.5f; - float halfsizey = voxelsizey * 0.5f; - float halfsizez = voxelsizez * 0.5f; - - table = vx__hash_table_alloc(VOXELIZER_HASH_TABLE_SIZE); - - for (int i = 0; i < m->nindices; i += 3) { - vx_triangle_t triangle; - - VX_ASSERT(m->indices[i+0] < m->nvertices); - VX_ASSERT(m->indices[i+1] < m->nvertices); - VX_ASSERT(m->indices[i+2] < m->nvertices); - - triangle.p1 = m->vertices[m->indices[i+0]]; - triangle.p2 = m->vertices[m->indices[i+1]]; - triangle.p3 = m->vertices[m->indices[i+2]]; - - if (vx__triangle_area(&triangle) < VOXELIZER_EPSILON) { - // triangle with 0 area - continue; - } - - vx_aabb_t aabb = vx__triangle_aabb(&triangle); - - aabb.min.x = vx__map_to_voxel(aabb.min.x, voxelsizex, true); - aabb.min.y = vx__map_to_voxel(aabb.min.y, voxelsizey, true); - aabb.min.z = vx__map_to_voxel(aabb.min.z, voxelsizez, true); - - aabb.max.x = vx__map_to_voxel(aabb.max.x, voxelsizex, false); - aabb.max.y = vx__map_to_voxel(aabb.max.y, voxelsizey, false); - aabb.max.z = vx__map_to_voxel(aabb.max.z, voxelsizez, false); - - for (float x = aabb.min.x; x < aabb.max.x; x += voxelsizex) { - for (float y = aabb.min.y; y < aabb.max.y; y += voxelsizey) { - for (float z = aabb.min.z; z < aabb.max.z; z += voxelsizez) { - vx_aabb_t saabb; - - saabb.min.x = x - halfsizex; - saabb.min.y = y - halfsizey; - saabb.min.z = z - halfsizez; - saabb.max.x = x + halfsizex; - saabb.max.y = y + halfsizey; - saabb.max.z = z + halfsizez; - - vx_vertex_t boxcenter = vx__aabb_center(&saabb); - vx_vertex_t halfsize = vx__aabb_half_size(&saabb); - - // HACK: some holes might appear, this - // precision factor reduces the artifact - halfsize.x += precision; - halfsize.y += precision; - halfsize.z += precision; - - if (vx__triangle_box_overlap(boxcenter, halfsize, triangle)) { - vx_vertex_t* nodedata = VX_MALLOC(vx_vertex_t, 1); - *nodedata = boxcenter; - - size_t hash = vx__vertex_hash(boxcenter, VOXELIZER_HASH_TABLE_SIZE); - - bool insert = vx__hash_table_insert(table, - hash, - nodedata, - vx__vertex_comp_func); - - if (insert) { - voxels++; - } - } - } - } - } - } - - outmesh = VX_MALLOC(vx_mesh_t, 1); - size_t nvertices = voxels * 8; - size_t nindices = voxels * VOXELIZER_INDICES_SIZE; - outmesh->nnormals = VOXELIZER_NORMAL_INDICES_SIZE; - outmesh->vertices = VX_CALLOC(vx_vertex_t, nvertices); - outmesh->normals = VX_CALLOC(vx_vertex_t, 6); - outmesh->indices = VX_CALLOC(unsigned int, nindices); - outmesh->normalindices = VX_CALLOC(unsigned int, nindices); - outmesh->nindices = 0; - outmesh->nvertices = 0; - - memcpy(outmesh->normals, vx_normals, 18 * sizeof(float)); - - float vertices[24] = { - -halfsizex, halfsizey, halfsizez, - -halfsizex, -halfsizey, halfsizez, - halfsizex, -halfsizey, halfsizez, - halfsizex, halfsizey, halfsizez, - -halfsizex, halfsizey, -halfsizez, - -halfsizex, -halfsizey, -halfsizez, - halfsizex, -halfsizey, -halfsizez, - halfsizex, halfsizey, -halfsizez, - }; - - for (size_t i = 0; i < table->size; ++i) { - if (table->elements[i] != NULL) { - vx_hash_table_node_t* node = table->elements[i]; - - if (!node) { - continue; - } - - vx_vertex_t* p = (vx_vertex_t*) node->data; - vx__add_voxel(outmesh, p, vertices); - - while (node->next) { - node = node->next; - p = (vx_vertex_t*) node->data; - vx__add_voxel(outmesh, p, vertices); - } - } - } - - vx__hash_table_free(table, true); - - return outmesh; -} - -#undef VOXELIZER_EPSILON -#undef VOXELIZER_INDICES_SIZE -#undef VOXELIZER_HASH_TABLE_SIZE - -#endif // VX_VOXELIZER_IMPLEMENTATION diff --git a/experimental/README.md b/experimental/README.md deleted file mode 100644 index a0603af9..00000000 --- a/experimental/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Experimental code for .obj loader. - -* Multi-threaded optimized parser : tinyobj_loader_opt.h - -## Requirements - -* C++-11 compiler - -## How to build - -``` -$ premak5 gmake -``` - -## Compile options - -* zstd compressed .obj support. `--with-zstd` premake option. -* gzip compressed .obj support. `--with-zlib` premake option. - -## Notes on AMD GPU + Linux - -You may need to link with libdrm(`-ldrm`). - -## Licenses - -* lfpAlloc : MIT license. diff --git a/experimental/lfpAlloc/Allocator.hpp b/experimental/lfpAlloc/Allocator.hpp deleted file mode 100644 index 4dddaabf..00000000 --- a/experimental/lfpAlloc/Allocator.hpp +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef LF_POOL_ALLOCATOR -#define LF_POOL_ALLOCATOR - -#include -#include -#include - -namespace lfpAlloc { -template -class lfpAllocator { -public: - using value_type = T; - using pointer = T*; - using const_pointer = const T*; - using reference = T&; - using const_reference = T const&; - - template - struct rebind { - typedef lfpAllocator other; - }; - - lfpAllocator() {} - - template - lfpAllocator(lfpAllocator&&) noexcept {} - - template - lfpAllocator(const lfpAllocator&) noexcept {} - - T* allocate(std::size_t count) { - if (sizeof(T) * count <= - alignof(std::max_align_t) * NumPools - sizeof(void*)) { - return reinterpret_cast( - dispatcher_.allocate(sizeof(T) * count)); - } else { - return new T[count]; - } - } - - void deallocate(T* p, std::size_t count) noexcept { - if (sizeof(T) * count <= - alignof(std::max_align_t) * NumPools - sizeof(void*)) { - dispatcher_.deallocate(p, sizeof(T) * count); - } else { - delete[] p; - } - } - - // Should not be required, but allocator_traits is not complete in - // gcc 4.9.1 - template - void destroy(U* p) { - p->~U(); - } - - template - void construct(U* p, Args&&... args) { - new (p) U(std::forward(args)...); - } - - template - friend bool operator==(const lfpAllocator&, - const lfpAllocator&) noexcept; - - template - friend class lfpAllocator; - -private: - static PoolDispatcher dispatcher_; -}; - -template -PoolDispatcher lfpAllocator::dispatcher_; - -template -inline bool operator==(const lfpAllocator&, - const lfpAllocator&) noexcept { - return N == M; -} - -template -inline bool operator!=(const lfpAllocator& left, - const lfpAllocator& right) noexcept { - return !(left == right); -} -} - -#endif diff --git a/experimental/lfpAlloc/ChunkList.hpp b/experimental/lfpAlloc/ChunkList.hpp deleted file mode 100644 index 760c670f..00000000 --- a/experimental/lfpAlloc/ChunkList.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef LF_POOL_ALLOC_CHUNK_LIST -#define LF_POOL_ALLOC_CHUNK_LIST - -#include -#include -#include - -#ifndef LFP_ALLOW_BLOCKING -static_assert(ATOMIC_POINTER_LOCK_FREE == 2, - "Atomic pointer is not lock-free."); -#endif - -namespace lfpAlloc { - -template -struct Cell { - uint8_t val_[Size]; - Cell* next_ = this + 1; -}; - -// For small types (less than the size of void*), no additional -// space is needed, so union val_ with next_ to avoid overhead. -template <> -struct Cell<0> { - Cell() : next_{this + 1} {} - union { - uint8_t val_[sizeof(Cell*)]; - Cell* next_; - }; -}; - -template -struct Chunk { - Chunk() noexcept { - auto& last = memBlock_[AllocationsPerChunk - 1]; - last.next_ = nullptr; - } - Cell memBlock_[AllocationsPerChunk]; -}; - -template -struct Node { - Node() : val_(), next_(nullptr) {} - Node(const T& val) : val_(val), next_(nullptr) {} - T val_; - std::atomic*> next_; -}; - -template -class ChunkList { - static constexpr auto CellSize = - (Size > sizeof(void*)) ? Size - sizeof(void*) : 0; - using Chunk_t = Chunk; - using Cell_t = Cell; - - using ChunkNode = Node; - using CellNode = Node; - -public: - static ChunkList& getInstance() { - static ChunkList c; - return c; - } - - Cell_t* allocateChain() { - CellNode* recentHead = head_.load(); - CellNode* currentNext = nullptr; - do { - // If there are no available chains, allocate a new chunk - if (!recentHead) { - ChunkNode* currentHandle; - - // Make a new node - auto newChunk = new ChunkNode(); - - // Add the chunk to the chain - do { - currentHandle = handle_.load(); - newChunk->next_ = currentHandle; - } while ( - !handle_.compare_exchange_weak(currentHandle, newChunk)); - return &newChunk->val_.memBlock_[0]; - } - - currentNext = recentHead->next_; - } while (!head_.compare_exchange_weak(recentHead, currentNext)); - - auto retnValue = recentHead->val_; - delete recentHead; - return retnValue; - } - - void deallocateChain(Cell_t* newCell) { - if (!newCell) { - return; - } - CellNode* currentHead = head_.load(); - - // Construct a new node to be added to the linked list - CellNode* newHead = new CellNode(newCell); - - // Add the chain to the linked list - do { - newHead->next_.store(currentHead, std::memory_order_release); - } while (!head_.compare_exchange_weak(currentHead, newHead)); - } - -private: - ChunkList() : handle_(nullptr), head_(nullptr) {} - - std::atomic handle_; - std::atomic head_; -}; -} - -#endif diff --git a/experimental/lfpAlloc/Pool.hpp b/experimental/lfpAlloc/Pool.hpp deleted file mode 100644 index 370dab7a..00000000 --- a/experimental/lfpAlloc/Pool.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef LF_POOL_ALLOC_POOL -#define LF_POOL_ALLOC_POOL - -#include -#include - -namespace lfpAlloc { -template -class Pool { - using ChunkList_t = ChunkList; - -public: - static constexpr auto CellSize = - (Size > sizeof(void*)) ? Size - sizeof(void*) : 0; - using Cell_t = Cell; - - Pool() : head_(nullptr) {} - - ~Pool() { ChunkList_t::getInstance().deallocateChain(head_); } - - void* allocate() { - // Head loaded from head_ - Cell_t* currentHead = head_; - Cell_t* next; - - // Out of cells to allocate - if (!currentHead) { - currentHead = ChunkList_t::getInstance().allocateChain(); - } - - next = currentHead->next_; - head_ = next; - return ¤tHead->val_; - } - - void deallocate(void* p) noexcept { - auto newHead = reinterpret_cast(p); - Cell_t* currentHead = head_; - newHead->next_ = currentHead; - head_ = newHead; - } - -private: - Cell_t* head_; -}; -} - -#endif diff --git a/experimental/lfpAlloc/PoolDispatcher.hpp b/experimental/lfpAlloc/PoolDispatcher.hpp deleted file mode 100644 index e4d1427f..00000000 --- a/experimental/lfpAlloc/PoolDispatcher.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef LF_POOL_DISPATCHER -#define LF_POOL_DISPATCHER - -#include -#include -#include -#include - -#ifndef LFP_ALLOCATIONS_PER_CHUNK -#define LFP_ALLOCATIONS_PER_CHUNK 64 * 100 -#endif - -namespace lfpAlloc { -namespace detail { - -template -struct Pools : Pools {}; - -template -struct Pools<0, Size...> { - using type = std::tuple...>; -}; -} - -template -class PoolDispatcher { -public: - void* allocate(std::size_t size) { return dispatchAllocate<0>(size); } - - void deallocate(void* p, std::size_t size) noexcept { - dispatchDeallocate<0>(p, size); - } - -private: - thread_local static typename detail::Pools::type pools_; - static_assert(NumPools > 0, "Invalid number of pools"); - - template - typename std::enable_if < - Index::type - dispatchAllocate(std::size_t const& requestSize) { - if (requestSize <= std::get(pools_).CellSize) { - return std::get(pools_).allocate(); - } else { - return dispatchAllocate(requestSize); - } - } - - template - typename std::enable_if::type - dispatchAllocate(std::size_t const&) { - assert(false && "Invalid allocation size."); - return nullptr; - } - - template - typename std::enable_if < - Index::type - dispatchDeallocate(void* p, std::size_t const& requestSize) noexcept { - if (requestSize <= std::get(pools_).CellSize) { - std::get(pools_).deallocate(p); - } else { - dispatchDeallocate(p, requestSize); - } - } - - template - typename std::enable_if::type - dispatchDeallocate(void*, std::size_t const&) noexcept { - assert(false && "Invalid deallocation size."); - } -}; - -template -thread_local typename detail::Pools::type - PoolDispatcher::pools_; -} - -#endif diff --git a/experimental/lfpAlloc/Utils.hpp b/experimental/lfpAlloc/Utils.hpp deleted file mode 100644 index 8740a795..00000000 --- a/experimental/lfpAlloc/Utils.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -namespace lfpAlloc { -namespace detail { -template -struct Log { - enum { value = 1 + Log::value }; -}; - -template -struct Log<1, base> { - enum { value = 0 }; -}; - -template -struct Log<0, base> { - enum { value = 0 }; -}; -} -} diff --git a/experimental/premake5.lua b/experimental/premake5.lua deleted file mode 100644 index 29511e17..00000000 --- a/experimental/premake5.lua +++ /dev/null @@ -1,94 +0,0 @@ -newoption { - trigger = "with-zlib", - description = "Build with zlib." -} - -newoption { - trigger = "with-zstd", - description = "Build with ZStandard compression." -} - -newoption { - trigger = "clang", - description = "Use clang compiler." -} - -newoption { - trigger = "asan", - description = "Enable AddressSanitizer(gcc or clang only)." -} - -solution "objview" - -- location ( "build" ) - configurations { "Release", "Debug" } - platforms {"native", "x64", "x32"} - - project "objview" - - kind "ConsoleApp" - language "C++" - files { "viewer.cc", "trackball.cc" } - includedirs { "./" } - includedirs { "../../" } - - flags { "c++11" } - - if _OPTIONS['clang'] then - toolset "clang" - end - - if _OPTIONS['with-zlib'] then - defines { 'ENABLE_ZLIB' } - links { 'z' } - end - - if _OPTIONS['asan'] then - buildoptions { '-fsanitize=address' } - linkoptions { '-fsanitize=address' } - end - - if _OPTIONS['with-zstd'] then - print("with-zstd") - defines { 'ENABLE_ZSTD' } - -- Set path to zstd installed dir. - includedirs { '$$HOME/local/include' } - libdirs { '$$HOME/local/lib' } - links { 'zstd' } - end - - -- Uncomment if you want address sanitizer(gcc/clang only) - --buildoptions { "-fsanitize=address" } - --linkoptions { "-fsanitize=address" } - - configuration { "linux" } - linkoptions { "`pkg-config --libs glfw3`" } - links { "GL", "GLU", "m", "GLEW", "X11", "Xrandr", "Xinerama", "Xi", "Xxf86vm", "Xcursor", "dl" } - linkoptions { "-pthread" } - - configuration { "windows" } - -- Path to GLFW3 - includedirs { '../../../local/glfw-3.2.bin.WIN64/include' } - libdirs { '../../../local/glfw-3.2.bin.WIN64/lib-vc2015' } - -- Path to GLEW - includedirs { '../../../local/glew-1.13.0/include' } - libdirs { '../../../local/glew-1.13.0/lib/Release/x64' } - - links { "glfw3", "glew32", "gdi32", "winmm", "user32", "glu32","opengl32", "kernel32" } - defines { "_CRT_SECURE_NO_WARNINGS" } - defines { "NOMINMAX" } - - configuration { "macosx" } - includedirs { "/usr/local/include" } - buildoptions { "-Wno-deprecated-declarations" } - libdirs { "/usr/local/lib" } - links { "glfw3", "GLEW" } - linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" } - - configuration "Debug" - defines { "DEBUG" } - flags { "Symbols"} - - configuration "Release" - defines { "NDEBUG" } - flags { "Optimize"} - diff --git a/experimental/tinyobj_loader_opt.h b/experimental/tinyobj_loader_opt.h deleted file mode 100644 index d2b007db..00000000 --- a/experimental/tinyobj_loader_opt.h +++ /dev/null @@ -1,1722 +0,0 @@ -// -// Optimized wavefront .obj loader. -// Requires lfpAlloc and C++11 -// - -/* -The MIT License (MIT) - -Copyright (c) 2012-2017 Syoyo Fujita and many contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -#ifndef TINOBJ_LOADER_OPT_H_ -#define TINOBJ_LOADER_OPT_H_ - -#ifdef _WIN32 -#define atoll(S) _atoi64(S) -#include -#else -#include -#include -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // C++11 -#include // C++11 -#include // C++11 - -#include "lfpAlloc/Allocator.hpp" - -namespace tinyobj_opt { - -// ---------------------------------------------------------------------------- -// Small vector class useful for multi-threaded environment. -// -// stack_container.h -// -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This allocator can be used with STL containers to provide a stack buffer -// from which to allocate memory and overflows onto the heap. This stack buffer -// would be allocated on the stack and allows us to avoid heap operations in -// some situations. -// -// STL likes to make copies of allocators, so the allocator itself can't hold -// the data. Instead, we make the creator responsible for creating a -// StackAllocator::Source which contains the data. Copying the allocator -// merely copies the pointer to this shared source, so all allocators created -// based on our allocator will share the same stack buffer. -// -// This stack buffer implementation is very simple. The first allocation that -// fits in the stack buffer will use the stack buffer. Any subsequent -// allocations will not use the stack buffer, even if there is unused room. -// This makes it appropriate for array-like containers, but the caller should -// be sure to reserve() in the container up to the stack buffer size. Otherwise -// the container will allocate a small array which will "use up" the stack -// buffer. -template -class StackAllocator : public std::allocator { - public: - typedef typename std::allocator::pointer pointer; - typedef typename std::allocator::size_type size_type; - - // Backing store for the allocator. The container owner is responsible for - // maintaining this for as long as any containers using this allocator are - // live. - struct Source { - Source() : used_stack_buffer_(false) {} - - // Casts the buffer in its right type. - T *stack_buffer() { return reinterpret_cast(stack_buffer_); } - const T *stack_buffer() const { - return reinterpret_cast(stack_buffer_); - } - - // - // IMPORTANT: Take care to ensure that stack_buffer_ is aligned - // since it is used to mimic an array of T. - // Be careful while declaring any unaligned types (like bool) - // before stack_buffer_. - // - - // The buffer itself. It is not of type T because we don't want the - // constructors and destructors to be automatically called. Define a POD - // buffer of the right size instead. - char stack_buffer_[sizeof(T[stack_capacity])]; - - // Set when the stack buffer is used for an allocation. We do not track - // how much of the buffer is used, only that somebody is using it. - bool used_stack_buffer_; - }; - - // Used by containers when they want to refer to an allocator of type U. - template - struct rebind { - typedef StackAllocator other; - }; - - // For the straight up copy c-tor, we can share storage. - StackAllocator(const StackAllocator &rhs) - : source_(rhs.source_) {} - - // ISO C++ requires the following constructor to be defined, - // and std::vector in VC++2008SP1 Release fails with an error - // in the class _Container_base_aux_alloc_real (from ) - // if the constructor does not exist. - // For this constructor, we cannot share storage; there's - // no guarantee that the Source buffer of Ts is large enough - // for Us. - // TODO(Google): If we were fancy pants, perhaps we could share storage - // iff sizeof(T) == sizeof(U). - template - StackAllocator(const StackAllocator &other) - : source_(NULL) { - (void)other; - } - - explicit StackAllocator(Source *source) : source_(source) {} - - // Actually do the allocation. Use the stack buffer if nobody has used it yet - // and the size requested fits. Otherwise, fall through to the standard - // allocator. - pointer allocate(size_type n, void *hint = 0) { - if (source_ != NULL && !source_->used_stack_buffer_ && - n <= stack_capacity) { - source_->used_stack_buffer_ = true; - return source_->stack_buffer(); - } else { - return std::allocator::allocate(n, hint); - } - } - - // Free: when trying to free the stack buffer, just mark it as free. For - // non-stack-buffer pointers, just fall though to the standard allocator. - void deallocate(pointer p, size_type n) { - if (source_ != NULL && p == source_->stack_buffer()) - source_->used_stack_buffer_ = false; - else - std::allocator::deallocate(p, n); - } - - private: - Source *source_; -}; - -// A wrapper around STL containers that maintains a stack-sized buffer that the -// initial capacity of the vector is based on. Growing the container beyond the -// stack capacity will transparently overflow onto the heap. The container must -// support reserve(). -// -// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this -// type. This object is really intended to be used only internally. You'll want -// to use the wrappers below for different types. -template -class StackContainer { - public: - typedef TContainerType ContainerType; - typedef typename ContainerType::value_type ContainedType; - typedef StackAllocator Allocator; - - // Allocator must be constructed before the container! - StackContainer() : allocator_(&stack_data_), container_(allocator_) { - // Make the container use the stack allocation by reserving our buffer size - // before doing anything else. - container_.reserve(stack_capacity); - } - - // Getters for the actual container. - // - // Danger: any copies of this made using the copy constructor must have - // shorter lifetimes than the source. The copy will share the same allocator - // and therefore the same stack buffer as the original. Use std::copy to - // copy into a "real" container for longer-lived objects. - ContainerType &container() { return container_; } - const ContainerType &container() const { return container_; } - - // Support operator-> to get to the container. This allows nicer syntax like: - // StackContainer<...> foo; - // std::sort(foo->begin(), foo->end()); - ContainerType *operator->() { return &container_; } - const ContainerType *operator->() const { return &container_; } - -#ifdef UNIT_TEST - // Retrieves the stack source so that that unit tests can verify that the - // buffer is being used properly. - const typename Allocator::Source &stack_data() const { return stack_data_; } -#endif - - protected: - typename Allocator::Source stack_data_; - unsigned char pad_[7]; - Allocator allocator_; - ContainerType container_; - - // DISALLOW_EVIL_CONSTRUCTORS(StackContainer); - StackContainer(const StackContainer &); - void operator=(const StackContainer &); -}; - -// StackVector -// -// Example: -// StackVector foo; -// foo->push_back(22); // we have overloaded operator-> -// foo[0] = 10; // as well as operator[] -template -class StackVector - : public StackContainer >, - stack_capacity> { - public: - StackVector() - : StackContainer >, - stack_capacity>() {} - - // We need to put this in STL containers sometimes, which requires a copy - // constructor. We can't call the regular copy constructor because that will - // take the stack buffer from the original. Here, we create an empty object - // and make a stack buffer of its own. - StackVector(const StackVector &other) - : StackContainer >, - stack_capacity>() { - this->container().assign(other->begin(), other->end()); - } - - StackVector &operator=( - const StackVector &other) { - this->container().assign(other->begin(), other->end()); - return *this; - } - - // Vectors are commonly indexed, which isn't very convenient even with - // operator-> (using "->at()" does exception stuff we don't want). - T &operator[](size_t i) { return this->container().operator[](i); } - const T &operator[](size_t i) const { - return this->container().operator[](i); - } -}; - -// ---------------------------------------------------------------------------- - -typedef struct { - std::string name; - - float ambient[3]; - float diffuse[3]; - float specular[3]; - float transmittance[3]; - float emission[3]; - float shininess; - float ior; // index of refraction - float dissolve; // 1 == opaque; 0 == fully transparent - // illumination model (see http://www.fileformat.info/format/material/) - int illum; - - int dummy; // Suppress padding warning. - - std::string ambient_texname; // map_Ka - std::string diffuse_texname; // map_Kd - std::string specular_texname; // map_Ks - std::string specular_highlight_texname; // map_Ns - std::string bump_texname; // map_bump, bump - std::string displacement_texname; // disp - std::string alpha_texname; // map_d - - // PBR extension - // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr - float roughness; // [0, 1] default 0 - float metallic; // [0, 1] default 0 - float sheen; // [0, 1] default 0 - float clearcoat_thickness; // [0, 1] default 0 - float clearcoat_roughness; // [0, 1] default 0 - float anisotropy; // aniso. [0, 1] default 0 - float anisotropy_rotation; // anisor. [0, 1] default 0 - std::string roughness_texname; // map_Pr - std::string metallic_texname; // map_Pm - std::string sheen_texname; // map_Ps - std::string emissive_texname; // map_Ke - std::string normal_texname; // norm. For normal mapping. - - std::map unknown_parameter; -} material_t; - -typedef struct { - std::string name; // group name or object name. - // Shape's corresponding faces are accessed by attrib.indices[face_offset, - // face_offset + length] NOTE: you'll need to sum up - // attrib.face_num_verts[face_offset, face_offset + length] to find actual - // number of faces. - unsigned int face_offset; - unsigned int length; -} shape_t; - -struct index_t { - int vertex_index, texcoord_index, normal_index; - index_t() : vertex_index(-1), texcoord_index(-1), normal_index(-1) {} - explicit index_t(int idx) - : vertex_index(idx), texcoord_index(idx), normal_index(idx) {} - index_t(int vidx, int vtidx, int vnidx) - : vertex_index(vidx), texcoord_index(vtidx), normal_index(vnidx) {} -}; - -typedef struct { - std::vector > vertices; - std::vector > normals; - std::vector > texcoords; - std::vector > indices; - - // # of vertices for each face. - // 3 for triangle, 4 for qual, ... - // If triangulation is enabled and the original face are quad, - // face_num_verts will be 6(3 + 3) - std::vector > face_num_verts; - - // Per-face material IDs. - std::vector > material_ids; -} attrib_t; - -typedef StackVector ShortString; - -#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) -#define IS_DIGIT(x) \ - (static_cast((x) - '0') < static_cast(10)) -#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) - -static inline void skip_space(const char **token) { - while ((*token)[0] == ' ' || (*token)[0] == '\t') { - (*token)++; - } -} - -static inline void skip_space_and_cr(const char **token) { - while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') { - (*token)++; - } -} - -static inline int until_space(const char *token) { - const char *p = token; - while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r') { - p++; - } - - return p - token; -} - -static inline int length_until_newline(const char *token, int n) { - int len = 0; - - // Assume token[n-1] = '\0' - for (len = 0; len < n - 1; len++) { - if (token[len] == '\n') { - break; - } - if ((token[len] == '\r') && ((len < (n - 2)) && (token[len + 1] != '\n'))) { - break; - } - } - - return len; -} - -// http://stackoverflow.com/questions/5710091/how-does-atoi-function-in-c-work -static inline int my_atoi(const char *c) { - int value = 0; - int sign = 1; - if (*c == '+' || *c == '-') { - if (*c == '-') sign = -1; - c++; - } - while (((*c) >= '0') && ((*c) <= '9')) { // isdigit(*c) - value *= 10; - value += (int)(*c - '0'); - c++; - } - return value * sign; -} - -// Make index zero-base, and also support relative index. -static inline int fixIndex(int idx, int n) { - if (idx > 0) return idx - 1; - if (idx == 0) return 0; - return n + idx; // negative value = relative -} - -// Parse raw triples: i, i/j/k, i//k, i/j -static index_t parseRawTriple(const char **token) { - index_t vi( - static_cast(0x80000000)); // 0x80000000 = -2147483648 = invalid - - vi.vertex_index = my_atoi((*token)); - while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && - (*token)[0] != '\t' && (*token)[0] != '\r') { - (*token)++; - } - if ((*token)[0] != '/') { - return vi; - } - (*token)++; - - // i//k - if ((*token)[0] == '/') { - (*token)++; - vi.normal_index = my_atoi((*token)); - while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && - (*token)[0] != '\t' && (*token)[0] != '\r') { - (*token)++; - } - return vi; - } - - // i/j/k or i/j - vi.texcoord_index = my_atoi((*token)); - while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && - (*token)[0] != '\t' && (*token)[0] != '\r') { - (*token)++; - } - if ((*token)[0] != '/') { - return vi; - } - - // i/j/k - (*token)++; // skip '/' - vi.normal_index = my_atoi((*token)); - while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && - (*token)[0] != '\t' && (*token)[0] != '\r') { - (*token)++; - } - return vi; -} - -static inline bool parseString(ShortString *s, const char **token) { - skip_space(token); - size_t e = until_space((*token)); - (*s)->insert((*s)->end(), (*token), (*token) + e); - (*token) += e; - return true; -} - -static inline int parseInt(const char **token) { - skip_space(token); - int i = my_atoi((*token)); - (*token) += until_space((*token)); - return i; -} - -// Tries to parse a floating point number located at s. -// -// s_end should be a location in the string where reading should absolutely -// stop. For example at the end of the string, to prevent buffer overflows. -// -// Parses the following EBNF grammar: -// sign = "+" | "-" ; -// END = ? anything not in digit ? -// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; -// integer = [sign] , digit , {digit} ; -// decimal = integer , ["." , integer] ; -// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; -// -// Valid strings are for example: -// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 -// -// If the parsing is a success, result is set to the parsed value and true -// is returned. -// -// The function is greedy and will parse until any of the following happens: -// - a non-conforming character is encountered. -// - s_end is reached. -// -// The following situations triggers a failure: -// - s >= s_end. -// - parse failure. -// -static bool tryParseDouble(const char *s, const char *s_end, double *result) { - if (s >= s_end) { - return false; - } - - double mantissa = 0.0; - // This exponent is base 2 rather than 10. - // However the exponent we parse is supposed to be one of ten, - // thus we must take care to convert the exponent/and or the - // mantissa to a * 2^E, where a is the mantissa and E is the - // exponent. - // To get the final double we will use ldexp, it requires the - // exponent to be in base 2. - int exponent = 0; - - // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED - // TO JUMP OVER DEFINITIONS. - char sign = '+'; - char exp_sign = '+'; - char const *curr = s; - - // How many characters were read in a loop. - int read = 0; - // Tells whether a loop terminated due to reaching s_end. - bool end_not_reached = false; - - /* - BEGIN PARSING. - */ - - // Find out what sign we've got. - if (*curr == '+' || *curr == '-') { - sign = *curr; - curr++; - } else if (IS_DIGIT(*curr)) { /* Pass through. */ - } else { - goto fail; - } - - // Read the integer part. - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - mantissa *= 10; - mantissa += static_cast(*curr - 0x30); - curr++; - read++; - end_not_reached = (curr != s_end); - } - - // We must make sure we actually got something. - if (read == 0) goto fail; - // We allow numbers of form "#", "###" etc. - if (!end_not_reached) goto assemble; - - // Read the decimal part. - if (*curr == '.') { - curr++; - read = 1; - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - // pow(10.0, -read) - double frac_value = 1.0; - for (int f = 0; f < read; f++) { - frac_value *= 0.1; - } - mantissa += static_cast(*curr - 0x30) * frac_value; - read++; - curr++; - end_not_reached = (curr != s_end); - } - } else if (*curr == 'e' || *curr == 'E') { - } else { - goto assemble; - } - - if (!end_not_reached) goto assemble; - - // Read the exponent part. - if (*curr == 'e' || *curr == 'E') { - curr++; - // Figure out if a sign is present and if it is. - end_not_reached = (curr != s_end); - if (end_not_reached && (*curr == '+' || *curr == '-')) { - exp_sign = *curr; - curr++; - } else if (IS_DIGIT(*curr)) { /* Pass through. */ - } else { - // Empty E is not allowed. - goto fail; - } - - read = 0; - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - exponent *= 10; - exponent += static_cast(*curr - 0x30); - curr++; - read++; - end_not_reached = (curr != s_end); - } - exponent *= (exp_sign == '+' ? 1 : -1); - if (read == 0) goto fail; - } - -assemble : - *result = (sign == '+' ? 1 : -1) * - (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) - : mantissa); - return true; -fail: - return false; -} - -static inline float parseFloat(const char **token) { - skip_space(token); -#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER - float f = static_cast(atof(*token)); - (*token) += strcspn((*token), " \t\r"); -#else - const char *end = (*token) + until_space((*token)); - double val = 0.0; - tryParseDouble((*token), end, &val); - float f = static_cast(val); - (*token) = end; -#endif - return f; -} - -static inline void parseFloat2(float *x, float *y, const char **token) { - (*x) = parseFloat(token); - (*y) = parseFloat(token); -} - -static inline void parseFloat3(float *x, float *y, float *z, - const char **token) { - (*x) = parseFloat(token); - (*y) = parseFloat(token); - (*z) = parseFloat(token); -} - -static void InitMaterial(material_t *material) { - material->name = ""; - material->ambient_texname = ""; - material->diffuse_texname = ""; - material->specular_texname = ""; - material->specular_highlight_texname = ""; - material->bump_texname = ""; - material->displacement_texname = ""; - material->alpha_texname = ""; - for (int i = 0; i < 3; i++) { - material->ambient[i] = 0.f; - material->diffuse[i] = 0.f; - material->specular[i] = 0.f; - material->transmittance[i] = 0.f; - material->emission[i] = 0.f; - } - material->illum = 0; - material->dissolve = 1.f; - material->shininess = 1.f; - material->ior = 1.f; - material->unknown_parameter.clear(); -} - -static void LoadMtl(std::map *material_map, - std::vector *materials, - std::istream *inStream) { - // Create a default material anyway. - material_t material; - InitMaterial(&material); - - size_t maxchars = 8192; // Alloc enough size. - std::vector buf(maxchars); // Alloc enough size. - while (inStream->peek() != -1) { - inStream->getline(&buf[0], static_cast(maxchars)); - - std::string linebuf(&buf[0]); - - // Trim trailing whitespace. - if (linebuf.size() > 0) { - linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); - } - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // new mtl - if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { - // flush previous material. - if (!material.name.empty()) { - material_map->insert(std::pair( - material.name, static_cast(materials->size()))); - materials->push_back(material); - } - - // initial temporary material - InitMaterial(&material); - - // set new mtl name - char namebuf[4096]; - token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - sscanf(token, "%s", namebuf); -#endif - material.name = namebuf; - continue; - } - - // ambient - if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { - token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); - material.ambient[0] = r; - material.ambient[1] = g; - material.ambient[2] = b; - continue; - } - - // diffuse - if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { - token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); - material.diffuse[0] = r; - material.diffuse[1] = g; - material.diffuse[2] = b; - continue; - } - - // specular - if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { - token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); - material.specular[0] = r; - material.specular[1] = g; - material.specular[2] = b; - continue; - } - - // transmittance - if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || - (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { - token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); - material.transmittance[0] = r; - material.transmittance[1] = g; - material.transmittance[2] = b; - continue; - } - - // ior(index of refraction) - if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { - token += 2; - material.ior = parseFloat(&token); - continue; - } - - // emission - if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { - token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); - material.emission[0] = r; - material.emission[1] = g; - material.emission[2] = b; - continue; - } - - // shininess - if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { - token += 2; - material.shininess = parseFloat(&token); - continue; - } - - // illum model - if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { - token += 6; - material.illum = parseInt(&token); - continue; - } - - // dissolve - if ((token[0] == 'd' && IS_SPACE(token[1]))) { - token += 1; - material.dissolve = parseFloat(&token); - continue; - } - - if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { - token += 2; - // Invert value of Tr(assume Tr is in range [0, 1]) - material.dissolve = 1.0f - parseFloat(&token); - continue; - } - - // PBR: roughness - if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { - token += 2; - material.roughness = parseFloat(&token); - continue; - } - - // PBR: metallic - if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { - token += 2; - material.metallic = parseFloat(&token); - continue; - } - - // PBR: sheen - if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { - token += 2; - material.sheen = parseFloat(&token); - continue; - } - - // PBR: clearcoat thickness - if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { - token += 2; - material.clearcoat_thickness = parseFloat(&token); - continue; - } - - // PBR: clearcoat roughness - if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { - token += 4; - material.clearcoat_roughness = parseFloat(&token); - continue; - } - - // PBR: anisotropy - if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { - token += 6; - material.anisotropy = parseFloat(&token); - continue; - } - - // PBR: anisotropy rotation - if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { - token += 7; - material.anisotropy_rotation = parseFloat(&token); - continue; - } - - // ambient texture - if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { - token += 7; - material.ambient_texname = token; - continue; - } - - // diffuse texture - if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { - token += 7; - material.diffuse_texname = token; - continue; - } - - // specular texture - if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { - token += 7; - material.specular_texname = token; - continue; - } - - // specular highlight texture - if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { - token += 7; - material.specular_highlight_texname = token; - continue; - } - - // bump texture - if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { - token += 9; - material.bump_texname = token; - continue; - } - - // alpha texture - if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { - token += 6; - material.alpha_texname = token; - continue; - } - - // bump texture - if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { - token += 5; - material.bump_texname = token; - continue; - } - - // displacement texture - if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { - token += 5; - material.displacement_texname = token; - continue; - } - - // PBR: roughness texture - if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { - token += 7; - material.roughness_texname = token; - continue; - } - - // PBR: metallic texture - if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { - token += 7; - material.metallic_texname = token; - continue; - } - - // PBR: sheen texture - if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { - token += 7; - material.sheen_texname = token; - continue; - } - - // PBR: emissive texture - if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { - token += 7; - material.emissive_texname = token; - continue; - } - - // PBR: normal map texture - if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { - token += 5; - material.normal_texname = token; - continue; - } - - // unknown parameter - const char *_space = strchr(token, ' '); - if (!_space) { - _space = strchr(token, '\t'); - } - if (_space) { - std::ptrdiff_t len = _space - token; - std::string key(token, static_cast(len)); - std::string value = _space + 1; - material.unknown_parameter.insert( - std::pair(key, value)); - } - } - // flush last material. - material_map->insert(std::pair( - material.name, static_cast(materials->size()))); - materials->push_back(material); -} - -typedef enum { - COMMAND_EMPTY, - COMMAND_V, - COMMAND_VN, - COMMAND_VT, - COMMAND_F, - COMMAND_G, - COMMAND_O, - COMMAND_USEMTL, - COMMAND_MTLLIB, - -} CommandType; - -typedef struct { - float vx, vy, vz; - float nx, ny, nz; - float tx, ty; - - // for f - std::vector > f; - // std::vector f; - std::vector > f_num_verts; - - const char *group_name; - unsigned int group_name_len; - const char *object_name; - unsigned int object_name_len; - const char *material_name; - unsigned int material_name_len; - - const char *mtllib_name; - unsigned int mtllib_name_len; - - CommandType type; -} Command; - -struct CommandCount { - size_t num_v; - size_t num_vn; - size_t num_vt; - size_t num_f; - size_t num_indices; - CommandCount() { - num_v = 0; - num_vn = 0; - num_vt = 0; - num_f = 0; - num_indices = 0; - } -}; - -class LoadOption { - public: - LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {} - - int req_num_threads; - bool triangulate; - bool verbose; -}; - -/// Parse wavefront .obj(.obj string data is expanded to linear char array -/// `buf') -/// -1 to req_num_threads use the number of HW threads in the running system. -bool parseObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, const char *buf, size_t len, - const LoadOption &option); - -} // namespace tinyobj_opt - -#endif // TINOBJ_LOADER_OPT_H_ - -#ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION - -namespace tinyobj_opt { - -static bool parseLine(Command *command, const char *p, size_t p_len, - bool triangulate = true) { - // @todo { operate directly on pointer `p'. to do that, add range check for - // string operatoion against `p', since `p' is not null-terminated at p[p_len] - // } - char linebuf[4096]; - assert(p_len < 4095); - memcpy(linebuf, p, p_len); - linebuf[p_len] = '\0'; - - const char *token = linebuf; - - command->type = COMMAND_EMPTY; - - // Skip leading space. - skip_space(&token); - - assert(token); - if (token[0] == '\0') { // empty line - return false; - } - - if (token[0] == '#') { // comment line - return false; - } - - // vertex - if (token[0] == 'v' && IS_SPACE((token[1]))) { - token += 2; - float x = 0.0f, y = 0.0f, z = 0.0f; - parseFloat3(&x, &y, &z, &token); - command->vx = x; - command->vy = y; - command->vz = z; - command->type = COMMAND_V; - return true; - } - - // normal - if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { - token += 3; - float x = 0.0f, y = 0.0f, z = 0.0f; - parseFloat3(&x, &y, &z, &token); - command->nx = x; - command->ny = y; - command->nz = z; - command->type = COMMAND_VN; - return true; - } - - // texcoord - if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { - token += 3; - float x = 0.0f, y = 0.0f; - parseFloat2(&x, &y, &token); - command->tx = x; - command->ty = y; - command->type = COMMAND_VT; - return true; - } - - // face - if (token[0] == 'f' && IS_SPACE((token[1]))) { - token += 2; - skip_space(&token); - - StackVector f; - - while (!IS_NEW_LINE(token[0])) { - index_t vi = parseRawTriple(&token); - skip_space_and_cr(&token); - - f->push_back(vi); - } - - command->type = COMMAND_F; - - if (triangulate) { - index_t i0 = f[0]; - index_t i1(-1); - index_t i2 = f[1]; - - for (size_t k = 2; k < f->size(); k++) { - i1 = i2; - i2 = f[k]; - command->f.emplace_back(i0); - command->f.emplace_back(i1); - command->f.emplace_back(i2); - - command->f_num_verts.emplace_back(3); - } - - } else { - for (size_t k = 0; k < f->size(); k++) { - command->f.emplace_back(f[k]); - } - - command->f_num_verts.emplace_back(f->size()); - } - - return true; - } - - // use mtl - if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { - token += 7; - - // int newMaterialId = -1; - // if (material_map.find(namebuf) != material_map.end()) { - // newMaterialId = material_map[namebuf]; - //} else { - // // { error!! material not found } - //} - - // if (newMaterialId != materialId) { - // materialId = newMaterialId; - //} - - // command->material_name = .insert(command->material_name->end(), namebuf, - // namebuf + strlen(namebuf)); - // command->material_name->push_back('\0'); - skip_space(&token); - command->material_name = p + (token - linebuf); - command->material_name_len = - length_until_newline(token, p_len - (token - linebuf)) + 1; - command->type = COMMAND_USEMTL; - - return true; - } - - // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - // By specification, `mtllib` should be appear only once in .obj - token += 7; - - skip_space(&token); - command->mtllib_name = p + (token - linebuf); - command->mtllib_name_len = - length_until_newline(token, p_len - (token - linebuf)) + 1; - command->type = COMMAND_MTLLIB; - - return true; - } - - // group name - if (token[0] == 'g' && IS_SPACE((token[1]))) { - // @todo { multiple group name. } - token += 2; - - command->group_name = p + (token - linebuf); - command->group_name_len = - length_until_newline(token, p_len - (token - linebuf)) + 1; - command->type = COMMAND_G; - - return true; - } - - // object name - if (token[0] == 'o' && IS_SPACE((token[1]))) { - // @todo { multiple object name? } - token += 2; - - command->object_name = p + (token - linebuf); - command->object_name_len = - length_until_newline(token, p_len - (token - linebuf)) + 1; - command->type = COMMAND_O; - - return true; - } - - return false; -} - -typedef struct { - size_t pos; - size_t len; -} LineInfo; - -// Idea come from https://github.com/antonmks/nvParse -// 1. mmap file -// 2. find newline(\n, \r\n, \r) and list of line data. -// 3. Do parallel parsing for each line. -// 4. Reconstruct final mesh data structure. - -// Raise # of max threads if you have more CPU cores... -// In 2018, 32 cores are getting common in high-end workstaion PC. -#define kMaxThreads (32) - -static inline bool is_line_ending(const char *p, size_t i, size_t end_i) { - if (p[i] == '\0') return true; - if (p[i] == '\n') return true; // this includes \r\n - if (p[i] == '\r') { - if (((i + 1) < end_i) && (p[i + 1] != '\n')) { // detect only \r case - return true; - } - } - return false; -} - -bool parseObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, const char *buf, size_t len, - const LoadOption &option) { - attrib->vertices.clear(); - attrib->normals.clear(); - attrib->texcoords.clear(); - attrib->indices.clear(); - attrib->face_num_verts.clear(); - attrib->material_ids.clear(); - shapes->clear(); - - if (len < 1) return false; - - auto num_threads = (option.req_num_threads < 0) - ? std::thread::hardware_concurrency() - : option.req_num_threads; - num_threads = - (std::max)(1, (std::min)(static_cast(num_threads), kMaxThreads)); - - if (option.verbose) { - std::cout << "# of threads = " << num_threads << std::endl; - } - - auto t1 = std::chrono::high_resolution_clock::now(); - - std::vector > - line_infos[kMaxThreads]; - for (size_t t = 0; t < static_cast(num_threads); t++) { - // Pre allocate enough memory. len / 128 / num_threads is just a heuristic - // value. - line_infos[t].reserve(len / 128 / num_threads); - } - - std::chrono::duration ms_linedetection; - std::chrono::duration ms_alloc; - std::chrono::duration ms_parse; - std::chrono::duration ms_load_mtl; - std::chrono::duration ms_merge; - std::chrono::duration ms_construct; - - // 1. Find '\n' and create line data. - { - StackVector workers; - - auto start_time = std::chrono::high_resolution_clock::now(); - auto chunk_size = len / num_threads; - - for (size_t t = 0; t < static_cast(num_threads); t++) { - workers->push_back(std::thread([&, t]() { - auto start_idx = (t + 0) * chunk_size; - auto end_idx = (std::min)((t + 1) * chunk_size, len - 1); - if (t == static_cast((num_threads - 1))) { - end_idx = len - 1; - } - - // true if the line currently read must be added to the current line - // info - bool new_line_found = - (t == 0) || is_line_ending(buf, start_idx - 1, end_idx); - - size_t prev_pos = start_idx; - for (size_t i = start_idx; i < end_idx; i++) { - if (is_line_ending(buf, i, end_idx)) { - if (!new_line_found) { - // first linebreak found in (chunk > 0), and a line before this - // linebreak belongs to previous chunk, so skip it. - prev_pos = i + 1; - new_line_found = true; - } else { - LineInfo info; - info.pos = prev_pos; - info.len = i - prev_pos; - - if (info.len > 0) { - line_infos[t].push_back(info); - } - - prev_pos = i + 1; - } - } - } - - // If at least one line started in this chunk, find where it ends in the - // rest of the buffer - if (new_line_found && (t < num_threads) && (buf[end_idx - 1] != '\n')) { - for (size_t i = end_idx; i < len; i++) { - if (is_line_ending(buf, i, len)) { - LineInfo info; - info.pos = prev_pos; - info.len = i - prev_pos; - - if (info.len > 0) { - line_infos[t].push_back(info); - } - - break; - } - } - } - })); - } - - for (size_t t = 0; t < workers->size(); t++) { - workers[t].join(); - } - - auto end_time = std::chrono::high_resolution_clock::now(); - - ms_linedetection = end_time - start_time; - } - - auto line_sum = 0; - for (size_t t = 0; t < num_threads; t++) { - // std::cout << t << ": # of lines = " << line_infos[t].size() << std::endl; - line_sum += line_infos[t].size(); - } - // std::cout << "# of lines = " << line_sum << std::endl; - - std::vector commands[kMaxThreads]; - - // 2. allocate buffer - auto t_alloc_start = std::chrono::high_resolution_clock::now(); - { - for (size_t t = 0; t < num_threads; t++) { - commands[t].reserve(line_infos[t].size()); - } - } - - CommandCount command_count[kMaxThreads]; - // Array index to `mtllib` line. According to wavefront .obj spec, `mtllib' - // should appear only once in .obj. - int mtllib_t_index = -1; - int mtllib_i_index = -1; - - ms_alloc = std::chrono::high_resolution_clock::now() - t_alloc_start; - - // 2. parse each line in parallel. - { - StackVector workers; - auto t_start = std::chrono::high_resolution_clock::now(); - - for (size_t t = 0; t < num_threads; t++) { - workers->push_back(std::thread([&, t]() { - for (size_t i = 0; i < line_infos[t].size(); i++) { - Command command; - bool ret = parseLine(&command, &buf[line_infos[t][i].pos], - line_infos[t][i].len, option.triangulate); - if (ret) { - if (command.type == COMMAND_V) { - command_count[t].num_v++; - } else if (command.type == COMMAND_VN) { - command_count[t].num_vn++; - } else if (command.type == COMMAND_VT) { - command_count[t].num_vt++; - } else if (command.type == COMMAND_F) { - command_count[t].num_f += command.f.size(); - command_count[t].num_indices += command.f_num_verts.size(); - } - - if (command.type == COMMAND_MTLLIB) { - // Save the indices of the `mtllib` command in `commands` to easily find it later - mtllib_t_index = t; - mtllib_i_index = commands[t].size(); - } - - commands[t].emplace_back(std::move(command)); - } - } - })); - } - - for (size_t t = 0; t < workers->size(); t++) { - workers[t].join(); - } - - auto t_end = std::chrono::high_resolution_clock::now(); - - ms_parse = t_end - t_start; - } - - std::map material_map; - - // Load material(if exits) - if (mtllib_i_index >= 0 && mtllib_t_index >= 0 && - commands[mtllib_t_index][mtllib_i_index].mtllib_name && - commands[mtllib_t_index][mtllib_i_index].mtllib_name_len > 0) { - std::string material_filename = - std::string(commands[mtllib_t_index][mtllib_i_index].mtllib_name, - commands[mtllib_t_index][mtllib_i_index].mtllib_name_len); - // std::cout << "mtllib :" << material_filename << std::endl; - - auto t1 = std::chrono::high_resolution_clock::now(); - if (material_filename.back() == '\r') { - material_filename.pop_back(); - } - std::ifstream ifs(material_filename); - if (ifs.good()) { - LoadMtl(&material_map, materials, &ifs); - - // std::cout << "maetrials = " << materials.size() << std::endl; - - ifs.close(); - } - - auto t2 = std::chrono::high_resolution_clock::now(); - - ms_load_mtl = t2 - t1; - } - - auto command_sum = 0; - for (size_t t = 0; t < num_threads; t++) { - // std::cout << t << ": # of commands = " << commands[t].size() << - // std::endl; - command_sum += commands[t].size(); - } - // std::cout << "# of commands = " << command_sum << std::endl; - - size_t num_v = 0; - size_t num_vn = 0; - size_t num_vt = 0; - size_t num_f = 0; - size_t num_indices = 0; - for (size_t t = 0; t < num_threads; t++) { - num_v += command_count[t].num_v; - num_vn += command_count[t].num_vn; - num_vt += command_count[t].num_vt; - num_f += command_count[t].num_f; - num_indices += command_count[t].num_indices; - } - - // std::cout << "# v " << num_v << std::endl; - // std::cout << "# vn " << num_vn << std::endl; - // std::cout << "# vt " << num_vt << std::endl; - // std::cout << "# f " << num_f << std::endl; - - // 4. merge - // @todo { parallelize merge. } - { - auto t_start = std::chrono::high_resolution_clock::now(); - - attrib->vertices.resize(num_v * 3); - attrib->normals.resize(num_vn * 3); - attrib->texcoords.resize(num_vt * 2); - attrib->indices.resize(num_f); - attrib->face_num_verts.resize(num_indices); - attrib->material_ids.resize(num_indices, -1); - - size_t v_offsets[kMaxThreads]; - size_t n_offsets[kMaxThreads]; - size_t t_offsets[kMaxThreads]; - size_t f_offsets[kMaxThreads]; - size_t face_offsets[kMaxThreads]; - - v_offsets[0] = 0; - n_offsets[0] = 0; - t_offsets[0] = 0; - f_offsets[0] = 0; - face_offsets[0] = 0; - - for (size_t t = 1; t < num_threads; t++) { - v_offsets[t] = v_offsets[t - 1] + command_count[t - 1].num_v; - n_offsets[t] = n_offsets[t - 1] + command_count[t - 1].num_vn; - t_offsets[t] = t_offsets[t - 1] + command_count[t - 1].num_vt; - f_offsets[t] = f_offsets[t - 1] + command_count[t - 1].num_f; - face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_indices; - } - - StackVector workers; - - for (size_t t = 0; t < num_threads; t++) { - workers->push_back(std::thread([&, t]() { - size_t v_count = v_offsets[t]; - size_t n_count = n_offsets[t]; - size_t t_count = t_offsets[t]; - size_t f_count = f_offsets[t]; - size_t face_count = face_offsets[t]; - - for (size_t i = 0; i < commands[t].size(); i++) { - if (commands[t][i].type == COMMAND_EMPTY) { - continue; - } else if (commands[t][i].type == COMMAND_USEMTL) { - if (commands[t][i].material_name && - commands[t][i].material_name_len > 0 && - // check if there are still faces after this command - face_count < num_indices) { - // Find next face - bool found = false; - size_t i_start = i + 1, t_next, i_next; - for (t_next = t; t_next < num_threads; t_next++) { - for (i_next = i_start; i_next < commands[t_next].size(); - i_next++) { - if (commands[t_next][i_next].type == COMMAND_F) { - found = true; - break; - } - } - if (found) break; - i_start = 0; - } - // Assign material to this face - if (found) { - std::string material_name(commands[t][i].material_name, - commands[t][i].material_name_len); - for (size_t k = 0; - k < commands[t_next][i_next].f_num_verts.size(); k++) { - if (material_map.find(material_name) != material_map.end()) { - attrib->material_ids[face_count + k] = - material_map[material_name]; - } else { - // Assign invalid material ID - // Set a different value than the default, to - // prevent following faces from being assigned a valid - // material - attrib->material_ids[face_count + k] = -2; - } - } - } - } - } else if (commands[t][i].type == COMMAND_V) { - attrib->vertices[3 * v_count + 0] = commands[t][i].vx; - attrib->vertices[3 * v_count + 1] = commands[t][i].vy; - attrib->vertices[3 * v_count + 2] = commands[t][i].vz; - v_count++; - } else if (commands[t][i].type == COMMAND_VN) { - attrib->normals[3 * n_count + 0] = commands[t][i].nx; - attrib->normals[3 * n_count + 1] = commands[t][i].ny; - attrib->normals[3 * n_count + 2] = commands[t][i].nz; - n_count++; - } else if (commands[t][i].type == COMMAND_VT) { - attrib->texcoords[2 * t_count + 0] = commands[t][i].tx; - attrib->texcoords[2 * t_count + 1] = commands[t][i].ty; - t_count++; - } else if (commands[t][i].type == COMMAND_F) { - for (size_t k = 0; k < commands[t][i].f.size(); k++) { - index_t &vi = commands[t][i].f[k]; - int vertex_index = fixIndex(vi.vertex_index, v_count); - int texcoord_index = fixIndex(vi.texcoord_index, t_count); - int normal_index = fixIndex(vi.normal_index, n_count); - attrib->indices[f_count + k] = - index_t(vertex_index, texcoord_index, normal_index); - } - for (size_t k = 0; k < commands[t][i].f_num_verts.size(); k++) { - attrib->face_num_verts[face_count + k] = - commands[t][i].f_num_verts[k]; - } - - f_count += commands[t][i].f.size(); - face_count += commands[t][i].f_num_verts.size(); - } - } - })); - } - - for (size_t t = 0; t < workers->size(); t++) { - workers[t].join(); - } - - // To each face with uninitialized material id, - // assign the material id of the last face preceding it that has one - for (size_t face_count = 1; face_count < num_indices; ++face_count) - if (attrib->material_ids[face_count] == -1) - attrib->material_ids[face_count] = attrib->material_ids[face_count - 1]; - - auto t_end = std::chrono::high_resolution_clock::now(); - ms_merge = t_end - t_start; - } - - auto t4 = std::chrono::high_resolution_clock::now(); - - // 5. Construct shape information. - { - auto t_start = std::chrono::high_resolution_clock::now(); - - // @todo { Can we boost the performance by multi-threaded execution? } - int face_count = 0; - shape_t shape; - shape.face_offset = 0; - shape.length = 0; - int face_prev_offset = 0; - for (size_t t = 0; t < num_threads; t++) { - for (size_t i = 0; i < commands[t].size(); i++) { - if (commands[t][i].type == COMMAND_O || - commands[t][i].type == COMMAND_G) { - std::string name; - if (commands[t][i].type == COMMAND_O) { - name = std::string(commands[t][i].object_name, - commands[t][i].object_name_len); - } else { - name = std::string(commands[t][i].group_name, - commands[t][i].group_name_len); - } - - if (face_count == 0) { - // 'o' or 'g' appears before any 'f' - shape.name = name; - shape.face_offset = face_count; - face_prev_offset = face_count; - } else { - if (shapes->size() == 0) { - // 'o' or 'g' after some 'v' lines. - // create a shape with null name - shape.length = face_count - face_prev_offset; - face_prev_offset = face_count; - - shapes->push_back(shape); - - } else { - if ((face_count - face_prev_offset) > 0) { - // push previous shape - shape.length = face_count - face_prev_offset; - shapes->push_back(shape); - face_prev_offset = face_count; - } - } - - // redefine shape. - shape.name = name; - shape.face_offset = face_count; - shape.length = 0; - } - } - if (commands[t][i].type == COMMAND_F) { - // Consider generation of multiple faces per `f` line by triangulation - face_count += commands[t][i].f_num_verts.size(); - } - } - } - - if ((face_count - face_prev_offset) > 0) { - shape.length = face_count - shape.face_offset; - if (shape.length > 0) { - shapes->push_back(shape); - } - } else { - // Guess no 'v' line occurrence after 'o' or 'g', so discards current - // shape information. - } - - auto t_end = std::chrono::high_resolution_clock::now(); - - ms_construct = t_end - t_start; - } - - std::chrono::duration ms_total = t4 - t1; - if (option.verbose) { - std::cout << "total parsing time: " << ms_total.count() << " ms\n"; - std::cout << " line detection : " << ms_linedetection.count() << " ms\n"; - std::cout << " alloc buf : " << ms_alloc.count() << " ms\n"; - std::cout << " parse : " << ms_parse.count() << " ms\n"; - std::cout << " merge : " << ms_merge.count() << " ms\n"; - std::cout << " construct : " << ms_construct.count() << " ms\n"; - std::cout << " mtl load : " << ms_load_mtl.count() << " ms\n"; - std::cout << "# of vertices = " << attrib->vertices.size() << std::endl; - std::cout << "# of normals = " << attrib->normals.size() << std::endl; - std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl; - std::cout << "# of face indices = " << attrib->indices.size() << std::endl; - std::cout << "# of indices = " << attrib->material_ids.size() << std::endl; - std::cout << "# of shapes = " << shapes->size() << std::endl; - } - - return true; -} - -} // namespace tinyobj_opt - -#endif // TINYOBJ_LOADER_OPT_IMPLEMENTATION diff --git a/experimental/trackball.cc b/experimental/trackball.cc deleted file mode 100644 index 27642e83..00000000 --- a/experimental/trackball.cc +++ /dev/null @@ -1,292 +0,0 @@ -/* - * (c) Copyright 1993, 1994, Silicon Graphics, Inc. - * ALL RIGHTS RESERVED - * Permission to use, copy, modify, and distribute this software for - * any purpose and without fee is hereby granted, provided that the above - * copyright notice appear in all copies and that both the copyright notice - * and this permission notice appear in supporting documentation, and that - * the name of Silicon Graphics, Inc. not be used in advertising - * or publicity pertaining to distribution of the software without specific, - * written prior permission. - * - * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" - * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON - * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, - * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY - * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, - * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF - * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN - * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE - * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. - * - * US Government Users Restricted Rights - * Use, duplication, or disclosure by the Government is subject to - * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph - * (c)(1)(ii) of the Rights in Technical Data and Computer Software - * clause at DFARS 252.227-7013 and/or in similar or successor - * clauses in the FAR or the DOD or NASA FAR Supplement. - * Unpublished-- rights reserved under the copyright laws of the - * United States. Contractor/manufacturer is Silicon Graphics, - * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. - * - * OpenGL(TM) is a trademark of Silicon Graphics, Inc. - */ -/* - * Trackball code: - * - * Implementation of a virtual trackball. - * Implemented by Gavin Bell, lots of ideas from Thant Tessman and - * the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129. - * - * Vector manip code: - * - * Original code from: - * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli - * - * Much mucking with by: - * Gavin Bell - */ -#include -#include "trackball.h" - -/* - * This size should really be based on the distance from the center of - * rotation to the point on the object underneath the mouse. That - * point would then track the mouse as closely as possible. This is a - * simple example, though, so that is left as an Exercise for the - * Programmer. - */ -#define TRACKBALLSIZE (0.8) - -/* - * Local function prototypes (not defined in trackball.h) - */ -static float tb_project_to_sphere(float, float, float); -static void normalize_quat(float[4]); - -static void vzero(float *v) { - v[0] = 0.0; - v[1] = 0.0; - v[2] = 0.0; -} - -static void vset(float *v, float x, float y, float z) { - v[0] = x; - v[1] = y; - v[2] = z; -} - -static void vsub(const float *src1, const float *src2, float *dst) { - dst[0] = src1[0] - src2[0]; - dst[1] = src1[1] - src2[1]; - dst[2] = src1[2] - src2[2]; -} - -static void vcopy(const float *v1, float *v2) { - int i; - for (i = 0; i < 3; i++) - v2[i] = v1[i]; -} - -static void vcross(const float *v1, const float *v2, float *cross) { - float temp[3]; - - temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]); - temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]); - temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]); - vcopy(temp, cross); -} - -static float vlength(const float *v) { - return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); -} - -static void vscale(float *v, float div) { - v[0] *= div; - v[1] *= div; - v[2] *= div; -} - -static void vnormal(float *v) { vscale(v, 1.0 / vlength(v)); } - -static float vdot(const float *v1, const float *v2) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; -} - -static void vadd(const float *src1, const float *src2, float *dst) { - dst[0] = src1[0] + src2[0]; - dst[1] = src1[1] + src2[1]; - dst[2] = src1[2] + src2[2]; -} - -/* - * Ok, simulate a track-ball. Project the points onto the virtual - * trackball, then figure out the axis of rotation, which is the cross - * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) - * Note: This is a deformed trackball-- is a trackball in the center, - * but is deformed into a hyperbolic sheet of rotation away from the - * center. This particular function was chosen after trying out - * several variations. - * - * It is assumed that the arguments to this routine are in the range - * (-1.0 ... 1.0) - */ -void trackball(float q[4], float p1x, float p1y, float p2x, float p2y) { - float a[3]; /* Axis of rotation */ - float phi; /* how much to rotate about axis */ - float p1[3], p2[3], d[3]; - float t; - - if (p1x == p2x && p1y == p2y) { - /* Zero rotation */ - vzero(q); - q[3] = 1.0; - return; - } - - /* - * First, figure out z-coordinates for projection of P1 and P2 to - * deformed sphere - */ - vset(p1, p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y)); - vset(p2, p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y)); - - /* - * Now, we want the cross product of P1 and P2 - */ - vcross(p2, p1, a); - - /* - * Figure out how much to rotate around that axis. - */ - vsub(p1, p2, d); - t = vlength(d) / (2.0 * TRACKBALLSIZE); - - /* - * Avoid problems with out-of-control values... - */ - if (t > 1.0) - t = 1.0; - if (t < -1.0) - t = -1.0; - phi = 2.0 * asin(t); - - axis_to_quat(a, phi, q); -} - -/* - * Given an axis and angle, compute quaternion. - */ -void axis_to_quat(float a[3], float phi, float q[4]) { - vnormal(a); - vcopy(a, q); - vscale(q, sin(phi / 2.0)); - q[3] = cos(phi / 2.0); -} - -/* - * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet - * if we are away from the center of the sphere. - */ -static float tb_project_to_sphere(float r, float x, float y) { - float d, t, z; - - d = sqrt(x * x + y * y); - if (d < r * 0.70710678118654752440) { /* Inside sphere */ - z = sqrt(r * r - d * d); - } else { /* On hyperbola */ - t = r / 1.41421356237309504880; - z = t * t / d; - } - return z; -} - -/* - * Given two rotations, e1 and e2, expressed as quaternion rotations, - * figure out the equivalent single rotation and stuff it into dest. - * - * This routine also normalizes the result every RENORMCOUNT times it is - * called, to keep error from creeping in. - * - * NOTE: This routine is written so that q1 or q2 may be the same - * as dest (or each other). - */ - -#define RENORMCOUNT 97 - -void add_quats(float q1[4], float q2[4], float dest[4]) { - static int count = 0; - float t1[4], t2[4], t3[4]; - float tf[4]; - - vcopy(q1, t1); - vscale(t1, q2[3]); - - vcopy(q2, t2); - vscale(t2, q1[3]); - - vcross(q2, q1, t3); - vadd(t1, t2, tf); - vadd(t3, tf, tf); - tf[3] = q1[3] * q2[3] - vdot(q1, q2); - - dest[0] = tf[0]; - dest[1] = tf[1]; - dest[2] = tf[2]; - dest[3] = tf[3]; - - if (++count > RENORMCOUNT) { - count = 0; - normalize_quat(dest); - } -} - -/* - * Quaternions always obey: a^2 + b^2 + c^2 + d^2 = 1.0 - * If they don't add up to 1.0, dividing by their magnitued will - * renormalize them. - * - * Note: See the following for more information on quaternions: - * - * - Shoemake, K., Animating rotation with quaternion curves, Computer - * Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985. - * - Pletinckx, D., Quaternion calculus as a basic tool in computer - * graphics, The Visual Computer 5, 2-13, 1989. - */ -static void normalize_quat(float q[4]) { - int i; - float mag; - - mag = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); - for (i = 0; i < 4; i++) - q[i] /= mag; -} - -/* - * Build a rotation matrix, given a quaternion rotation. - * - */ -void build_rotmatrix(float m[4][4], const float q[4]) { - m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]); - m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]); - m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]); - m[0][3] = 0.0; - - m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]); - m[1][1] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]); - m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]); - m[1][3] = 0.0; - - m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]); - m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]); - m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]); - m[2][3] = 0.0; - - m[3][0] = 0.0; - m[3][1] = 0.0; - m[3][2] = 0.0; - m[3][3] = 1.0; -} diff --git a/experimental/trackball.h b/experimental/trackball.h deleted file mode 100644 index b1f94374..00000000 --- a/experimental/trackball.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * (c) Copyright 1993, 1994, Silicon Graphics, Inc. - * ALL RIGHTS RESERVED - * Permission to use, copy, modify, and distribute this software for - * any purpose and without fee is hereby granted, provided that the above - * copyright notice appear in all copies and that both the copyright notice - * and this permission notice appear in supporting documentation, and that - * the name of Silicon Graphics, Inc. not be used in advertising - * or publicity pertaining to distribution of the software without specific, - * written prior permission. - * - * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" - * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON - * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, - * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY - * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, - * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF - * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN - * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE - * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. - * - * US Government Users Restricted Rights - * Use, duplication, or disclosure by the Government is subject to - * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph - * (c)(1)(ii) of the Rights in Technical Data and Computer Software - * clause at DFARS 252.227-7013 and/or in similar or successor - * clauses in the FAR or the DOD or NASA FAR Supplement. - * Unpublished-- rights reserved under the copyright laws of the - * United States. Contractor/manufacturer is Silicon Graphics, - * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. - * - * OpenGL(TM) is a trademark of Silicon Graphics, Inc. - */ -/* - * trackball.h - * A virtual trackball implementation - * Written by Gavin Bell for Silicon Graphics, November 1988. - */ - -/* - * Pass the x and y coordinates of the last and current positions of - * the mouse, scaled so they are from (-1.0 ... 1.0). - * - * The resulting rotation is returned as a quaternion rotation in the - * first paramater. - */ -void trackball(float q[4], float p1x, float p1y, float p2x, float p2y); - -void negate_quat(float *q, float *qn); - -/* - * Given two quaternions, add them together to get a third quaternion. - * Adding quaternions to get a compound rotation is analagous to adding - * translations to get a compound translation. When incrementally - * adding rotations, the first argument here should be the new - * rotation, the second and third the total rotation (which will be - * over-written with the resulting new total rotation). - */ -void add_quats(float *q1, float *q2, float *dest); - -/* - * A useful function, builds a rotation matrix in Matrix based on - * given quaternion. - */ -void build_rotmatrix(float m[4][4], const float q[4]); - -/* - * This function computes a quaternion based on an axis (defined by - * the given vector) and an angle about which to rotate. The angle is - * expressed in radians. The result is put into the third argument. - */ -void axis_to_quat(float a[3], float phi, float q[4]); diff --git a/experimental/viewer.cc b/experimental/viewer.cc deleted file mode 100644 index 4886b784..00000000 --- a/experimental/viewer.cc +++ /dev/null @@ -1,748 +0,0 @@ -// -// Simple .obj viewer(vertex only) -// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(ENABLE_ZLIB) -#include -#endif - -#if defined(ENABLE_ZSTD) -#include -#endif - -#include - -#ifdef __APPLE__ -#include -#else -#include -#endif - -#include - -#include "trackball.h" - -#define TINYOBJ_LOADER_OPT_IMPLEMENTATION -#include "tinyobj_loader_opt.h" - -typedef struct { - GLuint vb; // vertex buffer - int numTriangles; -} DrawObject; - -std::vector gDrawObjects; - -int width = 768; -int height = 768; - -double prevMouseX, prevMouseY; -bool mouseLeftPressed; -bool mouseMiddlePressed; -bool mouseRightPressed; -float curr_quat[4]; -float prev_quat[4]; -float eye[3], lookat[3], up[3]; - -GLFWwindow* window; - -void CheckErrors(std::string desc) { - GLenum e = glGetError(); - if (e != GL_NO_ERROR) { - fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc.c_str(), e, e); - exit(20); - } -} - -void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) { - float v10[3]; - v10[0] = v1[0] - v0[0]; - v10[1] = v1[1] - v0[1]; - v10[2] = v1[2] - v0[2]; - - float v20[3]; - v20[0] = v2[0] - v0[0]; - v20[1] = v2[1] - v0[1]; - v20[2] = v2[2] - v0[2]; - - N[0] = v20[1] * v10[2] - v20[2] * v10[1]; - N[1] = v20[2] * v10[0] - v20[0] * v10[2]; - N[2] = v20[0] * v10[1] - v20[1] * v10[0]; - - float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2]; - if (len2 > 0.0f) { - float len = sqrtf(len2); - - N[0] /= len; - N[1] /= len; - } -} - -const char *mmap_file(size_t *len, const char* filename) -{ - (*len) = 0; -#ifdef _WIN32 - HANDLE file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); - assert(file != INVALID_HANDLE_VALUE); - - HANDLE fileMapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL); - assert(fileMapping != INVALID_HANDLE_VALUE); - - LPVOID fileMapView = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0); - auto fileMapViewChar = (const char*)fileMapView; - assert(fileMapView != NULL); - - LARGE_INTEGER fileSize; - fileSize.QuadPart = 0; - GetFileSizeEx(file, &fileSize); - - (*len) = static_cast(fileSize.QuadPart); - return fileMapViewChar; - -#else - - FILE* f = fopen(filename, "rb" ); - if (!f) { - fprintf(stderr, "Failed to open file : %s\n", filename); - return nullptr; - } - fseek(f, 0, SEEK_END); - long fileSize = ftell(f); - fclose(f); - - if (fileSize < 16) { - fprintf(stderr, "Empty or invalid .obj : %s\n", filename); - return nullptr; - } - - struct stat sb; - char *p; - int fd; - - fd = open (filename, O_RDONLY); - if (fd == -1) { - perror ("open"); - return nullptr; - } - - if (fstat (fd, &sb) == -1) { - perror ("fstat"); - return nullptr; - } - - if (!S_ISREG (sb.st_mode)) { - fprintf (stderr, "%s is not a file\n", filename); - return nullptr; - } - - p = (char*)mmap (0, fileSize, PROT_READ, MAP_SHARED, fd, 0); - - if (p == MAP_FAILED) { - perror ("mmap"); - return nullptr; - } - - if (close (fd) == -1) { - perror ("close"); - return nullptr; - } - - (*len) = fileSize; - - return p; - -#endif -} - -bool gz_load(std::vector* buf, const char* filename) -{ -#ifdef ENABLE_ZLIB - gzFile file; - file = gzopen (filename, "r"); - if (! file) { - fprintf (stderr, "gzopen of '%s' failed: %s.\n", filename, - strerror (errno)); - exit (EXIT_FAILURE); - return false; - } - while (1) { - int err; - int bytes_read; - unsigned char buffer[1024]; - bytes_read = gzread (file, buffer, 1024); - buf->insert(buf->end(), buffer, buffer + 1024); - //printf ("%s", buffer); - if (bytes_read < 1024) { - if (gzeof (file)) { - break; - } - else { - const char * error_string; - error_string = gzerror (file, & err); - if (err) { - fprintf (stderr, "Error: %s.\n", error_string); - exit (EXIT_FAILURE); - return false; - } - } - } - } - gzclose (file); - return true; -#else - return false; -#endif -} - -#ifdef ENABLE_ZSTD -static off_t fsize_X(const char *filename) -{ - struct stat st; - if (stat(filename, &st) == 0) return st.st_size; - /* error */ - printf("stat: %s : %s \n", filename, strerror(errno)); - exit(1); -} - -static FILE* fopen_X(const char *filename, const char *instruction) -{ - FILE* const inFile = fopen(filename, instruction); - if (inFile) return inFile; - /* error */ - printf("fopen: %s : %s \n", filename, strerror(errno)); - exit(2); -} - -static void* malloc_X(size_t size) -{ - void* const buff = malloc(size); - if (buff) return buff; - /* error */ - printf("malloc: %s \n", strerror(errno)); - exit(3); -} -#endif - -bool zstd_load(std::vector* buf, const char* filename) -{ -#ifdef ENABLE_ZSTD - off_t const buffSize = fsize_X(filename); - FILE* const inFile = fopen_X(filename, "rb"); - void* const buffer = malloc_X(buffSize); - size_t const readSize = fread(buffer, 1, buffSize, inFile); - if (readSize != (size_t)buffSize) { - printf("fread: %s : %s \n", filename, strerror(errno)); - exit(4); - } - fclose(inFile); - - unsigned long long const rSize = ZSTD_getDecompressedSize(buffer, buffSize); - if (rSize==0) { - printf("%s : original size unknown \n", filename); - exit(5); - } - - buf->resize(rSize); - - size_t const dSize = ZSTD_decompress(buf->data(), rSize, buffer, buffSize); - - if (dSize != rSize) { - printf("error decoding %s : %s \n", filename, ZSTD_getErrorName(dSize)); - exit(7); - } - - free(buffer); - - return true; -#else - return false; -#endif -} - -const char* get_file_data(size_t *len, const char* filename) -{ - - const char *ext = strrchr(filename, '.'); - - size_t data_len = 0; - const char* data = nullptr; - - if (strcmp(ext, ".gz") == 0) { - // gzipped data. - - std::vector buf; - bool ret = gz_load(&buf, filename); - - if (ret) { - char *p = static_cast(malloc(buf.size() + 1)); // @fixme { implement deleter } - memcpy(p, &buf.at(0), buf.size()); - p[buf.size()] = '\0'; - data = p; - data_len = buf.size(); - } - - } else if (strcmp(ext, ".zst") == 0) { - printf("zstd\n"); - // Zstandard data. - - std::vector buf; - bool ret = zstd_load(&buf, filename); - - if (ret) { - char *p = static_cast(malloc(buf.size() + 1)); // @fixme { implement deleter } - memcpy(p, &buf.at(0), buf.size()); - p[buf.size()] = '\0'; - data = p; - data_len = buf.size(); - } - } else { - - data = mmap_file(&data_len, filename); - - } - - (*len) = data_len; - return data; -} - - -bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads, bool verbose) -{ - tinyobj_opt::attrib_t attrib; - std::vector shapes; - std::vector materials; - - auto load_t_begin = std::chrono::high_resolution_clock::now(); - size_t data_len = 0; - const char* data = get_file_data(&data_len, filename); - if (data == nullptr) { - printf("failed to load file\n"); - exit(-1); - return false; - } - auto load_t_end = std::chrono::high_resolution_clock::now(); - std::chrono::duration load_ms = load_t_end - load_t_begin; - if (verbose) { - std::cout << "filesize: " << data_len << std::endl; - std::cout << "load time: " << load_ms.count() << " [msecs]" << std::endl; - } - - - tinyobj_opt::LoadOption option; - option.req_num_threads = num_threads; - option.verbose = verbose; - bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option); - - if (!ret) { - std::cerr << "Failed to parse .obj" << std::endl; - return false; - } - bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); - bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); - - //std::cout << "vertices.size() = " << attrib.vertices.size() << std::endl; - //std::cout << "normals.size() = " << attrib.normals.size() << std::endl; - - { - DrawObject o; - std::vector vb; // pos(3float), normal(3float), color(3float) - size_t face_offset = 0; - for (size_t v = 0; v < attrib.face_num_verts.size(); v++) { - assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face(multiple of 3). - for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) { - tinyobj_opt::index_t idx0 = attrib.indices[face_offset+3*f+0]; - tinyobj_opt::index_t idx1 = attrib.indices[face_offset+3*f+1]; - tinyobj_opt::index_t idx2 = attrib.indices[face_offset+3*f+2]; - - float v[3][3]; - for (int k = 0; k < 3; k++) { - int f0 = idx0.vertex_index; - int f1 = idx1.vertex_index; - int f2 = idx2.vertex_index; - assert(f0 >= 0); - assert(f1 >= 0); - assert(f2 >= 0); - - v[0][k] = attrib.vertices[3*f0+k]; - v[1][k] = attrib.vertices[3*f1+k]; - v[2][k] = attrib.vertices[3*f2+k]; - bmin[k] = std::min(v[0][k], bmin[k]); - bmin[k] = std::min(v[1][k], bmin[k]); - bmin[k] = std::min(v[2][k], bmin[k]); - bmax[k] = std::max(v[0][k], bmax[k]); - bmax[k] = std::max(v[1][k], bmax[k]); - bmax[k] = std::max(v[2][k], bmax[k]); - } - - float n[3][3]; - - if (attrib.normals.size() > 0) { - int nf0 = idx0.normal_index; - int nf1 = idx1.normal_index; - int nf2 = idx2.normal_index; - - if (nf0 >= 0 && nf1 >= 0 && nf2 >= 0) { - assert(3*nf0+2 < attrib.normals.size()); - assert(3*nf1+2 < attrib.normals.size()); - assert(3*nf2+2 < attrib.normals.size()); - for (int k = 0; k < 3; k++) { - n[0][k] = attrib.normals[3*nf0+k]; - n[1][k] = attrib.normals[3*nf1+k]; - n[2][k] = attrib.normals[3*nf2+k]; - } - } else { - // compute geometric normal - CalcNormal(n[0], v[0], v[1], v[2]); - n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2]; - n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2]; - } - } else { - // compute geometric normal - CalcNormal(n[0], v[0], v[1], v[2]); - n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2]; - n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2]; - } - - for (int k = 0; k < 3; k++) { - vb.push_back(v[k][0]); - vb.push_back(v[k][1]); - vb.push_back(v[k][2]); - vb.push_back(n[k][0]); - vb.push_back(n[k][1]); - vb.push_back(n[k][2]); - // Use normal as color. - float c[3] = {n[k][0], n[k][1], n[k][2]}; - float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]; - if (len2 > 1.0e-6f) { - float len = sqrtf(len2); - - c[0] /= len; - c[1] /= len; - c[2] /= len; - } - vb.push_back(c[0] * 0.5 + 0.5); - vb.push_back(c[1] * 0.5 + 0.5); - vb.push_back(c[2] * 0.5 + 0.5); - } - } - face_offset += attrib.face_num_verts[v]; - } - - o.vb = 0; - o.numTriangles = 0; - if (vb.size() > 0) { - glGenBuffers(1, &o.vb); - glBindBuffer(GL_ARRAY_BUFFER, o.vb); - glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW); - o.numTriangles = vb.size() / 9 / 3; - } - - gDrawObjects.push_back(o); - } - - printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]); - printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]); - - return true; -} - -void reshapeFunc(GLFWwindow* window, int w, int h) -{ - (void)window; - // for retinal display. - int fb_w, fb_h; - glfwGetFramebufferSize(window, &fb_w, &fb_h); - - glViewport(0, 0, fb_w, fb_h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(45.0, (float)w / (float)h, 0.01f, 100.0f); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - width = w; - height = h; -} - -void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int mods) { - (void)window; - (void)scancode; - (void)mods; - if(action == GLFW_PRESS || action == GLFW_REPEAT){ - // Move camera - float mv_x = 0, mv_y = 0, mv_z = 0; - if(key == GLFW_KEY_K) mv_x += 1; - else if(key == GLFW_KEY_J) mv_x += -1; - else if(key == GLFW_KEY_L) mv_y += 1; - else if(key == GLFW_KEY_H) mv_y += -1; - else if(key == GLFW_KEY_P) mv_z += 1; - else if(key == GLFW_KEY_N) mv_z += -1; - //camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05); - // Close window - if(key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE); - - //init_frame = true; - } -} - -void clickFunc(GLFWwindow* window, int button, int action, int mods){ - (void)window; - (void)mods; - if(button == GLFW_MOUSE_BUTTON_LEFT){ - if(action == GLFW_PRESS){ - mouseLeftPressed = true; - trackball(prev_quat, 0.0, 0.0, 0.0, 0.0); - } else if(action == GLFW_RELEASE){ - mouseLeftPressed = false; - } - } - if(button == GLFW_MOUSE_BUTTON_RIGHT){ - if(action == GLFW_PRESS){ - mouseRightPressed = true; - } else if(action == GLFW_RELEASE){ - mouseRightPressed = false; - } - } - if(button == GLFW_MOUSE_BUTTON_MIDDLE){ - if(action == GLFW_PRESS){ - mouseMiddlePressed = true; - } else if(action == GLFW_RELEASE){ - mouseMiddlePressed = false; - } - } -} - -void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y){ - (void)window; - float rotScale = 1.0f; - float transScale = 2.0f; - - if(mouseLeftPressed){ - trackball(prev_quat, - rotScale * (2.0f * prevMouseX - width) / (float)width, - rotScale * (height - 2.0f * prevMouseY) / (float)height, - rotScale * (2.0f * mouse_x - width) / (float)width, - rotScale * (height - 2.0f * mouse_y) / (float)height); - - add_quats(prev_quat, curr_quat, curr_quat); - } else if (mouseMiddlePressed) { - eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width; - lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width; - eye[1] += transScale * (mouse_y - prevMouseY) / (float)height; - lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height; - } else if (mouseRightPressed) { - eye[2] += transScale * (mouse_y - prevMouseY) / (float)height; - lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height; - } - - // Update mouse point - prevMouseX = mouse_x; - prevMouseY = mouse_y; -} - -void Draw(const std::vector& drawObjects) -{ - glPolygonMode(GL_FRONT, GL_FILL); - glPolygonMode(GL_BACK, GL_FILL); - - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(1.0, 1.0); - glColor3f(1.0f, 1.0f, 1.0f); - for (size_t i = 0; i < drawObjects.size(); i++) { - DrawObject o = drawObjects[i]; - if (o.vb < 1) { - continue; - } - - glBindBuffer(GL_ARRAY_BUFFER, o.vb); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glVertexPointer(3, GL_FLOAT, 36, (const void*)0); - glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); - glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float)*6)); - - glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); - CheckErrors("drawarrays"); - } - - // draw wireframe - glDisable(GL_POLYGON_OFFSET_FILL); - glPolygonMode(GL_FRONT, GL_LINE); - glPolygonMode(GL_BACK, GL_LINE); - - glColor3f(0.0f, 0.0f, 0.4f); - for (size_t i = 0; i < drawObjects.size(); i++) { - DrawObject o = drawObjects[i]; - if (o.vb < 1) { - continue; - } - - glBindBuffer(GL_ARRAY_BUFFER, o.vb); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glVertexPointer(3, GL_FLOAT, 36, (const void*)0); - glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); - - glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); - CheckErrors("drawarrays"); - } -} - -static void Init() { - trackball(curr_quat, 0, 0, 0, 0); - - eye[0] = 0.0f; - eye[1] = 0.0f; - eye[2] = 3.0f; - - lookat[0] = 0.0f; - lookat[1] = 0.0f; - lookat[2] = 0.0f; - - up[0] = 0.0f; - up[1] = 1.0f; - up[2] = 0.0f; -} - - -int main(int argc, char **argv) -{ - if (argc < 2) { - std::cout << "view input.obj " << std::endl; - return 0; - } - - bool benchmark_only = false; - int num_threads = -1; - bool verbose = false; - - if (argc > 2) { - num_threads = atoi(argv[2]); - } - - if (argc > 3) { - benchmark_only = (atoi(argv[3]) > 0) ? true : false; - } - - if (argc > 4) { - verbose = true; - } - - if (benchmark_only) { - - tinyobj_opt::attrib_t attrib; - std::vector shapes; - std::vector materials; - - size_t data_len = 0; - const char* data = get_file_data(&data_len, argv[1]); - if (data == nullptr) { - printf("failed to load file\n"); - exit(-1); - return false; - } - - if (data_len < 4) { - printf("Empty file\n"); - exit(-1); - return false; - } - printf("filesize: %d\n", (int)data_len); - tinyobj_opt::LoadOption option; - option.req_num_threads = num_threads; - option.verbose = true; - - bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option); - - return ret; - } - - Init(); - - std::cout << "Initialize GLFW..." << std::endl; - - if(!glfwInit()){ - std::cerr << "Failed to initialize GLFW." << std::endl; - return -1; - } - - std::cout << "GLFW Init OK." << std::endl; - - - window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL); - if(window == NULL){ - std::cerr << "Failed to open GLFW window. " << std::endl; - glfwTerminate(); - return 1; - } - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - // Callback - glfwSetWindowSizeCallback(window, reshapeFunc); - glfwSetKeyCallback(window, keyboardFunc); - glfwSetMouseButtonCallback(window, clickFunc); - glfwSetCursorPosCallback(window, motionFunc); - - glewExperimental = true; - if (glewInit() != GLEW_OK) { - std::cerr << "Failed to initialize GLEW." << std::endl; - return -1; - } - - reshapeFunc(window, width, height); - - float bmin[3], bmax[3]; - if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads, verbose)) { - printf("failed to load & conv\n"); - return -1; - } - - float maxExtent = 0.5f * (bmax[0] - bmin[0]); - if (maxExtent < 0.5f * (bmax[1] - bmin[1])) { - maxExtent = 0.5f * (bmax[1] - bmin[1]); - } - if (maxExtent < 0.5f * (bmax[2] - bmin[2])) { - maxExtent = 0.5f * (bmax[2] - bmin[2]); - } - - while(glfwWindowShouldClose(window) == GL_FALSE) { - glfwPollEvents(); - glClearColor(0.1f, 0.2f, 0.3f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glEnable(GL_DEPTH_TEST); - - // camera & rotate - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - GLfloat mat[4][4]; - gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], up[1], up[2]); - build_rotmatrix(mat, curr_quat); - glMultMatrixf(&mat[0][0]); - - // Fit to -1, 1 - glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent); - - // Centerize object. - glTranslatef(-0.5*(bmax[0] + bmin[0]), -0.5*(bmax[1] + bmin[1]), -0.5*(bmax[2] + bmin[2])); - - Draw(gDrawObjects); - - glfwSwapBuffers(window); - } - - glfwTerminate(); -} diff --git a/fuzzer/README.md b/fuzzer/README.md deleted file mode 100644 index 1cd63a29..00000000 --- a/fuzzer/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Fuzzing test - -Do fuzzing test for tinyobjloader - -## Supported API - -* [x] ParseFromString - -## Requirements - -* clang with fuzzer support(`-fsanitize=fuzzer`. at least clang 8.0 should work) - -## Setup - -### Ubuntu 18.04 - -``` -$ sudo apt install clang++-8 -$ sudo apt install libfuzzer-8-dev -``` - -Optionally, if you didn't set `update-alternatives` you can set `clang++` to point to `clang++8` - -``` -$ sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 10 -$ sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 10 -``` - -## How to compile - -Fuzz target is compiled with the rest of the project when environment variable `LIB_FUZZING_ENGINE` is defined when running cmake -With clang, you can compile with -``` -$ export LIB_FUZZING_ENGINE=-fsanitize=fuzzer -$ mkdir build && cd build -$ cmake .. -DBUILD_SHARED_LIBS=OFF -$ make -j $(nproc) -``` - -## How to run - -Increase memory limit. e.g. `-rss_limit_mb=2000` -cf libfuzzer.info for all options - -``` -$ ./fuzz_ParseFromString -rss_limit_mb=2000 -``` - -## Regression tests - -See `regression_runner/` diff --git a/fuzzer/afl.tar.gz b/fuzzer/afl.tar.gz deleted file mode 100644 index 40a924f3..00000000 Binary files a/fuzzer/afl.tar.gz and /dev/null differ diff --git a/fuzzer/fuzz_ParseFromString.cc b/fuzzer/fuzz_ParseFromString.cc deleted file mode 100644 index aa45f89a..00000000 --- a/fuzzer/fuzz_ParseFromString.cc +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include -#include -#include - -#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc -#include "tiny_obj_loader.h" - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - tinyobj::ObjReaderConfig reader_config; - tinyobj::ObjReader reader; - if (Size < 2) { - return 0; - } - for (size_t i = 0; i < Size-1; i++) { - if (Data[i] == 0) { - std::string obj_text (reinterpret_cast(Data), i); - std::string mtl_text (reinterpret_cast(Data+i+1), Size-i-1); - reader.ParseFromString(obj_text, mtl_text,reader_config); - return 0; - } - } - return 0; -} - diff --git a/fuzzer/regression_runner/Makefile b/fuzzer/regression_runner/Makefile deleted file mode 100644 index f2c38a0d..00000000 --- a/fuzzer/regression_runner/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - clang++ -fsanitize=address,undefined ../../loader_example.cc diff --git a/fuzzer/regression_runner/README.md b/fuzzer/regression_runner/README.md deleted file mode 100644 index f59b9f6a..00000000 --- a/fuzzer/regression_runner/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Run fuzzer regression tests - -Currently we only support Linux + clang. - -## How to run - -``` -$ make -$ ./a.out ../regressions/ -``` - diff --git a/fuzzer/regressions/clusterfuzz-testcase-minimized-fuzz_ParseFromString-4877060179886080 b/fuzzer/regressions/clusterfuzz-testcase-minimized-fuzz_ParseFromString-4877060179886080 deleted file mode 100644 index e5094497..00000000 Binary files a/fuzzer/regressions/clusterfuzz-testcase-minimized-fuzz_ParseFromString-4877060179886080 and /dev/null differ diff --git a/fuzzer/runner.py b/fuzzer/runner.py deleted file mode 100644 index 0c06d4ba..00000000 --- a/fuzzer/runner.py +++ /dev/null @@ -1,20 +0,0 @@ -import os, sys -import glob -import subprocess - -def main(): - for g in glob.glob("../tests/afl/id*"): - print(g) - - cmd = ["../a.out", g] - - proc = subprocess.Popen(cmd) - try: - outs, errs = proc.communicate(timeout=15) - print(outs) - except TimeoutExpired: - proc.kill() - outs, errs = proc.communicate() - - -main() diff --git a/images/rungholt.jpg b/images/rungholt.jpg deleted file mode 100644 index 17718eb7..00000000 Binary files a/images/rungholt.jpg and /dev/null differ diff --git a/images/sanmugel.png b/images/sanmugel.png deleted file mode 100644 index 32ea150f..00000000 Binary files a/images/sanmugel.png and /dev/null differ diff --git a/jni/Android.mk b/jni/Android.mk deleted file mode 100644 index bc81f8f7..00000000 --- a/jni/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -# A simple test for the minimal standard C++ library -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := tinyobjloader -LOCAL_SRC_FILES := ../tiny_obj_loader.cc - -LOCAL_C_INCLUDES := ../ - -include $(BUILD_STATIC_LIBRARY) diff --git a/jni/Application.mk b/jni/Application.mk deleted file mode 100644 index e5d31919..00000000 --- a/jni/Application.mk +++ /dev/null @@ -1,2 +0,0 @@ -APP_ABI := all -APP_STL := stlport_static diff --git a/jni/Makefile b/jni/Makefile deleted file mode 100644 index 1f138538..00000000 --- a/jni/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - ndk-build diff --git a/jni/README b/jni/README deleted file mode 100644 index f93bc08c..00000000 --- a/jni/README +++ /dev/null @@ -1 +0,0 @@ -Just tests compilation with Android NDK r10. diff --git a/loader_example.cc b/loader_example.cc deleted file mode 100644 index 21feb684..00000000 --- a/loader_example.cc +++ /dev/null @@ -1,437 +0,0 @@ -// -// g++ loader_example.cc -// -#define TINYOBJLOADER_IMPLEMENTATION -#include "tiny_obj_loader.h" - -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#ifdef __cplusplus -extern "C" { -#endif -#include -#include -#ifdef __cplusplus -} -#endif -#pragma comment(lib, "winmm.lib") -#else -#if defined(__unix__) || defined(__APPLE__) -#include -#else -#include -#endif -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif -#endif - -class timerutil { - public: -#ifdef _WIN32 - typedef DWORD time_t; - - timerutil() { ::timeBeginPeriod(1); } - ~timerutil() { ::timeEndPeriod(1); } - - void start() { t_[0] = ::timeGetTime(); } - void end() { t_[1] = ::timeGetTime(); } - - time_t sec() { return (time_t)((t_[1] - t_[0]) / 1000); } - time_t msec() { return (time_t)((t_[1] - t_[0])); } - time_t usec() { return (time_t)((t_[1] - t_[0]) * 1000); } - time_t current() { return ::timeGetTime(); } - -#else -#if defined(__unix__) || defined(__APPLE__) - typedef unsigned long int time_t; - - void start() { gettimeofday(tv + 0, &tz); } - void end() { gettimeofday(tv + 1, &tz); } - - time_t sec() { return static_cast(tv[1].tv_sec - tv[0].tv_sec); } - time_t msec() { - return this->sec() * 1000 + - static_cast((tv[1].tv_usec - tv[0].tv_usec) / 1000); - } - time_t usec() { - return this->sec() * 1000000 + - static_cast(tv[1].tv_usec - tv[0].tv_usec); - } - time_t current() { - struct timeval t; - gettimeofday(&t, NULL); - return static_cast(t.tv_sec * 1000 + t.tv_usec); - } - -#else // C timer - // using namespace std; - typedef clock_t time_t; - - void start() { t_[0] = clock(); } - void end() { t_[1] = clock(); } - - time_t sec() { return (time_t)((t_[1] - t_[0]) / CLOCKS_PER_SEC); } - time_t msec() { return (time_t)((t_[1] - t_[0]) * 1000 / CLOCKS_PER_SEC); } - time_t usec() { return (time_t)((t_[1] - t_[0]) * 1000000 / CLOCKS_PER_SEC); } - time_t current() { return (time_t)clock(); } - -#endif -#endif - - private: -#ifdef _WIN32 - DWORD t_[2]; -#else -#if defined(__unix__) || defined(__APPLE__) - struct timeval tv[2]; - struct timezone tz; -#else - time_t t_[2]; -#endif -#endif -}; - -static void PrintInfo(const tinyobj::attrib_t& attrib, - const std::vector& shapes, - const std::vector& materials) { - std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; - std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; - std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) - << std::endl; - - std::cout << "# of shapes : " << shapes.size() << std::endl; - std::cout << "# of materials : " << materials.size() << std::endl; - - for (size_t v = 0; v < attrib.vertices.size() / 3; v++) { - printf(" v[%ld] = (%f, %f, %f)\n", static_cast(v), - static_cast(attrib.vertices[3 * v + 0]), - static_cast(attrib.vertices[3 * v + 1]), - static_cast(attrib.vertices[3 * v + 2])); - } - - for (size_t v = 0; v < attrib.normals.size() / 3; v++) { - printf(" n[%ld] = (%f, %f, %f)\n", static_cast(v), - static_cast(attrib.normals[3 * v + 0]), - static_cast(attrib.normals[3 * v + 1]), - static_cast(attrib.normals[3 * v + 2])); - } - - for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) { - printf(" uv[%ld] = (%f, %f)\n", static_cast(v), - static_cast(attrib.texcoords[2 * v + 0]), - static_cast(attrib.texcoords[2 * v + 1])); - } - - // For each shape - for (size_t i = 0; i < shapes.size(); i++) { - printf("shape[%ld].name = %s\n", static_cast(i), - shapes[i].name.c_str()); - printf("Size of shape[%ld].mesh.indices: %lu\n", static_cast(i), - static_cast(shapes[i].mesh.indices.size())); - printf("Size of shape[%ld].lines.indices: %lu\n", static_cast(i), - static_cast(shapes[i].lines.indices.size())); - printf("Size of shape[%ld].points.indices: %lu\n", static_cast(i), - static_cast(shapes[i].points.indices.size())); - - size_t index_offset = 0; - - assert(shapes[i].mesh.num_face_vertices.size() == - shapes[i].mesh.material_ids.size()); - - assert(shapes[i].mesh.num_face_vertices.size() == - shapes[i].mesh.smoothing_group_ids.size()); - - printf("shape[%ld].num_faces: %lu\n", static_cast(i), - static_cast(shapes[i].mesh.num_face_vertices.size())); - - // For each face - for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) { - size_t fnum = shapes[i].mesh.num_face_vertices[f]; - - printf(" face[%ld].fnum = %ld\n", static_cast(f), - static_cast(fnum)); - - // For each vertex in the face - for (size_t v = 0; v < fnum; v++) { - tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v]; - printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast(f), - static_cast(v), idx.vertex_index, idx.normal_index, - idx.texcoord_index); - } - - printf(" face[%ld].material_id = %d\n", static_cast(f), - shapes[i].mesh.material_ids[f]); - printf(" face[%ld].smoothing_group_id = %d\n", static_cast(f), - shapes[i].mesh.smoothing_group_ids[f]); - - index_offset += fnum; - } - - printf("shape[%ld].num_tags: %lu\n", static_cast(i), - static_cast(shapes[i].mesh.tags.size())); - for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { - printf(" tag[%ld] = %s ", static_cast(t), - shapes[i].mesh.tags[t].name.c_str()); - printf(" ints: ["); - for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) { - printf("%ld", static_cast(shapes[i].mesh.tags[t].intValues[j])); - if (j < (shapes[i].mesh.tags[t].intValues.size() - 1)) { - printf(", "); - } - } - printf("]"); - - printf(" floats: ["); - for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) { - printf("%f", static_cast( - shapes[i].mesh.tags[t].floatValues[j])); - if (j < (shapes[i].mesh.tags[t].floatValues.size() - 1)) { - printf(", "); - } - } - printf("]"); - - printf(" strings: ["); - for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) { - printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str()); - if (j < (shapes[i].mesh.tags[t].stringValues.size() - 1)) { - printf(", "); - } - } - printf("]"); - printf("\n"); - } - } - - for (size_t i = 0; i < materials.size(); i++) { - printf("material[%ld].name = %s\n", static_cast(i), - materials[i].name.c_str()); - printf(" material.Ka = (%f, %f ,%f)\n", - static_cast(materials[i].ambient[0]), - static_cast(materials[i].ambient[1]), - static_cast(materials[i].ambient[2])); - printf(" material.Kd = (%f, %f ,%f)\n", - static_cast(materials[i].diffuse[0]), - static_cast(materials[i].diffuse[1]), - static_cast(materials[i].diffuse[2])); - printf(" material.Ks = (%f, %f ,%f)\n", - static_cast(materials[i].specular[0]), - static_cast(materials[i].specular[1]), - static_cast(materials[i].specular[2])); - printf(" material.Tr = (%f, %f ,%f)\n", - static_cast(materials[i].transmittance[0]), - static_cast(materials[i].transmittance[1]), - static_cast(materials[i].transmittance[2])); - printf(" material.Ke = (%f, %f ,%f)\n", - static_cast(materials[i].emission[0]), - static_cast(materials[i].emission[1]), - static_cast(materials[i].emission[2])); - printf(" material.Ns = %f\n", - static_cast(materials[i].shininess)); - printf(" material.Ni = %f\n", static_cast(materials[i].ior)); - printf(" material.dissolve = %f\n", - static_cast(materials[i].dissolve)); - printf(" material.illum = %d\n", materials[i].illum); - printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); - printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); - printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); - printf(" material.map_Ns = %s\n", - materials[i].specular_highlight_texname.c_str()); - printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str()); - printf(" bump_multiplier = %f\n", static_cast(materials[i].bump_texopt.bump_multiplier)); - printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str()); - printf(" material.disp = %s\n", materials[i].displacement_texname.c_str()); - printf(" <>\n"); - printf(" material.Pr = %f\n", static_cast(materials[i].roughness)); - printf(" material.Pm = %f\n", static_cast(materials[i].metallic)); - printf(" material.Ps = %f\n", static_cast(materials[i].sheen)); - printf(" material.Pc = %f\n", static_cast(materials[i].clearcoat_thickness)); - printf(" material.Pcr = %f\n", static_cast(materials[i].clearcoat_roughness)); - printf(" material.aniso = %f\n", static_cast(materials[i].anisotropy)); - printf(" material.anisor = %f\n", static_cast(materials[i].anisotropy_rotation)); - printf(" material.map_Ke = %s\n", materials[i].emissive_texname.c_str()); - printf(" material.map_Pr = %s\n", materials[i].roughness_texname.c_str()); - printf(" material.map_Pm = %s\n", materials[i].metallic_texname.c_str()); - printf(" material.map_Ps = %s\n", materials[i].sheen_texname.c_str()); - printf(" material.norm = %s\n", materials[i].normal_texname.c_str()); - std::map::const_iterator it( - materials[i].unknown_parameter.begin()); - std::map::const_iterator itEnd( - materials[i].unknown_parameter.end()); - - for (; it != itEnd; it++) { - printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); - } - printf("\n"); - } -} - -static bool TestLoadObj(const char* filename, const char* basepath = NULL, - bool triangulate = true) { - std::cout << "Loading " << filename << std::endl; - - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - timerutil t; - t.start(); - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename, - basepath, triangulate); - t.end(); - printf("Parsing time: %lu [msecs]\n", t.msec()); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - if (!ret) { - printf("Failed to load/parse .obj.\n"); - return false; - } - - PrintInfo(attrib, shapes, materials); - - return true; -} - -static bool TestStreamLoadObj() { - std::cout << "Stream Loading " << std::endl; - - std::stringstream objStream; - objStream << "mtllib cube.mtl\n" - "\n" - "v 0.000000 2.000000 2.000000\n" - "v 0.000000 0.000000 2.000000\n" - "v 2.000000 0.000000 2.000000\n" - "v 2.000000 2.000000 2.000000\n" - "v 0.000000 2.000000 0.000000\n" - "v 0.000000 0.000000 0.000000\n" - "v 2.000000 0.000000 0.000000\n" - "v 2.000000 2.000000 0.000000\n" - "# 8 vertices\n" - "\n" - "g front cube\n" - "usemtl white\n" - "f 1 2 3 4\n" - "g back cube\n" - "# expects white material\n" - "f 8 7 6 5\n" - "g right cube\n" - "usemtl red\n" - "f 4 3 7 8\n" - "g top cube\n" - "usemtl white\n" - "f 5 1 4 8\n" - "g left cube\n" - "usemtl green\n" - "f 5 6 2 1\n" - "g bottom cube\n" - "usemtl white\n" - "f 2 6 7 3\n" - "# 6 elements"; - - std::string matStream( - "newmtl white\n" - "Ka 0 0 0\n" - "Kd 1 1 1\n" - "Ks 0 0 0\n" - "\n" - "newmtl red\n" - "Ka 0 0 0\n" - "Kd 1 0 0\n" - "Ks 0 0 0\n" - "\n" - "newmtl green\n" - "Ka 0 0 0\n" - "Kd 0 1 0\n" - "Ks 0 0 0\n" - "\n" - "newmtl blue\n" - "Ka 0 0 0\n" - "Kd 0 0 1\n" - "Ks 0 0 0\n" - "\n" - "newmtl light\n" - "Ka 20 20 20\n" - "Kd 1 1 1\n" - "Ks 0 0 0"); - - using namespace tinyobj; - class MaterialStringStreamReader : public MaterialReader { - public: - MaterialStringStreamReader(const std::string& matSStream) - : m_matSStream(matSStream) {} - virtual ~MaterialStringStreamReader() TINYOBJ_OVERRIDE {} - virtual bool operator()(const std::string& matId, - std::vector* materials, - std::map* matMap, - std::string* warn, - std::string* err) TINYOBJ_OVERRIDE { - (void)err; - (void)matId; - LoadMtl(matMap, materials, &m_matSStream, warn, err); - - return true; - } - - private: - std::stringstream m_matSStream; - }; - - MaterialStringStreamReader matSSReader(matStream); - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, &objStream, - &matSSReader); - - if (!err.empty()) { - std::cerr << err << std::endl; - } - - if (!ret) { - return false; - } - - PrintInfo(attrib, shapes, materials); - - return true; -} - -int main(int argc, char** argv) { - if (argc > 1) { - const char* basepath = "models/"; - if (argc > 2) { - basepath = argv[2]; - } - assert(true == TestLoadObj(argv[1], basepath)); - } else { - // assert(true == TestLoadObj("cornell_box.obj")); - // assert(true == TestLoadObj("cube.obj")); - assert(true == TestStreamLoadObj()); - assert(true == - TestLoadObj("models/catmark_torus_creases0.obj", "models/", false)); - } - - return 0; -} diff --git a/mapbox/LICENSE b/mapbox/LICENSE deleted file mode 100644 index 8bafb577..00000000 --- a/mapbox/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -ISC License - -Copyright (c) 2015, Mapbox - -Permission to use, copy, modify, and/or distribute this software for any purpose -with or without fee is hereby granted, provided that the above copyright notice -and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -THIS SOFTWARE. diff --git a/mapbox/earcut.hpp b/mapbox/earcut.hpp deleted file mode 100644 index 01bd7e96..00000000 --- a/mapbox/earcut.hpp +++ /dev/null @@ -1,820 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace mapbox { - -namespace util { - -template struct nth { - inline static typename std::tuple_element::type - get(const T& t) { return std::get(t); }; -}; - -} - -namespace detail { - -template -class Earcut { -public: - std::vector indices; - std::size_t vertices = 0; - - template - void operator()(const Polygon& points); - -private: - struct Node { - Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} - Node(const Node&) = delete; - Node& operator=(const Node&) = delete; - Node(Node&&) = delete; - Node& operator=(Node&&) = delete; - - const N i; - const double x; - const double y; - - // previous and next vertice nodes in a polygon ring - Node* prev = nullptr; - Node* next = nullptr; - - // z-order curve value - int32_t z = 0; - - // previous and next nodes in z-order - Node* prevZ = nullptr; - Node* nextZ = nullptr; - - // indicates whether this is a steiner point - bool steiner = false; - }; - - template Node* linkedList(const Ring& points, const bool clockwise); - Node* filterPoints(Node* start, Node* end = nullptr); - void earcutLinked(Node* ear, int pass = 0); - bool isEar(Node* ear); - bool isEarHashed(Node* ear); - Node* cureLocalIntersections(Node* start); - void splitEarcut(Node* start); - template Node* eliminateHoles(const Polygon& points, Node* outerNode); - Node* eliminateHole(Node* hole, Node* outerNode); - Node* findHoleBridge(Node* hole, Node* outerNode); - bool sectorContainsSector(const Node* m, const Node* p); - void indexCurve(Node* start); - Node* sortLinked(Node* list); - int32_t zOrder(const double x_, const double y_); - Node* getLeftmost(Node* start); - bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; - bool isValidDiagonal(Node* a, Node* b); - double area(const Node* p, const Node* q, const Node* r) const; - bool equals(const Node* p1, const Node* p2); - bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); - bool onSegment(const Node* p, const Node* q, const Node* r); - int sign(double val); - bool intersectsPolygon(const Node* a, const Node* b); - bool locallyInside(const Node* a, const Node* b); - bool middleInside(const Node* a, const Node* b); - Node* splitPolygon(Node* a, Node* b); - template Node* insertNode(std::size_t i, const Point& p, Node* last); - void removeNode(Node* p); - - bool hashing; - double minX, maxX; - double minY, maxY; - double inv_size = 0; - - template > - class ObjectPool { - public: - ObjectPool() { } - ObjectPool(std::size_t blockSize_) { - reset(blockSize_); - } - ~ObjectPool() { - clear(); - } - template - T* construct(Args&&... args) { - if (currentIndex >= blockSize) { - currentBlock = alloc_traits::allocate(alloc, blockSize); - allocations.emplace_back(currentBlock); - currentIndex = 0; - } - T* object = ¤tBlock[currentIndex++]; - alloc_traits::construct(alloc, object, std::forward(args)...); - return object; - } - void reset(std::size_t newBlockSize) { - for (auto allocation : allocations) { - alloc_traits::deallocate(alloc, allocation, blockSize); - } - allocations.clear(); - blockSize = std::max(1, newBlockSize); - currentBlock = nullptr; - currentIndex = blockSize; - } - void clear() { reset(blockSize); } - private: - T* currentBlock = nullptr; - std::size_t currentIndex = 1; - std::size_t blockSize = 1; - std::vector allocations; - Alloc alloc; - typedef typename std::allocator_traits alloc_traits; - }; - ObjectPool nodes; -}; - -template template -void Earcut::operator()(const Polygon& points) { - // reset - indices.clear(); - vertices = 0; - - if (points.empty()) return; - - double x; - double y; - int threshold = 80; - std::size_t len = 0; - - for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { - threshold -= static_cast(points[i].size()); - len += points[i].size(); - } - - //estimate size of nodes and indices - nodes.reset(len * 3 / 2); - indices.reserve(len + points[0].size()); - - Node* outerNode = linkedList(points[0], true); - if (!outerNode || outerNode->prev == outerNode->next) return; - - if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); - - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - hashing = threshold < 0; - if (hashing) { - Node* p = outerNode->next; - minX = maxX = outerNode->x; - minY = maxY = outerNode->y; - do { - x = p->x; - y = p->y; - minX = std::min(minX, x); - minY = std::min(minY, y); - maxX = std::max(maxX, x); - maxY = std::max(maxY, y); - p = p->next; - } while (p != outerNode); - - // minX, minY and size are later used to transform coords into integers for z-order calculation - inv_size = std::max(maxX - minX, maxY - minY); - inv_size = inv_size != .0 ? (1. / inv_size) : .0; - } - - earcutLinked(outerNode); - - nodes.clear(); -} - -// create a circular doubly linked list from polygon points in the specified winding order -template template -typename Earcut::Node* -Earcut::linkedList(const Ring& points, const bool clockwise) { - using Point = typename Ring::value_type; - double sum = 0; - const std::size_t len = points.size(); - std::size_t i, j; - Node* last = nullptr; - - // calculate original winding order of a polygon ring - for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) { - const auto& p1 = points[i]; - const auto& p2 = points[j]; - const double p20 = util::nth<0, Point>::get(p2); - const double p10 = util::nth<0, Point>::get(p1); - const double p11 = util::nth<1, Point>::get(p1); - const double p21 = util::nth<1, Point>::get(p2); - sum += (p20 - p10) * (p11 + p21); - } - - // link points into circular doubly-linked list in the specified winding order - if (clockwise == (sum > 0)) { - for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); - } else { - for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last); - } - - if (last && equals(last, last->next)) { - removeNode(last); - last = last->next; - } - - vertices += len; - - return last; -} - -// eliminate colinear or duplicate points -template -typename Earcut::Node* -Earcut::filterPoints(Node* start, Node* end) { - if (!end) end = start; - - Node* p = start; - bool again; - do { - again = false; - - if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { - removeNode(p); - p = end = p->prev; - - if (p == p->next) break; - again = true; - - } else { - p = p->next; - } - } while (again || p != end); - - return end; -} - -// main ear slicing loop which triangulates a polygon (given as a linked list) -template -void Earcut::earcutLinked(Node* ear, int pass) { - if (!ear) return; - - // interlink polygon nodes in z-order - if (!pass && hashing) indexCurve(ear); - - Node* stop = ear; - Node* prev; - Node* next; - - int iterations = 0; - - // iterate through ears, slicing them one by one - while (ear->prev != ear->next) { - iterations++; - prev = ear->prev; - next = ear->next; - - if (hashing ? isEarHashed(ear) : isEar(ear)) { - // cut off the triangle - indices.emplace_back(prev->i); - indices.emplace_back(ear->i); - indices.emplace_back(next->i); - - removeNode(ear); - - // skipping the next vertice leads to less sliver triangles - ear = next->next; - stop = next->next; - - continue; - } - - ear = next; - - // if we looped through the whole remaining polygon and can't find any more ears - if (ear == stop) { - // try filtering points and slicing again - if (!pass) earcutLinked(filterPoints(ear), 1); - - // if this didn't work, try curing all small self-intersections locally - else if (pass == 1) { - ear = cureLocalIntersections(filterPoints(ear)); - earcutLinked(ear, 2); - - // as a last resort, try splitting the remaining polygon into two - } else if (pass == 2) splitEarcut(ear); - - break; - } - } -} - -// check whether a polygon node forms a valid ear with adjacent nodes -template -bool Earcut::isEar(Node* ear) { - const Node* a = ear->prev; - const Node* b = ear; - const Node* c = ear->next; - - if (area(a, b, c) >= 0) return false; // reflex, can't be an ear - - // now make sure we don't have other points inside the potential ear - Node* p = ear->next->next; - - while (p != ear->prev) { - if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && - area(p->prev, p, p->next) >= 0) return false; - p = p->next; - } - - return true; -} - -template -bool Earcut::isEarHashed(Node* ear) { - const Node* a = ear->prev; - const Node* b = ear; - const Node* c = ear->next; - - if (area(a, b, c) >= 0) return false; // reflex, can't be an ear - - // triangle bbox; min & max are calculated like this for speed - const double minTX = std::min(a->x, std::min(b->x, c->x)); - const double minTY = std::min(a->y, std::min(b->y, c->y)); - const double maxTX = std::max(a->x, std::max(b->x, c->x)); - const double maxTY = std::max(a->y, std::max(b->y, c->y)); - - // z-order range for the current triangle bbox; - const int32_t minZ = zOrder(minTX, minTY); - const int32_t maxZ = zOrder(maxTX, maxTY); - - // first look for points inside the triangle in increasing z-order - Node* p = ear->nextZ; - - while (p && p->z <= maxZ) { - if (p != ear->prev && p != ear->next && - pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && - area(p->prev, p, p->next) >= 0) return false; - p = p->nextZ; - } - - // then look for points in decreasing z-order - p = ear->prevZ; - - while (p && p->z >= minZ) { - if (p != ear->prev && p != ear->next && - pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && - area(p->prev, p, p->next) >= 0) return false; - p = p->prevZ; - } - - return true; -} - -// go through all polygon nodes and cure small local self-intersections -template -typename Earcut::Node* -Earcut::cureLocalIntersections(Node* start) { - Node* p = start; - do { - Node* a = p->prev; - Node* b = p->next->next; - - // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) - if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { - indices.emplace_back(a->i); - indices.emplace_back(p->i); - indices.emplace_back(b->i); - - // remove two nodes involved - removeNode(p); - removeNode(p->next); - - p = start = b; - } - p = p->next; - } while (p != start); - - return filterPoints(p); -} - -// try splitting polygon into two and triangulate them independently -template -void Earcut::splitEarcut(Node* start) { - // look for a valid diagonal that divides the polygon into two - Node* a = start; - do { - Node* b = a->next->next; - while (b != a->prev) { - if (a->i != b->i && isValidDiagonal(a, b)) { - // split the polygon in two by the diagonal - Node* c = splitPolygon(a, b); - - // filter colinear points around the cuts - a = filterPoints(a, a->next); - c = filterPoints(c, c->next); - - // run earcut on each half - earcutLinked(a); - earcutLinked(c); - return; - } - b = b->next; - } - a = a->next; - } while (a != start); -} - -// link every hole into the outer loop, producing a single-ring polygon without holes -template template -typename Earcut::Node* -Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { - const size_t len = points.size(); - - std::vector queue; - for (size_t i = 1; i < len; i++) { - Node* list = linkedList(points[i], false); - if (list) { - if (list == list->next) list->steiner = true; - queue.push_back(getLeftmost(list)); - } - } - std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { - return a->x < b->x; - }); - - // process holes from left to right - for (size_t i = 0; i < queue.size(); i++) { - outerNode = eliminateHole(queue[i], outerNode); - outerNode = filterPoints(outerNode, outerNode->next); - } - - return outerNode; -} - -// find a bridge between vertices that connects hole with an outer ring and and link it -template -typename Earcut::Node* -Earcut::eliminateHole(Node* hole, Node* outerNode) { - Node* bridge = findHoleBridge(hole, outerNode); - if (!bridge) { - return outerNode; - } - - Node* bridgeReverse = splitPolygon(bridge, hole); - - // filter collinear points around the cuts - Node* filteredBridge = filterPoints(bridge, bridge->next); - filterPoints(bridgeReverse, bridgeReverse->next); - - // Check if input node was removed by the filtering - return outerNode == bridge ? filteredBridge : outerNode; -} - -// David Eberly's algorithm for finding a bridge between hole and outer polygon -template -typename Earcut::Node* -Earcut::findHoleBridge(Node* hole, Node* outerNode) { - Node* p = outerNode; - double hx = hole->x; - double hy = hole->y; - double qx = -std::numeric_limits::infinity(); - Node* m = nullptr; - - // find a segment intersected by a ray from the hole's leftmost Vertex to the left; - // segment's endpoint with lesser x will be potential connection Vertex - do { - if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { - double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); - if (x <= hx && x > qx) { - qx = x; - if (x == hx) { - if (hy == p->y) return p; - if (hy == p->next->y) return p->next; - } - m = p->x < p->next->x ? p : p->next; - } - } - p = p->next; - } while (p != outerNode); - - if (!m) return 0; - - if (hx == qx) return m; // hole touches outer segment; pick leftmost endpoint - - // look for points inside the triangle of hole Vertex, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex - - const Node* stop = m; - double tanMin = std::numeric_limits::infinity(); - double tanCur = 0; - - p = m; - double mx = m->x; - double my = m->y; - - do { - if (hx >= p->x && p->x >= mx && hx != p->x && - pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { - - tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential - - if (locallyInside(p, hole) && - (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) { - m = p; - tanMin = tanCur; - } - } - - p = p->next; - } while (p != stop); - - return m; -} - -// whether sector in vertex m contains sector in vertex p in the same coordinates -template -bool Earcut::sectorContainsSector(const Node* m, const Node* p) { - return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0; -} - -// interlink polygon nodes in z-order -template -void Earcut::indexCurve(Node* start) { - assert(start); - Node* p = start; - - do { - p->z = p->z ? p->z : zOrder(p->x, p->y); - p->prevZ = p->prev; - p->nextZ = p->next; - p = p->next; - } while (p != start); - - p->prevZ->nextZ = nullptr; - p->prevZ = nullptr; - - sortLinked(p); -} - -// Simon Tatham's linked list merge sort algorithm -// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html -template -typename Earcut::Node* -Earcut::sortLinked(Node* list) { - assert(list); - Node* p; - Node* q; - Node* e; - Node* tail; - int i, numMerges, pSize, qSize; - int inSize = 1; - - for (;;) { - p = list; - list = nullptr; - tail = nullptr; - numMerges = 0; - - while (p) { - numMerges++; - q = p; - pSize = 0; - for (i = 0; i < inSize; i++) { - pSize++; - q = q->nextZ; - if (!q) break; - } - - qSize = inSize; - - while (pSize > 0 || (qSize > 0 && q)) { - - if (pSize == 0) { - e = q; - q = q->nextZ; - qSize--; - } else if (qSize == 0 || !q) { - e = p; - p = p->nextZ; - pSize--; - } else if (p->z <= q->z) { - e = p; - p = p->nextZ; - pSize--; - } else { - e = q; - q = q->nextZ; - qSize--; - } - - if (tail) tail->nextZ = e; - else list = e; - - e->prevZ = tail; - tail = e; - } - - p = q; - } - - tail->nextZ = nullptr; - - if (numMerges <= 1) return list; - - inSize *= 2; - } -} - -// z-order of a Vertex given coords and size of the data bounding box -template -int32_t Earcut::zOrder(const double x_, const double y_) { - // coords are transformed into non-negative 15-bit integer range - int32_t x = static_cast(32767.0 * (x_ - minX) * inv_size); - int32_t y = static_cast(32767.0 * (y_ - minY) * inv_size); - - x = (x | (x << 8)) & 0x00FF00FF; - x = (x | (x << 4)) & 0x0F0F0F0F; - x = (x | (x << 2)) & 0x33333333; - x = (x | (x << 1)) & 0x55555555; - - y = (y | (y << 8)) & 0x00FF00FF; - y = (y | (y << 4)) & 0x0F0F0F0F; - y = (y | (y << 2)) & 0x33333333; - y = (y | (y << 1)) & 0x55555555; - - return x | (y << 1); -} - -// find the leftmost node of a polygon ring -template -typename Earcut::Node* -Earcut::getLeftmost(Node* start) { - Node* p = start; - Node* leftmost = start; - do { - if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y)) - leftmost = p; - p = p->next; - } while (p != start); - - return leftmost; -} - -// check if a point lies within a convex triangle -template -bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { - return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && - (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && - (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; -} - -// check if a diagonal between two polygon nodes is valid (lies in polygon interior) -template -bool Earcut::isValidDiagonal(Node* a, Node* b) { - return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges - ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible - (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors - (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case -} - -// signed area of a triangle -template -double Earcut::area(const Node* p, const Node* q, const Node* r) const { - return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); -} - -// check if two points are equal -template -bool Earcut::equals(const Node* p1, const Node* p2) { - return p1->x == p2->x && p1->y == p2->y; -} - -// check if two segments intersect -template -bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { - int o1 = sign(area(p1, q1, p2)); - int o2 = sign(area(p1, q1, q2)); - int o3 = sign(area(p2, q2, p1)); - int o4 = sign(area(p2, q2, q1)); - - if (o1 != o2 && o3 != o4) return true; // general case - - if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 - if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 - if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 - if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 - - return false; -} - -// for collinear points p, q, r, check if point q lies on segment pr -template -bool Earcut::onSegment(const Node* p, const Node* q, const Node* r) { - return q->x <= std::max(p->x, r->x) && - q->x >= std::min(p->x, r->x) && - q->y <= std::max(p->y, r->y) && - q->y >= std::min(p->y, r->y); -} - -template -int Earcut::sign(double val) { - return (0.0 < val) - (val < 0.0); -} - -// check if a polygon diagonal intersects any polygon segments -template -bool Earcut::intersectsPolygon(const Node* a, const Node* b) { - const Node* p = a; - do { - if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && - intersects(p, p->next, a, b)) return true; - p = p->next; - } while (p != a); - - return false; -} - -// check if a polygon diagonal is locally inside the polygon -template -bool Earcut::locallyInside(const Node* a, const Node* b) { - return area(a->prev, a, a->next) < 0 ? - area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : - area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; -} - -// check if the middle Vertex of a polygon diagonal is inside the polygon -template -bool Earcut::middleInside(const Node* a, const Node* b) { - const Node* p = a; - bool inside = false; - double px = (a->x + b->x) / 2; - double py = (a->y + b->y) / 2; - do { - if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && - (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) - inside = !inside; - p = p->next; - } while (p != a); - - return inside; -} - -// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits -// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a -// single ring -template -typename Earcut::Node* -Earcut::splitPolygon(Node* a, Node* b) { - Node* a2 = nodes.construct(a->i, a->x, a->y); - Node* b2 = nodes.construct(b->i, b->x, b->y); - Node* an = a->next; - Node* bp = b->prev; - - a->next = b; - b->prev = a; - - a2->next = an; - an->prev = a2; - - b2->next = a2; - a2->prev = b2; - - bp->next = b2; - b2->prev = bp; - - return b2; -} - -// create a node and util::optionally link it with previous one (in a circular doubly linked list) -template template -typename Earcut::Node* -Earcut::insertNode(std::size_t i, const Point& pt, Node* last) { - Node* p = nodes.construct(static_cast(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); - - if (!last) { - p->prev = p; - p->next = p; - - } else { - assert(last); - p->next = last->next; - p->prev = last; - last->next->prev = p; - last->next = p; - } - return p; -} - -template -void Earcut::removeNode(Node* p) { - p->next->prev = p->prev; - p->prev->next = p->next; - - if (p->prevZ) p->prevZ->nextZ = p->nextZ; - if (p->nextZ) p->nextZ->prevZ = p->prevZ; -} -} - -template -std::vector earcut(const Polygon& poly) { - mapbox::detail::Earcut earcut; - earcut(poly); - return std::move(earcut.indices); -} -} diff --git a/models/catmark_torus_creases0.obj b/models/catmark_torus_creases0.obj deleted file mode 100644 index bf18f158..00000000 --- a/models/catmark_torus_creases0.obj +++ /dev/null @@ -1,101 +0,0 @@ -# -# Copyright 2013 Pixar -# -# Licensed under the Apache License, Version 2.0 (the "Apache License") -# with the following modification; you may not use this file except in -# compliance with the Apache License and the following modification to it: -# Section 6. Trademarks. is deleted and replaced with: -# -# 6. Trademarks. This License does not grant permission to use the trade -# names, trademarks, service marks, or product names of the Licensor -# and its affiliates, except as required to comply with Section 4(c) of -# the License and to reproduce the content of the NOTICE file. -# -# You may obtain a copy of the Apache License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the Apache License with the above modification is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the Apache License for the specific -# language governing permissions and limitations under the Apache License. -# -# This file uses centimeters as units for non-parametric coordinates. - -v 1.25052 0.517982 0.353553 -v 0.597239 0.247384 0.353553 -v 0.597239 0.247384 -0.353553 -v 1.25052 0.517982 -0.353553 -v 0.517982 1.25052 0.353553 -v 0.247384 0.597239 0.353553 -v 0.247384 0.597239 -0.353553 -v 0.517982 1.25052 -0.353553 -v -0.517982 1.25052 0.353553 -v -0.247384 0.597239 0.353553 -v -0.247384 0.597239 -0.353553 -v -0.517982 1.25052 -0.353553 -v -1.25052 0.517982 0.353553 -v -0.597239 0.247384 0.353553 -v -0.597239 0.247384 -0.353553 -v -1.25052 0.517982 -0.353553 -v -1.25052 -0.517982 0.353553 -v -0.597239 -0.247384 0.353553 -v -0.597239 -0.247384 -0.353553 -v -1.25052 -0.517982 -0.353553 -v -0.517982 -1.25052 0.353553 -v -0.247384 -0.597239 0.353553 -v -0.247384 -0.597239 -0.353553 -v -0.517982 -1.25052 -0.353553 -v 0.517982 -1.25052 0.353553 -v 0.247384 -0.597239 0.353553 -v 0.247384 -0.597239 -0.353553 -v 0.517982 -1.25052 -0.353553 -v 1.25052 -0.517982 0.353553 -v 0.597239 -0.247384 0.353553 -v 0.597239 -0.247384 -0.353553 -v 1.25052 -0.517982 -0.353553 -vt 0 0 -vt 1 0 -vt 1 1 -vt 0 1 -f 5/1/1 6/2/2 2/3/3 1/4/4 -f 6/1/5 7/2/6 3/3/7 2/4/8 -f 7/1/9 8/2/10 4/3/11 3/4/12 -f 8/1/13 5/2/14 1/3/15 4/4/16 -f 9/1/17 10/2/18 6/3/19 5/4/20 -f 10/1/21 11/2/22 7/3/23 6/4/24 -f 11/1/25 12/2/26 8/3/27 7/4/28 -f 12/1/29 9/2/30 5/3/31 8/4/32 -f 13/1/33 14/2/34 10/3/35 9/4/36 -f 14/1/37 15/2/38 11/3/39 10/4/40 -f 15/1/41 16/2/42 12/3/43 11/4/44 -f 16/1/45 13/2/46 9/3/47 12/4/48 -f 17/1/49 18/2/50 14/3/51 13/4/52 -f 18/1/53 19/2/54 15/3/55 14/4/56 -f 19/1/57 20/2/58 16/3/59 15/4/60 -f 20/1/61 17/2/62 13/3/63 16/4/64 -f 21/1/65 22/2/66 18/3/67 17/4/68 -f 22/1/69 23/2/70 19/3/71 18/4/72 -f 23/1/73 24/2/74 20/3/75 19/4/76 -f 24/1/77 21/2/78 17/3/79 20/4/80 -f 25/1/81 26/2/82 22/3/83 21/4/84 -f 26/1/85 27/2/86 23/3/87 22/4/88 -f 27/1/89 28/2/90 24/3/91 23/4/92 -f 28/1/93 25/2/94 21/3/95 24/4/96 -f 29/1/97 30/2/98 26/3/99 25/4/100 -f 30/1/101 31/2/102 27/3/103 26/4/104 -f 31/1/105 32/2/106 28/3/107 27/4/108 -f 32/1/109 29/2/110 25/3/111 28/4/112 -f 1/1/113 2/2/114 30/3/115 29/4/116 -f 2/1/117 3/2/118 31/3/119 30/4/120 -f 3/1/121 4/2/122 32/3/123 31/4/124 -f 4/1/125 1/2/126 29/3/127 32/4/128 -t crease 2/1/0 1 5 4.7 -t crease 2/1/0 5 9 4.7 -t crease 2/1/0 9 13 4.7 -t crease 2/1/0 13 17 4.7 -t crease 2/1/0 17 21 4.7 -t crease 2/1/0 21 25 4.7 -t crease 2/1/0 25 29 4.7 -t crease 2/1/0 29 1 4.7 diff --git a/models/colorspace-issue-184.mtl b/models/colorspace-issue-184.mtl deleted file mode 100644 index 7f3f9a43..00000000 --- a/models/colorspace-issue-184.mtl +++ /dev/null @@ -1,9 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Kt 0.1 0.2 0.3 -map_Kd -colorspace sRGB -o 0.1 diffuse.jpg -map_Ks -s 0.1 0.2 specular.jpg -map_bump -colorspace linear -bm 3 bumpmap.jpg - diff --git a/models/colorspace-issue-184.obj b/models/colorspace-issue-184.obj deleted file mode 100644 index ae589cbe..00000000 --- a/models/colorspace-issue-184.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib colorspace-issue-184.mtl -o Test -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/cornell_box.mtl b/models/cornell_box.mtl deleted file mode 100644 index d3a1c7a6..00000000 --- a/models/cornell_box.mtl +++ /dev/null @@ -1,24 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/cornell_box.obj b/models/cornell_box.obj deleted file mode 100644 index 43e021f4..00000000 --- a/models/cornell_box.obj +++ /dev/null @@ -1,145 +0,0 @@ -# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. -# original cornell box data - # comment - -# empty line including some space - - -mtllib cornell_box.mtl - -o floor -usemtl white -v 552.8 0.0 0.0 -v 0.0 0.0 0.0 -v 0.0 0.0 559.2 -v 549.6 0.0 559.2 - -v 130.0 0.0 65.0 -v 82.0 0.0 225.0 -v 240.0 0.0 272.0 -v 290.0 0.0 114.0 - -v 423.0 0.0 247.0 -v 265.0 0.0 296.0 -v 314.0 0.0 456.0 -v 472.0 0.0 406.0 - -f 1 2 3 4 -f 8 7 6 5 -f 12 11 10 9 - -o light -usemtl light -v 343.0 548.0 227.0 -v 343.0 548.0 332.0 -v 213.0 548.0 332.0 -v 213.0 548.0 227.0 -f -4 -3 -2 -1 - -o ceiling -usemtl white -v 556.0 548.8 0.0 -v 556.0 548.8 559.2 -v 0.0 548.8 559.2 -v 0.0 548.8 0.0 -f -4 -3 -2 -1 - -o back_wall -usemtl white -v 549.6 0.0 559.2 -v 0.0 0.0 559.2 -v 0.0 548.8 559.2 -v 556.0 548.8 559.2 -f -4 -3 -2 -1 - -o front_wall -usemtl blue -v 549.6 0.0 0 -v 0.0 0.0 0 -v 0.0 548.8 0 -v 556.0 548.8 0 -#f -1 -2 -3 -4 - -o green_wall -usemtl green -v 0.0 0.0 559.2 -v 0.0 0.0 0.0 -v 0.0 548.8 0.0 -v 0.0 548.8 559.2 -f -4 -3 -2 -1 - -o red_wall -usemtl red -v 552.8 0.0 0.0 -v 549.6 0.0 559.2 -v 556.0 548.8 559.2 -v 556.0 548.8 0.0 -f -4 -3 -2 -1 - -o short_block -usemtl white - -v 130.0 165.0 65.0 -v 82.0 165.0 225.0 -v 240.0 165.0 272.0 -v 290.0 165.0 114.0 -f -4 -3 -2 -1 - -v 290.0 0.0 114.0 -v 290.0 165.0 114.0 -v 240.0 165.0 272.0 -v 240.0 0.0 272.0 -f -4 -3 -2 -1 - -v 130.0 0.0 65.0 -v 130.0 165.0 65.0 -v 290.0 165.0 114.0 -v 290.0 0.0 114.0 -f -4 -3 -2 -1 - -v 82.0 0.0 225.0 -v 82.0 165.0 225.0 -v 130.0 165.0 65.0 -v 130.0 0.0 65.0 -f -4 -3 -2 -1 - -v 240.0 0.0 272.0 -v 240.0 165.0 272.0 -v 82.0 165.0 225.0 -v 82.0 0.0 225.0 -f -4 -3 -2 -1 - -o tall_block -usemtl white - -v 423.0 330.0 247.0 -v 265.0 330.0 296.0 -v 314.0 330.0 456.0 -v 472.0 330.0 406.0 -f -4 -3 -2 -1 - -usemtl white -v 423.0 0.0 247.0 -v 423.0 330.0 247.0 -v 472.0 330.0 406.0 -v 472.0 0.0 406.0 -f -4 -3 -2 -1 - -v 472.0 0.0 406.0 -v 472.0 330.0 406.0 -v 314.0 330.0 456.0 -v 314.0 0.0 456.0 -f -4 -3 -2 -1 - -v 314.0 0.0 456.0 -v 314.0 330.0 456.0 -v 265.0 330.0 296.0 -v 265.0 0.0 296.0 -f -4 -3 -2 -1 - -v 265.0 0.0 296.0 -v 265.0 330.0 296.0 -v 423.0 330.0 247.0 -v 423.0 0.0 247.0 -f -4 -3 -2 -1 - diff --git a/models/cornell_box_multimaterial.obj b/models/cornell_box_multimaterial.obj deleted file mode 100644 index 68093bea..00000000 --- a/models/cornell_box_multimaterial.obj +++ /dev/null @@ -1,146 +0,0 @@ -# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. -# original cornell box data - # comment - -# empty line including some space - - -mtllib cornell_box.mtl - -o floor -usemtl white -v 552.8 0.0 0.0 -v 0.0 0.0 0.0 -v 0.0 0.0 559.2 -v 549.6 0.0 559.2 - -v 130.0 0.0 65.0 -v 82.0 0.0 225.0 -v 240.0 0.0 272.0 -v 290.0 0.0 114.0 - -v 423.0 0.0 247.0 -v 265.0 0.0 296.0 -v 314.0 0.0 456.0 -v 472.0 0.0 406.0 - -f 1 2 3 4 -f 8 7 6 5 -f 12 11 10 9 - -o light -usemtl light -v 343.0 548.0 227.0 -v 343.0 548.0 332.0 -v 213.0 548.0 332.0 -v 213.0 548.0 227.0 -f -4 -3 -2 -1 - -o ceiling -usemtl white -v 556.0 548.8 0.0 -v 556.0 548.8 559.2 -v 0.0 548.8 559.2 -v 0.0 548.8 0.0 -f -4 -3 -2 -1 - -o back_wall -usemtl white -v 549.6 0.0 559.2 -v 0.0 0.0 559.2 -v 0.0 548.8 559.2 -v 556.0 548.8 559.2 -f -4 -3 -2 -1 - -o front_wall -usemtl blue -v 549.6 0.0 0 -v 0.0 0.0 0 -v 0.0 548.8 0 -v 556.0 548.8 0 -#f -1 -2 -3 -4 - -o green_wall -usemtl green -v 0.0 0.0 559.2 -v 0.0 0.0 0.0 -v 0.0 548.8 0.0 -v 0.0 548.8 559.2 -f -4 -3 -2 -1 - -o red_wall -usemtl red -v 552.8 0.0 0.0 -v 549.6 0.0 559.2 -v 556.0 548.8 559.2 -v 556.0 548.8 0.0 -f -4 -3 -2 -1 - -o short_block -usemtl white - -v 130.0 165.0 65.0 -v 82.0 165.0 225.0 -v 240.0 165.0 272.0 -v 290.0 165.0 114.0 -f -4 -3 -2 -1 - -v 290.0 0.0 114.0 -v 290.0 165.0 114.0 -v 240.0 165.0 272.0 -v 240.0 0.0 272.0 -f -4 -3 -2 -1 - -v 130.0 0.0 65.0 -v 130.0 165.0 65.0 -v 290.0 165.0 114.0 -v 290.0 0.0 114.0 -f -4 -3 -2 -1 - -v 82.0 0.0 225.0 -v 82.0 165.0 225.0 -v 130.0 165.0 65.0 -v 130.0 0.0 65.0 -f -4 -3 -2 -1 - -v 240.0 0.0 272.0 -v 240.0 165.0 272.0 -v 82.0 165.0 225.0 -v 82.0 0.0 225.0 -f -4 -3 -2 -1 - -o tall_block -usemtl white - -v 423.0 330.0 247.0 -v 265.0 330.0 296.0 -v 314.0 330.0 456.0 -v 472.0 330.0 406.0 -f -4 -3 -2 -1 - -usemtl white -v 423.0 0.0 247.0 -v 423.0 330.0 247.0 -v 472.0 330.0 406.0 -v 472.0 0.0 406.0 -f -4 -3 -2 -1 - -v 472.0 0.0 406.0 -v 472.0 330.0 406.0 -v 314.0 330.0 456.0 -v 314.0 0.0 456.0 -f -4 -3 -2 -1 -usemtl green - -v 314.0 0.0 456.0 -v 314.0 330.0 456.0 -v 265.0 330.0 296.0 -v 265.0 0.0 296.0 -f -4 -3 -2 -1 - -v 265.0 0.0 296.0 -v 265.0 330.0 296.0 -v 423.0 330.0 247.0 -v 423.0 0.0 247.0 -f -4 -3 -2 -1 - diff --git a/models/cube-vertex-w-component.obj b/models/cube-vertex-w-component.obj deleted file mode 100644 index b909f26d..00000000 --- a/models/cube-vertex-w-component.obj +++ /dev/null @@ -1,31 +0,0 @@ -mtllib cube.mtl - -v 0.000000 2.000000 2.000000 0.1 -v 0.000000 0.000000 2.000000 0.2 -v 2.000000 0.000000 2.000000 0.3 -v 2.000000 2.000000 2.000000 0.4 -v 0.000000 2.000000 0.000000 0.5 -v 0.000000 0.000000 0.000000 0.6 -v 2.000000 0.000000 0.000000 0.7 -v 2.000000 2.000000 0.000000 0.8 -# 8 vertices - -g front cube -usemtl white -f 1 2 3 4 -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/models/cube-vertexcol.obj b/models/cube-vertexcol.obj deleted file mode 100644 index 494ce214..00000000 --- a/models/cube-vertexcol.obj +++ /dev/null @@ -1,31 +0,0 @@ -mtllib cube.mtl - -v 0.000000 2.000000 2.000000 0 0 0 -v 0.000000 0.000000 2.000000 0 0 1 -v 2.000000 0.000000 2.000000 0 1 0 -v 2.000000 2.000000 2.000000 0 1 1 -v 0.000000 2.000000 0.000000 1 0 0 -v 0.000000 0.000000 0.000000 1 0 1 -v 2.000000 0.000000 0.000000 1 1 0 -v 2.000000 2.000000 0.000000 1 1 1 -# 8 vertices - -g front cube -usemtl white -f 1 2 3 4 -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/models/cube.mtl b/models/cube.mtl deleted file mode 100644 index d3a1c7a6..00000000 --- a/models/cube.mtl +++ /dev/null @@ -1,24 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/cube.obj b/models/cube.obj deleted file mode 100644 index f12451f0..00000000 --- a/models/cube.obj +++ /dev/null @@ -1,32 +0,0 @@ -mtllib cube.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl white -f 1 2 3 4 -# two white spaces between 'back' and 'cube' -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/models/cube_w_BOM.mtl b/models/cube_w_BOM.mtl deleted file mode 100644 index 96255b54..00000000 --- a/models/cube_w_BOM.mtl +++ /dev/null @@ -1,24 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/cube_w_BOM.obj b/models/cube_w_BOM.obj deleted file mode 100644 index 3c395f04..00000000 --- a/models/cube_w_BOM.obj +++ /dev/null @@ -1,32 +0,0 @@ -mtllib cube_w_BOM.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl white -f 1 2 3 4 -# two white spaces between 'back' and 'cube' -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/models/invalid-face-definition.mtl b/models/invalid-face-definition.mtl deleted file mode 100644 index d3a1c7a6..00000000 --- a/models/invalid-face-definition.mtl +++ /dev/null @@ -1,24 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/invalid-face-definition.obj b/models/invalid-face-definition.obj deleted file mode 100644 index 73ca6783..00000000 --- a/models/invalid-face-definition.obj +++ /dev/null @@ -1,18 +0,0 @@ -mtllib invalid-face-definition.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl white -f 1 -g back cube -# expects white material -f 8 7 diff --git a/models/invalid-relative-texture-index.obj b/models/invalid-relative-texture-index.obj deleted file mode 100644 index ed3a571a..00000000 --- a/models/invalid-relative-texture-index.obj +++ /dev/null @@ -1,2 +0,0 @@ -vt 0 0 -f 1/-1 1/-1 1/-2 \ No newline at end of file diff --git a/models/invalid-relative-vertex-index.obj b/models/invalid-relative-vertex-index.obj deleted file mode 100644 index bddc54a9..00000000 --- a/models/invalid-relative-vertex-index.obj +++ /dev/null @@ -1 +0,0 @@ -f -4 -3 -2 \ No newline at end of file diff --git a/models/issue-138.mtl b/models/issue-138.mtl deleted file mode 100644 index 8894d7e7..00000000 --- a/models/issue-138.mtl +++ /dev/null @@ -1,23 +0,0 @@ -newmtl test1 - Ns 10.0000 - Ni 1.5000 - d 1.0000 - Tr 0.0000 - Tf 1.0000 1.0000 1.0000 - illum 2 - Ka 0.0000 0.0000 0.0000 - Kd 0.5 0.2 0.2 - Ks 0.0000 0.0000 0.0000 - Ke 0.0000 0.0000 0.0000 - - newmtl test2 - Ns 10.0000 - Ni 1.5000 - d 1.0000 - Tr 0.0000 - Tf 1.0000 1.0000 1.0000 - illum 2 - Ka 0.0000 0.0000 0.0000 - Kd 0.2 0.5 0.2 - Ks 0.0000 0.0000 0.0000 - Ke 0.0000 0.0000 0.0000 diff --git a/models/issue-138.obj b/models/issue-138.obj deleted file mode 100644 index 2465920d..00000000 --- a/models/issue-138.obj +++ /dev/null @@ -1,51 +0,0 @@ - -# cube.obj -# - -mtllib issue-138.mtl - -v -0.500000 -0.500000 0.500000 -v 0.500000 -0.500000 0.500000 -v -0.500000 0.500000 0.500000 -v 0.500000 0.500000 0.500000 -v -0.500000 0.500000 -0.500000 -v 0.500000 0.500000 -0.500000 -v -0.500000 -0.500000 -0.500000 -v 0.500000 -0.500000 -0.500000 - -vt 0.000000 0.000000 -vt 1.000000 0.000000 -vt 0.000000 1.000000 -vt 1.000000 1.000000 - -vn 0.000000 0.000000 1.000000 -vn 0.000000 1.000000 0.000000 -vn 0.000000 0.000000 -1.000000 -vn 0.000000 -1.000000 0.000000 -vn 1.000000 0.000000 0.000000 -vn -1.000000 0.000000 0.000000 - -usemtl test1 -g test1 -s 1 -f 1/1/1 2/2/1 3/3/1 -f 3/3/1 2/2/1 4/4/1 - -usemtl test2 -g test2 - -s 2 -f 3/1/2 4/2/2 5/3/2 -f 5/3/2 4/2/2 6/4/2 -s 3 -f 5/4/3 6/3/3 7/2/3 -f 7/2/3 6/3/3 8/1/3 -s 4 -f 7/1/4 8/2/4 1/3/4 -f 1/3/4 8/2/4 2/4/4 -s 5 -f 2/1/5 8/2/5 4/3/5 -f 4/3/5 8/2/5 6/4/5 -s 6 -f 7/1/6 1/2/6 5/3/6 -f 5/3/6 1/2/6 3/4/6 diff --git a/models/issue-140-zero-face-idx.mtl b/models/issue-140-zero-face-idx.mtl deleted file mode 100644 index 990a3457..00000000 --- a/models/issue-140-zero-face-idx.mtl +++ /dev/null @@ -1,2 +0,0 @@ -newmtl main -Kd 1 1 1 diff --git a/models/issue-140-zero-face-idx.obj b/models/issue-140-zero-face-idx.obj deleted file mode 100644 index 21a60609..00000000 --- a/models/issue-140-zero-face-idx.obj +++ /dev/null @@ -1,17 +0,0 @@ -mtllib issue-140-zero-face-idx.mtl - -v -0.5 -0.5 0 -v 0.5 -0.5 0 -v 0.5 0.5 0 -v -0.5 0.5 0 - -vt 0 0 0 -vt 1 0 0 -vt 1 1 0 -vt 0 1 0 - -vn 0 0 -1 - -usemtl main -f 0/0/0 1/1/0 3/3/0 -f 1/1/0 3/3/0 2/2/0 diff --git a/models/issue-161-inconsistent-f.obj b/models/issue-161-inconsistent-f.obj deleted file mode 100644 index 6cd8c43e..00000000 --- a/models/issue-161-inconsistent-f.obj +++ /dev/null @@ -1,37 +0,0 @@ -o cube -mtllib cube.mtl - -v -0.500000 -0.500000 0.500000 -v 0.500000 -0.500000 0.500000 -v -0.500000 0.500000 0.500000 -v 0.500000 0.500000 0.500000 -v -0.500000 0.500000 -0.500000 -v 0.500000 0.500000 -0.500000 -v -0.500000 -0.500000 -0.500000 -v 0.500000 -0.500000 -0.500000 - -vt 0.000000 0.000000 -vt 1.000000 0.000000 -vt 0.000000 1.000000 -vt 1.000000 1.000000 - -g cube -usemtl cube -s 1 -f 1/1 2/2 3/3 -f 3/3 2/2 4/4 -s 2 -f 3/1 4/2 5/3 -f 5/3 4/2 6/4 -s 3 -f 5/4 6/3 7/2 -f 7/2 6/3 8/1 -s 4 -f 7/1 8/2 1/3 -f 1/3 8/2 2/4 -s 5 -f 2/1 8/2 4/3 -f 4/3 8/2 6/4 -s 6 -f 7/1 1/2 5/3 -f 5 1 3 diff --git a/models/issue-162-smoothing-group.mtl b/models/issue-162-smoothing-group.mtl deleted file mode 100644 index 8894d7e7..00000000 --- a/models/issue-162-smoothing-group.mtl +++ /dev/null @@ -1,23 +0,0 @@ -newmtl test1 - Ns 10.0000 - Ni 1.5000 - d 1.0000 - Tr 0.0000 - Tf 1.0000 1.0000 1.0000 - illum 2 - Ka 0.0000 0.0000 0.0000 - Kd 0.5 0.2 0.2 - Ks 0.0000 0.0000 0.0000 - Ke 0.0000 0.0000 0.0000 - - newmtl test2 - Ns 10.0000 - Ni 1.5000 - d 1.0000 - Tr 0.0000 - Tf 1.0000 1.0000 1.0000 - illum 2 - Ka 0.0000 0.0000 0.0000 - Kd 0.2 0.5 0.2 - Ks 0.0000 0.0000 0.0000 - Ke 0.0000 0.0000 0.0000 diff --git a/models/issue-162-smoothing-group.obj b/models/issue-162-smoothing-group.obj deleted file mode 100644 index 0b0c8b35..00000000 --- a/models/issue-162-smoothing-group.obj +++ /dev/null @@ -1,51 +0,0 @@ - -# cube.obj -# - -mtllib issue-162-smoothing-group.mtl - -v -0.500000 -0.500000 0.500000 -v 0.500000 -0.500000 0.500000 -v -0.500000 0.500000 0.500000 -v 0.500000 0.500000 0.500000 -v -0.500000 0.500000 -0.500000 -v 0.500000 0.500000 -0.500000 -v -0.500000 -0.500000 -0.500000 -v 0.500000 -0.500000 -0.500000 - -vt 0.000000 0.000000 -vt 1.000000 0.000000 -vt 0.000000 1.000000 -vt 1.000000 1.000000 - -vn 0.000000 0.000000 1.000000 -vn 0.000000 1.000000 0.000000 -vn 0.000000 0.000000 -1.000000 -vn 0.000000 -1.000000 0.000000 -vn 1.000000 0.000000 0.000000 -vn -1.000000 0.000000 0.000000 - -usemtl test1 -g test1 -s 1 -f 1/1/1 2/2/1 3/3/1 -f 3/3/1 2/2/1 4/4/1 - -usemtl test2 -g test2 - -s off -f 3/1/2 4/2/2 5/3/2 -f 5/3/2 4/2/2 6/4/2 -s 3 -f 5/4/3 6/3/3 7/2/3 -f 7/2/3 6/3/3 8/1/3 -s 4 -f 7/1/4 8/2/4 1/3/4 -f 1/3/4 8/2/4 2/4/4 -s 0 -f 2/1/5 8/2/5 4/3/5 -f 4/3/5 8/2/5 6/4/5 -s 6 -f 7/1/6 1/2/6 5/3/6 -f 5/3/6 1/2/6 3/4/6 diff --git a/models/issue-235-usemtl-then-o.obj b/models/issue-235-usemtl-then-o.obj deleted file mode 100644 index 170d33e4..00000000 --- a/models/issue-235-usemtl-then-o.obj +++ /dev/null @@ -1,21 +0,0 @@ -mtllib issue-235.mtl - -v -1.000000 1.202466 1.000000 -v 1.000000 1.202466 1.000000 -v -1.000000 1.202466 -1.000000 -v 1.000000 1.202466 -1.000000 -vn 0.0000 1.0000 0.0000 -v -1.000000 0.000000 1.000000 -v 1.000000 0.000000 1.000000 -v -1.000000 0.000000 -1.000000 -v 1.000000 0.000000 -1.000000 -vn 0.0000 1.0000 0.0000 - -usemtl None -o Plane.001 -f 1//1 2//1 4//1 - -# Following geometry is ignored without fix for #235 -usemtl None1 -o Plane -f 5//2 6//2 8//2 diff --git a/models/issue-235.mtl b/models/issue-235.mtl deleted file mode 100644 index 63c46a43..00000000 --- a/models/issue-235.mtl +++ /dev/null @@ -1,11 +0,0 @@ -newmtl None -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl None1 -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - - diff --git a/models/issue-244-mtl-searchpaths.obj b/models/issue-244-mtl-searchpaths.obj deleted file mode 100644 index 58abea1c..00000000 --- a/models/issue-244-mtl-searchpaths.obj +++ /dev/null @@ -1,22 +0,0 @@ -# .mtl is located at tests/assets -mtllib issue-244.mtl - -v -1.000000 1.202466 1.000000 -v 1.000000 1.202466 1.000000 -v -1.000000 1.202466 -1.000000 -v 1.000000 1.202466 -1.000000 -vn 0.0000 1.0000 0.0000 -v -1.000000 0.000000 1.000000 -v 1.000000 0.000000 1.000000 -v -1.000000 0.000000 -1.000000 -v 1.000000 0.000000 -1.000000 -vn 0.0000 1.0000 0.0000 - -usemtl None -o Plane.001 -f 1//1 2//1 4//1 - -# Following geometry is ignored without fix for #235 -usemtl None1 -o Plane -f 5//2 6//2 8//2 diff --git a/models/issue-246-usemtl-whitespace.mtl b/models/issue-246-usemtl-whitespace.mtl deleted file mode 100644 index e7c91e33..00000000 --- a/models/issue-246-usemtl-whitespace.mtl +++ /dev/null @@ -1,4 +0,0 @@ -newmtl 1 -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/issue-246-usemtl-whitespace.obj b/models/issue-246-usemtl-whitespace.obj deleted file mode 100644 index f159349b..00000000 --- a/models/issue-246-usemtl-whitespace.obj +++ /dev/null @@ -1,17 +0,0 @@ -# .mtl is located at tests/assets -mtllib issue-246-usemtl-whitespace.mtl - -v -1.000000 1.202466 1.000000 -v 1.000000 1.202466 1.000000 -v -1.000000 1.202466 -1.000000 -v 1.000000 1.202466 -1.000000 -vn 0.0000 1.0000 0.0000 -v -1.000000 0.000000 1.000000 -v 1.000000 0.000000 1.000000 -v -1.000000 0.000000 -1.000000 -v 1.000000 0.000000 -1.000000 -vn 0.0000 1.0000 0.0000 - -usemtl 1 -o Plane.001 -f 1//1 2//1 4//1 diff --git a/models/issue-248-texres-texopt.mtl b/models/issue-248-texres-texopt.mtl deleted file mode 100644 index 537fa15b..00000000 --- a/models/issue-248-texres-texopt.mtl +++ /dev/null @@ -1,25 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 -map_Kd -texres 512 input.jpg - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/issue-248-texres-texopt.obj b/models/issue-248-texres-texopt.obj deleted file mode 100644 index 43058a91..00000000 --- a/models/issue-248-texres-texopt.obj +++ /dev/null @@ -1,32 +0,0 @@ -mtllib issue-248-texres-texopt.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl white -f 1 2 3 4 -# two white spaces between 'back' and 'cube' -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/models/issue-295-trianguation-failure.obj b/models/issue-295-trianguation-failure.obj deleted file mode 100644 index f3b2649c..00000000 --- a/models/issue-295-trianguation-failure.obj +++ /dev/null @@ -1,38 +0,0 @@ -#mtllib invalid.mtl -v 14678.0 0.0 9605.0 -v 14678.0 1.0 9605.0 -v 14678.0 0.0 9606.0 -v 14678.0 1.0 9606.0 -v 14678.0 0.0 9607.0 -v 14678.0 1.0 9607.0 -v 14678.0 0.0 9608.0 -v 14678.0 1.0 9608.0 -v 14679.0 0.0 9605.0 -v 14679.0 1.0 9605.0 -v 14679.0 0.0 9606.0 -v 14679.0 1.0 9606.0 -v 14679.0 0.0 9607.0 -v 14679.0 1.0 9607.0 -v 14679.0 0.0 9608.0 -v 14679.0 1.0 9608.0 -# UV -vt 0.0 0.0 -vt 1.0 0.0 -vt 1.0 1.0 -vt 0.0 1.0 -#usemtl invalid -o invalid -f 9/4 11/1 3/2 1/3 -f 4/1 12/2 10/3 2/4 -f 2/3 10/4 9/1 1/2 -f 3/2 4/3 2/4 1/1 -f 10/3 12/4 11/1 9/2 -f 11/4 13/1 5/2 3/3 -f 6/1 14/2 12/3 4/4 -f 5/2 6/3 4/4 3/1 -f 12/3 14/4 13/1 11/2 -f 13/4 15/1 7/2 5/3 -f 8/1 16/2 14/3 6/4 -f 15/2 16/3 8/4 7/1 -f 7/2 8/3 6/4 5/1 -f 14/3 16/4 15/1 13/2 diff --git a/models/issue-319-002.obj b/models/issue-319-002.obj deleted file mode 100644 index 8e056fa0..00000000 --- a/models/issue-319-002.obj +++ /dev/null @@ -1,39 +0,0 @@ -### -# -# OBJ File Generated by Meshlab -# -#### -# Object ZH2_001.obj -# -# Vertices: 19 -# Faces: 3 -# -#### -v 8219.830078 6406.934082 9.603000 -v 8219.632812 6406.582031 9.603000 -v 8219.632812 6406.582031 9.139000 -v 8219.973633 6405.420898 9.139000 -v 8211.128906 6404.090820 9.139000 -v 8211.128906 6404.090820 9.603000 -v 8211.469727 6402.930176 9.139000 -v 8211.469727 6402.930176 9.603000 -v 8211.133789 6402.831055 9.603000 -v 8210.793945 6403.992188 9.603000 -v 8210.713867 6404.264160 9.603000 -v 8211.840820 6403.038086 9.139000 -v 8219.899414 6404.861816 9.139000 -v 8219.755859 6405.352051 9.139000 -v 8211.985352 6402.544922 9.139000 -v 8232.911133 6378.534180 55.848999 -v 8226.281250 6376.591797 55.848999 -v 8226.341797 6376.384766 55.848999 -v 8233.450195 6378.466797 55.852001 -v 8233.450195 6378.466797 55.852001 -# 19 vertices, 0 vertices normals - -f 2 1 11 10 9 8 6 -f 5 7 12 15 13 14 4 3 -f 18 19 20 16 17 -# 3 faces, 0 coords texture - -# End of File diff --git a/models/issue-319-003.obj b/models/issue-319-003.obj deleted file mode 100644 index 882a25c1..00000000 --- a/models/issue-319-003.obj +++ /dev/null @@ -1,27 +0,0 @@ -#### -# -# OBJ File Generated by Meshlab -# -#### -# Object new 1.obj -# -# Vertices: 10 -# Faces: 1 -# -#### -v 8434.808594 6083.654785 2.387000 -v 8434.808594 6083.654785 71.633003 -v 8432.309570 6092.206055 71.633003 -v 8432.309570 6092.206055 63.955002 -v 8432.309570 6092.206055 2.387000 -v 8433.083984 6089.560059 71.633003 -v 8433.161133 6089.293945 71.633003 -v 8432.309570 6092.206055 64.323997 -v 8432.309570 6092.206055 67.152000 -v 8432.309570 6092.206055 68.078003 -# 10 vertices, 0 vertices normals - -f 6 7 2 1 5 4 8 9 10 3 -# 1 faces, 0 coords texture - -# End of File diff --git a/models/issue-330.obj b/models/issue-330.obj deleted file mode 100644 index aa46631c..00000000 --- a/models/issue-330.obj +++ /dev/null @@ -1,12 +0,0 @@ -v -105.342712 40.184242 -16.056709 -v -105.463989 40.202003 -16.003181 -v -105.564941 40.207558 -15.934708 -v -105.722252 40.151146 -16.112091 -v -105.610237 40.191372 -16.176643 -v -105.667282 40.189800 -15.864197 -v -105.751717 40.125790 -15.794304 -# 7 vertices, 0 vertices normals - -f 2 5 4 3 -f 4 6 3 -# 2 faces, 0 coords texture diff --git a/models/issue-356-leading-spaces-newmtl.mtl b/models/issue-356-leading-spaces-newmtl.mtl deleted file mode 100644 index f5a388e9..00000000 --- a/models/issue-356-leading-spaces-newmtl.mtl +++ /dev/null @@ -1,2 +0,0 @@ -newmtl aaa -Ka 1.000000 1.000000 1.000000 diff --git a/models/issue-356-leading-spaces-newmtl.obj b/models/issue-356-leading-spaces-newmtl.obj deleted file mode 100644 index b41984a8..00000000 --- a/models/issue-356-leading-spaces-newmtl.obj +++ /dev/null @@ -1,2 +0,0 @@ -mtllib issue-356-leading-spaces-newmtl.mtl -usemtl aaa diff --git a/models/issue-389-comment.obj b/models/issue-389-comment.obj deleted file mode 100644 index cf16d926..00000000 --- a/models/issue-389-comment.obj +++ /dev/null @@ -1,44 +0,0 @@ -g Part 1 -v 0.0576127 0.0488792 0.0423 -v 0.0576127 0.0488792 0 -v -0.0483158 0.0488792 0 -v -0.0483158 0.0488792 0.0423 -v -0.0483158 -0.0139454 0 -v -0.0483158 -0.0139454 0.0423 -v 0.0576127 -0.0139454 0 -v 0.0576127 -0.0139454 0.0423 -vn 0 1 0 -vn -1 0 0 -vn 0 -1 0 -vn 1 0 0 -vn 0 0 1 -vn 0 0 -1 -o mesh0 -f 1//1 2//1 3//1 -f 3//1 4//1 1//1 -o mesh1 -f 4//2 3//2 5//2 -f 5//2 6//2 4//2 -o mesh2 -f 6//3 5//3 7//3 -f 7//3 8//3 6//3 -o mesh3 -f 8//4 7//4 2//4 -f 2//4 1//4 8//4 -o mesh4 -f 8//5 1//5 4//5 -f 4//5 6//5 8//5 -o mesh5 -f 5//6 3//6 2//6 -f 2//6 7//6 5//6 - -# Zusätzliche Linien (aus der Oberseite) -o lines -v 0.0576127 0.0488792 0.0423 # Startpunkt Linie 1 (Ecke 1 Oberseite) -v 0.0576127 0.0488792 0.2423 # Endpunkt Linie 1 (2m Höhe) -v -0.0483158 -0.0139454 0.0423 # Startpunkt Linie 2 (Ecke 6 Oberseite) -v -0.0483158 -0.0139454 0.2423 # Endpunkt Linie 2 (2m Höhe) - -# Linien -l 1 9 # Linie 1 -l 6 10 # Linie 2 diff --git a/models/issue-391.mtl b/models/issue-391.mtl deleted file mode 100644 index c23ced4b..00000000 --- a/models/issue-391.mtl +++ /dev/null @@ -1,4 +0,0 @@ -newmtl has_kd -Kd 1 0 0 -newmtl has_map -map_Kd test.png \ No newline at end of file diff --git a/models/issue-391.obj b/models/issue-391.obj deleted file mode 100644 index 06d8774b..00000000 --- a/models/issue-391.obj +++ /dev/null @@ -1,9 +0,0 @@ -mtllib issue-391.mtl -v 0 0 0 -v 1 0 0 -v 0 1 0 -vn 0 0 1 -usemtl has_map -f 1//1 2//1 3//1 -usemtl has_kd -f 1//1 2//1 3//1 \ No newline at end of file diff --git a/models/issue-92.mtl b/models/issue-92.mtl deleted file mode 100644 index 5ebd6680..00000000 --- a/models/issue-92.mtl +++ /dev/null @@ -1,6 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -map_Kd tmp.png - diff --git a/models/issue-92.obj b/models/issue-92.obj deleted file mode 100644 index f7be3b6e..00000000 --- a/models/issue-92.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib issue-92.mtl -o Test -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/issue-95-2.mtl b/models/issue-95-2.mtl deleted file mode 100644 index 68d484c8..00000000 --- a/models/issue-95-2.mtl +++ /dev/null @@ -1,5 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Tf 0.1 0.2 0.3 diff --git a/models/issue-95-2.obj b/models/issue-95-2.obj deleted file mode 100644 index 456f854b..00000000 --- a/models/issue-95-2.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib issue-95-2.mtl -o Test -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/issue-95.mtl b/models/issue-95.mtl deleted file mode 100644 index 1d29fee9..00000000 --- a/models/issue-95.mtl +++ /dev/null @@ -1,5 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Kt 0.1 0.2 0.3 diff --git a/models/issue-95.obj b/models/issue-95.obj deleted file mode 100644 index 8ee267ea..00000000 --- a/models/issue-95.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib issue-95.mtl -o Test -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/leading-decimal-dot-issue-201.mtl b/models/leading-decimal-dot-issue-201.mtl deleted file mode 100644 index 1d29fee9..00000000 --- a/models/leading-decimal-dot-issue-201.mtl +++ /dev/null @@ -1,5 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Kt 0.1 0.2 0.3 diff --git a/models/leading-decimal-dot-issue-201.obj b/models/leading-decimal-dot-issue-201.obj deleted file mode 100644 index 202cfdcd..00000000 --- a/models/leading-decimal-dot-issue-201.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib leading-decimal-dot-issue-201.mtl -o Test -v .8e-1 -.7e+2 -5.532511 -v .575869 -.666304 5.896140 -v .940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/leading-zero-in-exponent-notation-issue-210.mtl b/models/leading-zero-in-exponent-notation-issue-210.mtl deleted file mode 100644 index 1d29fee9..00000000 --- a/models/leading-zero-in-exponent-notation-issue-210.mtl +++ /dev/null @@ -1,5 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Kt 0.1 0.2 0.3 diff --git a/models/leading-zero-in-exponent-notation-issue-210.obj b/models/leading-zero-in-exponent-notation-issue-210.obj deleted file mode 100644 index cf6f99bc..00000000 --- a/models/leading-zero-in-exponent-notation-issue-210.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib leading-zero-in-exponent-notation-issue-210.mtl -o Test -v .8e-001 -.7e+02 -5.532511 -v .575869 -.666304 5.896140 -v .940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/line-prim.obj b/models/line-prim.obj deleted file mode 100644 index 22f55d3f..00000000 --- a/models/line-prim.obj +++ /dev/null @@ -1,16 +0,0 @@ -mtllib cube.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g g0 -usemtl white -l 1 2 3 4 -l 5 6 7 8 diff --git a/models/map-bump.mtl b/models/map-bump.mtl deleted file mode 100644 index 6fb1291a..00000000 --- a/models/map-bump.mtl +++ /dev/null @@ -1,10 +0,0 @@ -newmtl Material.001 -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -map_Bump bump.jpg - -newmtl Material.003 -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/map-bump.obj b/models/map-bump.obj deleted file mode 100644 index 03071f1a..00000000 --- a/models/map-bump.obj +++ /dev/null @@ -1,817 +0,0 @@ -# https://github.com/syoyo/tinyobjloader/issues/68 -# Blender v2.73 (sub 0) OBJ File: 'enemy.blend' -# www.blender.org -mtllib map-bump.mtl -o Cube -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -v 1.620345 1.000000 -5.815706 -v 1.864152 1.000000 -6.334323 -v 0.575869 -0.129842 5.896143 -v 5.440438 -1.462153 -5.818601 -v 4.896782 -1.462153 -2.744413 -v 1.000825 -0.677484 1.899605 -v 5.440438 -1.246362 -5.818600 -v 1.000825 0.852342 1.899608 -v 4.896782 -1.246362 -2.744412 -v 1.160660 -0.450871 -2.356325 -v 1.704316 -0.450871 -5.430513 -v 1.000825 -0.351920 -1.293797 -v 1.000825 1.000000 -1.293794 -v 1.160660 -0.877888 -2.356326 -v 1.704316 -0.877888 -5.430514 -v 1.000825 -1.219172 -1.452514 -v 1.000825 1.000000 -1.452511 -v 1.000825 -0.351920 1.759410 -v 1.000825 1.000000 1.759413 -v 9.097919 1.221145 -6.212147 -v 8.356775 1.221145 -2.021231 -v 1.864151 -0.109586 -6.334325 -v 0.575869 -0.398073 5.896141 -v 9.097919 0.943958 -6.212148 -v 8.356775 0.943958 -2.021233 -v 1.061916 0.113661 -1.797961 -v 1.000825 0.161258 1.899606 -v 1.000825 0.324040 -1.293795 -v 1.803060 0.113661 -5.988876 -v 1.000825 -0.109586 -1.452513 -v 1.061916 0.776753 -1.797960 -v 1.803061 0.776753 -5.988875 -v 1.000825 0.324040 1.759412 -v 0.000825 -1.219172 -5.532512 -v 0.000825 -0.666304 5.896139 -v 0.000826 1.000000 -6.334325 -v 0.000825 -0.129842 5.896140 -v 0.000825 0.852342 1.899606 -v 0.000825 -0.677484 1.899604 -v 0.000825 -0.351920 -1.293797 -v 0.000825 1.000000 -1.293796 -v 0.000825 1.000000 -1.452513 -v 0.000825 -1.219172 -1.452515 -v 0.000825 -0.351920 1.759409 -v 0.000825 1.000000 1.759411 -v 0.000826 -0.109586 -6.334326 -v 0.000825 -0.398073 5.896140 -v 0.152918 1.000000 -5.815708 -v 0.152917 1.000000 -1.971130 -v 0.940448 1.168419 -1.971128 -v 1.620345 1.168419 -5.815706 -v 0.152918 1.168419 -5.815708 -v 0.152917 1.168419 -1.971130 -v 0.921118 1.091883 -1.050430 -v 0.921118 1.091883 1.516050 -v 0.080533 1.091883 -1.050432 -v 0.080533 1.091883 1.516048 -v 0.613003 -0.553430 5.546911 -v 0.963691 -0.559956 2.248834 -v 0.613003 -0.396857 5.546912 -v 0.963691 -0.070362 2.248835 -v 1.499370 -0.994317 3.966028 -v 1.850058 -0.997914 0.667950 -v 1.499370 -0.908021 3.966029 -v 1.850058 -0.728071 0.667951 -v 1.601022 0.760960 -6.334324 -v 1.601021 0.129454 -6.334325 -v 0.263955 0.760960 -6.334325 -v 0.263955 0.129454 -6.334325 -v 1.334809 0.760960 -7.515329 -v 1.334809 0.129455 -7.515330 -v 0.530168 0.760960 -7.515330 -v 0.530168 0.129455 -7.515330 -v 1.192720 0.649445 -7.515329 -v 1.192720 0.240971 -7.515330 -v 0.672258 0.649445 -7.515330 -v 0.672258 0.240971 -7.515330 -v 1.192719 0.649444 -6.524630 -v 1.192719 0.240970 -6.524631 -v 0.672257 0.649444 -6.524631 -v 0.672257 0.240970 -6.524631 -v 3.851026 0.431116 -1.883326 -v 3.851026 0.946662 -1.883325 -v 4.592170 0.946662 -6.074241 -v 4.592169 0.431116 -6.074242 -v 4.995714 0.561404 -1.918362 -v 4.995714 1.016394 -1.918360 -v 5.736857 1.016394 -6.109276 -v 5.736857 0.561404 -6.109277 -v 3.975454 0.471731 -2.162156 -v 3.975454 0.919244 -2.162155 -v 4.618796 0.919244 -5.800034 -v 4.618795 0.471730 -5.800035 -v 4.969088 0.584825 -2.192568 -v 4.969088 0.979775 -2.192567 -v 5.612430 0.979775 -5.830446 -v 5.612429 0.584825 -5.830447 -v 0.864214 -0.673890 3.184381 -v 0.864213 0.489129 3.184384 -v 0.864213 -0.018552 3.184383 -v 0.000825 0.489129 3.184382 -v 0.000825 -0.673890 3.184381 -v 0.850955 -0.557858 3.309075 -v 0.850955 -0.175321 3.309076 -v 1.737321 -0.996758 1.728192 -v 1.737321 -0.785920 1.728193 -v -1.864151 -1.219172 -5.532511 -v -0.575869 -0.666304 5.896140 -v -0.940448 1.000000 -1.971128 -v -1.620345 1.000000 -5.815706 -v -1.864152 1.000000 -6.334323 -v -0.575869 -0.129842 5.896143 -v -5.440438 -1.462153 -5.818601 -v -4.896782 -1.462153 -2.744413 -v -1.000825 -0.677484 1.899605 -v -5.440438 -1.246362 -5.818600 -v -1.000825 0.852342 1.899608 -v -4.896782 -1.246362 -2.744412 -v -1.160660 -0.450871 -2.356325 -v -1.704316 -0.450871 -5.430513 -v -1.000825 -0.351920 -1.293797 -v -1.000825 1.000000 -1.293794 -v -1.160660 -0.877888 -2.356326 -v -1.704316 -0.877888 -5.430514 -v -1.000825 -1.219172 -1.452514 -v -1.000825 1.000000 -1.452511 -v -1.000825 -0.351920 1.759410 -v -1.000825 1.000000 1.759413 -v -9.097919 1.221145 -6.212147 -v -8.356775 1.221145 -2.021231 -v -1.864151 -0.109586 -6.334325 -v -0.575869 -0.398073 5.896141 -v -9.097919 0.943958 -6.212148 -v -8.356775 0.943958 -2.021233 -v -1.061916 0.113661 -1.797961 -v -1.000825 0.161258 1.899606 -v -1.000825 0.324040 -1.293795 -v -1.803060 0.113661 -5.988876 -v -1.000825 -0.109586 -1.452513 -v -1.061916 0.776753 -1.797960 -v -1.803061 0.776753 -5.988875 -v -1.000825 0.324040 1.759412 -v -0.000825 -1.219172 -5.532512 -v -0.000825 -0.666304 5.896139 -v -0.000826 1.000000 -6.334325 -v -0.000825 -0.129842 5.896140 -v -0.000825 0.852342 1.899606 -v -0.000825 -0.677484 1.899604 -v -0.000825 -0.351920 -1.293797 -v -0.000825 1.000000 -1.293796 -v -0.000825 1.000000 -1.452513 -v -0.000825 -1.219172 -1.452515 -v -0.000825 -0.351920 1.759409 -v -0.000825 1.000000 1.759411 -v -0.000826 -0.109586 -6.334326 -v -0.000825 -0.398073 5.896140 -v -0.152918 1.000000 -5.815708 -v -0.152917 1.000000 -1.971130 -v -0.940448 1.168419 -1.971128 -v -1.620345 1.168419 -5.815706 -v -0.152918 1.168419 -5.815708 -v -0.152917 1.168419 -1.971130 -v -0.921118 1.091883 -1.050430 -v -0.921118 1.091883 1.516050 -v -0.080533 1.091883 -1.050432 -v -0.080533 1.091883 1.516048 -v -0.613003 -0.553430 5.546911 -v -0.963691 -0.559956 2.248834 -v -0.613003 -0.396857 5.546912 -v -0.963691 -0.070362 2.248835 -v -1.499370 -0.994317 3.966028 -v -1.850058 -0.997914 0.667950 -v -1.499370 -0.908021 3.966029 -v -1.850058 -0.728071 0.667951 -v -1.601022 0.760960 -6.334324 -v -1.601021 0.129454 -6.334325 -v -0.263955 0.760960 -6.334325 -v -0.263955 0.129454 -6.334325 -v -1.334809 0.760960 -7.515329 -v -1.334809 0.129455 -7.515330 -v -0.530168 0.760960 -7.515330 -v -0.530168 0.129455 -7.515330 -v -1.192720 0.649445 -7.515329 -v -1.192720 0.240971 -7.515330 -v -0.672258 0.649445 -7.515330 -v -0.672258 0.240971 -7.515330 -v -1.192719 0.649444 -6.524630 -v -1.192719 0.240970 -6.524631 -v -0.672257 0.649444 -6.524631 -v -0.672257 0.240970 -6.524631 -v -3.851026 0.431116 -1.883326 -v -3.851026 0.946662 -1.883325 -v -4.592170 0.946662 -6.074241 -v -4.592169 0.431116 -6.074242 -v -4.995714 0.561404 -1.918362 -v -4.995714 1.016394 -1.918360 -v -5.736857 1.016394 -6.109276 -v -5.736857 0.561404 -6.109277 -v -3.975454 0.471731 -2.162156 -v -3.975454 0.919244 -2.162155 -v -4.618796 0.919244 -5.800034 -v -4.618795 0.471730 -5.800035 -v -4.969088 0.584825 -2.192568 -v -4.969088 0.979775 -2.192567 -v -5.612430 0.979775 -5.830446 -v -5.612429 0.584825 -5.830447 -v -0.864214 -0.673890 3.184381 -v -0.864213 0.489129 3.184384 -v -0.864213 -0.018552 3.184383 -v -0.000825 0.489129 3.184382 -v -0.000825 -0.673890 3.184381 -v -0.850955 -0.557858 3.309075 -v -0.850955 -0.175321 3.309076 -v -1.737321 -0.996758 1.728192 -v -1.737321 -0.785920 1.728193 -vt 0.135351 -0.558072 -vt 0.003035 -0.363507 -vt 0.092282 -0.976844 -vt -0.081322 0.947351 -vt 0.100058 1.958891 -vt 0.050091 1.852185 -vt -0.092752 1.055565 -vt -0.251711 1.059474 -vt 0.075587 0.041384 -vt -0.086008 0.279003 -vt -0.086212 0.249830 -vt -0.276044 1.968137 -vt -0.246101 1.859467 -vt 0.009828 1.911388 -vt -0.133014 1.114769 -vt 0.413322 1.261595 -vt 0.299103 0.624605 -vt 1.243955 0.407183 -vt 0.515404 1.111487 -vt 1.358173 1.044173 -vt -0.081553 0.914324 -vt 0.080042 0.676706 -vt 0.401185 0.474498 -vt 1.295541 0.331328 -vt 0.365315 1.568841 -vt 0.299111 1.575740 -vt 0.143401 0.707357 -vt 0.629403 1.011947 -vt 0.449192 0.167251 -vt 1.409760 0.968317 -vt 0.986264 1.738667 -vt 1.573373 1.877873 -vt 1.417663 1.009490 -vt 0.237182 -0.196235 -vt 0.721785 1.030226 -vt 0.830554 0.870285 -vt 0.877494 1.898608 -vt 1.351399 1.106930 -vt 0.183935 0.557301 -vt 1.507109 1.975312 -vt 0.241636 0.439088 -vt 0.114297 -0.045011 -vt 0.140593 1.808834 -vt -0.015118 0.940452 -vt 0.156405 -1.071134 -vt 0.164119 -0.998223 -vt 0.040336 -1.068281 -vt 0.104459 -1.162571 -vt -0.165787 1.882802 -vt -0.014821 1.660811 -vt -0.287852 0.283965 -vt -0.293374 0.366508 -vt -0.289630 0.900550 -vt 0.035337 -0.191272 -vt 0.247348 0.172213 -vt 0.253300 1.021193 -vt -0.283166 0.952313 -vt -0.283398 0.919286 -vt 0.039792 0.444050 -vt 0.314806 -0.339851 -vt 0.112962 -0.334889 -vt -0.288056 0.254793 -vt -0.023788 -0.973990 -vt -0.155922 -0.359599 -vt 0.220528 -1.165425 -vt 0.108710 -0.748730 -vt -0.286364 1.918670 -vt -0.291973 1.118678 -vt -0.119962 0.896379 -vt -0.123707 0.362337 -vt 0.162891 -0.598569 -vt 0.467532 -0.853353 -vt 0.201549 -1.053262 -vt 0.161663 -0.198915 -vt 0.267667 -0.752638 -vt 0.278705 -0.371021 -vt 0.526390 -0.542053 -vt 0.483821 -0.479457 -vt 0.488162 -0.883689 -vt 0.500110 -0.105561 -vt 0.564618 -0.200418 -vt -0.110331 2.127229 -vt 0.040636 1.905238 -vt -0.010786 1.578087 -vt 0.104092 1.876168 -vt 0.255058 1.654176 -vt -0.054992 2.087323 -vt 0.203048 1.901245 -vt 0.052081 2.123235 -vt 0.042658 1.943733 -vt -0.056437 1.881175 -vt 0.147710 1.941151 -vt 0.050060 2.084741 -vt 0.146264 1.735002 -vt 0.041212 1.737584 -vt 0.048615 1.878591 -vt 0.663065 1.872485 -vt 0.786311 1.691257 -vt 0.507355 1.004102 -vt 0.630601 0.822874 -vt 0.955144 1.689498 -vt 0.860727 1.828333 -vt 0.725565 1.074543 -vt 0.819981 0.935708 -vt 0.674594 1.805657 -vt 0.539432 1.051867 -vt 0.646413 0.894554 -vt 0.781576 1.648344 -vt 0.240127 -0.712141 -vn 0.994400 0.000000 0.105700 -vn 0.000000 1.000000 0.000000 -vn 1.000000 0.000000 0.000000 -vn 0.984700 0.000000 0.174100 -vn 0.211800 0.976600 0.037500 -vn -0.103300 0.000000 -0.994600 -vn 0.103300 -0.000000 0.994600 -vn 0.911400 0.378700 0.161200 -vn -0.157300 -0.987200 -0.027800 -vn 0.113700 -0.993300 0.020100 -vn 0.030600 -0.000000 0.999500 -vn -0.061100 0.998100 -0.010800 -vn -0.030600 0.000000 -0.999500 -vn -0.000000 -0.000000 1.000000 -vn 0.000000 0.000000 -1.000000 -vn -0.755400 0.655300 0.000000 -vn 0.000000 -1.000000 0.000000 -vn -0.000000 -0.180000 0.983700 -vn 0.000000 -0.395500 -0.918500 -vn -0.000000 0.688500 0.725200 -vn 0.000000 -0.585700 -0.810500 -vn -0.000000 0.974900 0.222500 -vn -0.000000 -1.000000 0.002800 -vn -1.000000 0.000000 -0.000000 -vn -0.000000 0.935500 0.353200 -vn 0.755400 0.655300 0.000000 -vn 0.000000 0.935500 -0.353200 -vn 0.673800 0.724900 0.143400 -vn 0.872300 -0.000000 0.489100 -vn -0.872300 0.000000 -0.489100 -vn -0.518300 -0.853500 -0.054200 -vn -0.975500 0.000000 -0.219900 -vn 0.975500 0.000000 -0.219900 -vn -0.913200 0.000000 -0.407500 -vn -0.436900 0.896200 -0.077300 -vn -0.995300 -0.000000 0.096600 -vn -0.297300 -0.953400 -0.052600 -vn 0.473900 -0.876600 0.083800 -vn 0.913200 0.000000 0.407500 -vn 0.342200 0.937700 0.060500 -vn 0.995300 -0.000000 -0.096600 -vn -0.519200 -0.853000 -0.054300 -vn 0.722400 0.676400 0.143800 -vn -0.994400 0.000000 0.105700 -vn -0.984700 0.000000 0.174100 -vn -0.211800 0.976600 0.037500 -vn 0.103300 0.000000 -0.994600 -vn -0.103300 -0.000000 0.994600 -vn -0.911400 0.378700 0.161200 -vn 0.157300 -0.987200 -0.027800 -vn -0.113700 -0.993300 0.020100 -vn -0.030600 -0.000000 0.999500 -vn 0.061100 0.998100 -0.010800 -vn 0.030600 0.000000 -0.999500 -vn -0.691900 0.713200 0.112500 -vn -0.872300 -0.000000 0.489100 -vn 0.872300 0.000000 -0.489100 -vn 0.518300 -0.853500 -0.054200 -vn 0.913200 0.000000 -0.407500 -vn 0.436900 0.896200 -0.077300 -vn 0.995300 0.000000 0.096600 -vn 0.297300 -0.953300 -0.052600 -vn -0.473900 -0.876600 0.083800 -vn -0.913200 -0.000000 0.407500 -vn -0.342200 0.937700 0.060500 -vn -0.995300 -0.000000 -0.096600 -vn 0.519200 -0.853000 -0.054300 -vn -0.714800 0.690100 0.113700 -vn 0.974400 0.089700 0.206200 -vn 0.870400 0.288400 0.399100 -vn 0.691900 0.713200 0.112500 -vn -0.518000 -0.853700 -0.053400 -vn -0.519700 -0.852700 -0.053600 -vn 0.714800 0.690100 0.113700 -vn -0.974400 0.089700 0.206200 -vn -0.870400 0.288400 0.399100 -vn -0.673800 0.724900 0.143400 -vn 0.518000 -0.853700 -0.053400 -vn 0.297300 -0.953400 -0.052600 -vn 0.519700 -0.852700 -0.053600 -vn -0.722400 0.676400 0.143800 -vn -0.000000 0.962300 0.272000 -usemtl Material.001 -s off -f 103/1/1 102/2/1 6/3/1 -f 20/4/2 5/5/2 4/6/2 -f 20/4/2 3/7/2 52/8/2 -f 36/9/3 22/10/3 11/11/3 -f 39/12/2 51/13/2 4/6/2 -f 4/6/4 54/14/4 53/15/4 -f 14/16/5 13/17/5 12/18/5 -f 18/19/6 14/16/6 10/20/6 -f 20/4/3 16/21/3 31/22/3 -f 17/23/7 8/24/7 12/18/7 -f 25/25/4 32/26/4 29/27/4 -f 10/20/4 12/18/4 8/24/4 -f 1/28/8 18/19/8 17/23/8 -f 19/29/4 17/23/4 13/17/4 -f 25/25/4 14/16/4 18/19/4 -f 18/19/9 7/30/9 8/24/9 -f 92/31/10 27/32/10 28/33/10 -f 16/21/3 22/10/3 36/9/3 -f 31/22/3 36/9/3 21/34/3 -f 90/35/11 89/36/11 28/33/11 -f 91/37/12 90/35/12 24/38/12 -f 33/39/4 13/17/4 14/16/4 -f 23/40/4 24/38/4 28/33/4 -f 33/39/3 31/22/3 15/41/3 -f 21/34/3 36/9/3 30/42/3 -f 5/5/4 35/43/4 32/26/4 -f 5/5/4 20/4/4 34/44/4 -f 33/39/4 29/27/4 34/44/4 -f 91/37/13 23/40/13 27/32/13 -f 103/1/1 26/45/1 63/46/1 -f 26/45/14 50/47/14 38/48/14 -f 39/12/15 71/49/15 72/50/15 -f 48/51/16 60/52/16 59/53/16 -f 15/41/17 21/34/17 47/54/17 -f 19/29/17 46/55/17 37/56/17 -f 39/12/2 45/57/2 52/8/2 -f 20/4/2 45/57/2 44/58/2 -f 19/29/18 15/41/18 43/59/18 -f 9/60/19 42/61/19 47/54/19 -f 22/10/20 48/51/20 41/62/20 -f 25/25/21 1/28/21 37/56/21 -f 6/3/14 40/63/14 50/47/14 -f 104/64/22 40/63/22 6/3/22 -f 2/65/23 38/48/23 105/66/23 -f 55/67/2 56/68/2 53/15/2 -f 3/7/14 53/15/14 56/68/14 -f 51/13/15 55/67/15 54/14/15 -f 52/8/24 56/68/24 55/67/24 -f 57/69/2 59/53/2 60/52/2 -f 48/51/25 22/10/25 58/70/25 -f 16/21/26 57/69/26 58/70/26 -f 16/21/27 44/58/27 59/53/27 -f 107/71/28 63/46/28 67/72/28 -f 26/45/1 2/65/1 61/73/1 -f 9/60/1 30/42/1 64/74/1 -f 101/75/1 9/60/1 62/76/1 -f 108/77/1 109/78/1 67/72/1 -f 61/73/29 65/79/29 67/72/29 -f 62/76/30 64/74/30 68/80/30 -f 62/76/31 66/81/31 108/77/31 -f 71/49/32 75/82/32 76/83/32 -f 25/25/15 49/84/15 72/50/15 -f 5/5/15 69/85/15 71/49/15 -f 25/25/15 70/86/15 69/85/15 -f 76/83/15 75/82/15 79/87/15 -f 72/50/17 76/83/17 74/88/17 -f 71/49/2 69/85/2 73/89/2 -f 70/86/33 74/88/33 73/89/33 -f 80/90/3 79/87/3 83/91/3 -f 76/83/15 80/90/15 78/92/15 -f 75/82/15 73/89/15 77/93/15 -f 74/88/15 78/92/15 77/93/15 -f 82/94/15 84/95/15 83/91/15 -f 80/90/2 84/95/2 82/94/2 -f 77/93/17 81/96/17 83/91/17 -f 77/93/24 78/92/24 82/94/24 -f 35/43/13 87/97/13 88/98/13 -f 35/43/12 34/44/12 86/99/12 -f 34/44/11 29/27/11 85/100/11 -f 32/26/10 88/98/10 85/100/10 -f 92/31/34 100/101/34 99/102/34 -f 90/35/35 91/37/35 99/102/35 -f 89/36/36 90/35/36 98/103/36 -f 89/36/37 97/104/37 100/101/37 -f 95/105/13 99/102/13 100/101/13 -f 95/105/12 94/106/12 98/103/12 -f 94/106/11 93/107/11 97/104/11 -f 96/108/10 100/101/10 97/104/10 -f 88/98/38 96/108/38 93/107/38 -f 86/99/39 85/100/39 93/107/39 -f 87/97/40 86/99/40 94/106/40 -f 87/97/41 95/105/41 96/108/41 -f 106/109/42 108/77/42 65/79/42 -f 66/81/1 68/80/1 109/78/1 -f 101/75/1 106/109/1 61/73/1 -f 64/74/43 107/71/43 109/78/43 -f 101/75/23 105/66/23 42/61/23 -f 103/1/1 107/71/1 64/74/1 -f 30/42/1 11/11/1 102/2/1 -f 212/1/44 135/45/44 115/3/44 -f 129/4/2 112/7/2 113/6/2 -f 161/8/2 112/7/2 129/4/2 -f 145/9/24 139/42/24 120/11/24 -f 113/6/2 160/13/2 148/12/2 -f 162/15/45 163/14/45 113/6/45 -f 123/16/46 119/20/46 121/18/46 -f 127/19/47 116/30/47 119/20/47 -f 140/22/24 125/21/24 129/4/24 -f 121/18/48 117/24/48 126/23/48 -f 138/27/45 141/26/45 134/25/45 -f 117/24/45 121/18/45 119/20/45 -f 126/23/49 127/19/49 110/28/49 -f 122/17/45 126/23/45 128/29/45 -f 127/19/45 123/16/45 134/25/45 -f 117/24/50 116/30/50 127/19/50 -f 137/33/51 136/32/51 201/31/51 -f 145/9/24 131/10/24 125/21/24 -f 130/34/24 145/9/24 140/22/24 -f 199/35/52 133/38/52 137/33/52 -f 200/37/53 132/40/53 133/38/53 -f 123/16/45 122/17/45 142/39/45 -f 137/33/45 133/38/45 132/40/45 -f 124/41/24 140/22/24 142/39/24 -f 130/34/24 118/60/24 139/42/24 -f 141/26/45 144/43/45 114/5/45 -f 114/5/45 144/43/45 143/44/45 -f 143/44/45 138/27/45 142/39/45 -f 136/32/54 132/40/54 200/37/54 -f 212/1/44 216/71/44 172/46/44 -f 147/48/14 159/47/14 135/45/14 -f 181/50/15 180/49/15 148/12/15 -f 168/53/26 169/52/26 157/51/26 -f 124/41/17 152/59/17 156/54/17 -f 146/56/17 155/55/17 128/29/17 -f 148/12/2 160/13/2 161/8/2 -f 129/4/2 125/21/2 153/58/2 -f 155/55/18 152/59/18 124/41/18 -f 130/34/19 156/54/19 151/61/19 -f 131/10/20 120/11/20 150/62/20 -f 134/25/21 158/84/21 146/56/21 -f 159/47/14 149/63/14 115/3/14 -f 115/3/22 149/63/22 213/64/22 -f 214/66/23 147/48/23 111/65/23 -f 162/15/2 165/68/2 164/67/2 -f 165/68/14 162/15/14 112/7/14 -f 163/14/15 164/67/15 160/13/15 -f 164/67/3 165/68/3 161/8/3 -f 166/69/2 167/70/2 169/52/2 -f 157/51/25 169/52/25 167/70/25 -f 167/70/16 166/69/16 125/21/16 -f 125/21/27 166/69/27 168/53/27 -f 216/71/55 218/78/55 176/72/55 -f 135/45/44 172/46/44 170/73/44 -f 118/60/44 171/76/44 173/74/44 -f 210/75/44 215/109/44 171/76/44 -f 217/77/44 174/79/44 176/72/44 -f 176/72/56 174/79/56 170/73/56 -f 171/76/57 175/81/57 177/80/57 -f 217/77/58 175/81/58 171/76/58 -f 185/83/33 184/82/33 180/49/33 -f 134/25/15 179/86/15 181/50/15 -f 180/49/15 178/85/15 114/5/15 -f 178/85/15 179/86/15 134/25/15 -f 189/90/15 188/87/15 184/82/15 -f 183/88/17 185/83/17 181/50/17 -f 180/49/2 184/82/2 182/89/2 -f 182/89/32 183/88/32 179/86/32 -f 189/90/24 193/95/24 192/91/24 -f 187/92/15 189/90/15 185/83/15 -f 184/82/15 188/87/15 186/93/15 -f 186/93/15 187/92/15 183/88/15 -f 192/91/15 193/95/15 191/94/15 -f 191/94/2 193/95/2 189/90/2 -f 192/91/17 190/96/17 186/93/17 -f 186/93/3 190/96/3 191/94/3 -f 197/98/54 196/97/54 144/43/54 -f 144/43/53 196/97/53 195/99/53 -f 143/44/52 195/99/52 194/100/52 -f 194/100/51 197/98/51 141/26/51 -f 208/102/59 209/101/59 201/31/59 -f 199/35/60 207/103/60 208/102/60 -f 198/36/61 206/104/61 207/103/61 -f 209/101/62 206/104/62 198/36/62 -f 209/101/54 208/102/54 204/105/54 -f 204/105/53 208/102/53 207/103/53 -f 203/106/52 207/103/52 206/104/52 -f 206/104/51 209/101/51 205/108/51 -f 202/107/63 205/108/63 197/98/63 -f 195/99/64 203/106/64 202/107/64 -f 196/97/65 204/105/65 203/106/65 -f 205/108/66 204/105/66 196/97/66 -f 174/79/67 217/77/67 215/109/67 -f 175/81/44 217/77/44 218/78/44 -f 170/73/44 215/109/44 210/75/44 -f 173/74/68 177/80/68 218/78/68 -f 151/61/23 214/66/23 210/75/23 -f 173/74/44 216/71/44 212/1/44 -f 139/42/44 212/1/44 211/2/44 -f 26/45/1 103/1/1 6/3/1 -f 3/7/2 20/4/2 4/6/2 -f 45/57/2 20/4/2 52/8/2 -f 30/42/3 36/9/3 11/11/3 -f 5/5/2 39/12/2 4/6/2 -f 3/7/4 4/6/4 53/15/4 -f 10/20/5 14/16/5 12/18/5 -f 7/30/6 18/19/6 10/20/6 -f 33/39/3 20/4/3 31/22/3 -f 13/17/7 17/23/7 12/18/7 -f 33/39/4 25/25/4 29/27/4 -f 7/30/4 10/20/4 8/24/4 -f 19/29/69 1/28/69 17/23/69 -f 33/39/4 19/29/4 13/17/4 -f 1/28/70 25/25/70 18/19/70 -f 17/23/9 18/19/9 8/24/9 -f 89/36/10 92/31/10 28/33/10 -f 31/22/3 16/21/3 36/9/3 -f 15/41/3 31/22/3 21/34/3 -f 24/38/11 90/35/11 28/33/11 -f 23/40/12 91/37/12 24/38/12 -f 25/25/4 33/39/4 14/16/4 -f 27/32/4 23/40/4 28/33/4 -f 19/29/3 33/39/3 15/41/3 -f 9/60/3 21/34/3 30/42/3 -f 25/25/4 5/5/4 32/26/4 -f 35/43/4 5/5/4 34/44/4 -f 20/4/4 33/39/4 34/44/4 -f 92/31/13 91/37/13 27/32/13 -f 107/71/1 103/1/1 63/46/1 -f 2/65/14 26/45/14 38/48/14 -f 49/84/15 39/12/15 72/50/15 -f 44/58/16 48/51/16 59/53/16 -f 43/59/17 15/41/17 47/54/17 -f 1/28/17 19/29/17 37/56/17 -f 51/13/2 39/12/2 52/8/2 -f 16/21/2 20/4/2 44/58/2 -f 46/55/18 19/29/18 43/59/18 -f 21/34/19 9/60/19 47/54/19 -f 11/11/20 22/10/20 41/62/20 -f 49/84/21 25/25/21 37/56/21 -f 26/45/14 6/3/14 50/47/14 -f 102/2/22 104/64/22 6/3/22 -f 101/75/23 2/65/23 105/66/23 -f 54/14/2 55/67/2 53/15/2 -f 52/8/14 3/7/14 56/68/14 -f 4/6/15 51/13/15 54/14/15 -f 51/13/24 52/8/24 55/67/24 -f 58/70/2 57/69/2 60/52/2 -f 60/52/25 48/51/25 58/70/25 -f 22/10/26 16/21/26 58/70/26 -f 57/69/27 16/21/27 59/53/27 -f 109/78/71 107/71/71 67/72/71 -f 63/46/1 26/45/1 61/73/1 -f 62/76/1 9/60/1 64/74/1 -f 106/109/1 101/75/1 62/76/1 -f 65/79/1 108/77/1 67/72/1 -f 63/46/29 61/73/29 67/72/29 -f 66/81/30 62/76/30 68/80/30 -f 106/109/72 62/76/72 108/77/72 -f 72/50/32 71/49/32 76/83/32 -f 70/86/15 25/25/15 72/50/15 -f 39/12/15 5/5/15 71/49/15 -f 5/5/15 25/25/15 69/85/15 -f 80/90/15 76/83/15 79/87/15 -f 70/86/17 72/50/17 74/88/17 -f 75/82/2 71/49/2 73/89/2 -f 69/85/33 70/86/33 73/89/33 -f 84/95/3 80/90/3 83/91/3 -f 74/88/15 76/83/15 78/92/15 -f 79/87/15 75/82/15 77/93/15 -f 73/89/15 74/88/15 77/93/15 -f 81/96/15 82/94/15 83/91/15 -f 78/92/2 80/90/2 82/94/2 -f 79/87/17 77/93/17 83/91/17 -f 81/96/24 77/93/24 82/94/24 -f 32/26/13 35/43/13 88/98/13 -f 87/97/12 35/43/12 86/99/12 -f 86/99/11 34/44/11 85/100/11 -f 29/27/10 32/26/10 85/100/10 -f 91/37/34 92/31/34 99/102/34 -f 98/103/35 90/35/35 99/102/35 -f 97/104/36 89/36/36 98/103/36 -f 92/31/37 89/36/37 100/101/37 -f 96/108/13 95/105/13 100/101/13 -f 99/102/12 95/105/12 98/103/12 -f 98/103/11 94/106/11 97/104/11 -f 93/107/10 96/108/10 97/104/10 -f 85/100/38 88/98/38 93/107/38 -f 94/106/39 86/99/39 93/107/39 -f 95/105/40 87/97/40 94/106/40 -f 88/98/41 87/97/41 96/108/41 -f 61/73/73 106/109/73 65/79/73 -f 108/77/1 66/81/1 109/78/1 -f 2/65/1 101/75/1 61/73/1 -f 68/80/74 64/74/74 109/78/74 -f 9/60/23 101/75/23 42/61/23 -f 30/42/1 103/1/1 64/74/1 -f 103/1/1 30/42/1 102/2/1 -f 211/2/44 212/1/44 115/3/44 -f 114/5/2 129/4/2 113/6/2 -f 154/57/2 161/8/2 129/4/2 -f 131/10/24 145/9/24 120/11/24 -f 114/5/2 113/6/2 148/12/2 -f 112/7/45 162/15/45 113/6/45 -f 122/17/46 123/16/46 121/18/46 -f 123/16/47 127/19/47 119/20/47 -f 142/39/24 140/22/24 129/4/24 -f 122/17/48 121/18/48 126/23/48 -f 142/39/45 138/27/45 134/25/45 -f 116/30/45 117/24/45 119/20/45 -f 128/29/75 126/23/75 110/28/75 -f 142/39/45 122/17/45 128/29/45 -f 110/28/76 127/19/76 134/25/76 -f 126/23/50 117/24/50 127/19/50 -f 198/36/51 137/33/51 201/31/51 -f 140/22/24 145/9/24 125/21/24 -f 124/41/24 130/34/24 140/22/24 -f 198/36/52 199/35/52 137/33/52 -f 199/35/53 200/37/53 133/38/53 -f 134/25/45 123/16/45 142/39/45 -f 136/32/45 137/33/45 132/40/45 -f 128/29/24 124/41/24 142/39/24 -f 145/9/24 130/34/24 139/42/24 -f 134/25/45 141/26/45 114/5/45 -f 129/4/45 114/5/45 143/44/45 -f 129/4/45 143/44/45 142/39/45 -f 201/31/54 136/32/54 200/37/54 -f 135/45/44 212/1/44 172/46/44 -f 111/65/14 147/48/14 135/45/14 -f 158/84/15 181/50/15 148/12/15 -f 153/58/26 168/53/26 157/51/26 -f 130/34/17 124/41/17 156/54/17 -f 110/28/17 146/56/17 128/29/17 -f 154/57/2 148/12/2 161/8/2 -f 154/57/2 129/4/2 153/58/2 -f 128/29/18 155/55/18 124/41/18 -f 118/60/19 130/34/19 151/61/19 -f 157/51/20 131/10/20 150/62/20 -f 110/28/21 134/25/21 146/56/21 -f 135/45/14 159/47/14 115/3/14 -f 211/2/22 115/3/22 213/64/22 -f 210/75/23 214/66/23 111/65/23 -f 163/14/2 162/15/2 164/67/2 -f 161/8/14 165/68/14 112/7/14 -f 113/6/15 163/14/15 160/13/15 -f 160/13/3 164/67/3 161/8/3 -f 168/53/2 166/69/2 169/52/2 -f 131/10/25 157/51/25 167/70/25 -f 131/10/16 167/70/16 125/21/16 -f 153/58/27 125/21/27 168/53/27 -f 172/46/77 216/71/77 176/72/77 -f 111/65/44 135/45/44 170/73/44 -f 139/42/44 118/60/44 173/74/44 -f 118/60/44 210/75/44 171/76/44 -f 218/78/44 217/77/44 176/72/44 -f 172/46/56 176/72/56 170/73/56 -f 173/74/57 171/76/57 177/80/57 -f 215/109/78 217/77/78 171/76/78 -f 181/50/33 185/83/33 180/49/33 -f 158/84/15 134/25/15 181/50/15 -f 148/12/15 180/49/15 114/5/15 -f 114/5/15 178/85/15 134/25/15 -f 185/83/15 189/90/15 184/82/15 -f 179/86/17 183/88/17 181/50/17 -f 178/85/2 180/49/2 182/89/2 -f 178/85/32 182/89/32 179/86/32 -f 188/87/24 189/90/24 192/91/24 -f 183/88/15 187/92/15 185/83/15 -f 182/89/15 184/82/15 186/93/15 -f 182/89/15 186/93/15 183/88/15 -f 190/96/15 192/91/15 191/94/15 -f 187/92/2 191/94/2 189/90/2 -f 188/87/17 192/91/17 186/93/17 -f 187/92/3 186/93/3 191/94/3 -f 141/26/54 197/98/54 144/43/54 -f 143/44/53 144/43/53 195/99/53 -f 138/27/52 143/44/52 194/100/52 -f 138/27/51 194/100/51 141/26/51 -f 200/37/59 208/102/59 201/31/59 -f 200/37/60 199/35/60 208/102/60 -f 199/35/61 198/36/61 207/103/61 -f 201/31/79 209/101/79 198/36/79 -f 205/108/54 209/101/54 204/105/54 -f 203/106/53 204/105/53 207/103/53 -f 202/107/52 203/106/52 206/104/52 -f 202/107/51 206/104/51 205/108/51 -f 194/100/63 202/107/63 197/98/63 -f 194/100/64 195/99/64 202/107/64 -f 195/99/65 196/97/65 203/106/65 -f 197/98/66 205/108/66 196/97/66 -f 170/73/80 174/79/80 215/109/80 -f 177/80/44 175/81/44 218/78/44 -f 111/65/44 170/73/44 210/75/44 -f 216/71/81 173/74/81 218/78/81 -f 118/60/23 151/61/23 210/75/23 -f 139/42/44 173/74/44 212/1/44 -f 120/11/44 139/42/44 211/2/44 -usemtl Material.003 -f 41/62/82 104/64/82 102/2/82 -f 211/2/82 213/64/82 150/62/82 -f 11/11/82 41/62/82 102/2/82 -f 120/11/82 211/2/82 150/62/82 diff --git a/models/missing_material_file.obj b/models/missing_material_file.obj deleted file mode 100644 index 9e1d98c7..00000000 --- a/models/missing_material_file.obj +++ /dev/null @@ -1,145 +0,0 @@ -# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. -# original cornell box data - # comment - -# empty line including some space - - -#mtllib no_material.mtl - -o floor -usemtl white -v 552.8 0.0 0.0 -v 0.0 0.0 0.0 -v 0.0 0.0 559.2 -v 549.6 0.0 559.2 - -v 130.0 0.0 65.0 -v 82.0 0.0 225.0 -v 240.0 0.0 272.0 -v 290.0 0.0 114.0 - -v 423.0 0.0 247.0 -v 265.0 0.0 296.0 -v 314.0 0.0 456.0 -v 472.0 0.0 406.0 - -f 1 2 3 4 -f 8 7 6 5 -f 12 11 10 9 - -o light -usemtl light -v 343.0 548.0 227.0 -v 343.0 548.0 332.0 -v 213.0 548.0 332.0 -v 213.0 548.0 227.0 -f -4 -3 -2 -1 - -o ceiling -usemtl white -v 556.0 548.8 0.0 -v 556.0 548.8 559.2 -v 0.0 548.8 559.2 -v 0.0 548.8 0.0 -f -4 -3 -2 -1 - -o back_wall -usemtl white -v 549.6 0.0 559.2 -v 0.0 0.0 559.2 -v 0.0 548.8 559.2 -v 556.0 548.8 559.2 -f -4 -3 -2 -1 - -o front_wall -usemtl blue -v 549.6 0.0 0 -v 0.0 0.0 0 -v 0.0 548.8 0 -v 556.0 548.8 0 -#f -1 -2 -3 -4 - -o green_wall -usemtl green -v 0.0 0.0 559.2 -v 0.0 0.0 0.0 -v 0.0 548.8 0.0 -v 0.0 548.8 559.2 -f -4 -3 -2 -1 - -o red_wall -usemtl red -v 552.8 0.0 0.0 -v 549.6 0.0 559.2 -v 556.0 548.8 559.2 -v 556.0 548.8 0.0 -f -4 -3 -2 -1 - -o short_block -usemtl white - -v 130.0 165.0 65.0 -v 82.0 165.0 225.0 -v 240.0 165.0 272.0 -v 290.0 165.0 114.0 -f -4 -3 -2 -1 - -v 290.0 0.0 114.0 -v 290.0 165.0 114.0 -v 240.0 165.0 272.0 -v 240.0 0.0 272.0 -f -4 -3 -2 -1 - -v 130.0 0.0 65.0 -v 130.0 165.0 65.0 -v 290.0 165.0 114.0 -v 290.0 0.0 114.0 -f -4 -3 -2 -1 - -v 82.0 0.0 225.0 -v 82.0 165.0 225.0 -v 130.0 165.0 65.0 -v 130.0 0.0 65.0 -f -4 -3 -2 -1 - -v 240.0 0.0 272.0 -v 240.0 165.0 272.0 -v 82.0 165.0 225.0 -v 82.0 0.0 225.0 -f -4 -3 -2 -1 - -o tall_block -usemtl white - -v 423.0 330.0 247.0 -v 265.0 330.0 296.0 -v 314.0 330.0 456.0 -v 472.0 330.0 406.0 -f -4 -3 -2 -1 - -usemtl white -v 423.0 0.0 247.0 -v 423.0 330.0 247.0 -v 472.0 330.0 406.0 -v 472.0 0.0 406.0 -f -4 -3 -2 -1 - -v 472.0 0.0 406.0 -v 472.0 330.0 406.0 -v 314.0 330.0 456.0 -v 314.0 0.0 456.0 -f -4 -3 -2 -1 - -v 314.0 0.0 456.0 -v 314.0 330.0 456.0 -v 265.0 330.0 296.0 -v 265.0 0.0 296.0 -f -4 -3 -2 -1 - -v 265.0 0.0 296.0 -v 265.0 330.0 296.0 -v 423.0 330.0 247.0 -v 423.0 0.0 247.0 -f -4 -3 -2 -1 - diff --git a/models/mtl filename with whitespace issue46.mtl b/models/mtl filename with whitespace issue46.mtl deleted file mode 100644 index b79d99b0..00000000 --- a/models/mtl filename with whitespace issue46.mtl +++ /dev/null @@ -1,4 +0,0 @@ -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 diff --git a/models/mtl filename with whitespace issue46.obj b/models/mtl filename with whitespace issue46.obj deleted file mode 100644 index 72d1dc9e..00000000 --- a/models/mtl filename with whitespace issue46.obj +++ /dev/null @@ -1,31 +0,0 @@ -mtllib invalid-file-without-spaces.mtl invalid\ file\ with\ spaces.mtl mtl\ filename\ with\ whitespace\ issue46.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl green -f 1 2 3 4 -g back cube -usemtl green -f 8 7 6 5 -g right cube -usemtl green -f 4 3 7 8 -g left cube -usemtl green -f 5 6 2 1 -g top cube -usemtl green -f 5 1 4 8 -g bottom cube -usemtl green -f 2 6 7 3 -# 6 elements diff --git a/models/mtllib-multiple-files-issue-112.mtl b/models/mtllib-multiple-files-issue-112.mtl deleted file mode 100644 index 5ebd6680..00000000 --- a/models/mtllib-multiple-files-issue-112.mtl +++ /dev/null @@ -1,6 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -map_Kd tmp.png - diff --git a/models/mtllib-multiple-files-issue-112.obj b/models/mtllib-multiple-files-issue-112.obj deleted file mode 100644 index 9966dfb2..00000000 --- a/models/mtllib-multiple-files-issue-112.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib invalid-file-aaa.mtl invalid-file-bbb.mtl mtllib-multiple-files-issue-112.mtl -o Test -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/no_material.obj b/models/no_material.obj deleted file mode 100644 index 6f3688f7..00000000 --- a/models/no_material.obj +++ /dev/null @@ -1,133 +0,0 @@ -# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. -# original cornell box data - # comment - -# empty line including some space - - -o floor -v 552.8 0.0 0.0 -v 0.0 0.0 0.0 -v 0.0 0.0 559.2 -v 549.6 0.0 559.2 - -v 130.0 0.0 65.0 -v 82.0 0.0 225.0 -v 240.0 0.0 272.0 -v 290.0 0.0 114.0 - -v 423.0 0.0 247.0 -v 265.0 0.0 296.0 -v 314.0 0.0 456.0 -v 472.0 0.0 406.0 - -f 1 2 3 4 -f 8 7 6 5 -f 12 11 10 9 - -o light -v 343.0 548.0 227.0 -v 343.0 548.0 332.0 -v 213.0 548.0 332.0 -v 213.0 548.0 227.0 -f -4 -3 -2 -1 - -o ceiling -v 556.0 548.8 0.0 -v 556.0 548.8 559.2 -v 0.0 548.8 559.2 -v 0.0 548.8 0.0 -f -4 -3 -2 -1 - -o back_wall -v 549.6 0.0 559.2 -v 0.0 0.0 559.2 -v 0.0 548.8 559.2 -v 556.0 548.8 559.2 -f -4 -3 -2 -1 - -o front_wall -v 549.6 0.0 0 -v 0.0 0.0 0 -v 0.0 548.8 0 -v 556.0 548.8 0 -#f -1 -2 -3 -4 - -o green_wall -v 0.0 0.0 559.2 -v 0.0 0.0 0.0 -v 0.0 548.8 0.0 -v 0.0 548.8 559.2 -f -4 -3 -2 -1 - -o red_wall -v 552.8 0.0 0.0 -v 549.6 0.0 559.2 -v 556.0 548.8 559.2 -v 556.0 548.8 0.0 -f -4 -3 -2 -1 - -o short_block - -v 130.0 165.0 65.0 -v 82.0 165.0 225.0 -v 240.0 165.0 272.0 -v 290.0 165.0 114.0 -f -4 -3 -2 -1 - -v 290.0 0.0 114.0 -v 290.0 165.0 114.0 -v 240.0 165.0 272.0 -v 240.0 0.0 272.0 -f -4 -3 -2 -1 - -v 130.0 0.0 65.0 -v 130.0 165.0 65.0 -v 290.0 165.0 114.0 -v 290.0 0.0 114.0 -f -4 -3 -2 -1 - -v 82.0 0.0 225.0 -v 82.0 165.0 225.0 -v 130.0 165.0 65.0 -v 130.0 0.0 65.0 -f -4 -3 -2 -1 - -v 240.0 0.0 272.0 -v 240.0 165.0 272.0 -v 82.0 165.0 225.0 -v 82.0 0.0 225.0 -f -4 -3 -2 -1 - -o tall_block - -v 423.0 330.0 247.0 -v 265.0 330.0 296.0 -v 314.0 330.0 456.0 -v 472.0 330.0 406.0 -f -4 -3 -2 -1 - -v 423.0 0.0 247.0 -v 423.0 330.0 247.0 -v 472.0 330.0 406.0 -v 472.0 0.0 406.0 -f -4 -3 -2 -1 - -v 472.0 0.0 406.0 -v 472.0 330.0 406.0 -v 314.0 330.0 456.0 -v 314.0 0.0 456.0 -f -4 -3 -2 -1 - -v 314.0 0.0 456.0 -v 314.0 330.0 456.0 -v 265.0 330.0 296.0 -v 265.0 0.0 296.0 -f -4 -3 -2 -1 - -v 265.0 0.0 296.0 -v 265.0 330.0 296.0 -v 423.0 330.0 247.0 -v 423.0 0.0 247.0 -f -4 -3 -2 -1 - diff --git a/models/norm-texopt.mtl b/models/norm-texopt.mtl deleted file mode 100644 index e2d4a2c8..00000000 --- a/models/norm-texopt.mtl +++ /dev/null @@ -1,7 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Kt 0.1 0.2 0.3 -norm -bm 3 normalmap.jpg - diff --git a/models/norm-texopt.obj b/models/norm-texopt.obj deleted file mode 100644 index babe94d1..00000000 --- a/models/norm-texopt.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib norm-texopt.mtl -o Test -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/pbr-mat-ext.mtl b/models/pbr-mat-ext.mtl deleted file mode 100644 index bed905d4..00000000 --- a/models/pbr-mat-ext.mtl +++ /dev/null @@ -1,19 +0,0 @@ -# .MTL with PBR extension. -newmtl pbr -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 -Ke 0.1 0.1 0.1 -Pr 0.2 -Pm 0.3 -Ps 0.4 -Pc 0.5 -Pcr 0.6 -aniso 0.7 -anisor 0.8 -map_Pr roughness.tex -map_Pm metallic.tex -map_Ps sheen.tex -map_Ke emissive.tex -norm normalmap.tex - diff --git a/models/pbr-mat-ext.obj b/models/pbr-mat-ext.obj deleted file mode 100644 index bb3e3712..00000000 --- a/models/pbr-mat-ext.obj +++ /dev/null @@ -1,10 +0,0 @@ -mtllib pbr-mat-ext.mtl - -o floor -usemtl pbr -v 552.8 0.0 0.0 -v 0.0 0.0 0.0 -v 0.0 0.0 559.2 -v 549.6 0.0 559.2 - -f 1 2 3 4 diff --git a/models/points-prim.obj b/models/points-prim.obj deleted file mode 100644 index 21e89c01..00000000 --- a/models/points-prim.obj +++ /dev/null @@ -1,15 +0,0 @@ -mtllib cube.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g g0 -usemtl white -p 1 2 3 4 5 6 7 8 diff --git a/models/refl.mtl b/models/refl.mtl deleted file mode 100644 index 7e04f28f..00000000 --- a/models/refl.mtl +++ /dev/null @@ -1,25 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 -refl reflection.tga - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/refl.obj b/models/refl.obj deleted file mode 100644 index e9715f59..00000000 --- a/models/refl.obj +++ /dev/null @@ -1,32 +0,0 @@ -# Test for `refl` material parameter -mtllib refl.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl white -f 1 2 3 4 -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/models/skin-weight.obj b/models/skin-weight.obj deleted file mode 100644 index 41f182f5..00000000 --- a/models/skin-weight.obj +++ /dev/null @@ -1,43 +0,0 @@ -mtllib cube.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -vw 0 0 1.0 -vw 1 0 0.5 1 0.5 -vw 2 1 1.0 -vw 3 2 1.0 -vw 4 3 1.0 -vw 5 0 0.25 1 0.25 2 0.25 3 0.25 -# No weight for 6th vertex -# vw 6 0 1.0 -vw 7 0 1.0 -# max 4 joints - -g front cube -usemtl white -f 1 2 3 4 -# two white spaces between 'back' and 'cube' -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/models/smoothing-group-two-squares.obj b/models/smoothing-group-two-squares.obj deleted file mode 100644 index 5b216156..00000000 --- a/models/smoothing-group-two-squares.obj +++ /dev/null @@ -1,22 +0,0 @@ -# from tinyobjloader issue #29 - -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -v 4.000000 0.000000 -3.255298 -v 4.000000 2.000000 -3.255298 -#vn 0.000000 0.000000 1.000000 -#vn 0.000000 0.000000 1.000000 -#vn 0.276597 0.000000 0.960986 -#vn 0.276597 0.000000 0.960986 -#vn 0.531611 0.000000 0.846988 -#vn 0.531611 0.000000 0.846988 -# 6 vertices - -# 6 normals - -g all -s 1 -f 1//1 2//2 3//3 4//4 -f 4//4 3//3 5//5 diff --git a/models/smoothing-normal.mtl b/models/smoothing-normal.mtl deleted file mode 100644 index d3a1c7a6..00000000 --- a/models/smoothing-normal.mtl +++ /dev/null @@ -1,24 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/smoothing-normal.obj b/models/smoothing-normal.obj deleted file mode 100644 index 8a89dec9..00000000 --- a/models/smoothing-normal.obj +++ /dev/null @@ -1,35 +0,0 @@ -mtllib smoothing-normal.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl white -s 1 -f 1 2 3 4 -#g bottom cube -#usemtl white -s 1 -f 2 6 7 3 - -g back cube -# expects white material -s off -f 8 7 6 5 -#g right cube -#usemtl red -#f 4 3 7 8 -#g top cube -#usemtl white -#f 5 1 4 8 -#g left cube -#usemtl green -#f 5 6 2 1 -# 6 elements diff --git a/models/test-nan.obj b/models/test-nan.obj deleted file mode 100644 index 3c689250..00000000 --- a/models/test-nan.obj +++ /dev/null @@ -1,145 +0,0 @@ -# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. -# original cornell box data - # comment - -# empty line including some space - - -mtllib cornell_box.mtl - -o floor -usemtl white -v nan -nan nan -v inf -inf inf -v 1.#IND -1.#IND 1.#IND -v 1.#INF -1.#INF 1.#INF - -v 130.0 0.0 65.0 -v 82.0 0.0 225.0 -v 240.0 0.0 272.0 -v 290.0 0.0 114.0 - -v 423.0 0.0 247.0 -v 265.0 0.0 296.0 -v 314.0 0.0 456.0 -v 472.0 0.0 406.0 - -f 1 2 3 4 -f 8 7 6 5 -f 12 11 10 9 - -o light -usemtl light -v 343.0 548.0 227.0 -v 343.0 548.0 332.0 -v 213.0 548.0 332.0 -v 213.0 548.0 227.0 -f -4 -3 -2 -1 - -o ceiling -usemtl white -v 556.0 548.8 0.0 -v 556.0 548.8 559.2 -v 0.0 548.8 559.2 -v 0.0 548.8 0.0 -f -4 -3 -2 -1 - -o back_wall -usemtl white -v 549.6 0.0 559.2 -v 0.0 0.0 559.2 -v 0.0 548.8 559.2 -v 556.0 548.8 559.2 -f -4 -3 -2 -1 - -o front_wall -usemtl blue -v 549.6 0.0 0 -v 0.0 0.0 0 -v 0.0 548.8 0 -v 556.0 548.8 0 -#f -1 -2 -3 -4 - -o green_wall -usemtl green -v 0.0 0.0 559.2 -v 0.0 0.0 0.0 -v 0.0 548.8 0.0 -v 0.0 548.8 559.2 -f -4 -3 -2 -1 - -o red_wall -usemtl red -v 552.8 0.0 0.0 -v 549.6 0.0 559.2 -v 556.0 548.8 559.2 -v 556.0 548.8 0.0 -f -4 -3 -2 -1 - -o short_block -usemtl white - -v 130.0 165.0 65.0 -v 82.0 165.0 225.0 -v 240.0 165.0 272.0 -v 290.0 165.0 114.0 -f -4 -3 -2 -1 - -v 290.0 0.0 114.0 -v 290.0 165.0 114.0 -v 240.0 165.0 272.0 -v 240.0 0.0 272.0 -f -4 -3 -2 -1 - -v 130.0 0.0 65.0 -v 130.0 165.0 65.0 -v 290.0 165.0 114.0 -v 290.0 0.0 114.0 -f -4 -3 -2 -1 - -v 82.0 0.0 225.0 -v 82.0 165.0 225.0 -v 130.0 165.0 65.0 -v 130.0 0.0 65.0 -f -4 -3 -2 -1 - -v 240.0 0.0 272.0 -v 240.0 165.0 272.0 -v 82.0 165.0 225.0 -v 82.0 0.0 225.0 -f -4 -3 -2 -1 - -o tall_block -usemtl white - -v 423.0 330.0 247.0 -v 265.0 330.0 296.0 -v 314.0 330.0 456.0 -v 472.0 330.0 406.0 -f -4 -3 -2 -1 - -usemtl white -v 423.0 0.0 247.0 -v 423.0 330.0 247.0 -v 472.0 330.0 406.0 -v 472.0 0.0 406.0 -f -4 -3 -2 -1 - -v 472.0 0.0 406.0 -v 472.0 330.0 406.0 -v 314.0 330.0 456.0 -v 314.0 0.0 456.0 -f -4 -3 -2 -1 - -v 314.0 0.0 456.0 -v 314.0 330.0 456.0 -v 265.0 330.0 296.0 -v 265.0 0.0 296.0 -f -4 -3 -2 -1 - -v 265.0 0.0 296.0 -v 265.0 330.0 296.0 -v 423.0 330.0 247.0 -v 423.0 0.0 247.0 -f -4 -3 -2 -1 - diff --git a/models/texture-filename-with-whitespace.mtl b/models/texture-filename-with-whitespace.mtl deleted file mode 100644 index 70b1a4e9..00000000 --- a/models/texture-filename-with-whitespace.mtl +++ /dev/null @@ -1,28 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 -# filename with white space. -map_Kd texture 01.png - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 -# texture option + filename with white space. -bump -bm 2 bump 01.png - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/texture-filename-with-whitespace.obj b/models/texture-filename-with-whitespace.obj deleted file mode 100644 index 46e61e78..00000000 --- a/models/texture-filename-with-whitespace.obj +++ /dev/null @@ -1,31 +0,0 @@ -mtllib texture-filename-with-whitespace.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl white -f 1 2 3 4 -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/models/texture-options-issue-85.mtl b/models/texture-options-issue-85.mtl deleted file mode 100644 index d4d62add..00000000 --- a/models/texture-options-issue-85.mtl +++ /dev/null @@ -1,36 +0,0 @@ -newmtl default -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Kt 0.1 0.2 0.3 -map_Ka -clamp on ambient.jpg -map_Kd -o 0.1 diffuse.jpg -map_Ks -s 0.1 0.2 specular.jpg -map_Ns -t 0.1 0.2 0.3 specular_highlight.jpg -map_bump -bm 3 bumpmap.jpg - -newmtl bm2 -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Kt 0.1 0.2 0.3 -# blendu -map_Kd -blendu on diffuse.jpg -map_Ks -blendv off specular.jpg -map_Ns -mm 0.1 0.3 specular_highlight.jpg -# -bm after filename -map_bump -imfchan r bumpmap2.jpg -bm 1.5 - -newmtl bm3 -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Kt 0.1 0.2 0.3 -# type -map_Kd -type sphere diffuse.jpg -map_Ks -type cube_top specular.jpg -map_Ns -type cube_bottom specular_highlight.jpg -map_Ka -type cube_left ambient.jpg -map_d -type cube_right alpha.jpg -map_bump -type cube_front bump.jpg -disp -type cube_back displacement.jpg diff --git a/models/texture-options-issue-85.obj b/models/texture-options-issue-85.obj deleted file mode 100644 index f7abb0c5..00000000 --- a/models/texture-options-issue-85.obj +++ /dev/null @@ -1,7 +0,0 @@ -mtllib texture-options-issue-85.mtl -o Test -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -usemtl default -f 1 2 3 diff --git a/models/tr-and-d-issue-43.mtl b/models/tr-and-d-issue-43.mtl deleted file mode 100644 index 44fd7acd..00000000 --- a/models/tr-and-d-issue-43.mtl +++ /dev/null @@ -1,13 +0,0 @@ -newmtl Material.001 -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -d 0.75 -Tr 0.5 - -newmtl Material.002 -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 -Tr 0.5 -d 0.75 diff --git a/models/tr-and-d-issue-43.obj b/models/tr-and-d-issue-43.obj deleted file mode 100644 index d86c3cf4..00000000 --- a/models/tr-and-d-issue-43.obj +++ /dev/null @@ -1,817 +0,0 @@ -# https://github.com/syoyo/tinyobjloader/issues/68 -# Blender v2.73 (sub 0) OBJ File: 'enemy.blend' -# www.blender.org -mtllib tr-and-d-issue-43.mtl -o Cube -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -v 1.620345 1.000000 -5.815706 -v 1.864152 1.000000 -6.334323 -v 0.575869 -0.129842 5.896143 -v 5.440438 -1.462153 -5.818601 -v 4.896782 -1.462153 -2.744413 -v 1.000825 -0.677484 1.899605 -v 5.440438 -1.246362 -5.818600 -v 1.000825 0.852342 1.899608 -v 4.896782 -1.246362 -2.744412 -v 1.160660 -0.450871 -2.356325 -v 1.704316 -0.450871 -5.430513 -v 1.000825 -0.351920 -1.293797 -v 1.000825 1.000000 -1.293794 -v 1.160660 -0.877888 -2.356326 -v 1.704316 -0.877888 -5.430514 -v 1.000825 -1.219172 -1.452514 -v 1.000825 1.000000 -1.452511 -v 1.000825 -0.351920 1.759410 -v 1.000825 1.000000 1.759413 -v 9.097919 1.221145 -6.212147 -v 8.356775 1.221145 -2.021231 -v 1.864151 -0.109586 -6.334325 -v 0.575869 -0.398073 5.896141 -v 9.097919 0.943958 -6.212148 -v 8.356775 0.943958 -2.021233 -v 1.061916 0.113661 -1.797961 -v 1.000825 0.161258 1.899606 -v 1.000825 0.324040 -1.293795 -v 1.803060 0.113661 -5.988876 -v 1.000825 -0.109586 -1.452513 -v 1.061916 0.776753 -1.797960 -v 1.803061 0.776753 -5.988875 -v 1.000825 0.324040 1.759412 -v 0.000825 -1.219172 -5.532512 -v 0.000825 -0.666304 5.896139 -v 0.000826 1.000000 -6.334325 -v 0.000825 -0.129842 5.896140 -v 0.000825 0.852342 1.899606 -v 0.000825 -0.677484 1.899604 -v 0.000825 -0.351920 -1.293797 -v 0.000825 1.000000 -1.293796 -v 0.000825 1.000000 -1.452513 -v 0.000825 -1.219172 -1.452515 -v 0.000825 -0.351920 1.759409 -v 0.000825 1.000000 1.759411 -v 0.000826 -0.109586 -6.334326 -v 0.000825 -0.398073 5.896140 -v 0.152918 1.000000 -5.815708 -v 0.152917 1.000000 -1.971130 -v 0.940448 1.168419 -1.971128 -v 1.620345 1.168419 -5.815706 -v 0.152918 1.168419 -5.815708 -v 0.152917 1.168419 -1.971130 -v 0.921118 1.091883 -1.050430 -v 0.921118 1.091883 1.516050 -v 0.080533 1.091883 -1.050432 -v 0.080533 1.091883 1.516048 -v 0.613003 -0.553430 5.546911 -v 0.963691 -0.559956 2.248834 -v 0.613003 -0.396857 5.546912 -v 0.963691 -0.070362 2.248835 -v 1.499370 -0.994317 3.966028 -v 1.850058 -0.997914 0.667950 -v 1.499370 -0.908021 3.966029 -v 1.850058 -0.728071 0.667951 -v 1.601022 0.760960 -6.334324 -v 1.601021 0.129454 -6.334325 -v 0.263955 0.760960 -6.334325 -v 0.263955 0.129454 -6.334325 -v 1.334809 0.760960 -7.515329 -v 1.334809 0.129455 -7.515330 -v 0.530168 0.760960 -7.515330 -v 0.530168 0.129455 -7.515330 -v 1.192720 0.649445 -7.515329 -v 1.192720 0.240971 -7.515330 -v 0.672258 0.649445 -7.515330 -v 0.672258 0.240971 -7.515330 -v 1.192719 0.649444 -6.524630 -v 1.192719 0.240970 -6.524631 -v 0.672257 0.649444 -6.524631 -v 0.672257 0.240970 -6.524631 -v 3.851026 0.431116 -1.883326 -v 3.851026 0.946662 -1.883325 -v 4.592170 0.946662 -6.074241 -v 4.592169 0.431116 -6.074242 -v 4.995714 0.561404 -1.918362 -v 4.995714 1.016394 -1.918360 -v 5.736857 1.016394 -6.109276 -v 5.736857 0.561404 -6.109277 -v 3.975454 0.471731 -2.162156 -v 3.975454 0.919244 -2.162155 -v 4.618796 0.919244 -5.800034 -v 4.618795 0.471730 -5.800035 -v 4.969088 0.584825 -2.192568 -v 4.969088 0.979775 -2.192567 -v 5.612430 0.979775 -5.830446 -v 5.612429 0.584825 -5.830447 -v 0.864214 -0.673890 3.184381 -v 0.864213 0.489129 3.184384 -v 0.864213 -0.018552 3.184383 -v 0.000825 0.489129 3.184382 -v 0.000825 -0.673890 3.184381 -v 0.850955 -0.557858 3.309075 -v 0.850955 -0.175321 3.309076 -v 1.737321 -0.996758 1.728192 -v 1.737321 -0.785920 1.728193 -v -1.864151 -1.219172 -5.532511 -v -0.575869 -0.666304 5.896140 -v -0.940448 1.000000 -1.971128 -v -1.620345 1.000000 -5.815706 -v -1.864152 1.000000 -6.334323 -v -0.575869 -0.129842 5.896143 -v -5.440438 -1.462153 -5.818601 -v -4.896782 -1.462153 -2.744413 -v -1.000825 -0.677484 1.899605 -v -5.440438 -1.246362 -5.818600 -v -1.000825 0.852342 1.899608 -v -4.896782 -1.246362 -2.744412 -v -1.160660 -0.450871 -2.356325 -v -1.704316 -0.450871 -5.430513 -v -1.000825 -0.351920 -1.293797 -v -1.000825 1.000000 -1.293794 -v -1.160660 -0.877888 -2.356326 -v -1.704316 -0.877888 -5.430514 -v -1.000825 -1.219172 -1.452514 -v -1.000825 1.000000 -1.452511 -v -1.000825 -0.351920 1.759410 -v -1.000825 1.000000 1.759413 -v -9.097919 1.221145 -6.212147 -v -8.356775 1.221145 -2.021231 -v -1.864151 -0.109586 -6.334325 -v -0.575869 -0.398073 5.896141 -v -9.097919 0.943958 -6.212148 -v -8.356775 0.943958 -2.021233 -v -1.061916 0.113661 -1.797961 -v -1.000825 0.161258 1.899606 -v -1.000825 0.324040 -1.293795 -v -1.803060 0.113661 -5.988876 -v -1.000825 -0.109586 -1.452513 -v -1.061916 0.776753 -1.797960 -v -1.803061 0.776753 -5.988875 -v -1.000825 0.324040 1.759412 -v -0.000825 -1.219172 -5.532512 -v -0.000825 -0.666304 5.896139 -v -0.000826 1.000000 -6.334325 -v -0.000825 -0.129842 5.896140 -v -0.000825 0.852342 1.899606 -v -0.000825 -0.677484 1.899604 -v -0.000825 -0.351920 -1.293797 -v -0.000825 1.000000 -1.293796 -v -0.000825 1.000000 -1.452513 -v -0.000825 -1.219172 -1.452515 -v -0.000825 -0.351920 1.759409 -v -0.000825 1.000000 1.759411 -v -0.000826 -0.109586 -6.334326 -v -0.000825 -0.398073 5.896140 -v -0.152918 1.000000 -5.815708 -v -0.152917 1.000000 -1.971130 -v -0.940448 1.168419 -1.971128 -v -1.620345 1.168419 -5.815706 -v -0.152918 1.168419 -5.815708 -v -0.152917 1.168419 -1.971130 -v -0.921118 1.091883 -1.050430 -v -0.921118 1.091883 1.516050 -v -0.080533 1.091883 -1.050432 -v -0.080533 1.091883 1.516048 -v -0.613003 -0.553430 5.546911 -v -0.963691 -0.559956 2.248834 -v -0.613003 -0.396857 5.546912 -v -0.963691 -0.070362 2.248835 -v -1.499370 -0.994317 3.966028 -v -1.850058 -0.997914 0.667950 -v -1.499370 -0.908021 3.966029 -v -1.850058 -0.728071 0.667951 -v -1.601022 0.760960 -6.334324 -v -1.601021 0.129454 -6.334325 -v -0.263955 0.760960 -6.334325 -v -0.263955 0.129454 -6.334325 -v -1.334809 0.760960 -7.515329 -v -1.334809 0.129455 -7.515330 -v -0.530168 0.760960 -7.515330 -v -0.530168 0.129455 -7.515330 -v -1.192720 0.649445 -7.515329 -v -1.192720 0.240971 -7.515330 -v -0.672258 0.649445 -7.515330 -v -0.672258 0.240971 -7.515330 -v -1.192719 0.649444 -6.524630 -v -1.192719 0.240970 -6.524631 -v -0.672257 0.649444 -6.524631 -v -0.672257 0.240970 -6.524631 -v -3.851026 0.431116 -1.883326 -v -3.851026 0.946662 -1.883325 -v -4.592170 0.946662 -6.074241 -v -4.592169 0.431116 -6.074242 -v -4.995714 0.561404 -1.918362 -v -4.995714 1.016394 -1.918360 -v -5.736857 1.016394 -6.109276 -v -5.736857 0.561404 -6.109277 -v -3.975454 0.471731 -2.162156 -v -3.975454 0.919244 -2.162155 -v -4.618796 0.919244 -5.800034 -v -4.618795 0.471730 -5.800035 -v -4.969088 0.584825 -2.192568 -v -4.969088 0.979775 -2.192567 -v -5.612430 0.979775 -5.830446 -v -5.612429 0.584825 -5.830447 -v -0.864214 -0.673890 3.184381 -v -0.864213 0.489129 3.184384 -v -0.864213 -0.018552 3.184383 -v -0.000825 0.489129 3.184382 -v -0.000825 -0.673890 3.184381 -v -0.850955 -0.557858 3.309075 -v -0.850955 -0.175321 3.309076 -v -1.737321 -0.996758 1.728192 -v -1.737321 -0.785920 1.728193 -vt 0.135351 -0.558072 -vt 0.003035 -0.363507 -vt 0.092282 -0.976844 -vt -0.081322 0.947351 -vt 0.100058 1.958891 -vt 0.050091 1.852185 -vt -0.092752 1.055565 -vt -0.251711 1.059474 -vt 0.075587 0.041384 -vt -0.086008 0.279003 -vt -0.086212 0.249830 -vt -0.276044 1.968137 -vt -0.246101 1.859467 -vt 0.009828 1.911388 -vt -0.133014 1.114769 -vt 0.413322 1.261595 -vt 0.299103 0.624605 -vt 1.243955 0.407183 -vt 0.515404 1.111487 -vt 1.358173 1.044173 -vt -0.081553 0.914324 -vt 0.080042 0.676706 -vt 0.401185 0.474498 -vt 1.295541 0.331328 -vt 0.365315 1.568841 -vt 0.299111 1.575740 -vt 0.143401 0.707357 -vt 0.629403 1.011947 -vt 0.449192 0.167251 -vt 1.409760 0.968317 -vt 0.986264 1.738667 -vt 1.573373 1.877873 -vt 1.417663 1.009490 -vt 0.237182 -0.196235 -vt 0.721785 1.030226 -vt 0.830554 0.870285 -vt 0.877494 1.898608 -vt 1.351399 1.106930 -vt 0.183935 0.557301 -vt 1.507109 1.975312 -vt 0.241636 0.439088 -vt 0.114297 -0.045011 -vt 0.140593 1.808834 -vt -0.015118 0.940452 -vt 0.156405 -1.071134 -vt 0.164119 -0.998223 -vt 0.040336 -1.068281 -vt 0.104459 -1.162571 -vt -0.165787 1.882802 -vt -0.014821 1.660811 -vt -0.287852 0.283965 -vt -0.293374 0.366508 -vt -0.289630 0.900550 -vt 0.035337 -0.191272 -vt 0.247348 0.172213 -vt 0.253300 1.021193 -vt -0.283166 0.952313 -vt -0.283398 0.919286 -vt 0.039792 0.444050 -vt 0.314806 -0.339851 -vt 0.112962 -0.334889 -vt -0.288056 0.254793 -vt -0.023788 -0.973990 -vt -0.155922 -0.359599 -vt 0.220528 -1.165425 -vt 0.108710 -0.748730 -vt -0.286364 1.918670 -vt -0.291973 1.118678 -vt -0.119962 0.896379 -vt -0.123707 0.362337 -vt 0.162891 -0.598569 -vt 0.467532 -0.853353 -vt 0.201549 -1.053262 -vt 0.161663 -0.198915 -vt 0.267667 -0.752638 -vt 0.278705 -0.371021 -vt 0.526390 -0.542053 -vt 0.483821 -0.479457 -vt 0.488162 -0.883689 -vt 0.500110 -0.105561 -vt 0.564618 -0.200418 -vt -0.110331 2.127229 -vt 0.040636 1.905238 -vt -0.010786 1.578087 -vt 0.104092 1.876168 -vt 0.255058 1.654176 -vt -0.054992 2.087323 -vt 0.203048 1.901245 -vt 0.052081 2.123235 -vt 0.042658 1.943733 -vt -0.056437 1.881175 -vt 0.147710 1.941151 -vt 0.050060 2.084741 -vt 0.146264 1.735002 -vt 0.041212 1.737584 -vt 0.048615 1.878591 -vt 0.663065 1.872485 -vt 0.786311 1.691257 -vt 0.507355 1.004102 -vt 0.630601 0.822874 -vt 0.955144 1.689498 -vt 0.860727 1.828333 -vt 0.725565 1.074543 -vt 0.819981 0.935708 -vt 0.674594 1.805657 -vt 0.539432 1.051867 -vt 0.646413 0.894554 -vt 0.781576 1.648344 -vt 0.240127 -0.712141 -vn 0.994400 0.000000 0.105700 -vn 0.000000 1.000000 0.000000 -vn 1.000000 0.000000 0.000000 -vn 0.984700 0.000000 0.174100 -vn 0.211800 0.976600 0.037500 -vn -0.103300 0.000000 -0.994600 -vn 0.103300 -0.000000 0.994600 -vn 0.911400 0.378700 0.161200 -vn -0.157300 -0.987200 -0.027800 -vn 0.113700 -0.993300 0.020100 -vn 0.030600 -0.000000 0.999500 -vn -0.061100 0.998100 -0.010800 -vn -0.030600 0.000000 -0.999500 -vn -0.000000 -0.000000 1.000000 -vn 0.000000 0.000000 -1.000000 -vn -0.755400 0.655300 0.000000 -vn 0.000000 -1.000000 0.000000 -vn -0.000000 -0.180000 0.983700 -vn 0.000000 -0.395500 -0.918500 -vn -0.000000 0.688500 0.725200 -vn 0.000000 -0.585700 -0.810500 -vn -0.000000 0.974900 0.222500 -vn -0.000000 -1.000000 0.002800 -vn -1.000000 0.000000 -0.000000 -vn -0.000000 0.935500 0.353200 -vn 0.755400 0.655300 0.000000 -vn 0.000000 0.935500 -0.353200 -vn 0.673800 0.724900 0.143400 -vn 0.872300 -0.000000 0.489100 -vn -0.872300 0.000000 -0.489100 -vn -0.518300 -0.853500 -0.054200 -vn -0.975500 0.000000 -0.219900 -vn 0.975500 0.000000 -0.219900 -vn -0.913200 0.000000 -0.407500 -vn -0.436900 0.896200 -0.077300 -vn -0.995300 -0.000000 0.096600 -vn -0.297300 -0.953400 -0.052600 -vn 0.473900 -0.876600 0.083800 -vn 0.913200 0.000000 0.407500 -vn 0.342200 0.937700 0.060500 -vn 0.995300 -0.000000 -0.096600 -vn -0.519200 -0.853000 -0.054300 -vn 0.722400 0.676400 0.143800 -vn -0.994400 0.000000 0.105700 -vn -0.984700 0.000000 0.174100 -vn -0.211800 0.976600 0.037500 -vn 0.103300 0.000000 -0.994600 -vn -0.103300 -0.000000 0.994600 -vn -0.911400 0.378700 0.161200 -vn 0.157300 -0.987200 -0.027800 -vn -0.113700 -0.993300 0.020100 -vn -0.030600 -0.000000 0.999500 -vn 0.061100 0.998100 -0.010800 -vn 0.030600 0.000000 -0.999500 -vn -0.691900 0.713200 0.112500 -vn -0.872300 -0.000000 0.489100 -vn 0.872300 0.000000 -0.489100 -vn 0.518300 -0.853500 -0.054200 -vn 0.913200 0.000000 -0.407500 -vn 0.436900 0.896200 -0.077300 -vn 0.995300 0.000000 0.096600 -vn 0.297300 -0.953300 -0.052600 -vn -0.473900 -0.876600 0.083800 -vn -0.913200 -0.000000 0.407500 -vn -0.342200 0.937700 0.060500 -vn -0.995300 -0.000000 -0.096600 -vn 0.519200 -0.853000 -0.054300 -vn -0.714800 0.690100 0.113700 -vn 0.974400 0.089700 0.206200 -vn 0.870400 0.288400 0.399100 -vn 0.691900 0.713200 0.112500 -vn -0.518000 -0.853700 -0.053400 -vn -0.519700 -0.852700 -0.053600 -vn 0.714800 0.690100 0.113700 -vn -0.974400 0.089700 0.206200 -vn -0.870400 0.288400 0.399100 -vn -0.673800 0.724900 0.143400 -vn 0.518000 -0.853700 -0.053400 -vn 0.297300 -0.953400 -0.052600 -vn 0.519700 -0.852700 -0.053600 -vn -0.722400 0.676400 0.143800 -vn -0.000000 0.962300 0.272000 -usemtl Material.001 -s off -f 103/1/1 102/2/1 6/3/1 -f 20/4/2 5/5/2 4/6/2 -f 20/4/2 3/7/2 52/8/2 -f 36/9/3 22/10/3 11/11/3 -f 39/12/2 51/13/2 4/6/2 -f 4/6/4 54/14/4 53/15/4 -f 14/16/5 13/17/5 12/18/5 -f 18/19/6 14/16/6 10/20/6 -f 20/4/3 16/21/3 31/22/3 -f 17/23/7 8/24/7 12/18/7 -f 25/25/4 32/26/4 29/27/4 -f 10/20/4 12/18/4 8/24/4 -f 1/28/8 18/19/8 17/23/8 -f 19/29/4 17/23/4 13/17/4 -f 25/25/4 14/16/4 18/19/4 -f 18/19/9 7/30/9 8/24/9 -f 92/31/10 27/32/10 28/33/10 -f 16/21/3 22/10/3 36/9/3 -f 31/22/3 36/9/3 21/34/3 -f 90/35/11 89/36/11 28/33/11 -f 91/37/12 90/35/12 24/38/12 -f 33/39/4 13/17/4 14/16/4 -f 23/40/4 24/38/4 28/33/4 -f 33/39/3 31/22/3 15/41/3 -f 21/34/3 36/9/3 30/42/3 -f 5/5/4 35/43/4 32/26/4 -f 5/5/4 20/4/4 34/44/4 -f 33/39/4 29/27/4 34/44/4 -f 91/37/13 23/40/13 27/32/13 -f 103/1/1 26/45/1 63/46/1 -f 26/45/14 50/47/14 38/48/14 -f 39/12/15 71/49/15 72/50/15 -f 48/51/16 60/52/16 59/53/16 -f 15/41/17 21/34/17 47/54/17 -f 19/29/17 46/55/17 37/56/17 -f 39/12/2 45/57/2 52/8/2 -f 20/4/2 45/57/2 44/58/2 -f 19/29/18 15/41/18 43/59/18 -f 9/60/19 42/61/19 47/54/19 -f 22/10/20 48/51/20 41/62/20 -f 25/25/21 1/28/21 37/56/21 -f 6/3/14 40/63/14 50/47/14 -f 104/64/22 40/63/22 6/3/22 -f 2/65/23 38/48/23 105/66/23 -f 55/67/2 56/68/2 53/15/2 -f 3/7/14 53/15/14 56/68/14 -f 51/13/15 55/67/15 54/14/15 -f 52/8/24 56/68/24 55/67/24 -f 57/69/2 59/53/2 60/52/2 -f 48/51/25 22/10/25 58/70/25 -f 16/21/26 57/69/26 58/70/26 -f 16/21/27 44/58/27 59/53/27 -f 107/71/28 63/46/28 67/72/28 -f 26/45/1 2/65/1 61/73/1 -f 9/60/1 30/42/1 64/74/1 -f 101/75/1 9/60/1 62/76/1 -f 108/77/1 109/78/1 67/72/1 -f 61/73/29 65/79/29 67/72/29 -f 62/76/30 64/74/30 68/80/30 -f 62/76/31 66/81/31 108/77/31 -f 71/49/32 75/82/32 76/83/32 -f 25/25/15 49/84/15 72/50/15 -f 5/5/15 69/85/15 71/49/15 -f 25/25/15 70/86/15 69/85/15 -f 76/83/15 75/82/15 79/87/15 -f 72/50/17 76/83/17 74/88/17 -f 71/49/2 69/85/2 73/89/2 -f 70/86/33 74/88/33 73/89/33 -f 80/90/3 79/87/3 83/91/3 -f 76/83/15 80/90/15 78/92/15 -f 75/82/15 73/89/15 77/93/15 -f 74/88/15 78/92/15 77/93/15 -f 82/94/15 84/95/15 83/91/15 -f 80/90/2 84/95/2 82/94/2 -f 77/93/17 81/96/17 83/91/17 -f 77/93/24 78/92/24 82/94/24 -f 35/43/13 87/97/13 88/98/13 -f 35/43/12 34/44/12 86/99/12 -f 34/44/11 29/27/11 85/100/11 -f 32/26/10 88/98/10 85/100/10 -f 92/31/34 100/101/34 99/102/34 -f 90/35/35 91/37/35 99/102/35 -f 89/36/36 90/35/36 98/103/36 -f 89/36/37 97/104/37 100/101/37 -f 95/105/13 99/102/13 100/101/13 -f 95/105/12 94/106/12 98/103/12 -f 94/106/11 93/107/11 97/104/11 -f 96/108/10 100/101/10 97/104/10 -f 88/98/38 96/108/38 93/107/38 -f 86/99/39 85/100/39 93/107/39 -f 87/97/40 86/99/40 94/106/40 -f 87/97/41 95/105/41 96/108/41 -f 106/109/42 108/77/42 65/79/42 -f 66/81/1 68/80/1 109/78/1 -f 101/75/1 106/109/1 61/73/1 -f 64/74/43 107/71/43 109/78/43 -f 101/75/23 105/66/23 42/61/23 -f 103/1/1 107/71/1 64/74/1 -f 30/42/1 11/11/1 102/2/1 -f 212/1/44 135/45/44 115/3/44 -f 129/4/2 112/7/2 113/6/2 -f 161/8/2 112/7/2 129/4/2 -f 145/9/24 139/42/24 120/11/24 -f 113/6/2 160/13/2 148/12/2 -f 162/15/45 163/14/45 113/6/45 -f 123/16/46 119/20/46 121/18/46 -f 127/19/47 116/30/47 119/20/47 -f 140/22/24 125/21/24 129/4/24 -f 121/18/48 117/24/48 126/23/48 -f 138/27/45 141/26/45 134/25/45 -f 117/24/45 121/18/45 119/20/45 -f 126/23/49 127/19/49 110/28/49 -f 122/17/45 126/23/45 128/29/45 -f 127/19/45 123/16/45 134/25/45 -f 117/24/50 116/30/50 127/19/50 -f 137/33/51 136/32/51 201/31/51 -f 145/9/24 131/10/24 125/21/24 -f 130/34/24 145/9/24 140/22/24 -f 199/35/52 133/38/52 137/33/52 -f 200/37/53 132/40/53 133/38/53 -f 123/16/45 122/17/45 142/39/45 -f 137/33/45 133/38/45 132/40/45 -f 124/41/24 140/22/24 142/39/24 -f 130/34/24 118/60/24 139/42/24 -f 141/26/45 144/43/45 114/5/45 -f 114/5/45 144/43/45 143/44/45 -f 143/44/45 138/27/45 142/39/45 -f 136/32/54 132/40/54 200/37/54 -f 212/1/44 216/71/44 172/46/44 -f 147/48/14 159/47/14 135/45/14 -f 181/50/15 180/49/15 148/12/15 -f 168/53/26 169/52/26 157/51/26 -f 124/41/17 152/59/17 156/54/17 -f 146/56/17 155/55/17 128/29/17 -f 148/12/2 160/13/2 161/8/2 -f 129/4/2 125/21/2 153/58/2 -f 155/55/18 152/59/18 124/41/18 -f 130/34/19 156/54/19 151/61/19 -f 131/10/20 120/11/20 150/62/20 -f 134/25/21 158/84/21 146/56/21 -f 159/47/14 149/63/14 115/3/14 -f 115/3/22 149/63/22 213/64/22 -f 214/66/23 147/48/23 111/65/23 -f 162/15/2 165/68/2 164/67/2 -f 165/68/14 162/15/14 112/7/14 -f 163/14/15 164/67/15 160/13/15 -f 164/67/3 165/68/3 161/8/3 -f 166/69/2 167/70/2 169/52/2 -f 157/51/25 169/52/25 167/70/25 -f 167/70/16 166/69/16 125/21/16 -f 125/21/27 166/69/27 168/53/27 -f 216/71/55 218/78/55 176/72/55 -f 135/45/44 172/46/44 170/73/44 -f 118/60/44 171/76/44 173/74/44 -f 210/75/44 215/109/44 171/76/44 -f 217/77/44 174/79/44 176/72/44 -f 176/72/56 174/79/56 170/73/56 -f 171/76/57 175/81/57 177/80/57 -f 217/77/58 175/81/58 171/76/58 -f 185/83/33 184/82/33 180/49/33 -f 134/25/15 179/86/15 181/50/15 -f 180/49/15 178/85/15 114/5/15 -f 178/85/15 179/86/15 134/25/15 -f 189/90/15 188/87/15 184/82/15 -f 183/88/17 185/83/17 181/50/17 -f 180/49/2 184/82/2 182/89/2 -f 182/89/32 183/88/32 179/86/32 -f 189/90/24 193/95/24 192/91/24 -f 187/92/15 189/90/15 185/83/15 -f 184/82/15 188/87/15 186/93/15 -f 186/93/15 187/92/15 183/88/15 -f 192/91/15 193/95/15 191/94/15 -f 191/94/2 193/95/2 189/90/2 -f 192/91/17 190/96/17 186/93/17 -f 186/93/3 190/96/3 191/94/3 -f 197/98/54 196/97/54 144/43/54 -f 144/43/53 196/97/53 195/99/53 -f 143/44/52 195/99/52 194/100/52 -f 194/100/51 197/98/51 141/26/51 -f 208/102/59 209/101/59 201/31/59 -f 199/35/60 207/103/60 208/102/60 -f 198/36/61 206/104/61 207/103/61 -f 209/101/62 206/104/62 198/36/62 -f 209/101/54 208/102/54 204/105/54 -f 204/105/53 208/102/53 207/103/53 -f 203/106/52 207/103/52 206/104/52 -f 206/104/51 209/101/51 205/108/51 -f 202/107/63 205/108/63 197/98/63 -f 195/99/64 203/106/64 202/107/64 -f 196/97/65 204/105/65 203/106/65 -f 205/108/66 204/105/66 196/97/66 -f 174/79/67 217/77/67 215/109/67 -f 175/81/44 217/77/44 218/78/44 -f 170/73/44 215/109/44 210/75/44 -f 173/74/68 177/80/68 218/78/68 -f 151/61/23 214/66/23 210/75/23 -f 173/74/44 216/71/44 212/1/44 -f 139/42/44 212/1/44 211/2/44 -f 26/45/1 103/1/1 6/3/1 -f 3/7/2 20/4/2 4/6/2 -f 45/57/2 20/4/2 52/8/2 -f 30/42/3 36/9/3 11/11/3 -f 5/5/2 39/12/2 4/6/2 -f 3/7/4 4/6/4 53/15/4 -f 10/20/5 14/16/5 12/18/5 -f 7/30/6 18/19/6 10/20/6 -f 33/39/3 20/4/3 31/22/3 -f 13/17/7 17/23/7 12/18/7 -f 33/39/4 25/25/4 29/27/4 -f 7/30/4 10/20/4 8/24/4 -f 19/29/69 1/28/69 17/23/69 -f 33/39/4 19/29/4 13/17/4 -f 1/28/70 25/25/70 18/19/70 -f 17/23/9 18/19/9 8/24/9 -f 89/36/10 92/31/10 28/33/10 -f 31/22/3 16/21/3 36/9/3 -f 15/41/3 31/22/3 21/34/3 -f 24/38/11 90/35/11 28/33/11 -f 23/40/12 91/37/12 24/38/12 -f 25/25/4 33/39/4 14/16/4 -f 27/32/4 23/40/4 28/33/4 -f 19/29/3 33/39/3 15/41/3 -f 9/60/3 21/34/3 30/42/3 -f 25/25/4 5/5/4 32/26/4 -f 35/43/4 5/5/4 34/44/4 -f 20/4/4 33/39/4 34/44/4 -f 92/31/13 91/37/13 27/32/13 -f 107/71/1 103/1/1 63/46/1 -f 2/65/14 26/45/14 38/48/14 -f 49/84/15 39/12/15 72/50/15 -f 44/58/16 48/51/16 59/53/16 -f 43/59/17 15/41/17 47/54/17 -f 1/28/17 19/29/17 37/56/17 -f 51/13/2 39/12/2 52/8/2 -f 16/21/2 20/4/2 44/58/2 -f 46/55/18 19/29/18 43/59/18 -f 21/34/19 9/60/19 47/54/19 -f 11/11/20 22/10/20 41/62/20 -f 49/84/21 25/25/21 37/56/21 -f 26/45/14 6/3/14 50/47/14 -f 102/2/22 104/64/22 6/3/22 -f 101/75/23 2/65/23 105/66/23 -f 54/14/2 55/67/2 53/15/2 -f 52/8/14 3/7/14 56/68/14 -f 4/6/15 51/13/15 54/14/15 -f 51/13/24 52/8/24 55/67/24 -f 58/70/2 57/69/2 60/52/2 -f 60/52/25 48/51/25 58/70/25 -f 22/10/26 16/21/26 58/70/26 -f 57/69/27 16/21/27 59/53/27 -f 109/78/71 107/71/71 67/72/71 -f 63/46/1 26/45/1 61/73/1 -f 62/76/1 9/60/1 64/74/1 -f 106/109/1 101/75/1 62/76/1 -f 65/79/1 108/77/1 67/72/1 -f 63/46/29 61/73/29 67/72/29 -f 66/81/30 62/76/30 68/80/30 -f 106/109/72 62/76/72 108/77/72 -f 72/50/32 71/49/32 76/83/32 -f 70/86/15 25/25/15 72/50/15 -f 39/12/15 5/5/15 71/49/15 -f 5/5/15 25/25/15 69/85/15 -f 80/90/15 76/83/15 79/87/15 -f 70/86/17 72/50/17 74/88/17 -f 75/82/2 71/49/2 73/89/2 -f 69/85/33 70/86/33 73/89/33 -f 84/95/3 80/90/3 83/91/3 -f 74/88/15 76/83/15 78/92/15 -f 79/87/15 75/82/15 77/93/15 -f 73/89/15 74/88/15 77/93/15 -f 81/96/15 82/94/15 83/91/15 -f 78/92/2 80/90/2 82/94/2 -f 79/87/17 77/93/17 83/91/17 -f 81/96/24 77/93/24 82/94/24 -f 32/26/13 35/43/13 88/98/13 -f 87/97/12 35/43/12 86/99/12 -f 86/99/11 34/44/11 85/100/11 -f 29/27/10 32/26/10 85/100/10 -f 91/37/34 92/31/34 99/102/34 -f 98/103/35 90/35/35 99/102/35 -f 97/104/36 89/36/36 98/103/36 -f 92/31/37 89/36/37 100/101/37 -f 96/108/13 95/105/13 100/101/13 -f 99/102/12 95/105/12 98/103/12 -f 98/103/11 94/106/11 97/104/11 -f 93/107/10 96/108/10 97/104/10 -f 85/100/38 88/98/38 93/107/38 -f 94/106/39 86/99/39 93/107/39 -f 95/105/40 87/97/40 94/106/40 -f 88/98/41 87/97/41 96/108/41 -f 61/73/73 106/109/73 65/79/73 -f 108/77/1 66/81/1 109/78/1 -f 2/65/1 101/75/1 61/73/1 -f 68/80/74 64/74/74 109/78/74 -f 9/60/23 101/75/23 42/61/23 -f 30/42/1 103/1/1 64/74/1 -f 103/1/1 30/42/1 102/2/1 -f 211/2/44 212/1/44 115/3/44 -f 114/5/2 129/4/2 113/6/2 -f 154/57/2 161/8/2 129/4/2 -f 131/10/24 145/9/24 120/11/24 -f 114/5/2 113/6/2 148/12/2 -f 112/7/45 162/15/45 113/6/45 -f 122/17/46 123/16/46 121/18/46 -f 123/16/47 127/19/47 119/20/47 -f 142/39/24 140/22/24 129/4/24 -f 122/17/48 121/18/48 126/23/48 -f 142/39/45 138/27/45 134/25/45 -f 116/30/45 117/24/45 119/20/45 -f 128/29/75 126/23/75 110/28/75 -f 142/39/45 122/17/45 128/29/45 -f 110/28/76 127/19/76 134/25/76 -f 126/23/50 117/24/50 127/19/50 -f 198/36/51 137/33/51 201/31/51 -f 140/22/24 145/9/24 125/21/24 -f 124/41/24 130/34/24 140/22/24 -f 198/36/52 199/35/52 137/33/52 -f 199/35/53 200/37/53 133/38/53 -f 134/25/45 123/16/45 142/39/45 -f 136/32/45 137/33/45 132/40/45 -f 128/29/24 124/41/24 142/39/24 -f 145/9/24 130/34/24 139/42/24 -f 134/25/45 141/26/45 114/5/45 -f 129/4/45 114/5/45 143/44/45 -f 129/4/45 143/44/45 142/39/45 -f 201/31/54 136/32/54 200/37/54 -f 135/45/44 212/1/44 172/46/44 -f 111/65/14 147/48/14 135/45/14 -f 158/84/15 181/50/15 148/12/15 -f 153/58/26 168/53/26 157/51/26 -f 130/34/17 124/41/17 156/54/17 -f 110/28/17 146/56/17 128/29/17 -f 154/57/2 148/12/2 161/8/2 -f 154/57/2 129/4/2 153/58/2 -f 128/29/18 155/55/18 124/41/18 -f 118/60/19 130/34/19 151/61/19 -f 157/51/20 131/10/20 150/62/20 -f 110/28/21 134/25/21 146/56/21 -f 135/45/14 159/47/14 115/3/14 -f 211/2/22 115/3/22 213/64/22 -f 210/75/23 214/66/23 111/65/23 -f 163/14/2 162/15/2 164/67/2 -f 161/8/14 165/68/14 112/7/14 -f 113/6/15 163/14/15 160/13/15 -f 160/13/3 164/67/3 161/8/3 -f 168/53/2 166/69/2 169/52/2 -f 131/10/25 157/51/25 167/70/25 -f 131/10/16 167/70/16 125/21/16 -f 153/58/27 125/21/27 168/53/27 -f 172/46/77 216/71/77 176/72/77 -f 111/65/44 135/45/44 170/73/44 -f 139/42/44 118/60/44 173/74/44 -f 118/60/44 210/75/44 171/76/44 -f 218/78/44 217/77/44 176/72/44 -f 172/46/56 176/72/56 170/73/56 -f 173/74/57 171/76/57 177/80/57 -f 215/109/78 217/77/78 171/76/78 -f 181/50/33 185/83/33 180/49/33 -f 158/84/15 134/25/15 181/50/15 -f 148/12/15 180/49/15 114/5/15 -f 114/5/15 178/85/15 134/25/15 -f 185/83/15 189/90/15 184/82/15 -f 179/86/17 183/88/17 181/50/17 -f 178/85/2 180/49/2 182/89/2 -f 178/85/32 182/89/32 179/86/32 -f 188/87/24 189/90/24 192/91/24 -f 183/88/15 187/92/15 185/83/15 -f 182/89/15 184/82/15 186/93/15 -f 182/89/15 186/93/15 183/88/15 -f 190/96/15 192/91/15 191/94/15 -f 187/92/2 191/94/2 189/90/2 -f 188/87/17 192/91/17 186/93/17 -f 187/92/3 186/93/3 191/94/3 -f 141/26/54 197/98/54 144/43/54 -f 143/44/53 144/43/53 195/99/53 -f 138/27/52 143/44/52 194/100/52 -f 138/27/51 194/100/51 141/26/51 -f 200/37/59 208/102/59 201/31/59 -f 200/37/60 199/35/60 208/102/60 -f 199/35/61 198/36/61 207/103/61 -f 201/31/79 209/101/79 198/36/79 -f 205/108/54 209/101/54 204/105/54 -f 203/106/53 204/105/53 207/103/53 -f 202/107/52 203/106/52 206/104/52 -f 202/107/51 206/104/51 205/108/51 -f 194/100/63 202/107/63 197/98/63 -f 194/100/64 195/99/64 202/107/64 -f 195/99/65 196/97/65 203/106/65 -f 197/98/66 205/108/66 196/97/66 -f 170/73/80 174/79/80 215/109/80 -f 177/80/44 175/81/44 218/78/44 -f 111/65/44 170/73/44 210/75/44 -f 216/71/81 173/74/81 218/78/81 -f 118/60/23 151/61/23 210/75/23 -f 139/42/44 173/74/44 212/1/44 -f 120/11/44 139/42/44 211/2/44 -usemtl Material.002 -f 41/62/82 104/64/82 102/2/82 -f 211/2/82 213/64/82 150/62/82 -f 11/11/82 41/62/82 102/2/82 -f 120/11/82 211/2/82 150/62/82 diff --git a/models/usemtl-issue-104.obj b/models/usemtl-issue-104.obj deleted file mode 100644 index 51836765..00000000 --- a/models/usemtl-issue-104.obj +++ /dev/null @@ -1,30 +0,0 @@ -# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. -# original cornell box data - # comment - -# empty line including some space - - -mtllib cornell_box.mtl - -o floor -v 552.8 0.0 0.0 -v 0.0 0.0 0.0 -v 0.0 0.0 559.2 -v 549.6 0.0 559.2 - -v 130.0 0.0 65.0 -v 82.0 0.0 225.0 -v 240.0 0.0 272.0 -v 290.0 0.0 114.0 - -v 423.0 0.0 247.0 -v 265.0 0.0 296.0 -v 314.0 0.0 456.0 -v 472.0 0.0 406.0 - -f 1 2 3 4 -f 8 7 6 5 -f 12 11 10 9 - -usemtl white diff --git a/models/usemtl-issue-68.mtl b/models/usemtl-issue-68.mtl deleted file mode 100644 index 24a791eb..00000000 --- a/models/usemtl-issue-68.mtl +++ /dev/null @@ -1,9 +0,0 @@ -newmtl Material.001 -Ka 0 0 0 -Kd 0 0 0 -Ks 0 0 0 - -newmtl Material.003 -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 diff --git a/models/usemtl-issue-68.obj b/models/usemtl-issue-68.obj deleted file mode 100644 index dddc900e..00000000 --- a/models/usemtl-issue-68.obj +++ /dev/null @@ -1,817 +0,0 @@ -# https://github.com/syoyo/tinyobjloader/issues/68 -# Blender v2.73 (sub 0) OBJ File: 'enemy.blend' -# www.blender.org -mtllib usemtl-issue-68.mtl -o Cube -v 1.864151 -1.219172 -5.532511 -v 0.575869 -0.666304 5.896140 -v 0.940448 1.000000 -1.971128 -v 1.620345 1.000000 -5.815706 -v 1.864152 1.000000 -6.334323 -v 0.575869 -0.129842 5.896143 -v 5.440438 -1.462153 -5.818601 -v 4.896782 -1.462153 -2.744413 -v 1.000825 -0.677484 1.899605 -v 5.440438 -1.246362 -5.818600 -v 1.000825 0.852342 1.899608 -v 4.896782 -1.246362 -2.744412 -v 1.160660 -0.450871 -2.356325 -v 1.704316 -0.450871 -5.430513 -v 1.000825 -0.351920 -1.293797 -v 1.000825 1.000000 -1.293794 -v 1.160660 -0.877888 -2.356326 -v 1.704316 -0.877888 -5.430514 -v 1.000825 -1.219172 -1.452514 -v 1.000825 1.000000 -1.452511 -v 1.000825 -0.351920 1.759410 -v 1.000825 1.000000 1.759413 -v 9.097919 1.221145 -6.212147 -v 8.356775 1.221145 -2.021231 -v 1.864151 -0.109586 -6.334325 -v 0.575869 -0.398073 5.896141 -v 9.097919 0.943958 -6.212148 -v 8.356775 0.943958 -2.021233 -v 1.061916 0.113661 -1.797961 -v 1.000825 0.161258 1.899606 -v 1.000825 0.324040 -1.293795 -v 1.803060 0.113661 -5.988876 -v 1.000825 -0.109586 -1.452513 -v 1.061916 0.776753 -1.797960 -v 1.803061 0.776753 -5.988875 -v 1.000825 0.324040 1.759412 -v 0.000825 -1.219172 -5.532512 -v 0.000825 -0.666304 5.896139 -v 0.000826 1.000000 -6.334325 -v 0.000825 -0.129842 5.896140 -v 0.000825 0.852342 1.899606 -v 0.000825 -0.677484 1.899604 -v 0.000825 -0.351920 -1.293797 -v 0.000825 1.000000 -1.293796 -v 0.000825 1.000000 -1.452513 -v 0.000825 -1.219172 -1.452515 -v 0.000825 -0.351920 1.759409 -v 0.000825 1.000000 1.759411 -v 0.000826 -0.109586 -6.334326 -v 0.000825 -0.398073 5.896140 -v 0.152918 1.000000 -5.815708 -v 0.152917 1.000000 -1.971130 -v 0.940448 1.168419 -1.971128 -v 1.620345 1.168419 -5.815706 -v 0.152918 1.168419 -5.815708 -v 0.152917 1.168419 -1.971130 -v 0.921118 1.091883 -1.050430 -v 0.921118 1.091883 1.516050 -v 0.080533 1.091883 -1.050432 -v 0.080533 1.091883 1.516048 -v 0.613003 -0.553430 5.546911 -v 0.963691 -0.559956 2.248834 -v 0.613003 -0.396857 5.546912 -v 0.963691 -0.070362 2.248835 -v 1.499370 -0.994317 3.966028 -v 1.850058 -0.997914 0.667950 -v 1.499370 -0.908021 3.966029 -v 1.850058 -0.728071 0.667951 -v 1.601022 0.760960 -6.334324 -v 1.601021 0.129454 -6.334325 -v 0.263955 0.760960 -6.334325 -v 0.263955 0.129454 -6.334325 -v 1.334809 0.760960 -7.515329 -v 1.334809 0.129455 -7.515330 -v 0.530168 0.760960 -7.515330 -v 0.530168 0.129455 -7.515330 -v 1.192720 0.649445 -7.515329 -v 1.192720 0.240971 -7.515330 -v 0.672258 0.649445 -7.515330 -v 0.672258 0.240971 -7.515330 -v 1.192719 0.649444 -6.524630 -v 1.192719 0.240970 -6.524631 -v 0.672257 0.649444 -6.524631 -v 0.672257 0.240970 -6.524631 -v 3.851026 0.431116 -1.883326 -v 3.851026 0.946662 -1.883325 -v 4.592170 0.946662 -6.074241 -v 4.592169 0.431116 -6.074242 -v 4.995714 0.561404 -1.918362 -v 4.995714 1.016394 -1.918360 -v 5.736857 1.016394 -6.109276 -v 5.736857 0.561404 -6.109277 -v 3.975454 0.471731 -2.162156 -v 3.975454 0.919244 -2.162155 -v 4.618796 0.919244 -5.800034 -v 4.618795 0.471730 -5.800035 -v 4.969088 0.584825 -2.192568 -v 4.969088 0.979775 -2.192567 -v 5.612430 0.979775 -5.830446 -v 5.612429 0.584825 -5.830447 -v 0.864214 -0.673890 3.184381 -v 0.864213 0.489129 3.184384 -v 0.864213 -0.018552 3.184383 -v 0.000825 0.489129 3.184382 -v 0.000825 -0.673890 3.184381 -v 0.850955 -0.557858 3.309075 -v 0.850955 -0.175321 3.309076 -v 1.737321 -0.996758 1.728192 -v 1.737321 -0.785920 1.728193 -v -1.864151 -1.219172 -5.532511 -v -0.575869 -0.666304 5.896140 -v -0.940448 1.000000 -1.971128 -v -1.620345 1.000000 -5.815706 -v -1.864152 1.000000 -6.334323 -v -0.575869 -0.129842 5.896143 -v -5.440438 -1.462153 -5.818601 -v -4.896782 -1.462153 -2.744413 -v -1.000825 -0.677484 1.899605 -v -5.440438 -1.246362 -5.818600 -v -1.000825 0.852342 1.899608 -v -4.896782 -1.246362 -2.744412 -v -1.160660 -0.450871 -2.356325 -v -1.704316 -0.450871 -5.430513 -v -1.000825 -0.351920 -1.293797 -v -1.000825 1.000000 -1.293794 -v -1.160660 -0.877888 -2.356326 -v -1.704316 -0.877888 -5.430514 -v -1.000825 -1.219172 -1.452514 -v -1.000825 1.000000 -1.452511 -v -1.000825 -0.351920 1.759410 -v -1.000825 1.000000 1.759413 -v -9.097919 1.221145 -6.212147 -v -8.356775 1.221145 -2.021231 -v -1.864151 -0.109586 -6.334325 -v -0.575869 -0.398073 5.896141 -v -9.097919 0.943958 -6.212148 -v -8.356775 0.943958 -2.021233 -v -1.061916 0.113661 -1.797961 -v -1.000825 0.161258 1.899606 -v -1.000825 0.324040 -1.293795 -v -1.803060 0.113661 -5.988876 -v -1.000825 -0.109586 -1.452513 -v -1.061916 0.776753 -1.797960 -v -1.803061 0.776753 -5.988875 -v -1.000825 0.324040 1.759412 -v -0.000825 -1.219172 -5.532512 -v -0.000825 -0.666304 5.896139 -v -0.000826 1.000000 -6.334325 -v -0.000825 -0.129842 5.896140 -v -0.000825 0.852342 1.899606 -v -0.000825 -0.677484 1.899604 -v -0.000825 -0.351920 -1.293797 -v -0.000825 1.000000 -1.293796 -v -0.000825 1.000000 -1.452513 -v -0.000825 -1.219172 -1.452515 -v -0.000825 -0.351920 1.759409 -v -0.000825 1.000000 1.759411 -v -0.000826 -0.109586 -6.334326 -v -0.000825 -0.398073 5.896140 -v -0.152918 1.000000 -5.815708 -v -0.152917 1.000000 -1.971130 -v -0.940448 1.168419 -1.971128 -v -1.620345 1.168419 -5.815706 -v -0.152918 1.168419 -5.815708 -v -0.152917 1.168419 -1.971130 -v -0.921118 1.091883 -1.050430 -v -0.921118 1.091883 1.516050 -v -0.080533 1.091883 -1.050432 -v -0.080533 1.091883 1.516048 -v -0.613003 -0.553430 5.546911 -v -0.963691 -0.559956 2.248834 -v -0.613003 -0.396857 5.546912 -v -0.963691 -0.070362 2.248835 -v -1.499370 -0.994317 3.966028 -v -1.850058 -0.997914 0.667950 -v -1.499370 -0.908021 3.966029 -v -1.850058 -0.728071 0.667951 -v -1.601022 0.760960 -6.334324 -v -1.601021 0.129454 -6.334325 -v -0.263955 0.760960 -6.334325 -v -0.263955 0.129454 -6.334325 -v -1.334809 0.760960 -7.515329 -v -1.334809 0.129455 -7.515330 -v -0.530168 0.760960 -7.515330 -v -0.530168 0.129455 -7.515330 -v -1.192720 0.649445 -7.515329 -v -1.192720 0.240971 -7.515330 -v -0.672258 0.649445 -7.515330 -v -0.672258 0.240971 -7.515330 -v -1.192719 0.649444 -6.524630 -v -1.192719 0.240970 -6.524631 -v -0.672257 0.649444 -6.524631 -v -0.672257 0.240970 -6.524631 -v -3.851026 0.431116 -1.883326 -v -3.851026 0.946662 -1.883325 -v -4.592170 0.946662 -6.074241 -v -4.592169 0.431116 -6.074242 -v -4.995714 0.561404 -1.918362 -v -4.995714 1.016394 -1.918360 -v -5.736857 1.016394 -6.109276 -v -5.736857 0.561404 -6.109277 -v -3.975454 0.471731 -2.162156 -v -3.975454 0.919244 -2.162155 -v -4.618796 0.919244 -5.800034 -v -4.618795 0.471730 -5.800035 -v -4.969088 0.584825 -2.192568 -v -4.969088 0.979775 -2.192567 -v -5.612430 0.979775 -5.830446 -v -5.612429 0.584825 -5.830447 -v -0.864214 -0.673890 3.184381 -v -0.864213 0.489129 3.184384 -v -0.864213 -0.018552 3.184383 -v -0.000825 0.489129 3.184382 -v -0.000825 -0.673890 3.184381 -v -0.850955 -0.557858 3.309075 -v -0.850955 -0.175321 3.309076 -v -1.737321 -0.996758 1.728192 -v -1.737321 -0.785920 1.728193 -vt 0.135351 -0.558072 -vt 0.003035 -0.363507 -vt 0.092282 -0.976844 -vt -0.081322 0.947351 -vt 0.100058 1.958891 -vt 0.050091 1.852185 -vt -0.092752 1.055565 -vt -0.251711 1.059474 -vt 0.075587 0.041384 -vt -0.086008 0.279003 -vt -0.086212 0.249830 -vt -0.276044 1.968137 -vt -0.246101 1.859467 -vt 0.009828 1.911388 -vt -0.133014 1.114769 -vt 0.413322 1.261595 -vt 0.299103 0.624605 -vt 1.243955 0.407183 -vt 0.515404 1.111487 -vt 1.358173 1.044173 -vt -0.081553 0.914324 -vt 0.080042 0.676706 -vt 0.401185 0.474498 -vt 1.295541 0.331328 -vt 0.365315 1.568841 -vt 0.299111 1.575740 -vt 0.143401 0.707357 -vt 0.629403 1.011947 -vt 0.449192 0.167251 -vt 1.409760 0.968317 -vt 0.986264 1.738667 -vt 1.573373 1.877873 -vt 1.417663 1.009490 -vt 0.237182 -0.196235 -vt 0.721785 1.030226 -vt 0.830554 0.870285 -vt 0.877494 1.898608 -vt 1.351399 1.106930 -vt 0.183935 0.557301 -vt 1.507109 1.975312 -vt 0.241636 0.439088 -vt 0.114297 -0.045011 -vt 0.140593 1.808834 -vt -0.015118 0.940452 -vt 0.156405 -1.071134 -vt 0.164119 -0.998223 -vt 0.040336 -1.068281 -vt 0.104459 -1.162571 -vt -0.165787 1.882802 -vt -0.014821 1.660811 -vt -0.287852 0.283965 -vt -0.293374 0.366508 -vt -0.289630 0.900550 -vt 0.035337 -0.191272 -vt 0.247348 0.172213 -vt 0.253300 1.021193 -vt -0.283166 0.952313 -vt -0.283398 0.919286 -vt 0.039792 0.444050 -vt 0.314806 -0.339851 -vt 0.112962 -0.334889 -vt -0.288056 0.254793 -vt -0.023788 -0.973990 -vt -0.155922 -0.359599 -vt 0.220528 -1.165425 -vt 0.108710 -0.748730 -vt -0.286364 1.918670 -vt -0.291973 1.118678 -vt -0.119962 0.896379 -vt -0.123707 0.362337 -vt 0.162891 -0.598569 -vt 0.467532 -0.853353 -vt 0.201549 -1.053262 -vt 0.161663 -0.198915 -vt 0.267667 -0.752638 -vt 0.278705 -0.371021 -vt 0.526390 -0.542053 -vt 0.483821 -0.479457 -vt 0.488162 -0.883689 -vt 0.500110 -0.105561 -vt 0.564618 -0.200418 -vt -0.110331 2.127229 -vt 0.040636 1.905238 -vt -0.010786 1.578087 -vt 0.104092 1.876168 -vt 0.255058 1.654176 -vt -0.054992 2.087323 -vt 0.203048 1.901245 -vt 0.052081 2.123235 -vt 0.042658 1.943733 -vt -0.056437 1.881175 -vt 0.147710 1.941151 -vt 0.050060 2.084741 -vt 0.146264 1.735002 -vt 0.041212 1.737584 -vt 0.048615 1.878591 -vt 0.663065 1.872485 -vt 0.786311 1.691257 -vt 0.507355 1.004102 -vt 0.630601 0.822874 -vt 0.955144 1.689498 -vt 0.860727 1.828333 -vt 0.725565 1.074543 -vt 0.819981 0.935708 -vt 0.674594 1.805657 -vt 0.539432 1.051867 -vt 0.646413 0.894554 -vt 0.781576 1.648344 -vt 0.240127 -0.712141 -vn 0.994400 0.000000 0.105700 -vn 0.000000 1.000000 0.000000 -vn 1.000000 0.000000 0.000000 -vn 0.984700 0.000000 0.174100 -vn 0.211800 0.976600 0.037500 -vn -0.103300 0.000000 -0.994600 -vn 0.103300 -0.000000 0.994600 -vn 0.911400 0.378700 0.161200 -vn -0.157300 -0.987200 -0.027800 -vn 0.113700 -0.993300 0.020100 -vn 0.030600 -0.000000 0.999500 -vn -0.061100 0.998100 -0.010800 -vn -0.030600 0.000000 -0.999500 -vn -0.000000 -0.000000 1.000000 -vn 0.000000 0.000000 -1.000000 -vn -0.755400 0.655300 0.000000 -vn 0.000000 -1.000000 0.000000 -vn -0.000000 -0.180000 0.983700 -vn 0.000000 -0.395500 -0.918500 -vn -0.000000 0.688500 0.725200 -vn 0.000000 -0.585700 -0.810500 -vn -0.000000 0.974900 0.222500 -vn -0.000000 -1.000000 0.002800 -vn -1.000000 0.000000 -0.000000 -vn -0.000000 0.935500 0.353200 -vn 0.755400 0.655300 0.000000 -vn 0.000000 0.935500 -0.353200 -vn 0.673800 0.724900 0.143400 -vn 0.872300 -0.000000 0.489100 -vn -0.872300 0.000000 -0.489100 -vn -0.518300 -0.853500 -0.054200 -vn -0.975500 0.000000 -0.219900 -vn 0.975500 0.000000 -0.219900 -vn -0.913200 0.000000 -0.407500 -vn -0.436900 0.896200 -0.077300 -vn -0.995300 -0.000000 0.096600 -vn -0.297300 -0.953400 -0.052600 -vn 0.473900 -0.876600 0.083800 -vn 0.913200 0.000000 0.407500 -vn 0.342200 0.937700 0.060500 -vn 0.995300 -0.000000 -0.096600 -vn -0.519200 -0.853000 -0.054300 -vn 0.722400 0.676400 0.143800 -vn -0.994400 0.000000 0.105700 -vn -0.984700 0.000000 0.174100 -vn -0.211800 0.976600 0.037500 -vn 0.103300 0.000000 -0.994600 -vn -0.103300 -0.000000 0.994600 -vn -0.911400 0.378700 0.161200 -vn 0.157300 -0.987200 -0.027800 -vn -0.113700 -0.993300 0.020100 -vn -0.030600 -0.000000 0.999500 -vn 0.061100 0.998100 -0.010800 -vn 0.030600 0.000000 -0.999500 -vn -0.691900 0.713200 0.112500 -vn -0.872300 -0.000000 0.489100 -vn 0.872300 0.000000 -0.489100 -vn 0.518300 -0.853500 -0.054200 -vn 0.913200 0.000000 -0.407500 -vn 0.436900 0.896200 -0.077300 -vn 0.995300 0.000000 0.096600 -vn 0.297300 -0.953300 -0.052600 -vn -0.473900 -0.876600 0.083800 -vn -0.913200 -0.000000 0.407500 -vn -0.342200 0.937700 0.060500 -vn -0.995300 -0.000000 -0.096600 -vn 0.519200 -0.853000 -0.054300 -vn -0.714800 0.690100 0.113700 -vn 0.974400 0.089700 0.206200 -vn 0.870400 0.288400 0.399100 -vn 0.691900 0.713200 0.112500 -vn -0.518000 -0.853700 -0.053400 -vn -0.519700 -0.852700 -0.053600 -vn 0.714800 0.690100 0.113700 -vn -0.974400 0.089700 0.206200 -vn -0.870400 0.288400 0.399100 -vn -0.673800 0.724900 0.143400 -vn 0.518000 -0.853700 -0.053400 -vn 0.297300 -0.953400 -0.052600 -vn 0.519700 -0.852700 -0.053600 -vn -0.722400 0.676400 0.143800 -vn -0.000000 0.962300 0.272000 -usemtl Material.001 -s off -f 103/1/1 102/2/1 6/3/1 -f 20/4/2 5/5/2 4/6/2 -f 20/4/2 3/7/2 52/8/2 -f 36/9/3 22/10/3 11/11/3 -f 39/12/2 51/13/2 4/6/2 -f 4/6/4 54/14/4 53/15/4 -f 14/16/5 13/17/5 12/18/5 -f 18/19/6 14/16/6 10/20/6 -f 20/4/3 16/21/3 31/22/3 -f 17/23/7 8/24/7 12/18/7 -f 25/25/4 32/26/4 29/27/4 -f 10/20/4 12/18/4 8/24/4 -f 1/28/8 18/19/8 17/23/8 -f 19/29/4 17/23/4 13/17/4 -f 25/25/4 14/16/4 18/19/4 -f 18/19/9 7/30/9 8/24/9 -f 92/31/10 27/32/10 28/33/10 -f 16/21/3 22/10/3 36/9/3 -f 31/22/3 36/9/3 21/34/3 -f 90/35/11 89/36/11 28/33/11 -f 91/37/12 90/35/12 24/38/12 -f 33/39/4 13/17/4 14/16/4 -f 23/40/4 24/38/4 28/33/4 -f 33/39/3 31/22/3 15/41/3 -f 21/34/3 36/9/3 30/42/3 -f 5/5/4 35/43/4 32/26/4 -f 5/5/4 20/4/4 34/44/4 -f 33/39/4 29/27/4 34/44/4 -f 91/37/13 23/40/13 27/32/13 -f 103/1/1 26/45/1 63/46/1 -f 26/45/14 50/47/14 38/48/14 -f 39/12/15 71/49/15 72/50/15 -f 48/51/16 60/52/16 59/53/16 -f 15/41/17 21/34/17 47/54/17 -f 19/29/17 46/55/17 37/56/17 -f 39/12/2 45/57/2 52/8/2 -f 20/4/2 45/57/2 44/58/2 -f 19/29/18 15/41/18 43/59/18 -f 9/60/19 42/61/19 47/54/19 -f 22/10/20 48/51/20 41/62/20 -f 25/25/21 1/28/21 37/56/21 -f 6/3/14 40/63/14 50/47/14 -f 104/64/22 40/63/22 6/3/22 -f 2/65/23 38/48/23 105/66/23 -f 55/67/2 56/68/2 53/15/2 -f 3/7/14 53/15/14 56/68/14 -f 51/13/15 55/67/15 54/14/15 -f 52/8/24 56/68/24 55/67/24 -f 57/69/2 59/53/2 60/52/2 -f 48/51/25 22/10/25 58/70/25 -f 16/21/26 57/69/26 58/70/26 -f 16/21/27 44/58/27 59/53/27 -f 107/71/28 63/46/28 67/72/28 -f 26/45/1 2/65/1 61/73/1 -f 9/60/1 30/42/1 64/74/1 -f 101/75/1 9/60/1 62/76/1 -f 108/77/1 109/78/1 67/72/1 -f 61/73/29 65/79/29 67/72/29 -f 62/76/30 64/74/30 68/80/30 -f 62/76/31 66/81/31 108/77/31 -f 71/49/32 75/82/32 76/83/32 -f 25/25/15 49/84/15 72/50/15 -f 5/5/15 69/85/15 71/49/15 -f 25/25/15 70/86/15 69/85/15 -f 76/83/15 75/82/15 79/87/15 -f 72/50/17 76/83/17 74/88/17 -f 71/49/2 69/85/2 73/89/2 -f 70/86/33 74/88/33 73/89/33 -f 80/90/3 79/87/3 83/91/3 -f 76/83/15 80/90/15 78/92/15 -f 75/82/15 73/89/15 77/93/15 -f 74/88/15 78/92/15 77/93/15 -f 82/94/15 84/95/15 83/91/15 -f 80/90/2 84/95/2 82/94/2 -f 77/93/17 81/96/17 83/91/17 -f 77/93/24 78/92/24 82/94/24 -f 35/43/13 87/97/13 88/98/13 -f 35/43/12 34/44/12 86/99/12 -f 34/44/11 29/27/11 85/100/11 -f 32/26/10 88/98/10 85/100/10 -f 92/31/34 100/101/34 99/102/34 -f 90/35/35 91/37/35 99/102/35 -f 89/36/36 90/35/36 98/103/36 -f 89/36/37 97/104/37 100/101/37 -f 95/105/13 99/102/13 100/101/13 -f 95/105/12 94/106/12 98/103/12 -f 94/106/11 93/107/11 97/104/11 -f 96/108/10 100/101/10 97/104/10 -f 88/98/38 96/108/38 93/107/38 -f 86/99/39 85/100/39 93/107/39 -f 87/97/40 86/99/40 94/106/40 -f 87/97/41 95/105/41 96/108/41 -f 106/109/42 108/77/42 65/79/42 -f 66/81/1 68/80/1 109/78/1 -f 101/75/1 106/109/1 61/73/1 -f 64/74/43 107/71/43 109/78/43 -f 101/75/23 105/66/23 42/61/23 -f 103/1/1 107/71/1 64/74/1 -f 30/42/1 11/11/1 102/2/1 -f 212/1/44 135/45/44 115/3/44 -f 129/4/2 112/7/2 113/6/2 -f 161/8/2 112/7/2 129/4/2 -f 145/9/24 139/42/24 120/11/24 -f 113/6/2 160/13/2 148/12/2 -f 162/15/45 163/14/45 113/6/45 -f 123/16/46 119/20/46 121/18/46 -f 127/19/47 116/30/47 119/20/47 -f 140/22/24 125/21/24 129/4/24 -f 121/18/48 117/24/48 126/23/48 -f 138/27/45 141/26/45 134/25/45 -f 117/24/45 121/18/45 119/20/45 -f 126/23/49 127/19/49 110/28/49 -f 122/17/45 126/23/45 128/29/45 -f 127/19/45 123/16/45 134/25/45 -f 117/24/50 116/30/50 127/19/50 -f 137/33/51 136/32/51 201/31/51 -f 145/9/24 131/10/24 125/21/24 -f 130/34/24 145/9/24 140/22/24 -f 199/35/52 133/38/52 137/33/52 -f 200/37/53 132/40/53 133/38/53 -f 123/16/45 122/17/45 142/39/45 -f 137/33/45 133/38/45 132/40/45 -f 124/41/24 140/22/24 142/39/24 -f 130/34/24 118/60/24 139/42/24 -f 141/26/45 144/43/45 114/5/45 -f 114/5/45 144/43/45 143/44/45 -f 143/44/45 138/27/45 142/39/45 -f 136/32/54 132/40/54 200/37/54 -f 212/1/44 216/71/44 172/46/44 -f 147/48/14 159/47/14 135/45/14 -f 181/50/15 180/49/15 148/12/15 -f 168/53/26 169/52/26 157/51/26 -f 124/41/17 152/59/17 156/54/17 -f 146/56/17 155/55/17 128/29/17 -f 148/12/2 160/13/2 161/8/2 -f 129/4/2 125/21/2 153/58/2 -f 155/55/18 152/59/18 124/41/18 -f 130/34/19 156/54/19 151/61/19 -f 131/10/20 120/11/20 150/62/20 -f 134/25/21 158/84/21 146/56/21 -f 159/47/14 149/63/14 115/3/14 -f 115/3/22 149/63/22 213/64/22 -f 214/66/23 147/48/23 111/65/23 -f 162/15/2 165/68/2 164/67/2 -f 165/68/14 162/15/14 112/7/14 -f 163/14/15 164/67/15 160/13/15 -f 164/67/3 165/68/3 161/8/3 -f 166/69/2 167/70/2 169/52/2 -f 157/51/25 169/52/25 167/70/25 -f 167/70/16 166/69/16 125/21/16 -f 125/21/27 166/69/27 168/53/27 -f 216/71/55 218/78/55 176/72/55 -f 135/45/44 172/46/44 170/73/44 -f 118/60/44 171/76/44 173/74/44 -f 210/75/44 215/109/44 171/76/44 -f 217/77/44 174/79/44 176/72/44 -f 176/72/56 174/79/56 170/73/56 -f 171/76/57 175/81/57 177/80/57 -f 217/77/58 175/81/58 171/76/58 -f 185/83/33 184/82/33 180/49/33 -f 134/25/15 179/86/15 181/50/15 -f 180/49/15 178/85/15 114/5/15 -f 178/85/15 179/86/15 134/25/15 -f 189/90/15 188/87/15 184/82/15 -f 183/88/17 185/83/17 181/50/17 -f 180/49/2 184/82/2 182/89/2 -f 182/89/32 183/88/32 179/86/32 -f 189/90/24 193/95/24 192/91/24 -f 187/92/15 189/90/15 185/83/15 -f 184/82/15 188/87/15 186/93/15 -f 186/93/15 187/92/15 183/88/15 -f 192/91/15 193/95/15 191/94/15 -f 191/94/2 193/95/2 189/90/2 -f 192/91/17 190/96/17 186/93/17 -f 186/93/3 190/96/3 191/94/3 -f 197/98/54 196/97/54 144/43/54 -f 144/43/53 196/97/53 195/99/53 -f 143/44/52 195/99/52 194/100/52 -f 194/100/51 197/98/51 141/26/51 -f 208/102/59 209/101/59 201/31/59 -f 199/35/60 207/103/60 208/102/60 -f 198/36/61 206/104/61 207/103/61 -f 209/101/62 206/104/62 198/36/62 -f 209/101/54 208/102/54 204/105/54 -f 204/105/53 208/102/53 207/103/53 -f 203/106/52 207/103/52 206/104/52 -f 206/104/51 209/101/51 205/108/51 -f 202/107/63 205/108/63 197/98/63 -f 195/99/64 203/106/64 202/107/64 -f 196/97/65 204/105/65 203/106/65 -f 205/108/66 204/105/66 196/97/66 -f 174/79/67 217/77/67 215/109/67 -f 175/81/44 217/77/44 218/78/44 -f 170/73/44 215/109/44 210/75/44 -f 173/74/68 177/80/68 218/78/68 -f 151/61/23 214/66/23 210/75/23 -f 173/74/44 216/71/44 212/1/44 -f 139/42/44 212/1/44 211/2/44 -f 26/45/1 103/1/1 6/3/1 -f 3/7/2 20/4/2 4/6/2 -f 45/57/2 20/4/2 52/8/2 -f 30/42/3 36/9/3 11/11/3 -f 5/5/2 39/12/2 4/6/2 -f 3/7/4 4/6/4 53/15/4 -f 10/20/5 14/16/5 12/18/5 -f 7/30/6 18/19/6 10/20/6 -f 33/39/3 20/4/3 31/22/3 -f 13/17/7 17/23/7 12/18/7 -f 33/39/4 25/25/4 29/27/4 -f 7/30/4 10/20/4 8/24/4 -f 19/29/69 1/28/69 17/23/69 -f 33/39/4 19/29/4 13/17/4 -f 1/28/70 25/25/70 18/19/70 -f 17/23/9 18/19/9 8/24/9 -f 89/36/10 92/31/10 28/33/10 -f 31/22/3 16/21/3 36/9/3 -f 15/41/3 31/22/3 21/34/3 -f 24/38/11 90/35/11 28/33/11 -f 23/40/12 91/37/12 24/38/12 -f 25/25/4 33/39/4 14/16/4 -f 27/32/4 23/40/4 28/33/4 -f 19/29/3 33/39/3 15/41/3 -f 9/60/3 21/34/3 30/42/3 -f 25/25/4 5/5/4 32/26/4 -f 35/43/4 5/5/4 34/44/4 -f 20/4/4 33/39/4 34/44/4 -f 92/31/13 91/37/13 27/32/13 -f 107/71/1 103/1/1 63/46/1 -f 2/65/14 26/45/14 38/48/14 -f 49/84/15 39/12/15 72/50/15 -f 44/58/16 48/51/16 59/53/16 -f 43/59/17 15/41/17 47/54/17 -f 1/28/17 19/29/17 37/56/17 -f 51/13/2 39/12/2 52/8/2 -f 16/21/2 20/4/2 44/58/2 -f 46/55/18 19/29/18 43/59/18 -f 21/34/19 9/60/19 47/54/19 -f 11/11/20 22/10/20 41/62/20 -f 49/84/21 25/25/21 37/56/21 -f 26/45/14 6/3/14 50/47/14 -f 102/2/22 104/64/22 6/3/22 -f 101/75/23 2/65/23 105/66/23 -f 54/14/2 55/67/2 53/15/2 -f 52/8/14 3/7/14 56/68/14 -f 4/6/15 51/13/15 54/14/15 -f 51/13/24 52/8/24 55/67/24 -f 58/70/2 57/69/2 60/52/2 -f 60/52/25 48/51/25 58/70/25 -f 22/10/26 16/21/26 58/70/26 -f 57/69/27 16/21/27 59/53/27 -f 109/78/71 107/71/71 67/72/71 -f 63/46/1 26/45/1 61/73/1 -f 62/76/1 9/60/1 64/74/1 -f 106/109/1 101/75/1 62/76/1 -f 65/79/1 108/77/1 67/72/1 -f 63/46/29 61/73/29 67/72/29 -f 66/81/30 62/76/30 68/80/30 -f 106/109/72 62/76/72 108/77/72 -f 72/50/32 71/49/32 76/83/32 -f 70/86/15 25/25/15 72/50/15 -f 39/12/15 5/5/15 71/49/15 -f 5/5/15 25/25/15 69/85/15 -f 80/90/15 76/83/15 79/87/15 -f 70/86/17 72/50/17 74/88/17 -f 75/82/2 71/49/2 73/89/2 -f 69/85/33 70/86/33 73/89/33 -f 84/95/3 80/90/3 83/91/3 -f 74/88/15 76/83/15 78/92/15 -f 79/87/15 75/82/15 77/93/15 -f 73/89/15 74/88/15 77/93/15 -f 81/96/15 82/94/15 83/91/15 -f 78/92/2 80/90/2 82/94/2 -f 79/87/17 77/93/17 83/91/17 -f 81/96/24 77/93/24 82/94/24 -f 32/26/13 35/43/13 88/98/13 -f 87/97/12 35/43/12 86/99/12 -f 86/99/11 34/44/11 85/100/11 -f 29/27/10 32/26/10 85/100/10 -f 91/37/34 92/31/34 99/102/34 -f 98/103/35 90/35/35 99/102/35 -f 97/104/36 89/36/36 98/103/36 -f 92/31/37 89/36/37 100/101/37 -f 96/108/13 95/105/13 100/101/13 -f 99/102/12 95/105/12 98/103/12 -f 98/103/11 94/106/11 97/104/11 -f 93/107/10 96/108/10 97/104/10 -f 85/100/38 88/98/38 93/107/38 -f 94/106/39 86/99/39 93/107/39 -f 95/105/40 87/97/40 94/106/40 -f 88/98/41 87/97/41 96/108/41 -f 61/73/73 106/109/73 65/79/73 -f 108/77/1 66/81/1 109/78/1 -f 2/65/1 101/75/1 61/73/1 -f 68/80/74 64/74/74 109/78/74 -f 9/60/23 101/75/23 42/61/23 -f 30/42/1 103/1/1 64/74/1 -f 103/1/1 30/42/1 102/2/1 -f 211/2/44 212/1/44 115/3/44 -f 114/5/2 129/4/2 113/6/2 -f 154/57/2 161/8/2 129/4/2 -f 131/10/24 145/9/24 120/11/24 -f 114/5/2 113/6/2 148/12/2 -f 112/7/45 162/15/45 113/6/45 -f 122/17/46 123/16/46 121/18/46 -f 123/16/47 127/19/47 119/20/47 -f 142/39/24 140/22/24 129/4/24 -f 122/17/48 121/18/48 126/23/48 -f 142/39/45 138/27/45 134/25/45 -f 116/30/45 117/24/45 119/20/45 -f 128/29/75 126/23/75 110/28/75 -f 142/39/45 122/17/45 128/29/45 -f 110/28/76 127/19/76 134/25/76 -f 126/23/50 117/24/50 127/19/50 -f 198/36/51 137/33/51 201/31/51 -f 140/22/24 145/9/24 125/21/24 -f 124/41/24 130/34/24 140/22/24 -f 198/36/52 199/35/52 137/33/52 -f 199/35/53 200/37/53 133/38/53 -f 134/25/45 123/16/45 142/39/45 -f 136/32/45 137/33/45 132/40/45 -f 128/29/24 124/41/24 142/39/24 -f 145/9/24 130/34/24 139/42/24 -f 134/25/45 141/26/45 114/5/45 -f 129/4/45 114/5/45 143/44/45 -f 129/4/45 143/44/45 142/39/45 -f 201/31/54 136/32/54 200/37/54 -f 135/45/44 212/1/44 172/46/44 -f 111/65/14 147/48/14 135/45/14 -f 158/84/15 181/50/15 148/12/15 -f 153/58/26 168/53/26 157/51/26 -f 130/34/17 124/41/17 156/54/17 -f 110/28/17 146/56/17 128/29/17 -f 154/57/2 148/12/2 161/8/2 -f 154/57/2 129/4/2 153/58/2 -f 128/29/18 155/55/18 124/41/18 -f 118/60/19 130/34/19 151/61/19 -f 157/51/20 131/10/20 150/62/20 -f 110/28/21 134/25/21 146/56/21 -f 135/45/14 159/47/14 115/3/14 -f 211/2/22 115/3/22 213/64/22 -f 210/75/23 214/66/23 111/65/23 -f 163/14/2 162/15/2 164/67/2 -f 161/8/14 165/68/14 112/7/14 -f 113/6/15 163/14/15 160/13/15 -f 160/13/3 164/67/3 161/8/3 -f 168/53/2 166/69/2 169/52/2 -f 131/10/25 157/51/25 167/70/25 -f 131/10/16 167/70/16 125/21/16 -f 153/58/27 125/21/27 168/53/27 -f 172/46/77 216/71/77 176/72/77 -f 111/65/44 135/45/44 170/73/44 -f 139/42/44 118/60/44 173/74/44 -f 118/60/44 210/75/44 171/76/44 -f 218/78/44 217/77/44 176/72/44 -f 172/46/56 176/72/56 170/73/56 -f 173/74/57 171/76/57 177/80/57 -f 215/109/78 217/77/78 171/76/78 -f 181/50/33 185/83/33 180/49/33 -f 158/84/15 134/25/15 181/50/15 -f 148/12/15 180/49/15 114/5/15 -f 114/5/15 178/85/15 134/25/15 -f 185/83/15 189/90/15 184/82/15 -f 179/86/17 183/88/17 181/50/17 -f 178/85/2 180/49/2 182/89/2 -f 178/85/32 182/89/32 179/86/32 -f 188/87/24 189/90/24 192/91/24 -f 183/88/15 187/92/15 185/83/15 -f 182/89/15 184/82/15 186/93/15 -f 182/89/15 186/93/15 183/88/15 -f 190/96/15 192/91/15 191/94/15 -f 187/92/2 191/94/2 189/90/2 -f 188/87/17 192/91/17 186/93/17 -f 187/92/3 186/93/3 191/94/3 -f 141/26/54 197/98/54 144/43/54 -f 143/44/53 144/43/53 195/99/53 -f 138/27/52 143/44/52 194/100/52 -f 138/27/51 194/100/51 141/26/51 -f 200/37/59 208/102/59 201/31/59 -f 200/37/60 199/35/60 208/102/60 -f 199/35/61 198/36/61 207/103/61 -f 201/31/79 209/101/79 198/36/79 -f 205/108/54 209/101/54 204/105/54 -f 203/106/53 204/105/53 207/103/53 -f 202/107/52 203/106/52 206/104/52 -f 202/107/51 206/104/51 205/108/51 -f 194/100/63 202/107/63 197/98/63 -f 194/100/64 195/99/64 202/107/64 -f 195/99/65 196/97/65 203/106/65 -f 197/98/66 205/108/66 196/97/66 -f 170/73/80 174/79/80 215/109/80 -f 177/80/44 175/81/44 218/78/44 -f 111/65/44 170/73/44 210/75/44 -f 216/71/81 173/74/81 218/78/81 -f 118/60/23 151/61/23 210/75/23 -f 139/42/44 173/74/44 212/1/44 -f 120/11/44 139/42/44 211/2/44 -usemtl Material.003 -f 41/62/82 104/64/82 102/2/82 -f 211/2/82 213/64/82 150/62/82 -f 11/11/82 41/62/82 102/2/82 -f 120/11/82 211/2/82 150/62/82 diff --git a/pbr-mtl.md b/pbr-mtl.md deleted file mode 100644 index b5856216..00000000 --- a/pbr-mtl.md +++ /dev/null @@ -1,29 +0,0 @@ -## PBR material extension. - -The spec can be found in either - -https://benhouston3d.com/blog/extended-wavefront-obj-mtl-for-pbr/ - -or Internet Archive: https://web.archive.org/web/20230210121526/http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr - -* Kd/map_Kd (base/diffuse) // reuse -* Ks/map_Ks (specular) // reuse -* d or Tr (opacity) // reuse -* map_d/map_Tr (opacitymap) // reuse -* Tf (translucency) // reuse -* bump/-bm (bump map) // reuse -* disp (displacement map) // reuse - -PBR material parameters as defined by the Disney PBR. - -* Pr/map_Pr (roughness) // new -* Pm/map_Pm (metallic) // new -* Ps/map_Ps (sheen) // new -* Pc (clearcoat thickness) // new -* Pcr (clearcoat roughness) // new -* Ke/map_Ke (emissive) // new -* aniso (anisotropy) // new -* anisor (anisotropy rotation) // new -* norm (normal map) // new - -EoL. diff --git a/premake4.lua b/premake4.lua deleted file mode 100644 index cd5b2952..00000000 --- a/premake4.lua +++ /dev/null @@ -1,29 +0,0 @@ -sources = { - "loader_example.cc", - } - --- premake4.lua -solution "TinyObjLoaderSolution" - configurations { "Release", "Debug" } - - if (os.is("windows")) then - platforms { "x32", "x64" } - else - platforms { "native", "x32", "x64" } - end - - -- A project defines one build target - project "tinyobjloader" - kind "ConsoleApp" - language "C++" - files { sources } - - configuration "Debug" - defines { "DEBUG" } -- -DDEBUG - flags { "Symbols" } - targetname "loader_example_debug" - - configuration "Release" - -- defines { "NDEBUG" } -- -NDEBUG - flags { "Symbols", "Optimize" } - targetname "loader_example" diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index d3ba7cf3..00000000 --- a/pyproject.toml +++ /dev/null @@ -1,39 +0,0 @@ -[build-system] - -requires = [ - # NOTE: setuptools_scm>=8 is not supported in py3.6 cibuildwheel env. - # so use older setuptools_scm for a while - #"setuptools>=64", - #"setuptools_scm>=8", - "setuptools>=45", - "setuptools_scm[toml]<8", - "wheel", - "pybind11>=2.10.0", -] -build-backend = "setuptools.build_meta" - -[tool.black] -line-length = 140 - -[project] -name = "tinyobjloader" - -# version: Use setuptools_scm -dynamic = ["version", "classifiers", "authors", "description"] - - -readme = {file = "README.md", content-type = "text/markdown"} - -# Project URLs in pyproject.toml is not mature. -# so write it to setup.py -# https://github.com/pypa/packaging-problems/issues/606 -# -# [project.urils] - - -[tool.setuptools_scm] -# setuptools_scm>=8 -#version_file = "python/_version.py" - -# setuptools_scm<8 -write_to = "python/_version.py" diff --git a/python/LICENSE b/python/LICENSE deleted file mode 100644 index 3af18aba..00000000 --- a/python/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2012-2019 Syoyo Fujita and many contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/python/Makefile b/python/Makefile deleted file mode 100644 index ede9c2d2..00000000 --- a/python/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - cd .. && python -m pip install . - -t: - python sample.py - -.PHONY: t diff --git a/python/README.md b/python/README.md deleted file mode 100644 index 8f9aa5d2..00000000 --- a/python/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# tinyobjloader, Wavefront .obj loader - -`tinyobjloader` is a python wrapper for C++ wavefront .obj loader. -`tinyobjloader` is rather fast and feature rich than other pure python version of .obj loader. - -## Requirements - -* python 3.6+ - -## Install - -You can install `tinyobjloader` with pip. - -``` -$ pip install tinyobjloader -``` - -## Quick tutorial - -```py -import sys -import tinyobjloader - -# Create reader. -reader = tinyobjloader.ObjReader() - -filename = "cornellbox.obj" - -# Load .obj(and .mtl) using default configuration -ret = reader.ParseFromFile(filename) - -if ret == False: - print("Warn:", reader.Warning()) - pint("Err:", reader.Error()) - print("Failed to load : ", filename) - - sys.exit(-1) - -if reader.Warning(): - print("Warn:", reader.Warning()) - -attrib = reader.GetAttrib() -print("attrib.vertices = ", len(attrib.vertices)) -print("attrib.normals = ", len(attrib.normals)) -print("attrib.texcoords = ", len(attrib.texcoords)) - -materials = reader.GetMaterials() -print("Num materials: ", len(materials)) -for m in materials: - print(m.name) - print(m.diffuse) - -shapes = reader.GetShapes() -print("Num shapes: ", len(shapes)) -for shape in shapes: - print(shape.name) - print("num_indices = {}".format(len(shape.mesh.indices))) - -``` - -## More detailed usage - -Please take a look at `python/sample.py` file in tinyobjloader git repo. - -https://github.com/syoyo/tinyobjloader/blob/master/python/sample.py - -## How to build - -Using `cibuildwheel` is a recommended way to build a python module. -See $tinyobjloader/azure-pipelines.yml for details. - -### Developer build - -Assume pip is installed. - -``` -$ git clone https://github.com/tinyobjloader/tinyobjloader -$ cd tinyobjloader -$ python -m pip install . -``` - -## License - -MIT(tinyobjloader) and ISC(mapbox earcut) license. - -## TODO - * [ ] Writer saver diff --git a/python/bindings.cc b/python/bindings.cc deleted file mode 100644 index e7e6c951..00000000 --- a/python/bindings.cc +++ /dev/null @@ -1,248 +0,0 @@ -#include -#include -#include -#include - -// Use double precision for better python integration. -#define TINYOBJLOADER_USE_DOUBLE - -// define some helper functions for pybind11 -#define TINY_OBJ_LOADER_PYTHON_BINDING -#include "../tiny_obj_loader.h" - -namespace py = pybind11; - -using namespace tinyobj; - -PYBIND11_MODULE(tinyobjloader, tobj_module) -{ - tobj_module.doc() = "Python bindings for TinyObjLoader."; - - // register struct - py::class_(tobj_module, "ObjReaderConfig") - .def(py::init<>()) - .def_readwrite("triangulate", &ObjReaderConfig::triangulate); - - // py::init<>() for default constructor - py::class_(tobj_module, "ObjReader") - .def(py::init<>()) - .def("ParseFromFile", &ObjReader::ParseFromFile, py::arg("filename"), py::arg("option") = ObjReaderConfig()) - .def("ParseFromString", &ObjReader::ParseFromString, py::arg("obj_text"), py::arg("mtl_text"), py::arg("option") = ObjReaderConfig()) - .def("Valid", &ObjReader::Valid) - .def("GetAttrib", &ObjReader::GetAttrib) - .def("GetShapes", &ObjReader::GetShapes) - .def("GetMaterials", &ObjReader::GetMaterials) - .def("Warning", &ObjReader::Warning) - .def("Error", &ObjReader::Error); - - py::class_(tobj_module, "attrib_t") - .def(py::init<>()) - .def_readonly("vertices", &attrib_t::vertices) - .def_readonly("vertex_weights", &attrib_t::vertex_weights) - .def_readonly("skin_weights", &attrib_t::skin_weights) - .def_readonly("normals", &attrib_t::normals) - .def_readonly("texcoords", &attrib_t::texcoords) - .def_readonly("colors", &attrib_t::colors) - .def("numpy_vertices", [] (attrib_t &instance) { - auto ret = py::array_t(instance.vertices.size()); - py::buffer_info buf = ret.request(); - memcpy(buf.ptr, instance.vertices.data(), instance.vertices.size() * sizeof(real_t)); - return ret; - }) - .def("numpy_vertex_weights", [] (attrib_t &instance) { - auto ret = py::array_t(instance.vertex_weights.size()); - py::buffer_info buf = ret.request(); - memcpy(buf.ptr, instance.vertex_weights.data(), instance.vertex_weights.size() * sizeof(real_t)); - return ret; - }) - .def("numpy_normals", [] (attrib_t &instance) { - auto ret = py::array_t(instance.normals.size()); - py::buffer_info buf = ret.request(); - memcpy(buf.ptr, instance.normals.data(), instance.normals.size() * sizeof(real_t)); - return ret; - }) - .def("numpy_texcoords", [] (attrib_t &instance) { - auto ret = py::array_t(instance.texcoords.size()); - py::buffer_info buf = ret.request(); - memcpy(buf.ptr, instance.texcoords.data(), instance.texcoords.size() * sizeof(real_t)); - return ret; - }) - .def("numpy_colors", [] (attrib_t &instance) { - auto ret = py::array_t(instance.colors.size()); - py::buffer_info buf = ret.request(); - memcpy(buf.ptr, instance.colors.data(), instance.colors.size() * sizeof(real_t)); - return ret; - }) - ; - - py::class_(tobj_module, "shape_t") - .def(py::init<>()) - .def_readwrite("name", &shape_t::name) - .def_readwrite("mesh", &shape_t::mesh) - .def_readwrite("lines", &shape_t::lines) - .def_readwrite("points", &shape_t::points); - - py::class_(tobj_module, "index_t") - .def(py::init<>()) - .def_readwrite("vertex_index", &index_t::vertex_index) - .def_readwrite("normal_index", &index_t::normal_index) - .def_readwrite("texcoord_index", &index_t::texcoord_index) - ; - - // NOTE(syoyo): It looks it is rather difficult to expose assignment by array index to - // python world for array variable. - // For example following python scripting does not work well. - // - // print(mat.diffuse) - // >>> [0.1, 0.2, 0.3] - // mat.diffuse[1] = 1.0 - // print(mat.diffuse) - // >>> [0.1, 0.2, 0.3] # No modification - // - // https://github.com/pybind/pybind11/issues/1134 - // - // so, we need to update array variable like this: - // - // diffuse = mat.diffuse - // diffuse[1] = 1.0 - // mat.diffuse = diffuse - // - py::class_(tobj_module, "material_t") - .def(py::init<>()) - .def_readwrite("name", &material_t::name) - .def_property("ambient", &material_t::GetAmbient, &material_t::SetAmbient) - .def_property("diffuse", &material_t::GetDiffuse, &material_t::SetDiffuse) - .def_property("specular", &material_t::GetSpecular, &material_t::SetSpecular) - .def_property("transmittance", &material_t::GetTransmittance, &material_t::SetTransmittance) - .def_readwrite("shininess", &material_t::shininess) - .def_readwrite("ior", &material_t::ior) - .def_readwrite("dissolve", &material_t::dissolve) - .def_readwrite("illum", &material_t::illum) - .def_readwrite("ambient_texname", &material_t::ambient_texname) - .def_readwrite("diffuse_texname", &material_t::diffuse_texname) - .def_readwrite("specular_texname", &material_t::specular_texname) - .def_readwrite("specular_highlight_texname", &material_t::specular_highlight_texname) - .def_readwrite("bump_texname", &material_t::bump_texname) - .def_readwrite("displacement_texname", &material_t::displacement_texname) - .def_readwrite("alpha_texname", &material_t::alpha_texname) - .def_readwrite("reflection_texname", &material_t::reflection_texname) - // TODO(syoyo): Expose texture parameter - // PBR - .def_readwrite("roughness", &material_t::roughness) - .def_readwrite("metallic", &material_t::metallic) - .def_readwrite("sheen", &material_t::sheen) - .def_readwrite("clearcoat_thickness", &material_t::clearcoat_thickness) - .def_readwrite("clearcoat_roughness", &material_t::clearcoat_roughness) - .def_readwrite("anisotropy", &material_t::anisotropy) - .def_readwrite("anisotropy_rotation", &material_t::anisotropy_rotation) - - .def_readwrite("roughness_texname", &material_t::roughness_texname) - .def_readwrite("metallic_texname", &material_t::metallic_texname) - .def_readwrite("sheen_texname", &material_t::sheen_texname) - .def_readwrite("emissive_texname", &material_t::emissive_texname) - .def_readwrite("normal_texname", &material_t::normal_texname) - - .def("GetCustomParameter", &material_t::GetCustomParameter) - ; - - py::class_(tobj_module, "mesh_t", py::buffer_protocol()) - .def(py::init<>()) - .def_readonly("num_face_vertices", &mesh_t::num_face_vertices) - .def("numpy_num_face_vertices", [] (mesh_t &instance) { - auto ret = py::array_t(instance.num_face_vertices.size()); - py::buffer_info buf = ret.request(); - memcpy(buf.ptr, instance.num_face_vertices.data(), instance.num_face_vertices.size() * sizeof(unsigned char)); - return ret; - }) - .def("vertex_indices", [](mesh_t &self) { - // NOTE: we cannot use py::buffer_info and py:buffer as a return type. - // py::memoriview is not suited for vertex indices usecase, since indices data may be used after - // deleting C++ mesh_t object in Python world. - // - // So create a dedicated Python object(std::vector) - - std::vector indices; - indices.resize(self.indices.size()); - for (size_t i = 0; i < self.indices.size(); i++) { - indices[i] = self.indices[i].vertex_index; - } - - return indices; - }) - .def("normal_indices", [](mesh_t &self) { - - std::vector indices; - indices.resize(self.indices.size()); - for (size_t i = 0; i < self.indices.size(); i++) { - indices[i] = self.indices[i].normal_index; - } - - return indices; - }) - .def("texcoord_indices", [](mesh_t &self) { - - std::vector indices; - indices.resize(self.indices.size()); - for (size_t i = 0; i < self.indices.size(); i++) { - indices[i] = self.indices[i].texcoord_index; - } - - return indices; - }) - .def_readonly("indices", &mesh_t::indices) - .def("numpy_indices", [] (mesh_t &instance) { - // Flatten indexes. index_t is composed of 3 ints(vertex_index, normal_index, texcoord_index). - // numpy_indices = [0, -1, -1, 1, -1, -1, ...] - // C++11 or later should pack POD struct tightly and does not reorder variables, - // so we can memcpy to copy data. - // Still, we check the size of struct and byte offsets of each variable just for sure. - static_assert(sizeof(index_t) == 12, "sizeof(index_t) must be 12"); - static_assert(offsetof(index_t, vertex_index) == 0, "offsetof(index_t, vertex_index) must be 0"); - static_assert(offsetof(index_t, normal_index) == 4, "offsetof(index_t, normal_index) must be 4"); - static_assert(offsetof(index_t, texcoord_index) == 8, "offsetof(index_t, texcoord_index) must be 8"); - - auto ret = py::array_t(instance.indices.size() * 3); - py::buffer_info buf = ret.request(); - memcpy(buf.ptr, instance.indices.data(), instance.indices.size() * 3 * sizeof(int)); - return ret; - }) - .def_readonly("material_ids", &mesh_t::material_ids) - .def("numpy_material_ids", [] (mesh_t &instance) { - auto ret = py::array_t(instance.material_ids.size()); - py::buffer_info buf = ret.request(); - memcpy(buf.ptr, instance.material_ids.data(), instance.material_ids.size() * sizeof(int)); - return ret; - }); - - py::class_(tobj_module, "lines_t") - .def(py::init<>()) - .def_readonly("indices", &lines_t::indices) - .def_readonly("num_line_vertices", &lines_t::num_line_vertices) - ; - - py::class_(tobj_module, "points_t") - .def(py::init<>()) - .def_readonly("indices", &points_t::indices) - ; - - py::class_(tobj_module, "joint_and_weight_t") - .def(py::init<>()) - .def_readonly("joint_id", &joint_and_weight_t::joint_id, "Joint index(NOTE: Joint info is provided externally, not from .obj") - .def_readonly("weight", &joint_and_weight_t::weight, "Weight value(NOTE: weight is not normalized)") - ; - - py::class_(tobj_module, "skin_weight_t") - .def(py::init<>()) - .def_readonly("vertex_id", &skin_weight_t::vertex_id) - .def_readonly("weightValues", &skin_weight_t::weightValues) - ; - - py::class_(tobj_module, "tag_t") - .def(py::init<>()) - .def_readonly("name", &tag_t::name) - .def_readonly("intValues", &tag_t::intValues) - .def_readonly("floatValues", &tag_t::floatValues) - .def_readonly("stringValues", &tag_t::stringValues) - ; -} - diff --git a/python/sample.py b/python/sample.py deleted file mode 100644 index 45c97612..00000000 --- a/python/sample.py +++ /dev/null @@ -1,138 +0,0 @@ -import sys -import tinyobjloader - -is_numpy_available = False -try: - import numpy - - is_numpy_available = True -except: - print( - "NumPy not installed. Do not use numpy_*** API. If you encounter slow performance, see a performance tips for non-numpy API https://github.com/tinyobjloader/tinyobjloader/issues/275" - ) - -filename = "../models/cornell_box.obj" - -if len(sys.argv) > 1: - filename = sys.argv[1] - - -reader = tinyobjloader.ObjReader() - -# Load .obj(and .mtl) using default configuration -ret = reader.ParseFromFile(filename) - -# Optionally you can set custom `config` -# config = tinyobj.ObjReaderConfig() -# config.triangulate = False -# ret = reader.ParseFromFile(filename, config) - -if ret == False: - print("Failed to load : ", filename) - print("Warn:", reader.Warning()) - print("Err:", reader.Error()) - sys.exit(-1) - -if reader.Warning(): - print("Warn:", reader.Warning()) - -attrib = reader.GetAttrib() -print("len(attrib.vertices) = ", len(attrib.vertices)) -print("len(attrib.vertex_weights) = ", len(attrib.vertex_weights)) -print("len(attrib.normals) = ", len(attrib.normals)) -print("len(attrib.texcoords) = ", len(attrib.texcoords)) -print("len(attrib.colors) = ", len(attrib.colors)) -print("len(attrib.skin_weights) = ", len(attrib.skin_weights)) - -# vertex data must be `xyzxyzxyz...` -assert len(attrib.vertices) % 3 == 0 - -# normal data must be `xyzxyzxyz...` -assert len(attrib.normals) % 3 == 0 - -# texcoords data must be `uvuvuv...` -assert len(attrib.texcoords) % 2 == 0 - -# colors data must be `rgbrgbrgb...` -assert len(attrib.texcoords) % 3 == 0 - -# Performance note -# (direct?) array access through member variable is quite slow. -# https://github.com/tinyobjloader/tinyobjloader/issues/275#issuecomment-753465833 -# -# We encourage first copy(?) varible to Python world: -# -# vertices = attrib.vertices -# -# for i in range(...) -# v = vertices[i] -# -# Or please consider using numpy_*** interface(e.g. numpy_vertices()) - -for i, v in enumerate(attrib.vertices): - print("v[{}] = {}".format(i, v)) - -# vw is filled with 1.0 if [w] component is not present in `v` line in .obj -for i, w in enumerate(attrib.vertex_weights): - print("vweight[{}] = {}".format(i, w)) - -for i, v in enumerate(attrib.normals): - print("vn[{}] = {}".format(i, v)) - -for i, v in enumerate(attrib.texcoords): - print("vt[{}] = {}".format(i, v)) - -for i, v in enumerate(attrib.colors): - print("vcol[{}] = {}".format(i, v)) - -if len(attrib.skin_weights): - print("num skin weights", len(attrib.skin_weights)) - - for i, skin in enumerate(attrib.skin_weights): - print("skin_weight[{}]".format(i)) - print(" vertex_id = ", skin.vertex_id) - print(" len(weights) = ", len(skin.weightValues)) - for k, w in enumerate(skin.weightValues): - print(" [{}] joint_id: {}, weight: {}".format(k, w.joint_id, w.weight)) - -if is_numpy_available: - print("numpy_v = {}".format(attrib.numpy_vertices())) - print("numpy_vn = {}".format(attrib.numpy_normals())) - print("numpy_vt = {}".format(attrib.numpy_texcoords())) - print("numpy_vcol = {}".format(attrib.numpy_colors())) - -materials = reader.GetMaterials() -print("Num materials: ", len(materials)) -for m in materials: - print(m.name) - print(m.diffuse) - print(m.diffuse_texname) - # Partial update(array indexing) does not work - # m.diffuse[1] = 1.0 - - # Update with full object assignment works - m.diffuse = [1, 2, 3] - print(m.diffuse) - - # print(m.shininess) - # print(m.illum) - -shapes = reader.GetShapes() -print("Num shapes: ", len(shapes)) -for shape in shapes: - print(shape.name) - print("len(num_indices) = {}".format(len(shape.mesh.indices))) - for i, idx in enumerate(shape.mesh.indices): - print("[{}] v_idx {}".format(i, idx.vertex_index)) - print("[{}] vn_idx {}".format(i, idx.normal_index)) - print("[{}] vt_idx {}".format(i, idx.texcoord_index)) - print("material_ids = {}".format(shape.mesh.material_ids)) - - # faster access to indices - a = shape.mesh.vertex_indices() - print("vertex_indices", shape.mesh.vertex_indices()) - - if is_numpy_available: - print("numpy_indices = {}".format(shape.mesh.numpy_indices())) - print("numpy_num_face_vertices = {}".format(shape.mesh.numpy_num_face_vertices())) - print("numpy_material_ids = {}".format(shape.mesh.numpy_material_ids())) diff --git a/python/tiny_obj_loader.cc b/python/tiny_obj_loader.cc deleted file mode 100644 index 821542e7..00000000 --- a/python/tiny_obj_loader.cc +++ /dev/null @@ -1,9 +0,0 @@ -// Use double precision for better python integration. -// Need also define this in `binding.cc`(and all compilation units) -#define TINYOBJLOADER_USE_DOUBLE - -// Use robust triangulation by using Mapbox earcut. -#define TINYOBJLOADER_USE_MAPBOX_EARCUT - -#define TINYOBJLOADER_IMPLEMENTATION -#include "../tiny_obj_loader.h" diff --git a/setup.py b/setup.py deleted file mode 100644 index cb950871..00000000 --- a/setup.py +++ /dev/null @@ -1,69 +0,0 @@ -# Adapted from https://github.com/pybind/python_example/blob/master/setup.py -import sys - -#from pybind11 import get_cmake_dir -# Available at setup time due to pyproject.toml -from pybind11.setup_helpers import Pybind11Extension#, build_ext -from setuptools import setup - -try: - # try to read setuptools_scm generated _version.py - from .python import _version -except: - __version__ = "2.0.0rc10" - -with open("README.md", "r", encoding="utf8") as fh: - long_description = fh.read() - -# The main interface is through Pybind11Extension. -# * You can add cxx_std=11/14/17, and then build_ext can be removed. -# * You can set include_pybind11=false to add the include directory yourself, -# say from a submodule. -# -# Note: -# Sort input source files if you glob sources to ensure bit-for-bit -# reproducible builds (https://github.com/pybind/python_example/pull/53) - -ext_modules = [ - Pybind11Extension("tinyobjloader", - sorted(["python/bindings.cc", "python/tiny_obj_loader.cc"]), - # Example: passing in the version to the compiled code - define_macros = [('VERSION_INFO', __version__)], - cxx_std=11, - ), -] - -setup( - name="tinyobjloader", - packages=['python'], - #version=__version__, - author="Syoyo Fujita", - author_email="syoyo@lighttransport.com", - url="https://github.com/tinyobjloader/tinyobjloader", - #project_urls={ - # "Issue Tracker": "https://github.com/tinyobjloader/tinyobjloader/issues", - #}, - description="Tiny but powerful Wavefront OBJ loader", - long_description=long_description, - long_description_content_type='text/markdown', - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - "Intended Audience :: Manufacturing", - "Topic :: Artistic Software", - "Topic :: Multimedia :: Graphics :: 3D Modeling", - "Topic :: Scientific/Engineering :: Visualization", - "License :: OSI Approved :: MIT License", - "License :: OSI Approved :: ISC License (ISCL)", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - ], - ext_modules=ext_modules, - #extras_require={"test": "pytest"}, - # Currently, build_ext only provides an optional "highest supported C++ - # level" feature, but in the future it may provide more features. - # cmdclass={"build_ext": build_ext}, - #zip_safe=False, - #python_requires=">=3.6", -) diff --git a/tests/LICENSE.acutest.txt b/tests/LICENSE.acutest.txt deleted file mode 100644 index 97309201..00000000 --- a/tests/LICENSE.acutest.txt +++ /dev/null @@ -1,22 +0,0 @@ - -# The MIT License (MIT) - -Copyright © 2013-2017 Martin 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. diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index 83d297d2..00000000 --- a/tests/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: clean - -CXX ?= clang++ -CXXFLAGS ?= -g -O1 -EXTRA_CXXFLAGS ?= -std=c++03 -fsanitize=address - -tester: tester.cc ../tiny_obj_loader.h - $(CXX) $(CXXFLAGS) $(EXTRA_CXXFLAGS) -o tester tester.cc - -all: tester - -check: tester - ./tester - -clean: - rm -rf tester - diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 1b0b43d1..00000000 --- a/tests/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Build&Test - -## Use makefile - - $ make check - -## Use ninja + kuroga - -Assume - -* ninja 1.4+ -* python 2.6+ - -Are installed. - -### Linux/MacOSX - - $ python kuroga.py config-posix.py - $ ninja - -### Windows - -Visual Studio 2013 is required to build tester. - -On Windows console. - - > python kuroga.py config-msvc.py - > vcbuild.bat - - -Or on msys2 bash, - - $ python kuroga.py config-msvc.py - $ cmd //c vcbuild.bat - - - diff --git a/tests/acutest.h b/tests/acutest.h deleted file mode 100644 index 29a51c33..00000000 --- a/tests/acutest.h +++ /dev/null @@ -1,1514 +0,0 @@ -/* - * Acutest -- Another C/C++ Unit Test facility - * - * - * Copyright (c) 2013-2019 Martin Mitas - * - * 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. - */ - -#ifndef ACUTEST_H__ -#define ACUTEST_H__ - - -/************************ - *** Public interface *** - ************************/ - -/* By default, "acutest.h" provides the main program entry point (function - * main()). However, if the test suite is composed of multiple source files - * which include "acutest.h", then this causes a problem of multiple main() - * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all - * compilation units but one. - */ - -/* Macro to specify list of unit tests in the suite. - * The unit test implementation MUST provide list of unit tests it implements - * with this macro: - * - * TEST_LIST = { - * { "test1_name", test1_func_ptr }, - * { "test2_name", test2_func_ptr }, - * ... - * { 0 } - * }; - * - * The list specifies names of each test (must be unique) and pointer to - * a function implementing it. The function does not take any arguments - * and has no return values, i.e. every test function has to be compatible - * with this prototype: - * - * void test_func(void); - */ -#define TEST_LIST const struct test__ test_list__[] - - -/* Macros for testing whether an unit test succeeds or fails. These macros - * can be used arbitrarily in functions implementing the unit tests. - * - * If any condition fails throughout execution of a test, the test fails. - * - * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows - * also to specify an error message to print out if the condition fails. - * (It expects printf-like format string and its parameters). The macros - * return non-zero (condition passes) or 0 (condition fails). - * - * That can be useful when more conditions should be checked only if some - * preceding condition passes, as illustrated in this code snippet: - * - * SomeStruct* ptr = allocate_some_struct(); - * if(TEST_CHECK(ptr != NULL)) { - * TEST_CHECK(ptr->member1 < 100); - * TEST_CHECK(ptr->member2 > 200); - * } - */ -#define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__) -#define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond) - -#ifdef __cplusplus -/* Macros to verify that the code (the 1st argument) throws exception of given - * type (the 2nd argument). (Note these macros are only available in C++.) - * - * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like - * message. - * - * For example: - * - * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType); - * - * If the function_that_throw() throws ExpectedExceptionType, the check passes. - * If the function throws anything incompatible with ExpectedExceptionType - * (or if it does not thrown an exception at all), the check fails. - */ -#define TEST_EXCEPTION(code, exctype) \ - do { \ - bool exc_ok__ = false; \ - const char *msg__ = NULL; \ - try { \ - code; \ - msg__ = "No exception thrown."; \ - } catch(exctype const&) { \ - exc_ok__= true; \ - } catch(...) { \ - msg__ = "Unexpected exception thrown."; \ - } \ - test_check__(exc_ok__, __FILE__, __LINE__, #code " throws " #exctype); \ - if(msg__ != NULL) \ - test_message__("%s", msg__); \ - } while(0) -#define TEST_EXCEPTION_(code, exctype, ...) \ - do { \ - bool exc_ok__ = false; \ - const char *msg__ = NULL; \ - try { \ - code; \ - msg__ = "No exception thrown."; \ - } catch(exctype const&) { \ - exc_ok__= true; \ - } catch(...) { \ - msg__ = "Unexpected exception thrown."; \ - } \ - test_check__(exc_ok__, __FILE__, __LINE__, __VA_ARGS__); \ - if(msg__ != NULL) \ - test_message__("%s", msg__); \ - } while(0) -#endif /* #ifdef __cplusplus */ - - -/* Sometimes it is useful to split execution of more complex unit tests to some - * smaller parts and associate those parts with some names. - * - * This is especially handy if the given unit test is implemented as a loop - * over some vector of multiple testing inputs. Using these macros allow to use - * sort of subtitle for each iteration of the loop (e.g. outputting the input - * itself or a name associated to it), so that if any TEST_CHECK condition - * fails in the loop, it can be easily seen which iteration triggers the - * failure, without the need to manually output the iteration-specific data in - * every single TEST_CHECK inside the loop body. - * - * TEST_CASE allows to specify only single string as the name of the case, - * TEST_CASE_ provides all the power of printf-like string formatting. - * - * Note that the test cases cannot be nested. Starting a new test case ends - * implicitly the previous one. To end the test case explicitly (e.g. to end - * the last test case after exiting the loop), you may use TEST_CASE(NULL). - */ -#define TEST_CASE_(...) test_case__(__VA_ARGS__) -#define TEST_CASE(name) test_case__("%s", name); - - -/* printf-like macro for outputting an extra information about a failure. - * - * Intended use is to output some computed output versus the expected value, - * e.g. like this: - * - * if(!TEST_CHECK(produced == expected)) { - * TEST_MSG("Expected: %d", expected); - * TEST_MSG("Produced: %d", produced); - * } - * - * Note the message is only written down if the most recent use of any checking - * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed. - * This means the above is equivalent to just this: - * - * TEST_CHECK(produced == expected); - * TEST_MSG("Expected: %d", expected); - * TEST_MSG("Produced: %d", produced); - * - * The macro can deal with multi-line output fairly well. It also automatically - * adds a final new-line if there is none present. - */ -#define TEST_MSG(...) test_message__(__VA_ARGS__) - - -/* Maximal output per TEST_MSG call. Longer messages are cut. - * You may define another limit prior including "acutest.h" - */ -#ifndef TEST_MSG_MAXSIZE - #define TEST_MSG_MAXSIZE 1024 -#endif - - -/* Macro for dumping a block of memory. - * - * Its inteded use is very similar to what TEST_MSG is for, but instead of - * generating any printf-like message, this is for dumping raw block of a - * memory in a hexadecimal form: - * - * TEST_CHECK(size_produced == size_expected && memcmp(addr_produced, addr_expected, size_produced) == 0); - * TEST_DUMP("Expected:", addr_expected, size_expected); - * TEST_DUMP("Produced:", addr_produced, size_produced); - */ -#define TEST_DUMP(title, addr, size) test_dump__(title, addr, size) - -/* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut. - * You may define another limit prior including "acutest.h" - */ -#ifndef TEST_DUMP_MAXSIZE - #define TEST_DUMP_MAXSIZE 1024 -#endif - - -/********************** - *** Implementation *** - **********************/ - -/* The unit test files should not rely on anything below. */ - -#include -#include -#include -#include -#include - -#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) - #define ACUTEST_UNIX__ 1 - #include - #include - #include - #include - #include - #include - - #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC - #define ACUTEST_HAS_POSIX_TIMER__ 1 - #endif -#endif - -#if defined(__gnu_linux__) - #define ACUTEST_LINUX__ 1 - #include - #include -#endif - -#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) - #define ACUTEST_WIN__ 1 - #include - #include -#endif - -#ifdef __cplusplus - #include -#endif - - -/* Note our global private identifiers end with '__' to mitigate risk of clash - * with the unit tests implementation. */ - - -#ifdef __cplusplus - extern "C" { -#endif - - -struct test__ { - const char* name; - void (*func)(void); -}; - -extern const struct test__ test_list__[]; - -int test_check__(int cond, const char* file, int line, const char* fmt, ...); -void test_case__(const char* fmt, ...); -void test_message__(const char* fmt, ...); -void test_dump__(const char* title, const void* addr, size_t size); - - -#ifndef TEST_NO_MAIN - -static char* test_argv0__ = NULL; -static size_t test_list_size__ = 0; -static const struct test__** tests__ = NULL; -static char* test_flags__ = NULL; -static size_t test_count__ = 0; -static int test_no_exec__ = -1; -static int test_no_summary__ = 0; -static int test_tap__ = 0; -static int test_skip_mode__ = 0; -static int test_worker__ = 0; -static int test_worker_index__ = 0; -static int test_cond_failed__ = 0; - -static int test_stat_failed_units__ = 0; -static int test_stat_run_units__ = 0; - -static const struct test__* test_current_unit__ = NULL; -static int test_current_index__ = 0; -static char test_case_name__[64] = ""; -static int test_current_already_logged__ = 0; -static int test_case_current_already_logged__ = 0; -static int test_verbose_level__ = 2; -static int test_current_failures__ = 0; -static int test_colorize__ = 0; -static int test_timer__ = 0; - -#if defined ACUTEST_WIN__ - static LARGE_INTEGER test_timer_freq__; - static LARGE_INTEGER test_timer_start__; - static LARGE_INTEGER test_timer_end__; - - static void - test_timer_init__(void) - { - QueryPerformanceFrequency(&test_timer_freq__); - } - - static void - test_timer_get_time__(LARGE_INTEGER* ts) - { - QueryPerformanceCounter(ts); - } - - static void - test_timer_print_diff__(void) - { - double duration = test_timer_end__.QuadPart - test_timer_start__.QuadPart; - duration /= test_timer_freq__.QuadPart; - printf("%.6lf secs", duration); - } -#elif defined ACUTEST_HAS_POSIX_TIMER__ - static clockid_t test_timer_id__; - struct timespec test_timer_start__; - struct timespec test_timer_end__; - - static void - test_timer_init__(void) - { - if(test_timer__ == 1) - #ifdef CLOCK_MONOTONIC_RAW - /* linux specific; not subject of NTP adjustements or adjtime() */ - test_timer_id__ = CLOCK_MONOTONIC_RAW; - #else - test_timer_id__ = CLOCK_MONOTONIC; - #endif - else if(test_timer__ == 2) - test_timer_id__ = CLOCK_PROCESS_CPUTIME_ID; - } - - static void - test_timer_get_time__(struct timespec* ts) - { - clock_gettime(test_timer_id__, ts); - } - - static void - test_timer_print_diff__(void) - { - double duration = ((double) test_timer_end__.tv_sec + - (double) test_timer_end__.tv_nsec * 10e-9) - - - ((double) test_timer_start__.tv_sec + - (double) test_timer_start__.tv_nsec * 10e-9); - printf("%.6lf secs", duration); - } -#else - static int test_timer_start__; - static int test_timer_end__; - - void - test_timer_init__(void) - {} - - static void - test_timer_get_time__(int* ts) - { - (void) ts; - } - - static void - test_timer_print_diff__(void) - {} -#endif - -#define TEST_COLOR_DEFAULT__ 0 -#define TEST_COLOR_GREEN__ 1 -#define TEST_COLOR_RED__ 2 -#define TEST_COLOR_DEFAULT_INTENSIVE__ 3 -#define TEST_COLOR_GREEN_INTENSIVE__ 4 -#define TEST_COLOR_RED_INTENSIVE__ 5 - -static int -test_print_in_color__(int color, const char* fmt, ...) -{ - va_list args; - char buffer[256]; - int n; - - va_start(args, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, args); - va_end(args); - buffer[sizeof(buffer)-1] = '\0'; - - if(!test_colorize__) { - return printf("%s", buffer); - } - -#if defined ACUTEST_UNIX__ - { - const char* col_str; - switch(color) { - case TEST_COLOR_GREEN__: col_str = "\033[0;32m"; break; - case TEST_COLOR_RED__: col_str = "\033[0;31m"; break; - case TEST_COLOR_GREEN_INTENSIVE__: col_str = "\033[1;32m"; break; - case TEST_COLOR_RED_INTENSIVE__: col_str = "\033[1;31m"; break; - case TEST_COLOR_DEFAULT_INTENSIVE__: col_str = "\033[1m"; break; - default: col_str = "\033[0m"; break; - } - printf("%s", col_str); - n = printf("%s", buffer); - printf("\033[0m"); - return n; - } -#elif defined ACUTEST_WIN__ - { - HANDLE h; - CONSOLE_SCREEN_BUFFER_INFO info; - WORD attr; - - h = GetStdHandle(STD_OUTPUT_HANDLE); - GetConsoleScreenBufferInfo(h, &info); - - switch(color) { - case TEST_COLOR_GREEN__: attr = FOREGROUND_GREEN; break; - case TEST_COLOR_RED__: attr = FOREGROUND_RED; break; - case TEST_COLOR_GREEN_INTENSIVE__: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; - case TEST_COLOR_RED_INTENSIVE__: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; - case TEST_COLOR_DEFAULT_INTENSIVE__: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; - default: attr = 0; break; - } - if(attr != 0) - SetConsoleTextAttribute(h, attr); - n = printf("%s", buffer); - SetConsoleTextAttribute(h, info.wAttributes); - return n; - } -#else - n = printf("%s", buffer); - return n; -#endif -} - -static void -test_begin_test_line__(const struct test__* test) -{ - if(!test_tap__) { - if(test_verbose_level__ >= 3) { - test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name); - test_current_already_logged__++; - } else if(test_verbose_level__ >= 1) { - int n; - char spaces[48]; - - n = test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name); - memset(spaces, ' ', sizeof(spaces)); - if(n < (int) sizeof(spaces)) - printf("%.*s", (int) sizeof(spaces) - n, spaces); - } else { - test_current_already_logged__ = 1; - } - } -} - -static void -test_finish_test_line__(int result) -{ - if(test_tap__) { - const char* str = (result == 0) ? "ok" : "not ok"; - - printf("%s %u - %s\n", str, test_current_index__ + 1, test_current_unit__->name); - - if(result == 0 && test_timer__) { - printf("# Duration: "); - test_timer_print_diff__(); - printf("\n"); - } - } else { - int color = (result == 0) ? TEST_COLOR_GREEN_INTENSIVE__ : TEST_COLOR_RED_INTENSIVE__; - const char* str = (result == 0) ? "OK" : "FAILED"; - printf("[ "); - test_print_in_color__(color, str); - printf(" ]"); - - if(result == 0 && test_timer__) { - printf(" "); - test_timer_print_diff__(); - } - - printf("\n"); - } -} - -static void -test_line_indent__(int level) -{ - static const char spaces[] = " "; - int n = level * 2; - - if(test_tap__ && n > 0) { - n--; - printf("#"); - } - - while(n > 16) { - printf("%s", spaces); - n -= 16; - } - printf("%.*s", n, spaces); -} - -int -test_check__(int cond, const char* file, int line, const char* fmt, ...) -{ - const char *result_str; - int result_color; - int verbose_level; - - if(cond) { - result_str = "ok"; - result_color = TEST_COLOR_GREEN__; - verbose_level = 3; - } else { - if(!test_current_already_logged__ && test_current_unit__ != NULL) - test_finish_test_line__(-1); - - result_str = "failed"; - result_color = TEST_COLOR_RED__; - verbose_level = 2; - test_current_failures__++; - test_current_already_logged__++; - } - - if(test_verbose_level__ >= verbose_level) { - va_list args; - - if(!test_case_current_already_logged__ && test_case_name__[0]) { - test_line_indent__(1); - test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__); - test_current_already_logged__++; - test_case_current_already_logged__++; - } - - test_line_indent__(test_case_name__[0] ? 2 : 1); - if(file != NULL) { - if(test_verbose_level__ < 3) { -#ifdef ACUTEST_WIN__ - const char* lastsep1 = strrchr(file, '\\'); - const char* lastsep2 = strrchr(file, '/'); - if(lastsep1 == NULL) - lastsep1 = file-1; - if(lastsep2 == NULL) - lastsep2 = file-1; - file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1; -#else - const char* lastsep = strrchr(file, '/'); - if(lastsep != NULL) - file = lastsep+1; -#endif - } - printf("%s:%d: Check ", file, line); - } - - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - - printf("... "); - test_print_in_color__(result_color, result_str); - printf("\n"); - test_current_already_logged__++; - } - - test_cond_failed__ = (cond == 0); - return !test_cond_failed__; -} - -void -test_case__(const char* fmt, ...) -{ - va_list args; - - if(test_verbose_level__ < 2) - return; - - if(test_case_name__[0]) { - test_case_current_already_logged__ = 0; - test_case_name__[0] = '\0'; - } - - if(fmt == NULL) - return; - - va_start(args, fmt); - vsnprintf(test_case_name__, sizeof(test_case_name__) - 1, fmt, args); - va_end(args); - test_case_name__[sizeof(test_case_name__) - 1] = '\0'; - - if(test_verbose_level__ >= 3) { - test_line_indent__(1); - test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__); - test_current_already_logged__++; - test_case_current_already_logged__++; - } -} - -void -test_message__(const char* fmt, ...) -{ - char buffer[TEST_MSG_MAXSIZE]; - char* line_beg; - char* line_end; - va_list args; - - if(test_verbose_level__ < 2) - return; - - /* We allow extra message only when something is already wrong in the - * current test. */ - if(test_current_unit__ == NULL || !test_cond_failed__) - return; - - va_start(args, fmt); - vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args); - va_end(args); - buffer[TEST_MSG_MAXSIZE-1] = '\0'; - - line_beg = buffer; - while(1) { - line_end = strchr(line_beg, '\n'); - if(line_end == NULL) - break; - test_line_indent__(test_case_name__[0] ? 3 : 2); - printf("%.*s\n", (int)(line_end - line_beg), line_beg); - line_beg = line_end + 1; - } - if(line_beg[0] != '\0') { - test_line_indent__(test_case_name__[0] ? 3 : 2); - printf("%s\n", line_beg); - } -} - -void -test_dump__(const char* title, const void* addr, size_t size) -{ - static const size_t BYTES_PER_LINE = 16; - size_t line_beg; - size_t truncate = 0; - - if(test_verbose_level__ < 2) - return; - - /* We allow extra message only when something is already wrong in the - * current test. */ - if(test_current_unit__ == NULL || !test_cond_failed__) - return; - - if(size > TEST_DUMP_MAXSIZE) { - truncate = size - TEST_DUMP_MAXSIZE; - size = TEST_DUMP_MAXSIZE; - } - - test_line_indent__(test_case_name__[0] ? 3 : 2); - printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title); - - for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) { - size_t line_end = line_beg + BYTES_PER_LINE; - size_t off; - - test_line_indent__(test_case_name__[0] ? 4 : 3); - printf("%08x: ", line_beg); - for(off = line_beg; off < line_end; off++) { - if(off < size) - printf(" %02x", ((unsigned char*)addr)[off]); - else - printf(" "); - } - - printf(" "); - for(off = line_beg; off < line_end; off++) { - unsigned char byte = ((unsigned char*)addr)[off]; - if(off < size) - printf("%c", (iscntrl(byte) ? '.' : byte)); - else - break; - } - - printf("\n"); - } - - if(truncate > 0) { - test_line_indent__(test_case_name__[0] ? 4 : 3); - printf(" ... (and more %u bytes)\n", (unsigned) truncate); - } -} - -static void -test_list_names__(void) -{ - const struct test__* test; - - printf("Unit tests:\n"); - for(test = &test_list__[0]; test->func != NULL; test++) - printf(" %s\n", test->name); -} - -static void -test_remember__(int i) -{ - if(test_flags__[i]) - return; - else - test_flags__[i] = 1; - - tests__[test_count__] = &test_list__[i]; - test_count__++; -} - -static int -test_name_contains_word__(const char* name, const char* pattern) -{ - static const char word_delim[] = " \t-_."; - const char* substr; - size_t pattern_len; - int starts_on_word_boundary; - int ends_on_word_boundary; - - pattern_len = strlen(pattern); - - substr = strstr(name, pattern); - while(substr != NULL) { - starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL); - ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL); - - if(starts_on_word_boundary && ends_on_word_boundary) - return 1; - - substr = strstr(substr+1, pattern); - } - - return 0; -} - -static int -test_lookup__(const char* pattern) -{ - int i; - int n = 0; - - /* Try exact match. */ - for(i = 0; i < (int) test_list_size__; i++) { - if(strcmp(test_list__[i].name, pattern) == 0) { - test_remember__(i); - n++; - break; - } - } - if(n > 0) - return n; - - /* Try word match. */ - for(i = 0; i < (int) test_list_size__; i++) { - if(test_name_contains_word__(test_list__[i].name, pattern)) { - test_remember__(i); - n++; - } - } - if(n > 0) - return n; - - /* Try relaxed match. */ - for(i = 0; i < (int) test_list_size__; i++) { - if(strstr(test_list__[i].name, pattern) != NULL) { - test_remember__(i); - n++; - } - } - - return n; -} - - -/* Called if anything goes bad in Acutest, or if the unit test ends in other - * way then by normal returning from its function (e.g. exception or some - * abnormal child process termination). */ -static void -test_error__(const char* fmt, ...) -{ - va_list args; - - if(test_verbose_level__ == 0) - return; - - if(test_verbose_level__ <= 2 && !test_current_already_logged__ && test_current_unit__ != NULL) { - if(test_tap__) { - test_finish_test_line__(-1); - } else { - printf("[ "); - test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED"); - printf(" ]\n"); - } - } - - if(test_verbose_level__ >= 2) { - test_line_indent__(1); - if(test_verbose_level__ >= 3) - test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "ERROR: "); - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - printf("\n"); - } - - if(test_verbose_level__ >= 3) { - printf("\n"); - } -} - -/* Call directly the given test unit function. */ -static int -test_do_run__(const struct test__* test, int index) -{ - test_current_unit__ = test; - test_current_index__ = index; - test_current_failures__ = 0; - test_current_already_logged__ = 0; - test_cond_failed__ = 0; - - test_timer_init__(); - - test_begin_test_line__(test); - -#ifdef __cplusplus - try { -#endif - - /* This is good to do for case the test unit e.g. crashes. */ - fflush(stdout); - fflush(stderr); - - test_timer_get_time__(&test_timer_start__); - test->func(); - test_timer_get_time__(&test_timer_end__); - - if(test_verbose_level__ >= 3) { - test_line_indent__(1); - if(test_current_failures__ == 0) { - test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS: "); - printf("All conditions have passed.\n"); - - if(test_timer__) { - test_line_indent__(1); - printf("Duration: "); - test_timer_print_diff__(); - printf("\n"); - } - } else { - test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED: "); - printf("%d condition%s %s failed.\n", - test_current_failures__, - (test_current_failures__ == 1) ? "" : "s", - (test_current_failures__ == 1) ? "has" : "have"); - } - printf("\n"); - } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) { - test_finish_test_line__(0); - } - - test_case__(NULL); - test_current_unit__ = NULL; - return (test_current_failures__ == 0) ? 0 : -1; - -#ifdef __cplusplus - } catch(std::exception& e) { - const char* what = e.what(); - if(what != NULL) - test_error__("Threw std::exception: %s", what); - else - test_error__("Threw std::exception"); - return -1; - } catch(...) { - test_error__("Threw an exception"); - return -1; - } -#endif -} - -/* Trigger the unit test. If possible (and not suppressed) it starts a child - * process who calls test_do_run__(), otherwise it calls test_do_run__() - * directly. */ -static void -test_run__(const struct test__* test, int index) -{ - int failed = 1; - - test_current_unit__ = test; - test_current_already_logged__ = 0; - - if(!test_no_exec__) { - -#if defined(ACUTEST_UNIX__) - - pid_t pid; - int exit_code; - - /* Make sure the child starts with empty I/O buffers. */ - fflush(stdout); - fflush(stderr); - - pid = fork(); - if(pid == (pid_t)-1) { - test_error__("Cannot fork. %s [%d]", strerror(errno), errno); - failed = 1; - } else if(pid == 0) { - /* Child: Do the test. */ - failed = (test_do_run__(test, index) != 0); - exit(failed ? 1 : 0); - } else { - /* Parent: Wait until child terminates and analyze its exit code. */ - waitpid(pid, &exit_code, 0); - if(WIFEXITED(exit_code)) { - switch(WEXITSTATUS(exit_code)) { - case 0: failed = 0; break; /* test has passed. */ - case 1: /* noop */ break; /* "normal" failure. */ - default: test_error__("Unexpected exit code [%d]", WEXITSTATUS(exit_code)); - } - } else if(WIFSIGNALED(exit_code)) { - char tmp[32]; - const char* signame; - switch(WTERMSIG(exit_code)) { - case SIGINT: signame = "SIGINT"; break; - case SIGHUP: signame = "SIGHUP"; break; - case SIGQUIT: signame = "SIGQUIT"; break; - case SIGABRT: signame = "SIGABRT"; break; - case SIGKILL: signame = "SIGKILL"; break; - case SIGSEGV: signame = "SIGSEGV"; break; - case SIGILL: signame = "SIGILL"; break; - case SIGTERM: signame = "SIGTERM"; break; - default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break; - } - test_error__("Test interrupted by %s", signame); - } else { - test_error__("Test ended in an unexpected way [%d]", exit_code); - } - } - -#elif defined(ACUTEST_WIN__) - - char buffer[512] = {0}; - STARTUPINFOA startupInfo; - PROCESS_INFORMATION processInfo; - DWORD exitCode; - - /* Windows has no fork(). So we propagate all info into the child - * through a command line arguments. */ - _snprintf(buffer, sizeof(buffer)-1, - "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"", - test_argv0__, index, test_timer__ ? "--timer" : "", - test_tap__ ? "--tap" : "", test_verbose_level__, - test_colorize__ ? "always" : "never", - test->name); - memset(&startupInfo, 0, sizeof(startupInfo)); - startupInfo.cb = sizeof(STARTUPINFO); - if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { - WaitForSingleObject(processInfo.hProcess, INFINITE); - GetExitCodeProcess(processInfo.hProcess, &exitCode); - CloseHandle(processInfo.hThread); - CloseHandle(processInfo.hProcess); - failed = (exitCode != 0); - } else { - test_error__("Cannot create unit test subprocess [%ld].", GetLastError()); - failed = 1; - } - -#else - - /* A platform where we don't know how to run child process. */ - failed = (test_do_run__(test, index) != 0); - -#endif - - } else { - /* Child processes suppressed through --no-exec. */ - failed = (test_do_run__(test, index) != 0); - } - - test_current_unit__ = NULL; - - test_stat_run_units__++; - if(failed) - test_stat_failed_units__++; -} - -#if defined(ACUTEST_WIN__) -/* Callback for SEH events. */ -static LONG CALLBACK -test_exception_filter__(EXCEPTION_POINTERS *ptrs) -{ - test_error__("Unhandled SEH exception %08lx at %p.", - ptrs->ExceptionRecord->ExceptionCode, - ptrs->ExceptionRecord->ExceptionAddress); - fflush(stdout); - fflush(stderr); - return EXCEPTION_EXECUTE_HANDLER; -} -#endif - - -#define TEST_CMDLINE_OPTFLAG_OPTIONALARG__ 0x0001 -#define TEST_CMDLINE_OPTFLAG_REQUIREDARG__ 0x0002 - -#define TEST_CMDLINE_OPTID_NONE__ 0 -#define TEST_CMDLINE_OPTID_UNKNOWN__ (-0x7fffffff + 0) -#define TEST_CMDLINE_OPTID_MISSINGARG__ (-0x7fffffff + 1) -#define TEST_CMDLINE_OPTID_BOGUSARG__ (-0x7fffffff + 2) - -typedef struct TEST_CMDLINE_OPTION__ { - char shortname; - const char* longname; - int id; - unsigned flags; -} TEST_CMDLINE_OPTION__; - -static int -test_cmdline_handle_short_opt_group__(const TEST_CMDLINE_OPTION__* options, - const char* arggroup, - int (*callback)(int /*optval*/, const char* /*arg*/)) -{ - const TEST_CMDLINE_OPTION__* opt; - int i; - int ret = 0; - - for(i = 0; arggroup[i] != '\0'; i++) { - for(opt = options; opt->id != 0; opt++) { - if(arggroup[i] == opt->shortname) - break; - } - - if(opt->id != 0 && !(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) { - ret = callback(opt->id, NULL); - } else { - /* Unknown option. */ - char badoptname[3]; - badoptname[0] = '-'; - badoptname[1] = arggroup[i]; - badoptname[2] = '\0'; - ret = callback((opt->id != 0 ? TEST_CMDLINE_OPTID_MISSINGARG__ : TEST_CMDLINE_OPTID_UNKNOWN__), - badoptname); - } - - if(ret != 0) - break; - } - - return ret; -} - -#define TEST_CMDLINE_AUXBUF_SIZE__ 32 - -static int -test_cmdline_read__(const TEST_CMDLINE_OPTION__* options, int argc, char** argv, - int (*callback)(int /*optval*/, const char* /*arg*/)) -{ - - const TEST_CMDLINE_OPTION__* opt; - char auxbuf[TEST_CMDLINE_AUXBUF_SIZE__+1]; - int after_doubledash = 0; - int i = 1; - int ret = 0; - - auxbuf[TEST_CMDLINE_AUXBUF_SIZE__] = '\0'; - - while(i < argc) { - if(after_doubledash || strcmp(argv[i], "-") == 0) { - /* Non-option argument. */ - ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); - } else if(strcmp(argv[i], "--") == 0) { - /* End of options. All the remaining members are non-option arguments. */ - after_doubledash = 1; - } else if(argv[i][0] != '-') { - /* Non-option argument. */ - ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); - } else { - for(opt = options; opt->id != 0; opt++) { - if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) { - size_t len = strlen(opt->longname); - if(strncmp(argv[i]+2, opt->longname, len) == 0) { - /* Regular long option. */ - if(argv[i][2+len] == '\0') { - /* with no argument provided. */ - if(!(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) - ret = callback(opt->id, NULL); - else - ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]); - break; - } else if(argv[i][2+len] == '=') { - /* with an argument provided. */ - if(opt->flags & (TEST_CMDLINE_OPTFLAG_OPTIONALARG__ | TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) { - ret = callback(opt->id, argv[i]+2+len+1); - } else { - sprintf(auxbuf, "--%s", opt->longname); - ret = callback(TEST_CMDLINE_OPTID_BOGUSARG__, auxbuf); - } - break; - } else { - continue; - } - } - } else if(opt->shortname != '\0' && argv[i][0] == '-') { - if(argv[i][1] == opt->shortname) { - /* Regular short option. */ - if(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__) { - if(argv[i][2] != '\0') - ret = callback(opt->id, argv[i]+2); - else if(i+1 < argc) - ret = callback(opt->id, argv[++i]); - else - ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]); - break; - } else { - ret = callback(opt->id, NULL); - - /* There might be more (argument-less) short options - * grouped together. */ - if(ret == 0 && argv[i][2] != '\0') - ret = test_cmdline_handle_short_opt_group__(options, argv[i]+2, callback); - break; - } - } - } - } - - if(opt->id == 0) { /* still not handled? */ - if(argv[i][0] != '-') { - /* Non-option argument. */ - ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); - } else { - /* Unknown option. */ - char* badoptname = argv[i]; - - if(strncmp(badoptname, "--", 2) == 0) { - /* Strip any argument from the long option. */ - char* assignement = strchr(badoptname, '='); - if(assignement != NULL) { - size_t len = assignement - badoptname; - if(len > TEST_CMDLINE_AUXBUF_SIZE__) - len = TEST_CMDLINE_AUXBUF_SIZE__; - strncpy(auxbuf, badoptname, len); - auxbuf[len] = '\0'; - badoptname = auxbuf; - } - } - - ret = callback(TEST_CMDLINE_OPTID_UNKNOWN__, badoptname); - } - } - } - - if(ret != 0) - return ret; - i++; - } - - return ret; -} - -static void -test_help__(void) -{ - printf("Usage: %s [options] [test...]\n", test_argv0__); - printf("\n"); - printf("Run the specified unit tests; or if the option '--skip' is used, run all\n"); - printf("tests in the suite but those listed. By default, if no tests are specified\n"); - printf("on the command line, all unit tests in the suite are run.\n"); - printf("\n"); - printf("Options:\n"); - printf(" -s, --skip Execute all unit tests but the listed ones\n"); - printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n"); - printf(" (WHEN is one of 'auto', 'always', 'never')\n"); -#if defined ACUTEST_WIN__ - printf(" -t, --timer Measure test duration\n"); -#elif defined ACUTEST_HAS_POSIX_TIMER__ - printf(" -t, --timer Measure test duration (real time)\n"); - printf(" --timer=TIMER Measure test duration, using given timer\n"); - printf(" (TIMER is one of 'real', 'cpu')\n"); -#endif - printf(" -E, --no-exec Same as --exec=never\n"); - printf(" --no-summary Suppress printing of test results summary\n"); - printf(" --tap Produce TAP-compliant output\n"); - printf(" (See https://testanything.org/)\n"); - printf(" -l, --list List unit tests in the suite and exit\n"); - printf(" -v, --verbose Make output more verbose\n"); - printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); - printf(" 0 ... Be silent\n"); - printf(" 1 ... Output one line per test (and summary)\n"); - printf(" 2 ... As 1 and failed conditions (this is default)\n"); - printf(" 3 ... As 1 and all conditions (and extended summary)\n"); - printf(" --color[=WHEN] Enable colorized output\n"); - printf(" (WHEN is one of 'auto', 'always', 'never')\n"); - printf(" --no-color Same as --color=never\n"); - printf(" -h, --help Display this help and exit\n"); - - if(test_list_size__ < 16) { - printf("\n"); - test_list_names__(); - } -} - -static const TEST_CMDLINE_OPTION__ test_cmdline_options__[] = { - { 's', "skip", 's', 0 }, - { 0, "exec", 'e', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, - { 'E', "no-exec", 'E', 0 }, -#if defined ACUTEST_WIN__ - { 't', "timer", 't', 0 }, -#elif defined ACUTEST_HAS_POSIX_TIMER__ - { 't', "timer", 't', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, -#endif - { 0, "no-summary", 'S', 0 }, - { 0, "tap", 'T', 0 }, - { 'l', "list", 'l', 0 }, - { 'v', "verbose", 'v', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, - { 0, "color", 'c', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, - { 0, "no-color", 'C', 0 }, - { 'h', "help", 'h', 0 }, - { 0, "worker", 'w', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ }, /* internal */ - { 0, NULL, 0, 0 } -}; - -static int -test_cmdline_callback__(int id, const char* arg) -{ - switch(id) { - case 's': - test_skip_mode__ = 1; - break; - - case 'e': - if(arg == NULL || strcmp(arg, "always") == 0) { - test_no_exec__ = 0; - } else if(strcmp(arg, "never") == 0) { - test_no_exec__ = 1; - } else if(strcmp(arg, "auto") == 0) { - /*noop*/ - } else { - fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", test_argv0__, arg); - fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); - exit(2); - } - break; - - case 'E': - test_no_exec__ = 1; - break; - - case 't': -#if defined ACUTEST_WIN__ || defined ACUTEST_HAS_POSIX_TIMER__ - if(arg == NULL || strcmp(arg, "real") == 0) { - test_timer__ = 1; - #ifndef ACUTEST_WIN__ - } else if(strcmp(arg, "cpu") == 0) { - test_timer__ = 2; - #endif - } else { - fprintf(stderr, "%s: Unrecognized argument '%s' for option --timer.\n", test_argv0__, arg); - fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); - exit(2); - } -#endif - break; - - case 'S': - test_no_summary__ = 1; - break; - - case 'T': - test_tap__ = 1; - break; - - case 'l': - test_list_names__(); - exit(0); - - case 'v': - test_verbose_level__ = (arg != NULL ? atoi(arg) : test_verbose_level__+1); - break; - - case 'c': - if(arg == NULL || strcmp(arg, "always") == 0) { - test_colorize__ = 1; - } else if(strcmp(arg, "never") == 0) { - test_colorize__ = 0; - } else if(strcmp(arg, "auto") == 0) { - /*noop*/ - } else { - fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", test_argv0__, arg); - fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); - exit(2); - } - break; - - case 'C': - test_colorize__ = 0; - break; - - case 'h': - test_help__(); - exit(0); - - case 'w': - test_worker__ = 1; - test_worker_index__ = atoi(arg); - break; - - case 0: - if(test_lookup__(arg) == 0) { - fprintf(stderr, "%s: Unrecognized unit test '%s'\n", test_argv0__, arg); - fprintf(stderr, "Try '%s --list' for list of unit tests.\n", test_argv0__); - exit(2); - } - break; - - case TEST_CMDLINE_OPTID_UNKNOWN__: - fprintf(stderr, "Unrecognized command line option '%s'.\n", arg); - fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); - exit(2); - - case TEST_CMDLINE_OPTID_MISSINGARG__: - fprintf(stderr, "The command line option '%s' requires an argument.\n", arg); - fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); - exit(2); - - case TEST_CMDLINE_OPTID_BOGUSARG__: - fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg); - fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); - exit(2); - } - - return 0; -} - - -#ifdef ACUTEST_LINUX__ -static int -test_is_tracer_present__(void) -{ - char buf[256+32+1]; - int tracer_present = 0; - int fd; - ssize_t n_read; - - fd = open("/proc/self/status", O_RDONLY); - if(fd == -1) - return 0; - - n_read = read(fd, buf, sizeof(buf)-1); - while(n_read > 0) { - static const char pattern[] = "TracerPid:"; - const char* field; - - buf[n_read] = '\0'; - field = strstr(buf, pattern); - if(field != NULL && field < buf + sizeof(buf) - 32) { - pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); - tracer_present = (tracer_pid != 0); - break; - } - - if(n_read == sizeof(buf)-1) { - memmove(buf, buf + sizeof(buf)-1 - 32, 32); - n_read = read(fd, buf+32, sizeof(buf)-1-32); - if(n_read > 0) - n_read += 32; - } - } - - close(fd); - return tracer_present; -} -#endif - -int -main(int argc, char** argv) -{ - int i; - test_argv0__ = argv[0]; - -#if defined ACUTEST_UNIX__ - test_colorize__ = isatty(STDOUT_FILENO); -#elif defined ACUTEST_WIN__ - #if defined __BORLANDC__ - test_colorize__ = isatty(_fileno(stdout)); - #else - test_colorize__ = _isatty(_fileno(stdout)); - #endif -#else - test_colorize__ = 0; -#endif - - /* Count all test units */ - test_list_size__ = 0; - for(i = 0; test_list__[i].func != NULL; i++) - test_list_size__++; - - tests__ = (const struct test__**) malloc(sizeof(const struct test__*) * test_list_size__); - test_flags__ = (char*) malloc(sizeof(char) * test_list_size__); - if(tests__ == NULL || test_flags__ == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(2); - } - memset((void*) test_flags__, 0, sizeof(char) * test_list_size__); - - /* Parse options */ - test_cmdline_read__(test_cmdline_options__, argc, argv, test_cmdline_callback__); - -#if defined(ACUTEST_WIN__) - SetUnhandledExceptionFilter(test_exception_filter__); -#endif - - /* By default, we want to run all tests. */ - if(test_count__ == 0) { - for(i = 0; test_list__[i].func != NULL; i++) - tests__[i] = &test_list__[i]; - test_count__ = test_list_size__; - } - - /* Guess whether we want to run unit tests as child processes. */ - if(test_no_exec__ < 0) { - test_no_exec__ = 0; - - if(test_count__ <= 1) { - test_no_exec__ = 1; - } else { -#ifdef ACUTEST_WIN__ - if(IsDebuggerPresent()) - test_no_exec__ = 1; -#endif -#ifdef ACUTEST_LINUX__ - if(test_is_tracer_present__()) - test_no_exec__ = 1; -#endif - } - } - - if(test_tap__) { - /* TAP requires we know test result ("ok", "not ok") before we output - * anything about the test, and this gets problematic for larger verbose - * levels. */ - if(test_verbose_level__ > 2) - test_verbose_level__ = 2; - - /* TAP harness should provide some summary. */ - test_no_summary__ = 1; - - if(!test_worker__) - printf("1..%d\n", (int) test_count__); - } - - /* Run the tests */ - if(!test_skip_mode__) { - /* Run the listed tests. */ - for(i = 0; i < (int) test_count__; i++) - test_run__(tests__[i], test_worker_index__ + i); - } else { - /* Run all tests except those listed. */ - int index = test_worker_index__; - for(i = 0; test_list__[i].func != NULL; i++) { - if(!test_flags__[i]) - test_run__(&test_list__[i], index++); - } - } - - /* Write a summary */ - if(!test_no_summary__ && test_verbose_level__ >= 1) { - if(test_verbose_level__ >= 3) { - test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Summary:\n"); - - printf(" Count of all unit tests: %4d\n", (int) test_list_size__); - printf(" Count of run unit tests: %4d\n", test_stat_run_units__); - printf(" Count of failed unit tests: %4d\n", test_stat_failed_units__); - printf(" Count of skipped unit tests: %4d\n", (int) test_list_size__ - test_stat_run_units__); - } - - if(test_stat_failed_units__ == 0) { - test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS:"); - printf(" All unit tests have passed.\n"); - } else { - test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED:"); - printf(" %d of %d unit tests %s failed.\n", - test_stat_failed_units__, test_stat_run_units__, - (test_stat_failed_units__ == 1) ? "has" : "have"); - } - - if(test_verbose_level__ >= 3) - printf("\n"); - } - - free((void*) tests__); - free((void*) test_flags__); - - return (test_stat_failed_units__ == 0) ? 0 : 1; -} - - -#endif /* #ifndef TEST_NO_MAIN */ - -#ifdef __cplusplus - } /* extern "C" */ -#endif - - -#endif /* #ifndef ACUTEST_H__ */ diff --git a/tests/assets/issue-244.mtl b/tests/assets/issue-244.mtl deleted file mode 100644 index 63c46a43..00000000 --- a/tests/assets/issue-244.mtl +++ /dev/null @@ -1,11 +0,0 @@ -newmtl None -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl None1 -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - - diff --git a/tests/config-msvc.py b/tests/config-msvc.py deleted file mode 100644 index a7771def..00000000 --- a/tests/config-msvc.py +++ /dev/null @@ -1,52 +0,0 @@ -exe = "tester.exe" - -toolchain = "msvc" - -# optional -link_pool_depth = 1 - -# optional -builddir = { - "gnu" : "build" - , "msvc" : "build" - , "clang" : "build" - } - -includes = { - "gnu" : [ "-I." ] - , "msvc" : [ "/I." ] - , "clang" : [ "-I." ] - } - -defines = { - "gnu" : [ "-DEXAMPLE=1" ] - , "msvc" : [ "/DEXAMPLE=1" ] - , "clang" : [ "-DEXAMPLE=1" ] - } - -cflags = { - "gnu" : [ "-O2", "-g" ] - , "msvc" : [ "/O2" ] - , "clang" : [ "-O2", "-g" ] - } - -cxxflags = { - "gnu" : [ "-O2", "-g" ] - , "msvc" : [ "/O2", "/W4", "/EHsc"] - , "clang" : [ "-O2", "-g", "-fsanitize=address" ] - } - -ldflags = { - "gnu" : [ ] - , "msvc" : [ ] - , "clang" : [ "-fsanitize=address" ] - } - -# optionsl -cxx_files = [ "tester.cc" ] -c_files = [ ] - -# You can register your own toolchain through register_toolchain function -def register_toolchain(ninja): - pass - diff --git a/tests/config-posix.py b/tests/config-posix.py deleted file mode 100644 index 29cc4d55..00000000 --- a/tests/config-posix.py +++ /dev/null @@ -1,53 +0,0 @@ -exe = "tester" - -# "gnu" or "clang" -toolchain = "gnu" - -# optional -link_pool_depth = 1 - -# optional -builddir = { - "gnu" : "build" - , "msvc" : "build" - , "clang" : "build" - } - -includes = { - "gnu" : [ "-I." ] - , "msvc" : [ "/I." ] - , "clang" : [ "-I." ] - } - -defines = { - "gnu" : [ ] - , "msvc" : [ ] - , "clang" : [ ] - } - -cflags = { - "gnu" : [ "-O2", "-g" ] - , "msvc" : [ "/O2" ] - , "clang" : [ "-O2", "-g" ] - } - -# Warn as much as possible: http://qiita.com/MitsutakaTakeda/items/6b9966f890cc9b944d75 -cxxflags = { - "gnu" : [ "-O2", "-g", "-pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wno-unused", "-fsanitize=address" ] - , "msvc" : [ "/O2", "/W4" ] - , "clang" : [ "-O2", "-g", "-Werror -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic", "-fsanitize=address" ] - } - -ldflags = { - "gnu" : [ "-fsanitize=address" ] - , "msvc" : [ ] - , "clang" : [ "-fsanitize=address" ] - } - -cxx_files = [ "tester.cc" ] -c_files = [ ] - -# You can register your own toolchain through register_toolchain function -def register_toolchain(ninja): - pass - diff --git a/tests/issue-177.mtl b/tests/issue-177.mtl deleted file mode 100644 index d3a1c7a6..00000000 --- a/tests/issue-177.mtl +++ /dev/null @@ -1,24 +0,0 @@ -newmtl white -Ka 0 0 0 -Kd 1 1 1 -Ks 0 0 0 - -newmtl red -Ka 0 0 0 -Kd 1 0 0 -Ks 0 0 0 - -newmtl green -Ka 0 0 0 -Kd 0 1 0 -Ks 0 0 0 - -newmtl blue -Ka 0 0 0 -Kd 0 0 1 -Ks 0 0 0 - -newmtl light -Ka 20 20 20 -Kd 1 1 1 -Ks 0 0 0 diff --git a/tests/issue-177.obj b/tests/issue-177.obj deleted file mode 100644 index 0d0ac684..00000000 --- a/tests/issue-177.obj +++ /dev/null @@ -1,31 +0,0 @@ -mtllib issue-177.mtl - -v 0.000000 2.000000 2.000000 -v 0.000000 0.000000 2.000000 -v 2.000000 0.000000 2.000000 -v 2.000000 2.000000 2.000000 -v 0.000000 2.000000 0.000000 -v 0.000000 0.000000 0.000000 -v 2.000000 0.000000 0.000000 -v 2.000000 2.000000 0.000000 -# 8 vertices - -g front cube -usemtl white -f 1 2 3 4 -g back cube -# expects white material -f 8 7 6 5 -g right cube -usemtl red -f 4 3 7 8 -g top cube -usemtl white -f 5 1 4 8 -g left cube -usemtl green -f 5 6 2 1 -g bottom cube -usemtl white -f 2 6 7 3 -# 6 elements diff --git a/tests/kuroga.py b/tests/kuroga.py deleted file mode 100755 index 56d3f863..00000000 --- a/tests/kuroga.py +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/env python - -# -# Kuroga, single python file meta-build system for ninja -# https://github.com/lighttransport/kuroga -# -# Requirements: python 2.6 or 2.7 -# -# Usage: $ python kuroga.py input.py -# - -import imp -import re -import textwrap -import glob -import os -import sys - -# gcc preset -def add_gnu_rule(ninja): - ninja.rule('gnucxx', description='CXX $out', - command='$gnucxx -MMD -MF $out.d $gnudefines $gnuincludes $gnucxxflags -c $in -o $out', - depfile='$out.d', deps='gcc') - ninja.rule('gnucc', description='CC $out', - command='$gnucc -MMD -MF $out.d $gnudefines $gnuincludes $gnucflags -c $in -o $out', - depfile='$out.d', deps='gcc') - ninja.rule('gnulink', description='LINK $out', pool='link_pool', - command='$gnuld -o $out $in $libs $gnuldflags') - ninja.rule('gnuar', description='AR $out', pool='link_pool', - command='$gnuar rsc $out $in') - ninja.rule('gnustamp', description='STAMP $out', command='touch $out') - ninja.newline() - - ninja.variable('gnucxx', 'g++') - ninja.variable('gnucc', 'gcc') - ninja.variable('gnuld', '$gnucxx') - ninja.variable('gnuar', 'ar') - ninja.newline() - -# clang preset -def add_clang_rule(ninja): - ninja.rule('clangcxx', description='CXX $out', - command='$clangcxx -MMD -MF $out.d $clangdefines $clangincludes $clangcxxflags -c $in -o $out', - depfile='$out.d', deps='gcc') - ninja.rule('clangcc', description='CC $out', - command='$clangcc -MMD -MF $out.d $clangdefines $clangincludes $clangcflags -c $in -o $out', - depfile='$out.d', deps='gcc') - ninja.rule('clanglink', description='LINK $out', pool='link_pool', - command='$clangld -o $out $in $libs $clangldflags') - ninja.rule('clangar', description='AR $out', pool='link_pool', - command='$clangar rsc $out $in') - ninja.rule('clangstamp', description='STAMP $out', command='touch $out') - ninja.newline() - - ninja.variable('clangcxx', 'clang++') - ninja.variable('clangcc', 'clang') - ninja.variable('clangld', '$clangcxx') - ninja.variable('clangar', 'ar') - ninja.newline() - -# msvc preset -def add_msvc_rule(ninja): - ninja.rule('msvccxx', description='CXX $out', - command='$msvccxx /TP /showIncludes $msvcdefines $msvcincludes $msvccxxflags -c $in /Fo$out', - depfile='$out.d', deps='msvc') - ninja.rule('msvccc', description='CC $out', - command='$msvccc /TC /showIncludes $msvcdefines $msvcincludes $msvccflags -c $in /Fo$out', - depfile='$out.d', deps='msvc') - ninja.rule('msvclink', description='LINK $out', pool='link_pool', - command='$msvcld $msvcldflags $in $libs /OUT:$out') - ninja.rule('msvcar', description='AR $out', pool='link_pool', - command='$msvcar $in /OUT:$out') - #ninja.rule('msvcstamp', description='STAMP $out', command='touch $out') - ninja.newline() - - ninja.variable('msvccxx', 'cl.exe') - ninja.variable('msvccc', 'cl.exe') - ninja.variable('msvcld', 'link.exe') - ninja.variable('msvcar', 'lib.exe') - ninja.newline() - -# -- from ninja_syntax.py -- -def escape_path(word): - return word.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:') - -class Writer(object): - def __init__(self, output, width=78): - self.output = output - self.width = width - - def newline(self): - self.output.write('\n') - - def comment(self, text, has_path=False): - for line in textwrap.wrap(text, self.width - 2, break_long_words=False, - break_on_hyphens=False): - self.output.write('# ' + line + '\n') - - def variable(self, key, value, indent=0): - if value is None: - return - if isinstance(value, list): - value = ' '.join(filter(None, value)) # Filter out empty strings. - self._line('%s = %s' % (key, value), indent) - - def pool(self, name, depth): - self._line('pool %s' % name) - self.variable('depth', depth, indent=1) - - def rule(self, name, command, description=None, depfile=None, - generator=False, pool=None, restat=False, rspfile=None, - rspfile_content=None, deps=None): - self._line('rule %s' % name) - self.variable('command', command, indent=1) - if description: - self.variable('description', description, indent=1) - if depfile: - self.variable('depfile', depfile, indent=1) - if generator: - self.variable('generator', '1', indent=1) - if pool: - self.variable('pool', pool, indent=1) - if restat: - self.variable('restat', '1', indent=1) - if rspfile: - self.variable('rspfile', rspfile, indent=1) - if rspfile_content: - self.variable('rspfile_content', rspfile_content, indent=1) - if deps: - self.variable('deps', deps, indent=1) - - def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, - variables=None): - outputs = as_list(outputs) - out_outputs = [escape_path(x) for x in outputs] - all_inputs = [escape_path(x) for x in as_list(inputs)] - - if implicit: - implicit = [escape_path(x) for x in as_list(implicit)] - all_inputs.append('|') - all_inputs.extend(implicit) - if order_only: - order_only = [escape_path(x) for x in as_list(order_only)] - all_inputs.append('||') - all_inputs.extend(order_only) - - self._line('build %s: %s' % (' '.join(out_outputs), - ' '.join([rule] + all_inputs))) - - if variables: - if isinstance(variables, dict): - iterator = iter(variables.items()) - else: - iterator = iter(variables) - - for key, val in iterator: - self.variable(key, val, indent=1) - - return outputs - - def include(self, path): - self._line('include %s' % path) - - def subninja(self, path): - self._line('subninja %s' % path) - - def default(self, paths): - self._line('default %s' % ' '.join(as_list(paths))) - - def _count_dollars_before_index(self, s, i): - """Returns the number of '$' characters right in front of s[i].""" - dollar_count = 0 - dollar_index = i - 1 - while dollar_index > 0 and s[dollar_index] == '$': - dollar_count += 1 - dollar_index -= 1 - return dollar_count - - def _line(self, text, indent=0): - """Write 'text' word-wrapped at self.width characters.""" - leading_space = ' ' * indent - while len(leading_space) + len(text) > self.width: - # The text is too wide; wrap if possible. - - # Find the rightmost space that would obey our width constraint and - # that's not an escaped space. - available_space = self.width - len(leading_space) - len(' $') - space = available_space - while True: - space = text.rfind(' ', 0, space) - if (space < 0 or - self._count_dollars_before_index(text, space) % 2 == 0): - break - - if space < 0: - # No such space; just use the first unescaped space we can find. - space = available_space - 1 - while True: - space = text.find(' ', space + 1) - if (space < 0 or - self._count_dollars_before_index(text, space) % 2 == 0): - break - if space < 0: - # Give up on breaking. - break - - self.output.write(leading_space + text[0:space] + ' $\n') - text = text[space+1:] - - # Subsequent lines are continuations, so indent them. - leading_space = ' ' * (indent+2) - - self.output.write(leading_space + text + '\n') - - def close(self): - self.output.close() - - -def as_list(input): - if input is None: - return [] - if isinstance(input, list): - return input - return [input] - -# -- end from ninja_syntax.py -- - -def gen(ninja, toolchain, config): - - ninja.variable('ninja_required_version', '1.4') - ninja.newline() - - if hasattr(config, "builddir"): - builddir = config.builddir[toolchain] - ninja.variable(toolchain + 'builddir', builddir) - else: - builddir = '' - - ninja.variable(toolchain + 'defines', config.defines[toolchain] or []) - ninja.variable(toolchain + 'includes', config.includes[toolchain] or []) - ninja.variable(toolchain + 'cflags', config.cflags[toolchain] or []) - ninja.variable(toolchain + 'cxxflags', config.cxxflags[toolchain] or []) - ninja.variable(toolchain + 'ldflags', config.ldflags[toolchain] or []) - ninja.newline() - - if hasattr(config, "link_pool_depth"): - ninja.pool('link_pool', depth=config.link_pool_depth) - else: - ninja.pool('link_pool', depth=4) - ninja.newline() - - # Add default toolchain(gnu, clang and msvc) - add_gnu_rule(ninja) - add_clang_rule(ninja) - add_msvc_rule(ninja) - - obj_files = [] - - cc = toolchain + 'cc' - cxx = toolchain + 'cxx' - link = toolchain + 'link' - ar = toolchain + 'ar' - - if hasattr(config, "cxx_files"): - for src in config.cxx_files: - srcfile = src - obj = os.path.splitext(srcfile)[0] + '.o' - obj = os.path.join(builddir, obj); - obj_files.append(obj) - ninja.build(obj, cxx, srcfile) - ninja.newline() - - if hasattr(config, "c_files"): - for src in config.c_files: - srcfile = src - obj = os.path.splitext(srcfile)[0] + '.o' - obj = os.path.join(builddir, obj); - obj_files.append(obj) - ninja.build(obj, cc, srcfile) - ninja.newline() - - targetlist = [] - if hasattr(config, "exe"): - ninja.build(config.exe, link, obj_files) - targetlist.append(config.exe) - - if hasattr(config, "staticlib"): - ninja.build(config.staticlib, ar, obj_files) - targetlist.append(config.staticlib) - - ninja.build('all', 'phony', targetlist) - ninja.newline() - - ninja.default('all') - -def main(): - if len(sys.argv) < 2: - print("Usage: python kuroga.py config.py") - sys.exit(1) - - config = imp.load_source("config", sys.argv[1]) - - f = open('build.ninja', 'w') - ninja = Writer(f) - - if hasattr(config, "register_toolchain"): - config.register_toolchain(ninja) - - gen(ninja, config.toolchain, config) - f.close() - -main() diff --git a/tests/tester.cc b/tests/tester.cc deleted file mode 100644 index b5c4d0db..00000000 --- a/tests/tester.cc +++ /dev/null @@ -1,1636 +0,0 @@ -#define TINYOBJLOADER_IMPLEMENTATION -#include "../tiny_obj_loader.h" - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Weverything" -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wmissing-declarations" -#pragma GCC diagnostic ignored "-Wignored-qualifiers" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wformat" -#pragma GCC diagnostic ignored "-Wswitch-default" -#endif - - -#include "acutest.h" - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -#include -#include -#include -#include -#include -#include - -template -static bool FloatEquals(const T& a, const T& b) { - // Edit eps value as you wish. - const T eps = std::numeric_limits::epsilon() * static_cast(100); - - const T abs_diff = std::abs(a - b); - - if (abs_diff < eps) { - return true; - } else { - return false; - } -} - -static void PrintInfo(const tinyobj::attrib_t& attrib, - const std::vector& shapes, - const std::vector& materials, - bool triangulate = true) { - std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; - std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; - std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) - << std::endl; - - std::cout << "# of shapes : " << shapes.size() << std::endl; - std::cout << "# of materials : " << materials.size() << std::endl; - - for (size_t v = 0; v < attrib.vertices.size() / 3; v++) { - printf(" v[%ld] = (%f, %f, %f)\n", v, - static_cast(attrib.vertices[3 * v + 0]), - static_cast(attrib.vertices[3 * v + 1]), - static_cast(attrib.vertices[3 * v + 2])); - } - - for (size_t v = 0; v < attrib.normals.size() / 3; v++) { - printf(" n[%ld] = (%f, %f, %f)\n", v, - static_cast(attrib.normals[3 * v + 0]), - static_cast(attrib.normals[3 * v + 1]), - static_cast(attrib.normals[3 * v + 2])); - } - - for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) { - printf(" uv[%ld] = (%f, %f)\n", v, - static_cast(attrib.texcoords[2 * v + 0]), - static_cast(attrib.texcoords[2 * v + 1])); - } - - for (size_t i = 0; i < shapes.size(); i++) { - printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); - printf("Size of shape[%ld].indices: %ld\n", i, - shapes[i].mesh.indices.size()); - - if (triangulate) { - printf("Size of shape[%ld].material_ids: %ld\n", i, - shapes[i].mesh.material_ids.size()); - assert((shapes[i].mesh.indices.size() % 3) == 0); - for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { - tinyobj::index_t i0 = shapes[i].mesh.indices[3 * f + 0]; - tinyobj::index_t i1 = shapes[i].mesh.indices[3 * f + 1]; - tinyobj::index_t i2 = shapes[i].mesh.indices[3 * f + 2]; - printf(" idx[%ld] = %d/%d/%d, %d/%d/%d, %d/%d/%d. mat_id = %d\n", f, - i0.vertex_index, i0.normal_index, i0.texcoord_index, - i1.vertex_index, i1.normal_index, i1.texcoord_index, - i2.vertex_index, i2.normal_index, i2.texcoord_index, - shapes[i].mesh.material_ids[f]); - } - } else { - for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) { - tinyobj::index_t idx = shapes[i].mesh.indices[f]; - printf(" idx[%ld] = %d/%d/%d\n", f, idx.vertex_index, idx.normal_index, - idx.texcoord_index); - } - - printf("Size of shape[%ld].material_ids: %ld\n", i, - shapes[i].mesh.material_ids.size()); - assert(shapes[i].mesh.material_ids.size() == - shapes[i].mesh.num_face_vertices.size()); - for (size_t m = 0; m < shapes[i].mesh.material_ids.size(); m++) { - printf(" material_id[%ld] = %d\n", m, shapes[i].mesh.material_ids[m]); - } - } - - printf("shape[%ld].num_faces: %ld\n", i, - shapes[i].mesh.num_face_vertices.size()); - for (size_t v = 0; v < shapes[i].mesh.num_face_vertices.size(); v++) { - printf(" num_vertices[%ld] = %ld\n", v, - static_cast(shapes[i].mesh.num_face_vertices[v])); - } - - // printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); - // assert((shapes[i].mesh.positions.size() % 3) == 0); - // for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { - // printf(" v[%ld] = (%f, %f, %f)\n", v, - // static_cast(shapes[i].mesh.positions[3*v+0]), - // static_cast(shapes[i].mesh.positions[3*v+1]), - // static_cast(shapes[i].mesh.positions[3*v+2])); - //} - - printf("shape[%ld].num_tags: %ld\n", i, shapes[i].mesh.tags.size()); - for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { - printf(" tag[%ld] = %s ", t, shapes[i].mesh.tags[t].name.c_str()); - printf(" ints: ["); - for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) { - printf("%ld", static_cast(shapes[i].mesh.tags[t].intValues[j])); - if (j < (shapes[i].mesh.tags[t].intValues.size() - 1)) { - printf(", "); - } - } - printf("]"); - - printf(" floats: ["); - for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) { - printf("%f", static_cast( - shapes[i].mesh.tags[t].floatValues[j])); - if (j < (shapes[i].mesh.tags[t].floatValues.size() - 1)) { - printf(", "); - } - } - printf("]"); - - printf(" strings: ["); - for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) { - printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str()); - if (j < (shapes[i].mesh.tags[t].stringValues.size() - 1)) { - printf(", "); - } - } - printf("]"); - printf("\n"); - } - } - - for (size_t i = 0; i < materials.size(); i++) { - printf("material[%ld].name = %s\n", i, materials[i].name.c_str()); - printf(" material.Ka = (%f, %f ,%f)\n", - static_cast(materials[i].ambient[0]), - static_cast(materials[i].ambient[1]), - static_cast(materials[i].ambient[2])); - printf(" material.Kd = (%f, %f ,%f)\n", - static_cast(materials[i].diffuse[0]), - static_cast(materials[i].diffuse[1]), - static_cast(materials[i].diffuse[2])); - printf(" material.Ks = (%f, %f ,%f)\n", - static_cast(materials[i].specular[0]), - static_cast(materials[i].specular[1]), - static_cast(materials[i].specular[2])); - printf(" material.Tr = (%f, %f ,%f)\n", - static_cast(materials[i].transmittance[0]), - static_cast(materials[i].transmittance[1]), - static_cast(materials[i].transmittance[2])); - printf(" material.Ke = (%f, %f ,%f)\n", - static_cast(materials[i].emission[0]), - static_cast(materials[i].emission[1]), - static_cast(materials[i].emission[2])); - printf(" material.Ns = %f\n", - static_cast(materials[i].shininess)); - printf(" material.Ni = %f\n", static_cast(materials[i].ior)); - printf(" material.dissolve = %f\n", - static_cast(materials[i].dissolve)); - printf(" material.illum = %d\n", materials[i].illum); - printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); - printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); - printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); - printf(" material.map_Ns = %s\n", - materials[i].specular_highlight_texname.c_str()); - printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str()); - printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str()); - printf(" material.disp = %s\n", materials[i].displacement_texname.c_str()); - printf(" material.refl = %s\n", materials[i].reflection_texname.c_str()); - std::map::const_iterator it( - materials[i].unknown_parameter.begin()); - std::map::const_iterator itEnd( - materials[i].unknown_parameter.end()); - - for (; it != itEnd; it++) { - printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); - } - printf("\n"); - } -} - -static bool TestLoadObj(const char* filename, const char* basepath = NULL, - bool triangulate = true) { - std::cout << "Loading " << filename << std::endl; - - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - filename, basepath, triangulate); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - if (!ret) { - printf("Failed to load/parse .obj.\n"); - return false; - } - - PrintInfo(attrib, shapes, materials, triangulate); - - return true; -} - -static bool TestLoadObjFromPreopenedFile(const char* filename, - const char* basepath = NULL, - bool readMaterials = true, - bool triangulate = true) { - std::string fullFilename = std::string(basepath) + filename; - std::cout << "Loading " << fullFilename << std::endl; - - std::ifstream fileStream(fullFilename.c_str()); - - if (!fileStream) { - std::cerr << "Could not find specified file: " << fullFilename << std::endl; - return false; - } - - tinyobj::MaterialStreamReader materialStreamReader(fileStream); - tinyobj::MaterialStreamReader* materialReader = - readMaterials ? &materialStreamReader : NULL; - - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - &fileStream, materialReader); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - if (!ret) { - printf("Failed to load/parse .obj.\n"); - return false; - } - - std::cout << "Loaded material count: " << materials.size() << "\n"; - - return true; -} - -static bool TestStreamLoadObj() { - std::cout << "Stream Loading " << std::endl; - - std::stringstream objStream; - objStream << "mtllib cube.mtl\n" - "\n" - "v 0.000000 2.000000 2.000000\n" - "v 0.000000 0.000000 2.000000\n" - "v 2.000000 0.000000 2.000000\n" - "v 2.000000 2.000000 2.000000\n" - "v 0.000000 2.000000 0.000000\n" - "v 0.000000 0.000000 0.000000\n" - "v 2.000000 0.000000 0.000000\n" - "v 2.000000 2.000000 0.000000\n" - "# 8 vertices\n" - "\n" - "g front cube\n" - "usemtl white\n" - "f 1 2 3 4\n" - "g back cube\n" - "# expects white material\n" - "f 8 7 6 5\n" - "g right cube\n" - "usemtl red\n" - "f 4 3 7 8\n" - "g top cube\n" - "usemtl white\n" - "f 5 1 4 8\n" - "g left cube\n" - "usemtl green\n" - "f 5 6 2 1\n" - "g bottom cube\n" - "usemtl white\n" - "f 2 6 7 3\n" - "# 6 elements"; - - std::string matStream( - "newmtl white\n" - "Ka 0 0 0\n" - "Kd 1 1 1\n" - "Ks 0 0 0\n" - "\n" - "newmtl red\n" - "Ka 0 0 0\n" - "Kd 1 0 0\n" - "Ks 0 0 0\n" - "\n" - "newmtl green\n" - "Ka 0 0 0\n" - "Kd 0 1 0\n" - "Ks 0 0 0\n" - "\n" - "newmtl blue\n" - "Ka 0 0 0\n" - "Kd 0 0 1\n" - "Ks 0 0 0\n" - "\n" - "newmtl light\n" - "Ka 20 20 20\n" - "Kd 1 1 1\n" - "Ks 0 0 0"); - - using namespace tinyobj; - class MaterialStringStreamReader : public MaterialReader { - public: - MaterialStringStreamReader(const std::string& matSStream) - : m_matSStream(matSStream) {} - virtual ~MaterialStringStreamReader() {} - virtual bool operator()(const std::string& matId, - std::vector* materials, - std::map* matMap, - std::string* warn, std::string* err) { - (void)matId; - (void)warn; - (void)err; - std::string warning; - std::string error_msg; - LoadMtl(matMap, materials, &m_matSStream, &warning, &error_msg); - return true; - } - - private: - std::stringstream m_matSStream; - }; - - MaterialStringStreamReader matSSReader(matStream); - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - &objStream, &matSSReader); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - if (!ret) { - return false; - } - - PrintInfo(attrib, shapes, materials); - - return true; -} - -const char* gMtlBasePath = "../models/"; - -void test_cornell_box() { - TEST_CHECK(true == TestLoadObj("../models/cornell_box.obj", gMtlBasePath)); -} - -void test_catmark_torus_creases0() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/catmark_torus_creases0.obj", - gMtlBasePath, /*triangulate*/ false); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(8 == shapes[0].mesh.tags.size()); -} - -void test_pbr() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/pbr-mat-ext.obj", gMtlBasePath, - /*triangulate*/ false); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(FloatEquals(0.2f, materials[0].roughness)); - TEST_CHECK(FloatEquals(0.3f, materials[0].metallic)); - TEST_CHECK(FloatEquals(0.4f, materials[0].sheen)); - TEST_CHECK(FloatEquals(0.5f, materials[0].clearcoat_thickness)); - TEST_CHECK(FloatEquals(0.6f, materials[0].clearcoat_roughness)); - TEST_CHECK(FloatEquals(0.7f, materials[0].anisotropy)); - TEST_CHECK(FloatEquals(0.8f, materials[0].anisotropy_rotation)); - TEST_CHECK(0 == materials[0].roughness_texname.compare("roughness.tex")); - TEST_CHECK(0 == materials[0].metallic_texname.compare("metallic.tex")); - TEST_CHECK(0 == materials[0].sheen_texname.compare("sheen.tex")); - TEST_CHECK(0 == materials[0].emissive_texname.compare("emissive.tex")); - TEST_CHECK(0 == materials[0].normal_texname.compare("normalmap.tex")); -} - -void test_stream_load() { TEST_CHECK(true == TestStreamLoadObj()); } - -void test_stream_load_from_file_skipping_materials() { - TEST_CHECK(true == TestLoadObjFromPreopenedFile( - "../models/pbr-mat-ext.obj", gMtlBasePath, - /*readMaterials*/ false, /*triangulate*/ false)); -} - -void test_stream_load_from_file_with_materials() { - TEST_CHECK(true == TestLoadObjFromPreopenedFile( - "../models/pbr-mat-ext.obj", gMtlBasePath, - /*readMaterials*/ true, /*triangulate*/ false)); -} - -void test_trailing_whitespace_in_mtl_issue92() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/issue-92.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(0 == materials[0].diffuse_texname.compare("tmp.png")); -} - -void test_transmittance_filter_issue95() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/issue-95.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(FloatEquals(0.1f, materials[0].transmittance[0])); - TEST_CHECK(FloatEquals(0.2f, materials[0].transmittance[1])); - TEST_CHECK(FloatEquals(0.3f, materials[0].transmittance[2])); -} - -void test_transmittance_filter_Tf_issue95() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/issue-95-2.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(FloatEquals(0.1f, materials[0].transmittance[0])); - TEST_CHECK(FloatEquals(0.2f, materials[0].transmittance[1])); - TEST_CHECK(FloatEquals(0.3f, materials[0].transmittance[2])); -} - -void test_transmittance_filter_Kt_issue95() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/issue-95.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(FloatEquals(0.1f, materials[0].transmittance[0])); - TEST_CHECK(FloatEquals(0.2f, materials[0].transmittance[1])); - TEST_CHECK(FloatEquals(0.3f, materials[0].transmittance[2])); -} - -void test_usemtl_at_last_line_issue104() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/usemtl-issue-104.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); -} - -void test_texture_opts_issue85() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = - tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/texture-options-issue-85.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(3 == materials.size()); - TEST_CHECK(0 == materials[0].name.compare("default")); - TEST_CHECK(0 == materials[1].name.compare("bm2")); - TEST_CHECK(0 == materials[2].name.compare("bm3")); - TEST_CHECK(true == materials[0].ambient_texopt.clamp); - TEST_CHECK(FloatEquals(0.1f, materials[0].diffuse_texopt.origin_offset[0])); - TEST_CHECK(FloatEquals(0.0f, materials[0].diffuse_texopt.origin_offset[1])); - TEST_CHECK(FloatEquals(0.0f, materials[0].diffuse_texopt.origin_offset[2])); - TEST_CHECK(FloatEquals(0.1f, materials[0].specular_texopt.scale[0])); - TEST_CHECK(FloatEquals(0.2f, materials[0].specular_texopt.scale[1])); - TEST_CHECK(FloatEquals(1.0f, materials[0].specular_texopt.scale[2])); - TEST_CHECK( - FloatEquals(0.1f, materials[0].specular_highlight_texopt.turbulence[0])); - TEST_CHECK( - FloatEquals(0.2f, materials[0].specular_highlight_texopt.turbulence[1])); - TEST_CHECK( - FloatEquals(0.3f, materials[0].specular_highlight_texopt.turbulence[2])); - TEST_CHECK(FloatEquals(3.0f, materials[0].bump_texopt.bump_multiplier)); - - TEST_CHECK( - FloatEquals(0.1f, materials[1].specular_highlight_texopt.brightness)); - TEST_CHECK( - FloatEquals(0.3f, materials[1].specular_highlight_texopt.contrast)); - TEST_CHECK('r' == materials[1].bump_texopt.imfchan); - - TEST_CHECK(tinyobj::TEXTURE_TYPE_SPHERE == materials[2].diffuse_texopt.type); - TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_TOP == - materials[2].specular_texopt.type); - TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_BOTTOM == - materials[2].specular_highlight_texopt.type); - TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_LEFT == - materials[2].ambient_texopt.type); - TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_RIGHT == - materials[2].alpha_texopt.type); - TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_FRONT == materials[2].bump_texopt.type); - TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_BACK == - materials[2].displacement_texopt.type); -} - -void test_mtllib_multiple_filenames_issue112() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/mtllib-multiple-files-issue-112.obj", - gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == materials.size()); -} - -void test_tr_and_d_issue43() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/tr-and-d-issue-43.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(2 == materials.size()); - - TEST_CHECK(FloatEquals(0.75f, materials[0].dissolve)); - TEST_CHECK(FloatEquals(0.75f, materials[1].dissolve)); -} - -void test_refl() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/refl.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - PrintInfo(attrib, shapes, materials); - - TEST_CHECK(true == ret); - TEST_CHECK(5 == materials.size()); - - TEST_CHECK(materials[0].reflection_texname.compare("reflection.tga") == 0); -} - -void test_map_Bump() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/map-bump.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - PrintInfo(attrib, shapes, materials); - - TEST_CHECK(true == ret); - TEST_CHECK(2 == materials.size()); - - TEST_CHECK(materials[0].bump_texname.compare("bump.jpg") == 0); -} - -void test_g_ignored_issue138() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/issue-138.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - PrintInfo(attrib, shapes, materials); - - TEST_CHECK(true == ret); - TEST_CHECK(2 == shapes.size()); - TEST_CHECK(2 == materials.size()); -} - -void test_vertex_col_ext_issue144() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/cube-vertexcol.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - // PrintInfo(attrib, shapes, materials); - - TEST_CHECK(true == ret); - TEST_CHECK((8 * 3) == attrib.colors.size()); - - TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 0 + 0])); - TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 0 + 1])); - TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 0 + 2])); - - TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 1 + 0])); - TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 1 + 1])); - TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 1 + 2])); - - TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 4 + 0])); - - TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 7 + 0])); - TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 7 + 1])); - TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 7 + 2])); -} - -void test_norm_texopts() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/norm-texopt.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(FloatEquals(3.0f, materials[0].normal_texopt.bump_multiplier)); -} - -void test_zero_face_idx_value_issue140() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = - tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/issue-140-zero-face-idx.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(false == ret); - TEST_CHECK(!err.empty()); -} - -void test_invalid_relative_vertex_index() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = - tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/invalid-relative-vertex-index.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(false == ret); - TEST_CHECK(!err.empty()); -} - -void test_invalid_texture_vertex_index() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = - tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/invalid-relative-texture-vertex-index.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(false == ret); - TEST_CHECK(!err.empty()); -} - -void test_texture_name_whitespace_issue145() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/texture-filename-with-whitespace.obj", - gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(err.empty()); - TEST_CHECK(2 < materials.size()); - - TEST_CHECK(0 == materials[0].diffuse_texname.compare("texture 01.png")); - TEST_CHECK(0 == materials[1].bump_texname.compare("bump 01.png")); - TEST_CHECK(FloatEquals(2.0f, materials[1].bump_texopt.bump_multiplier)); -} - -void test_smoothing_group_issue162() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = - tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/issue-162-smoothing-group.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(2 == shapes.size()); - - TEST_CHECK(2 == shapes[0].mesh.smoothing_group_ids.size()); - TEST_CHECK(1 == shapes[0].mesh.smoothing_group_ids[0]); - TEST_CHECK(1 == shapes[0].mesh.smoothing_group_ids[1]); - - TEST_CHECK(10 == shapes[1].mesh.smoothing_group_ids.size()); - TEST_CHECK(0 == shapes[1].mesh.smoothing_group_ids[0]); - TEST_CHECK(0 == shapes[1].mesh.smoothing_group_ids[1]); - TEST_CHECK(3 == shapes[1].mesh.smoothing_group_ids[2]); - TEST_CHECK(3 == shapes[1].mesh.smoothing_group_ids[3]); - TEST_CHECK(4 == shapes[1].mesh.smoothing_group_ids[4]); - TEST_CHECK(4 == shapes[1].mesh.smoothing_group_ids[5]); - TEST_CHECK(0 == shapes[1].mesh.smoothing_group_ids[6]); - TEST_CHECK(0 == shapes[1].mesh.smoothing_group_ids[7]); - TEST_CHECK(6 == shapes[1].mesh.smoothing_group_ids[8]); - TEST_CHECK(6 == shapes[1].mesh.smoothing_group_ids[9]); -} - -void test_invalid_face_definition() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = - tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/invalid-face-definition.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(0 == shapes[0].mesh.indices.size()); -} - -void test_Empty_mtl_basedir_issue177() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - - // A case where the user explicitly provides an empty string - // Win32 specific? - const char* userBaseDir = ""; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "issue-177.obj", userBaseDir); - - // if mtl loading fails, we get an warning message here - ret &= warn.empty(); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); -} - -void test_line_primitive() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/line-prim.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(8 == shapes[0].lines.indices.size()); - TEST_CHECK(2 == shapes[0].lines.num_line_vertices.size()); -} - -void test_points_primitive() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/points-prim.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(8 == shapes[0].points.indices.size()); -} - -void test_multiple_group_names() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/cube.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(6 == shapes.size()); - TEST_CHECK(0 == shapes[0].name.compare("front cube")); - TEST_CHECK(0 == shapes[1].name.compare("back cube")); // multiple whitespaces - // are aggregated as - // single white space. -} - -void test_initialize_all_texopts() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/cornell_box.obj", gMtlBasePath, false); - - TEST_CHECK(ret == true); - TEST_CHECK(0 < materials.size()); - -#define TEST_CHECK_DEFAULT_TEXOPT(texopt) \ - TEST_CHECK(tinyobj::TEXTURE_TYPE_NONE == texopt.type); \ - TEST_CHECK(0.0 == texopt.brightness); \ - TEST_CHECK(1.0 == texopt.contrast); \ - TEST_CHECK(false == texopt.clamp); \ - TEST_CHECK(true == texopt.blendu); \ - TEST_CHECK(true == texopt.blendv); \ - TEST_CHECK(1.0 == texopt.bump_multiplier); \ - for (int j = 0; j < 3; j++) { \ - TEST_CHECK(0.0 == texopt.origin_offset[j]); \ - TEST_CHECK(1.0 == texopt.scale[j]); \ - TEST_CHECK(0.0 == texopt.turbulence[j]); \ - } - for (size_t i = 0; i < materials.size(); i++) { - TEST_CHECK_DEFAULT_TEXOPT(materials[i].ambient_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].diffuse_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].specular_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].specular_highlight_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].bump_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].displacement_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].alpha_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].reflection_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].roughness_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].metallic_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].sheen_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].emissive_texopt); - TEST_CHECK_DEFAULT_TEXOPT(materials[i].normal_texopt); - } -#undef TEST_CHECK_DEFAULT_TEXOPT -} - -void test_colorspace_issue184() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = - tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/colorspace-issue-184.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(0 == materials[0].diffuse_texopt.colorspace.compare("sRGB")); - TEST_CHECK(0 == materials[0].specular_texopt.colorspace.size()); - TEST_CHECK(0 == materials[0].bump_texopt.colorspace.compare("linear")); -} - -void test_leading_decimal_dots_issue201() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/leading-decimal-dot-issue-201.obj", - gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(FloatEquals(0.8e-1f, attrib.vertices[0])); - TEST_CHECK(FloatEquals(-.7e+2f, attrib.vertices[1])); - TEST_CHECK(FloatEquals(.575869f, attrib.vertices[3])); - TEST_CHECK(FloatEquals(-.666304f, attrib.vertices[4])); - TEST_CHECK(FloatEquals(.940448f, attrib.vertices[6])); -} - -void test_mtl_default_search_path_v2_API_issue208() { - tinyobj::ObjReader reader; - - bool ret = reader.ParseFromFile("../models/cornell_box.obj"); - - std::cout << "WARN: " << reader.Warning() << "\n"; - - TEST_CHECK(ret == true); - TEST_CHECK(reader.Warning().empty()); -} - -void test_leading_zero_in_exponent_notation_issue210() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj( - &attrib, &shapes, &materials, &warn, &err, - "../models/leading-zero-in-exponent-notation-issue-210.obj", - gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(FloatEquals(0.8e-001f, attrib.vertices[0])); - TEST_CHECK(FloatEquals(-.7e+02f, attrib.vertices[1])); - - std::cout << "exp " << 0.8e-01 << std::endl; - std::cout << "bora " << attrib.vertices[0] << std::endl; -} - -void test_usemtl_then_o_issue235() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj( - &attrib, &shapes, &materials, &warn, &err, - "../models/issue-235-usemtl-then-o.obj", - gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(2 == shapes.size()); - TEST_CHECK(2 == materials.size()); - TEST_CHECK(4 == shapes[1].mesh.indices[0].vertex_index); -} - -void test_mtl_searchpaths_issue244() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - // .mtl is located at ./assets/issue-244.mtl -#if _WIN32 - std::string search_paths("../;../models;./assets"); -#else - std::string search_paths("../:../models:./assets"); -#endif - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj( - &attrib, &shapes, &materials, &warn, &err, - "../models/issue-244-mtl-searchpaths.obj", - search_paths.c_str()); - - TEST_CHECK(warn.empty()); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(2 == shapes.size()); - TEST_CHECK(2 == materials.size()); - TEST_CHECK(4 == shapes[1].mesh.indices[0].vertex_index); -} - -void test_usemtl_whitespace_issue246() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj( - &attrib, &shapes, &materials, &warn, &err, - "../models/issue-246-usemtl-whitespace.obj", - gMtlBasePath); - - TEST_CHECK(warn.empty()); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - TEST_CHECK(1 == materials.size()); - TEST_CHECK(0 == shapes[0].mesh.material_ids[0]); -} - -void test_texres_texopt_issue248() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj( - &attrib, &shapes, &materials, &warn, &err, - "../models/issue-248-texres-texopt.obj", - gMtlBasePath); - - TEST_CHECK(warn.empty()); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 < materials.size()); - TEST_CHECK(512 == materials[0].diffuse_texopt.texture_resolution); - TEST_CHECK("input.jpg" == materials[0].diffuse_texname); -} - -void test_mtl_filename_with_whitespace_issue46() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = - tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/mtl filename with whitespace issue46.obj", - gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - TEST_CHECK(true == ret); - TEST_CHECK(1 == materials.size()); - TEST_CHECK("green" == materials[0].name); -} - -void test_face_missing_issue295() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj( - &attrib, &shapes, &materials, &warn, &err, - "../models/issue-295-trianguation-failure.obj", - gMtlBasePath, /* triangualte */true); - - TEST_CHECK(warn.empty()); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(1 == shapes.size()); - - // 14 quad faces are triangulated into 28 triangles. - TEST_CHECK(28 == shapes[0].mesh.num_face_vertices.size()); - TEST_CHECK(28 == shapes[0].mesh.smoothing_group_ids.size()); - TEST_CHECK(28 == shapes[0].mesh.material_ids.size()); - TEST_CHECK((3 * 28) == shapes[0].mesh.indices.size()); // 28 triangle faces x 3 -} - -void test_comment_issue389() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj( - &attrib, &shapes, &materials, &warn, &err, - "../models/issue-389-comment.obj", - gMtlBasePath, /* triangualte */false); - - TEST_CHECK(warn.empty()); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); -} - -void test_default_kd_for_multiple_materials_issue391() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/issue-391.obj", gMtlBasePath); - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - const tinyobj::real_t kGrey[] = {0.6, 0.6, 0.6}; - const tinyobj::real_t kRed[] = {1.0, 0.0, 0.0}; - - TEST_CHECK(true == ret); - TEST_CHECK(2 == materials.size()); - for (size_t i = 0; i < materials.size(); ++i) { - const tinyobj::material_t& material = materials[i]; - if (material.name == "has_map") { - for (int i = 0; i < 3; ++i) TEST_CHECK(material.diffuse[i] == kGrey[i]); - } else if (material.name == "has_kd") { - for (int i = 0; i < 3; ++i) TEST_CHECK(material.diffuse[i] == kRed[i]); - } else { - std::cerr << "Unexpected material found!" << std::endl; - TEST_CHECK(false); - } - } -} - -void test_removeUtf8Bom() { - // Basic input with BOM - std::string withBOM = "\xEF\xBB\xBFhello world"; - TEST_CHECK(tinyobj::removeUtf8Bom(withBOM) == "hello world"); - - // Input without BOM - std::string noBOM = "hello world"; - TEST_CHECK(tinyobj::removeUtf8Bom(noBOM) == "hello world"); - - // Leaves short string unchanged - std::string shortStr = "\xEF"; - TEST_CHECK(tinyobj::removeUtf8Bom(shortStr) == shortStr); - - std::string shortStr2 = "\xEF\xBB"; - TEST_CHECK(tinyobj::removeUtf8Bom(shortStr2) == shortStr2); - - // BOM only returns empty string - std::string justBom = "\xEF\xBB\xBF"; - TEST_CHECK(tinyobj::removeUtf8Bom(justBom) == ""); - - // Empty string - std::string emptyStr = ""; - TEST_CHECK(tinyobj::removeUtf8Bom(emptyStr) == ""); -} - -void test_loadObj_with_BOM() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string warn; - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, - "../models/cube_w_BOM.obj", gMtlBasePath); - - if (!warn.empty()) { - std::cout << "WARN: " << warn << std::endl; - } - - if (!err.empty()) { - std::cerr << "ERR: " << err << std::endl; - } - - TEST_CHECK(true == ret); - TEST_CHECK(6 == shapes.size()); - TEST_CHECK(0 == shapes[0].name.compare("front cube")); - TEST_CHECK(0 == shapes[1].name.compare("back cube")); // multiple whitespaces - // are aggregated as - // single white space. -} - - -// Fuzzer test. -// Just check if it does not crash. -// Disable by default since Windows filesystem can't create filename of afl -// testdata -#if 0 - -void test_afl000000", "[AFL]() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "./afl/id:000000,sig:11,src:000000,op:havoc,rep:128", gMtlBasePath); - - TEST_CHECK(true == ret); -} - -void test_afl000001", "[AFL]() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - - std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "./afl/id:000001,sig:11,src:000000,op:havoc,rep:64", gMtlBasePath); - - TEST_CHECK(true == ret); -} -#endif - -#if 0 -int -main( - int argc, - char **argv) -{ - if (argc > 1) { - const char* basepath = NULL; - if (argc > 2) { - basepath = argv[2]; - } - assert(true == TestLoadObj(argv[1], basepath)); - } else { - //assert(true == TestLoadObj("cornell_box.obj")); - //assert(true == TestLoadObj("cube.obj")); - assert(true == TestStreamLoadObj()); - assert(true == TestLoadObj("catmark_torus_creases0.obj", NULL, false)); - } - - return 0; -} -#endif - -TEST_LIST = { - {"cornell_box", test_cornell_box}, - {"catmark_torus_creases0", test_catmark_torus_creases0}, - {"pbr", test_pbr}, - {"stream_load", test_stream_load}, - {"stream_load_from_file_skipping_materials", - test_stream_load_from_file_skipping_materials}, - {"stream_load_from_file_with_materials", - test_stream_load_from_file_with_materials}, - {"trailing_whitespace_in_mtl_issue92", - test_trailing_whitespace_in_mtl_issue92}, - {"transmittance_filter_issue95", test_transmittance_filter_issue95}, - {"transmittance_filter_Tf_issue95", test_transmittance_filter_Tf_issue95}, - {"transmittance_filter_Kt_issue95", test_transmittance_filter_Kt_issue95}, - {"usemtl_at_last_line_issue104", test_usemtl_at_last_line_issue104}, - {"texture_opts_issue85", test_texture_opts_issue85}, - {"mtllib_multiple_filenames_issue112", - test_mtllib_multiple_filenames_issue112}, - {"tr_and_d_issue43", test_tr_and_d_issue43}, - {"refl", test_refl}, - {"map_bump", test_map_Bump}, - {"g_ignored_issue138", test_g_ignored_issue138}, - {"vertex_col_ext_issue144", test_vertex_col_ext_issue144}, - {"norm_texopts", test_norm_texopts}, - {"zero_face_idx_value_issue140", test_zero_face_idx_value_issue140}, - {"texture_name_whitespace_issue145", test_texture_name_whitespace_issue145}, - {"smoothing_group_issue162", test_smoothing_group_issue162}, - {"invalid_face_definition", test_invalid_face_definition}, - {"Empty_mtl_basedir_issue177", test_Empty_mtl_basedir_issue177}, - {"line_primitive", test_line_primitive}, - {"points_primitive", test_points_primitive}, - {"multiple_group_names", test_multiple_group_names}, - {"initialize_all_texopts", test_initialize_all_texopts}, - {"colorspace_issue184", test_colorspace_issue184}, - {"leading_decimal_dots_issue201", test_leading_decimal_dots_issue201}, - {"mtl_default_search_path_v2_API_issue208", - test_mtl_default_search_path_v2_API_issue208}, - {"leading_zero_in_exponent_notation_issue210", - test_leading_zero_in_exponent_notation_issue210}, - {"usemtl_then_o_issue235", - test_usemtl_then_o_issue235}, - {"mtl_searchpaths_issue244", - test_mtl_searchpaths_issue244}, - {"usemtl_whitespece_issue246", - test_usemtl_whitespace_issue246}, - {"texres_texopt_issue248", - test_texres_texopt_issue248}, - {"test_mtl_filename_with_whitespace_issue46", - test_mtl_filename_with_whitespace_issue46}, - {"test_face_missing_issue295", - test_face_missing_issue295}, - {"test_comment_issue389", - test_comment_issue389}, - {"test_invalid_relative_vertex_index", - test_invalid_relative_vertex_index}, - {"test_invalid_texture_vertex_index", - test_invalid_texture_vertex_index}, - {"default_kd_for_multiple_materials_issue391", - test_default_kd_for_multiple_materials_issue391}, - {"test_removeUtf8Bom", test_removeUtf8Bom}, - {"test_loadObj_with_BOM", test_loadObj_with_BOM}, - {NULL, NULL}}; diff --git a/tests/vcbuild.bat b/tests/vcbuild.bat deleted file mode 100644 index e8646735..00000000 --- a/tests/vcbuild.bat +++ /dev/null @@ -1,4 +0,0 @@ -chcp 437 -python kuroga.py config-msvc.py -call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64 -ninja diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc deleted file mode 100644 index e57d0444..00000000 --- a/tiny_obj_loader.cc +++ /dev/null @@ -1,2 +0,0 @@ -#define TINYOBJLOADER_IMPLEMENTATION -#include "tiny_obj_loader.h" diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h deleted file mode 100644 index a927864e..00000000 --- a/tiny_obj_loader.h +++ /dev/null @@ -1,3517 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2012-Present, Syoyo Fujita and many contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -// -// version 2.0.0 : Add new object oriented API. 1.x API is still provided. -// * Add python binding. -// * Support line primitive. -// * Support points primitive. -// * Support multiple search path for .mtl(v1 API). -// * Support vertex skinning weight `vw`(as an tinyobj -// extension). Note that this differs vertex weight([w] -// component in `v` line) -// * Support escaped whitespece in mtllib -// * Add robust triangulation using Mapbox -// earcut(TINYOBJLOADER_USE_MAPBOX_EARCUT). -// version 1.4.0 : Modifed ParseTextureNameAndOption API -// version 1.3.1 : Make ParseTextureNameAndOption API public -// version 1.3.0 : Separate warning and error message(breaking API of LoadObj) -// version 1.2.3 : Added color space extension('-colorspace') to tex opts. -// version 1.2.2 : Parse multiple group names. -// version 1.2.1 : Added initial support for line('l') primitive(PR #178) -// version 1.2.0 : Hardened implementation(#175) -// version 1.1.1 : Support smoothing groups(#162) -// version 1.1.0 : Support parsing vertex color(#144) -// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) -// version 1.0.7 : Support multiple tex options(#126) -// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) -// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) -// version 1.0.4 : Support multiple filenames for 'mtllib'(#112) -// version 1.0.3 : Support parsing texture options(#85) -// version 1.0.2 : Improve parsing speed by about a factor of 2 for large -// files(#105) -// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) -// version 1.0.0 : Change data structure. Change license from BSD to MIT. -// - -// -// Use this in *one* .cc -// #define TINYOBJLOADER_IMPLEMENTATION -// #include "tiny_obj_loader.h" -// - -#ifndef TINY_OBJ_LOADER_H_ -#define TINY_OBJ_LOADER_H_ - -#include -#include -#include - -namespace tinyobj { - -// TODO(syoyo): Better C++11 detection for older compiler -#if __cplusplus > 199711L -#define TINYOBJ_OVERRIDE override -#else -#define TINYOBJ_OVERRIDE -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif - -#pragma clang diagnostic ignored "-Wpadded" - -#endif - -// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... -// -// -blendu on | off # set horizontal texture blending -// (default on) -// -blendv on | off # set vertical texture blending -// (default on) -// -boost real_value # boost mip-map sharpness -// -mm base_value gain_value # modify texture map values (default -// 0 1) -// # base_value = brightness, -// gain_value = contrast -// -o u [v [w]] # Origin offset (default -// 0 0 0) -// -s u [v [w]] # Scale (default -// 1 1 1) -// -t u [v [w]] # Turbulence (default -// 0 0 0) -// -texres resolution # texture resolution to create -// -clamp on | off # only render texels in the clamped -// 0-1 range (default off) -// # When unclamped, textures are -// repeated across a surface, -// # when clamped, only texels which -// fall within the 0-1 -// # range are rendered. -// -bm mult_value # bump multiplier (for bump maps -// only) -// -// -imfchan r | g | b | m | l | z # specifies which channel of the file -// is used to -// # create a scalar or bump texture. -// r:red, g:green, -// # b:blue, m:matte, l:luminance, -// z:z-depth.. -// # (the default for bump is 'l' and -// for decal is 'm') -// bump -imfchan r bumpmap.tga # says to use the red channel of -// bumpmap.tga as the bumpmap -// -// For reflection maps... -// -// -type sphere # specifies a sphere for a "refl" -// reflection map -// -type cube_top | cube_bottom | # when using a cube map, the texture -// file for each -// cube_front | cube_back | # side of the cube is specified -// separately -// cube_left | cube_right -// -// TinyObjLoader extension. -// -// -colorspace SPACE # Color space of the texture. e.g. -// 'sRGB` or 'linear' -// - -#ifdef TINYOBJLOADER_USE_DOUBLE -//#pragma message "using double" -typedef double real_t; -#else -//#pragma message "using float" -typedef float real_t; -#endif - -typedef enum { - TEXTURE_TYPE_NONE, // default - TEXTURE_TYPE_SPHERE, - TEXTURE_TYPE_CUBE_TOP, - TEXTURE_TYPE_CUBE_BOTTOM, - TEXTURE_TYPE_CUBE_FRONT, - TEXTURE_TYPE_CUBE_BACK, - TEXTURE_TYPE_CUBE_LEFT, - TEXTURE_TYPE_CUBE_RIGHT -} texture_type_t; - -struct texture_option_t { - texture_type_t type; // -type (default TEXTURE_TYPE_NONE) - real_t sharpness; // -boost (default 1.0?) - real_t brightness; // base_value in -mm option (default 0) - real_t contrast; // gain_value in -mm option (default 1) - real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) - real_t scale[3]; // -s u [v [w]] (default 1 1 1) - real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) - int texture_resolution; // -texres resolution (No default value in the spec. - // We'll use -1) - bool clamp; // -clamp (default false) - char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') - bool blendu; // -blendu (default on) - bool blendv; // -blendv (default on) - real_t bump_multiplier; // -bm (for bump maps only, default 1.0) - - // extension - std::string colorspace; // Explicitly specify color space of stored texel - // value. Usually `sRGB` or `linear` (default empty). -}; - -struct material_t { - std::string name; - - real_t ambient[3]; - real_t diffuse[3]; - real_t specular[3]; - real_t transmittance[3]; - real_t emission[3]; - real_t shininess; - real_t ior; // index of refraction - real_t dissolve; // 1 == opaque; 0 == fully transparent - // illumination model (see http://www.fileformat.info/format/material/) - int illum; - - int dummy; // Suppress padding warning. - - std::string ambient_texname; // map_Ka. For ambient or ambient occlusion. - std::string diffuse_texname; // map_Kd - std::string specular_texname; // map_Ks - std::string specular_highlight_texname; // map_Ns - std::string bump_texname; // map_bump, map_Bump, bump - std::string displacement_texname; // disp - std::string alpha_texname; // map_d - std::string reflection_texname; // refl - - texture_option_t ambient_texopt; - texture_option_t diffuse_texopt; - texture_option_t specular_texopt; - texture_option_t specular_highlight_texopt; - texture_option_t bump_texopt; - texture_option_t displacement_texopt; - texture_option_t alpha_texopt; - texture_option_t reflection_texopt; - - // PBR extension - // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr - real_t roughness; // [0, 1] default 0 - real_t metallic; // [0, 1] default 0 - real_t sheen; // [0, 1] default 0 - real_t clearcoat_thickness; // [0, 1] default 0 - real_t clearcoat_roughness; // [0, 1] default 0 - real_t anisotropy; // aniso. [0, 1] default 0 - real_t anisotropy_rotation; // anisor. [0, 1] default 0 - real_t pad0; - std::string roughness_texname; // map_Pr - std::string metallic_texname; // map_Pm - std::string sheen_texname; // map_Ps - std::string emissive_texname; // map_Ke - std::string normal_texname; // norm. For normal mapping. - - texture_option_t roughness_texopt; - texture_option_t metallic_texopt; - texture_option_t sheen_texopt; - texture_option_t emissive_texopt; - texture_option_t normal_texopt; - - int pad2; - - std::map unknown_parameter; - -#ifdef TINY_OBJ_LOADER_PYTHON_BINDING - // For pybind11 - std::array GetDiffuse() { - std::array values; - values[0] = double(diffuse[0]); - values[1] = double(diffuse[1]); - values[2] = double(diffuse[2]); - - return values; - } - - std::array GetSpecular() { - std::array values; - values[0] = double(specular[0]); - values[1] = double(specular[1]); - values[2] = double(specular[2]); - - return values; - } - - std::array GetTransmittance() { - std::array values; - values[0] = double(transmittance[0]); - values[1] = double(transmittance[1]); - values[2] = double(transmittance[2]); - - return values; - } - - std::array GetEmission() { - std::array values; - values[0] = double(emission[0]); - values[1] = double(emission[1]); - values[2] = double(emission[2]); - - return values; - } - - std::array GetAmbient() { - std::array values; - values[0] = double(ambient[0]); - values[1] = double(ambient[1]); - values[2] = double(ambient[2]); - - return values; - } - - void SetDiffuse(std::array &a) { - diffuse[0] = real_t(a[0]); - diffuse[1] = real_t(a[1]); - diffuse[2] = real_t(a[2]); - } - - void SetAmbient(std::array &a) { - ambient[0] = real_t(a[0]); - ambient[1] = real_t(a[1]); - ambient[2] = real_t(a[2]); - } - - void SetSpecular(std::array &a) { - specular[0] = real_t(a[0]); - specular[1] = real_t(a[1]); - specular[2] = real_t(a[2]); - } - - void SetTransmittance(std::array &a) { - transmittance[0] = real_t(a[0]); - transmittance[1] = real_t(a[1]); - transmittance[2] = real_t(a[2]); - } - - std::string GetCustomParameter(const std::string &key) { - std::map::const_iterator it = - unknown_parameter.find(key); - - if (it != unknown_parameter.end()) { - return it->second; - } - return std::string(); - } - -#endif -}; - -struct tag_t { - std::string name; - - std::vector intValues; - std::vector floatValues; - std::vector stringValues; -}; - -struct joint_and_weight_t { - int joint_id; - real_t weight; -}; - -struct skin_weight_t { - int vertex_id; // Corresponding vertex index in `attrib_t::vertices`. - // Compared to `index_t`, this index must be positive and - // start with 0(does not allow relative indexing) - std::vector weightValues; -}; - -// Index struct to support different indices for vtx/normal/texcoord. -// -1 means not used. -struct index_t { - int vertex_index; - int normal_index; - int texcoord_index; -}; - -struct mesh_t { - std::vector indices; - std::vector - num_face_vertices; // The number of vertices per - // face. 3 = triangle, 4 = quad, ... - std::vector material_ids; // per-face material ID - std::vector smoothing_group_ids; // per-face smoothing group - // ID(0 = off. positive value - // = group id) - std::vector tags; // SubD tag -}; - -// struct path_t { -// std::vector indices; // pairs of indices for lines -//}; - -struct lines_t { - // Linear flattened indices. - std::vector indices; // indices for vertices(poly lines) - std::vector num_line_vertices; // The number of vertices per line. -}; - -struct points_t { - std::vector indices; // indices for points -}; - -struct shape_t { - std::string name; - mesh_t mesh; - lines_t lines; - points_t points; -}; - -// Vertex attributes -struct attrib_t { - std::vector vertices; // 'v'(xyz) - - // For backward compatibility, we store vertex weight in separate array. - std::vector vertex_weights; // 'v'(w) - std::vector normals; // 'vn' - std::vector texcoords; // 'vt'(uv) - - // For backward compatibility, we store texture coordinate 'w' in separate - // array. - std::vector texcoord_ws; // 'vt'(w) - std::vector colors; // extension: vertex colors - - // - // TinyObj extension. - // - - // NOTE(syoyo): array index is based on the appearance order. - // To get a corresponding skin weight for a specific vertex id `vid`, - // Need to reconstruct a look up table: `skin_weight_t::vertex_id` == `vid` - // (e.g. using std::map, std::unordered_map) - std::vector skin_weights; - - attrib_t() {} - - // - // For pybind11 - // - const std::vector &GetVertices() const { return vertices; } - - const std::vector &GetVertexWeights() const { return vertex_weights; } -}; - -struct callback_t { - // W is optional and set to 1 if there is no `w` item in `v` line - void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); - void (*vertex_color_cb)(void *user_data, real_t x, real_t y, real_t z, - real_t r, real_t g, real_t b, bool has_color); - void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); - - // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in - // `vt` line. - void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); - - // called per 'f' line. num_indices is the number of face indices(e.g. 3 for - // triangle, 4 for quad) - // 0 will be passed for undefined index in index_t members. - void (*index_cb)(void *user_data, index_t *indices, int num_indices); - // `name` material name, `material_id` = the array index of material_t[]. -1 - // if - // a material not found in .mtl - void (*usemtl_cb)(void *user_data, const char *name, int material_id); - // `materials` = parsed material data. - void (*mtllib_cb)(void *user_data, const material_t *materials, - int num_materials); - // There may be multiple group names - void (*group_cb)(void *user_data, const char **names, int num_names); - void (*object_cb)(void *user_data, const char *name); - - callback_t() - : vertex_cb(NULL), - vertex_color_cb(NULL), - normal_cb(NULL), - texcoord_cb(NULL), - index_cb(NULL), - usemtl_cb(NULL), - mtllib_cb(NULL), - group_cb(NULL), - object_cb(NULL) {} -}; - -class MaterialReader { - public: - MaterialReader() {} - virtual ~MaterialReader(); - - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, std::string *warn, - std::string *err) = 0; -}; - -/// -/// Read .mtl from a file. -/// -class MaterialFileReader : public MaterialReader { - public: - // Path could contain separator(';' in Windows, ':' in Posix) - explicit MaterialFileReader(const std::string &mtl_basedir) - : m_mtlBaseDir(mtl_basedir) {} - virtual ~MaterialFileReader() TINYOBJ_OVERRIDE {} - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, std::string *warn, - std::string *err) TINYOBJ_OVERRIDE; - - private: - std::string m_mtlBaseDir; -}; - -/// -/// Read .mtl from a stream. -/// -class MaterialStreamReader : public MaterialReader { - public: - explicit MaterialStreamReader(std::istream &inStream) - : m_inStream(inStream) {} - virtual ~MaterialStreamReader() TINYOBJ_OVERRIDE {} - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, std::string *warn, - std::string *err) TINYOBJ_OVERRIDE; - - private: - std::istream &m_inStream; -}; - -// v2 API -struct ObjReaderConfig { - bool triangulate; // triangulate polygon? - - // Currently not used. - // "simple" or empty: Create triangle fan - // "earcut": Use the algorithm based on Ear clipping - std::string triangulation_method; - - /// Parse vertex color. - /// If vertex color is not present, its filled with default value. - /// false = no vertex color - /// This will increase memory of parsed .obj - bool vertex_color; - - /// - /// Search path to .mtl file. - /// Default = "" = search from the same directory of .obj file. - /// Valid only when loading .obj from a file. - /// - std::string mtl_search_path; - - ObjReaderConfig() - : triangulate(true), triangulation_method("simple"), vertex_color(true) {} -}; - -/// -/// Wavefront .obj reader class(v2 API) -/// -class ObjReader { - public: - ObjReader() : valid_(false) {} - - /// - /// Load .obj and .mtl from a file. - /// - /// @param[in] filename wavefront .obj filename - /// @param[in] config Reader configuration - /// - bool ParseFromFile(const std::string &filename, - const ObjReaderConfig &config = ObjReaderConfig()); - - /// - /// Parse .obj from a text string. - /// Need to supply .mtl text string by `mtl_text`. - /// This function ignores `mtllib` line in .obj text. - /// - /// @param[in] obj_text wavefront .obj filename - /// @param[in] mtl_text wavefront .mtl filename - /// @param[in] config Reader configuration - /// - bool ParseFromString(const std::string &obj_text, const std::string &mtl_text, - const ObjReaderConfig &config = ObjReaderConfig()); - - /// - /// .obj was loaded or parsed correctly. - /// - bool Valid() const { return valid_; } - - const attrib_t &GetAttrib() const { return attrib_; } - - const std::vector &GetShapes() const { return shapes_; } - - const std::vector &GetMaterials() const { return materials_; } - - /// - /// Warning message(may be filled after `Load` or `Parse`) - /// - const std::string &Warning() const { return warning_; } - - /// - /// Error message(filled when `Load` or `Parse` failed) - /// - const std::string &Error() const { return error_; } - - private: - bool valid_; - - attrib_t attrib_; - std::vector shapes_; - std::vector materials_; - - std::string warning_; - std::string error_; -}; - -/// ==>>========= Legacy v1 API ============================================= - -/// Loads .obj from a file. -/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data -/// 'shapes' will be filled with parsed shape data -/// Returns true when loading .obj become success. -/// Returns warning message into `warn`, and error message into `err` -/// 'mtl_basedir' is optional, and used for base directory for .mtl file. -/// In default(`NULL'), .mtl file is searched from an application's working -/// directory. -/// 'triangulate' is optional, and used whether triangulate polygon face in .obj -/// or not. -/// Option 'default_vcols_fallback' specifies whether vertex colors should -/// always be defined, even if no colors are given (fallback to white). -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *warn, - std::string *err, const char *filename, - const char *mtl_basedir = NULL, bool triangulate = true, - bool default_vcols_fallback = true); - -/// Loads .obj from a file with custom user callback. -/// .mtl is loaded as usual and parsed material_t data will be passed to -/// `callback.mtllib_cb`. -/// Returns true when loading .obj/.mtl become success. -/// Returns warning message into `warn`, and error message into `err` -/// See `examples/callback_api/` for how to use this function. -bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, - void *user_data = NULL, - MaterialReader *readMatFn = NULL, - std::string *warn = NULL, std::string *err = NULL); - -/// Loads object from a std::istream, uses `readMatFn` to retrieve -/// std::istream for materials. -/// Returns true when loading .obj become success. -/// Returns warning and error message into `err` -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *warn, - std::string *err, std::istream *inStream, - MaterialReader *readMatFn = NULL, bool triangulate = true, - bool default_vcols_fallback = true); - -/// Loads materials into std::map -void LoadMtl(std::map *material_map, - std::vector *materials, std::istream *inStream, - std::string *warning, std::string *err); - -/// -/// Parse texture name and texture option for custom texture parameter through -/// material::unknown_parameter -/// -/// @param[out] texname Parsed texture name -/// @param[out] texopt Parsed texopt -/// @param[in] linebuf Input string -/// -bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, - const char *linebuf); - -/// =<<========== Legacy v1 API ============================================= - -} // namespace tinyobj - -#endif // TINY_OBJ_LOADER_H_ - -#ifdef TINYOBJLOADER_IMPLEMENTATION -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT - -#ifdef TINYOBJLOADER_DONOT_INCLUDE_MAPBOX_EARCUT -// Assume earcut.hpp is included outside of tiny_obj_loader.h -#else - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Weverything" -#endif - -#include - -#include "mapbox/earcut.hpp" - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif - -#endif // TINYOBJLOADER_USE_MAPBOX_EARCUT - -namespace tinyobj { - -MaterialReader::~MaterialReader() {} - -struct vertex_index_t { - int v_idx, vt_idx, vn_idx; - vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} - explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} - vertex_index_t(int vidx, int vtidx, int vnidx) - : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} -}; - -// Internal data structure for face representation -// index + smoothing group. -struct face_t { - unsigned int - smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off. - int pad_; - std::vector vertex_indices; // face vertex indices. - - face_t() : smoothing_group_id(0), pad_(0) {} -}; - -// Internal data structure for line representation -struct __line_t { - // l v1/vt1 v2/vt2 ... - // In the specification, line primitrive does not have normal index, but - // TinyObjLoader allow it - std::vector vertex_indices; -}; - -// Internal data structure for points representation -struct __points_t { - // p v1 v2 ... - // In the specification, point primitrive does not have normal index and - // texture coord index, but TinyObjLoader allow it. - std::vector vertex_indices; -}; - -struct tag_sizes { - tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} - int num_ints; - int num_reals; - int num_strings; -}; - -struct obj_shape { - std::vector v; - std::vector vn; - std::vector vt; -}; - -// -// Manages group of primitives(face, line, points, ...) -struct PrimGroup { - std::vector faceGroup; - std::vector<__line_t> lineGroup; - std::vector<__points_t> pointsGroup; - - void clear() { - faceGroup.clear(); - lineGroup.clear(); - pointsGroup.clear(); - } - - bool IsEmpty() const { - return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty(); - } - - // TODO(syoyo): bspline, surface, ... -}; - -// See -// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf -static std::istream &safeGetline(std::istream &is, std::string &t) { - t.clear(); - - // The characters in the stream are read one-by-one using a std::streambuf. - // That is faster than reading them one-by-one using the std::istream. - // Code that uses streambuf this way must be guarded by a sentry object. - // The sentry object performs various tasks, - // such as thread synchronization and updating the stream state. - - std::istream::sentry se(is, true); - std::streambuf *sb = is.rdbuf(); - - if (se) { - for (;;) { - int c = sb->sbumpc(); - switch (c) { - case '\n': - return is; - case '\r': - if (sb->sgetc() == '\n') sb->sbumpc(); - return is; - case EOF: - // Also handle the case when the last line has no line ending - if (t.empty()) is.setstate(std::ios::eofbit); - return is; - default: - t += static_cast(c); - } - } - } - - return is; -} - -#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) -#define IS_DIGIT(x) \ - (static_cast((x) - '0') < static_cast(10)) -#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) - -template -static inline std::string toString(const T &t) { - std::stringstream ss; - ss << t; - return ss.str(); -} - -static inline std::string removeUtf8Bom(const std::string& input) { - // UTF-8 BOM = 0xEF,0xBB,0xBF - if (input.size() >= 3 && - static_cast(input[0]) == 0xEF && - static_cast(input[1]) == 0xBB && - static_cast(input[2]) == 0xBF) { - return input.substr(3); // Skip BOM - } - return input; -} - -struct warning_context { - std::string *warn; - size_t line_number; -}; - -// Make index zero-base, and also support relative index. -static inline bool fixIndex(int idx, int n, int *ret, bool allow_zero, - const warning_context &context) { - if (!ret) { - return false; - } - - if (idx > 0) { - (*ret) = idx - 1; - return true; - } - - if (idx == 0) { - // zero is not allowed according to the spec. - if (context.warn) { - (*context.warn) += - "A zero value index found (will have a value of -1 for normal and " - "tex indices. Line " + - toString(context.line_number) + ").\n"; - } - - (*ret) = idx - 1; - return allow_zero; - } - - if (idx < 0) { - (*ret) = n + idx; // negative value = relative - if ((*ret) < 0) { - return false; // invalid relative index - } - return true; - } - - return false; // never reach here. -} - -static inline std::string parseString(const char **token) { - std::string s; - (*token) += strspn((*token), " \t"); - size_t e = strcspn((*token), " \t\r"); - s = std::string((*token), &(*token)[e]); - (*token) += e; - return s; -} - -static inline int parseInt(const char **token) { - (*token) += strspn((*token), " \t"); - int i = atoi((*token)); - (*token) += strcspn((*token), " \t\r"); - return i; -} - -// Tries to parse a floating point number located at s. -// -// s_end should be a location in the string where reading should absolutely -// stop. For example at the end of the string, to prevent buffer overflows. -// -// Parses the following EBNF grammar: -// sign = "+" | "-" ; -// END = ? anything not in digit ? -// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; -// integer = [sign] , digit , {digit} ; -// decimal = integer , ["." , integer] ; -// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; -// -// Valid strings are for example: -// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 -// -// If the parsing is a success, result is set to the parsed value and true -// is returned. -// -// The function is greedy and will parse until any of the following happens: -// - a non-conforming character is encountered. -// - s_end is reached. -// -// The following situations triggers a failure: -// - s >= s_end. -// - parse failure. -// -static bool tryParseDouble(const char *s, const char *s_end, double *result) { - if (s >= s_end) { - return false; - } - - double mantissa = 0.0; - // This exponent is base 2 rather than 10. - // However the exponent we parse is supposed to be one of ten, - // thus we must take care to convert the exponent/and or the - // mantissa to a * 2^E, where a is the mantissa and E is the - // exponent. - // To get the final double we will use ldexp, it requires the - // exponent to be in base 2. - int exponent = 0; - - // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED - // TO JUMP OVER DEFINITIONS. - char sign = '+'; - char exp_sign = '+'; - char const *curr = s; - - // How many characters were read in a loop. - int read = 0; - // Tells whether a loop terminated due to reaching s_end. - bool end_not_reached = false; - bool leading_decimal_dots = false; - - /* - BEGIN PARSING. - */ - - // Find out what sign we've got. - if (*curr == '+' || *curr == '-') { - sign = *curr; - curr++; - if ((curr != s_end) && (*curr == '.')) { - // accept. Somethig like `.7e+2`, `-.5234` - leading_decimal_dots = true; - } - } else if (IS_DIGIT(*curr)) { /* Pass through. */ - } else if (*curr == '.') { - // accept. Somethig like `.7e+2`, `-.5234` - leading_decimal_dots = true; - } else { - goto fail; - } - - // Read the integer part. - end_not_reached = (curr != s_end); - if (!leading_decimal_dots) { - while (end_not_reached && IS_DIGIT(*curr)) { - mantissa *= 10; - mantissa += static_cast(*curr - 0x30); - curr++; - read++; - end_not_reached = (curr != s_end); - } - - // We must make sure we actually got something. - if (read == 0) goto fail; - } - - // We allow numbers of form "#", "###" etc. - if (!end_not_reached) goto assemble; - - // Read the decimal part. - if (*curr == '.') { - curr++; - read = 1; - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - static const double pow_lut[] = { - 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, - }; - const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; - - // NOTE: Don't use powf here, it will absolutely murder precision. - mantissa += static_cast(*curr - 0x30) * - (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read)); - read++; - curr++; - end_not_reached = (curr != s_end); - } - } else if (*curr == 'e' || *curr == 'E') { - } else { - goto assemble; - } - - if (!end_not_reached) goto assemble; - - // Read the exponent part. - if (*curr == 'e' || *curr == 'E') { - curr++; - // Figure out if a sign is present and if it is. - end_not_reached = (curr != s_end); - if (end_not_reached && (*curr == '+' || *curr == '-')) { - exp_sign = *curr; - curr++; - } else if (IS_DIGIT(*curr)) { /* Pass through. */ - } else { - // Empty E is not allowed. - goto fail; - } - - read = 0; - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - // To avoid annoying MSVC's min/max macro definiton, - // Use hardcoded int max value - if (exponent > - (2147483647 / 10)) { // 2147483647 = std::numeric_limits::max() - // Integer overflow - goto fail; - } - exponent *= 10; - exponent += static_cast(*curr - 0x30); - curr++; - read++; - end_not_reached = (curr != s_end); - } - exponent *= (exp_sign == '+' ? 1 : -1); - if (read == 0) goto fail; - } - -assemble: - *result = (sign == '+' ? 1 : -1) * - (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) - : mantissa); - return true; -fail: - return false; -} - -static inline real_t parseReal(const char **token, double default_value = 0.0) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - double val = default_value; - tryParseDouble((*token), end, &val); - real_t f = static_cast(val); - (*token) = end; - return f; -} - -static inline bool parseReal(const char **token, real_t *out) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - double val; - bool ret = tryParseDouble((*token), end, &val); - if (ret) { - real_t f = static_cast(val); - (*out) = f; - } - (*token) = end; - return ret; -} - -static inline void parseReal2(real_t *x, real_t *y, const char **token, - const double default_x = 0.0, - const double default_y = 0.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); -} - -static inline void parseReal3(real_t *x, real_t *y, real_t *z, - const char **token, const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); -} - -#if 0 // not used -static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, - const char **token, const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0, - const double default_w = 1.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); - (*w) = parseReal(token, default_w); -} -#endif - -// Extension: parse vertex with colors(6 items) -// Return 3: xyz, 4: xyzw, 6: xyzrgb -// `r`: red(case 6) or [w](case 4) -static inline int parseVertexWithColor(real_t *x, real_t *y, real_t *z, - real_t *r, real_t *g, real_t *b, - const char **token, - const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0) { - // TODO: Check error - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); - - // - 4 components(x, y, z, w) ot 6 components - bool has_r = parseReal(token, r); - - if (!has_r) { - (*r) = (*g) = (*b) = 1.0; - return 3; - } - - bool has_g = parseReal(token, g); - - if (!has_g) { - (*g) = (*b) = 1.0; - return 4; - } - - bool has_b = parseReal(token, b); - - if (!has_b) { - (*r) = (*g) = (*b) = 1.0; - return 3; // treated as xyz - } - - return 6; -} - -static inline bool parseOnOff(const char **token, bool default_value = true) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - - bool ret = default_value; - if ((0 == strncmp((*token), "on", 2))) { - ret = true; - } else if ((0 == strncmp((*token), "off", 3))) { - ret = false; - } - - (*token) = end; - return ret; -} - -static inline texture_type_t parseTextureType( - const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - texture_type_t ty = default_value; - - if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { - ty = TEXTURE_TYPE_CUBE_TOP; - } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { - ty = TEXTURE_TYPE_CUBE_BOTTOM; - } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { - ty = TEXTURE_TYPE_CUBE_LEFT; - } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { - ty = TEXTURE_TYPE_CUBE_RIGHT; - } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { - ty = TEXTURE_TYPE_CUBE_FRONT; - } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { - ty = TEXTURE_TYPE_CUBE_BACK; - } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { - ty = TEXTURE_TYPE_SPHERE; - } - - (*token) = end; - return ty; -} - -static tag_sizes parseTagTriple(const char **token) { - tag_sizes ts; - - (*token) += strspn((*token), " \t"); - ts.num_ints = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return ts; - } - - (*token)++; // Skip '/' - - (*token) += strspn((*token), " \t"); - ts.num_reals = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return ts; - } - (*token)++; // Skip '/' - - ts.num_strings = parseInt(token); - - return ts; -} - -// Parse triples with index offsets: i, i/j/k, i//k, i/j -static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize, - vertex_index_t *ret, const warning_context &context) { - if (!ret) { - return false; - } - - vertex_index_t vi(-1); - - if (!fixIndex(atoi((*token)), vsize, &vi.v_idx, false, context)) { - return false; - } - - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - (*ret) = vi; - return true; - } - (*token)++; - - // i//k - if ((*token)[0] == '/') { - (*token)++; - if (!fixIndex(atoi((*token)), vnsize, &vi.vn_idx, true, context)) { - return false; - } - (*token) += strcspn((*token), "/ \t\r"); - (*ret) = vi; - return true; - } - - // i/j/k or i/j - if (!fixIndex(atoi((*token)), vtsize, &vi.vt_idx, true, context)) { - return false; - } - - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - (*ret) = vi; - return true; - } - - // i/j/k - (*token)++; // skip '/' - if (!fixIndex(atoi((*token)), vnsize, &vi.vn_idx, true, context)) { - return false; - } - (*token) += strcspn((*token), "/ \t\r"); - - (*ret) = vi; - - return true; -} - -// Parse raw triples: i, i/j/k, i//k, i/j -static vertex_index_t parseRawTriple(const char **token) { - vertex_index_t vi(static_cast(0)); // 0 is an invalid index in OBJ - - vi.v_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return vi; - } - (*token)++; - - // i//k - if ((*token)[0] == '/') { - (*token)++; - vi.vn_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - return vi; - } - - // i/j/k or i/j - vi.vt_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return vi; - } - - // i/j/k - (*token)++; // skip '/' - vi.vn_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - return vi; -} - -bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, - const char *linebuf) { - // @todo { write more robust lexer and parser. } - bool found_texname = false; - std::string texture_name; - - const char *token = linebuf; // Assume line ends with NULL - - while (!IS_NEW_LINE((*token))) { - token += strspn(token, " \t"); // skip space - if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { - token += 8; - texopt->blendu = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { - token += 8; - texopt->blendv = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { - token += 7; - texopt->clamp = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { - token += 7; - texopt->sharpness = parseReal(&token, 1.0); - } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { - token += 4; - texopt->bump_multiplier = parseReal(&token, 1.0); - } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), - &(texopt->origin_offset[2]), &token); - } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), - &token, 1.0, 1.0, 1.0); - } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), - &(texopt->turbulence[2]), &token); - } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { - token += 5; - texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); - } else if ((0 == strncmp(token, "-texres", 7)) && IS_SPACE((token[7]))) { - token += 7; - // TODO(syoyo): Check if arg is int type. - texopt->texture_resolution = parseInt(&token); - } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { - token += 9; - token += strspn(token, " \t"); - const char *end = token + strcspn(token, " \t\r"); - if ((end - token) == 1) { // Assume one char for -imfchan - texopt->imfchan = (*token); - } - token = end; - } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { - token += 4; - parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); - } else if ((0 == strncmp(token, "-colorspace", 11)) && - IS_SPACE((token[11]))) { - token += 12; - texopt->colorspace = parseString(&token); - } else { -// Assume texture filename -#if 0 - size_t len = strcspn(token, " \t\r"); // untile next space - texture_name = std::string(token, token + len); - token += len; - - token += strspn(token, " \t"); // skip space -#else - // Read filename until line end to parse filename containing whitespace - // TODO(syoyo): Support parsing texture option flag after the filename. - texture_name = std::string(token); - token += texture_name.length(); -#endif - - found_texname = true; - } - } - - if (found_texname) { - (*texname) = texture_name; - return true; - } else { - return false; - } -} - -static void InitTexOpt(texture_option_t *texopt, const bool is_bump) { - if (is_bump) { - texopt->imfchan = 'l'; - } else { - texopt->imfchan = 'm'; - } - texopt->bump_multiplier = static_cast(1.0); - texopt->clamp = false; - texopt->blendu = true; - texopt->blendv = true; - texopt->sharpness = static_cast(1.0); - texopt->brightness = static_cast(0.0); - texopt->contrast = static_cast(1.0); - texopt->origin_offset[0] = static_cast(0.0); - texopt->origin_offset[1] = static_cast(0.0); - texopt->origin_offset[2] = static_cast(0.0); - texopt->scale[0] = static_cast(1.0); - texopt->scale[1] = static_cast(1.0); - texopt->scale[2] = static_cast(1.0); - texopt->turbulence[0] = static_cast(0.0); - texopt->turbulence[1] = static_cast(0.0); - texopt->turbulence[2] = static_cast(0.0); - texopt->texture_resolution = -1; - texopt->type = TEXTURE_TYPE_NONE; -} - -static void InitMaterial(material_t *material) { - InitTexOpt(&material->ambient_texopt, /* is_bump */ false); - InitTexOpt(&material->diffuse_texopt, /* is_bump */ false); - InitTexOpt(&material->specular_texopt, /* is_bump */ false); - InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false); - InitTexOpt(&material->bump_texopt, /* is_bump */ true); - InitTexOpt(&material->displacement_texopt, /* is_bump */ false); - InitTexOpt(&material->alpha_texopt, /* is_bump */ false); - InitTexOpt(&material->reflection_texopt, /* is_bump */ false); - InitTexOpt(&material->roughness_texopt, /* is_bump */ false); - InitTexOpt(&material->metallic_texopt, /* is_bump */ false); - InitTexOpt(&material->sheen_texopt, /* is_bump */ false); - InitTexOpt(&material->emissive_texopt, /* is_bump */ false); - InitTexOpt(&material->normal_texopt, - /* is_bump */ false); // @fixme { is_bump will be true? } - material->name = ""; - material->ambient_texname = ""; - material->diffuse_texname = ""; - material->specular_texname = ""; - material->specular_highlight_texname = ""; - material->bump_texname = ""; - material->displacement_texname = ""; - material->reflection_texname = ""; - material->alpha_texname = ""; - for (int i = 0; i < 3; i++) { - material->ambient[i] = static_cast(0.0); - material->diffuse[i] = static_cast(0.0); - material->specular[i] = static_cast(0.0); - material->transmittance[i] = static_cast(0.0); - material->emission[i] = static_cast(0.0); - } - material->illum = 0; - material->dissolve = static_cast(1.0); - material->shininess = static_cast(1.0); - material->ior = static_cast(1.0); - - material->roughness = static_cast(0.0); - material->metallic = static_cast(0.0); - material->sheen = static_cast(0.0); - material->clearcoat_thickness = static_cast(0.0); - material->clearcoat_roughness = static_cast(0.0); - material->anisotropy_rotation = static_cast(0.0); - material->anisotropy = static_cast(0.0); - material->roughness_texname = ""; - material->metallic_texname = ""; - material->sheen_texname = ""; - material->emissive_texname = ""; - material->normal_texname = ""; - - material->unknown_parameter.clear(); -} - -// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html -template -static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) { - int i, j, c = 0; - for (i = 0, j = nvert - 1; i < nvert; j = i++) { - if (((verty[i] > testy) != (verty[j] > testy)) && - (testx < - (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + - vertx[i])) - c = !c; - } - return c; -} - -struct TinyObjPoint { - real_t x, y, z; - TinyObjPoint() : x(0), y(0), z(0) {} - TinyObjPoint(real_t x_, real_t y_, real_t z_) : x(x_), y(y_), z(z_) {} -}; - -inline TinyObjPoint cross(const TinyObjPoint &v1, const TinyObjPoint &v2) { - return TinyObjPoint(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, - v1.x * v2.y - v1.y * v2.x); -} - -inline real_t dot(const TinyObjPoint &v1, const TinyObjPoint &v2) { - return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z); -} - -inline real_t GetLength(TinyObjPoint &e) { - return std::sqrt(e.x * e.x + e.y * e.y + e.z * e.z); -} - -inline TinyObjPoint Normalize(TinyObjPoint e) { - real_t inv_length = real_t(1) / GetLength(e); - return TinyObjPoint(e.x * inv_length, e.y * inv_length, e.z * inv_length); -} - -inline TinyObjPoint WorldToLocal(const TinyObjPoint &a, const TinyObjPoint &u, - const TinyObjPoint &v, const TinyObjPoint &w) { - return TinyObjPoint(dot(a, u), dot(a, v), dot(a, w)); -} - -// TODO(syoyo): refactor function. -static bool exportGroupsToShape(shape_t *shape, const PrimGroup &prim_group, - const std::vector &tags, - const int material_id, const std::string &name, - bool triangulate, const std::vector &v, - std::string *warn) { - if (prim_group.IsEmpty()) { - return false; - } - - shape->name = name; - - // polygon - if (!prim_group.faceGroup.empty()) { - // Flatten vertices and indices - for (size_t i = 0; i < prim_group.faceGroup.size(); i++) { - const face_t &face = prim_group.faceGroup[i]; - - size_t npolys = face.vertex_indices.size(); - - if (npolys < 3) { - // Face must have 3+ vertices. - if (warn) { - (*warn) += "Degenerated face found\n."; - } - continue; - } - - if (triangulate && npolys != 3) { - if (npolys == 4) { - vertex_index_t i0 = face.vertex_indices[0]; - vertex_index_t i1 = face.vertex_indices[1]; - vertex_index_t i2 = face.vertex_indices[2]; - vertex_index_t i3 = face.vertex_indices[3]; - - size_t vi0 = size_t(i0.v_idx); - size_t vi1 = size_t(i1.v_idx); - size_t vi2 = size_t(i2.v_idx); - size_t vi3 = size_t(i3.v_idx); - - if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || - ((3 * vi2 + 2) >= v.size()) || ((3 * vi3 + 2) >= v.size())) { - // Invalid triangle. - // FIXME(syoyo): Is it ok to simply skip this invalid triangle? - if (warn) { - (*warn) += "Face with invalid vertex index found.\n"; - } - continue; - } - - real_t v0x = v[vi0 * 3 + 0]; - real_t v0y = v[vi0 * 3 + 1]; - real_t v0z = v[vi0 * 3 + 2]; - real_t v1x = v[vi1 * 3 + 0]; - real_t v1y = v[vi1 * 3 + 1]; - real_t v1z = v[vi1 * 3 + 2]; - real_t v2x = v[vi2 * 3 + 0]; - real_t v2y = v[vi2 * 3 + 1]; - real_t v2z = v[vi2 * 3 + 2]; - real_t v3x = v[vi3 * 3 + 0]; - real_t v3y = v[vi3 * 3 + 1]; - real_t v3z = v[vi3 * 3 + 2]; - - // There are two candidates to split the quad into two triangles. - // - // Choose the shortest edge. - // TODO: Is it better to determine the edge to split by calculating - // the area of each triangle? - // - // +---+ - // |\ | - // | \ | - // | \| - // +---+ - // - // +---+ - // | /| - // | / | - // |/ | - // +---+ - - real_t e02x = v2x - v0x; - real_t e02y = v2y - v0y; - real_t e02z = v2z - v0z; - real_t e13x = v3x - v1x; - real_t e13y = v3y - v1y; - real_t e13z = v3z - v1z; - - real_t sqr02 = e02x * e02x + e02y * e02y + e02z * e02z; - real_t sqr13 = e13x * e13x + e13y * e13y + e13z * e13z; - - index_t idx0, idx1, idx2, idx3; - - idx0.vertex_index = i0.v_idx; - idx0.normal_index = i0.vn_idx; - idx0.texcoord_index = i0.vt_idx; - idx1.vertex_index = i1.v_idx; - idx1.normal_index = i1.vn_idx; - idx1.texcoord_index = i1.vt_idx; - idx2.vertex_index = i2.v_idx; - idx2.normal_index = i2.vn_idx; - idx2.texcoord_index = i2.vt_idx; - idx3.vertex_index = i3.v_idx; - idx3.normal_index = i3.vn_idx; - idx3.texcoord_index = i3.vt_idx; - - if (sqr02 < sqr13) { - // [0, 1, 2], [0, 2, 3] - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx2); - shape->mesh.indices.push_back(idx3); - } else { - // [0, 1, 3], [1, 2, 3] - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx3); - - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - shape->mesh.indices.push_back(idx3); - } - - // Two triangle faces - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.num_face_vertices.push_back(3); - - shape->mesh.material_ids.push_back(material_id); - shape->mesh.material_ids.push_back(material_id); - - shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); - shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); - - } else { -#ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT - vertex_index_t i0 = face.vertex_indices[0]; - vertex_index_t i0_2 = i0; - - // TMW change: Find the normal axis of the polygon using Newell's - // method - TinyObjPoint n; - for (size_t k = 0; k < npolys; ++k) { - i0 = face.vertex_indices[k % npolys]; - size_t vi0 = size_t(i0.v_idx); - - size_t j = (k + 1) % npolys; - i0_2 = face.vertex_indices[j]; - size_t vi0_2 = size_t(i0_2.v_idx); - - real_t v0x = v[vi0 * 3 + 0]; - real_t v0y = v[vi0 * 3 + 1]; - real_t v0z = v[vi0 * 3 + 2]; - - real_t v0x_2 = v[vi0_2 * 3 + 0]; - real_t v0y_2 = v[vi0_2 * 3 + 1]; - real_t v0z_2 = v[vi0_2 * 3 + 2]; - - const TinyObjPoint point1(v0x, v0y, v0z); - const TinyObjPoint point2(v0x_2, v0y_2, v0z_2); - - TinyObjPoint a(point1.x - point2.x, point1.y - point2.y, - point1.z - point2.z); - TinyObjPoint b(point1.x + point2.x, point1.y + point2.y, - point1.z + point2.z); - - n.x += (a.y * b.z); - n.y += (a.z * b.x); - n.z += (a.x * b.y); - } - real_t length_n = GetLength(n); - // Check if zero length normal - if (length_n <= 0) { - continue; - } - // Negative is to flip the normal to the correct direction - real_t inv_length = -real_t(1.0) / length_n; - n.x *= inv_length; - n.y *= inv_length; - n.z *= inv_length; - - TinyObjPoint axis_w, axis_v, axis_u; - axis_w = n; - TinyObjPoint a; - if (std::fabs(axis_w.x) > real_t(0.9999999)) { - a = TinyObjPoint(0, 1, 0); - } else { - a = TinyObjPoint(1, 0, 0); - } - axis_v = Normalize(cross(axis_w, a)); - axis_u = cross(axis_w, axis_v); - using Point = std::array; - - // first polyline define the main polygon. - // following polylines define holes(not used in tinyobj). - std::vector > polygon; - - std::vector polyline; - - // TMW change: Find best normal and project v0x and v0y to those - // coordinates, instead of picking a plane aligned with an axis (which - // can flip polygons). - - // Fill polygon data(facevarying vertices). - for (size_t k = 0; k < npolys; k++) { - i0 = face.vertex_indices[k]; - size_t vi0 = size_t(i0.v_idx); - - assert(((3 * vi0 + 2) < v.size())); - - real_t v0x = v[vi0 * 3 + 0]; - real_t v0y = v[vi0 * 3 + 1]; - real_t v0z = v[vi0 * 3 + 2]; - - TinyObjPoint polypoint(v0x, v0y, v0z); - TinyObjPoint loc = WorldToLocal(polypoint, axis_u, axis_v, axis_w); - - polyline.push_back({loc.x, loc.y}); - } - - polygon.push_back(polyline); - std::vector indices = mapbox::earcut(polygon); - // => result = 3 * faces, clockwise - - assert(indices.size() % 3 == 0); - - // Reconstruct vertex_index_t - for (size_t k = 0; k < indices.size() / 3; k++) { - { - index_t idx0, idx1, idx2; - idx0.vertex_index = face.vertex_indices[indices[3 * k + 0]].v_idx; - idx0.normal_index = - face.vertex_indices[indices[3 * k + 0]].vn_idx; - idx0.texcoord_index = - face.vertex_indices[indices[3 * k + 0]].vt_idx; - idx1.vertex_index = face.vertex_indices[indices[3 * k + 1]].v_idx; - idx1.normal_index = - face.vertex_indices[indices[3 * k + 1]].vn_idx; - idx1.texcoord_index = - face.vertex_indices[indices[3 * k + 1]].vt_idx; - idx2.vertex_index = face.vertex_indices[indices[3 * k + 2]].v_idx; - idx2.normal_index = - face.vertex_indices[indices[3 * k + 2]].vn_idx; - idx2.texcoord_index = - face.vertex_indices[indices[3 * k + 2]].vt_idx; - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); - } - } - -#else // Built-in ear clipping triangulation - vertex_index_t i0 = face.vertex_indices[0]; - vertex_index_t i1(-1); - vertex_index_t i2 = face.vertex_indices[1]; - - // find the two axes to work in - size_t axes[2] = {1, 2}; - for (size_t k = 0; k < npolys; ++k) { - i0 = face.vertex_indices[(k + 0) % npolys]; - i1 = face.vertex_indices[(k + 1) % npolys]; - i2 = face.vertex_indices[(k + 2) % npolys]; - size_t vi0 = size_t(i0.v_idx); - size_t vi1 = size_t(i1.v_idx); - size_t vi2 = size_t(i2.v_idx); - - if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || - ((3 * vi2 + 2) >= v.size())) { - // Invalid triangle. - // FIXME(syoyo): Is it ok to simply skip this invalid triangle? - continue; - } - real_t v0x = v[vi0 * 3 + 0]; - real_t v0y = v[vi0 * 3 + 1]; - real_t v0z = v[vi0 * 3 + 2]; - real_t v1x = v[vi1 * 3 + 0]; - real_t v1y = v[vi1 * 3 + 1]; - real_t v1z = v[vi1 * 3 + 2]; - real_t v2x = v[vi2 * 3 + 0]; - real_t v2y = v[vi2 * 3 + 1]; - real_t v2z = v[vi2 * 3 + 2]; - real_t e0x = v1x - v0x; - real_t e0y = v1y - v0y; - real_t e0z = v1z - v0z; - real_t e1x = v2x - v1x; - real_t e1y = v2y - v1y; - real_t e1z = v2z - v1z; - real_t cx = std::fabs(e0y * e1z - e0z * e1y); - real_t cy = std::fabs(e0z * e1x - e0x * e1z); - real_t cz = std::fabs(e0x * e1y - e0y * e1x); - const real_t epsilon = std::numeric_limits::epsilon(); - // std::cout << "cx " << cx << ", cy " << cy << ", cz " << cz << - // "\n"; - if (cx > epsilon || cy > epsilon || cz > epsilon) { - // std::cout << "corner\n"; - // found a corner - if (cx > cy && cx > cz) { - // std::cout << "pattern0\n"; - } else { - // std::cout << "axes[0] = 0\n"; - axes[0] = 0; - if (cz > cx && cz > cy) { - // std::cout << "axes[1] = 1\n"; - axes[1] = 1; - } - } - break; - } - } - - face_t remainingFace = face; // copy - size_t guess_vert = 0; - vertex_index_t ind[3]; - real_t vx[3]; - real_t vy[3]; - - // How many iterations can we do without decreasing the remaining - // vertices. - size_t remainingIterations = face.vertex_indices.size(); - size_t previousRemainingVertices = - remainingFace.vertex_indices.size(); - - while (remainingFace.vertex_indices.size() > 3 && - remainingIterations > 0) { - // std::cout << "remainingIterations " << remainingIterations << - // "\n"; - - npolys = remainingFace.vertex_indices.size(); - if (guess_vert >= npolys) { - guess_vert -= npolys; - } - - if (previousRemainingVertices != npolys) { - // The number of remaining vertices decreased. Reset counters. - previousRemainingVertices = npolys; - remainingIterations = npolys; - } else { - // We didn't consume a vertex on previous iteration, reduce the - // available iterations. - remainingIterations--; - } - - for (size_t k = 0; k < 3; k++) { - ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; - size_t vi = size_t(ind[k].v_idx); - if (((vi * 3 + axes[0]) >= v.size()) || - ((vi * 3 + axes[1]) >= v.size())) { - // ??? - vx[k] = static_cast(0.0); - vy[k] = static_cast(0.0); - } else { - vx[k] = v[vi * 3 + axes[0]]; - vy[k] = v[vi * 3 + axes[1]]; - } - } - - // - // area is calculated per face - // - real_t e0x = vx[1] - vx[0]; - real_t e0y = vy[1] - vy[0]; - real_t e1x = vx[2] - vx[1]; - real_t e1y = vy[2] - vy[1]; - real_t cross = e0x * e1y - e0y * e1x; - // std::cout << "axes = " << axes[0] << ", " << axes[1] << "\n"; - // std::cout << "e0x, e0y, e1x, e1y " << e0x << ", " << e0y << ", " - // << e1x << ", " << e1y << "\n"; - - real_t area = - (vx[0] * vy[1] - vy[0] * vx[1]) * static_cast(0.5); - // std::cout << "cross " << cross << ", area " << area << "\n"; - // if an internal angle - if (cross * area < static_cast(0.0)) { - // std::cout << "internal \n"; - guess_vert += 1; - // std::cout << "guess vert : " << guess_vert << "\n"; - continue; - } - - // check all other verts in case they are inside this triangle - bool overlap = false; - for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { - size_t idx = (guess_vert + otherVert) % npolys; - - if (idx >= remainingFace.vertex_indices.size()) { - // std::cout << "???0\n"; - // ??? - continue; - } - - size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx); - - if (((ovi * 3 + axes[0]) >= v.size()) || - ((ovi * 3 + axes[1]) >= v.size())) { - // std::cout << "???1\n"; - // ??? - continue; - } - real_t tx = v[ovi * 3 + axes[0]]; - real_t ty = v[ovi * 3 + axes[1]]; - if (pnpoly(3, vx, vy, tx, ty)) { - // std::cout << "overlap\n"; - overlap = true; - break; - } - } - - if (overlap) { - // std::cout << "overlap2\n"; - guess_vert += 1; - continue; - } - - // this triangle is an ear - { - index_t idx0, idx1, idx2; - idx0.vertex_index = ind[0].v_idx; - idx0.normal_index = ind[0].vn_idx; - idx0.texcoord_index = ind[0].vt_idx; - idx1.vertex_index = ind[1].v_idx; - idx1.normal_index = ind[1].vn_idx; - idx1.texcoord_index = ind[1].vt_idx; - idx2.vertex_index = ind[2].v_idx; - idx2.normal_index = ind[2].vn_idx; - idx2.texcoord_index = ind[2].vt_idx; - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); - } - - // remove v1 from the list - size_t removed_vert_index = (guess_vert + 1) % npolys; - while (removed_vert_index + 1 < npolys) { - remainingFace.vertex_indices[removed_vert_index] = - remainingFace.vertex_indices[removed_vert_index + 1]; - removed_vert_index += 1; - } - remainingFace.vertex_indices.pop_back(); - } - - // std::cout << "remainingFace.vi.size = " << - // remainingFace.vertex_indices.size() << "\n"; - if (remainingFace.vertex_indices.size() == 3) { - i0 = remainingFace.vertex_indices[0]; - i1 = remainingFace.vertex_indices[1]; - i2 = remainingFace.vertex_indices[2]; - { - index_t idx0, idx1, idx2; - idx0.vertex_index = i0.v_idx; - idx0.normal_index = i0.vn_idx; - idx0.texcoord_index = i0.vt_idx; - idx1.vertex_index = i1.v_idx; - idx1.normal_index = i1.vn_idx; - idx1.texcoord_index = i1.vt_idx; - idx2.vertex_index = i2.v_idx; - idx2.normal_index = i2.vn_idx; - idx2.texcoord_index = i2.vt_idx; - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); - } - } -#endif - } // npolys - } else { - for (size_t k = 0; k < npolys; k++) { - index_t idx; - idx.vertex_index = face.vertex_indices[k].v_idx; - idx.normal_index = face.vertex_indices[k].vn_idx; - idx.texcoord_index = face.vertex_indices[k].vt_idx; - shape->mesh.indices.push_back(idx); - } - - shape->mesh.num_face_vertices.push_back( - static_cast(npolys)); - shape->mesh.material_ids.push_back(material_id); // per face - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); // per face - } - } - - shape->mesh.tags = tags; - } - - // line - if (!prim_group.lineGroup.empty()) { - // Flatten indices - for (size_t i = 0; i < prim_group.lineGroup.size(); i++) { - for (size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size(); - j++) { - const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j]; - - index_t idx; - idx.vertex_index = vi.v_idx; - idx.normal_index = vi.vn_idx; - idx.texcoord_index = vi.vt_idx; - - shape->lines.indices.push_back(idx); - } - - shape->lines.num_line_vertices.push_back( - int(prim_group.lineGroup[i].vertex_indices.size())); - } - } - - // points - if (!prim_group.pointsGroup.empty()) { - // Flatten & convert indices - for (size_t i = 0; i < prim_group.pointsGroup.size(); i++) { - for (size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size(); - j++) { - const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j]; - - index_t idx; - idx.vertex_index = vi.v_idx; - idx.normal_index = vi.vn_idx; - idx.texcoord_index = vi.vt_idx; - - shape->points.indices.push_back(idx); - } - } - } - - return true; -} - -// Split a string with specified delimiter character and escape character. -// https://rosettacode.org/wiki/Tokenize_a_string_with_escaping#C.2B.2B -static void SplitString(const std::string &s, char delim, char escape, - std::vector &elems) { - std::string token; - - bool escaping = false; - for (size_t i = 0; i < s.size(); ++i) { - char ch = s[i]; - if (escaping) { - escaping = false; - } else if (ch == escape) { - escaping = true; - continue; - } else if (ch == delim) { - if (!token.empty()) { - elems.push_back(token); - } - token.clear(); - continue; - } - token += ch; - } - - elems.push_back(token); -} - -static std::string JoinPath(const std::string &dir, - const std::string &filename) { - if (dir.empty()) { - return filename; - } else { - // check '/' - char lastChar = *dir.rbegin(); - if (lastChar != '/') { - return dir + std::string("/") + filename; - } else { - return dir + filename; - } - } -} - -void LoadMtl(std::map *material_map, - std::vector *materials, std::istream *inStream, - std::string *warning, std::string *err) { - (void)err; - - // Create a default material anyway. - material_t material; - InitMaterial(&material); - - // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification. - bool has_d = false; - bool has_tr = false; - - // has_kd is used to set a default diffuse value when map_Kd is present - // and Kd is not. - bool has_kd = false; - - std::stringstream warn_ss; - - size_t line_no = 0; - std::string linebuf; - while (inStream->peek() != -1) { - safeGetline(*inStream, linebuf); - line_no++; - - // Trim trailing whitespace. - if (linebuf.size() > 0) { - linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); - } - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - if (line_no == 1) { - linebuf = removeUtf8Bom(linebuf); - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // new mtl - if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { - // flush previous material. - if (!material.name.empty()) { - material_map->insert(std::pair( - material.name, static_cast(materials->size()))); - materials->push_back(material); - } - - // initial temporary material - InitMaterial(&material); - - has_d = false; - has_tr = false; - has_kd = false; - - // set new mtl name - token += 7; - { - std::string namebuf = parseString(&token); - // TODO: empty name check? - if (namebuf.empty()) { - if (warning) { - (*warning) += "empty material name in `newmtl`\n"; - } - } - material.name = namebuf; - } - continue; - } - - // ambient - if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.ambient[0] = r; - material.ambient[1] = g; - material.ambient[2] = b; - continue; - } - - // diffuse - if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.diffuse[0] = r; - material.diffuse[1] = g; - material.diffuse[2] = b; - has_kd = true; - continue; - } - - // specular - if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.specular[0] = r; - material.specular[1] = g; - material.specular[2] = b; - continue; - } - - // transmittance - if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || - (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.transmittance[0] = r; - material.transmittance[1] = g; - material.transmittance[2] = b; - continue; - } - - // ior(index of refraction) - if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { - token += 2; - material.ior = parseReal(&token); - continue; - } - - // emission - if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.emission[0] = r; - material.emission[1] = g; - material.emission[2] = b; - continue; - } - - // shininess - if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { - token += 2; - material.shininess = parseReal(&token); - continue; - } - - // illum model - if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { - token += 6; - material.illum = parseInt(&token); - continue; - } - - // dissolve - if ((token[0] == 'd' && IS_SPACE(token[1]))) { - token += 1; - material.dissolve = parseReal(&token); - - if (has_tr) { - warn_ss << "Both `d` and `Tr` parameters defined for \"" - << material.name - << "\". Use the value of `d` for dissolve (line " << line_no - << " in .mtl.)\n"; - } - has_d = true; - continue; - } - if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { - token += 2; - if (has_d) { - // `d` wins. Ignore `Tr` value. - warn_ss << "Both `d` and `Tr` parameters defined for \"" - << material.name - << "\". Use the value of `d` for dissolve (line " << line_no - << " in .mtl.)\n"; - } else { - // We invert value of Tr(assume Tr is in range [0, 1]) - // NOTE: Interpretation of Tr is application(exporter) dependent. For - // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) - material.dissolve = static_cast(1.0) - parseReal(&token); - } - has_tr = true; - continue; - } - - // PBR: roughness - if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { - token += 2; - material.roughness = parseReal(&token); - continue; - } - - // PBR: metallic - if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { - token += 2; - material.metallic = parseReal(&token); - continue; - } - - // PBR: sheen - if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { - token += 2; - material.sheen = parseReal(&token); - continue; - } - - // PBR: clearcoat thickness - if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { - token += 2; - material.clearcoat_thickness = parseReal(&token); - continue; - } - - // PBR: clearcoat roughness - if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { - token += 4; - material.clearcoat_roughness = parseReal(&token); - continue; - } - - // PBR: anisotropy - if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { - token += 6; - material.anisotropy = parseReal(&token); - continue; - } - - // PBR: anisotropy rotation - if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { - token += 7; - material.anisotropy_rotation = parseReal(&token); - continue; - } - - // ambient or ambient occlusion texture - if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.ambient_texname), - &(material.ambient_texopt), token); - continue; - } - - // diffuse texture - if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.diffuse_texname), - &(material.diffuse_texopt), token); - - // Set a decent diffuse default value if a diffuse texture is specified - // without a matching Kd value. - if (!has_kd) { - material.diffuse[0] = static_cast(0.6); - material.diffuse[1] = static_cast(0.6); - material.diffuse[2] = static_cast(0.6); - } - - continue; - } - - // specular texture - if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.specular_texname), - &(material.specular_texopt), token); - continue; - } - - // specular highlight texture - if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.specular_highlight_texname), - &(material.specular_highlight_texopt), token); - continue; - } - - // bump texture - if (((0 == strncmp(token, "map_bump", 8)) || - (0 == strncmp(token, "map_Bump", 8))) && - IS_SPACE(token[8])) { - token += 9; - ParseTextureNameAndOption(&(material.bump_texname), - &(material.bump_texopt), token); - continue; - } - - // bump texture - if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.bump_texname), - &(material.bump_texopt), token); - continue; - } - - // alpha texture - if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { - token += 6; - material.alpha_texname = token; - ParseTextureNameAndOption(&(material.alpha_texname), - &(material.alpha_texopt), token); - continue; - } - - // displacement texture - if (((0 == strncmp(token, "map_disp", 8)) || - (0 == strncmp(token, "map_Disp", 8))) && - IS_SPACE(token[8])) { - token += 9; - ParseTextureNameAndOption(&(material.displacement_texname), - &(material.displacement_texopt), token); - continue; - } - - // displacement texture - if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.displacement_texname), - &(material.displacement_texopt), token); - continue; - } - - // reflection map - if ((0 == strncmp(token, "refl", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.reflection_texname), - &(material.reflection_texopt), token); - continue; - } - - // PBR: roughness texture - if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.roughness_texname), - &(material.roughness_texopt), token); - continue; - } - - // PBR: metallic texture - if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.metallic_texname), - &(material.metallic_texopt), token); - continue; - } - - // PBR: sheen texture - if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.sheen_texname), - &(material.sheen_texopt), token); - continue; - } - - // PBR: emissive texture - if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.emissive_texname), - &(material.emissive_texopt), token); - continue; - } - - // PBR: normal map texture - if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.normal_texname), - &(material.normal_texopt), token); - continue; - } - - // unknown parameter - const char *_space = strchr(token, ' '); - if (!_space) { - _space = strchr(token, '\t'); - } - if (_space) { - std::ptrdiff_t len = _space - token; - std::string key(token, static_cast(len)); - std::string value = _space + 1; - material.unknown_parameter.insert( - std::pair(key, value)); - } - } - // flush last material. - material_map->insert(std::pair( - material.name, static_cast(materials->size()))); - materials->push_back(material); - - if (warning) { - (*warning) = warn_ss.str(); - } -} - -bool MaterialFileReader::operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, - std::string *warn, std::string *err) { - if (!m_mtlBaseDir.empty()) { -#ifdef _WIN32 - char sep = ';'; -#else - char sep = ':'; -#endif - - // https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g - std::vector paths; - std::istringstream f(m_mtlBaseDir); - - std::string s; - while (getline(f, s, sep)) { - paths.push_back(s); - } - - for (size_t i = 0; i < paths.size(); i++) { - std::string filepath = JoinPath(paths[i], matId); - - std::ifstream matIStream(filepath.c_str()); - if (matIStream) { - LoadMtl(matMap, materials, &matIStream, warn, err); - - return true; - } - } - - std::stringstream ss; - ss << "Material file [ " << matId - << " ] not found in a path : " << m_mtlBaseDir << "\n"; - if (warn) { - (*warn) += ss.str(); - } - return false; - - } else { - std::string filepath = matId; - std::ifstream matIStream(filepath.c_str()); - if (matIStream) { - LoadMtl(matMap, materials, &matIStream, warn, err); - - return true; - } - - std::stringstream ss; - ss << "Material file [ " << filepath - << " ] not found in a path : " << m_mtlBaseDir << "\n"; - if (warn) { - (*warn) += ss.str(); - } - - return false; - } -} - -bool MaterialStreamReader::operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, - std::string *warn, std::string *err) { - (void)err; - (void)matId; - if (!m_inStream) { - std::stringstream ss; - ss << "Material stream in error state. \n"; - if (warn) { - (*warn) += ss.str(); - } - return false; - } - - LoadMtl(matMap, materials, &m_inStream, warn, err); - - return true; -} - -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *warn, - std::string *err, const char *filename, const char *mtl_basedir, - bool triangulate, bool default_vcols_fallback) { - attrib->vertices.clear(); - attrib->normals.clear(); - attrib->texcoords.clear(); - attrib->colors.clear(); - shapes->clear(); - - std::stringstream errss; - - std::ifstream ifs(filename); - if (!ifs) { - errss << "Cannot open file [" << filename << "]\n"; - if (err) { - (*err) = errss.str(); - } - return false; - } - - std::string baseDir = mtl_basedir ? mtl_basedir : ""; - if (!baseDir.empty()) { -#ifndef _WIN32 - const char dirsep = '/'; -#else - const char dirsep = '\\'; -#endif - if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep; - } - MaterialFileReader matFileReader(baseDir); - - return LoadObj(attrib, shapes, materials, warn, err, &ifs, &matFileReader, - triangulate, default_vcols_fallback); -} - -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *warn, - std::string *err, std::istream *inStream, - MaterialReader *readMatFn /*= NULL*/, bool triangulate, - bool default_vcols_fallback) { - std::stringstream errss; - - std::vector v; - std::vector vertex_weights; // optional [w] component in `v` - std::vector vn; - std::vector vt; - std::vector vc; - std::vector vw; // tinyobj extension: vertex skin weights - std::vector tags; - PrimGroup prim_group; - std::string name; - - // material - std::set material_filenames; - std::map material_map; - int material = -1; - - // smoothing group id - unsigned int current_smoothing_id = - 0; // Initial value. 0 means no smoothing. - - int greatest_v_idx = -1; - int greatest_vn_idx = -1; - int greatest_vt_idx = -1; - - shape_t shape; - - bool found_all_colors = true; // check if all 'v' line has color info - - size_t line_num = 0; - std::string linebuf; - while (inStream->peek() != -1) { - safeGetline(*inStream, linebuf); - - line_num++; - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - if (line_num == 1) { - linebuf = removeUtf8Bom(linebuf); - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // vertex - if (token[0] == 'v' && IS_SPACE((token[1]))) { - token += 2; - real_t x, y, z; - real_t r, g, b; - - int num_components = parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); - found_all_colors &= (num_components == 6); - - v.push_back(x); - v.push_back(y); - v.push_back(z); - - vertex_weights.push_back( - r); // r = w, and initialized to 1.0 when `w` component is not found. - - if ((num_components == 6) || default_vcols_fallback) { - vc.push_back(r); - vc.push_back(g); - vc.push_back(b); - } - - continue; - } - - // normal - if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; - parseReal3(&x, &y, &z, &token); - vn.push_back(x); - vn.push_back(y); - vn.push_back(z); - continue; - } - - // texcoord - if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y; - parseReal2(&x, &y, &token); - vt.push_back(x); - vt.push_back(y); - continue; - } - - // skin weight. tinyobj extension - if (token[0] == 'v' && token[1] == 'w' && IS_SPACE((token[2]))) { - token += 3; - - // vw ... - // example: - // vw 0 0 0.25 1 0.25 2 0.5 - - // TODO(syoyo): Add syntax check - int vid = 0; - vid = parseInt(&token); - - skin_weight_t sw; - - sw.vertex_id = vid; - - while (!IS_NEW_LINE(token[0]) && token[0] != '#') { - real_t j, w; - // joint_id should not be negative, weight may be negative - // TODO(syoyo): # of elements check - parseReal2(&j, &w, &token, -1.0); - - if (j < static_cast(0)) { - if (err) { - std::stringstream ss; - ss << "Failed parse `vw' line. joint_id is negative. " - "line " - << line_num << ".)\n"; - (*err) += ss.str(); - } - return false; - } - - joint_and_weight_t jw; - - jw.joint_id = int(j); - jw.weight = w; - - sw.weightValues.push_back(jw); - - size_t n = strspn(token, " \t\r"); - token += n; - } - - vw.push_back(sw); - } - - warning_context context; - context.warn = warn; - context.line_number = line_num; - - // line - if (token[0] == 'l' && IS_SPACE((token[1]))) { - token += 2; - - __line_t line; - - while (!IS_NEW_LINE(token[0]) && token[0] != '#') { - vertex_index_t vi; - if (!parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2), &vi, context)) { - if (err) { - (*err) += - "Failed to parse `l' line (e.g. a zero value for vertex index. " - "Line " + - toString(line_num) + ").\n"; - } - return false; - } - - line.vertex_indices.push_back(vi); - - size_t n = strspn(token, " \t\r"); - token += n; - } - - prim_group.lineGroup.push_back(line); - - continue; - } - - // points - if (token[0] == 'p' && IS_SPACE((token[1]))) { - token += 2; - - __points_t pts; - - while (!IS_NEW_LINE(token[0]) && token[0] != '#') { - vertex_index_t vi; - if (!parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2), &vi, context)) { - if (err) { - (*err) += - "Failed to parse `p' line (e.g. a zero value for vertex index. " - "Line " + - toString(line_num) + ").\n"; - } - return false; - } - - pts.vertex_indices.push_back(vi); - - size_t n = strspn(token, " \t\r"); - token += n; - } - - prim_group.pointsGroup.push_back(pts); - - continue; - } - - // face - if (token[0] == 'f' && IS_SPACE((token[1]))) { - token += 2; - token += strspn(token, " \t"); - - face_t face; - - face.smoothing_group_id = current_smoothing_id; - face.vertex_indices.reserve(3); - - while (!IS_NEW_LINE(token[0]) && token[0] != '#') { - vertex_index_t vi; - if (!parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2), &vi, context)) { - if (err) { - (*err) += - "Failed to parse `f' line (e.g. a zero value for vertex index " - "or invalid relative vertex index). Line " + - toString(line_num) + ").\n"; - } - return false; - } - - greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx; - greatest_vn_idx = - greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx; - greatest_vt_idx = - greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx; - - face.vertex_indices.push_back(vi); - size_t n = strspn(token, " \t\r"); - token += n; - } - - // replace with emplace_back + std::move on C++11 - prim_group.faceGroup.push_back(face); - - continue; - } - - // use mtl - if ((0 == strncmp(token, "usemtl", 6))) { - token += 6; - std::string namebuf = parseString(&token); - - int newMaterialId = -1; - std::map::const_iterator it = - material_map.find(namebuf); - if (it != material_map.end()) { - newMaterialId = it->second; - } else { - // { error!! material not found } - if (warn) { - (*warn) += "material [ '" + namebuf + "' ] not found in .mtl\n"; - } - } - - if (newMaterialId != material) { - // Create per-face material. Thus we don't add `shape` to `shapes` at - // this time. - // just clear `faceGroup` after `exportGroupsToShape()` call. - exportGroupsToShape(&shape, prim_group, tags, material, name, - triangulate, v, warn); - prim_group.faceGroup.clear(); - material = newMaterialId; - } - - continue; - } - - // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - if (readMatFn) { - token += 7; - - std::vector filenames; - SplitString(std::string(token), ' ', '\\', filenames); - - if (filenames.empty()) { - if (warn) { - std::stringstream ss; - ss << "Looks like empty filename for mtllib. Use default " - "material (line " - << line_num << ".)\n"; - - (*warn) += ss.str(); - } - } else { - bool found = false; - for (size_t s = 0; s < filenames.size(); s++) { - if (material_filenames.count(filenames[s]) > 0) { - found = true; - continue; - } - - std::string warn_mtl; - std::string err_mtl; - bool ok = (*readMatFn)(filenames[s].c_str(), materials, - &material_map, &warn_mtl, &err_mtl); - if (warn && (!warn_mtl.empty())) { - (*warn) += warn_mtl; - } - - if (err && (!err_mtl.empty())) { - (*err) += err_mtl; - } - - if (ok) { - found = true; - material_filenames.insert(filenames[s]); - break; - } - } - - if (!found) { - if (warn) { - (*warn) += - "Failed to load material file(s). Use default " - "material.\n"; - } - } - } - } - - continue; - } - - // group name - if (token[0] == 'g' && IS_SPACE((token[1]))) { - // flush previous face group. - bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, - triangulate, v, warn); - (void)ret; // return value not used. - - if (shape.mesh.indices.size() > 0) { - shapes->push_back(shape); - } - - shape = shape_t(); - - // material = -1; - prim_group.clear(); - - std::vector names; - - while (!IS_NEW_LINE(token[0]) && token[0] != '#') { - std::string str = parseString(&token); - names.push_back(str); - token += strspn(token, " \t\r"); // skip tag - } - - // names[0] must be 'g' - - if (names.size() < 2) { - // 'g' with empty names - if (warn) { - std::stringstream ss; - ss << "Empty group name. line: " << line_num << "\n"; - (*warn) += ss.str(); - name = ""; - } - } else { - std::stringstream ss; - ss << names[1]; - - // tinyobjloader does not support multiple groups for a primitive. - // Currently we concatinate multiple group names with a space to get - // single group name. - - for (size_t i = 2; i < names.size(); i++) { - ss << " " << names[i]; - } - - name = ss.str(); - } - - continue; - } - - // object name - if (token[0] == 'o' && IS_SPACE((token[1]))) { - // flush previous face group. - bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, - triangulate, v, warn); - (void)ret; // return value not used. - - if (shape.mesh.indices.size() > 0 || shape.lines.indices.size() > 0 || - shape.points.indices.size() > 0) { - shapes->push_back(shape); - } - - // material = -1; - prim_group.clear(); - shape = shape_t(); - - // @todo { multiple object name? } - token += 2; - std::stringstream ss; - ss << token; - name = ss.str(); - - continue; - } - - if (token[0] == 't' && IS_SPACE(token[1])) { - const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize. - tag_t tag; - - token += 2; - - tag.name = parseString(&token); - - tag_sizes ts = parseTagTriple(&token); - - if (ts.num_ints < 0) { - ts.num_ints = 0; - } - if (ts.num_ints > max_tag_nums) { - ts.num_ints = max_tag_nums; - } - - if (ts.num_reals < 0) { - ts.num_reals = 0; - } - if (ts.num_reals > max_tag_nums) { - ts.num_reals = max_tag_nums; - } - - if (ts.num_strings < 0) { - ts.num_strings = 0; - } - if (ts.num_strings > max_tag_nums) { - ts.num_strings = max_tag_nums; - } - - tag.intValues.resize(static_cast(ts.num_ints)); - - for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { - tag.intValues[i] = parseInt(&token); - } - - tag.floatValues.resize(static_cast(ts.num_reals)); - for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { - tag.floatValues[i] = parseReal(&token); - } - - tag.stringValues.resize(static_cast(ts.num_strings)); - for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { - tag.stringValues[i] = parseString(&token); - } - - tags.push_back(tag); - - continue; - } - - if (token[0] == 's' && IS_SPACE(token[1])) { - // smoothing group id - token += 2; - - // skip space. - token += strspn(token, " \t"); // skip space - - if (token[0] == '\0') { - continue; - } - - if (token[0] == '\r' || token[1] == '\n') { - continue; - } - - if (strlen(token) >= 3 && token[0] == 'o' && token[1] == 'f' && - token[2] == 'f') { - current_smoothing_id = 0; - } else { - // assume number - int smGroupId = parseInt(&token); - if (smGroupId < 0) { - // parse error. force set to 0. - // FIXME(syoyo): Report warning. - current_smoothing_id = 0; - } else { - current_smoothing_id = static_cast(smGroupId); - } - } - - continue; - } // smoothing group id - - // Ignore unknown command. - } - - // not all vertices have colors, no default colors desired? -> clear colors - if (!found_all_colors && !default_vcols_fallback) { - vc.clear(); - } - - if (greatest_v_idx >= static_cast(v.size() / 3)) { - if (warn) { - std::stringstream ss; - ss << "Vertex indices out of bounds (line " << line_num << ".)\n\n"; - (*warn) += ss.str(); - } - } - if (greatest_vn_idx >= static_cast(vn.size() / 3)) { - if (warn) { - std::stringstream ss; - ss << "Vertex normal indices out of bounds (line " << line_num - << ".)\n\n"; - (*warn) += ss.str(); - } - } - if (greatest_vt_idx >= static_cast(vt.size() / 2)) { - if (warn) { - std::stringstream ss; - ss << "Vertex texcoord indices out of bounds (line " << line_num - << ".)\n\n"; - (*warn) += ss.str(); - } - } - - bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, - triangulate, v, warn); - // exportGroupsToShape return false when `usemtl` is called in the last - // line. - // we also add `shape` to `shapes` when `shape.mesh` has already some - // faces(indices) - if (ret || shape.mesh.indices - .size()) { // FIXME(syoyo): Support other prims(e.g. lines) - shapes->push_back(shape); - } - prim_group.clear(); // for safety - - if (err) { - (*err) += errss.str(); - } - - attrib->vertices.swap(v); - attrib->vertex_weights.swap(vertex_weights); - attrib->normals.swap(vn); - attrib->texcoords.swap(vt); - attrib->texcoord_ws.swap(vt); - attrib->colors.swap(vc); - attrib->skin_weights.swap(vw); - - return true; -} - -bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, - void *user_data /*= NULL*/, - MaterialReader *readMatFn /*= NULL*/, - std::string *warn, /* = NULL*/ - std::string *err /*= NULL*/) { - std::stringstream errss; - - // material - std::set material_filenames; - std::map material_map; - int material_id = -1; // -1 = invalid - - std::vector indices; - std::vector materials; - std::vector names; - names.reserve(2); - std::vector names_out; - - std::string linebuf; - while (inStream.peek() != -1) { - safeGetline(inStream, linebuf); - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // vertex - if (token[0] == 'v' && IS_SPACE((token[1]))) { - token += 2; - real_t x, y, z; - real_t r, g, b; - - int num_components = parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); - if (callback.vertex_cb) { - callback.vertex_cb(user_data, x, y, z, r); // r=w is optional - } - if (callback.vertex_color_cb) { - bool found_color = (num_components == 6); - callback.vertex_color_cb(user_data, x, y, z, r, g, b, found_color); - } - continue; - } - - // normal - if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; - parseReal3(&x, &y, &z, &token); - if (callback.normal_cb) { - callback.normal_cb(user_data, x, y, z); - } - continue; - } - - // texcoord - if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; // y and z are optional. default = 0.0 - parseReal3(&x, &y, &z, &token); - if (callback.texcoord_cb) { - callback.texcoord_cb(user_data, x, y, z); - } - continue; - } - - // face - if (token[0] == 'f' && IS_SPACE((token[1]))) { - token += 2; - token += strspn(token, " \t"); - - indices.clear(); - while (!IS_NEW_LINE(token[0]) && token[0] != '#') { - vertex_index_t vi = parseRawTriple(&token); - - index_t idx; - idx.vertex_index = vi.v_idx; - idx.normal_index = vi.vn_idx; - idx.texcoord_index = vi.vt_idx; - - indices.push_back(idx); - size_t n = strspn(token, " \t\r"); - token += n; - } - - if (callback.index_cb && indices.size() > 0) { - callback.index_cb(user_data, &indices.at(0), - static_cast(indices.size())); - } - - continue; - } - - // use mtl - if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { - token += 7; - std::stringstream ss; - ss << token; - std::string namebuf = ss.str(); - - int newMaterialId = -1; - std::map::const_iterator it = - material_map.find(namebuf); - if (it != material_map.end()) { - newMaterialId = it->second; - } else { - // { warn!! material not found } - if (warn && (!callback.usemtl_cb)) { - (*warn) += "material [ " + namebuf + " ] not found in .mtl\n"; - } - } - - if (newMaterialId != material_id) { - material_id = newMaterialId; - } - - if (callback.usemtl_cb) { - callback.usemtl_cb(user_data, namebuf.c_str(), material_id); - } - - continue; - } - - // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - if (readMatFn) { - token += 7; - - std::vector filenames; - SplitString(std::string(token), ' ', '\\', filenames); - - if (filenames.empty()) { - if (warn) { - (*warn) += - "Looks like empty filename for mtllib. Use default " - "material. \n"; - } - } else { - bool found = false; - for (size_t s = 0; s < filenames.size(); s++) { - if (material_filenames.count(filenames[s]) > 0) { - found = true; - continue; - } - - std::string warn_mtl; - std::string err_mtl; - bool ok = (*readMatFn)(filenames[s].c_str(), &materials, - &material_map, &warn_mtl, &err_mtl); - - if (warn && (!warn_mtl.empty())) { - (*warn) += warn_mtl; // This should be warn message. - } - - if (err && (!err_mtl.empty())) { - (*err) += err_mtl; - } - - if (ok) { - found = true; - material_filenames.insert(filenames[s]); - break; - } - } - - if (!found) { - if (warn) { - (*warn) += - "Failed to load material file(s). Use default " - "material.\n"; - } - } else { - if (callback.mtllib_cb) { - callback.mtllib_cb(user_data, &materials.at(0), - static_cast(materials.size())); - } - } - } - } - - continue; - } - - // group name - if (token[0] == 'g' && IS_SPACE((token[1]))) { - names.clear(); - - while (!IS_NEW_LINE(token[0]) && token[0] != '#') { - std::string str = parseString(&token); - names.push_back(str); - token += strspn(token, " \t\r"); // skip tag - } - - assert(names.size() > 0); - - if (callback.group_cb) { - if (names.size() > 1) { - // create const char* array. - names_out.resize(names.size() - 1); - for (size_t j = 0; j < names_out.size(); j++) { - names_out[j] = names[j + 1].c_str(); - } - callback.group_cb(user_data, &names_out.at(0), - static_cast(names_out.size())); - - } else { - callback.group_cb(user_data, NULL, 0); - } - } - - continue; - } - - // object name - if (token[0] == 'o' && IS_SPACE((token[1]))) { - // @todo { multiple object name? } - token += 2; - - std::stringstream ss; - ss << token; - std::string object_name = ss.str(); - - if (callback.object_cb) { - callback.object_cb(user_data, object_name.c_str()); - } - - continue; - } - -#if 0 // @todo - if (token[0] == 't' && IS_SPACE(token[1])) { - tag_t tag; - - token += 2; - std::stringstream ss; - ss << token; - tag.name = ss.str(); - - token += tag.name.size() + 1; - - tag_sizes ts = parseTagTriple(&token); - - tag.intValues.resize(static_cast(ts.num_ints)); - - for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { - tag.intValues[i] = atoi(token); - token += strcspn(token, "/ \t\r") + 1; - } - - tag.floatValues.resize(static_cast(ts.num_reals)); - for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { - tag.floatValues[i] = parseReal(&token); - token += strcspn(token, "/ \t\r") + 1; - } - - tag.stringValues.resize(static_cast(ts.num_strings)); - for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { - std::stringstream ss; - ss << token; - tag.stringValues[i] = ss.str(); - token += tag.stringValues[i].size() + 1; - } - - tags.push_back(tag); - } -#endif - - // Ignore unknown command. - } - - if (err) { - (*err) += errss.str(); - } - - return true; -} - -bool ObjReader::ParseFromFile(const std::string &filename, - const ObjReaderConfig &config) { - std::string mtl_search_path; - - if (config.mtl_search_path.empty()) { - // - // split at last '/'(for unixish system) or '\\'(for windows) to get - // the base directory of .obj file - // - size_t pos = filename.find_last_of("/\\"); - if (pos != std::string::npos) { - mtl_search_path = filename.substr(0, pos); - } - } else { - mtl_search_path = config.mtl_search_path; - } - - valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, - filename.c_str(), mtl_search_path.c_str(), - config.triangulate, config.vertex_color); - - return valid_; -} - -bool ObjReader::ParseFromString(const std::string &obj_text, - const std::string &mtl_text, - const ObjReaderConfig &config) { - std::stringbuf obj_buf(obj_text); - std::stringbuf mtl_buf(mtl_text); - - std::istream obj_ifs(&obj_buf); - std::istream mtl_ifs(&mtl_buf); - - MaterialStreamReader mtl_ss(mtl_ifs); - - valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, - &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color); - - return valid_; -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -} // namespace tinyobj - -#endif diff --git a/tinyobjloader-config.cmake.in b/tinyobjloader-config.cmake.in deleted file mode 100644 index 91f01b0e..00000000 --- a/tinyobjloader-config.cmake.in +++ /dev/null @@ -1,9 +0,0 @@ -@PACKAGE_INIT@ - -set(TINYOBJLOADER_VERSION "@TINYOBJLOADER_VERSION@") - -set_and_check(TINYOBJLOADER_INCLUDE_DIRS "@PACKAGE_TINYOBJLOADER_INCLUDE_DIR@") -set_and_check(TINYOBJLOADER_LIBRARY_DIRS "@PACKAGE_TINYOBJLOADER_LIBRARY_DIR@") -set(TINYOBJLOADER_LIBRARIES @LIBRARY_NAME@) - -include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") diff --git a/tinyobjloader.pc.in b/tinyobjloader.pc.in deleted file mode 100644 index 048a2873..00000000 --- a/tinyobjloader.pc.in +++ /dev/null @@ -1,15 +0,0 @@ -# Generated by CMake @CMAKE_VERSION@ for @PROJECT_NAME@. Any changes to this -# file will be overwritten by the next CMake run. The input file was -# tinyobjloader.pc.in. - -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -libdir=${prefix}/@TINYOBJLOADER_LIBRARY_DIR@ -includedir=${prefix}/@TINYOBJLOADER_INCLUDE_DIR@ - -Name: @PROJECT_NAME@ -Description: Tiny but powerful single file wavefront obj loader -URL: https://syoyo.github.io/tinyobjloader/ -Version: @TINYOBJLOADER_VERSION@ -Libs: -L${libdir} -l@LIBRARY_NAME@ -Cflags: -I${includedir} diff --git a/tools/windows/premake5.exe b/tools/windows/premake5.exe deleted file mode 100644 index 51c05a80..00000000 Binary files a/tools/windows/premake5.exe and /dev/null differ diff --git a/vcsetup.bat b/vcsetup.bat deleted file mode 100644 index 921c1e9e..00000000 --- a/vcsetup.bat +++ /dev/null @@ -1 +0,0 @@ -.\\tools\\windows\\premake5.exe vs2013