diff --git a/.gitignore b/.gitignore index 69c6493..f8050cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .vscode/ .cache/ build/ -doc/ +doc/_build .idea/ .DS_Store CMakeLists.txt.user diff --git a/.gitmodules b/.gitmodules index 7124775..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "externals/tinyobjloader"] - path = externals/tinyobjloader - url = https://github.com/tinyobjloader/tinyobjloader.git diff --git a/CITATION.cff b/CITATION.cff index b6c6e66..ceb0eed 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -31,7 +31,7 @@ authors: family-names: Boudon affiliation: CIRAD orcid: 'https://orcid.org/0000-0001-9636-3102' -repository-code: 'https://github.com/openalea-incubator/spice' +repository-code: 'https://github.com/openalea/spice' keywords: - photon mapping - light simulation diff --git a/CMakeLists.txt b/CMakeLists.txt index e8f82ac..56443d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,16 @@ cmake_minimum_required(VERSION 3.20) project(photon_mapping LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD 26) option(BUILD_TESTS "build c++ tests" OFF) option(BUILD_CXX_EXAMPLE "build c++ example" OFF) +option(HIP "GPU support with HIP" OFF) + +# --- Setting flags +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + # --- Build setup find_package(TBB CONFIG REQUIRED) find_package(Threads REQUIRED) @@ -18,6 +23,7 @@ endif() # apple newer C++ features with old SDK if(APPLE) set(CMAKE_CXX_FLAGS "${CXXFLAGS} -D_LIBCPP_DISABLE_AVAILABILITY") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13") endif() # Embree4 @@ -29,9 +35,31 @@ else() message(FATAL_ERROR "Could not find Embree") endif() +# find tinyobjloader & tinygltf +include(FetchContent) + +set(TINYOBJLOADER_BUILD_TEST_LOADER OFF CACHE INTERNAL "") + +FetchContent_Declare( + tinyobjloader + GIT_REPOSITORY https://github.com/tinyobjloader/tinyobjloader.git + GIT_TAG release +) -# externals -add_subdirectory("externals") +set(TINYGLTF_BUILD_EXAMPLES OFF CACHE INTERNAL "") + +FetchContent_Declare( + tinygltf + GIT_REPOSITORY https://github.com/syoyo/tinygltf.git + GIT_TAG v2.9.7 +) + +# Bring both packages into the build tree +FetchContent_MakeAvailable(tinyobjloader tinygltf) + +if(HIP) + find_package(hiprt CONFIG REQUIRED) +endif() add_subdirectory("src/cpp/include") @@ -41,13 +69,13 @@ add_library(pm INTERFACE src/python/ext_photonmapping_nano.cpp) set_target_properties(pm PROPERTIES - CXX_STANDARD 23 + CXX_STANDARD 26 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS YES ) -target_link_libraries(pm INTERFACE TBB::tbb OpenMP::OpenMP_CXX tinyobjloader embree Threads::Threads photon_map primitive triangle image) +target_link_libraries(pm INTERFACE TBB::tbb OpenMP::OpenMP_CXX embree Threads::Threads photon_map primitive triangle image) # wrapper add_subdirectory("src/python/") diff --git a/conda/environment.yml b/conda/environment.yml index a43eaee..975532f 100644 --- a/conda/environment.yml +++ b/conda/environment.yml @@ -6,9 +6,11 @@ dependencies: - openalea.lpy - openalea.plantgl - openalea.widgets + - tinyobjloader - embree - make - cxx-compiler + - tinygltf - cmake - tbb - tbb-devel diff --git a/doc/Makefile b/doc/Makefile index 8b7a1b7..e99f4d1 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,17 +1,12 @@ -# {# pkglts, sphinx # Makefile for Sphinx documentation # # You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXATUOBUILD = sphinx-autobuild +PAPER = +BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 @@ -20,19 +15,22 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - +.PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" + @echo " rtdhtml Build html using same settings used on ReadtheDocs" + @echo " livehtml Make standalone HTML files and rebuild the documentation when a change is detected. Also includes a livereload enabled web server" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" + @echo " epub3 to make an epub3" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @@ -46,64 +44,112 @@ help: @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " dummy to check syntax errors of document sources" +.PHONY: clean clean: rm -rf $(BUILDDIR)/* + rm -rf generated/* + rm -rf auto_gallery/ +.PHONY: html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." +.PHONY: rtdhtml +rtdhtml: + $(SPHINXBUILD) -T -j auto -E -W --keep-going -b html -d $(BUILDDIR)/doctrees -D language=en . $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + + +.PHONY: livehtml +livehtml: + # @echo "$(SPHINXATUOBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html" + $(SPHINXATUOBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + +.PHONY: dirhtml dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." +.PHONY: singlehtml singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." +.PHONY: html-noplot +html-noplot: + $(SPHINXBUILD) -D plot_gallery=0 -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: pickle pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." +.PHONY: json json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." +.PHONY: htmlhelp htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." +.PHONY: qthelp qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pkglts.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/xarray.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pkglts.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/xarray.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." +.PHONY: devhelp devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/pkglts" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pkglts" + @echo "# mkdir -p $$HOME/.local/share/devhelp/xarray" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/xarray" @echo "# devhelp" +.PHONY: epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + +.PHONY: latex latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @@ -111,28 +157,33 @@ latex: @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." +.PHONY: latexpdf latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." +.PHONY: latexpdfja latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." +.PHONY: text text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." +.PHONY: man man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." +.PHONY: texinfo texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @@ -140,41 +191,58 @@ texinfo: @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." +.PHONY: info info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." +.PHONY: gettext gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." +.PHONY: changes changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." +.PHONY: linkcheck linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." +.PHONY: doctest doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." +.PHONY: pseudoxml pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." -# #} \ No newline at end of file +.PHONY: dummy +dummy: + $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy + @echo + @echo "Build finished. Dummy builder generates no files." \ No newline at end of file diff --git a/doc/index.rst b/doc/index.rst index 987fd63..e7e13ef 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -15,6 +15,7 @@ Openalea Spice documentation guide.rst code_example.rst notebook_examples.rst + references.rst .. include:: ../README.md diff --git a/doc/make.bat b/doc/make.bat deleted file mode 100644 index 9220cc0..0000000 --- a/doc/make.bat +++ /dev/null @@ -1,244 +0,0 @@ -REM {# pkglts, sphinx -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. xml to make Docutils-native XML files - echo. pseudoxml to make pseudoxml-XML files for display purposes - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - - -%SPHINXBUILD% 2> nul -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pkglts.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pkglts.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdf" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf - cd %BUILDDIR%/.. - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %BUILDDIR%/.. - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -if "%1" == "xml" ( - %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The XML files are in %BUILDDIR%/xml. - goto end -) - -if "%1" == "pseudoxml" ( - %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. - goto end -) - -:end -REM #} diff --git a/doc/references.rst b/doc/references.rst new file mode 100644 index 0000000..dbc3ded --- /dev/null +++ b/doc/references.rst @@ -0,0 +1,15 @@ +References +======================================= + +original photon mapping git: https://github.com/yumcyaWiz/photon_mapping + +* Jensen, Henrik Wann. Realistic image synthesis using photon mapping. AK Peters/crc Press, 2001. +* https://pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/Stochastic_Progressive_Photon_Mapping# +* http://www.cs.cmu.edu/afs/cs/academic/class/15462-s12/www/lec_slides/lec18.pdf +* `memoRANDOM` +* `McGuire Computer Graphics Archive` +* `Rendering Resources | Benedikt Bitterli's Portfolio` +* `Jensen, Henrik Wann. "Global illumination using photon maps." Eurographics workshop on Rendering techniques. Springer, Vienna, 1996.` +* Veach, Eric. Robust Monte Carlo methods for light transport simulation. Stanford University, 1998. +* `Christensen, Per H. "Faster photon map global illumination." Journal of graphics tools 4.3 (1999): 1-10.` +* `Hachisuka, Toshiya, Jacopo Pantaleoni, and Henrik Wann Jensen. "A path space extension for robust light transport simulation." ACM Transactions on Graphics (TOG) 31.6 (2012): 1-10.` diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index d22f844..ec90ace 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -1,3 +1,3 @@ add_executable(main "main.cpp") -target_link_libraries(main PUBLIC pm) -target_include_directories(main INTERFACE "../../src/cpp/include") +target_link_libraries(main PUBLIC pm image triangle primitive photon_map) +target_include_directories(main PUBLIC "../../src/cpp/include") \ No newline at end of file diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt deleted file mode 100644 index 8cacb8c..0000000 --- a/externals/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -# tinyobjloader -add_library(tinyobjloader INTERFACE) -target_compile_definitions(tinyobjloader INTERFACE TINYOBJLOADER_IMPLEMENTATION) -target_include_directories(tinyobjloader SYSTEM INTERFACE tinyobjloader) \ No newline at end of file diff --git a/externals/tinyobjloader/.clang-format b/externals/tinyobjloader/.clang-format deleted file mode 100644 index 74210b0..0000000 --- a/externals/tinyobjloader/.clang-format +++ /dev/null @@ -1,7 +0,0 @@ ---- -BasedOnStyle: Google -IndentWidth: 2 -TabWidth: 2 -UseTab: Never -BreakBeforeBraces: Attach -Standard: Cpp03 diff --git a/externals/tinyobjloader/.drone.yml b/externals/tinyobjloader/.drone.yml deleted file mode 100644 index 971557f..0000000 --- a/externals/tinyobjloader/.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/externals/tinyobjloader/.github/ISSUE_TEMPLATE/config.yml b/externals/tinyobjloader/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3ba13e0..0000000 --- a/externals/tinyobjloader/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false diff --git a/externals/tinyobjloader/.github/ISSUE_TEMPLATE/issue-report.md b/externals/tinyobjloader/.github/ISSUE_TEMPLATE/issue-report.md deleted file mode 100644 index 1361a32..0000000 --- a/externals/tinyobjloader/.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/externals/tinyobjloader/.gitignore b/externals/tinyobjloader/.gitignore deleted file mode 100644 index 0f8d620..0000000 --- a/externals/tinyobjloader/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -#common folder for building objects -build/ -/python/dist/ -/python/*.egg-info -/python/.eggs -/python/tiny_obj_loader.h diff --git a/externals/tinyobjloader/.travis.yml b/externals/tinyobjloader/.travis.yml deleted file mode 100644 index 12b67f2..0000000 --- a/externals/tinyobjloader/.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/externals/tinyobjloader/CMakeLists.txt b/externals/tinyobjloader/CMakeLists.txt deleted file mode 100644 index f64b42c..0000000 --- a/externals/tinyobjloader/CMakeLists.txt +++ /dev/null @@ -1,165 +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. -project(tinyobjloader) -cmake_minimum_required(VERSION 3.2) -set(TINYOBJLOADER_SOVERSION 2) -set(TINYOBJLOADER_VERSION 2.0.0-rc.9) - -#optional double precision support -option(TINYOBJLOADER_USE_DOUBLE "Build library with double precision instead of single (float)" OFF) - -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) - -add_library(${LIBRARY_NAME} ${tinyobjloader-Source}) - -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() - -#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/externals/tinyobjloader/LICENSE b/externals/tinyobjloader/LICENSE deleted file mode 100644 index e9fbe44..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/README.md b/externals/tinyobjloader/README.md deleted file mode 100644 index 143e042..0000000 --- a/externals/tinyobjloader/README.md +++ /dev/null @@ -1,431 +0,0 @@ -# tinyobjloader - -[![Build Status](https://travis-ci.org/tinyobjloader/tinyobjloader.svg?branch=master)](https://travis-ci.org/tinyobjloader/tinyobjloader) - -[![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) - -[![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) - -[![Coverage Status](https://coveralls.io/repos/github/syoyo/tinyobjloader/badge.svg?branch=master)](https://coveralls.io/github/syoyo/tinyobjloader?branch=master) - -[![AUR version](https://img.shields.io/aur/version/tinyobjloader?logo=arch-linux)](https://aur.archlinux.org/packages/tinyobjloader) - -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. - -`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) - -If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c . - -Version notice --------------- - -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). - -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 - -## What's new - -* 29 Jul, 2021 : Added Mapbox's earcut for robust triangulation. Also fixes triangulation bug. -* 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! - -## Requirements - -* C++03 compiler - -### Old version - -Previous old version is available in `v0.9.x` branch. - -## Example - -![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 -* Your project here! (Letting us know via github issue is welcome!) - -### 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 -* Material - * Unknown material attributes are returned as key-value(value is string) map. -* Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification) -* PBR material extension for .MTL. Its proposed here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr -* 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 - - -## TODO - -* [ ] Fix obj_sticker example. -* [ ] More unit test codes. -* [x] Texture options - -## 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) - -Alghouth 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) - - 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 | - +-----------+-----------+-----------+-----------+ +-----------+ - -``` - -Each `shape_t::mesh_t` does not contain vertex data but contains array index to `attrib_t`. -See `loader_example.cc` for more details. - - -``` - -mesh_t::indices => array of vertex indices. - - +----+----+----+----+----+----+----+----+----+----+ +--------+ - | 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 trinagulation 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 trinagulation. 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 trinagulation. 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. - -Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core m5 1.2GHz). - -* 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) - -## Python binding - -### CI + PyPI upload - -cibuildwheels + twine upload for each git tagging event is handled in Azure Pipeline. - -#### How to bump version(For developer) - -* Bump version in CMakeLists.txt -* Update version in `python/setup.py` -* Commit and push `master`. Confirm C.I. build is OK. -* Create tag starting with `v`(e.g. `v2.1.0`) -* `git push --tags` - * cibuildwheels + pypi upload(through twine) will be automatically triggered in Azure Pipeline. - -## Tests - -Unit tests are provided in `tests` directory. See `tests/README.md` for details. diff --git a/externals/tinyobjloader/appveyor.yml b/externals/tinyobjloader/appveyor.yml deleted file mode 100644 index 93f691c..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/azure-pipelines.yml b/externals/tinyobjloader/azure-pipelines.yml deleted file mode 100644 index a71c43b..0000000 --- a/externals/tinyobjloader/azure-pipelines.yml +++ /dev/null @@ -1,163 +0,0 @@ -variables: - # https://cibuildwheel.readthedocs.io/en/stable/cpp_standards/ - # python2.7 + C++11(pybind11) is not available. - CIBW_SKIP: "cp27-win* pp27-win32 pp36-win32" - CIBW_BEFORE_BUILD: "pip install pybind11" - #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: | - pip install black==19.10b0 - black --check python/ - displayName: Check Python code format - - - job: linux - pool: {vmImage: "Ubuntu-16.04"} - steps: - - task: UsePythonVersion@0 - - bash: | - python3 -m pip install --upgrade pip - pip3 install cibuildwheel twine - # Make the header files available to the build. - cp *.h python - mkdir python/mapbox - cp mapbox/earcut.hpp python/mapbox/ - cd python - - # Source dist - python3 setup.py sdist - ls -la dist/* - - # build binary wheels - cibuildwheel --output-dir wheelhouse . - - - task: CopyFiles@2 - inputs: - contents: 'python/wheelhouse/**' - targetFolder: $(Build.ArtifactStagingDirectory) - - - task: CopyFiles@2 - inputs: - contents: 'python/dist/**' - targetFolder: $(Build.ArtifactStagingDirectory) - - - task: PublishBuildArtifacts@1 - inputs: - path: $(Build.ArtifactStagingDirectory) - artifactName: tinyobjDeployLinux - - - job: macos - pool: {vmImage: 'macOS-10.15'} - 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 - # Make the header files available to the build. - cp *.h python - mkdir python/mapbox - cp mapbox/earcut.hpp python/mapbox/earcut.hpp - cd python - cibuildwheel --output-dir wheelhouse . - - task: CopyFiles@2 - inputs: - contents: 'python/wheelhouse/*.whl' - targetFolder: $(Build.ArtifactStagingDirectory) - - task: PublishBuildArtifacts@1 - inputs: - path: $(Build.ArtifactStagingDirectory) - artifactName: tinyobjDeployMacOS - - - job: windows - pool: {vmImage: 'windows-2019'} - steps: - - task: UsePythonVersion@0 - - bash: | - python -m pip install --upgrade pip - pip install cibuildwheel - # Make the header files available to the build. - cp *.h python - mkdir python/mapbox - cp mapbox/earcut.hpp python/mapbox/ - cd python - cibuildwheel --output-dir wheelhouse . - - task: CopyFiles@2 - inputs: - contents: 'python/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-16.04'} - 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/python/dist/* - echo tinyobjDeployLinux/python/wheelhouse/* tinyobjDeployMacOS/python/wheelhouse/* tinyobjDeployWindows/python/wheelhouse/* - twine upload -u "__token__" --skip-existing tinyobjDeployLinux/python/dist/* tinyobjDeployLinux/python/wheelhouse/* tinyobjDeployMacOS/python/wheelhouse/* tinyobjDeployWindows/python/wheelhouse/* - env: - TWINE_PASSWORD: $(pypiToken2) - -trigger: - branches: - include: - - '*' - tags: - include: - - 'v*' - -pr: - branches: - include: - - "*" diff --git a/externals/tinyobjloader/build.ninja b/externals/tinyobjloader/build.ninja deleted file mode 100644 index 77801bb..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/cmake_uninstall.cmake.in b/externals/tinyobjloader/cmake_uninstall.cmake.in deleted file mode 100644 index 2037e36..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/deps/cpplint.py b/externals/tinyobjloader/deps/cpplint.py deleted file mode 100755 index ccc25d4..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/callback_api/Makefile b/externals/tinyobjloader/examples/callback_api/Makefile deleted file mode 100644 index 45d60d8..0000000 --- a/externals/tinyobjloader/examples/callback_api/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - clang++ -I../../ -Wall -g -o example main.cc diff --git a/externals/tinyobjloader/examples/callback_api/main.cc b/externals/tinyobjloader/examples/callback_api/main.cc deleted file mode 100644 index 1911501..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/obj_sticher/obj_sticher.cc b/externals/tinyobjloader/examples/obj_sticher/obj_sticher.cc deleted file mode 100644 index 1057d47..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/obj_sticher/obj_writer.cc b/externals/tinyobjloader/examples/obj_sticher/obj_writer.cc deleted file mode 100644 index 9ea8d7c..0000000 --- a/externals/tinyobjloader/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.specular[1], mat.specular[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/externals/tinyobjloader/examples/obj_sticher/obj_writer.h b/externals/tinyobjloader/examples/obj_sticher/obj_writer.h deleted file mode 100644 index 262c681..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/obj_sticher/premake4.lua b/externals/tinyobjloader/examples/obj_sticher/premake4.lua deleted file mode 100644 index 9c2deb6..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/skin_weight/Makefile b/externals/tinyobjloader/examples/skin_weight/Makefile deleted file mode 100644 index 59e4e3c..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/skin_weight/README.md b/externals/tinyobjloader/examples/skin_weight/README.md deleted file mode 100644 index 800d847..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/skin_weight/main.cc b/externals/tinyobjloader/examples/skin_weight/main.cc deleted file mode 100644 index a3770af..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/viewer/README.md b/externals/tinyobjloader/examples/viewer/README.md deleted file mode 100644 index 76207bf..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/viewer/premake4.lua b/externals/tinyobjloader/examples/viewer/premake4.lua deleted file mode 100644 index 4e1e54f..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/viewer/stb_image.h b/externals/tinyobjloader/examples/viewer/stb_image.h deleted file mode 100644 index a3c1129..0000000 --- a/externals/tinyobjloader/examples/viewer/stb_image.h +++ /dev/null @@ -1,6755 +0,0 @@ -/* stb_image - v2.12 - public domain image loader - http://nothings.org/stb_image.h - 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-bit-per-channel (16 bpc not supported) - - 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. - - - Revision 2.00 release notes: - - - Progressive JPEG is now supported. - - - PPM and PGM binary formats are now supported, thanks to Ken Miller. - - - x86 platforms now make use of SSE2 SIMD instructions for - JPEG decoding, and ARM platforms can use NEON SIMD if requested. - This work was done by Fabian "ryg" Giesen. SSE2 is used by - default, but NEON must be enabled explicitly; see docs. - - With other JPEG optimizations included in this version, we see - 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup - on a JPEG on an ARM machine, relative to previous versions of this - library. The same results will not obtain for all JPGs and for all - x86/ARM machines. (Note that progressive JPEGs are significantly - slower to decode than regular JPEGs.) This doesn't mean that this - is the fastest JPEG decoder in the land; rather, it brings it - closer to parity with standard libraries. If you want the fastest - decode, look elsewhere. (See "Philosophy" section of docs below.) - - See final bullet items below for more info on SIMD. - - - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing - the memory allocator. Unlike other STBI libraries, these macros don't - support a context parameter, so if you need to pass a context in to - the allocator, you'll have to store it in a global or a thread-local - variable. - - - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and - STBI_NO_LINEAR. - STBI_NO_HDR: suppress implementation of .hdr reader format - STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API - - - 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) - - Note that you can define multiples of these, and you will get all - of them ("only x" and "only y" is interpreted to mean "only x&y"). - - - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still - want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - - - Compilation of all SIMD code can be suppressed with - #define STBI_NO_SIMD - It should not be necessary to disable SIMD unless you have issues - compiling (e.g. using an x86 compiler which doesn't support SSE - intrinsics or that doesn't support the method used to detect - SSE2 support at run-time), and even those can be reported as - bugs so I can refine the built-in compile-time checking to be - smarter. - - - The old STBI_SIMD system which allowed installing a user-defined - IDCT etc. has been removed. If you need this, don't upgrade. My - assumption is that almost nobody was doing this, and those who - were will find the built-in SIMD more satisfactory anyway. - - - RGB values computed for JPEG images are slightly different from - previous versions of stb_image. (This is due to using less - integer precision in SIMD.) The C code has been adjusted so - that the same RGB values will be computed regardless of whether - SIMD support is available, so your app should always produce - consistent results. But these results are slightly different from - previous versions. (Specifically, about 3% of available YCbCr values - will compute different RGB results from pre-1.49 versions by +-1; - most of the deviating values are one smaller in the G channel.) - - - If you must produce consistent results with previous versions of - stb_image, #define STBI_JPEG_OLD and you will get the same results - you used to; however, you will not get the SIMD speedups for - the YCbCr-to-RGB conversion step (although you should still see - significant JPEG speedup from the other changes). - - Please note that STBI_JPEG_OLD is a temporary feature; it will be - removed in future versions of the library. It is only intended for - near-term back-compatibility use. - - - Latest revision history: - 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 - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) partial animated GIF support - limited 16-bit PSD support - minor bugs, code cleanup, and compiler warnings - 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) additional corruption checking - stbi_set_flip_vertically_on_load - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD - progressive JPEG - PGM/PPM support - STBI_MALLOC,STBI_REALLOC,STBI_FREE - STBI_NO_*, STBI_ONLY_* - GIF bugfix - - 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) - urraka@github (animated gif) Junggon Kim (PNM comments) - Daniel Gibson (16-bit TGA) - - Optimizations & bugfixes - Fabian "ryg" Giesen - Arseny Kapoulkine - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson - Dave Moore Roy Eltham Hayaki Saito Phil Jordan - Won Chun Luke Graham Johan Duparc Nathan Reed - the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis - Janez Zemva John Bartholomew Michal Cichon svdijk@github - Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson - Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github - Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan - Ryamond Barbiero Paul Du Bois Engin Manap snagar@github - Michaelangel007@github Oriol Ferrer Mesia socks-the-fox - Blazej Dariusz Roszkowski - - -LICENSE - -This software is dual-licensed to the public domain and under the following -license: you are granted a perpetual, irrevocable license to copy, modify, -publish, and distribute this file as you see fit. - -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 16-bit-per-channel PNG -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - no 1-bit BMP -// - 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 *comp -- outputs # of image components in image file -// int req_comp -- 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 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp 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, *comp 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. -// -// =========================================================================== -// -// 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 -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small 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. -// -// The output of the JPEG decoder is slightly different from versions where -// SIMD support was introduced (that is, for versions before 1.49). The -// difference is only +-1 in the 8-bit RGB channels, and only on a small -// fraction of pixels. You can force the pre-1.49 behavior by defining -// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path -// and hence cost some performance. -// -// 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 now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. 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: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// 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). -// - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for req_comp - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#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; - -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int 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); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); - #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 -// NOT 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); - -#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); - -#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); - -// 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 - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#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) >> (32 - (y)))) -#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(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// NOTE: not clear do we actually need this for the 64-bit path? -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; -// this is just broken and gcc are jerks for not fixing it properly -// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) -#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 - -static int stbi__sse2_available() -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -static int stbi__sse2_available() -{ -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later - // GCC 4.8+ has a nice way to do this - return __builtin_cpu_supports("sse2"); -#else - // portable way to do this, preferably without using GCC inline ASM? - // just bail for now. - return 0; -#endif -} -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#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]; - - 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->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->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) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((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; -} - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -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 stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -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 stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -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 stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -#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); -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 stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -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 stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, 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 stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// 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 = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load = flag_true_if_should_flip; -} - -static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); - 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); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); - - if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - stbi_uc temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } - } - } - - return result; -} - -#ifndef STBI_NO_HDR -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 w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - float temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } - } - } -} -#endif - -#ifndef STBI_NO_STDIO - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if 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_flip(&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; -} -#endif //!STBI_NO_STDIO - -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_flip(&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_flip(&s,x,y,comp,req_comp); -} - -#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)) { - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_flip(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 - stbi__context s; - stbi__start_file(&s,f); - return stbi__hdr_test(&s); - #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); - 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; -} - -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; -} - -static void stbi__skip(stbi__context *s, int n) -{ - 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; -} - -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; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -#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); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - - -////////////////////////////////////////////////////////////////////////////// -// -// 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); -} - -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(req_comp * x * y); - 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 COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case 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 (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; - default: STBI_ASSERT(0); - } - #undef CASE - } - - STBI_FREE(data); - return good; -} - -#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 = (float *) stbi__malloc(x * y * comp * sizeof(float)); - 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 (k < comp) output[i*comp + k] = data[i*comp+k]/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 = (stbi_uc *) stbi__malloc(x * y * comp); - 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_uc 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 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,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 >= (1 << 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 += (-1 << 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 << 8) + (run << 4) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - 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 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 = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// 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 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_uc *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) 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); - 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 << 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) << 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) << 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) << 12) - -// 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] << 2; - 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); - 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 = 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_uc *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; - int t = q & 15,i; - if (p != 0) 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__get8(z->s); - L -= 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) { - stbi__skip(z->s, stbi__get16be(z->s)-2); - return 1; - } - return 0; -} - -// 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__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 - c = stbi__get8(s); - if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires - 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 unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) { // some version of jpegtran outputs non-JFIF-compliant files! - // somethings output this (see http://fileformats.archiveteam.org/wiki/JPEG#Color_format) - if (z->img_comp[i].id != rgb[i]) - return stbi__err("bad component ID","Corrupt JPEG"); - ++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 ((1 << 30) / s->img_x / s->img_n < s->img_y) 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; - } - - // 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; - 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 - 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].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - } - return 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); - z->img_comp[i].linebuf = NULL; - if (z->progressive) { - z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; - z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; - z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } else { - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - } - } - - 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->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; - } else if (x != 0) { - return stbi__err("junk before marker", "Corrupt JPEG"); - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } 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; -} - -#ifdef STBI_JPEG_OLD -// this is the same YCbCr-to-RGB calculation that stb_image has used -// historically before the algorithm changes in 1.49 -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) -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] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - 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; - } -} -#else -// 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 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* float2fixed(1.40200f); - g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* 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 - -#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* float2fixed(1.40200f); - g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* 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; - #ifndef STBI_JPEG_OLD - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif - 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) -{ - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - STBI_FREE(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].raw_coeff) { - STBI_FREE(j->img_comp[i].raw_coeff); - j->img_comp[i].raw_coeff = 0; - j->img_comp[i].coeff = 0; - } - if (j->img_comp[i].linebuf) { - STBI_FREE(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } -} - -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; - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n; - 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; - - if (z->s->img_n == 3 && n < 3) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - 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(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 (z->rgb == 3) { - 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 - 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 { - 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; // report original components, not output - return output; - } -} - -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - 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; - j.s = s; - stbi__setup_jpeg(&j); - r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); - stbi__rewind(s); - 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; - 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))); - 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) - -// 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[288]; - stbi__uint16 value[288]; -} 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, 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 stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - 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]; - STBI_ASSERT(z->size[b] == s); - 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) 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; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - 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 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 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 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 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 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; - - 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 < hlit + hdist) { - 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 if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); - n += c; - } - } - if (n != hlit+hdist) 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; - } - STBI_ASSERT(a->num_bits == 0); - // 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 ((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; -} - -// @TODO: should statically initialize these for optimal thread safety -static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; -static void stbi__init_zdefaults(void) -{ - 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__zdefault_distance[31]) stbi__init_zdefaults(); - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) 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 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 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(x * y * output_bytes); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } else { // interlaced: - 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 = cur - stride; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - 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; - } - - // 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 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; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); 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] + cur[k-filter_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; - } - #undef CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define 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) { - CASE(STBI__F_none) cur[k] = raw[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); 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] + cur[k- output_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); break; - } - #undef 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) -{ - 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(a->s->img_x * a->s->img_y * out_n); - 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_n + out_x*out_n, - a->out + (j*x+i)*out_n, out_n); - } - } - 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(pixel_count * pal_img_n); - 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__reduce_png(stbi__png *p) -{ - int i; - int img_len = p->s->img_x * p->s->img_y * p->s->img_out_n; - stbi_uc *reduced; - stbi__uint16 *orig = (stbi__uint16*)p->out; - - if (p->depth != 16) return 1; // don't need to do anything if not 16-bit data - - reduced = (stbi_uc *)stbi__malloc(img_len); - if (p == NULL) return stbi__err("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 a decent approx of 16->8 bit scaling - - p->out = reduced; - STBI_FREE(orig); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -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) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / 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) (((a) << 24) + ((b) << 16) + ((c) << 8) + (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]; - 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); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) 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__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; - } - STBI_FREE(z->expanded); z->expanded = NULL; - 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 unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) -{ - unsigned char *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 == 16) { - if (!stbi__reduce_png(p)) { - return result; - } - } - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(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 unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); -} - -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); -} -#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, z >>= 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; -} - -static int stbi__shiftsigned(int v, int shift, int bits) -{ - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; -} stbi__bmp_data; - -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; - - 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 (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - 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) { - 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 { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - // 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 { - 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); - 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 stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - 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; - - 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); - - 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 - 14 - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; - } - - 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 - - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); - 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 - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - 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; - 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 - 14 - 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); - } - 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)); - 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; - // else: fall-through - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fall-through - 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 -void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = 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] = (r * 255)/31; - out[1] = (g * 255)/31; - out[2] = (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 stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - // 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]; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - - // 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; - - tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); - 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) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); - 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; - // 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 stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - int pixelCount; - int channelCount, compression; - int channel, i, count, len; - int bitdepth; - int w,h; - stbi_uc *out; - - // 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); - - // 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"); - - // Create the destination image. - 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 preceeded 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. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - 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 ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - } - } - - } 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 value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; - if (channel >= channelCount) { - // Fill this channel with default data. - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } else { - // Read the data. - if (bitdepth == 16) { - 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); - } - } - } - } - - if (channelCount >= 4) { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - // remove weird white matte from PSD - 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); - } - } - } - - if (req_comp && req_comp != 4) { - 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 stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) -{ - stbi_uc *result; - int i, x,y; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "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(x*y*4); - 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, *old_out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags, delay; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; - 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; -} 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 (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 (!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; - - // 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; - - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; - - if (c[3] >= 128) { - 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 > 4096) 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"); - } - } - } -} - -static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) -{ - int x, y; - stbi_uc *c = g->pal[g->bgindex]; - for (y = y0; y < y1; y += 4 * g->w) { - for (x = x0; x < x1; x += 4) { - stbi_uc *p = &g->out[y + x]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = 0; - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) -{ - int i; - stbi_uc *prev_out = 0; - - if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) - return 0; // stbi__g_failure_reason set by stbi__gif_header - - prev_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - - switch ((g->eflags & 0x1C) >> 2) { - case 0: // unspecified (also always used on 1st frame) - stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); - break; - case 1: // do not dispose - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - g->old_out = prev_out; - break; - case 2: // dispose to background - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); - break; - case 3: // dispose to previous - if (g->old_out) { - for (i = g->start_y; i < g->max_y; i += 4 * g->w) - memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); - } - break; - } - - for (;;) { - switch (stbi__get8(s)) { - case 0x2C: /* Image Descriptor */ - { - int prev_trans = -1; - 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; - - 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) { - if (g->transparent >= 0 && (g->eflags & 0x01)) { - prev_trans = g->pal[g->transparent][3]; - g->pal[g->transparent][3] = 0; - } - 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 == NULL) return NULL; - - if (prev_trans != -1) - g->pal[g->transparent][3] = (stbi_uc) prev_trans; - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = stbi__get16le(s); - g->transparent = stbi__get8(s); - } 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"); - } - } - - STBI_NOTUSED(req_comp); -} - -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *u = 0; - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - memset(g, 0, sizeof(*g)); - - u = stbi__gif_load_next(s, g, comp, req_comp); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g->w; - *y = g->h; - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g->w, g->h); - } - else if (g->out) - STBI_FREE(g->out); - STBI_FREE(g); - 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 = "#?RADIANCE\n"; - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s); - 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) -{ - 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; - - - // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 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); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); - - // 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(width * 4); - - for (k = 0; k < 4; ++k) { - i = 0; - while (i < width) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - 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); - } - 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; - - 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); - stbi__rewind( s ); - if (p == NULL) - return 0; - *x = s->img_x; - *y = s->img_y; - *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; - 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); - if (stbi__get16be(s) != 8) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - 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; - stbi__pic_packet packets[10]; - - 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) -// Does not support 16-bit-per-channel - -#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 stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *out; - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - *x = s->img_x; - *y = s->img_y; - *comp = s->img_n; - - out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - 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; - char c, p, t; - - 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 > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#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"); -} - -#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; -} -#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); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 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 -*/ diff --git a/externals/tinyobjloader/examples/viewer/trackball.cc b/externals/tinyobjloader/examples/viewer/trackball.cc deleted file mode 100644 index 86ff3b3..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/viewer/trackball.h b/externals/tinyobjloader/examples/viewer/trackball.h deleted file mode 100644 index b1f9437..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/viewer/viewer.cc b/externals/tinyobjloader/examples/viewer/viewer.cc deleted file mode 100644 index bc959c9..0000000 --- a/externals/tinyobjloader/examples/viewer/viewer.cc +++ /dev/null @@ -1,892 +0,0 @@ -// -// Simple .obj viewer(vertex only) -// -#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]; - -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; - } -} - -// 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 -} // 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 attrib; - std::vector shapes; - - 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(&attrib, &shapes, &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)(attrib.vertices.size()) / 3); - printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3); - printf("# of texcoords = %d\n", (int)(attrib.texcoords.size()) / 2); - printf("# of materials = %d\n", (int)materials.size()); - printf("# of shapes = %d\n", (int)shapes.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(); - - { - 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 (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); - - // 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); - 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 - 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; - } - - 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/externals/tinyobjloader/examples/voxelize/Makefile b/externals/tinyobjloader/examples/voxelize/Makefile deleted file mode 100644 index 98189d9..0000000 --- a/externals/tinyobjloader/examples/voxelize/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - g++ -o voxelizer main.cc diff --git a/externals/tinyobjloader/examples/voxelize/README.md b/externals/tinyobjloader/examples/voxelize/README.md deleted file mode 100644 index 8a113b6..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/voxelize/main.cc b/externals/tinyobjloader/examples/voxelize/main.cc deleted file mode 100644 index 6f11939..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/examples/voxelize/voxelizer.h b/externals/tinyobjloader/examples/voxelize/voxelizer.h deleted file mode 100644 index 4958695..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/README.md b/externals/tinyobjloader/experimental/README.md deleted file mode 100644 index a0603af..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/lfpAlloc/Allocator.hpp b/externals/tinyobjloader/experimental/lfpAlloc/Allocator.hpp deleted file mode 100644 index 4dddaab..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/lfpAlloc/ChunkList.hpp b/externals/tinyobjloader/experimental/lfpAlloc/ChunkList.hpp deleted file mode 100644 index 760c670..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/lfpAlloc/LICENSE b/externals/tinyobjloader/experimental/lfpAlloc/LICENSE deleted file mode 100644 index b9e2c10..0000000 --- a/externals/tinyobjloader/experimental/lfpAlloc/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Adam Schwalm - -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. \ No newline at end of file diff --git a/externals/tinyobjloader/experimental/lfpAlloc/Pool.hpp b/externals/tinyobjloader/experimental/lfpAlloc/Pool.hpp deleted file mode 100644 index 370dab7..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/lfpAlloc/PoolDispatcher.hpp b/externals/tinyobjloader/experimental/lfpAlloc/PoolDispatcher.hpp deleted file mode 100644 index e4d1427..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/lfpAlloc/Utils.hpp b/externals/tinyobjloader/experimental/lfpAlloc/Utils.hpp deleted file mode 100644 index 8740a79..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/premake5.lua b/externals/tinyobjloader/experimental/premake5.lua deleted file mode 100644 index 29511e1..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/tinyobj_loader_opt.h b/externals/tinyobjloader/experimental/tinyobj_loader_opt.h deleted file mode 100644 index d2b007d..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/trackball.cc b/externals/tinyobjloader/experimental/trackball.cc deleted file mode 100644 index 27642e8..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/trackball.h b/externals/tinyobjloader/experimental/trackball.h deleted file mode 100644 index b1f9437..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/experimental/viewer.cc b/externals/tinyobjloader/experimental/viewer.cc deleted file mode 100644 index 4886b78..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/fuzzer/README.md b/externals/tinyobjloader/fuzzer/README.md deleted file mode 100644 index 1cd63a2..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/fuzzer/afl.tar.gz b/externals/tinyobjloader/fuzzer/afl.tar.gz deleted file mode 100644 index 40a924f..0000000 Binary files a/externals/tinyobjloader/fuzzer/afl.tar.gz and /dev/null differ diff --git a/externals/tinyobjloader/fuzzer/fuzz_ParseFromString.cc b/externals/tinyobjloader/fuzzer/fuzz_ParseFromString.cc deleted file mode 100644 index aa45f89..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/fuzzer/regression_runner/Makefile b/externals/tinyobjloader/fuzzer/regression_runner/Makefile deleted file mode 100644 index f2c38a0..0000000 --- a/externals/tinyobjloader/fuzzer/regression_runner/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - clang++ -fsanitize=address,undefined ../../loader_example.cc diff --git a/externals/tinyobjloader/fuzzer/regression_runner/README.md b/externals/tinyobjloader/fuzzer/regression_runner/README.md deleted file mode 100644 index f59b9f6..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/fuzzer/regressions/clusterfuzz-testcase-minimized-fuzz_ParseFromString-4877060179886080 b/externals/tinyobjloader/fuzzer/regressions/clusterfuzz-testcase-minimized-fuzz_ParseFromString-4877060179886080 deleted file mode 100644 index e509449..0000000 Binary files a/externals/tinyobjloader/fuzzer/regressions/clusterfuzz-testcase-minimized-fuzz_ParseFromString-4877060179886080 and /dev/null differ diff --git a/externals/tinyobjloader/fuzzer/runner.py b/externals/tinyobjloader/fuzzer/runner.py deleted file mode 100644 index d9b5b61..0000000 --- a/externals/tinyobjloader/fuzzer/runner.py +++ /dev/null @@ -1,19 +0,0 @@ -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/externals/tinyobjloader/images/rungholt.jpg b/externals/tinyobjloader/images/rungholt.jpg deleted file mode 100644 index 17718eb..0000000 Binary files a/externals/tinyobjloader/images/rungholt.jpg and /dev/null differ diff --git a/externals/tinyobjloader/images/sanmugel.png b/externals/tinyobjloader/images/sanmugel.png deleted file mode 100644 index 9d6b5f0..0000000 Binary files a/externals/tinyobjloader/images/sanmugel.png and /dev/null differ diff --git a/externals/tinyobjloader/jni/Android.mk b/externals/tinyobjloader/jni/Android.mk deleted file mode 100644 index bc81f8f..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/jni/Application.mk b/externals/tinyobjloader/jni/Application.mk deleted file mode 100644 index e5d3191..0000000 --- a/externals/tinyobjloader/jni/Application.mk +++ /dev/null @@ -1,2 +0,0 @@ -APP_ABI := all -APP_STL := stlport_static diff --git a/externals/tinyobjloader/jni/Makefile b/externals/tinyobjloader/jni/Makefile deleted file mode 100644 index 1f13853..0000000 --- a/externals/tinyobjloader/jni/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - ndk-build diff --git a/externals/tinyobjloader/jni/README b/externals/tinyobjloader/jni/README deleted file mode 100644 index f93bc08..0000000 --- a/externals/tinyobjloader/jni/README +++ /dev/null @@ -1 +0,0 @@ -Just tests compilation with Android NDK r10. diff --git a/externals/tinyobjloader/loader_example.cc b/externals/tinyobjloader/loader_example.cc deleted file mode 100644 index 8143bb7..0000000 --- a/externals/tinyobjloader/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_thickness)); - 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/externals/tinyobjloader/mapbox/LICENSE b/externals/tinyobjloader/mapbox/LICENSE deleted file mode 100644 index 8bafb57..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/mapbox/earcut.hpp b/externals/tinyobjloader/mapbox/earcut.hpp deleted file mode 100644 index 01bd7e9..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/catmark_torus_creases0.obj b/externals/tinyobjloader/models/catmark_torus_creases0.obj deleted file mode 100644 index bf18f15..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/colorspace-issue-184.mtl b/externals/tinyobjloader/models/colorspace-issue-184.mtl deleted file mode 100644 index 7f3f9a4..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/colorspace-issue-184.obj b/externals/tinyobjloader/models/colorspace-issue-184.obj deleted file mode 100644 index ae589cb..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/cornell_box.mtl b/externals/tinyobjloader/models/cornell_box.mtl deleted file mode 100644 index d3a1c7a..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/cornell_box.obj b/externals/tinyobjloader/models/cornell_box.obj deleted file mode 100644 index 43e021f..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/cornell_box_multimaterial.obj b/externals/tinyobjloader/models/cornell_box_multimaterial.obj deleted file mode 100644 index 68093be..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/cube-vertexcol.obj b/externals/tinyobjloader/models/cube-vertexcol.obj deleted file mode 100644 index 494ce21..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/cube.mtl b/externals/tinyobjloader/models/cube.mtl deleted file mode 100644 index d3a1c7a..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/cube.obj b/externals/tinyobjloader/models/cube.obj deleted file mode 100644 index f12451f..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/invalid-face-definition.mtl b/externals/tinyobjloader/models/invalid-face-definition.mtl deleted file mode 100644 index d3a1c7a..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/invalid-face-definition.obj b/externals/tinyobjloader/models/invalid-face-definition.obj deleted file mode 100644 index 73ca678..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-138.mtl b/externals/tinyobjloader/models/issue-138.mtl deleted file mode 100644 index 8894d7e..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-138.obj b/externals/tinyobjloader/models/issue-138.obj deleted file mode 100644 index 2465920..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-140-zero-face-idx.mtl b/externals/tinyobjloader/models/issue-140-zero-face-idx.mtl deleted file mode 100644 index 990a345..0000000 --- a/externals/tinyobjloader/models/issue-140-zero-face-idx.mtl +++ /dev/null @@ -1,2 +0,0 @@ -newmtl main -Kd 1 1 1 diff --git a/externals/tinyobjloader/models/issue-140-zero-face-idx.obj b/externals/tinyobjloader/models/issue-140-zero-face-idx.obj deleted file mode 100644 index 21a6060..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-161-inconsistent-f.obj b/externals/tinyobjloader/models/issue-161-inconsistent-f.obj deleted file mode 100644 index 6cd8c43..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-162-smoothing-group.mtl b/externals/tinyobjloader/models/issue-162-smoothing-group.mtl deleted file mode 100644 index 8894d7e..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-162-smoothing-group.obj b/externals/tinyobjloader/models/issue-162-smoothing-group.obj deleted file mode 100644 index 0b0c8b3..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-235-usemtl-then-o.obj b/externals/tinyobjloader/models/issue-235-usemtl-then-o.obj deleted file mode 100644 index 170d33e..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-235.mtl b/externals/tinyobjloader/models/issue-235.mtl deleted file mode 100644 index 63c46a4..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-244-mtl-searchpaths.obj b/externals/tinyobjloader/models/issue-244-mtl-searchpaths.obj deleted file mode 100644 index 58abea1..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-246-usemtl-whitespace.mtl b/externals/tinyobjloader/models/issue-246-usemtl-whitespace.mtl deleted file mode 100644 index e7c91e3..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-246-usemtl-whitespace.obj b/externals/tinyobjloader/models/issue-246-usemtl-whitespace.obj deleted file mode 100644 index f159349..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-248-texres-texopt.mtl b/externals/tinyobjloader/models/issue-248-texres-texopt.mtl deleted file mode 100644 index 537fa15..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-248-texres-texopt.obj b/externals/tinyobjloader/models/issue-248-texres-texopt.obj deleted file mode 100644 index 43058a9..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-295-trianguation-failure.obj b/externals/tinyobjloader/models/issue-295-trianguation-failure.obj deleted file mode 100644 index f3b2649..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-92.mtl b/externals/tinyobjloader/models/issue-92.mtl deleted file mode 100644 index 5ebd668..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-92.obj b/externals/tinyobjloader/models/issue-92.obj deleted file mode 100644 index f7be3b6..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-95-2.mtl b/externals/tinyobjloader/models/issue-95-2.mtl deleted file mode 100644 index 68d484c..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-95-2.obj b/externals/tinyobjloader/models/issue-95-2.obj deleted file mode 100644 index 456f854..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-95.mtl b/externals/tinyobjloader/models/issue-95.mtl deleted file mode 100644 index 1d29fee..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/issue-95.obj b/externals/tinyobjloader/models/issue-95.obj deleted file mode 100644 index 8ee267e..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/leading-decimal-dot-issue-201.mtl b/externals/tinyobjloader/models/leading-decimal-dot-issue-201.mtl deleted file mode 100644 index 1d29fee..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/leading-decimal-dot-issue-201.obj b/externals/tinyobjloader/models/leading-decimal-dot-issue-201.obj deleted file mode 100644 index 202cfdc..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/leading-zero-in-exponent-notation-issue-210.mtl b/externals/tinyobjloader/models/leading-zero-in-exponent-notation-issue-210.mtl deleted file mode 100644 index 1d29fee..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/leading-zero-in-exponent-notation-issue-210.obj b/externals/tinyobjloader/models/leading-zero-in-exponent-notation-issue-210.obj deleted file mode 100644 index cf6f99b..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/line-prim.obj b/externals/tinyobjloader/models/line-prim.obj deleted file mode 100644 index 22f55d3..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/map-bump.mtl b/externals/tinyobjloader/models/map-bump.mtl deleted file mode 100644 index 6fb1291..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/map-bump.obj b/externals/tinyobjloader/models/map-bump.obj deleted file mode 100644 index 03071f1..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/missing_material_file.obj b/externals/tinyobjloader/models/missing_material_file.obj deleted file mode 100644 index 9e1d98c..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/mtl filename with whitespace issue46.mtl b/externals/tinyobjloader/models/mtl filename with whitespace issue46.mtl deleted file mode 100644 index b79d99b..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/mtl filename with whitespace issue46.obj b/externals/tinyobjloader/models/mtl filename with whitespace issue46.obj deleted file mode 100644 index 72d1dc9..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/mtllib-multiple-files-issue-112.mtl b/externals/tinyobjloader/models/mtllib-multiple-files-issue-112.mtl deleted file mode 100644 index 5ebd668..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/mtllib-multiple-files-issue-112.obj b/externals/tinyobjloader/models/mtllib-multiple-files-issue-112.obj deleted file mode 100644 index 9966dfb..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/no_material.obj b/externals/tinyobjloader/models/no_material.obj deleted file mode 100644 index 6f3688f..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/norm-texopt.mtl b/externals/tinyobjloader/models/norm-texopt.mtl deleted file mode 100644 index e2d4a2c..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/norm-texopt.obj b/externals/tinyobjloader/models/norm-texopt.obj deleted file mode 100644 index babe94d..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/pbr-mat-ext.mtl b/externals/tinyobjloader/models/pbr-mat-ext.mtl deleted file mode 100644 index bed905d..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/pbr-mat-ext.obj b/externals/tinyobjloader/models/pbr-mat-ext.obj deleted file mode 100644 index bb3e371..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/points-prim.obj b/externals/tinyobjloader/models/points-prim.obj deleted file mode 100644 index 21e89c0..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/refl.mtl b/externals/tinyobjloader/models/refl.mtl deleted file mode 100644 index 7e04f28..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/refl.obj b/externals/tinyobjloader/models/refl.obj deleted file mode 100644 index e9715f5..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/skin-weight.obj b/externals/tinyobjloader/models/skin-weight.obj deleted file mode 100644 index 41f182f..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/smoothing-group-two-squares.obj b/externals/tinyobjloader/models/smoothing-group-two-squares.obj deleted file mode 100644 index 5b21615..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/smoothing-normal.mtl b/externals/tinyobjloader/models/smoothing-normal.mtl deleted file mode 100644 index d3a1c7a..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/smoothing-normal.obj b/externals/tinyobjloader/models/smoothing-normal.obj deleted file mode 100644 index 8a89dec..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/test-nan.obj b/externals/tinyobjloader/models/test-nan.obj deleted file mode 100644 index 3c68925..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/texture-filename-with-whitespace.mtl b/externals/tinyobjloader/models/texture-filename-with-whitespace.mtl deleted file mode 100644 index 70b1a4e..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/texture-filename-with-whitespace.obj b/externals/tinyobjloader/models/texture-filename-with-whitespace.obj deleted file mode 100644 index 46e61e7..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/texture-options-issue-85.mtl b/externals/tinyobjloader/models/texture-options-issue-85.mtl deleted file mode 100644 index d4d62ad..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/texture-options-issue-85.obj b/externals/tinyobjloader/models/texture-options-issue-85.obj deleted file mode 100644 index f7abb0c..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/tr-and-d-issue-43.mtl b/externals/tinyobjloader/models/tr-and-d-issue-43.mtl deleted file mode 100644 index 44fd7ac..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/tr-and-d-issue-43.obj b/externals/tinyobjloader/models/tr-and-d-issue-43.obj deleted file mode 100644 index d86c3cf..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/usemtl-issue-104.obj b/externals/tinyobjloader/models/usemtl-issue-104.obj deleted file mode 100644 index 5183676..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/usemtl-issue-68.mtl b/externals/tinyobjloader/models/usemtl-issue-68.mtl deleted file mode 100644 index 24a791e..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/models/usemtl-issue-68.obj b/externals/tinyobjloader/models/usemtl-issue-68.obj deleted file mode 100644 index dddc900..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/premake4.lua b/externals/tinyobjloader/premake4.lua deleted file mode 100644 index cd5b295..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/python/LICENSE b/externals/tinyobjloader/python/LICENSE deleted file mode 100644 index 3af18ab..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/python/MANIFEST.in b/externals/tinyobjloader/python/MANIFEST.in deleted file mode 100644 index 90ef93d..0000000 --- a/externals/tinyobjloader/python/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -# Copy the header file into the python/ folder. -include ../tiny_obj_loader.h -# Include it in the source distribution. -include tiny_obj_loader.h - -include pyproject.toml diff --git a/externals/tinyobjloader/python/Makefile b/externals/tinyobjloader/python/Makefile deleted file mode 100644 index 06352ed..0000000 --- a/externals/tinyobjloader/python/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - python setup.py build - -t: - python sample.py - -.PHONY: t diff --git a/externals/tinyobjloader/python/README.md b/externals/tinyobjloader/python/README.md deleted file mode 100644 index 913668f..0000000 --- a/externals/tinyobjloader/python/README.md +++ /dev/null @@ -1,90 +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.x(3.6+ recommended) - * python 2.7 may work, but not officially supported. - -## 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 an recommended way to build a python module. -See $tinyobjloader/azure-pipelines.yml for details. - -### Developer build - -Edit `setup.py` and uncomment `Developer option` lines - -Assume pip is installed. - -``` -$ pip install pybind11 -$ python setup.py build -``` - -## License - -MIT(tinyobjloader) and ISC(mapbox earcut) license. - -## TODO - * [ ] Writer saver - diff --git a/externals/tinyobjloader/python/bindings.cc b/externals/tinyobjloader/python/bindings.cc deleted file mode 100644 index 08592d7..0000000 --- a/externals/tinyobjloader/python/bindings.cc +++ /dev/null @@ -1,163 +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("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_readonly("normals", &attrib_t::normals) - .def_readonly("texcoords", &attrib_t::texcoords) - .def_readonly("colors", &attrib_t::colors) - ; - - 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") - .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_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<>()); - - py::class_(tobj_module, "points_t") - .def(py::init<>()); - -} - diff --git a/externals/tinyobjloader/python/pyproject.toml b/externals/tinyobjloader/python/pyproject.toml deleted file mode 100644 index 51e27c4..0000000 --- a/externals/tinyobjloader/python/pyproject.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build-system] -requires = ["setuptools", "wheel", "pybind11>=2.3"] diff --git a/externals/tinyobjloader/python/sample.py b/externals/tinyobjloader/python/sample.py deleted file mode 100644 index 2f30c00..0000000 --- a/externals/tinyobjloader/python/sample.py +++ /dev/null @@ -1,78 +0,0 @@ -import sys -import tinyobjloader - -filename = "../models/cornell_box.obj" - - -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("attrib.vertices = ", len(attrib.vertices)) -print("attrib.normals = ", len(attrib.normals)) -print("attrib.texcoords = ", len(attrib.texcoords)) - -# 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 - -for (i, v) in enumerate(attrib.vertices): - print("v[{}] = {}".format(i, v)) - -for (i, v) in enumerate(attrib.normals): - print("vn[{}] = {}".format(i, v)) - -for (i, v) in enumerate(attrib.texcoords): - print("vt[{}] = {}".format(i, t)) - -print("numpy_vertices = {}".format(attrib.numpy_vertices())) - -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("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/externals/tinyobjloader/python/setup.py b/externals/tinyobjloader/python/setup.py deleted file mode 100644 index 061b27e..0000000 --- a/externals/tinyobjloader/python/setup.py +++ /dev/null @@ -1,130 +0,0 @@ -import setuptools - -from distutils.command.build_ext import build_ext - -with open("README.md", "r") as fh: - long_description = fh.read() - -# Adapted from https://github.com/pybind/python_example/blob/master/setup.py -class get_pybind_include(object): - """Helper class to determine the pybind11 include path - The purpose of this class is to postpone importing pybind11 - until it is actually installed, so that the ``get_include()`` - method can be invoked. """ - - def __init__(self, user=False, pep517=False): - self.user = user - self.pep517 = pep517 - - def __str__(self): - import os - import pybind11 - - interpreter_include_path = pybind11.get_include(self.user) - - if self.pep517: - # When pybind11 is installed permanently in site packages, the headers - # will be in the interpreter include path above. PEP 517 provides an - # experimental feature for build system dependencies. When installing - # a package from a source distribvution, first its build dependencies - # are installed in a temporary location. pybind11 does not return the - # correct path for this condition, so we glom together a second path, - # and ultimately specify them _both_ in the include search path. - # https://github.com/pybind/pybind11/issues/1067 - return os.path.abspath( - os.path.join( - os.path.dirname(pybind11.__file__), - "..", - "..", - "..", - "..", - "include", - os.path.basename(interpreter_include_path), - ) - ) - else: - return interpreter_include_path - - -# unix = default compiler name? -copt = {"unix": ["-std=c++11"], "gcc": ["-std=c++11"], "clang": ["std=c++11"]} -# TODO: set C++ version for msvc? {'msvc': ["/std:c++14"] } - -# ext_compile_args = ["-std=c++11"] -# ext_link_args = [] - -# https://stackoverflow.com/questions/724664/python-distutils-how-to-get-a-compiler-that-is-going-to-be-used -class build_ext_subclass(build_ext): - def build_extensions(self): - c = self.compiler.compiler_type - if c in copt: - for e in self.extensions: - e.extra_compile_args = copt[c] - - # if lopt.has_key(c): - # for e in self.extensions: - # e.extra_link_args = lopt[ c ] - build_ext.build_extensions(self) - - -# Developer option -# -# if platform.system() == "Darwin": -# # XCode10 or later does not support libstdc++, so we need to use libc++. -# # macosx-version 10.6 does not support libc++, so we require min macosx version 10.9. -# ext_compile_args.append("-stdlib=libc++") -# ext_compile_args.append("-mmacosx-version-min=10.9") -# ext_link_args.append("-stdlib=libc++") -# ext_link_args.append("-mmacosx-version-min=10.9") - -# `tiny_obj_loader.cc` contains implementation of tiny_obj_loader. -m = setuptools.Extension( - "tinyobjloader", - # extra_compile_args=ext_compile_args, - # extra_link_args=ext_link_args, - sources=["bindings.cc", "tiny_obj_loader.cc"], - include_dirs=[ - # Support `build_ext` finding tinyobjloader (without first running - # `sdist`). - "..", - # Support `build_ext` finding pybind 11 (provided it's permanently - # installed). - get_pybind_include(), - get_pybind_include(user=True), - # Support building from a source distribution finding pybind11 from - # a PEP 517 temporary install. - get_pybind_include(pep517=True), - ], - language="c++", -) - - -setuptools.setup( - name="tinyobjloader", - version="2.0.0rc9", - description="Tiny but powerful Wavefront OBJ loader", - long_description=long_description, - long_description_content_type="text/markdown", - author="Syoyo Fujita", - author_email="syoyo@lighttransport.com", - url="https://github.com/tinyobjloader/tinyobjloader", - project_urls={ - "Issue Tracker": "https://github.com/tinyobjloader/tinyobjloader/issues", - }, - 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", - ], - packages=setuptools.find_packages(), - ext_modules=[m], - cmdclass={"build_ext": build_ext_subclass}, -) diff --git a/externals/tinyobjloader/python/tiny_obj_loader.cc b/externals/tinyobjloader/python/tiny_obj_loader.cc deleted file mode 100644 index a0b8bc6..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/LICENSE.acutest.txt b/externals/tinyobjloader/tests/LICENSE.acutest.txt deleted file mode 100644 index 9730920..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/Makefile b/externals/tinyobjloader/tests/Makefile deleted file mode 100644 index 83d297d..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/README.md b/externals/tinyobjloader/tests/README.md deleted file mode 100644 index 1b0b43d..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/acutest.h b/externals/tinyobjloader/tests/acutest.h deleted file mode 100644 index 29a51c3..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/assets/issue-244.mtl b/externals/tinyobjloader/tests/assets/issue-244.mtl deleted file mode 100644 index 63c46a4..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/config-msvc.py b/externals/tinyobjloader/tests/config-msvc.py deleted file mode 100644 index a7771de..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/config-posix.py b/externals/tinyobjloader/tests/config-posix.py deleted file mode 100644 index 29cc4d5..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/issue-177.mtl b/externals/tinyobjloader/tests/issue-177.mtl deleted file mode 100644 index d3a1c7a..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/issue-177.obj b/externals/tinyobjloader/tests/issue-177.obj deleted file mode 100644 index 0d0ac68..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tests/kuroga.py b/externals/tinyobjloader/tests/kuroga.py deleted file mode 100755 index 32a318f..0000000 --- a/externals/tinyobjloader/tests/kuroga.py +++ /dev/null @@ -1,311 +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 os -import sys -import textwrap - - -# 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/externals/tinyobjloader/tests/tester.cc b/externals/tinyobjloader/tests/tester.cc deleted file mode 100644 index ebb5290..0000000 --- a/externals/tinyobjloader/tests/tester.cc +++ /dev/null @@ -1,1470 +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_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 -} - -// 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}, - {NULL, NULL}}; diff --git a/externals/tinyobjloader/tests/vcbuild.bat b/externals/tinyobjloader/tests/vcbuild.bat deleted file mode 100644 index e864673..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tiny_obj_loader.cc b/externals/tinyobjloader/tiny_obj_loader.cc deleted file mode 100644 index e57d044..0000000 --- a/externals/tinyobjloader/tiny_obj_loader.cc +++ /dev/null @@ -1,2 +0,0 @@ -#define TINYOBJLOADER_IMPLEMENTATION -#include "tiny_obj_loader.h" diff --git a/externals/tinyobjloader/tiny_obj_loader.h b/externals/tinyobjloader/tiny_obj_loader.h deleted file mode 100644 index c4bb454..0000000 --- a/externals/tinyobjloader/tiny_obj_loader.h +++ /dev/null @@ -1,3319 +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. -// * Support line primitive. -// * Support points primitive. -// * Support multiple search path for .mtl(v1 API). -// * Support vertex weight `vw`(as an tinyobj extension) -// * 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 - 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, - // ... Up to 255 vertices per face. - 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 (*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), - 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) {} - ~ObjReader() {} - - /// - /// 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 - -#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')) - -// Make index zero-base, and also support relative index. -static inline bool fixIndex(int idx, int n, int *ret) { - if (!ret) { - return false; - } - - if (idx > 0) { - (*ret) = idx - 1; - return true; - } - - if (idx == 0) { - // zero is not allowed according to the spec. - return false; - } - - if (idx < 0) { - (*ret) = n + idx; // negative value = relative - 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); -} - -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); -} - -// Extension: parse vertex with colors(6 items) -static inline bool 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) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); - - const bool found_color = - parseReal(token, r) && parseReal(token, g) && parseReal(token, b); - - if (!found_color) { - (*r) = (*g) = (*b) = 1.0; - } - - return found_color; -} - -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) { - if (!ret) { - return false; - } - - vertex_index_t vi(-1); - - if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) { - 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))) { - 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))) { - 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))) { - 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; -} - -// 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) { - 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 { - 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; - } - } - -#ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT - using Point = std::array; - - // first polyline define the main polygon. - // following polylines define holes(not used in tinyobj). - std::vector > polygon; - - std::vector polyline; - - // 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 + axes[0]]; - real_t v0y = v[vi0 * 3 + axes[1]]; - - polyline.push_back({v0x, v0y}); - } - - 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 - - - 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; - } - - // 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; - - // set new mtl name - token += 7; - { - std::stringstream sstr; - sstr << token; - material.name = sstr.str(); - } - 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 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)) && IS_SPACE(token[8])) { - token += 9; - ParseTextureNameAndOption(&(material.bump_texname), - &(material.bump_texopt), token); - continue; - } - - // bump texture - if ((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, "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 vn; - std::vector vt; - std::vector vc; - std::vector vw; - std::vector tags; - PrimGroup prim_group; - std::string name; - - // material - 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; - - 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; - } - - // 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; - - found_all_colors &= parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); - - v.push_back(x); - v.push_back(y); - v.push_back(z); - - if (found_all_colors || 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])) { - 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); - } - - // line - if (token[0] == 'l' && IS_SPACE((token[1]))) { - token += 2; - - __line_t line; - - while (!IS_NEW_LINE(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)) { - if (err) { - std::stringstream ss; - ss << "Failed parse `l' line(e.g. zero value for vertex index. " - "line " - << line_num << ".)\n"; - (*err) += ss.str(); - } - 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])) { - vertex_index_t vi; - if (!parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2), &vi)) { - if (err) { - std::stringstream ss; - ss << "Failed parse `p' line(e.g. zero value for vertex index. " - "line " - << line_num << ".)\n"; - (*err) += ss.str(); - } - 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])) { - vertex_index_t vi; - if (!parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2), &vi)) { - if (err) { - std::stringstream ss; - ss << "Failed parse `f' line(e.g. zero value for face index. line " - << line_num << ".)\n"; - (*err) += ss.str(); - } - 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++) { - 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; - 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])) { - 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(v); - 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::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; - // TODO(syoyo): Support parsing vertex color extension. - real_t x, y, z, w; // w is optional. default = 1.0 - parseV(&x, &y, &z, &w, &token); - if (callback.vertex_cb) { - callback.vertex_cb(user_data, x, y, z, w); - } - 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])) { - 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++) { - 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; - 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])) { - 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/externals/tinyobjloader/tinyobjloader-config.cmake.in b/externals/tinyobjloader/tinyobjloader-config.cmake.in deleted file mode 100644 index 91f01b0..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tinyobjloader.pc.in b/externals/tinyobjloader/tinyobjloader.pc.in deleted file mode 100644 index 048a287..0000000 --- a/externals/tinyobjloader/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/externals/tinyobjloader/tools/windows/premake5.exe b/externals/tinyobjloader/tools/windows/premake5.exe deleted file mode 100644 index 51c05a8..0000000 Binary files a/externals/tinyobjloader/tools/windows/premake5.exe and /dev/null differ diff --git a/externals/tinyobjloader/vcsetup.bat b/externals/tinyobjloader/vcsetup.bat deleted file mode 100644 index 921c1e9..0000000 --- a/externals/tinyobjloader/vcsetup.bat +++ /dev/null @@ -1 +0,0 @@ -.\\tools\\windows\\premake5.exe vs2013 diff --git a/pyproject.toml b/pyproject.toml index 931503b..40ba261 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,8 @@ dependencies = [ "embree", "llvm-openmp", "tbb", - "tbb-devel" + "tbb-devel", + "tinyobjloader", ] [tool.setuptools_scm] diff --git a/src/cpp/include/image.cpp b/src/cpp/include/image.cpp index b201008..2cc351b 100644 --- a/src/cpp/include/image.cpp +++ b/src/cpp/include/image.cpp @@ -1,4 +1,5 @@ #include "image.hpp" +#include Image::Image(unsigned int width, unsigned int height) : width(width) diff --git a/src/cpp/include/image.hpp b/src/cpp/include/image.hpp index b6a50d6..b2d1785 100644 --- a/src/cpp/include/image.hpp +++ b/src/cpp/include/image.hpp @@ -1,10 +1,6 @@ #ifndef IMAGE_H #define IMAGE_H -#include - -#include -#include #include #include diff --git a/src/cpp/include/material.hpp b/src/cpp/include/material.hpp index 0577d70..cd1eb39 100644 --- a/src/cpp/include/material.hpp +++ b/src/cpp/include/material.hpp @@ -4,7 +4,6 @@ #include "core.hpp" #include "sampler.hpp" #include -#include struct Material { diff --git a/src/cpp/include/photon_map.cpp b/src/cpp/include/photon_map.cpp index dd44c66..8ffe7a4 100644 --- a/src/cpp/include/photon_map.cpp +++ b/src/cpp/include/photon_map.cpp @@ -1,5 +1,5 @@ #include "photon_map.hpp" -#ifdef defined(_MSC_VER) +#ifdef _MSC_VER namespace std { #include diff --git a/src/cpp/include/primitive.hpp b/src/cpp/include/primitive.hpp index fad2b74..c79c947 100644 --- a/src/cpp/include/primitive.hpp +++ b/src/cpp/include/primitive.hpp @@ -1,7 +1,6 @@ #ifndef PRIMITIVE_H #define PRIMITIVE_H -#include #include #include diff --git a/src/cpp/include/scene.hpp b/src/cpp/include/scene.hpp index a8e40d2..8d95613 100644 --- a/src/cpp/include/scene.hpp +++ b/src/cpp/include/scene.hpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -11,7 +10,8 @@ #include #include -#include "../../../externals/tinyobjloader/tiny_obj_loader.h" +#include +#include #include "core.hpp" #include "primitive.hpp" @@ -856,6 +856,189 @@ class Scene } } + const unsigned char* getBufferData(const tinygltf::Model& model, int accessorIdx, size_t& byteStride, size_t& count) { + if (accessorIdx < 0) return nullptr; + const auto& accessor = model.accessors[accessorIdx]; + const auto& bufferView = model.bufferViews[accessor.bufferView]; + const auto& buffer = model.buffers[bufferView.buffer]; + + count = accessor.count; + byteStride = accessor.ByteStride(bufferView); + return &(buffer.data[accessor.byteOffset + bufferView.byteOffset]); + } + + void loadModelGLTF(const std::string& filename) + { + std::filesystem::path filepath(filename); + clear(); + + tinygltf::Model model; + tinygltf::TinyGLTF loader; + std::string err; + std::string warn; + + bool ret = false; + if (filepath.extension() == ".glb") { + ret = loader.LoadBinaryFromFile(&model, &err, &warn, filepath.string()); + } else { + ret = loader.LoadASCIIFromFile(&model, &err, &warn, filepath.string()); + } + + if (!warn.empty()) std::cout << "[Scene] " << warn << std::endl; + if (!ret) { + if (!err.empty()) std::cout << "[Scene] failed to load " << filepath.string() << ": " << err << std::endl; + return; + } + + // map glTF materials to tinyobj::material_t + std::vector convertedMaterials; + for (const auto& gtMat : model.materials) { + tinyobj::material_t m; + m.name = gtMat.name; + + m.diffuse[0] = static_cast(gtMat.pbrMetallicRoughness.baseColorFactor[0]); + m.diffuse[1] = static_cast(gtMat.pbrMetallicRoughness.baseColorFactor[1]); + m.diffuse[2] = static_cast(gtMat.pbrMetallicRoughness.baseColorFactor[2]); + + m.dissolve = static_cast(gtMat.pbrMetallicRoughness.baseColorFactor[3]); + + m.emission[0] = static_cast(gtMat.emissiveFactor[0]); + m.emission[1] = static_cast(gtMat.emissiveFactor[1]); + m.emission[2] = static_cast(gtMat.emissiveFactor[2]); + + m.roughness = static_cast(gtMat.pbrMetallicRoughness.roughnessFactor); + m.metallic = static_cast(gtMat.pbrMetallicRoughness.metallicFactor); + + convertedMaterials.push_back(m); + } + + for (const auto& mesh : model.meshes) { + for (const auto& primitive : mesh.primitives) { + // only triangles + if (primitive.mode != TINYGLTF_MODE_TRIANGLES) continue; + + // get positions + size_t posStride = 0, posCount = 0; + auto posIt = primitive.attributes.find("POSITION"); + if (posIt == primitive.attributes.end()) continue; + const unsigned char* posBuffer = getBufferData(model, posIt->second, posStride, posCount); + + // get normals (optional) + size_t normStride = 0, normCount = 0; + auto normIt = primitive.attributes.find("NORMAL"); + const unsigned char* normBuffer = (normIt != primitive.attributes.end()) ? + getBufferData(model, normIt->second, normStride, normCount) : nullptr; + + // get indices + std::vector primitiveIndices; + if (primitive.indices >= 0) { + const auto& indexAccessor = model.accessors[primitive.indices]; + size_t idxStride = 0, idxCount = 0; + const unsigned char* idxBuffer = getBufferData(model, primitive.indices, idxStride, idxCount); + + for (size_t i = 0; i < idxCount; ++i) { + uint32_t idx = 0; + if (indexAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { + idx = *reinterpret_cast(idxBuffer + i * idxStride); + } else if (indexAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { + idx = *reinterpret_cast(idxBuffer + i * idxStride); + } else if (indexAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { + idx = *reinterpret_cast(idxBuffer + i * idxStride); + } + primitiveIndices.push_back(idx); + } + } else { + // Unindexed primitives: generate sequential indices + for (uint32_t i = 0; i < posCount; ++i) primitiveIndices.push_back(i); + } + + for (size_t f = 0; f < primitiveIndices.size(); f += 3) { + std::vector faceVertices; + std::vector faceNormals; + + for (size_t v = 0; v < 3; ++v) { + uint32_t idx = primitiveIndices[f + v]; + + // read position + const float* pPtr = reinterpret_cast(posBuffer + idx * posStride); + faceVertices.push_back(Vec3f(pPtr[0], pPtr[1], pPtr[2])); + + // read normal if it exists + if (normBuffer) { + const float* nPtr = reinterpret_cast(normBuffer + idx * normStride); + faceNormals.push_back(Vec3f(nPtr[0], nPtr[1], nPtr[2])); + } + } + + if (faceNormals.empty()) { + Vec3f v1 = normalize(faceVertices[1] - faceVertices[0]); + Vec3f v2 = normalize(faceVertices[2] - faceVertices[0]); + Vec3f n = normalize(cross(v1, v2)); + faceNormals.push_back(n); + faceNormals.push_back(n); + faceNormals.push_back(n); + } + + for (int i = 0; i < 3; ++i) { + this->vertices.push_back(faceVertices[i][0]); + this->vertices.push_back(faceVertices[i][1]); + this->vertices.push_back(faceVertices[i][2]); + + this->normals.push_back(faceNormals[i][0]); + this->normals.push_back(faceNormals[i][1]); + this->normals.push_back(faceNormals[i][2]); + + this->indices.push_back(this->indices.size()); + } + + // map primitive material + std::optional material = std::nullopt; + if (primitive.material >= 0 && primitive.material < convertedMaterials.size()) { + material = convertedMaterials[primitive.material]; + } + this->materials.push_back(material); + } + } + } + + // populate triangles + for (size_t faceID = 0; faceID < nFaces(); ++faceID) { + this->triangles.emplace_back(vertices.data(), indices.data(), normals.data(), faceID); + } + + // populate bxdfs + for (size_t faceID = 0; faceID < nFaces(); ++faceID) { + const auto material = this->materials[faceID]; + if (material) { + tinyobj::material_t m = material.value(); + if (m.dissolve != 1.0f && m.dissolve != 0.0f) // tinygltf sets alpha to 1.0 by default + this->bxdfs.push_back(createBxDF(m, m.dissolve, 1.0f - m.dissolve, 0.5f)); + else + this->bxdfs.push_back(createBxDF(m)); + } else { + this->bxdfs.push_back(createDefaultBxDF()); + } + } + + // populate lights, primitives + for (size_t faceID = 0; faceID < nFaces(); ++faceID) { + std::shared_ptr light = nullptr; + const auto material = this->materials[faceID]; + std::string sh_name = ""; + + if (material) { + tinyobj::material_t m = material.value(); + sh_name = m.name; + light = createAreaLight(m, &this->triangles[faceID]); + if (light != nullptr) { + lights.push_back(light); + } + } + + primitives.emplace_back(&this->triangles[faceID], this->bxdfs[faceID], sh_name, light); + } + } + /** * @fn uint32_t nVertices() const * @brief Get the number of vertices in the scene @@ -932,6 +1115,47 @@ class Scene rtcCommitScene(scene); } + // void build_hiprt(hiprtContext context) + // { + // std::cout << "[Scene] Building AMD HIP RT scene..." << std::endl; + + // float* d_vertices; + // uint32_t* d_indices; + // hipMalloc(&d_vertices, vertices.size() * sizeof(float)); + // hipMalloc(&d_indices, indices.size() * sizeof(uint32_t)); + // hipMemcpy(d_vertices, vertices.data(), vertices.size() * sizeof(float), hipMemcpyHostToDevice); + // hipMemcpy(d_indices, indices.data(), indices.size() * sizeof(uint32_t), hipMemcpyHostToDevice); + + // hiprtTriangleMeshPrimitive meshGeom = {}; + // meshGeom.vertices = d_vertices; + // meshGeom.vertexCount = nVertices(); + // meshGeom.vertexStride = 3 * sizeof(float); + // meshGeom.triangleIndices = d_indices; + // meshGeom.triangleCount = nFaces(); + // meshGeom.triangleStride = 3 * sizeof(uint32_t); + + // hiprtGeometryBuildInput input = {}; + // input.type = hiprtGeometryTypeTriangleMesh; + // input.primitive.triangleMesh = meshGeom; + + // hiprtBuildOptions options = {}; + // options.buildFlags = hiprtBuildFlagHintFastTrace; + + // hiprtGeometry geometry; + // hiprtCreateGeometry(context, &input, &options, &geometry); + + // size_t scratchSize; + // hiprtGetGeometryBuildTemporaryBufferSize(context, &input, &options, &scratchSize); + + // void* d_scratch; + // hipMalloc(&d_scratch, scratchSize); + + // hiprtBuildGeometry(context, hiprtBuildOperationBuild, &input, &options, d_scratch, nullptr, geometry); + + // hipFree(d_scratch); + + // } + /** * @fn bool intersect(const Ray &ray, IntersectInfo &info) const * @brief Gets the intersection information of a ray in the scene. diff --git a/src/cpp/test/CMakeLists.txt b/src/cpp/test/CMakeLists.txt index 4fcdd35..f2f7b77 100644 --- a/src/cpp/test/CMakeLists.txt +++ b/src/cpp/test/CMakeLists.txt @@ -1,11 +1,15 @@ add_executable(test_image "test_image.cpp") -target_link_libraries(test_image PUBLIC pm) +target_link_libraries(test_image PUBLIC pm image triangle primitive photon_map) +target_include_directories(test_image PUBLIC "../include") add_executable(test_camera "test_camera.cpp") -target_link_libraries(test_camera PUBLIC pm) +target_link_libraries(test_camera PUBLIC pm image triangle primitive photon_map) +target_include_directories(test_camera PUBLIC "../include") add_executable(test_intersector "test_intersector.cpp") -target_link_libraries(test_intersector PUBLIC pm) +target_link_libraries(test_intersector PUBLIC pm image triangle primitive photon_map) +target_include_directories(test_intersector PUBLIC "../include") add_executable(visualize_photon_map "visualize_photon_map.cpp") -target_link_libraries(visualize_photon_map PUBLIC pm) +target_link_libraries(visualize_photon_map PUBLIC pm image triangle primitive photon_map) +target_include_directories(visualize_photon_map PUBLIC "../include") \ No newline at end of file diff --git a/src/openalea/spice/loader/load_plant.py b/src/openalea/spice/loader/load_plant.py index 94462d2..bf117b7 100644 --- a/src/openalea/spice/loader/load_plant.py +++ b/src/openalea/spice/loader/load_plant.py @@ -1,6 +1,6 @@ try: from openalea.lpy import Lsystem -except: +except ImportError: pass from openalea.spice import ( diff --git a/src/openalea/spice/reader/read_rad_geo.py b/src/openalea/spice/reader/read_rad_geo.py index 01ab62a..e09c256 100644 --- a/src/openalea/spice/reader/read_rad_geo.py +++ b/src/openalea/spice/reader/read_rad_geo.py @@ -188,12 +188,12 @@ def read_rad(file: Path, scale_factor: float, invert_normals: bool): if type in ("point_light", "point"): i += 1 vert = [] - l = re.split(r"\s+|;+", lines[i]) + line = re.split(r"\s+|;+", lines[i]) vert.append( ( - float(l[0]) / scale_factor, - float(l[1]) / scale_factor, - float(l[2]) / scale_factor, + float(line[0]) / scale_factor, + float(line[1]) / scale_factor, + float(line[2]) / scale_factor, ) ) shapes[name] = { @@ -209,15 +209,15 @@ def read_rad(file: Path, scale_factor: float, invert_normals: bool): tmp = i + 1 nbCoords = int(size / 3) if type == "cylinder": - l = re.split(r"\s+|;+", lines[i + 1]) + l1 = re.split(r"\s+|;+", lines[i + 1]) l2 = re.split(r"\s+|;+", lines[i + 2]) l3 = re.split(r"\s+|;+", lines[i + 3]) r = float(l3[0]) / scale_factor x, y, z = ( - float(l[0]) / scale_factor, - float(l[1]) / scale_factor, - float(l[2]) / scale_factor, + float(l1[0]) / scale_factor, + float(l1[1]) / scale_factor, + float(l1[2]) / scale_factor, ) x2, y2, z2 = ( float(l2[0]) / scale_factor, @@ -237,16 +237,16 @@ def read_rad(file: Path, scale_factor: float, invert_normals: bool): } i += 3 elif type == "cone": - l = re.split(r"\s+|;+", lines[i + 1]) + l1 = re.split(r"\s+|;+", lines[i + 1]) l2 = re.split(r"\s+|;+", lines[i + 2]) l3 = re.split(r"\s+|;+", lines[i + 3]) r = float(l3[0]) / scale_factor r2 = float(l3[1]) / scale_factor x, y, z = ( - float(l[0]) / scale_factor, - float(l[1]) / scale_factor, - float(l[2]) / scale_factor, + float(l1[0]) / scale_factor, + float(l1[1]) / scale_factor, + float(l1[2]) / scale_factor, ) x2, y2, z2 = ( float(l2[0]) / scale_factor, @@ -288,12 +288,12 @@ def read_rad(file: Path, scale_factor: float, invert_normals: bool): i += 3 else: for j in range(tmp, tmp + nbCoords): - l = re.split(r"\s+|;+", lines[j]) + line = re.split(r"\s+|;+", lines[j]) vert.append( ( - float(l[0]) / scale_factor, - float(l[1]) / scale_factor, - float(l[2]) / scale_factor, + float(line[0]) / scale_factor, + float(line[1]) / scale_factor, + float(line[2]) / scale_factor, ) ) i = j diff --git a/src/openalea/spice/simulator.py b/src/openalea/spice/simulator.py index a8295d5..29c7684 100644 --- a/src/openalea/spice/simulator.py +++ b/src/openalea/spice/simulator.py @@ -688,7 +688,6 @@ def r(): light_id = phmap.getIthPhoton(i).lightId photons.append(((photon[0], photon[1], photon[2]), light_id)) if mode == "oawidgets": - from openalea.widgets.plantgl import PlantGL import k3d from k3d.colormaps import matplotlib_color_maps @@ -774,7 +773,6 @@ def r(): pgl.Viewer.display(ph_sc) return None elif mode == "oawidgets": - from openalea.widgets.plantgl import PlantGL import k3d from k3d.colormaps import matplotlib_color_maps diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 89a416f..9d65c9c 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -15,5 +15,5 @@ execute_process( find_package(nanobind CONFIG REQUIRED) nanobind_add_module(libspice LTO ext_photonmapping_nano.cpp) -target_link_libraries(libspice PUBLIC tinyobjloader embree OpenMP::OpenMP_CXX pm) +target_link_libraries(libspice PUBLIC tinyobjloader tinygltf embree OpenMP::OpenMP_CXX pm) #endif() diff --git a/src/python/ext_photonmapping_nano.cpp b/src/python/ext_photonmapping_nano.cpp index 5703ad4..76fa6e9 100644 --- a/src/python/ext_photonmapping_nano.cpp +++ b/src/python/ext_photonmapping_nano.cpp @@ -507,6 +507,11 @@ NB_MODULE(libspice, m) &Scene::loadModel, "Function to load a model in the scene, must be an .obj file path", nb::arg("filepath")) + .def( + "loadModelGLTF", + &Scene::loadModelGLTF, + "Function to load a model in the scene, must be an .gltf/.glb file path", + nb::arg("filepath")) .def("setupTriangles", &Scene::setupTriangles) .def("addFaceInfos", &Scene::addFaceInfos, diff --git a/tests/data/cornellbox-water2.glb b/tests/data/cornellbox-water2.glb new file mode 100644 index 0000000..0368df9 Binary files /dev/null and b/tests/data/cornellbox-water2.glb differ diff --git a/tests/test_load_environment.py b/tests/test_load_environment.py index 0e1fa4d..4cd35de 100644 --- a/tests/test_load_environment.py +++ b/tests/test_load_environment.py @@ -1,9 +1,12 @@ +import pathlib from openalea.plantgl.scenegraph import QuadSet, Shape from openalea.plantgl.all import Scene as pglScene from openalea.spice import Scene from openalea.spice.loader.load_environment import addEnvironment, addLightDirectionPgl +filepath = pathlib.Path(__file__).parent.resolve() / 'data' + def test_addEnvironment(): sc = Scene() # A list of the coordinates of the square @@ -18,3 +21,11 @@ def test_addEnvironment(): addEnvironment(sc, sh, 400, {},{},{}) addLightDirectionPgl(pglScene([sh]), 1) + + +def test_load_GLTF(): + sc = Scene() + sc.loadModelGLTF(str(filepath / 'cornellbox-water2.glb')) + + assert sc.nFaces() > 0 + diff --git a/tests/test_simple_simulation.py b/tests/test_simple_simulation.py index bfe7d0a..c03da15 100644 --- a/tests/test_simple_simulation.py +++ b/tests/test_simple_simulation.py @@ -6,15 +6,16 @@ from openalea.spice.configuration import Configuration from openalea.spice.simulator import Simulator +import shutil filepath = pathlib.Path(__file__).parent.resolve() / 'data' def test_configuration(): simulator = Simulator(config_file=filepath / "simulation.ini") - assert type(simulator.configuration) == Configuration + assert type(simulator.configuration) is Configuration simulator.configuration.read_file(filepath / "simulation_2.ini") - assert type(simulator.configuration) == Configuration + assert type(simulator.configuration) is Configuration assert simulator.configuration.KEEP_ALL == 0 try: @@ -84,6 +85,8 @@ def test_simple_simulation(): str(filepath / "point_calibration.csv")) simulator.results.writeResults() + shutil.rmtree('./results') + @pytest.mark.skip(reason="Segfault with pytest") def test_visualization():